mcu_hi3321_watch/tjd/service/service_sport.c
2025-05-26 20:15:20 +08:00

555 lines
22 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "service_sport.h"
#include "TjdUiMsgCenter.h"
#include "cmsis_os2.h"
#include "common_def.h"
#include "cwm_data_deal.h"
#include "hr_api.h"
#include "rtc_api.h"
#include "securec.h"
#include "service_hrsensor.h"
#include "soc_osal.h"
#include "sql_fit.h"
#include "sql_setting.h"
#include "sys_config.h"
#include "sys_typedef.h"
#include "task_service_timer.h"
#include <power_display_service.h>
#include <stdint.h>
#include <stdio.h>
#define ENABLE_PRINT_INFO 1
#if ENABLE_PRINT_INFO
#define static_print_info(...) sys_ui_log_i(__VA_ARGS__) // 一般信息打印宏控制
#define static_print_warn(...) sys_ui_log_w(__VA_ARGS__) // 警告信息打印一般常开
#define static_print_error(...) sys_ui_log_e(__VA_ARGS__) // 错误信息打印一般常开
#else
#define static_print_info(...)
#define static_print_warn(...)
#define static_print_error(...)
#endif
#define SUCCESS 0
#define ERROR 1
pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;
bool if_popup = false; // 定义和初始化变量
static bool serviceIfOpen = false;
static bool ifSuspend = false;
static uint64_t last_ms_time_stamp = 0; //(ms)
static uint64_t current_ms_time_stamp = 0; //(ms)
static uint64_t pause_start_time_stamp = 0; // 记录暂停开始时间(ms)
static uint64_t pause_duration = 0; // 暂停时长(ms)
static SportData lastData; // 上次运动数据
static SportData curData; // 当前运动数据
static float curDistance = 0;
static uint32_t recent_steps = 0; // 最近一次运动步数
static uint32_t exerciseDuration = 0; // 运动时长(s)
static uint32_t end_remind_time_stamp = 0; // 记录运动结束提醒时间戳(ms)
// static uint32_t suspendDuration = 1;
static struct rtc_time localTime;
static Reminder sport_end_remind; // 记录运动结束提醒信息
static SportType_E sport_type = SPORT_TYPE_MAX; // 记录当前运动类型
static uint32_t heartRateValue = 0; // 心率值
static uint32_t heart_rate_trigger_time = 0; // 记录心率触发时间
static bool heart_rate_first_triggered = false;
static SportMsgType end_remind_type = SPORT_MSG_TYPE_END_30_MIN_REMIND;
static uint32_t MILLISEC_TO_NANOSEC = 1000000;
static uint16_t SEC_TO_MILLISEC = 1000;
static bool movement_force_remind_need_trigger = true; // 记录运动强制提醒是否需要触发标志位
static bool movement_auto_end_need_trigger = true; // 记录运动强制提醒是否需要触发标志位
// motion_recognition_entry:用于识别是否是从运动识别开始运动的标志位,其中高位表示是否是从运动识别开始运动,低位表示运动类型
uint8_t motion_recognition_entry = 0x00;
//=============================================
// 运动数据结构体定义
uint8_t g_cur_sport_type = 0;
int g_goal_sport_time = 3600; //目标运动时长: s
double g_goal_sport_dist = 3000; //目标运动距离m
double g_current_sport_dist; //当前运动距离m
int g_goal_sport_calorie = 100; //目标运动卡路里消耗kcal
int g_current_sport_calorie; //当前运动卡路里消耗kcal
int g_current_heart_rate_value; //当前心率值: bpm
bool g_sport_popup_flag_arry[3] = {0}; //运动弹窗标志
void service_set_cur_sport_type(uint8_t type) { g_cur_sport_type = type; }
void service_reset_sport_pop_up_flag(void)
{
for (int i = 0; i < 3; i++) {
g_sport_popup_flag_arry[i] = 0;
}
}
void service__set_pop_up_flag(uint8_t index, bool value) { g_sport_popup_flag_arry[index] = value; }
void service_set_pop_up_flag(uint8_t index, bool value) { g_sport_popup_flag_arry[index] = value; }
void service_set_sport_goal_time(int timestamp) { g_goal_sport_time = timestamp; }
int service_get_sport_goal_time(void) { return g_goal_sport_time; }
double service_get_goal_sport_dist(void) { return g_goal_sport_dist; }
void service_set_goal_sport_dist(double value) { g_goal_sport_dist = value; }
int service_get_goal_sport_calorie(void) { return g_goal_sport_calorie; }
void service_set_goal_sport_calorie(int value) { g_goal_sport_calorie = value; }
//=============================================
uint8_t getmotion_recognition_entry(void) { return motion_recognition_entry; } //外部引用调用
void setmotion_recognition_entry(uint8_t motion) { motion_recognition_entry = motion; } //外部引用调用
void set_movement_force_remind_need_trigger(uint8_t state) { movement_force_remind_need_trigger = state; }
void set_movement_auto_end_need_trigger(uint8_t state) { movement_auto_end_need_trigger = state; }
uint32_t GetTime(void)
{
struct timespec time;
clock_gettime(CLOCK_MONOTONIC, &time);
return time.tv_sec * SEC_TO_MILLISEC + time.tv_nsec / MILLISEC_TO_NANOSEC;
}
uint32_t GetElapseTime(uint32_t startTime)
{
uint32_t currentTime = GetTime();
uint32_t elapseTime;
if (currentTime > startTime) {
elapseTime = currentTime - startTime;
} else {
elapseTime = (UINT32_MAX - startTime) + currentTime + 1;
}
// printf("startTime = %d, currentTime = %d, elapseTime = %d\n", startTime, currentTime, elapseTime);
return elapseTime;
}
uint8_t sporting_hr_control_handler(SportingHrAction action, void *param)
{
uint8_t ret = 0;
hr_api_info_t hr_api_info = {HR_SERVICE_UI_SPORT, 0xffffffff};
service_hrs_api *hrsensorInfo = tjd_service_hrs_get_ops();
switch (action) {
case HrStart:
hrsensorInfo->open(hr_api_info);
break;
case HrStop:
hrsensorInfo->close();
break;
case HrGetStatus:
// ret = HeartRateGetWearStatus();
break;
default:
break;
}
return ret;
}
// 获取运动数据函数
int tjd_sql_sport_get_data(SportData *sportData)
{
if (sportData != NULL) {
tjd_driver_rtc_get_ops()->get_rtc_time(&localTime);
sportData->steps = sql_fit_get_day_step(localTime.tm_wday);
sportData->calories = sql_fit_get_day_calorie(localTime.tm_wday);
sportData->distance = sql_fit_get_day_distance(localTime.tm_wday);
return SUCCESS;
}
return ERROR;
}
// 检查并发布弹窗
void check_and_publish_popup(bool *sport_status, uint8_t *heart_rate_array, int curSportTime, SportData curData)
{
for (int i = 0; i < 3; i++) {
static_print_info("sport_status[%d] = %d\n", i, sport_status[i]);
}
Reminder reminders[4] = {{SPORT_MSG_TYPE_NONE, PRIORITY_GOAL, 0, false, NULL},
{SPORT_MSG_TYPE_NONE, PRIORITY_HEART_RATE, 0, false, NULL},
{SPORT_MSG_TYPE_NONE, PRIORITY_DISTANCE, 0, false, NULL},
{SPORT_MSG_TYPE_NONE, PRIORITY_TIME, 0, false, NULL}};
// 目标提醒
if (sport_status[0] && !if_popup) {
reminders[PRIORITY_GOAL].msgType = SPORT_MSG_TYPE_DISTANCE;
reminders[PRIORITY_GOAL].value = g_goal_sport_dist;
static_print_info("line = %d ,curSportDist=%d,g_goal_sport_dist=%d\n", __LINE__, curData.distance,
g_goal_sport_dist);
reminders[PRIORITY_GOAL].isGoalReached = (curData.distance >= g_goal_sport_dist);
if (reminders[PRIORITY_GOAL].isGoalReached) {
g_sport_popup_flag_arry[0] = false; // 已发布,清除状态 // 已发布,清除状态
}
}
if (sport_status[1] && !if_popup) {
reminders[PRIORITY_GOAL].msgType = SPORT_MSG_TYPE_TIME;
reminders[PRIORITY_GOAL].value = g_goal_sport_time;
static_print_info("line = %d ,curSportTime=%d,g_goal_sport_time()=%d\n", __LINE__, curSportTime,
g_goal_sport_time);
reminders[PRIORITY_GOAL].isGoalReached = (curSportTime >= g_goal_sport_time);
if (reminders[PRIORITY_GOAL].isGoalReached) {
g_sport_popup_flag_arry[1] = false; // 已发布,清除状态
}
}
if (sport_status[2] && !if_popup) {
reminders[PRIORITY_GOAL].msgType = SPORT_MSG_TYPE_CALORIE;
reminders[PRIORITY_GOAL].value = g_goal_sport_calorie;
reminders[PRIORITY_GOAL].isGoalReached = (curData.calories >= g_goal_sport_calorie);
if (reminders[PRIORITY_GOAL].isGoalReached) {
g_sport_popup_flag_arry[2] = false; // 已发布,清除状态; // 已发布,清除状态
}
}
// 心率提醒
heartRateValue = tjd_service_hrsensor_get_cur_hrvalue();
// static_print_info("heartRateValue = %d\n", heartRateValue);
for (size_t j = 0; j < 5; j++) {
// static_print_info("heart_rate_array[%d] = %d\n", j, heart_rate_array[j]);
if (heart_rate_array[j] && j != 4) {
switch (heartRateValue) {
// case 60 ... 150:
case 139 ... 150:
reminders[PRIORITY_HEART_RATE].msgType = SPORT_MSG_TYPE_HEART_RATE_1;
// sql_fit_set_sport_heart_rate_remind_value(0, 0);
reminders[PRIORITY_HEART_RATE].isGoalReached = true;
break;
case 151 ... 162:
reminders[PRIORITY_HEART_RATE].msgType = SPORT_MSG_TYPE_HEART_RATE_2;
// sql_fit_set_sport_heart_rate_remind_value(1, 0);
reminders[PRIORITY_HEART_RATE].isGoalReached = true;
break;
case 163 ... 174:
reminders[PRIORITY_HEART_RATE].msgType = SPORT_MSG_TYPE_HEART_RATE_3;
// sql_fit_set_sport_heart_rate_remind_value(2, 0);
reminders[PRIORITY_HEART_RATE].isGoalReached = true;
break;
case 175 ... 999:
reminders[PRIORITY_HEART_RATE].msgType = SPORT_MSG_TYPE_HEART_RATE_4;
// sql_fit_set_sport_heart_rate_remind_value(3, 0);
reminders[PRIORITY_HEART_RATE].isGoalReached = true;
break;
default:
reminders[PRIORITY_HEART_RATE].isGoalReached = false;
break;
}
}
}
// static_print_info("line = %d ,curSportDist = %d, sql_fit_get_sport_dist_remind_value() = %d ", __LINE__,
// curData.distance, sql_fit_get_sport_dist_remind_value());
// 单段距离提醒
if ((curData.distance != 0) && sql_fit_get_sport_dist_remind_value() != 0 &&
(curData.distance % sql_fit_get_sport_dist_remind_value() == 0) &&
(sql_fit_get_sport_dist_remind_value() != 99999)) {
reminders[PRIORITY_DISTANCE].msgType = SPORT_MSG_TYPE_DISTANCE_REMIND;
reminders[PRIORITY_DISTANCE].value = curData.distance;
reminders[PRIORITY_DISTANCE].isGoalReached = true;
}
// // 时间提醒
// static_print_info("line = %d ,curSportTime = %d, sql_fit_get_sport_time_remind_value() = %d ", __LINE__,
// curData.distance, sql_fit_get_sport_time_remind_value());
if (curSportTime != 0 && sql_fit_get_sport_time_remind_value() != 0 &&
(curSportTime % sql_fit_get_sport_time_remind_value() == 0) &&
(sql_fit_get_sport_time_remind_value() != 99999)) {
reminders[PRIORITY_TIME].msgType = SPORT_MSG_TYPE_TIME_REMIND;
reminders[PRIORITY_TIME].value = curSportTime;
reminders[PRIORITY_TIME].isGoalReached = true;
}
// printf("exerciseDuration = %d, heart_rate_trigger_time = %d, value = %d, heart_rate_first_triggered = %d\n",
// exerciseDuration, heart_rate_trigger_time, exerciseDuration - heart_rate_trigger_time,
// heart_rate_first_triggered);
for (int i = 0; i < 4; ++i) {
if (reminders[i].msgType != SPORT_MSG_TYPE_NONE) {
if (heart_rate_first_triggered && (reminders[i].priority == PRIORITY_HEART_RATE) &&
((exerciseDuration - heart_rate_trigger_time) < 60))
continue;
publish_reminder(&reminders[i]);
}
}
}
void check_and_pop_sport_end_reminder(uint32_t steps)
{
if (recent_steps == steps) {
end_remind_time_stamp++;
recent_steps == steps;
switch (end_remind_type) {
case SPORT_MSG_TYPE_END_30_MIN_REMIND:
if (end_remind_time_stamp == 30 * 60) { // 30*60
end_remind_type++;
sport_end_remind.value = 30;
sport_end_remind.isGoalReached = true;
sport_end_remind.msgType = SPORT_MSG_TYPE_END_30_MIN_REMIND;
} else
sport_end_remind.isGoalReached = false;
break;
case SPORT_MSG_TYPE__END_50_MIN_REMIND:
if (end_remind_time_stamp == 50 * 60) { // 50*60
end_remind_type++;
sport_end_remind.value = 50;
sport_end_remind.isGoalReached = true;
sport_end_remind.msgType = SPORT_MSG_TYPE__END_50_MIN_REMIND;
} else
sport_end_remind.isGoalReached = false;
break;
case SPORT_MSG_TYPE__END_60_MIN_REMIND:
if (end_remind_time_stamp == 60 * 60) { // 60*60
end_remind_type++;
sport_end_remind.value = 60;
sport_end_remind.isGoalReached = true;
sport_end_remind.msgType = SPORT_MSG_TYPE__END_60_MIN_REMIND;
} else
sport_end_remind.isGoalReached = false;
break;
case SPORT_MSG_TYPE__END_65_MIN_REMIND:
if (end_remind_time_stamp == 65 * 60) { // 65*60
end_remind_type++;
sport_end_remind.value = 65;
sport_end_remind.isGoalReached = true;
sport_end_remind.msgType = SPORT_MSG_TYPE__END_65_MIN_REMIND;
} else
sport_end_remind.isGoalReached = false;
break;
case SPORT_MSG_TYPE__END_70_MIN_REMIND:
if (end_remind_time_stamp == 70 * 60) { // 70*60
end_remind_type++;
sport_end_remind.value = 70;
sport_end_remind.isGoalReached = true;
sport_end_remind.msgType = SPORT_MSG_TYPE__END_70_MIN_REMIND;
} else
sport_end_remind.isGoalReached = false;
break;
default:
sport_end_remind.isGoalReached = false;
break;
}
if (sport_end_remind.isGoalReached) {
sport_end_remind.priority = PRIORITY_SPORT_END;
sport_end_remind.content = NULL;
publish_reminder(&sport_end_remind);
}
} else {
recent_steps = steps;
end_remind_time_stamp = 0;
end_remind_type = SPORT_MSG_TYPE_END_30_MIN_REMIND;
}
printf("func = %s, steps = %d\n", __func__, steps);
}
// 发布提醒
void publish_reminder(Reminder *reminder)
{
if (reminder->isGoalReached) {
// 目标达成,显示目标达成界面
// if (reminder->priority == PRIORITY_DISTANCE || reminder->priority == PRIORITY_TIME) {
// reminder->isGoalReached = false;
// }
TjdUiMsgEventPublish(TJDUI_TOPIC_EVENT_SPORT, &reminder, sizeof(reminder));
tjd_service_sport_set_screen_on(5);
heart_rate_trigger_time = exerciseDuration;
heart_rate_first_triggered = true;
reminder->isGoalReached = false;
}
if_popup = false; // 重置弹窗状态,以便下一次提醒可以触发
}
// int curSportDist = 0;
// int curSportTime = 0;
static signed int tjd_service_sport_handle(void *param)
{
// static_print_info("==sport_service==sql_fit_get_sport_goal_time()=%d===%d===\n", sql_fit_get_sport_goal_time(),
// sql_fit_get_current_sport_goal_time());
pthread_mutex_lock(&data_mutex); // 锁定互斥锁
int msgType = 0;
SportData totalData;
uint8_t *heart_rate_array;
sql_fit_get_heart_rate_arry(&heart_rate_array);
// int curSportTime = sql_fit_get_current_sport_goal_time();
// curSportTime = curSportTime + 300;
// int curSportDist = sql_fit_get_current_sport_dist() * 1000;
// sql_fit_get_pop_up_flag(&sport_status);
// sql_fit_get_heart_rate_arry(&heart_rate_array);
// static_print_info("line = %d==curSportTime===%d===curSportDist=%d==sql_fit_get_current_sport_dist=%d====\n",
// __LINE__, curSportTime, curSportDist, sql_fit_get_current_sport_dist());
if (!tjd_sql_sport_get_data(&totalData)) {
static_print_info("tjd_sql_sport_get_data get cur data success");
curData.steps = totalData.steps - lastData.steps;
curData.calories = totalData.calories - lastData.calories;
curData.distance = totalData.distance - lastData.distance;
// curSportDist++;
// curData.distance = curSportDist;
static_print_info("curSteps=%d,curCalories=%d,curDistance=%d\n", curData.steps, curData.calories,
curData.distance);
printf("recent_steps = %d,sql_setting_get_motion_end_enable = %d\n", recent_steps,
sql_setting_get_motion_end_enable());
if (sql_setting_get_motion_end_enable() && sport_type == SPORT_TYPE_1) {
check_and_pop_sport_end_reminder(curData.steps);
}
// 弹窗逻辑
check_and_publish_popup(g_sport_popup_flag_arry, heart_rate_array, exerciseDuration, curData);
}
if (sql_setting_get_motion_end_enable() && motion_recognition_entry >> 4) {
if (sql_setting_get_motion_out_time() == exerciseDuration) {
uint8_t msg_data = MOVEMENT_TIMEOUT_REMINDER | sql_setting_get_motion_out_time() / 3600;
TjdUiMsgEventPublish(TJDUI_TOPIC_EVENT_ACTIVITY_RECOGNITION, &msg_data, sizeof(msg_data));
};
if ((sql_setting_get_motion_out_time() + 300000) == exerciseDuration && movement_force_remind_need_trigger) {
uint8_t msg_data = MOVEMENT_TIMEOUT_FORCED_END_REMINDER | sql_setting_get_motion_out_time() / 3600;
TjdUiMsgEventPublish(TJDUI_TOPIC_EVENT_ACTIVITY_RECOGNITION, &msg_data, sizeof(msg_data));
};
if ((sql_setting_get_motion_out_time() + 350000) == exerciseDuration && movement_auto_end_need_trigger) {
uint8_t msg_data = MOVEMENT_AUTO_END_REMINDER;
TjdUiMsgEventPublish(TJDUI_TOPIC_EVENT_ACTIVITY_RECOGNITION, &msg_data, sizeof(msg_data));
};
}
if (!ifSuspend) {
current_ms_time_stamp = GetTime();
update_exercise_duration(current_ms_time_stamp);
}
pthread_mutex_unlock(&data_mutex); // 解锁互斥锁
// static_print_info("==sport_service==exerciseDuration=%d==\n", exerciseDuration);
return 250;
}
// 运动服务开始函数
static void tjd_service_sport_start_send(void)
{
serviceIfOpen = true;
if (tjd_sql_sport_get_data(&lastData)) {
static_print_error("tjd_sql_sport_get_data get last data fail");
}
last_ms_time_stamp = GetTime();
printf("last_ms_time_stamp = %d\n", last_ms_time_stamp);
// sporting_hr_control_handler(HrStart, NULL);
queue_default_info_t msg_data = {tjd_service_sport_handle, NULL, 250, NULL};
osal_msg_queue_write_copy(tjd_task_service_timer_get_queue_id(), (void *)&msg_data, sizeof(queue_default_info_t),
0);
}
void tjd_service_sport_data_reset(void)
{
ifSuspend = false;
serviceIfOpen = false;
heart_rate_first_triggered = false;
exerciseDuration = 0;
end_remind_time_stamp = 0;
heart_rate_trigger_time = 0;
pause_duration = 0; // 清空暂停时长
motion_recognition_entry = 0x00;
movement_force_remind_need_trigger = true;
movement_auto_end_need_trigger = true;
// sporting_hr_control_handler(HrStop, NULL);
}
// 运动服务结束函数
static void tjd_service_sport_end_send(void)
{
tjd_service_sport_data_reset();
queue_default_info_t msg_data = {tjd_service_sport_handle, NULL, 0, NULL};
osal_msg_queue_write_copy(tjd_task_service_timer_get_queue_id(), (void *)&msg_data, sizeof(queue_default_info_t),
0);
}
void tjd_service_sport_open(void) { tjd_service_sport_start_send(); }
void tjd_service_sport_close(void) { tjd_service_sport_end_send(); }
bool tjd_get_service_if_open(void) { return serviceIfOpen; }
// 获取运动数据函数
SportingData_t tjd_service_sport_get_data(void)
{
// pthread_mutex_lock(&data_mutex); // 锁定互斥锁
SportingData_t sportData;
sportData.heartRate = tjd_service_hrsensor_get_cur_hrvalue();
sportData.exerciseDuration = exerciseDuration;
sportData.steps = curData.steps;
sportData.calories = curData.calories;
// sportData.distance = (curDistance ++)/1000;
sportData.distance = curData.distance;
// pthread_mutex_unlock(&data_mutex); // 解锁互斥锁
return sportData;
}
// 计算并更新运动时长的函数
void update_exercise_duration(uint64_t current_ms_time_stamp)
{
uint64_t elapsedTime = 0;
if (!pause_duration) {
elapsedTime = current_ms_time_stamp - pause_duration - last_ms_time_stamp;
pause_duration = 0; // 清空暂停时长
} else {
elapsedTime = current_ms_time_stamp - last_ms_time_stamp;
}
exerciseDuration = elapsedTime / 1000; // 累加总运动时长(秒)
// last_ms_time_stamp = current_ms_time_stamp; // 更新上次时间戳
}
// 暂停功能
void pause_exercise(void) { pause_start_time_stamp = GetTime(); }
// 恢复功能
void resume_exercise(void)
{
pause_duration = GetElapseTime(pause_start_time_stamp); // 更新暂停时长
pause_start_time_stamp = 0; // 清空暂停开始时间戳 F
last_ms_time_stamp = last_ms_time_stamp + pause_duration; // 更新上次时间戳
}
bool tjd_service_sport_get_if_suspend(void) { return ifSuspend; }
void tjd_service_sport_set_if_suspend(bool state)
{
if (state) {
pause_exercise();
} else {
resume_exercise();
}
ifSuspend = state;
}
void tjd_service_sport_set_if_popup(bool state) { if_popup = state; }
void tjd_service_sport_set_end_remind_type(uint8_t type) { end_remind_type = (SportMsgType)type; }
SportType_E tjd_service_sport_get_sport_type(void) { return sport_type; }
void tjd_service_sport_set_sport_type(uint8_t type) { sport_type = type; }
SportMsgType tjd_service_sport_get_end_remind_type(void) { return end_remind_type; }
static void tjd_service_sport_set_screen_on(int32_t timeout)
{
const power_display_svr_api_t *display_api = power_display_svr_get_api();
if (display_api->get_screen_state() != SCREEN_ON) {
display_api->turn_on_screen();
}
// display_api->set_screen_set_keepon_timeout(timeout);
return;
}