* @FilePath:
* /sdk/application/wearable/fd230app/nativeui/traing/src/TraingModel.cpp
*/
-#include "traing/include/TraingModel.h"
-#include "cmsis_os.h"
-#include "custom_utils.h"
-#include "safemodemanager/include/SafeModeManagerModelC.h"
-#include "thread_adapter.h"
-#include "traing/training_card/include/TraingCardPresenter.h"
-#include "uicomponents/UIComponents.h"
-#include "wearable_log.h"
-#include <cmsis_os2.h>
-#include <cstdint>
-#include <fstream>
-#include <iomanip>
-#include <iostream>
-#include <service/graphic_service.h>
-#include <sstream>
-#include <string>
-
-#include "ConfigModel.h"
-
-#ifdef SUPPORT_BLE_SERVICE
-#include "bleEvent.h"
-#include "bleService.h"
-#endif
-
-#ifdef SUPPORT_PPG_SERVICE
-#include "ppg_hal.h"
-#include "ppg_service.h"
-#endif
-
-#ifdef SUPPORT_CYWEE_SERVICE
-#include "cywee_hal.h"
-#include "cywee_service.h"
-#endif
-
-#define SPORT_TIMER_DATA 1
-
-#include "broadcast_feature.h"
-#include "broadcast_service.h"
-#include "custom_runtime.h"
-#include "power_display_service.h"
-#include "samgr_lite.h"
-
-#include "traingrecord/include/TraingRecordModel.h"
-
-#include "vibrator_service.h"
-#include <algorithm>
-#include <cmath>
-#include <filesystem>
-#include <fstream>
-#include <limits.h>
-#include <list>
-#include <memory>
-#include <mutex>
-#include <numeric>
-#include <queue>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <vector>
-
-#include "chip_system_time.h"
-#include "cmath"
-#include "system/include/SystemLogModel.h"
-#include <custom_save_runtime.h>
-
-#include "graphic_service.h"
-
-#if defined(SUPPORT_GNSS)
-#include "NMEA0183.h"
-#include "gnss_ctl.h"
-#include "pgnss_sample.h"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-static void Training_Notify(void *args, const Topic topic, Request *request);
-
-using namespace OHOS;
-
-Subscriber TraingSubscirber = {
- .identity = NULL,
- .args = NULL,
- .Notify = Training_Notify,
-};
-
-void TraingSubscribeTopic(Topic topic) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "stepsubscribeTopic, topic=%u", topic);
- BroadcastApi *broadcastApi = NULL;
- IUnknown *api = SAMGR_GetInstance()->GetFeatureApi(BROADCAST_SERVICE_NAME,
- BROADCAST_FEATURE_NAME);
- if (api == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "stepsubscribeTopic, GetFeatureApi failed");
- return;
- }
- int result =
- api->QueryInterface(api, DEFAULT_VERSION, (void **)&broadcastApi);
- if (result != 0 || broadcastApi == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "stepsubscribeTopic, QueryInterface failed");
- return;
- }
- broadcastApi->SubscribeTopic((IUnknown *)broadcastApi, topic,
- &TraingSubscirber);
- broadcastApi->Release((IUnknown *)broadcastApi);
-}
-
-void TraingUnsubscribeTopic(Topic topic) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "TraingUnsubscribeTopic, topic=%u",
- topic);
- BroadcastApi *broadcastApi = NULL;
- IUnknown *api = SAMGR_GetInstance()->GetFeatureApi(BROADCAST_SERVICE_NAME,
- BROADCAST_FEATURE_NAME);
- if (api == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "TraingUnsubscribeTopic, GetFeatureApi failed");
- return;
- }
- int result =
- api->QueryInterface(api, DEFAULT_VERSION, (void **)&broadcastApi);
- if (result != 0 || broadcastApi == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "TraingUnsubscribeTopic, QueryInterface failed");
- return;
- }
- broadcastApi->UnsubscribeTopic((IUnknown *)broadcastApi, topic,
- &TraingSubscirber);
- broadcastApi->Release((IUnknown *)broadcastApi);
-}
-int flag = 0;
-static void Training_Notify(void *args, const Topic topic, Request *request) {
-#ifdef SUPPORT_CYWEE_SERVICE
- UNUSED(args);
- UNUSED(topic);
- if (request == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "request is NULL");
- return;
- }
- //蓝牙开启运动
- if (request->msgId == TOPIC_EVENT_BLE_TRAINING) {
- training_ble *traingbleInfo = new training_ble();
- int rets = memcpy_s(traingbleInfo, sizeof(training_ble),
- (training_ble *)request->data, request->len);
- if (rets != EOK) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "memcpy_s act_result_t fail ret = %d", rets);
- return;
- }
-#ifdef SUPPORT_BLE_SERVICE
- TraingCardPresenter::GetInstance()->send_sport_cmd(traingbleInfo);
-#endif
- delete traingbleInfo;
- }
- //增加训练runtime控制
- if (uapi_dev_runtime_get_train_status() == 0 ||
- TraingModel::GetInstance()->GetSportState() == ExerciseState::End) {
- return;
- }
- //训练基本信息更新
- if (request->msgId == TOPIC_EVENT_ACTIVITY_UPDATE) {
- act_result_t *cyweeInfo = new act_result_t();
- int rets = memcpy_s(cyweeInfo, sizeof(act_result_t),
- (act_result_t *)request->data, request->len);
- if (rets != EOK) {
- delete cyweeInfo; // 处理内存分配失败的情况
- cyweeInfo = NULL;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "memcpy_s act_result_t fail ret = %d", rets);
- return;
- }
- TraingModel::GetInstance()->AnalysisData(cyweeInfo);
- // 更新其他目标完成度
- TraingModel::GetInstance()->CheckSportTag();
- delete cyweeInfo; // 释放内存
- }
- //训练额外信息
- if (request->msgId == TOPIC_EVENT_ACTIVITY_EXTRA_INFO_UPDATE) {
- act_ext_result_t *cyweeInfo_ext = new act_ext_result_t();
- int rets = memcpy_s(cyweeInfo_ext, sizeof(act_ext_result_t),
- (act_ext_result_t *)request->data, request->len);
- if (rets != EOK) {
- delete cyweeInfo_ext; // 处理内存分配失败的情况
- cyweeInfo_ext = NULL;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "memcpy_s act_ext_result_t fail ret = %d", rets);
- return;
- }
- TraingModel::GetInstance()->AnalysisExtraData(cyweeInfo_ext);
- delete cyweeInfo_ext; // 释放内存
- }
- //训练心率
- if (request->msgId == TOPIC_EVENT_HEARTRATE_UPDATA) {
- uint8_t heart = *((u_int8_t *)(request->data)); // 进行类型转换
- TraingModel::GetInstance()->AnalysisHeartData(heart);
- }
-#endif
-}
-#ifdef __cplusplus
-}
-#endif
-
-void store_sport_data() {
- // printf("运动数据存储!\r\n");
- OHOS::TraingModel::GetInstance()->RecordSportData();
- return;
-}
-
-namespace OHOS {
-#if TIMER
-// 声明定时器变量
-static osTimerId_t g_startCounting = NULL; // 定时器
-static constexpr uint64_t MS_ONE_SECONDS = 1000;
-static int g_motionTimes{0};
-static constexpr int32 NUM_TEN = 10;
-static constexpr int16 ERROR = 0;
-static constexpr int16 SUCCESS = 1;
-static constexpr int16 NUM_TIME_MIN = 60;
-static constexpr int16 NUM_TIME_HOUR = 3600;
-static constexpr int32 NUM_TIME_DAY = 86400;
-#endif
-
-// #define min(x, y) (((x) < (y)) ? (x) : (y))
-// #define max(x, y) (((x) > (y)) ? (x) : (y))
-
-SportData *sportdata = new SportData();
-int shock_count{0}; //震动提醒
-
-TraingModel ::TraingModel() {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TraingModel::TraingModel()");
- // sportdata->Initialize(); // 初始化结构体
-}
-TraingModel ::~TraingModel() {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TraingModel::~TraingModel()");
- // TraingUnsubscribeTopic(TOPIC_EVENT_ACTIVITY_UPDATE); // 取消订阅
- TraingUnsubscribeTopic(TOPIC_EVENT_BLE_TRAINING);
- // TraingUnsubscribeTopic(TOPIC_EVENT_HEARTRATE_UPDATA);
- // TraingUnsubscribeTopic(TOPIC_EVENT_ACTIVITY_EXTRA_INFO_UPDATE);
-}
-TraingModel *TraingModel::GetInstance(void) {
- static TraingModel instance;
- return &instance;
-}
-
-void TraingModel ::Init() {
- // TraingSubscribeTopic(TOPIC_EVENT_ACTIVITY_UPDATE); // 进行消息订阅
- TraingSubscribeTopic(TOPIC_EVENT_BLE_TRAINING);
- // TraingSubscribeTopic(TOPIC_EVENT_HEARTRATE_UPDATA);
- // TraingSubscribeTopic(TOPIC_EVENT_ACTIVITY_EXTRA_INFO_UPDATE);
-}
-/*设置运动类型*/
-void TraingModel::SetSportType(Exercisetype type) { sportType_ = type; }
-/*获取训练Id->协议*/
-int TraingModel::GetBleSportId(Exercisetype sporttype, UIResourceTextId id) {
- int offset = 0;
-
- switch (sporttype) {
- case Exercisetype::FITNESS: // 0~57
- offset = 0;
- break;
- case Exercisetype::DANCE: // 0~9
- offset = 58;
- break;
- case Exercisetype::LEISURE: // 0~23
- offset = 58 + 10;
- break;
- case Exercisetype::BALL: // 0~28
- offset = 58 + 10 + 24;
- break;
- case Exercisetype::WATER: // 0~23
- offset = 58 + 10 + 24 + 29;
- break;
- case Exercisetype::ICE: // 0~11
- offset = 58 + 10 + 24 + 29 + 24;
- break;
- case Exercisetype::EXTREME: // 0~9
- offset = 58 + 10 + 24 + 29 + 24 + 12;
- break;
- case Exercisetype::OTHER: // 0~8
- offset = 58 + 10 + 24 + 29 + 24 + 12 + 10;
- break;
- default:
- offset = 0;
- break;
- }
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "编译id offset %d id %d", offset, id);
- return id - offset;
-}
-
-/*结束数据icon*/
-std::string sportImages[8] = {
- FITNESS_54_IMAGE, DANCE_54_IMAGE, REST_54_IMAGE, BALL_54_IMAGE,
- SWIMMING_54_IMAGE, ICE_54_IMAGE, LIMIT_54_IMAGE, OTHER_54_IMAGE,
-};
-/*训练记录Icon*/
-std::string sportIcons[8] = {
- FITNESS_IMAGE, DANCE_IMAGE, REST_IMAGE, BALL_IMAGE,
- SWIMMING_IMAGE, ICE_IMAGE, LIMIT_IMAGE, OTHER_IMAGE,
-};
-/*特殊运动icon*/
-std::string sportIcons_special[6] = {RUNNING_60_IMAGE_END, WALKING_60_IMAGE_END,
- RIDING_60_IMAGE_END, RUNNING_IMAGE,
- WALKING_IMAGE, RIDING_IMAGE};
-
-/*
- type
- 0 是运动结束图标
- 1 是运动类型图标
-*/
-std::string TraingModel::GetSportIcon(Exercisetype sporttype,
- UIResourceTextId id, uint8_t type) {
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- " GetSportIcon sporttype %d id %d type %d", sporttype, id,
- type);
- bool specialsport = false;
- uint8_t specialindex = 0;
- switch (id) {
- case UIResourceTextId::STR_EXERCISE_OUTDOORRUNNING:
- case UIResourceTextId::STR_EXERCISE_INDOORRUNNING:
- specialsport = true;
- specialindex = 0;
- break;
- case UIResourceTextId::STR_EXERCISE_OUTDOORWALKING:
- case UIResourceTextId::STR_EXERCISE_INDOORWALKING:
- specialsport = true;
- specialindex = 1;
- break;
- case UIResourceTextId::STR_EXERCISE_OUTDOORBIKING:
- specialsport = true;
- specialindex = 2;
- break;
- }
- int index = static_cast<int>(sporttype);
- if (specialsport) {
- // 特殊运动
- if (type == 0) {
- return sportIcons_special[specialindex]; //返回特殊运动大图标
- } else {
- return sportIcons_special[specialindex + 3]; //返回特殊运动小图标
- }
- }
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, " GetResource index %d", index);
- if (type == 0) {
- return sportImages[index]; //返回运动类大图标
- } else {
- return sportIcons[index]; //返回运动类小图标
- }
- return "";
-}
-/*设置运动Id*/
-void TraingModel::SetSportEvent(UIResourceTextId id) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, " SetSportType %d", sportId_);
- //需要是结束开启的运动才重新配置
- if (sportState_ == ExerciseState::General) {
- IsSpecialSport(id); // 设置特殊性
- sportId_ = id;
- }
-}
-/*获取运动Id*/
-UIResourceTextId TraingModel::GetSportEvent() {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, " GetSportEvent %d", sportId_);
- return sportId_;
-}
-/*获取运动项目*/
-ExerciseClass TraingModel::GetSportId() {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, " GetSportType %d", sportId_);
- // 设置结构体里面的运动类型
- sportdata->sportId = sportId_;
- sportdata->sportType = sportType_;
-
- switch (sportId_) {
- // 室外跑步 室外走路
- case UIResourceTextId::STR_EXERCISE_OUTDOORRUNNING:
- case UIResourceTextId::STR_EXERCISE_OUTDOORWALKING:
- // return ExerciseClass::OUTDOOR; 统一使用室内
-
- // 室内跑步 室内走路
- case UIResourceTextId::STR_EXERCISE_INDOORRUNNING:
- case UIResourceTextId::STR_EXERCISE_INDOORWALKING:
- return ExerciseClass::INDOOR;
-
- // 跑步机
- case UIResourceTextId::STR_EXERCISE_TREADMILL:
- case UIResourceTextId::STR_EXERCISE_STROLLMACHINE:
- return ExerciseClass::TREADMILL;
-
- // 自由训练
- default:
- return ExerciseClass::FREE_TRAINING;
- }
-}
-/*判断是否为需要Gps的运动*/
-bool TraingModel::IsNeedGPS(UIResourceTextId sportId_) {
- //跑步 走路 骑行需要GPS
- switch (sportId_) {
- case UIResourceTextId::STR_EXERCISE_OUTDOORRUNNING:
- case UIResourceTextId::STR_EXERCISE_OUTDOORWALKING:
- case UIResourceTextId::STR_EXERCISE_INDOORRUNNING:
- case UIResourceTextId::STR_EXERCISE_INDOORWALKING:
- case UIResourceTextId::STR_EXERCISE_TREADMILL:
- case UIResourceTextId::STR_EXERCISE_STROLLMACHINE:
- return true;
- default:
- return false;
- }
-}
-/*判断是否为特殊运动*/
-void TraingModel::IsSpecialSport(UIResourceTextId sportId_) {
- switch (sportId_) {
- case UIResourceTextId::STR_EXERCISE_OUTDOORRUNNING: // 室外跑步 室外走路
- case UIResourceTextId::STR_EXERCISE_OUTDOORWALKING:
- case UIResourceTextId::STR_EXERCISE_INDOORRUNNING: // 室内跑步 室内走路
- case UIResourceTextId::STR_EXERCISE_INDOORWALKING:
- case UIResourceTextId::STR_EXERCISE_TREADMILL: // 跑步机
- case UIResourceTextId::STR_EXERCISE_STROLLMACHINE:
- isspecialsport_ = true;
- break;
- default: // 自由训练
- isspecialsport_ = false;
- break;
- }
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, " 特殊训练 %d 训练项 %d",
- isspecialsport_, sportId_);
-}
-
-bool TraingModel::GetIsSpecial() { return isspecialsport_; }
-/*获取目标字符串*/
-std::string TraingModel::GetTagStr() {
- /*
- 通用目标配置: 时长、卡路里
- 不通用目标配置: 步数、距离
-
- 如果配置项是特殊目标
- -> 普通训练: 无训练目标
- -> 特殊训练: 有训练目标
+ #include "traing/include/TraingModel.h"
+ #include "cmsis_os.h"
+ #include "custom_utils.h"
+ #include "safemodemanager/include/SafeModeManagerModelC.h"
+ #include "thread_adapter.h"
+ #include "traing/training_card/include/TraingCardPresenter.h"
+ #include "uicomponents/UIComponents.h"
+ #include "wearable_log.h"
+ #include <cmsis_os2.h>
+ #include <cstdint>
+ #include <fstream>
+ #include <iomanip>
+ #include <iostream>
+ #include <service/graphic_service.h>
+ #include <sstream>
+ #include <string>
+
+ #include "ConfigModel.h"
+
+ #ifdef SUPPORT_BLE_SERVICE
+ #include "bleEvent.h"
+ #include "bleService.h"
+ #endif
+
+ #ifdef SUPPORT_PPG_SERVICE
+ #include "ppg_hal.h"
+ #include "ppg_service.h"
+ #endif
+
+ #ifdef SUPPORT_CYWEE_SERVICE
+ #include "cywee_hal.h"
+ #include "cywee_service.h"
+ #endif
+
+ #define SPORT_TIMER_DATA 1
+
+ #include "broadcast_feature.h"
+ #include "broadcast_service.h"
+ #include "custom_runtime.h"
+ #include "power_display_service.h"
+ #include "samgr_lite.h"
+
+ #include "traingrecord/include/TraingRecordModel.h"
+
+ #include "vibrator_service.h"
+ #include <algorithm>
+ #include <cmath>
+ #include <filesystem>
+ #include <fstream>
+ #include <limits.h>
+ #include <list>
+ #include <memory>
+ #include <mutex>
+ #include <numeric>
+ #include <queue>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/stat.h>
+ #include <sys/time.h>
+ #include <vector>
+
+ #include "chip_system_time.h"
+ #include "cmath"
+ #include "system/include/SystemLogModel.h"
+ #include <custom_save_runtime.h>
+
+ #include "graphic_service.h"
+
+ #if defined(SUPPORT_GNSS)
+ #include "NMEA0183.h"
+ #include "gnss_ctl.h"
+ #include "pgnss_sample.h"
+ #endif
+
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+ static void Training_Notify(void *args, const Topic topic, Request *request);
+
+ using namespace OHOS;
+
+ Subscriber TraingSubscirber = {
+ .identity = NULL,
+ .args = NULL,
+ .Notify = Training_Notify,
+ };
+
+ void TraingSubscribeTopic(Topic topic) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "stepsubscribeTopic, topic=%u", topic);
+ BroadcastApi *broadcastApi = NULL;
+ IUnknown *api = SAMGR_GetInstance()->GetFeatureApi(BROADCAST_SERVICE_NAME,
+ BROADCAST_FEATURE_NAME);
+ if (api == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "stepsubscribeTopic, GetFeatureApi failed");
+ return;
+ }
+ int result =
+ api->QueryInterface(api, DEFAULT_VERSION, (void **)&broadcastApi);
+ if (result != 0 || broadcastApi == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "stepsubscribeTopic, QueryInterface failed");
+ return;
+ }
+ broadcastApi->SubscribeTopic((IUnknown *)broadcastApi, topic,
+ &TraingSubscirber);
+ broadcastApi->Release((IUnknown *)broadcastApi);
+ }
+
+ void TraingUnsubscribeTopic(Topic topic) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "TraingUnsubscribeTopic, topic=%u",
+ topic);
+ BroadcastApi *broadcastApi = NULL;
+ IUnknown *api = SAMGR_GetInstance()->GetFeatureApi(BROADCAST_SERVICE_NAME,
+ BROADCAST_FEATURE_NAME);
+ if (api == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "TraingUnsubscribeTopic, GetFeatureApi failed");
+ return;
+ }
+ int result =
+ api->QueryInterface(api, DEFAULT_VERSION, (void **)&broadcastApi);
+ if (result != 0 || broadcastApi == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "TraingUnsubscribeTopic, QueryInterface failed");
+ return;
+ }
+ broadcastApi->UnsubscribeTopic((IUnknown *)broadcastApi, topic,
+ &TraingSubscirber);
+ broadcastApi->Release((IUnknown *)broadcastApi);
+ }
+ int flag = 0;
+ static void Training_Notify(void *args, const Topic topic, Request *request) {
+ #ifdef SUPPORT_CYWEE_SERVICE
+ UNUSED(args);
+ UNUSED(topic);
+ if (request == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "request is NULL");
+ return;
+ }
+ //蓝牙开启运动
+ if (request->msgId == TOPIC_EVENT_BLE_TRAINING) {
+ training_ble *traingbleInfo = new training_ble();
+ int rets = memcpy_s(traingbleInfo, sizeof(training_ble),
+ (training_ble *)request->data, request->len);
+ if (rets != EOK) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "memcpy_s act_result_t fail ret = %d", rets);
+ return;
+ }
+ #ifdef SUPPORT_BLE_SERVICE
+ TraingCardPresenter::GetInstance()->send_sport_cmd(traingbleInfo);
+ #endif
+ delete traingbleInfo;
+ }
+ //增加训练runtime控制
+ if (uapi_dev_runtime_get_train_status() == 0 ||
+ TraingModel::GetInstance()->GetSportState() == ExerciseState::End) {
+ return;
+ }
+ //训练基本信息更新
+ if (request->msgId == TOPIC_EVENT_ACTIVITY_UPDATE) {
+ act_result_t *cyweeInfo = new act_result_t();
+ int rets = memcpy_s(cyweeInfo, sizeof(act_result_t),
+ (act_result_t *)request->data, request->len);
+ if (rets != EOK) {
+ delete cyweeInfo; // 处理内存分配失败的情况
+ cyweeInfo = NULL;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "memcpy_s act_result_t fail ret = %d", rets);
+ return;
+ }
+ TraingModel::GetInstance()->AnalysisData(cyweeInfo);
+ // 更新其他目标完成度
+ TraingModel::GetInstance()->CheckSportTag();
+ delete cyweeInfo; // 释放内存
+ }
+ //训练额外信息
+ if (request->msgId == TOPIC_EVENT_ACTIVITY_EXTRA_INFO_UPDATE) {
+ act_ext_result_t *cyweeInfo_ext = new act_ext_result_t();
+ int rets = memcpy_s(cyweeInfo_ext, sizeof(act_ext_result_t),
+ (act_ext_result_t *)request->data, request->len);
+ if (rets != EOK) {
+ delete cyweeInfo_ext; // 处理内存分配失败的情况
+ cyweeInfo_ext = NULL;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "memcpy_s act_ext_result_t fail ret = %d", rets);
+ return;
+ }
+ TraingModel::GetInstance()->AnalysisExtraData(cyweeInfo_ext);
+ delete cyweeInfo_ext; // 释放内存
+ }
+ //训练心率
+ if (request->msgId == TOPIC_EVENT_HEARTRATE_UPDATA) {
+ uint8_t heart = *((u_int8_t *)(request->data)); // 进行类型转换
+ TraingModel::GetInstance()->AnalysisHeartData(heart);
+ }
+ #endif
+ }
+ #ifdef __cplusplus
+ }
+ #endif
+
+ void store_sport_data() {
+ // printf("运动数据存储!\r\n");
+ OHOS::TraingModel::GetInstance()->RecordSportData();
+ return;
+ }
+
+ namespace OHOS {
+ #if TIMER
+ // 声明定时器变量
+ static osTimerId_t g_startCounting = NULL; // 定时器
+ static constexpr uint64_t MS_ONE_SECONDS = 1000;
+ static int g_motionTimes{0};
+ static constexpr int32 NUM_TEN = 10;
+ static constexpr int16 ERROR = 0;
+ static constexpr int16 SUCCESS = 1;
+ static constexpr int16 NUM_TIME_MIN = 60;
+ static constexpr int16 NUM_TIME_HOUR = 3600;
+ static constexpr int32 NUM_TIME_DAY = 86400;
+ #endif
+
+ // #define min(x, y) (((x) < (y)) ? (x) : (y))
+ // #define max(x, y) (((x) > (y)) ? (x) : (y))
+
+ SportData *sportdata = new SportData();
+ int shock_count{0}; //震动提醒
+
+ TraingModel ::TraingModel() {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TraingModel::TraingModel()");
+ // sportdata->Initialize(); // 初始化结构体
+ }
+ TraingModel ::~TraingModel() {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TraingModel::~TraingModel()");
+ // TraingUnsubscribeTopic(TOPIC_EVENT_ACTIVITY_UPDATE); // 取消订阅
+ TraingUnsubscribeTopic(TOPIC_EVENT_BLE_TRAINING);
+ // TraingUnsubscribeTopic(TOPIC_EVENT_HEARTRATE_UPDATA);
+ // TraingUnsubscribeTopic(TOPIC_EVENT_ACTIVITY_EXTRA_INFO_UPDATE);
+ }
+ TraingModel *TraingModel::GetInstance(void) {
+ static TraingModel instance;
+ return &instance;
+ }
+
+ void TraingModel ::Init() {
+ // TraingSubscribeTopic(TOPIC_EVENT_ACTIVITY_UPDATE); // 进行消息订阅
+ TraingSubscribeTopic(TOPIC_EVENT_BLE_TRAINING);
+ // TraingSubscribeTopic(TOPIC_EVENT_HEARTRATE_UPDATA);
+ // TraingSubscribeTopic(TOPIC_EVENT_ACTIVITY_EXTRA_INFO_UPDATE);
+ }
+ /*设置运动类型*/
+ void TraingModel::SetSportType(Exercisetype type) { sportType_ = type; }
+ /*获取训练Id->协议*/
+ int TraingModel::GetBleSportId(Exercisetype sporttype, UIResourceTextId id) {
+ int offset = 0;
+
+ switch (sporttype) {
+ case Exercisetype::FITNESS: // 0~57
+ offset = 0;
+ break;
+ case Exercisetype::DANCE: // 0~9
+ offset = 58;
+ break;
+ case Exercisetype::LEISURE: // 0~23
+ offset = 58 + 10;
+ break;
+ case Exercisetype::BALL: // 0~28
+ offset = 58 + 10 + 24;
+ break;
+ case Exercisetype::WATER: // 0~23
+ offset = 58 + 10 + 24 + 29;
+ break;
+ case Exercisetype::ICE: // 0~11
+ offset = 58 + 10 + 24 + 29 + 24;
+ break;
+ case Exercisetype::EXTREME: // 0~9
+ offset = 58 + 10 + 24 + 29 + 24 + 12;
+ break;
+ case Exercisetype::OTHER: // 0~8
+ offset = 58 + 10 + 24 + 29 + 24 + 12 + 10;
+ break;
+ default:
+ offset = 0;
+ break;
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "编译id offset %d id %d", offset, id);
+ return id - offset;
+ }
+
+ /*结束数据icon*/
+ std::string sportImages[8] = {
+ FITNESS_54_IMAGE, DANCE_54_IMAGE, REST_54_IMAGE, BALL_54_IMAGE,
+ SWIMMING_54_IMAGE, ICE_54_IMAGE, LIMIT_54_IMAGE, OTHER_54_IMAGE,
+ };
+ /*训练记录Icon*/
+ std::string sportIcons[8] = {
+ FITNESS_IMAGE, DANCE_IMAGE, REST_IMAGE, BALL_IMAGE,
+ SWIMMING_IMAGE, ICE_IMAGE, LIMIT_IMAGE, OTHER_IMAGE,
+ };
+ /*特殊运动icon*/
+ std::string sportIcons_special[6] = {RUNNING_60_IMAGE_END, WALKING_60_IMAGE_END,
+ RIDING_60_IMAGE_END, RUNNING_IMAGE,
+ WALKING_IMAGE, RIDING_IMAGE};
+
+ /*
+ type
+ 0 是运动结束图标
+ 1 是运动类型图标
+ */
+ std::string TraingModel::GetSportIcon(Exercisetype sporttype,
+ UIResourceTextId id, uint8_t type) {
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ " GetSportIcon sporttype %d id %d type %d", sporttype, id,
+ type);
+ bool specialsport = false;
+ uint8_t specialindex = 0;
+ switch (id) {
+ case UIResourceTextId::STR_EXERCISE_OUTDOORRUNNING:
+ case UIResourceTextId::STR_EXERCISE_INDOORRUNNING:
+ specialsport = true;
+ specialindex = 0;
+ break;
+ case UIResourceTextId::STR_EXERCISE_OUTDOORWALKING:
+ case UIResourceTextId::STR_EXERCISE_INDOORWALKING:
+ specialsport = true;
+ specialindex = 1;
+ break;
+ case UIResourceTextId::STR_EXERCISE_OUTDOORBIKING:
+ specialsport = true;
+ specialindex = 2;
+ break;
+ }
+ int index = static_cast<int>(sporttype);
+ if (specialsport) {
+ // 特殊运动
+ if (type == 0) {
+ return sportIcons_special[specialindex]; //返回特殊运动大图标
+ } else {
+ return sportIcons_special[specialindex + 3]; //返回特殊运动小图标
+ }
+ }
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, " GetResource index %d", index);
+ if (type == 0) {
+ return sportImages[index]; //返回运动类大图标
+ } else {
+ return sportIcons[index]; //返回运动类小图标
+ }
+ return "";
+ }
+ /*设置运动Id*/
+ void TraingModel::SetSportEvent(UIResourceTextId id) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, " SetSportType %d", sportId_);
+ //需要是结束开启的运动才重新配置
+ if (sportState_ == ExerciseState::General) {
+ IsSpecialSport(id); // 设置特殊性
+ sportId_ = id;
+ }
+ }
+ /*获取运动Id*/
+ UIResourceTextId TraingModel::GetSportEvent() {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, " GetSportEvent %d", sportId_);
+ return sportId_;
+ }
+ /*获取运动项目*/
+ ExerciseClass TraingModel::GetSportId() {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, " GetSportType %d", sportId_);
+ // 设置结构体里面的运动类型
+ sportdata->sportId = sportId_;
+ sportdata->sportType = sportType_;
+
+ switch (sportId_) {
+ // 室外跑步 室外走路
+ case UIResourceTextId::STR_EXERCISE_OUTDOORRUNNING:
+ case UIResourceTextId::STR_EXERCISE_OUTDOORWALKING:
+ // return ExerciseClass::OUTDOOR; 统一使用室内
+
+ // 室内跑步 室内走路
+ case UIResourceTextId::STR_EXERCISE_INDOORRUNNING:
+ case UIResourceTextId::STR_EXERCISE_INDOORWALKING:
+ return ExerciseClass::INDOOR;
+
+ // 跑步机
+ case UIResourceTextId::STR_EXERCISE_TREADMILL:
+ case UIResourceTextId::STR_EXERCISE_STROLLMACHINE:
+ return ExerciseClass::TREADMILL;
+
+ // 自由训练
+ default:
+ return ExerciseClass::FREE_TRAINING;
+ }
+ }
+ /*判断是否为需要Gps的运动*/
+ bool TraingModel::IsNeedGPS(UIResourceTextId sportId_) {
+ //跑步 走路 骑行需要GPS
+ switch (sportId_) {
+ case UIResourceTextId::STR_EXERCISE_OUTDOORRUNNING:
+ case UIResourceTextId::STR_EXERCISE_OUTDOORWALKING:
+ case UIResourceTextId::STR_EXERCISE_INDOORRUNNING:
+ case UIResourceTextId::STR_EXERCISE_INDOORWALKING:
+ case UIResourceTextId::STR_EXERCISE_TREADMILL:
+ case UIResourceTextId::STR_EXERCISE_STROLLMACHINE:
+ return true;
+ default:
+ return false;
+ }
+ }
+ /*判断是否为特殊运动*/
+ void TraingModel::IsSpecialSport(UIResourceTextId sportId_) {
+ switch (sportId_) {
+ case UIResourceTextId::STR_EXERCISE_OUTDOORRUNNING: // 室外跑步 室外走路
+ case UIResourceTextId::STR_EXERCISE_OUTDOORWALKING:
+ case UIResourceTextId::STR_EXERCISE_INDOORRUNNING: // 室内跑步 室内走路
+ case UIResourceTextId::STR_EXERCISE_INDOORWALKING:
+ case UIResourceTextId::STR_EXERCISE_TREADMILL: // 跑步机
+ case UIResourceTextId::STR_EXERCISE_STROLLMACHINE:
+ isspecialsport_ = true;
+ break;
+ default: // 自由训练
+ isspecialsport_ = false;
+ break;
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, " 特殊训练 %d 训练项 %d",
+ isspecialsport_, sportId_);
+ }
+
+ bool TraingModel::GetIsSpecial() { return isspecialsport_; }
+ /*获取目标字符串*/
+ std::string TraingModel::GetTagStr() {
+ /*
+ 通用目标配置: 时长、卡路里
+ 不通用目标配置: 步数、距离
+
+ 如果配置项是特殊目标
+ -> 普通训练: 无训练目标
+ -> 特殊训练: 有训练目标
+ */
+
+ std::string tagStr;
+
+ switch (sportTagType_) {
+ case ExerciseTag::Duration:
+ tagStr = std::to_string(sportTag_) + " min"; // 时长目标
+ break;
+
+ case ExerciseTag::Calories:
+ tagStr = std::to_string(sportTag_) + " kcal"; // 卡路里目标
+ break;
+
+ case ExerciseTag::Steps:
+ case ExerciseTag::Distance:
+ if (isspecialsport_) // 检查是否为特殊训练
+ {
+ // 只在特殊训练模式下使用步数和距离目标
+ if (sportTagType_ == ExerciseTag::Steps) {
+ tagStr = std::to_string(sportTag_) + " steps"; // 步数目标
+ } else {
+ tagStr = std::to_string(sportTag_) + " km"; // 距离目标
+ }
+ } else {
+ tagStr = ""; // 普通训练下不使用目标
+ }
+ break;
+
+ default:
+ tagStr = ""; // 未知目标类型
+ break;
+ }
+
+ return tagStr;
+ }
+ /*设置运动状态*/
+ void TraingModel::SetSportState(ExerciseState state) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, " SetSportState %d", state);
+ if (state != ExerciseState::General) {
+ /*app发起的运动*/
+ if (Openingway == true) {
+ if (state == ExerciseState::Start) {
+ sportdata->duration = 0;
+ }
+ #ifdef SUPPORT_BLE_SERVICE
+ sendtraningstatedata(static_cast<uint8_t>(state), sportdata->duration);
+ #endif
+ }
+ }
+ sportState_ = state;
+ #ifdef SYSTEM_LOG_SERVICE
+ // 记录运动状态
+ if (state == ExerciseState::End) {
+ uapi_save_runtime_log(RUNTIME_LOG_SPORT_END);
+ } else if (state == ExerciseState::Continue) {
+ uapi_save_runtime_log(RUNTIME_LOG_SPORT_RESUME);
+ } else if (state == ExerciseState::Pause) {
+ uapi_save_runtime_log(RUNTIME_LOG_SPORT_PAUSE);
+ }
+ #endif
+ }
+ /*获取运动状态*/
+ ExerciseState TraingModel::GetSportState() { return sportState_; }
+ // 计算平均心率的函数
+ double CalculateAverageHeartRate(const std::vector<int> &heartRates) {
+ if (heartRates.size() <= 2) {
+ // 如果心率数据点少于等于2,无法去掉最高和最低值
+ return 0.0; // 或者返回心率的平均值
+ }
+
+ // 创建一个副本以避免修改原始数据
+ std::vector<int> sortedHeartRates = heartRates;
+
+ // 排序
+ std::sort(sortedHeartRates.begin(), sortedHeartRates.end());
+
+ // 去掉一个最高值和一个最低值
+ sortedHeartRates.erase(sortedHeartRates.begin()); // 去掉最低值
+ sortedHeartRates.pop_back(); // 去掉最高值
+
+ // 计算剩余心率的平均值
+ double sum = 0;
+ for (int rate : sortedHeartRates) {
+ sum += rate;
+ }
+
+ return sum / sortedHeartRates.size(); // 返回平均值
+ }
+ #ifdef SUPPORT_CYWEE_SERVICE
+ /*分析数据*/
+ void TraingModel::AnalysisData(act_result_t *cyweeInfo) {
+ if (cyweeInfo == NULL)
+ return;
+
+ // 不同的运动类型解析不同的数据
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "AnalysisData:[steps %d, distance %d,calories %d, pace %d, "
+ "step_frequency %d, step_length %d]",
+ cyweeInfo->steps, cyweeInfo->distance, cyweeInfo->calories,
+ cyweeInfo->pace, cyweeInfo->step_frequency,
+ cyweeInfo->step_length);
+ // 通用数据
+ sportdata->steps = cyweeInfo->steps;
+ sportdata->distance = cyweeInfo->distance;
+ sportdata->calories = cyweeInfo->calories;
+
+ // sportdata->cdc = cyweeInfo->step_frequency;
+ // sportdata->std = cyweeInfo->step_length;
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, " pace %d", sportdata->pc);
+
+ sportdata->pc = cyweeInfo->pace;
+
+ // 更新最慢配速
+ if (sportdata->spc < sportdata->pc && sportdata->pc != 0) {
+ sportdata->spc = sportdata->pc;
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "spc %d", sportdata->spc);
+
+ // 计算平均心率
+ if (sportdata->heart > 50) {
+ sportdata->avgheart = Averageheartrate(sportdata->heart);
+ }
+
+ // 心率提醒
+ if (sportdata->heart >= user_maxHeart && user_maxHeart != 0) {
+ uapi_set_screen_on_api(); // 亮屏检测
+ if (true != OHOS::ConfigModel::GetInstance()->GetDnotdisturb() &&
+ (!uapi_sleep_mode_flag_get())) {
+ custom_VibratorFunc_int(0);
+ }
+ if (TraingCardView::GetInstance()) {
+ GraphicService::GetInstance()->PostGraphicEvent(
+ std::bind(&TraingCardView::SetPopupViewStatus,
+ TraingCardView::GetInstance(), HeartPopup, true));
+ if (TraingCardView::GetInstance()->GetHeartMaxLabel()) {
+ GraphicService::GetInstance()->PostGraphicEvent(
+ std::bind(&TraingCardView::SetHeartMaxLabel,
+ TraingCardView::GetInstance(), user_maxHeart));
+ }
+ }
+ }
+ }
+ #endif
+
+ #if 0
+ int avg_step_freq;//平均步频
+ int avg_step_len;//平均步长
+ int avg_pace;//平均配速
+ int max_step_freq;//最大步频
+ int max_pace;//最大配速
+ int activity_zone;//活动程度区间 0低度 1中度 2高度
+ int ativity_zone_moderate_duration;//中度活动时间
+ int ativity_zone_high_duration;//高度活动时间
+ int MHR_zone;//心率区间
+ int distance_motion;//体感距离
+ #endif
+
+ /*分析activity extra info数据*/
+ #ifdef SUPPORT_CYWEE_SERVICE
+ void TraingModel::AnalysisExtraData(act_ext_result_t *cyweeInfo) {
+
+ //判断是否为特殊训练
+ if (isspecialsport_ == false) {
+ return;
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "[apc:%d fpc:%d cdc:%d std:%d]",
+ cyweeInfo->avg_pace, cyweeInfo->max_pace,
+ cyweeInfo->avg_step_freq, cyweeInfo->avg_step_len);
+ //平均配速
+ if (cyweeInfo->avg_pace != 0) {
+ sportdata->apc = cyweeInfo->avg_pace;
+ }
+ //最快配速
+ if (cyweeInfo->max_pace != 0) {
+ sportdata->fpc = cyweeInfo->max_pace;
+ }
+ //平均步频
+ if (cyweeInfo->avg_step_freq != 0) {
+ sportdata->cdc = cyweeInfo->avg_step_freq;
+ }
+ //平均步长
+ if (cyweeInfo->avg_step_len != 0) {
+ sportdata->std = cyweeInfo->avg_step_len * 100; // 前者cm 后者m
+ }
+ }
+ #endif
+ /*分析运动心率数据*/
+ void TraingModel::AnalysisHeartData(uint8_t hrs) {
+ sportdata->heart = hrs;
+ SetIntensity(sportdata->heart); // 设置训练强度
+ }
+
+ /*打印函数*/
+ void TraingModel::printf_data(SportData *data) {
+ /*打印测试一下数据*/
+ printf("\r\n****************************************\r\n");
+ printf("Sport type: %d\n", data->sportType);
+ printf("Sport ID: %d\n", data->sportId);
+ printf("Start date: %s\n", data->start_date.c_str());
+ printf("Start time: %s\n", data->start_time.c_str());
+ printf("End time: %s\n", data->end_time.c_str());
+ printf("Goal degree: %d\n", data->goaldegree);
+ printf("Duration: %d\n", data->duration);
+ printf("Distance: %d\n", data->distance);
+ printf("Average heart rate: %d\n", data->avgheart);
+ printf("Steps: %d\n", data->steps);
+ printf("Intensity: %d\n", data->intensity);
+ printf("\r\n****************************************\r\n");
+ }
+ /*获取数据*/
+ SportData *TraingModel::GetSportdata() {
+ // printf_data(sportdata);
+ return sportdata;
+ }
+ /* 设置训练数据[为训练记录准备] */
+ void TraingModel::SetSportdata(SportData *data) {
+ if (data != nullptr && sportdata != nullptr) {
+ sportdata->sportType = data->sportType;
+ sportdata->sportId = data->sportId;
+ sportdata->start_date = data->start_date;
+ sportdata->start_time = data->start_time;
+ sportdata->end_time = data->end_time;
+ sportdata->goaldegree = data->goaldegree;
+ sportdata->duration = data->duration;
+ sportdata->distance = data->distance;
+ sportdata->avgheart = data->avgheart;
+ sportdata->steps = data->steps;
+ sportdata->intensity = data->intensity;
+ sportdata->calories = data->calories;
+ sportdata->apc = data->apc;
+ sportdata->fpc = data->fpc;
+ sportdata->spc = data->spc;
+ sportdata->std = data->std;
+ sportdata->cdc = data->cdc;
+ sportdata->starttime = data->starttime; //更新开始时间
+ printf_data(data); //打印该运动数据
+ IsSpecialSport(sportdata->sportId); // 配置训练特殊性
+ }
+ }
+ /*获取训练卡路里*/
+ int TraingModel::GetCal() { return sportdata->calories; }
+ /*获取训练目标*/
+ int TraingModel::GetSportTag() { return sportdata->tag; }
+ /*获取训练项目类型*/
+ ExerciseTag TraingModel::GetSportTagtype() { return sportdata->sportTagType; }
+ /*重置训练配置*/
+ void TraingModel::ResetSharedTrainingData() {
+ sportdata->Reset(); // 重置结构体
+ heartRateData.clear(); // 清空心率数据
+
+ ClearPaceTable(); //重置配速数据
+ ClearHeartTable(); // 清空心率数据
+
+ recentHeartRates.clear();
+ delayed.clear(); // 重置 delayed 字典
+
+ while (!maxHeap.empty()) { // 清空最大堆
+ maxHeap.pop();
+ }
+ while (!minHeap.empty()) { // 清空最小堆
+ minHeap.pop();
+ }
+
+ totalHeartRate = 0;
+ countHeartRate = 0;
+ heartcount = 0;
+
+ Gettagstuats = false; // 重置训练达标标志位
+ Openstate = true; // 开启运动开始标志位
+
+ shock_count = 0; //重置震动计数
+
+ gpsData.clear(); // 清空gps数据
+ canvasData.clear(); // 清空画布数据
+ }
+
+ uint32_t TraingModel::get_current_time() {
+ uint32_t time;
+ if (uapi_get_timestampe_ss(&time) != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to get timestamp");
+ return 0;
+ }
+ return time;
+ }
+
+ /*设置运动开始与结束*/
+ void TraingModel::SetTimestamp() {
+ uint32_t time;
+ if (uapi_get_timestampe_ss(&time) != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to get timestamp");
+ return;
+ }
+
+ ExerciseState currentState = GetSportState(); // 获取当前运动状态
+
+ // 增加标志位互斥保障"开始"和"结束"的唯一性操作
+ if (currentState == ExerciseState::Start && !Startstate) {
+ // 设置训练 runtime 为 1 训练中
+ uapi_dev_runtime_set_train_status(1);
+ if (Openingway) {
+ // uapi_ble_connect_param_update(BLE_CONNECT_INTERVAL_HIGH);
+ }
+
+ // 更新用户数据
+ SetUserSportdata();
+
+ ResetSharedTrainingData(); // 重置共享训练数据
+
+ // printf("SetTimestamp::time:%u \r\n", time);
+ sportdata->starttime = time;
+
+ sportdata->sportType = sportType_; // 训练类型
+ sportdata->sportId = sportId_;
+
+ IsSpecialSport(sportdata->sportId); // 配置运动特殊性状态
+
+ if (sportTag_) // 有值才设置[训练目标]
+ {
+ sportdata->sportTagType = sportTagType_;
+ sportdata->tag = sportTag_;
+ }
+
+ current_goal = GetCurrentSportTag();
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "训练目标:类型: %d 值: %d 当前目标值:%d", sportTagType_,
+ sportTag_, current_goal);
+
+ // 设置起始日期和时间
+ SetStartDateTime();
+
+ Startstate = true;
+ Endstate = false; // 确保"开始"和"结束"状态互斥
+
+ //打印目前运动配置项
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "*******************************");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始运动");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportType:%d", sportdata->sportType);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportId:%d", sportdata->sportId);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportTagType:%d", sportdata->sportTagType);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportTag:%d", sportdata->tag);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "*******************************");
+
+
+ sport_start_data_timer(300); //开启数据获取定时器 刷新频率是300ms
+
+ #ifdef SYSTEM_LOG_SERVICE
+ //日志记录
+ if (Openingway) {
+ uapi_save_runtime_log(RUNTIME_LOG_APP_SPORT_LAUNCH); // app发起运动
+ } else {
+ uapi_save_runtime_log(RUNTIME_LOG_DIAL_SPORT_LAUNCH); //手表发起运动
+ }
+ if (isspecialsport_) {
+ uapi_save_runtime_log(RUNTIME_LOG_SPORT_SPECIAL_START); //特殊运动
+ } else {
+ uapi_save_runtime_log(RUNTIME_LOG_SPORT_NORMAL_START); //普通运动
+ }
+ #endif
+
+ } else if (currentState == ExerciseState::End && !Endstate) {
+ sport_stop_data_timer(); //停止数据获取定时器
+
+ // 设置训练 runtime 为 0 训练结束
+ uapi_dev_runtime_set_train_status(0);
+ if (Openingway) {
+ // uapi_ble_connect_param_update(BLE_CONNECT_INTERVAL_SLOW);
+ }
+
+ sportdata->endtime = time;
+
+ SetEndTime();
+
+ sportState_ = ExerciseState::General;
+ Openstate = false; // 关闭运动标志位
+
+ // 记录运动数据
+ // RecordSportData();
+
+ //配置最后的训练强度(平均心率为标准)
+ SetIntensity(sportdata->avgheart);
+
+ //更新gps点位
+ GetPointData();
+
+ do_custom_fileoperate_service_check_msg(
+ CUSTOM_FILEOPERATE_MSG_TYPE_SPORTDATA_SAVE, nullptr, 0);
+
+ Startstate = false;
+ Endstate = true;
+
+ stop_heart_gps(); // 停止心率和gps数据采集
+ gnss_stop_timer(); // 停止gps定时器
+
+ #if 0
+ // 重置训练目标 [发生在训练前]
+ sportTag_ = 0;
+ sportTagType_ = ExerciseTag::UNKNOW;
+ #endif
+ /*运动结束并且是手表发起的运动*/
+ if (Openingway == false) {
+ sendtraningstateend();
+ }
+ /*运动结束并且是app发起的运动*/
+ else {
+ sendtraningstatedata(0, sportdata->duration);
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "结束运动");
+ }
+ }
+ /*设置开始时间*/
+ void TraingModel::SetStartDateTime() {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->[%d]\r\n",
+ sportdata->duration);
+ std::string startDate, startTime;
+ if (!GetFormattedDate(startDate, "Failed to get start date"))
+ return;
+ if (!GetFormattedTime(startTime, "Failed to get start time"))
+ return;
+
+ sportdata->start_date = startDate;
+ sportdata->start_time = startTime;
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->start_time [%s]\r\n",
+ sportdata->start_time.c_str());
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->[%d]\r\n",
+ sportdata->duration);
+ }
+ /*设置结束时间*/
+ void TraingModel::SetEndTime() {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->[%d]\r\n",
+ sportdata->duration);
+ std::string endTime;
+ if (GetFormattedTime(endTime, "Failed to get end time")) {
+ sportdata->end_time = endTime;
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->end_time [%s]\r\n",
+ sportdata->end_time.c_str());
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->[%d]\r\n",
+ sportdata->duration);
+ }
+ /*获取日期字符串*/
+ bool TraingModel::GetFormattedDate(std::string &dateOutput,
+ const char *errorMsg) {
+ char *date = (char *)malloc(9 * sizeof(char)); // 分配内存给start_date
+ if (uapi_get_YYMMDD_Time(date, '/') != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%s", errorMsg);
+ free(date);
+ return false;
+ }
+ dateOutput = std::string(date);
+ free(date); // 释放内存
+ return true;
+ }
+ /*获取时间字符串*/
+ bool TraingModel::GetFormattedTime(std::string &timeOutput,
+ const char *errorMsg) {
+ char *time = (char *)malloc(9 * sizeof(char)); // 分配内存给start_time
+ if (uapi_get_HHMMSS_Time(time, ':', true) != ERRCODE_SUCC) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%s", errorMsg);
+ free(time);
+ return false;
+ }
+ timeOutput = std::string(time);
+ free(time); // 释放内存
+ return true;
+ }
+ /*保存数据*/
+ void TraingModel::RecordSportData() {
+ #if READTEST
+ if (SaveSportData() == false) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveSportData error!");
+ }
+ #else
+ if (MomentumFlag) {
+ // GetGoaldegree(); // 设置目标完成度
+ if (SaveSportData()) {
+ TraingRecordModel::GetInstance().SetJsonStatus(true);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveSportData ok!");
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveSportData error!");
+ }
+ }
+ #endif
+ }
+ // 获取训练时长时间
+ std::string TraingModel::GetTrainingTime() {
+ int hour = sportdata->duration / NUM_TIME_HOUR;
+ int min = (sportdata->duration % NUM_TIME_HOUR) / NUM_TIME_MIN;
+ int sec = (sportdata->duration % NUM_TIME_HOUR) % NUM_TIME_MIN;
+
+ std::ostringstream oss;
+ oss << std::setfill('0') << std::setw(2) << hour << ":" << std::setw(2) << min
+ << ":" << std::setw(2) << sec;
+
+ std::string triningtime = oss.str();
+ return triningtime;
+ }
+ /*解析时间戳 返回例如:13:00:22*/
+ std::string TraingModel::AnalysisTimestamp(uint64_t timestamp) {
+ // 将时间戳转换为小时、分钟、秒
+ int hours = timestamp / 3600000; // 1小时 = 3600000毫秒
+ int minutes = (timestamp % 3600000) / 60000; // 1分钟 = 60000毫秒
+ int seconds = (timestamp % 60000) / 1000; // 1秒 = 1000毫秒
+
+ // 将小时、分钟、秒转换为字符串
+ std::string hoursStr = std::to_string(hours);
+ std::string minutesStr = std::to_string(minutes);
+ std::string secondsStr = std::to_string(seconds);
+
+ // 格式化输出为 HH:MM:SS
+ std::stringstream ss;
+ ss << std::setfill('0') << std::setw(2) << hoursStr << ":" << std::setw(2)
+ << minutesStr << ":" << std::setw(2) << secondsStr;
+
+ return ss.str();
+ }
+ int TraingModel::CalculateAverageHeartRate(int newHeartRate) {
+ // 将新的心率添加到数据列表
+ heartRateData.push_back(newHeartRate);
+ totalHeartRate += newHeartRate;
+ countHeartRate++;
+
+ // 将新的心率插入到堆中
+ maxHeap.push(newHeartRate);
+ minHeap.push(newHeartRate);
+
+ // 如果超过窗口大小,移除最早的心率
+ if (heartRateData.size() > windowSize) {
+ int oldestRate = heartRateData[0];
+ totalHeartRate -= oldestRate;
+ countHeartRate--;
+
+ // 将老旧值从列表中移除
+ heartRateData.erase(heartRateData.begin());
+
+ // 标记老旧值为待删除
+ delayed[oldestRate]++;
+ }
+
+ // 清理堆中的老旧值
+ while (!maxHeap.empty() && delayed[maxHeap.top()] > 0) {
+ delayed[maxHeap.top()]--;
+ maxHeap.pop();
+ }
+
+ while (!minHeap.empty() && delayed[minHeap.top()] > 0) {
+ delayed[minHeap.top()]--;
+ minHeap.pop();
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "新心率: %d, 当前心率总和: %d, 心率计数: %d", newHeartRate,
+ totalHeartRate, countHeartRate);
+
+ // 如果心率数量少于 3,直接返回当前的平均值
+ if (countHeartRate < 3) {
+ return totalHeartRate / countHeartRate; // 无法去除最大最小值
+ }
+
+ // 取出当前未被删除的最大和最小的心率
+ int maxRate = maxHeap.top();
+ int minRate = minHeap.top();
+
+ // 进行去极值后的平均计算
+ int average = (totalHeartRate - maxRate - minRate) / (countHeartRate - 2);
+
+ // 打印计算结果
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "去极值后的平均心率: %d (去掉最大值: %d, 去掉最小值: %d)",
+ average, maxRate, minRate);
+
+ return average;
+ }
+ // 显示心率
+ int TraingModel::CalculateFilteredHeartRate(int newHeartRate) {
+ // 将新的心率添加到最近心率列表
+ recentHeartRates.push_back(newHeartRate);
+
+ // 如果超过窗口大小,移除最早的心率
+ if (recentHeartRates.size() > windowSize) {
+ recentHeartRates.erase(recentHeartRates.begin());
+ }
+
+ // 计算当前窗口中的平均心率
+ int total =
+ std::accumulate(recentHeartRates.begin(), recentHeartRates.end(), 0);
+
+ // 打印当前窗口的平均心率
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "过滤后当前窗口的平均心率: %d",
+ (total / recentHeartRates.size()));
+
+ return total / recentHeartRates.size(); // 返回计算后的平均值
+ }
+ int TraingModel::Averageheartrate(int newHeartRate) {
+ heartcount++;
+ totalHeartRate += newHeartRate;
+ return totalHeartRate / heartcount; // 返回计算的平均值
+ }
+ // 设置训练目标类型以及训练目标
+ void TraingModel::SetSportTag(ExerciseTag tagtpye, int tag) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "设置目标 tagtpye[%d] tag[%d]",
+ tagtpye, tag);
+ sportTagType_ = tagtpye;
+ sportTag_ = tag;
+ }
+
+ /* 设置训练强度 */
+ void TraingModel::SetIntensity(int heart) {
+ // sportdata->intensity = active_state;
+ /*
+ 训练强度
+ 220-年龄=最高心率
+
+ 分为四个强度区间
+ 较低强度=最高心率*59%以下
+ 中等强度=最高心率*60%~69%
+ 高等强度=最高心率*70%~79%
+ 超高强度=最高心率*80%以上
+ */
+ // 确保最大心率为正值
+ if (user_Intensity <= 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "maxHeartRate %d is error",
+ user_Intensity);
+ return;
+ }
+
+ // 将 heart 转换为 double 以避免整数除法
+ double heartRateAsDouble = static_cast<double>(heart);
+ double intensityPercentage =
+ (heartRateAsDouble / (double)user_Intensity) * 100;
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "heart: %d, maxHeartRate: %d, intensityPercentage: %.2f", heart,
+ HeartModel, intensityPercentage);
+
+ if (intensityPercentage < LOW_INTENSITY_THRESHOLD) {
+ // 较低强度
+ sportdata->intensity = LOW_INTENSITY;
+ } else if (intensityPercentage >= MEDIUM_INTENSITY_LOWER_THRESHOLD &&
+ intensityPercentage <= MEDIUM_INTENSITY_UPPER_THRESHOLD) {
+ // 中等强度
+ sportdata->intensity = MEDIUM_INTENSITY;
+ } else if (intensityPercentage >= HIGH_INTENSITY_LOWER_THRESHOLD &&
+ intensityPercentage <= HIGH_INTENSITY_UPPER_THRESHOLD) {
+ // 高等强度
+ sportdata->intensity = HIGH_INTENSITY;
+ } else {
+ // 超高强度
+ sportdata->intensity = VERY_HIGH_INTENSITY;
+ }
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "maxHeartRate: %d, heartRate: %d, intensityPercentage: %.2f",
+ maxHeartRate, heart, intensityPercentage);
+ }
+ /*获取训练强度STR*/
+ UIResourceTextId TraingModel::GetIntensity() {
+ switch (sportdata->intensity) {
+ case LOW_INTENSITY:
+ return UIResourceTextId::STR_LOWER_STRENGTH;
+ break;
+ case MEDIUM_INTENSITY:
+ return UIResourceTextId::STR_MEDIUM_STRENGTH;
+ break;
+ case HIGH_INTENSITY:
+ return UIResourceTextId::STR_HIGH_STRENGTH;
+ break;
+ case VERY_HIGH_INTENSITY:
+ return UIResourceTextId::STR_ULTRA_HIGH_STRENGTH;
+ break;
+ default:
+ return UIResourceTextId::STR_LOWER_STRENGTH;
+ break;
+ }
+ }
+
+ /*
+ 时长:10~600分钟 实际出值 秒
+ 距离:1~20km 实际出值 m
+ 步数:500~50000步 实际出值 步
+ 卡路里:100~1000kcal 实际出值 kcal
+ */
+
+ /* 获取当前的目标值 */
+ int TraingModel::GetSportTagData() {
+ if (sportdata->tag == 0 || current_goal == 0) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
+ return 0;
+ }
+
+ float goaldegree = 0.0f; // 初始化目标完成度
+
+ // 根据设置的训练目标类型去对应相对的数据
+ switch (sportdata->sportTagType) {
+ case ExerciseTag::Duration: {
+ // 计算完成度
+ goaldegree = (static_cast<float>(sportdata->duration) / current_goal) * 100;
+ break;
+ }
+ case ExerciseTag::Calories: {
+ // 计算目标完成度
+ goaldegree = (static_cast<float>(sportdata->calories) / current_goal) * 100;
+ break;
+ }
+ case ExerciseTag::Distance: {
+ // 计算目标完成度
+ goaldegree = (static_cast<float>(sportdata->distance) / current_goal) * 100;
+ break;
+ }
+ case ExerciseTag::Steps: {
+ // 计算目标完成度
+ goaldegree = (static_cast<float>(sportdata->steps) / current_goal) * 100;
+ break;
+ }
+ default:
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->sportTagType is error");
+ return 0;
+ break;
+ }
+
+ WEARABLE_LOGD(
+ WEARABLE_LOG_MODULE_APP,
+ " [goaldegree %d, sportdata->tag %d, sportdata->duration %d, "
+ "sportdata->calories %d,sportdata->distance %d, sportdata->steps %d]",
+ goaldegree, sportdata->tag, sportdata->duration, sportdata->calories,
+ sportdata->distance, sportdata->steps);
+
+ return static_cast<int>(round(goaldegree)); // 返回目标完成度
+ }
+
+ //获取目标值
+ uint32_t TraingModel::GetCurrentSportTag() {
+ if (sportdata->tag == 0) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
+ return 0;
+ }
+
+ float goaldegree = 0.0f; // 初始化目标完成度
+
+ // 根据设置的训练目标类型去对应相对的数据
+ switch (sportdata->sportTagType) {
+ case ExerciseTag::Duration: {
+ // 转换目标时长为秒
+ return static_cast<float>(sportdata->tag * 60);
+ break;
+ }
+ case ExerciseTag::Calories: {
+ // 计算目标完成度
+ return sportdata->tag;
+ break;
+ }
+ case ExerciseTag::Distance: {
+ return static_cast<float>(sportdata->tag * 1000);
+ break;
+ }
+ case ExerciseTag::Steps: {
+ return sportdata->tag;
+ break;
+ }
+ default:
+ return 0;
+ break;
+ }
+ }
+ //获取是否达到目标 使用比较
+ bool TraingModel::IsGetGoaldegree() {
+ if (sportdata->tag == 0) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
+ return false;
+ }
+
+ float goaldegree = 0.0f; // 初始化目标完成度
+
+ // 根据设置的训练目标类型去对应相对的数据
+ switch (sportdata->sportTagType) {
+ case ExerciseTag::Calories: {
+ // 计算目标完成度
+ if (sportdata->calories >= current_goal) {
+ return true;
+ }
+ return false;
+ break;
+ }
+ case ExerciseTag::Distance: {
+ if (sportdata->distance >= current_goal) {
+ return true;
+ }
+ return false;
+ break;
+ }
+ case ExerciseTag::Steps: {
+ if (sportdata->steps >= current_goal) {
+ return true;
+ }
+ return false;
+ break;
+ }
+ default:
+ return false;
+ break;
+ }
+ }
+
+ /* 获取训练目标完成百分比 */
+ int TraingModel::GetGoaldegree() {
+ if (sportdata->tag == 0) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
+ return 0; // 如果目标值为 0,返回 0%
+ }
+
+ int goaldegree = GetSportTagData();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "goaldegree %d", goaldegree);
+ return goaldegree; // 直接返回目标完成度
+ }
+
+ void TraingModel::updata_goal() {
+ if (sportdata->tag == 0)
+ return;
+
+ sportdata->goaldegree = GetGoaldegree();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->goaldegree %d",
+ sportdata->goaldegree);
+ }
+
+ /* 训练目标校验 */
+ void TraingModel::CheckSportTag() {
+ // 运动开始才计算
+ if (sportdata->tag && sportdata->sportTagType != ExerciseTag::Duration) {
+ if (IsGetGoaldegree() && !Gettagstuats) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "目标完成");
+ uapi_set_screen_on_api(); // 亮屏检测
+ if (!OHOS::ConfigModel::GetInstance()->GetDnotdisturb()) {
+ custom_VibratorFunc_int(0); // 震动提示
+ }
+ if (TraingCardView::GetInstance()) {
+ GraphicService::GetInstance()->PostGraphicEvent(
+ std::bind(&TraingCardView::SetPopupViewStatus,
+ TraingCardView::GetInstance(), GetTagPopup, true));
+ auto animator = TraingCardView::GetInstance()->GetAnimator();
+ if (animator) {
+ animator->Start(); // 启动动画
+ }
+ }
+ Gettagstuats = true; // 更新状态
+ }
+ }
+ }
+ // 校验时间获取目标
+ void TraingModel::CheckTimeTag() {
+ if (sportdata->tag == 0 || sportdata->sportTagType != ExerciseTag::Duration) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
+ return;
+ }
+ if (sportdata->duration == current_goal && !Gettagstuats) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "目标完成");
+ uapi_set_screen_on_api(); // 亮屏检测
+ if (!OHOS::ConfigModel::GetInstance()->GetDnotdisturb()) {
+ custom_VibratorFunc_int(0); // 震动提示
+ }
+ if (TraingCardView::GetInstance()) {
+ GraphicService::GetInstance()->PostGraphicEvent(
+ std::bind(&TraingCardView::SetPopupViewStatus,
+ TraingCardView::GetInstance(), GetTagPopup, true));
+ auto animator = TraingCardView::GetInstance()->GetAnimator();
+ if (animator) {
+ animator->Start(); // 启动动画
+ }
+ }
+ Gettagstuats = true; // 更新状态
+ }
+ }
+
+ //获取运动时长
+ int TraingModel::GetDuration() { return sportdata->duration; }
+ /*获取训练类型Id*/
+ UIResourceTextId TraingModel::GetSportTypeId(Exercisetype type) {
+ // 根据设置的训练类型去对应相对的数据
+ switch (type) {
+ // 健身类
+ case Exercisetype::FITNESS:
+ return STR_EXERCISE_LIST;
+ // 舞蹈类
+ case Exercisetype::DANCE:
+ return STR_DANCING_LIST;
+ // 休闲运动
+ case Exercisetype::LEISURE:
+ return STR_RECREATIONAL_LIST;
+ // 球类
+ case Exercisetype::BALL:
+ return STR_BALL_LIST;
+ // 水上运动
+ case Exercisetype::WATER:
+ return STR_WATER_LIST;
+ // 冰上运动
+ case Exercisetype::ICE:
+ return STR_ICE_LIST;
+ // 极限运动
+ case Exercisetype::EXTREME:
+ return STR_EXTREME_LIST;
+ // 其他
+ case Exercisetype::OTHER:
+ return STR_OTHER_LIST;
+ default:
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Not ExerciseClass type!");
+ break;
+ }
+
+ return STR_EXERCISE_LIST; // 添加默认返回值[健身类]
+ }
+ /*打印存储数据*/
+ void TraingModel::printTrainingData(const Trainingdata &data) {
+ printf("Trainingdata:\n");
+ printf("src: %d\n", data.src);
+ printf("bt: %u\n", data.bt);
+ printf("ty: %u\n", data.ty);
+ printf("st: %u\n", data.st);
+ printf("sp: %u\n", data.sp);
+ printf("tgt: %u\n", data.tgt);
+ printf("tg: %u\n", data.tg);
+ printf("pg: %u\n", data.pg);
+ printf("du: %u\n", data.du);
+ printf("ca: %u\n", data.ca);
+ printf("di: %u\n", data.di);
+ printf("hr: %u\n", data.hr);
+ printf("stp: %u\n", data.stp);
+ printf("ti: %u\n", data.ti);
+ printf("cir: %u\n", data.cir);
+ printf("alt: %u\n", data.alt);
+ }
+
+ /*
+ uint32_t src; // 训练来源:0 - 手表开启的训练,1 - APP开启的训练
+ uint32_t bt; // 运动类型:0 - 健身类,1 - 舞蹈类,2 - 休闲运动,3 - 球类,4 - 水上项目,5 - 冰上项目,6 - 极限运动,7 - 其他
+ uint32_t ty; //训练类型:自由训练
+ uint32_t st; // 开始时间戳(单位:秒)
+ uint32_t sp; // 结束时间戳(单位:秒)
+ uint32_t tgt; // 训练目标类型:0 - 距离,1 - 卡路里,2- 时间,3 - 步数
+ uint32_t tg; //训练目标值:路程(单位:米),卡路里(单位:kcal),时间(单位:分钟),步数(单位:步)
+ uint32_t pg; // 目标完成度(单位:%)
+ uint32_t du; // 训练总时长(单位:秒)
+ uint32_t ca; // 消耗卡路里(单位:kcal)
+ uint32_t di; // 完成的距离(单位:米)
+ uint32_t hr; // 平均心率
+ uint32_t stp; // 步数
+ uint32_t ti; // 训练强度:0 - 较低强度,1 - 中等强度,2 - 高等强度,3 - 超高强度
+ uint32_t cir; // 圈数
+ uint32_t alt; // 最高海拔高度
+ */
+ /*保存数据*/
+ bool TraingModel::SaveSportData() {
+
+ // 配置训练存储状态
+ uapi_dev_runtime_set_train_storage_status(1);
+
+ Trainingdata data;
+ data.src = Openingway;
+ data.bt = static_cast<uint32_t>(sportdata->sportType);
+ data.ty = static_cast<uint32_t>(GetBleSportId(sportdata->sportType, sportdata->sportId));
+ data.st = static_cast<uint32_t>(sportdata->starttime);
+ data.sp = static_cast<uint32_t>(sportdata->endtime);
+ data.tgt = static_cast<uint32_t>(sportdata->sportTagType);
+ if (sportdata->sportTagType == ExerciseTag::Distance) {
+ data.tg = static_cast<uint32_t>(sportdata->tag * 1000);
+ } else {
+ data.tg = static_cast<uint32_t>(sportdata->tag);
+ }
+ data.pg = static_cast<uint32_t>(sportdata->goaldegree);
+ data.du = static_cast<uint32_t>(sportdata->duration);
+ data.ca = static_cast<uint32_t>(sportdata->calories * 10);
+ data.di = static_cast<uint32_t>(sportdata->distance);
+ data.hr = static_cast<uint32_t>(sportdata->avgheart);
+ data.stp = static_cast<uint32_t>(sportdata->steps);
+ data.ti = static_cast<uint32_t>(sportdata->intensity);
+ /* 230 目前不支持【圈数】【最高海拔高度】 */
+ data.cir = 0;
+ data.alt = 0;
+ /* 配速相关 */
+ data.apc = static_cast<uint32_t>(sportdata->apc);
+ data.fpc = static_cast<uint32_t>(sportdata->fpc);
+ data.spc = static_cast<uint32_t>(sportdata->spc);
+ data.std = static_cast<uint32_t>(sportdata->std);
+ data.cdc = static_cast<uint32_t>(sportdata->cdc);
+
+ // 创建sport文件夹
+ CreateDirectoryIfNotExists(TRAINING_DATA_DIRECTORY);
+
+ // 保存数据到 bin
+ if (write_Trainingdata_data(data) != 1) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "write_Trainingdata_data is error!");
+ uapi_dev_runtime_set_train_storage_status(0); // 恢复状态
+ return false;
+ }
+
+ // 保存数据到 json
+ if (!SaveDataToJson(sportdata)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveDataToJson is error!");
+ uapi_dev_runtime_set_train_storage_status(0); // 恢复状态
+ return false;
+ }
+
+ if (isspecialsport_) {
+ // 保存心率表
+ if (!SaveHeartTable()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveHeartRateData is error!");
+ // uapi_dev_runtime_set_train_storage_status(0); // 恢复状态
+ // return false;
+ }
+ // 保存速度表
+ if (!SavePaceTable()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveSpeedData is error!");
+ // return false;
+ }
+ // 保存GPS数据
+ if (!SaveCanvasData()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveCanvasData is error!");
+ // return false;
+ }
+ if (!SaveGPSData()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData is error!");
+ // return false;
+ }
+ }
+
+ // 如果所有操作成功,则恢复训练存储状态
+ uapi_dev_runtime_set_train_storage_status(0);
+ return true;
+ }
+ /*设置进入方式*/
+ void TraingModel::SetEntryType(ENTEYTYPE type) { entey_type = type; }
+ /*获取进入方式*/
+ ENTEYTYPE TraingModel::GetEntryType() { return entey_type; }
+ /*设置开启方式*/
+ void TraingModel::SetSportOpenWay(bool way) { Openingway = way; }
+ /*设置运动是否开始*/
+ void TraingModel::SetSportOpenState(bool state) { Openstate = state; }
+ /*获取运动是否开始*/
+ bool TraingModel::GetSportOpenState() { return Openstate; }
+ /*获取运动时长*/
+ uint32_t TraingModel::GetSportTime() { return sportdata->duration; }
+ /*获取训练量是否达标*/
+ bool TraingModel::GetMomentum() {
+ #if 0
+ int caldata = TraingModel::GetInstance()->GetCal();
+ /*判断是否产生至少1Kcald的运动量*/
+ if (caldata >= 1)
+ {
+ MomentumFlag = true;
+ }
+ else
+ {
+ MomentumFlag = false;
+ }
+ #endif
+ int duration = sportdata->duration;
+ //判断运动时间是否大于1分钟
+ if (duration >= 60) {
+ MomentumFlag = true;
+ } else {
+ MomentumFlag = false;
+ }
+ return MomentumFlag;
+ }
+
+ //用户运动数据配置
+ void TraingModel::SetUserSportdata() {
+ // 获取用户的最高心率提醒
+ user_maxHeart = ConfigModel::GetInstance()->get_configData_int(
+ ConfigModel::CONFIGMODEL_ITEM_TRAINING_HEART_MAX);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "用户最高心率提醒: %d", user_maxHeart);
+
+ // 获取用户年龄
+ user_Age = ConfigModel::GetInstance()->GetUserAge();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "用户年龄: %d", user_Age);
+
+ // 获取自动训练设置
+ user_autoget = ConfigModel::GetInstance()->get_configData_int(
+ ConfigModel::CONFIGMODEL_ITEM_TRANING_AUTO);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "自动获取设置: %d", user_autoget);
+
+ // 获取训练频率设置
+ user_freq = ConfigModel::GetInstance()->get_configData_int(
+ ConfigModel::CONFIGMODEL_ITEM_TRANING_FREQ);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "训练频率: %d", user_freq);
+
+ user_Intensity = MAX_HEART_RATE_TRAINING - user_Age;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "用户运动强度心率: %d",
+ user_Intensity);
+ }
+ //运动过程中更新心率配置
+ void TraingModel::SetMaxHeart(int maxheart) {
+ // 获取用户运动数据配置
+ user_maxHeart = maxheart;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "用户最高心率: %d", user_maxHeart);
+ }
+ /*配速表与心率表相关*/
+ //清空心率表
+ void TraingModel::ClearHeartTable() {
+ heartRateTable.clear(); // 清空心率数据表
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Heart Rate Table Cleared.\r\n");
+ }
+ //清空配速表
+ void TraingModel::ClearPaceTable() {
+ paceTable.clear(); // 清空配速数据表
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Pace Table Cleared.\r\n");
+ }
+ // 添加心率数据
+ bool TraingModel::AddHeartRateData(uint32_t timestamp, uint8_t heartRate) {
+ // 检查重复时间戳
+ for (const auto &entry : heartRateTable) {
+ if (entry.timestamp == timestamp) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "时间戳重复无法添加数据 AddHeartRateData is error!");
+ return false; // 返回重复
+ }
+ }
+
+ if (heartRateTable.size() >= MAX_ENTRIES) {
+ WEARABLE_LOGE(
+ WEARABLE_LOG_MODULE_APP,
+ "达到最大心率数据条目限制 无法添加新的数据 AddHeartRateData is error!");
+ return false; // 返回条目满了
+ }
+
+ HeartRateEntry newEntry = {timestamp, heartRate};
+ heartRateTable.push_back(newEntry);
+ return true; // 返回成功
+ }
+
+ // 添加配速数据
+ bool TraingModel::AddPaceData(uint32_t timestamp, uint16_t pace) {
+ // 检查重复时间戳
+ for (const auto &entry : paceTable) {
+ if (entry.timestamp == timestamp) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "时间戳重复无法添加数据 AddPaceData is error!"); // 修正这里
+ return false; // 返回重复
+ }
+ }
+
+ if (paceTable.size() >= MAX_ENTRIES) {
+ WEARABLE_LOGE(
+ WEARABLE_LOG_MODULE_APP,
+ "达到配速数据条目限制 无法添加新的数据 AddPaceData is error!");
+ return false; // 返回条目满了
+ }
+
+ PaceEntry newEntry = {timestamp, pace};
+ paceTable.push_back(newEntry);
+ return true; // 返回成功
+ }
+ //心率表打印
+ void TraingModel::PrintHeartRateTable() {
+ size_t heartRateCount = heartRateTable.size();
+ if (heartRateCount == 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "心率表为空,无法打印。");
+ return;
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Heart Rate Table:");
+ for (const auto &entry : heartRateTable) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "时间戳: %u, 心率: %d",
+ entry.timestamp, entry.heartRate);
+ }
+ }
+ //配速表打印
+ void TraingModel::PrintPaceTable() {
+ size_t paceCount = paceTable.size();
+ if (paceCount == 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "配速表为空,无法打印。");
+ return;
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Pace Table:");
+ for (const auto &entry : paceTable) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "时间戳: %u, 配速: %d",
+ entry.timestamp, entry.pace);
+ }
+ }
+ // 保存心率表
+ bool TraingModel::SaveHeartTable() {
+
+ //打印数组
+ PrintHeartRateTable();
+
+ CreateDirectoryIfNotExists(TRAINING_HEART_FILE);
+
+ size_t heartRateCount = heartRateTable.size();
+ if (heartRateCount == 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "心率表为空,无法保存。");
+ return false;
+ }
+
+ std::string filePath =
+ TRAINING_HEART_FILE + std::to_string(sportdata->starttime) + ".bin";
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveHeartTable filePath: %s",
+ filePath.c_str());
+ std::ofstream heartRateFile(filePath, std::ios::binary);
+ if (!heartRateFile) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
+ filePath.c_str());
+ return false; // 返回失败
+ }
+
+ // 存储表数据
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "heartRateCount = %d", heartRateCount);
+ if (!heartRateFile.write(
+ reinterpret_cast<const char *>(heartRateTable.data()),
+ heartRateCount * sizeof(HeartRateEntry))) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "写入心率数据失败。");
+ return false; // 返回失败
+ }
+
+ heartRateFile.close();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Heart Rate Table Saved.");
+ return true; // 返回成功
+ }
+
+ // 保存配速表
+ bool TraingModel::SavePaceTable() {
+
+ //打印数组
+ PrintPaceTable();
+
+ CreateDirectoryIfNotExists(TRAINING_PACE_FILE);
+
+ size_t paceCount = paceTable.size();
+ if (paceCount == 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "配速表为空,无法保存。");
+ return false;
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "paceCount = %d", paceCount);
+
+ std::string filePath =
+ TRAINING_PACE_FILE + std::to_string(sportdata->starttime) + ".bin";
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SavePaceTable filePath: %s",
+ filePath.c_str());
+
+ std::ofstream paceFile(filePath, std::ios::binary);
+ if (!paceFile) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
+ filePath.c_str());
+ return false; // 返回失败
+ }
+ // 写入配速数据
+ if (!paceFile.write(reinterpret_cast<const char *>(paceTable.data()),
+ paceCount * sizeof(PaceEntry))) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "写入配速数据失败。");
+ return false; // 返回失败
+ }
+
+ paceFile.close();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SavePaceTable Success.");
+ return true; // 返回成功
+ }
+ // 返回心率表路径
+ std::string TraingModel::GetHeartTablePath(uint32_t timestamp) {
+ // 首先检查完全匹配的时间戳
+ std::string exactFilePath =
+ TRAINING_HEART_FILE + std::to_string(timestamp) + ".bin";
+ if (IsFileAccessibleAndNotEmpty(exactFilePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到心率表: %s",
+ exactFilePath.c_str());
+ return exactFilePath; // 返回完全匹配的文件路径
+ }
+
+ // 如果未找到,检查时间戳相差1到2秒的文件
+ for (int i = -2; i <= 2; ++i) {
+ uint32_t checkTimestamp = timestamp + i;
+ std::string filePath =
+ TRAINING_HEART_FILE + std::to_string(checkTimestamp) + ".bin";
+ if (IsFileAccessibleAndNotEmpty(filePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到心率表: %s",
+ filePath.c_str());
+ return filePath; // 返回找到的文件路径
+ }
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的心率表。");
+ return ""; // 未找到文件
+ }
+
+ // 返回配速表路径
+ std::string TraingModel::GetPaceTablePath(uint32_t timestamp) {
+ // 首先检查完全匹配的时间戳
+ std::string exactFilePath =
+ TRAINING_PACE_FILE + std::to_string(timestamp) + ".bin";
+ if (IsFileAccessibleAndNotEmpty(exactFilePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到配速表: %s",
+ exactFilePath.c_str());
+ return exactFilePath; // 返回完全匹配的文件路径
+ }
+
+ // 如果未找到,检查时间戳相差1到2秒的文件
+ for (int i = -2; i <= 2; ++i) {
+ uint32_t checkTimestamp = timestamp + i;
+ std::string filePath =
+ TRAINING_PACE_FILE + std::to_string(checkTimestamp) + ".bin";
+ if (IsFileAccessibleAndNotEmpty(filePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到配速表: %s",
+ filePath.c_str());
+ return filePath; // 返回找到的文件路径
+ }
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的配速表。");
+ return ""; // 未找到文件
+ }
+ /*发送实时数据*/
+ void TraingModel::SendLiveData() {
+ #ifdef SUPPORT_BLE_SERVICE
+ msg_get_train_data_info_t data;
+ memset(&data, 0, sizeof(msg_get_train_data_info_t));
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->starttime %d \r\n",
+ sportdata->starttime);
+ data.st = sportdata->starttime;
+ data.hr = sportdata->heart;
+ data.ca = sportdata->calories * 10; // app端需要乘10
+ data.di = sportdata->distance;
+ data.stp = sportdata->steps;
+ data.ti = sportdata->intensity;
+ data.pc = sportdata->pc;
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "SendLiveData Begin!");
+ sendtraningdata(&data); // 发送数据函数
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "SendLiveData Stop!");
+ #endif
+ }
+ /* -------------------------------- 运动识别发起处理
+ * -------------------------------- */
+ void TraingModel::set_sport_config(UIResourceTextId id) {
+ if (id >= MAXSPORTID) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "id is error");
+ return;
+ }
+ // 开始训练 ->不返回
+ NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGCRAD);
+ // 配置运动项
+ SetSportEvent(id);
+ // 获取运动项目
+ ExerciseClass type = GetSportId();
+ // 开启对应的运动
+ TraingCardPresenter::GetInstance()->OpenSensor(type);
+ // 设置为手表开启运动
+ SetSportOpenWay(false);
+ }
+
+ /* -------------------------------------------------------------------------- */
+
+ #include <chrono>
+ #include <iomanip>
+ #include <sstream>
+ #include <string>
+
+ std::string TraingModel::DisplayMotionDuration() {
+ // 清空 motionDuration
+ motionDuration.clear();
+
+ // 重置运动次数
+ if (g_motionTimes >= NUM_TIME_DAY) {
+ g_motionTimes = 0;
+ }
+
+ // 计算和格式化运动时长
+ int hours = g_motionTimes / NUM_TIME_HOUR;
+ int minutes = (g_motionTimes % NUM_TIME_HOUR) / NUM_TIME_MIN;
+ int seconds = g_motionTimes % NUM_TIME_MIN;
+
+ std::ostringstream oss;
+ oss << std::setfill('0') << std::setw(2) << hours << ":" << std::setw(2)
+ << minutes << ":" << std::setw(2) << seconds;
+
+ motionDuration = oss.str();
+
+ // // 获取当前时间(以微秒为单位)
+ // uint32_t currentTime = static_cast<uint32_t>(
+ // std::chrono::duration_cast<std::chrono::microseconds>(
+ // std::chrono::system_clock::now().time_since_epoch())
+ // .count());
+
+ // // 记录和检查上一次调用的时间
+ // static uint32_t lastCallTime = 0;
+ // if (currentTime - lastCallTime < 1000000) { // 小于1秒
+ // return motionDuration;
+ // }
+ // lastCallTime = currentTime; // 更新最后调用时间
+
+ // 如果 g_motionTimes 不为零,进行计算和数据存储
+ if (g_motionTimes > 0) {
+ sportdata->duration = g_motionTimes; // 设置训练时长
+
+ // 每分钟进行配速和心率数据存储
+ // StoreDataIfMinutePassed();
+ }
+
+ return motionDuration; // 返回格式化的运动时长
+ }
+
+ // 检查并发送实时数据
+ void TraingModel::SendLiveDataIfNecessary() {
+ // 检查自动获取的配置 app发起
+ if (user_autoget && Openingway) {
+ // 发送实时数据
+ if (g_motionTimes % user_freq == 0) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "SendLiveData is OK\r\n");
+ SendLiveData(); // 发送实时数据
+ }
+ }
+ }
+
+ // 每5秒进行配速和心率数据存储
+ void TraingModel::StoreDataIfMinutePassed() {
+ if (sportdata->duration % 5 == 0) {
+ uint32_t timestamp;
+ //进行数据的筛选
+ if (uapi_get_timestampe_ss(×tamp) == ERRCODE_SUCC) {
+ if (sportdata->heart != 0)
+ AddHeartRateData(timestamp, sportdata->heart);
+ if (sportdata->apc != 0)
+ AddPaceData(timestamp, sportdata->apc);
+ }
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "duration:[%d] g_motionTimes[%d]\r\n",
+ sportdata->duration, g_motionTimes);
+ }
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+ #include <stdbool.h> // 确保包含该头文件以使用布尔类型
+
+ // 全局变量
+ static bool isPaused = false; // 定时器暂停状态
+ static uint32_t pauseStartTimestamp = 0; // 当前暂停开始的时间戳
+ static uint32_t totalPausedDuration = 0; // 总暂停时长
+ // static uint32_t g_motionTimes = 0; // 总运动时间
+ // static TimerHandle_t g_startCounting = NULL; // 定时器句柄
+
+ /* 日志错误信息 */
+ void LogError(const char *msg, osStatus_t ret) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%s ret = %lx!!", msg, ret);
+ }
+
+ /* 定时器回调函数 */
+ void TraingTimerCallback(void) {
+ // 只在活动状态下进行更新
+
+ if (pauseStartTimestamp == 0) {
+ //开始时将该值设置为运动开始值
+ pauseStartTimestamp = sportdata->starttime;
+ }
+
+ uint32_t currentTimestamp =
+ OHOS::TraingModel::GetInstance()->get_current_time(); // 获取当前时间戳
+ g_motionTimes = (currentTimestamp - pauseStartTimestamp) +
+ totalPausedDuration; // 更新运动时间
+ sportdata->duration = g_motionTimes; // 更新持续时间
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "更新运动时间。当前运动时间: %d %d %d %d", currentTimestamp,
+ pauseStartTimestamp, totalPausedDuration, g_motionTimes);
+
+ // 检查并发送实时数据
+ OHOS::TraingModel::GetInstance()->SendLiveDataIfNecessary();
+ OHOS::TraingModel::GetInstance()->CheckTimeTag();
+
+ // 记录特殊活动的心率/配速数据
+ if (OHOS::TraingModel::GetInstance()->GetIsSpecial()) {
+ OHOS::TraingModel::GetInstance()->StoreDataIfMinutePassed();
+ }
+
+ // 振动反馈
+ if ((sportdata->goaldegree >= 100) && (shock_count < 4)) {
+ shock_count++;
+ custom_VibratorFunc_int(0); // 触发振动反馈
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "触发了振动。振动计数: %d",
+ shock_count);
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "TraingTimerCallback 被执行。");
+ }
+
+ /* 启动定时器 */
+ void StartTimer(void) {
+ if (g_startCounting != NULL) {
+ LogError("定时器已经存在", osError);
+ return;
+ }
+
+ g_startCounting = osTimerNew((osTimerFunc_t)TraingTimerCallback,
+ osTimerPeriodic, NULL, NULL);
+ if (g_startCounting == NULL) {
+ LogError("定时器创建失败", osError);
+ return;
+ }
+
+ int tick = osMs2Tick(MS_ONE_SECONDS);
+ osStatus_t retTimer = osTimerStart(g_startCounting, tick);
+ if (retTimer != osOK) {
+ LogError("定时器启动失败", retTimer);
+ osTimerDelete(g_startCounting);
+ g_startCounting = NULL;
+ }
+ }
+
+ /* 停止并重置定时器 */
+ void StopAndResetTimer(void) {
+ if (g_startCounting != NULL) {
+ osTimerStop(g_startCounting);
+ osStatus_t ret = osTimerDelete(g_startCounting);
+ if (ret != osOK) {
+ LogError("定时器删除失败", ret);
+ }
+ g_startCounting = NULL;
+ g_motionTimes = 0;
+ pauseStartTimestamp = 0;
+ totalPausedDuration = 0; // 重置总暂停时长
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "定时器已停止并重置。");
+ }
+ }
+
+ /* 暂停定时器 */
+ void PauseTimer(void) {
+ if (g_startCounting != NULL) {
+ osTimerStop(g_startCounting);
+
+ totalPausedDuration = sportdata->duration; //记录暂停前的时间;后续加上
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "定时器已暂停。暂停开始时间戳: %u",
+ pauseStartTimestamp);
+ }
+ }
+
+ /* 恢复定时器 */
+ void ResumeTimer(void) {
+ if (g_startCounting != NULL) {
+
+ uint32_t currentTimestamp =
+ OHOS::TraingModel::GetInstance()->get_current_time(); // 获取当前时间戳
+ pauseStartTimestamp = currentTimestamp; // 保存暂停开始的时间戳 将这个更新
+
+ int tick = osMs2Tick(MS_ONE_SECONDS);
+ if (osTimerStart(g_startCounting, tick) != osOK) {
+ LogError("恢复定时器时启动失败", osError);
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "定时器已恢复。当前运动时间: %u %d",
+ g_motionTimes, pauseStartTimestamp);
+ }
+ }
+
+ #ifdef __cplusplus
+ }
+ #endif
+ /* ---------------------------------- 数据存储
+ * ---------------------------------- */
+ static int32 SaveTraingDataToFile(void) {
+ time_t now = time(nullptr);
+ struct tm *timeinfo = localtime(&now);
+ char filename[20];
+ strftime(filename, sizeof(filename), "%Y%m%d.bin", timeinfo);
+
+ uint32 size = sizeof(Trainingdata);
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "SaveTraingDataToFile !!");
+
+ char filePath[20];
+ sprintf(filePath, "%s%s", TRAINING_DATA_DIRECTORY, filename);
+
+ std::ofstream fd(filePath, std::ios::binary);
+ if (!fd.is_open()) {
+ if (mkdir(TRAINING_DATA_DIRECTORY, S_IRWXU) != 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to create directory");
+ return 0;
+ }
+
+ fd.open(filePath, std::ios::binary);
+ if (!fd.is_open()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open file for write");
+ return 0;
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Directory created successfully!!");
+ }
+
+ fd.write(reinterpret_cast<const char *>(&sportdata), size);
+ fd.close();
+ return 1;
+ }
+ static std::vector<Trainingdata> LoadTrainingDataFromTodayFile() {
+ std::vector<Trainingdata> trainingRecords;
+
+ time_t now = time(nullptr);
+ struct tm *timeinfo = localtime(&now);
+ char filename[20];
+ strftime(filename, sizeof(filename), "%Y%m%d.bin", timeinfo);
+ // 找到训练文件夹里面的当天文件
+ char filePath[20];
+ sprintf(filePath, "%s%s", TRAINING_DATA_DIRECTORY, filename);
+
+ std::ifstream fd(filePath, std::ios::binary);
+ if (!fd.is_open()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open today's file");
+ return trainingRecords; // Return an empty vector if file open fails
+ }
+
+ // 读取训练信息
+ while (!fd.eof()) {
+ Trainingdata record;
+ fd.read(reinterpret_cast<char *>(&record), sizeof(Trainingdata));
+ if (fd.gcount() == sizeof(Trainingdata)) {
+ trainingRecords.push_back(record);
+ }
+ }
+
+ fd.close();
+
+ return trainingRecords;
+ }
+
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+
+ #include <errno.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
+ #include <sys/stat.h>
+
+ // 获得数据个数
+ static int get_Trainingdata_data_num(FILE *fp) {
+ if (fp == NULL) {
+ return 0; // 如果文件指针为空,返回0
+ }
+
+ fseek(fp, 0, SEEK_END); // 将文件指针移动到文件末尾
+ long size = ftell(fp); // 获取当前文件指针位置,即文件大小
+ fseek(fp, 0, SEEK_SET); // 将文件指针移回文件开头
+
+ return (size / sizeof(Trainingdata));
+ }
+
+ // 写训练bin文件
+ int write_Trainingdata_data(Trainingdata data) {
+ char file_path[MAX_FILE_PATH_LENGTH] = {0};
+ char timeday[12] = {0};
+ uapi_get_YYMMDD_Time(timeday, '_');
+ snprintf(file_path, sizeof(file_path), "%s%s.bin", TRAINING_PATH, timeday);
+
+ // 检查目录是否存在,如果不存在则创建
+ struct stat st = {0};
+ if (stat(TRAINING_PATH, &st) == -1) {
+ if (mkdir(TRAINING_PATH, 0700) != 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to create directory: %s",
+ strerror(errno));
+ return -1; // 返回错误码
+ }
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%s", file_path);
+ FILE *fp = fopen(file_path, "ab+");
+ if (fp == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open file for append: %s",
+ strerror(errno));
+ fp = fopen(file_path, "wb+");
+ if (fp == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Failed to open file for write: %s", strerror(errno));
+ return -1; // 返回错误码
+ }
+ }
+
+ int ret = fwrite((void *)&data, sizeof(Trainingdata), 1, fp);
+ if (ret != 1) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "fwrite_data error! %d", ret);
+ fclose(fp);
+ return -1; // 返回错误码
+ }
+
+ fclose(fp); // 关闭文件
+ return 1; // 返回成功码
+ }
+
+ // 读数据
+ Trainingdata *read_Trainingdata_data(char *timedata, uint16_t offset) {
+ char file_path[128] = {0};
+ snprintf(file_path, sizeof(file_path), "%s%s.bin", TRAINING_PATH, timedata);
+
+ FILE *fp = fopen(file_path, "rb");
+ if (fp == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open file for read: %s",
+ strerror(errno));
+ return NULL;
+ }
+
+ Trainingdata *data = (Trainingdata *)malloc(sizeof(Trainingdata));
+ if (data == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Failed to allocate memory for Trainingdata");
+ fclose(fp);
+ return NULL;
+ }
+
+ int num_data = get_Trainingdata_data_num(fp);
+ if (num_data <= offset) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Offset %d exceeds number of data %d", offset, num_data);
+ free(data); // 释放内存
+ fclose(fp);
+ return NULL;
+ }
+
+ fseek(fp, sizeof(Trainingdata) * offset, SEEK_SET);
+ fread(data, sizeof(Trainingdata), 1, fp);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Data src: %d", data->src);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Offset: %d, Size: %zu", offset,
+ sizeof(Trainingdata));
+
+ fclose(fp); // 关闭文件
+ return data; // 返回读取的数据
+ }
+
+ #ifdef __cplusplus
+ }
+ #endif
+
+ /* -------------------------------------------------------------------------- */
+ /*检查是否存在数据*/
+ bool TraingModel::CheckDataJson() {
+ // 读取record.json文件
+ std::ifstream file(TRAINING_RECORD_FILE);
+
+ // 检查文件是否打开成功
+ if (!file.is_open()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Unable to open file");
+ return false;
+ }
+
+ // 检查文件是否为空
+ file.seekg(0, std::ios::end);
+ if (file.tellg() == 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "File is empty");
+ file.close();
+ return false;
+ }
+
+ // 解析json
+ std::vector<SportData> sportDataVector;
+ if (!LoadDataFormJson(sportDataVector)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LoadDataFormJson is error");
+ file.close();
+ return false;
+ }
+
+ // 关闭文件
+ file.close();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "CheckDataJson is ok");
+ return true;
+ }
+
+ bool TraingModel::SaveDataToJson(SportData *data) {
+ FILE *fp;
+ char *jsonStr = NULL;
+
+ // 读取现有的 JSON 数据
+ fp = fopen(TRAINING_RECORD_FILE, "r");
+ cJSON *data_root = NULL;
+ cJSON *dataArray = NULL;
+
+ if (fp != NULL) {
+ fseek(fp, 0, SEEK_END);
+ long fileSize = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ jsonStr = (char *)malloc(fileSize + 1);
+ if (jsonStr != nullptr) {
+ if (fread(jsonStr, fileSize, 1, fp) == 1) {
+ data_root = cJSON_Parse(jsonStr);
+ if (data_root != NULL) {
+ dataArray = cJSON_GetObjectItem(data_root, "data");
+ }
+ }
+ }
+ fclose(fp);
+ }
+
+ if (dataArray == NULL) {
+ data_root = cJSON_CreateObject();
+ cJSON_AddItemToObject(data_root, "data", cJSON_CreateArray());
+ dataArray = cJSON_GetObjectItem(data_root, "data");
+ }
+
+ // 创建或更新数据节点
+ cJSON *sportData = cJSON_CreateObject();
+ cJSON_AddNumberToObject(sportData, "sportTypeId",
+ int(data->sportType)); // 训练项目类 Id
+ cJSON_AddNumberToObject(sportData, "sportId", data->sportId); // 训练项目 Id
+ cJSON_AddStringToObject(sportData, "startDate",
+ (data->start_date).c_str()); // 训练开始日期字符串
+ cJSON_AddStringToObject(sportData, "startTime",
+ (data->start_time).c_str()); // 训练开始时间
+ cJSON_AddStringToObject(sportData, "endTime",
+ (data->end_time).c_str()); // 训练结束时间
+ cJSON_AddNumberToObject(sportData, "goalDegree",
+ data->goaldegree); // 训练目标完成度
+ cJSON_AddNumberToObject(sportData, "duration",
+ data->duration); // 训练时长(秒)
+ cJSON_AddNumberToObject(sportData, "distance", data->distance); // 训练距离
+ cJSON_AddNumberToObject(sportData, "avgHeartRate",
+ data->avgheart); // 训练平均心率
+ cJSON_AddNumberToObject(sportData, "steps", data->steps); // 训练步数
+ cJSON_AddNumberToObject(sportData, "intensity", data->intensity); // 训练强度
+ cJSON_AddNumberToObject(sportData, "apc", data->apc); // 训练平均配速
+ cJSON_AddNumberToObject(sportData, "cdc", data->cdc); // 训练步频
+ cJSON_AddNumberToObject(sportData, "std", data->std); // 训练步幅
+ cJSON_AddNumberToObject(sportData, "calories", data->calories); //卡路里
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "starttime is %d", data->starttime);
+ cJSON_AddNumberToObject(sportData, "start_time",
+ data->starttime); // 开始时间戳
+
+ // 添加运动数据节点到数组并限制数量
+ cJSON_AddItemToArray(dataArray, sportData);
+ while (cJSON_GetArraySize(dataArray) > MAX_DATA_COUNT) {
+ cJSON_DeleteItemFromArray(dataArray, 0);
+ }
+
+ // 将更新后的 JSON 数据写入文件
+ char *jsonStrNew = cJSON_Print(data_root);
+ fp = fopen(TRAINING_RECORD_FILE, "w");
+ if (fp != NULL) {
+ fwrite(jsonStrNew, strlen(jsonStrNew), 1, fp);
+ fclose(fp);
+ }
+
+ // 释放资源
+ cJSON_Delete(data_root);
+ free(jsonStr);
+ cJSON_free(jsonStrNew);
+
+ return true;
+ }
+
+ /* 加载训练数据函数 */
+ /* 加载训练数据函数 */
+ bool TraingModel::LoadDataFormJson(std::vector<SportData> &sportDataVector) {
+ // 清空输出向量,避免旧数据干扰
+ sportDataVector.clear();
+
+ // 打开JSON文件
+ std::ifstream file(TRAINING_RECORD_FILE);
+ if (!file.is_open()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open file: %s",
+ TRAINING_RECORD_FILE);
+ return false;
+ }
+
+ // 读取文件内容到字符串
+ std::string jsonData((std::istreambuf_iterator<char>(file)),
+ std::istreambuf_iterator<char>());
+ file.close();
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Read %zu bytes from JSON file",
+ jsonData.size());
+
+ // 解析JSON根对象
+ cJSON *root = cJSON_Parse(jsonData.c_str());
+ if (root == NULL) {
+ const char *errorPtr = cJSON_GetErrorPtr();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to parse JSON: %s",
+ errorPtr ? errorPtr : "Unknown error");
+ return false;
+ }
+
+ // 查找并验证data数组
+ cJSON *dataArray = cJSON_GetObjectItem(root, "data");
+ if (dataArray == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Missing 'data' array in JSON");
+ cJSON_Delete(root);
+ return false;
+ }
+ if (!cJSON_IsArray(dataArray)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "'data' is not an array in JSON");
+ cJSON_Delete(root);
+ return false;
+ }
+
+ // 检查数组大小
+ int dataCount = cJSON_GetArraySize(dataArray);
+ if (dataCount == 0) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Empty 'data' array in JSON");
+ cJSON_Delete(root);
+ return false;
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Found %d items in 'data' array",
+ dataCount);
+
+ // 遍历解析每个数据项
+ for (int i = 0; i < dataCount; i++) {
+ cJSON *item = cJSON_GetArrayItem(dataArray, i);
+ if (item == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Null item at index %d in 'data' array", i);
+ continue; // 跳过无效项,继续处理其他项
+ }
+
+ SportData sportData;
+ bool isValid = true;
+ std::string missingFields; // 记录缺失的字段
+
+ // 解析sportTypeId字段
+ if (cJSON *sportTypeIdItem = cJSON_GetObjectItem(item, "sportTypeId")) {
+ if (cJSON_IsNumber(sportTypeIdItem)) {
+ sportData.sportType = (Exercisetype)sportTypeIdItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "sportTypeId ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "sportTypeId ";
+ }
+
+ // 解析sportId字段
+ if (cJSON *sportIdItem = cJSON_GetObjectItem(item, "sportId")) {
+ if (cJSON_IsNumber(sportIdItem)) {
+ sportData.sportId = (UIResourceTextId)sportIdItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "sportId ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "sportId ";
+ }
+
+ // 解析startDate字段
+ if (cJSON *startDateItem = cJSON_GetObjectItem(item, "startDate")) {
+ if (cJSON_IsString(startDateItem) && startDateItem->valuestring) {
+ sportData.start_date = startDateItem->valuestring;
+ } else {
+ isValid = false;
+ missingFields += "startDate ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "startDate ";
+ }
+
+ // 解析startTime字段
+ if (cJSON *startTimeItem = cJSON_GetObjectItem(item, "startTime")) {
+ if (cJSON_IsString(startTimeItem) && startTimeItem->valuestring) {
+ sportData.start_time = startTimeItem->valuestring;
+ } else {
+ isValid = false;
+ missingFields += "startTime ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "startTime ";
+ }
+
+ // 解析endTime字段
+ if (cJSON *endTimeItem = cJSON_GetObjectItem(item, "endTime")) {
+ if (cJSON_IsString(endTimeItem) && endTimeItem->valuestring) {
+ sportData.end_time = endTimeItem->valuestring;
+ } else {
+ isValid = false;
+ missingFields += "endTime ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "endTime ";
+ }
+
+ // 解析goalDegree字段
+ if (cJSON *goalDegreeItem = cJSON_GetObjectItem(item, "goalDegree")) {
+ if (cJSON_IsNumber(goalDegreeItem)) {
+ sportData.goaldegree = goalDegreeItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "goalDegree ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "goalDegree ";
+ }
+
+ // 解析duration字段
+ if (cJSON *durationItem = cJSON_GetObjectItem(item, "duration")) {
+ if (cJSON_IsNumber(durationItem)) {
+ sportData.duration = durationItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "duration ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "duration ";
+ }
+
+ // 解析distance字段
+ if (cJSON *distanceItem = cJSON_GetObjectItem(item, "distance")) {
+ if (cJSON_IsNumber(distanceItem)) {
+ sportData.distance = distanceItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "distance ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "distance ";
+ }
+
+ // 解析avgHeartRate字段
+ if (cJSON *avgHeartRateItem = cJSON_GetObjectItem(item, "avgHeartRate")) {
+ if (cJSON_IsNumber(avgHeartRateItem)) {
+ sportData.avgheart = avgHeartRateItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "avgHeartRate ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "avgHeartRate ";
+ }
+
+ // 解析steps字段
+ if (cJSON *stepsItem = cJSON_GetObjectItem(item, "steps")) {
+ if (cJSON_IsNumber(stepsItem)) {
+ sportData.steps = stepsItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "steps ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "steps ";
+ }
+
+ // 解析intensity字段
+ if (cJSON *intensityItem = cJSON_GetObjectItem(item, "intensity")) {
+ if (cJSON_IsNumber(intensityItem)) {
+ sportData.intensity = intensityItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "intensity ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "intensity ";
+ }
+
+ // 解析apc字段
+ if (cJSON *apcItem = cJSON_GetObjectItem(item, "apc")) {
+ if (cJSON_IsNumber(apcItem)) {
+ sportData.apc = apcItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "apc ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "apc ";
+ }
+
+ // 解析cdc字段
+ if (cJSON *cdcItem = cJSON_GetObjectItem(item, "cdc")) {
+ if (cJSON_IsNumber(cdcItem)) {
+ sportData.cdc = cdcItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "cdc ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "cdc ";
+ }
+
+ // 解析std字段
+ if (cJSON *stdItem = cJSON_GetObjectItem(item, "std")) {
+ if (cJSON_IsNumber(stdItem)) {
+ sportData.std = stdItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "std ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "std ";
+ }
+
+ // 解析calories字段
+ if (cJSON *caloriesItem = cJSON_GetObjectItem(item, "calories")) {
+ if (cJSON_IsNumber(caloriesItem)) {
+ sportData.calories = caloriesItem->valueint;
+ } else {
+ isValid = false;
+ missingFields += "calories ";
+ }
+ } else {
+ isValid = false;
+ missingFields += "calories ";
+ }
+
+ // 解析starttime字段(时间戳)
+ if (cJSON *starttimeItem = cJSON_GetObjectItem(item, "start_time")) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Found starttime field. Type: %d",
+ starttimeItem->type);
+ if (cJSON_IsNumber(starttimeItem)) {
+ sportData.starttime = starttimeItem->valueint;
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Parsed starttime: %d",
+ sportData.starttime);
+ } else {
+ // 打印详细信息,帮助定位问题
+ const char *valueStr =
+ starttimeItem->valuestring ? starttimeItem->valuestring : "null";
+ WEARABLE_LOGE(
+ WEARABLE_LOG_MODULE_APP,
+ "Invalid starttime type. Expected number, got type %d (value: %s)",
+ starttimeItem->type, valueStr);
+ isValid = false;
+ }
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Missing starttime field");
+ isValid = false;
+ }
+
+ // 处理解析结果
+ if (isValid) {
+ sportDataVector.push_back(sportData);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Successfully parsed item %d", i);
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Invalid data in item %d. Missing fields: %s", i,
+ missingFields.c_str());
+ }
+ }
+
+ cJSON_Delete(root);
+
+ // 检查是否成功解析了任何数据
+ if (sportDataVector.empty()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "No valid data items found in JSON");
+ return false;
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "Successfully loaded %zu valid data items",
+ sportDataVector.size());
+ return true;
+ }
+
+ //////////////////////////////////////GPS///////////////////////////////////////////////
+ /*
+ Latitude(纬度):范围:-90° 至 +90°(或南纬 90° 至北纬 90°);
+ Longitude(经度):范围:-180° 至 +180°(或西经 180° 至东经 180°);
+
+ -画布数据集最多是100个定位坐标,超过按比例进行拿取100个点位;
+ -通过映射关系,将经纬度转换为400*400画布上的坐标;
+ -但是不能靠近400,以免显示不全等问题,350*350;
+ -如果处理负数,需要将其转换为正数,然后再进行映射?
+ -如果有正数又有负数,应该怎么处理?
+ */
+
+ static double getmin(double a, double b) { return a < b ? a : b; }
+ static double getmax(double a, double b) { return a > b ? a : b; }
+ // 计算点p到由p1和p2确定的线段的垂直距离(优化版)
+ static double perpendicularDistance(const Point &p, const Point &p1,
+ const Point &p2) {
+ // 计算向量 p1->p2
+ double dx = p2.x - p1.x;
+ double dy = p2.y - p1.y;
+
+ // 线段长度的平方
+ double len2 = dx * dx + dy * dy;
+
+ // 如果线段退化为一个点,直接计算点到点的距离
+ if (len2 == 0) {
+ return std::hypot(p.x - p1.x, p.y - p1.y);
+ }
+
+ // 计算投影比例 t
+ double t = ((p.x - p1.x) * dx + (p.y - p1.y) * dy) / len2;
+
+ if (t < 0) {
+ // 投影在p1之前,返回p到p1的距离
+ return std::hypot(p.x - p1.x, p.y - p1.y);
+ }
+
+ if (t > 1) {
+ // 投影在p2之后,返回p到p2的距离
+ return std::hypot(p.x - p2.x, p.y - p2.y);
+ }
+
+ // 投影在线段上,计算投影点并返回距离
+ double projX = p1.x + t * dx;
+ double projY = p1.y + t * dy;
+ return std::hypot(p.x - projX, p.y - projY);
+ }
+
+ // Douglas-Peucker算法实现(优化版)
+ static void douglasPeucker(const std::vector<Point> &points, double epsilon,
+ std::vector<Point> &result) {
+ if (points.size() <= 2) {
+ // 点数量不足,直接返回
+ result = points;
+ return;
+ }
+
+ double dmax = 0;
+ int index = 0;
+ int end = points.size() - 1;
+
+ // 找到距离最大的点
+ for (int i = 1; i < end; i++) {
+ double d = perpendicularDistance(points[i], points[0], points[end]);
+ if (d > dmax) {
+ index = i;
+ dmax = d;
+ }
+ }
+
+ // 如果最大距离大于阈值,则递归处理
+ if (dmax > epsilon) {
+ std::vector<Point> recResults1, recResults2;
+
+ // 递归处理前半部分
+ douglasPeucker(
+ std::vector<Point>(points.begin(), points.begin() + index + 1), epsilon,
+ recResults1);
+
+ // 递归处理后半部分
+ douglasPeucker(std::vector<Point>(points.begin() + index, points.end()),
+ epsilon, recResults2);
+
+ // 合并结果(避免重复添加端点)
+ result.assign(recResults1.begin(), recResults1.end() - 1);
+ result.insert(result.end(), recResults2.begin(), recResults2.end());
+ } else {
+ // 否则保留首尾点
+ result = {points[0], points[end]};
+ }
+ }
+
+ // 改进的GetPointData函数(优化版)
+ void TraingModel::GetPointData() {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "Generating point data from %zu GPS points", gpsData.size());
+
+ std::vector<GPSData> sampledData;
+
+ // 处理GPS数据
+ if (gpsData.size() > 100) {
+ // 计算经纬度范围
+ double minLat = 90.0, maxLat = -90.0;
+ double minLon = 180.0, maxLon = -180.0;
+
+ // 同时计算平均值用于判断数据分布
+ double sumLat = 0, sumLon = 0;
+
+ for (const auto &point : gpsData) {
+ minLat = getmin(minLat, point.latitude);
+ maxLat = getmax(maxLat, point.latitude);
+ minLon = getmin(minLon, point.longitude);
+ maxLon = getmax(maxLon, point.longitude);
+ sumLat += point.latitude;
+ sumLon += point.longitude;
+ }
+
+ // 计算经纬度范围和平均值
+ double rangeLat = maxLat - minLat;
+ double rangeLon = maxLon - minLon;
+ double avgLat = sumLat / gpsData.size();
+ double avgLon = sumLon / gpsData.size();
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "GPS range: lat=[%.6f, %.6f], lon=[%.6f, %.6f]", minLat,
+ maxLat, minLon, maxLon);
+
+ // 检查数据是否集中在极小区域或几乎相同
+ const double MIN_SIGNIFICANT_RANGE = 0.01; // 根据实际情况调整
+ bool isDataDegenerate =
+ (rangeLat < MIN_SIGNIFICANT_RANGE && rangeLon < MIN_SIGNIFICANT_RANGE);
+
+ if (isDataDegenerate) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "GPS data is degenerate (very small range)");
+ // 使用均匀采样,避免算法失效
+ double step = static_cast<double>(gpsData.size() - 1) / 99;
+ for (int i = 0; i < 100; i++) {
+ int index = static_cast<int>(i * step);
+ sampledData.push_back(gpsData[index]);
+ }
+ } else {
+ // 转换GPS坐标到屏幕坐标
+ std::vector<Point> screenPoints;
+ screenPoints.reserve(gpsData.size());
+
+ for (const auto &point : gpsData) {
+ double normLat = (point.latitude - minLat) / rangeLat;
+ double normLon = (point.longitude - minLon) / rangeLon;
+ screenPoints.push_back(
+ {normLon * 300 + 50, (1.0 - normLat) * 300 + 50});
+ }
+
+ // 使用Douglas-Peucker算法简化轨迹
+ std::vector<Point> simplifiedPoints;
+ douglasPeucker(screenPoints, 0.5, simplifiedPoints);
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "After Douglas-Peucker: %zu points",
+ simplifiedPoints.size());
+
+ // 处理简化结果
+ if (simplifiedPoints.size() <= 2) {
+ WEARABLE_LOGE(
+ WEARABLE_LOG_MODULE_APP,
+ "Trajectory over-simplified! Using uniform sampling instead.");
+ // 过度简化时回退到均匀采样
+ double step = static_cast<double>(gpsData.size() - 1) / 99;
+ for (int i = 0; i < 100; i++) {
+ int index = static_cast<int>(i * step);
+ sampledData.push_back(gpsData[index]);
+ }
+ } else if (simplifiedPoints.size() > 100) {
+ // 简化后仍超过100点,再次均匀采样
+ double step = static_cast<double>(simplifiedPoints.size() - 1) / 99;
+ for (int i = 0; i < 100; i++) {
+ int index = static_cast<int>(i * step);
+ // 屏幕坐标反推回GPS坐标
+ double normX = (simplifiedPoints[index].x - 50) / 300;
+ double normY = (simplifiedPoints[index].y - 50) / 300;
+ double lat = minLat + (1.0 - normY) * rangeLat;
+ double lon = minLon + normX * rangeLon;
+ sampledData.push_back({0, lat, lon});
+ }
+ } else {
+ // 简化后点数量合适,直接使用
+ for (const auto &point : simplifiedPoints) {
+ double normX = (point.x - 50) / 300;
+ double normY = (point.y - 50) / 300;
+ double lat = minLat + (1.0 - normY) * rangeLat;
+ double lon = minLon + normX * rangeLon;
+ sampledData.push_back({0, lat, lon});
+ }
+ }
+ }
+ } else {
+ // GPS点数不足100,直接使用全部数据
+ sampledData = gpsData;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Using all %zu GPS points (no sampling)", sampledData.size());
+ }
+
+ // 映射到画布坐标
+ canvasData.clear();
+ canvasData.reserve(sampledData.size());
+
+ // 如果没有采样数据,直接返回
+ if (sampledData.empty()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "No sampled data to map!");
+ return;
+ }
+
+ // 基于采样数据重新计算经纬度范围
+ double minLat = 90.0, maxLat = -90.0;
+ double minLon = 180.0, maxLon = -180.0;
+
+ for (const auto &point : sampledData) {
+ minLat = getmin(minLat, point.latitude);
+ maxLat = getmax(maxLat, point.latitude);
+ minLon = getmin(minLon, point.longitude);
+ maxLon = getmax(maxLon, point.longitude);
+ }
+
+ double rangeLat = maxLat - minLat;
+ double rangeLon = maxLon - minLon;
+
+ // 避免除零错误
+ if (rangeLat == 0)
+ rangeLat = 1e-10;
+ if (rangeLon == 0)
+ rangeLon = 1e-10;
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "Sampled GPS range: lat=[%.6f, %.6f], lon=[%.6f, %.6f]", minLat,
+ maxLat, minLon, maxLon);
+
+ // 映射每个点到画布
+ for (const auto &point : sampledData) {
+ double normLat = (point.latitude - minLat) / rangeLat;
+ double normLon = (point.longitude - minLon) / rangeLon;
+
+ // 转换到画布坐标
+ double x = normLon * 300 + 50;
+ double y = (1.0 - normLat) * 300 + 50;
+
+ canvasData.push_back({x, y});
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "Mapped point: (%.6f, %.6f) -> (%.2f, %.2f)", point.latitude,
+ point.longitude, x, y);
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Generated %zu canvas points",
+ canvasData.size());
+ }
+
+ // 添加经纬度数据集
+ void TraingModel::AddGPSData(uint32_t timestamp, double latitude,
+ double longitude) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== AddGPSData函数调用开始 ===");
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "输入参数: timestamp=%u, latitude=%.6f, longitude=%.6f",
+ timestamp, latitude, longitude);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "当前GPS数据点数量: %zu",
+ gpsData.size());
+
+ // 判断数据是否合理
+ if (latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "错误: GPS数据无效 - 纬度=%.6f (范围: -90~90), 经度=%.6f "
+ "(范围: -180~180)",
+ latitude, longitude);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "=== AddGPSData函数调用结束(数据无效) ===");
+ return;
+ }
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS数据有效性检查通过");
+
+ // 校验时间戳是否递增
+ if (!gpsData.empty() && timestamp <= gpsData.back().timestamp) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "错误: 时间戳未递增 - 当前=%u, 上一个=%u, 差值=%d", timestamp,
+ gpsData.back().timestamp,
+ (int)timestamp - (int)gpsData.back().timestamp);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "=== AddGPSData函数调用结束(时间戳错误) ===");
+ return;
+ }
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "时间戳递增检查通过");
+
+ // 添加数据
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "准备添加GPS数据到容器...");
+ gpsData.push_back({timestamp, latitude, longitude});
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "成功添加GPS数据 #%zu: 时间戳=%u, 坐标=(%.6f, %.6f)",
+ gpsData.size(), timestamp, latitude, longitude);
+
+ // 打印当前GPS数据统计信息
+ if (gpsData.size() > 1) {
+ double lat_diff = latitude - gpsData[gpsData.size() - 2].latitude;
+ double lon_diff = longitude - gpsData[gpsData.size() - 2].longitude;
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "与上一个点的距离差: 纬度差=%.6f, 经度差=%.6f", lat_diff,
+ lon_diff);
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== AddGPSData函数调用结束 ===");
+ }
+
+ //保存canvas数据
+ bool TraingModel::SaveCanvasData() {
+
+ //打印一下gps的数据
+ for (const auto &point : gpsData) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "GPS Data: Latitude = %f, Longitude = %f", point.latitude,
+ point.longitude);
+ }
+
+ if (canvasData.size() == 0) {
+ WEARABLE_LOGE(WEARING_LOG_MODULE_APP, "canvasData is empty");
+ return false;
+ }
+
+ CreateDirectoryIfNotExists(TRAINING_CANVAS_FILE);
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "canvasData.size() = %zu",
+ canvasData.size());
+
+ std::string filePath =
+ TRAINING_CANVAS_FILE + std::to_string(sportdata->starttime) + ".bin";
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveCanvasData filePath: %s",
+ filePath.c_str());
+
+ std::ofstream gpsFile(filePath, std::ios::binary);
+ if (!gpsFile) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
+ filePath.c_str());
+ return false; // 返回失败
+ }
+ // 写入gps数据
+ if (!gpsFile.write(reinterpret_cast<const char *>(canvasData.data()),
+ canvasData.size() * sizeof(Point))) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "写入GPS数据失败。");
+ return false; // 返回失败
+ }
+
+ gpsFile.close();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData Success.");
+ return true; // 返回成功
+ }
+ // 清除canvas数据
+ void TraingModel::ClearCanvasData() { canvasData.clear(); }
+ // 获取轨迹点集
+ std::vector<Point> TraingModel::GetCanvasData() const { return canvasData; }
+ // 获取canvas数据
+ std::string TraingModel::GetCanvasDataPath(uint32_t timestamp) {
+ // 首先检查完全匹配的时间戳
+ std::string exactFilePath =
+ TRAINING_CANVAS_FILE + std::to_string(timestamp) + ".bin";
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "canvas表: %s", exactFilePath.c_str());
+ if (IsFileAccessibleAndNotEmpty(exactFilePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到canvas表: %s",
+ exactFilePath.c_str());
+ return exactFilePath; // 返回完全匹配的文件路径
+ }
+
+ // 如果未找到,检查时间戳相差1到2秒的文件
+ for (int i = -2; i <= 2; ++i) {
+ uint32_t checkTimestamp = timestamp + i;
+ std::string filePath =
+ TRAINING_CANVAS_FILE + std::to_string(checkTimestamp) + ".bin";
+ if (IsFileAccessibleAndNotEmpty(filePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到canvas表: %s",
+ filePath.c_str());
+ return filePath; // 返回找到的文件路径
+ }
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的找到canvas表。");
+ return ""; // 未找到文件
+ }
+ // 加载GPS数据
+ bool TraingModel::LoadCanvasData(const std::string &filePath) {
+ std::ifstream gpsFile(filePath, std::ios::binary);
+ if (!gpsFile) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
+ filePath.c_str());
+ return false;
+ }
+
+ // 清空现有数据
+ canvasData.clear();
+
+ // 动态读取数据(假设文件格式允许逐点读取)
+ Point point;
+ while (gpsFile.read(reinterpret_cast<char *>(&point), sizeof(Point))) {
+ canvasData.push_back(point);
+ }
+
+ gpsFile.close();
+
+ if (canvasData.empty()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未读取到有效数据: %s",
+ filePath.c_str());
+ return false;
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "LoadCanvasData Success. 加载了 %zu 个点", canvasData.size());
+ return true;
+ }
+ //////////////////////////////////////gps存储///////////////////////////////////////
+ //存储gps数据
+ bool TraingModel::SaveGPSData() {
+ //打印gps数据
+ for (const auto &point : gpsData) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "GPS Data: Latitude = %f, Longitude = %f", point.`latitude,
+ point.longitude);
+ }
+ CreateDirectoryIfNotExists(TRAINING_GPS_FILE);
+ std::string filePath =
+ TRAINING_GPS_FILE + std::to_string(sportdata->starttime) + ".bin";
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData filePath: %s",
+ filePath.c_str());
+
+ std::ofstream gpsFile(filePath, std::ios::binary);
+ if (!gpsFile) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
+ filePath.c_str());
+ return false; // 返回失败
+ }
+ // 写入gps数据
+ if (!gpsFile.write(reinterpret_cast<const char *>(gpsData.data()),
+ gpsData.size() * sizeof(GPSData))) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "写入GPS数据失败。");
+ return false; // 返回失败
+ }
+ gpsFile.close();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData Success.");
+ return true;
+ }
+ std::string TraingModel::GetGPSDataPath(uint32_t timestamp) {
+ // 首先检查完全匹配的时间戳
+ std::string exactFilePath =
+ TRAINING_GPS_FILE + std::to_string(timestamp) + ".bin";
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "gps表: %s", exactFilePath.c_str());
+ if (IsFileAccessibleAndNotEmpty(exactFilePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到gps表: %s",
+ exactFilePath.c_str());
+ return exactFilePath; // 返回完全匹配的文件路径
+ }
+
+ // 如果未找到,检查时间戳相差1到2秒的文件
+ for (int i = -2; i <= 2; ++i) {
+ uint32_t checkTimestamp = timestamp + i;
+ std::string filePath =
+ TRAINING_GPS_FILE + std::to_string(checkTimestamp) + ".bin";
+ if (IsFileAccessibleAndNotEmpty(filePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到gps表: %s", filePath.c_str());
+ return filePath; // 返回找到的文件路径
+ }
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的找到gps表。");
+ return "";
+ }
+
+ //获取GPS连接状态
+ bool TraingModel::GetGPSConnectState() { return gpsconnect; }
+ //设置GPS连接状态
+ void TraingModel::SetGPSConnectState(bool state) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS连接状态设置 ===");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "当前GPS连接状态: %d, 新状态: %d",
+ gpsconnect, state);
+
+ if (gpsconnect != state) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS连接状态发生变化: %d -> %d",
+ gpsconnect, state);
+ gpsconnect = state;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS连接状态已更新为: %d",
+ gpsconnect);
+
+ //状态改变记录
+ // SystemLogModel::GetInstance()->save_gps_status_log_type(gpsconnect);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS状态变化记录完成");
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS连接状态未发生变化,保持: %d",
+ gpsconnect);
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS连接状态设置完成 ===");
+ }
+ //运动心率、gps开启准备
+ void TraingModel::start_training_heart() {
+ if (gpsopen == false) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始启动GPS定位服务...");
+ //开启心率检测
+ #ifdef SUPPORT_PPG_SERVICE
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "启动心率检测服务");
+ ppg_hr_start();
+ #endif
+ gpsopen = true;
+ }
+ }
+ //运动心率、gps关闭
+ void TraingModel::stop_heart_gps() {
+ if (gpsopen == true) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始关闭GPS定位服务...");
+ // 关闭gps
+ #if defined(SUPPORT_GNSS)
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "调用gnss_stop()关闭GPS服务");
+ gnss_stop();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "gnss_stop()调用完成");
+ #endif
+
+ //关闭心率检测
+ #ifdef SUPPORT_PPG_SERVICE
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "关闭心率检测服务");
+ ppg_hr_stop();
+ #endif
+
+ gpsopen = false;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "GPS定位服务关闭完成,gpsopen设置为false");
+ }
+ }
+
+ /////////////////////////////////////位置服务定时器/////////////////////////////////////
+ /*
+ * 位置服务定时器说明:
+ * - 功能:负责获取GPS定位数据,更新GPS连接状态,添加经纬度数据
+ * - 频率:1秒间隔,保证位置数据的实时性
+ * - 触发条件:仅在训练进行中且训练状态非结束时执行
+ */
+ static osTimerId_t g_location_timer = nullptr;
+ static int location_timer_interval = 1; // 位置数据获取间隔,单位秒
+ static int gps_timer_count = 0;
+
+ /*位置服务定时器到期时的处理*/
+ static void gnss_timer_callbackProc(int data) {
+ /*
+ GPS数据获取处理
+ 1、目前gps注册有问题 ;需要延迟2s 去开启gps;
+ */
+ gps_timer_count++;
+ if (gps_timer_count < 2) {
+ return;
+ }
+ if (gps_timer_count == 2) {
+ #if defined(SUPPORT_GNSS)
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开启gps");
+ gnss_start();
+ #endif
+ }
+ #if defined(SUPPORT_GNSS)
+ gps_data_t send_gps_data = {0};
+ gps_data_t *gps_data = getPGPSGlobaldata();
+
+ if (gps_data == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "错误: gps_data == NULL,GPS全局数据获取失败");
+ return;
+ }
+
+ if (gps_data->last_gps_time_ms > 0) {
+ memcpy_s(&send_gps_data, sizeof(gps_data_t), gps_data, sizeof(gps_data_t));
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS时间戳无效,跳过本次处理");
+ return;
+ }
+
+ // 判断是否定位成功并设置连接状态
+ bool isGpsConnected = (send_gps_data.status > 1);
+ TraingModel::GetInstance()->SetGPSConnectState(isGpsConnected);
+
+ // 判断是否在运动或训练已结束
+ int train_status = uapi_dev_runtime_get_train_status();
+ ExerciseState sport_state = TraingModel::GetInstance()->GetSportState();
+
+ if (train_status == 0 || sport_state == ExerciseState::End) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "运动已结束或未开始,跳过GPS数据添加");
+ return;
+ }
+
+ // 如果定位成功,添加经纬度数据
+ if (isGpsConnected) {
+ uint32_t time = TraingModel::GetInstance()->get_current_time();
+ TraingModel::GetInstance()->AddGPSData(time, send_gps_data.location.lat,
+ send_gps_data.location.lng);
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定位失败,跳过数据添加");
+ }
+ #endif
+ }
+
+ /*
+ * @brief 位置服务定时器回调函数
+ */
+ static void gnss_timer_callback(int data) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "gnss_timer_callback %d", data);
+ GraphicService::GetInstance()->PostGraphicEvent(
+ std::bind(gnss_timer_callbackProc, data));
+ }
+
+ /*
+ * @brief 开启位置服务定时器函数
+ * @param interval 时间间隔,单位ms
+ */
+ void TraingModel::gnss_start_timer(uint32_t interval) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS定时器启动流程开始 ===");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "定时器间隔: %u ms", interval);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "当前定时器状态: %p",
+ g_location_timer);
+
+ /* -------------------------- 1.检查位置服务定时器是否已经存在,如果存在则返回
+ * ------------------------- */
+ if (g_location_timer != nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: GPS定时器已存在,跳过创建");
+ return;
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器不存在,开始创建...");
+
+ /* --------------------------------- 2.创建位置服务定时器
+ * -------------------------------- */
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "调用osTimerNew创建GPS定时器...");
+ g_location_timer = osTimerNew((osTimerFunc_t)gnss_timer_callback,
+ osTimerPeriodic, nullptr, nullptr);
+ // 检查定时器创建是否成功
+ if (g_location_timer == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: GPS定时器创建失败!!");
+ return;
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器创建成功: %p",
+ g_location_timer);
+
+ /* ----------------------------- 3.将毫秒转换为系统时钟节拍数
+ * ---------------------------- */
+ int tick = osMs2Tick(interval);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "时间转换: %u ms -> %d ticks",
+ interval, tick);
+
+ /* --------------------------------- 4.启动位置服务定时器
+ * -------------------------------- */
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "启动GPS定时器...");
+ osStatus_t ret = osTimerStart(g_location_timer, tick);
+ /* -------------------- 5.检查位置服务定时器启动是否成功,如果启动失败,删除定时器并置为
+ * nullptr -------------------- */
+ if (ret != osOK) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "错误: GPS定时器启动失败, ret = %lx!!", ret);
+ if (g_location_timer != nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "删除失败的GPS定时器...");
+ ret = osTimerDelete(g_location_timer);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器删除结果: %lx", ret);
+ }
+ g_location_timer = nullptr;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "=== GPS定时器启动流程结束(失败) ===");
+ return;
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器启动成功");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS定时器启动流程结束(成功) ===");
+ }
+
+ /*
+ * @brief 停止位置服务定时器
+ */
+ void TraingModel::gnss_stop_timer(void) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS定时器停止流程开始 ===");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "当前定时器状态: %p",
+ g_location_timer);
+
+ // 检查位置服务定时器是否存在
+ if (g_location_timer != nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器存在,开始停止流程...");
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "停止GPS定时器...");
+ osTimerStop(g_location_timer);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器已停止");
+
+ // 删除定时器并置为 nullptr
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "删除GPS定时器...");
+ osStatus_t ret = osTimerDelete(g_location_timer);
+ if (ret != osOK) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "错误: GPS定时器删除失败, ret = %lx!!", ret);
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器删除成功");
+ }
+ g_location_timer = nullptr;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器指针已置空");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS定时器停止流程结束 ===");
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器不存在,跳过停止流程");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "=== GPS定时器停止流程结束(无需停止) ===");
+ }
+ gps_timer_count = 0;
+ }
+
+ ///////////////////////////////////////运动数据采集定时器//////////////////////////////////
+ /*
+ * 运动数据采集定时器说明:
+ * - 功能:负责获取运动数据(步数、距离、卡路里、心率等),更新训练目标完成度
+ * - 频率:300ms间隔,保证运动数据的实时性和准确性
+ * - 触发条件:仅在训练进行中且训练状态非结束时执行
+ * - 包含服务:CYWEE运动数据、PPG心率数据、训练目标检查、UI更新
+ */
+ #if SPORT_TIMER_DATA
+ // 声明运动数据采集定时器变量
+ static osTimerId_t g_sport_start_data_timer = nullptr;
+ static int sport_start_data_timer_interval = 300; // 运动数据获取间隔,单位毫秒
+
+ /*
+ * @brief 获取运动基础数据
+ * @return 是否成功获取数据
+ */
+ bool TraingModel::GetSportActivityData(void) {
+ #ifdef SUPPORT_CYWEE_SERVICE
+ act_result_t cyweeInfo;
+ cywee_get_act_data(&cyweeInfo);
+ // 更新运动基础数据
+ TraingModel::GetInstance()->AnalysisData(&cyweeInfo);
+ // 更新其他目标完成度
+ TraingModel::GetInstance()->CheckSportTag();
+ #endif
+ return true;
+ }
+
+ /*
+ * @brief 获取运动额外数据
+ * @return 是否成功获取数据
*/
-
- std::string tagStr;
-
- switch (sportTagType_) {
- case ExerciseTag::Duration:
- tagStr = std::to_string(sportTag_) + " min"; // 时长目标
- break;
-
- case ExerciseTag::Calories:
- tagStr = std::to_string(sportTag_) + " kcal"; // 卡路里目标
- break;
-
- case ExerciseTag::Steps:
- case ExerciseTag::Distance:
- if (isspecialsport_) // 检查是否为特殊训练
- {
- // 只在特殊训练模式下使用步数和距离目标
- if (sportTagType_ == ExerciseTag::Steps) {
- tagStr = std::to_string(sportTag_) + " steps"; // 步数目标
- } else {
- tagStr = std::to_string(sportTag_) + " km"; // 距离目标
- }
- } else {
- tagStr = ""; // 普通训练下不使用目标
- }
- break;
-
- default:
- tagStr = ""; // 未知目标类型
- break;
- }
-
- return tagStr;
-}
-/*设置运动状态*/
-void TraingModel::SetSportState(ExerciseState state) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, " SetSportState %d", state);
- if (state != ExerciseState::General) {
- /*app发起的运动*/
- if (Openingway == true) {
- if (state == ExerciseState::Start) {
- sportdata->duration = 0;
- }
-#ifdef SUPPORT_BLE_SERVICE
- sendtraningstatedata(static_cast<uint8_t>(state), sportdata->duration);
-#endif
- }
- }
- sportState_ = state;
-#ifdef SYSTEM_LOG_SERVICE
- // 记录运动状态
- if (state == ExerciseState::End) {
- uapi_save_runtime_log(RUNTIME_LOG_SPORT_END);
- } else if (state == ExerciseState::Continue) {
- uapi_save_runtime_log(RUNTIME_LOG_SPORT_RESUME);
- } else if (state == ExerciseState::Pause) {
- uapi_save_runtime_log(RUNTIME_LOG_SPORT_PAUSE);
- }
-#endif
-}
-/*获取运动状态*/
-ExerciseState TraingModel::GetSportState() { return sportState_; }
-// 计算平均心率的函数
-double CalculateAverageHeartRate(const std::vector<int> &heartRates) {
- if (heartRates.size() <= 2) {
- // 如果心率数据点少于等于2,无法去掉最高和最低值
- return 0.0; // 或者返回心率的平均值
- }
-
- // 创建一个副本以避免修改原始数据
- std::vector<int> sortedHeartRates = heartRates;
-
- // 排序
- std::sort(sortedHeartRates.begin(), sortedHeartRates.end());
-
- // 去掉一个最高值和一个最低值
- sortedHeartRates.erase(sortedHeartRates.begin()); // 去掉最低值
- sortedHeartRates.pop_back(); // 去掉最高值
-
- // 计算剩余心率的平均值
- double sum = 0;
- for (int rate : sortedHeartRates) {
- sum += rate;
- }
-
- return sum / sortedHeartRates.size(); // 返回平均值
-}
-#ifdef SUPPORT_CYWEE_SERVICE
-/*分析数据*/
-void TraingModel::AnalysisData(act_result_t *cyweeInfo) {
- if (cyweeInfo == NULL)
- return;
-
- // 不同的运动类型解析不同的数据
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "AnalysisData:[steps %d, distance %d,calories %d, pace %d, "
- "step_frequency %d, step_length %d]",
- cyweeInfo->steps, cyweeInfo->distance, cyweeInfo->calories,
- cyweeInfo->pace, cyweeInfo->step_frequency,
- cyweeInfo->step_length);
- // 通用数据
- sportdata->steps = cyweeInfo->steps;
- sportdata->distance = cyweeInfo->distance;
- sportdata->calories = cyweeInfo->calories;
-
- // sportdata->cdc = cyweeInfo->step_frequency;
- // sportdata->std = cyweeInfo->step_length;
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, " pace %d", sportdata->pc);
-
- sportdata->pc = cyweeInfo->pace;
-
- // 更新最慢配速
- if (sportdata->spc < sportdata->pc && sportdata->pc != 0) {
- sportdata->spc = sportdata->pc;
- }
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "spc %d", sportdata->spc);
-
- // 计算平均心率
- if (sportdata->heart > 50) {
- sportdata->avgheart = Averageheartrate(sportdata->heart);
- }
-
- // 心率提醒
- if (sportdata->heart >= user_maxHeart && user_maxHeart != 0) {
- uapi_set_screen_on_api(); // 亮屏检测
- if (true != OHOS::ConfigModel::GetInstance()->GetDnotdisturb() &&
- (!uapi_sleep_mode_flag_get())) {
- custom_VibratorFunc_int(0);
- }
- if (TraingCardView::GetInstance()) {
- GraphicService::GetInstance()->PostGraphicEvent(
- std::bind(&TraingCardView::SetPopupViewStatus,
- TraingCardView::GetInstance(), HeartPopup, true));
- if (TraingCardView::GetInstance()->GetHeartMaxLabel()) {
- GraphicService::GetInstance()->PostGraphicEvent(
- std::bind(&TraingCardView::SetHeartMaxLabel,
- TraingCardView::GetInstance(), user_maxHeart));
- }
- }
- }
-}
-#endif
-
-#if 0
- int avg_step_freq;//平均步频
- int avg_step_len;//平均步长
- int avg_pace;//平均配速
- int max_step_freq;//最大步频
- int max_pace;//最大配速
- int activity_zone;//活动程度区间 0低度 1中度 2高度
- int ativity_zone_moderate_duration;//中度活动时间
- int ativity_zone_high_duration;//高度活动时间
- int MHR_zone;//心率区间
- int distance_motion;//体感距离
-#endif
-
-/*分析activity extra info数据*/
-#ifdef SUPPORT_CYWEE_SERVICE
-void TraingModel::AnalysisExtraData(act_ext_result_t *cyweeInfo) {
-
- //判断是否为特殊训练
- if (isspecialsport_ == false) {
- return;
- }
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "[apc:%d fpc:%d cdc:%d std:%d]",
- cyweeInfo->avg_pace, cyweeInfo->max_pace,
- cyweeInfo->avg_step_freq, cyweeInfo->avg_step_len);
- //平均配速
- if (cyweeInfo->avg_pace != 0) {
- sportdata->apc = cyweeInfo->avg_pace;
- }
- //最快配速
- if (cyweeInfo->max_pace != 0) {
- sportdata->fpc = cyweeInfo->max_pace;
- }
- //平均步频
- if (cyweeInfo->avg_step_freq != 0) {
- sportdata->cdc = cyweeInfo->avg_step_freq;
- }
- //平均步长
- if (cyweeInfo->avg_step_len != 0) {
- sportdata->std = cyweeInfo->avg_step_len * 100; // 前者cm 后者m
- }
-}
-#endif
-/*分析运动心率数据*/
-void TraingModel::AnalysisHeartData(uint8_t hrs) {
- sportdata->heart = hrs;
- SetIntensity(sportdata->heart); // 设置训练强度
-}
-
-/*打印函数*/
-void TraingModel::printf_data(SportData *data) {
- /*打印测试一下数据*/
- printf("\r\n****************************************\r\n");
- printf("Sport type: %d\n", data->sportType);
- printf("Sport ID: %d\n", data->sportId);
- printf("Start date: %s\n", data->start_date.c_str());
- printf("Start time: %s\n", data->start_time.c_str());
- printf("End time: %s\n", data->end_time.c_str());
- printf("Goal degree: %d\n", data->goaldegree);
- printf("Duration: %d\n", data->duration);
- printf("Distance: %d\n", data->distance);
- printf("Average heart rate: %d\n", data->avgheart);
- printf("Steps: %d\n", data->steps);
- printf("Intensity: %d\n", data->intensity);
- printf("\r\n****************************************\r\n");
-}
-/*获取数据*/
-SportData *TraingModel::GetSportdata() {
- // printf_data(sportdata);
- return sportdata;
-}
-/* 设置训练数据[为训练记录准备] */
-void TraingModel::SetSportdata(SportData *data) {
- if (data != nullptr && sportdata != nullptr) {
- sportdata->sportType = data->sportType;
- sportdata->sportId = data->sportId;
- sportdata->start_date = data->start_date;
- sportdata->start_time = data->start_time;
- sportdata->end_time = data->end_time;
- sportdata->goaldegree = data->goaldegree;
- sportdata->duration = data->duration;
- sportdata->distance = data->distance;
- sportdata->avgheart = data->avgheart;
- sportdata->steps = data->steps;
- sportdata->intensity = data->intensity;
- sportdata->calories = data->calories;
- sportdata->apc = data->apc;
- sportdata->fpc = data->fpc;
- sportdata->spc = data->spc;
- sportdata->std = data->std;
- sportdata->cdc = data->cdc;
- sportdata->starttime = data->starttime; //更新开始时间
- printf_data(data); //打印该运动数据
- IsSpecialSport(sportdata->sportId); // 配置训练特殊性
- }
-}
-/*获取训练卡路里*/
-int TraingModel::GetCal() { return sportdata->calories; }
-/*获取训练目标*/
-int TraingModel::GetSportTag() { return sportdata->tag; }
-/*获取训练项目类型*/
-ExerciseTag TraingModel::GetSportTagtype() { return sportdata->sportTagType; }
-/*重置训练配置*/
-void TraingModel::ResetSharedTrainingData() {
- sportdata->Reset(); // 重置结构体
- heartRateData.clear(); // 清空心率数据
-
- ClearPaceTable(); //重置配速数据
- ClearHeartTable(); // 清空心率数据
-
- recentHeartRates.clear();
- delayed.clear(); // 重置 delayed 字典
-
- while (!maxHeap.empty()) { // 清空最大堆
- maxHeap.pop();
- }
- while (!minHeap.empty()) { // 清空最小堆
- minHeap.pop();
- }
-
- totalHeartRate = 0;
- countHeartRate = 0;
- heartcount = 0;
-
- Gettagstuats = false; // 重置训练达标标志位
- Openstate = true; // 开启运动开始标志位
-
- shock_count = 0; //重置震动计数
-
- gpsData.clear(); // 清空gps数据
- canvasData.clear(); // 清空画布数据
-}
-
-uint32_t TraingModel::get_current_time() {
- uint32_t time;
- if (uapi_get_timestampe_ss(&time) != ERRCODE_SUCC) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to get timestamp");
- return 0;
- }
- return time;
-}
-
-/*设置运动开始与结束*/
-void TraingModel::SetTimestamp() {
- uint32_t time;
- if (uapi_get_timestampe_ss(&time) != ERRCODE_SUCC) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to get timestamp");
- return;
- }
-
- ExerciseState currentState = GetSportState(); // 获取当前运动状态
-
- // 增加标志位互斥保障"开始"和"结束"的唯一性操作
- if (currentState == ExerciseState::Start && !Startstate) {
- // 设置训练 runtime 为 1 训练中
- uapi_dev_runtime_set_train_status(1);
- if (Openingway) {
- // uapi_ble_connect_param_update(BLE_CONNECT_INTERVAL_HIGH);
- }
-
- // 更新用户数据
- SetUserSportdata();
-
- ResetSharedTrainingData(); // 重置共享训练数据
-
- // printf("SetTimestamp::time:%u \r\n", time);
- sportdata->starttime = time;
-
- sportdata->sportType = sportType_; // 训练类型
- sportdata->sportId = sportId_;
-
- IsSpecialSport(sportdata->sportId); // 配置运动特殊性状态
-
- if (sportTag_) // 有值才设置[训练目标]
- {
- sportdata->sportTagType = sportTagType_;
- sportdata->tag = sportTag_;
- }
-
- current_goal = GetCurrentSportTag();
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "训练目标:类型: %d 值: %d 当前目标值:%d", sportTagType_,
- sportTag_, current_goal);
-
- // 设置起始日期和时间
- SetStartDateTime();
-
- Startstate = true;
- Endstate = false; // 确保"开始"和"结束"状态互斥
-
- //打印目前运动配置项
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "*******************************");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始运动");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportType:%d", sportdata->sportType);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportId:%d", sportdata->sportId);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportTagType:%d", sportdata->sportTagType);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportTag:%d", sportdata->tag);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "*******************************");
-
-
- sport_start_data_timer(300); //开启数据获取定时器 刷新频率是300ms
-
-#ifdef SYSTEM_LOG_SERVICE
- //日志记录
- if (Openingway) {
- uapi_save_runtime_log(RUNTIME_LOG_APP_SPORT_LAUNCH); // app发起运动
- } else {
- uapi_save_runtime_log(RUNTIME_LOG_DIAL_SPORT_LAUNCH); //手表发起运动
- }
- if (isspecialsport_) {
- uapi_save_runtime_log(RUNTIME_LOG_SPORT_SPECIAL_START); //特殊运动
- } else {
- uapi_save_runtime_log(RUNTIME_LOG_SPORT_NORMAL_START); //普通运动
- }
-#endif
-
- } else if (currentState == ExerciseState::End && !Endstate) {
- sport_stop_data_timer(); //停止数据获取定时器
-
- // 设置训练 runtime 为 0 训练结束
- uapi_dev_runtime_set_train_status(0);
- if (Openingway) {
- // uapi_ble_connect_param_update(BLE_CONNECT_INTERVAL_SLOW);
- }
-
- sportdata->endtime = time;
-
- SetEndTime();
-
- sportState_ = ExerciseState::General;
- Openstate = false; // 关闭运动标志位
-
- // 记录运动数据
- // RecordSportData();
-
- //配置最后的训练强度(平均心率为标准)
- SetIntensity(sportdata->avgheart);
-
- //更新gps点位
- GetPointData();
-
- do_custom_fileoperate_service_check_msg(
- CUSTOM_FILEOPERATE_MSG_TYPE_SPORTDATA_SAVE, nullptr, 0);
-
- Startstate = false;
- Endstate = true;
-
- stop_heart_gps(); // 停止心率和gps数据采集
- gnss_stop_timer(); // 停止gps定时器
-
-#if 0
- // 重置训练目标 [发生在训练前]
- sportTag_ = 0;
- sportTagType_ = ExerciseTag::UNKNOW;
-#endif
- /*运动结束并且是手表发起的运动*/
- if (Openingway == false) {
- sendtraningstateend();
- }
- /*运动结束并且是app发起的运动*/
- else {
- sendtraningstatedata(0, sportdata->duration);
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "结束运动");
- }
-}
-/*设置开始时间*/
-void TraingModel::SetStartDateTime() {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->[%d]\r\n",
- sportdata->duration);
- std::string startDate, startTime;
- if (!GetFormattedDate(startDate, "Failed to get start date"))
- return;
- if (!GetFormattedTime(startTime, "Failed to get start time"))
- return;
-
- sportdata->start_date = startDate;
- sportdata->start_time = startTime;
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->start_time [%s]\r\n",
- sportdata->start_time.c_str());
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->[%d]\r\n",
- sportdata->duration);
-}
-/*设置结束时间*/
-void TraingModel::SetEndTime() {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->[%d]\r\n",
- sportdata->duration);
- std::string endTime;
- if (GetFormattedTime(endTime, "Failed to get end time")) {
- sportdata->end_time = endTime;
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->end_time [%s]\r\n",
- sportdata->end_time.c_str());
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->[%d]\r\n",
- sportdata->duration);
-}
-/*获取日期字符串*/
-bool TraingModel::GetFormattedDate(std::string &dateOutput,
- const char *errorMsg) {
- char *date = (char *)malloc(9 * sizeof(char)); // 分配内存给start_date
- if (uapi_get_YYMMDD_Time(date, '/') != ERRCODE_SUCC) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%s", errorMsg);
- free(date);
- return false;
- }
- dateOutput = std::string(date);
- free(date); // 释放内存
- return true;
-}
-/*获取时间字符串*/
-bool TraingModel::GetFormattedTime(std::string &timeOutput,
- const char *errorMsg) {
- char *time = (char *)malloc(9 * sizeof(char)); // 分配内存给start_time
- if (uapi_get_HHMMSS_Time(time, ':', true) != ERRCODE_SUCC) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%s", errorMsg);
- free(time);
- return false;
- }
- timeOutput = std::string(time);
- free(time); // 释放内存
- return true;
-}
-/*保存数据*/
-void TraingModel::RecordSportData() {
-#if READTEST
- if (SaveSportData() == false) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveSportData error!");
- }
-#else
- if (MomentumFlag) {
- // GetGoaldegree(); // 设置目标完成度
- if (SaveSportData()) {
- TraingRecordModel::GetInstance().SetJsonStatus(true);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveSportData ok!");
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveSportData error!");
- }
- }
-#endif
-}
-// 获取训练时长时间
-std::string TraingModel::GetTrainingTime() {
- int hour = sportdata->duration / NUM_TIME_HOUR;
- int min = (sportdata->duration % NUM_TIME_HOUR) / NUM_TIME_MIN;
- int sec = (sportdata->duration % NUM_TIME_HOUR) % NUM_TIME_MIN;
-
- std::ostringstream oss;
- oss << std::setfill('0') << std::setw(2) << hour << ":" << std::setw(2) << min
- << ":" << std::setw(2) << sec;
-
- std::string triningtime = oss.str();
- return triningtime;
-}
-/*解析时间戳 返回例如:13:00:22*/
-std::string TraingModel::AnalysisTimestamp(uint64_t timestamp) {
- // 将时间戳转换为小时、分钟、秒
- int hours = timestamp / 3600000; // 1小时 = 3600000毫秒
- int minutes = (timestamp % 3600000) / 60000; // 1分钟 = 60000毫秒
- int seconds = (timestamp % 60000) / 1000; // 1秒 = 1000毫秒
-
- // 将小时、分钟、秒转换为字符串
- std::string hoursStr = std::to_string(hours);
- std::string minutesStr = std::to_string(minutes);
- std::string secondsStr = std::to_string(seconds);
-
- // 格式化输出为 HH:MM:SS
- std::stringstream ss;
- ss << std::setfill('0') << std::setw(2) << hoursStr << ":" << std::setw(2)
- << minutesStr << ":" << std::setw(2) << secondsStr;
-
- return ss.str();
-}
-int TraingModel::CalculateAverageHeartRate(int newHeartRate) {
- // 将新的心率添加到数据列表
- heartRateData.push_back(newHeartRate);
- totalHeartRate += newHeartRate;
- countHeartRate++;
-
- // 将新的心率插入到堆中
- maxHeap.push(newHeartRate);
- minHeap.push(newHeartRate);
-
- // 如果超过窗口大小,移除最早的心率
- if (heartRateData.size() > windowSize) {
- int oldestRate = heartRateData[0];
- totalHeartRate -= oldestRate;
- countHeartRate--;
-
- // 将老旧值从列表中移除
- heartRateData.erase(heartRateData.begin());
-
- // 标记老旧值为待删除
- delayed[oldestRate]++;
- }
-
- // 清理堆中的老旧值
- while (!maxHeap.empty() && delayed[maxHeap.top()] > 0) {
- delayed[maxHeap.top()]--;
- maxHeap.pop();
- }
-
- while (!minHeap.empty() && delayed[minHeap.top()] > 0) {
- delayed[minHeap.top()]--;
- minHeap.pop();
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "新心率: %d, 当前心率总和: %d, 心率计数: %d", newHeartRate,
- totalHeartRate, countHeartRate);
-
- // 如果心率数量少于 3,直接返回当前的平均值
- if (countHeartRate < 3) {
- return totalHeartRate / countHeartRate; // 无法去除最大最小值
- }
-
- // 取出当前未被删除的最大和最小的心率
- int maxRate = maxHeap.top();
- int minRate = minHeap.top();
-
- // 进行去极值后的平均计算
- int average = (totalHeartRate - maxRate - minRate) / (countHeartRate - 2);
-
- // 打印计算结果
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "去极值后的平均心率: %d (去掉最大值: %d, 去掉最小值: %d)",
- average, maxRate, minRate);
-
- return average;
-}
-// 显示心率
-int TraingModel::CalculateFilteredHeartRate(int newHeartRate) {
- // 将新的心率添加到最近心率列表
- recentHeartRates.push_back(newHeartRate);
-
- // 如果超过窗口大小,移除最早的心率
- if (recentHeartRates.size() > windowSize) {
- recentHeartRates.erase(recentHeartRates.begin());
- }
-
- // 计算当前窗口中的平均心率
- int total =
- std::accumulate(recentHeartRates.begin(), recentHeartRates.end(), 0);
-
- // 打印当前窗口的平均心率
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "过滤后当前窗口的平均心率: %d",
- (total / recentHeartRates.size()));
-
- return total / recentHeartRates.size(); // 返回计算后的平均值
-}
-int TraingModel::Averageheartrate(int newHeartRate) {
- heartcount++;
- totalHeartRate += newHeartRate;
- return totalHeartRate / heartcount; // 返回计算的平均值
-}
-// 设置训练目标类型以及训练目标
-void TraingModel::SetSportTag(ExerciseTag tagtpye, int tag) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "设置目标 tagtpye[%d] tag[%d]",
- tagtpye, tag);
- sportTagType_ = tagtpye;
- sportTag_ = tag;
-}
-
-/* 设置训练强度 */
-void TraingModel::SetIntensity(int heart) {
- // sportdata->intensity = active_state;
- /*
- 训练强度
- 220-年龄=最高心率
-
- 分为四个强度区间
- 较低强度=最高心率*59%以下
- 中等强度=最高心率*60%~69%
- 高等强度=最高心率*70%~79%
- 超高强度=最高心率*80%以上
+ bool TraingModel::GetSportExtraData(void) {
+ #ifdef SUPPORT_CYWEE_SERVICE
+ act_ext_result_t cyweeInfo_ext;
+ cywee_get_act_ext_data(&cyweeInfo_ext);
+ TraingModel::GetInstance()->AnalysisExtraData(&cyweeInfo_ext);
+ #endif
+ return true;
+ }
+
+ /*
+ * @brief 获取心率数据
+ * @return 获取到的心率值,如果服务不支持则返回0
*/
- // 确保最大心率为正值
- if (user_Intensity <= 0) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "maxHeartRate %d is error",
- user_Intensity);
- return;
- }
-
- // 将 heart 转换为 double 以避免整数除法
- double heartRateAsDouble = static_cast<double>(heart);
- double intensityPercentage =
- (heartRateAsDouble / (double)user_Intensity) * 100;
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "heart: %d, maxHeartRate: %d, intensityPercentage: %.2f", heart,
- HeartModel, intensityPercentage);
-
- if (intensityPercentage < LOW_INTENSITY_THRESHOLD) {
- // 较低强度
- sportdata->intensity = LOW_INTENSITY;
- } else if (intensityPercentage >= MEDIUM_INTENSITY_LOWER_THRESHOLD &&
- intensityPercentage <= MEDIUM_INTENSITY_UPPER_THRESHOLD) {
- // 中等强度
- sportdata->intensity = MEDIUM_INTENSITY;
- } else if (intensityPercentage >= HIGH_INTENSITY_LOWER_THRESHOLD &&
- intensityPercentage <= HIGH_INTENSITY_UPPER_THRESHOLD) {
- // 高等强度
- sportdata->intensity = HIGH_INTENSITY;
- } else {
- // 超高强度
- sportdata->intensity = VERY_HIGH_INTENSITY;
- }
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "maxHeartRate: %d, heartRate: %d, intensityPercentage: %.2f",
- maxHeartRate, heart, intensityPercentage);
-}
-/*获取训练强度STR*/
-UIResourceTextId TraingModel::GetIntensity() {
- switch (sportdata->intensity) {
- case LOW_INTENSITY:
- return UIResourceTextId::STR_LOWER_STRENGTH;
- break;
- case MEDIUM_INTENSITY:
- return UIResourceTextId::STR_MEDIUM_STRENGTH;
- break;
- case HIGH_INTENSITY:
- return UIResourceTextId::STR_HIGH_STRENGTH;
- break;
- case VERY_HIGH_INTENSITY:
- return UIResourceTextId::STR_ULTRA_HIGH_STRENGTH;
- break;
- default:
- return UIResourceTextId::STR_LOWER_STRENGTH;
- break;
- }
-}
-
-/*
-时长:10~600分钟 实际出值 秒
-距离:1~20km 实际出值 m
-步数:500~50000步 实际出值 步
-卡路里:100~1000kcal 实际出值 kcal
-*/
-
-/* 获取当前的目标值 */
-int TraingModel::GetSportTagData() {
- if (sportdata->tag == 0 || current_goal == 0) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
- return 0;
- }
-
- float goaldegree = 0.0f; // 初始化目标完成度
-
- // 根据设置的训练目标类型去对应相对的数据
- switch (sportdata->sportTagType) {
- case ExerciseTag::Duration: {
- // 计算完成度
- goaldegree = (static_cast<float>(sportdata->duration) / current_goal) * 100;
- break;
- }
- case ExerciseTag::Calories: {
- // 计算目标完成度
- goaldegree = (static_cast<float>(sportdata->calories) / current_goal) * 100;
- break;
- }
- case ExerciseTag::Distance: {
- // 计算目标完成度
- goaldegree = (static_cast<float>(sportdata->distance) / current_goal) * 100;
- break;
- }
- case ExerciseTag::Steps: {
- // 计算目标完成度
- goaldegree = (static_cast<float>(sportdata->steps) / current_goal) * 100;
- break;
- }
- default:
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->sportTagType is error");
- return 0;
- break;
- }
-
- WEARABLE_LOGD(
- WEARABLE_LOG_MODULE_APP,
- " [goaldegree %d, sportdata->tag %d, sportdata->duration %d, "
- "sportdata->calories %d,sportdata->distance %d, sportdata->steps %d]",
- goaldegree, sportdata->tag, sportdata->duration, sportdata->calories,
- sportdata->distance, sportdata->steps);
-
- return static_cast<int>(round(goaldegree)); // 返回目标完成度
-}
-
-//获取目标值
-uint32_t TraingModel::GetCurrentSportTag() {
- if (sportdata->tag == 0) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
- return 0;
- }
-
- float goaldegree = 0.0f; // 初始化目标完成度
-
- // 根据设置的训练目标类型去对应相对的数据
- switch (sportdata->sportTagType) {
- case ExerciseTag::Duration: {
- // 转换目标时长为秒
- return static_cast<float>(sportdata->tag * 60);
- break;
- }
- case ExerciseTag::Calories: {
- // 计算目标完成度
- return sportdata->tag;
- break;
- }
- case ExerciseTag::Distance: {
- return static_cast<float>(sportdata->tag * 1000);
- break;
- }
- case ExerciseTag::Steps: {
- return sportdata->tag;
- break;
- }
- default:
- return 0;
- break;
- }
-}
-//获取是否达到目标 使用比较
-bool TraingModel::IsGetGoaldegree() {
- if (sportdata->tag == 0) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
- return false;
- }
-
- float goaldegree = 0.0f; // 初始化目标完成度
-
- // 根据设置的训练目标类型去对应相对的数据
- switch (sportdata->sportTagType) {
- case ExerciseTag::Calories: {
- // 计算目标完成度
- if (sportdata->calories >= current_goal) {
- return true;
- }
- return false;
- break;
- }
- case ExerciseTag::Distance: {
- if (sportdata->distance >= current_goal) {
- return true;
- }
- return false;
- break;
- }
- case ExerciseTag::Steps: {
- if (sportdata->steps >= current_goal) {
- return true;
- }
- return false;
- break;
- }
- default:
- return false;
- break;
- }
-}
-
-/* 获取训练目标完成百分比 */
-int TraingModel::GetGoaldegree() {
- if (sportdata->tag == 0) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
- return 0; // 如果目标值为 0,返回 0%
- }
-
- int goaldegree = GetSportTagData();
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "goaldegree %d", goaldegree);
- return goaldegree; // 直接返回目标完成度
-}
-
-void TraingModel::updata_goal() {
- if (sportdata->tag == 0)
- return;
-
- sportdata->goaldegree = GetGoaldegree();
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->goaldegree %d",
- sportdata->goaldegree);
-}
-
-/* 训练目标校验 */
-void TraingModel::CheckSportTag() {
- // 运动开始才计算
- if (sportdata->tag && sportdata->sportTagType != ExerciseTag::Duration) {
- if (IsGetGoaldegree() && !Gettagstuats) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "目标完成");
- uapi_set_screen_on_api(); // 亮屏检测
- if (!OHOS::ConfigModel::GetInstance()->GetDnotdisturb()) {
- custom_VibratorFunc_int(0); // 震动提示
- }
- if (TraingCardView::GetInstance()) {
- GraphicService::GetInstance()->PostGraphicEvent(
- std::bind(&TraingCardView::SetPopupViewStatus,
- TraingCardView::GetInstance(), GetTagPopup, true));
- auto animator = TraingCardView::GetInstance()->GetAnimator();
- if (animator) {
- animator->Start(); // 启动动画
- }
- }
- Gettagstuats = true; // 更新状态
- }
- }
-}
-// 校验时间获取目标
-void TraingModel::CheckTimeTag() {
- if (sportdata->tag == 0 || sportdata->sportTagType != ExerciseTag::Duration) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
- return;
- }
- if (sportdata->duration == current_goal && !Gettagstuats) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "目标完成");
- uapi_set_screen_on_api(); // 亮屏检测
- if (!OHOS::ConfigModel::GetInstance()->GetDnotdisturb()) {
- custom_VibratorFunc_int(0); // 震动提示
- }
- if (TraingCardView::GetInstance()) {
- GraphicService::GetInstance()->PostGraphicEvent(
- std::bind(&TraingCardView::SetPopupViewStatus,
- TraingCardView::GetInstance(), GetTagPopup, true));
- auto animator = TraingCardView::GetInstance()->GetAnimator();
- if (animator) {
- animator->Start(); // 启动动画
- }
- }
- Gettagstuats = true; // 更新状态
- }
-}
-
-//获取运动时长
-int TraingModel::GetDuration() { return sportdata->duration; }
-/*获取训练类型Id*/
-UIResourceTextId TraingModel::GetSportTypeId(Exercisetype type) {
- // 根据设置的训练类型去对应相对的数据
- switch (type) {
- // 健身类
- case Exercisetype::FITNESS:
- return STR_EXERCISE_LIST;
- // 舞蹈类
- case Exercisetype::DANCE:
- return STR_DANCING_LIST;
- // 休闲运动
- case Exercisetype::LEISURE:
- return STR_RECREATIONAL_LIST;
- // 球类
- case Exercisetype::BALL:
- return STR_BALL_LIST;
- // 水上运动
- case Exercisetype::WATER:
- return STR_WATER_LIST;
- // 冰上运动
- case Exercisetype::ICE:
- return STR_ICE_LIST;
- // 极限运动
- case Exercisetype::EXTREME:
- return STR_EXTREME_LIST;
- // 其他
- case Exercisetype::OTHER:
- return STR_OTHER_LIST;
- default:
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Not ExerciseClass type!");
- break;
- }
-
- return STR_EXERCISE_LIST; // 添加默认返回值[健身类]
-}
-/*打印存储数据*/
-void TraingModel::printTrainingData(const Trainingdata &data) {
- printf("Trainingdata:\n");
- printf("src: %d\n", data.src);
- printf("bt: %u\n", data.bt);
- printf("ty: %u\n", data.ty);
- printf("st: %u\n", data.st);
- printf("sp: %u\n", data.sp);
- printf("tgt: %u\n", data.tgt);
- printf("tg: %u\n", data.tg);
- printf("pg: %u\n", data.pg);
- printf("du: %u\n", data.du);
- printf("ca: %u\n", data.ca);
- printf("di: %u\n", data.di);
- printf("hr: %u\n", data.hr);
- printf("stp: %u\n", data.stp);
- printf("ti: %u\n", data.ti);
- printf("cir: %u\n", data.cir);
- printf("alt: %u\n", data.alt);
-}
-
-/*
- uint32_t src; // 训练来源:0 - 手表开启的训练,1 - APP开启的训练
- uint32_t bt; // 运动类型:0 - 健身类,1 - 舞蹈类,2 - 休闲运动,3 - 球类,4
- - 水上项目,5 - 冰上项目,6 - 极限运动,7 - 其他 uint32_t ty; //
- 训练类型:自由训练 uint32_t st; // 开始时间戳(单位:秒) uint32_t sp; //
- 结束时间戳(单位:秒) uint32_t tgt; // 训练目标类型:0 - 距离,1 - 卡路里,2
- - 时间,3 - 步数 uint32_t tg; //
- 训练目标值:路程(单位:米),卡路里(单位:kcal),时间(单位:分钟),步数(单位:步)
- uint32_t pg; // 目标完成度(单位:%)
- uint32_t du; // 训练总时长(单位:秒)
- uint32_t ca; // 消耗卡路里(单位:kcal)
- uint32_t di; // 完成的距离(单位:米)
- uint32_t hr; // 平均心率
- uint32_t stp; // 步数
- uint32_t ti; // 训练强度:0 - 较低强度,1 - 中等强度,2 - 高等强度,3 -
- 超高强度 uint32_t cir; // 圈数 uint32_t alt; // 最高海拔高度
-*/
-/*保存数据*/
-bool TraingModel::SaveSportData() {
-
- // 配置训练存储状态
- uapi_dev_runtime_set_train_storage_status(1);
-
- Trainingdata data;
- data.src = Openingway;
- data.bt = static_cast<uint32_t>(sportdata->sportType);
- data.ty = static_cast<uint32_t>(GetBleSportId(sportdata->sportType, sportdata->sportId));
- data.st = static_cast<uint32_t>(sportdata->starttime);
- data.sp = static_cast<uint32_t>(sportdata->endtime);
- data.tgt = static_cast<uint32_t>(sportdata->sportTagType);
- if (sportdata->sportTagType == ExerciseTag::Distance) {
- data.tg = static_cast<uint32_t>(sportdata->tag * 1000);
- } else {
- data.tg = static_cast<uint32_t>(sportdata->tag);
- }
- data.pg = static_cast<uint32_t>(sportdata->goaldegree);
- data.du = static_cast<uint32_t>(sportdata->duration);
- data.ca = static_cast<uint32_t>(sportdata->calories * 10);
- data.di = static_cast<uint32_t>(sportdata->distance);
- data.hr = static_cast<uint32_t>(sportdata->avgheart);
- data.stp = static_cast<uint32_t>(sportdata->steps);
- data.ti = static_cast<uint32_t>(sportdata->intensity);
- /* 230 目前不支持【圈数】【最高海拔高度】 */
- data.cir = 0;
- data.alt = 0;
- /* 配速相关 */
- data.apc = static_cast<uint32_t>(sportdata->apc);
- data.fpc = static_cast<uint32_t>(sportdata->fpc);
- data.spc = static_cast<uint32_t>(sportdata->spc);
- data.std = static_cast<uint32_t>(sportdata->std);
- data.cdc = static_cast<uint32_t>(sportdata->cdc);
-
- // 创建sport文件夹
- CreateDirectoryIfNotExists(TRAINING_DATA_DIRECTORY);
-
- // 保存数据到 bin
- if (write_Trainingdata_data(data) != 1) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "write_Trainingdata_data is error!");
- uapi_dev_runtime_set_train_storage_status(0); // 恢复状态
- return false;
- }
-
- // 保存数据到 json
- if (!SaveDataToJson(sportdata)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveDataToJson is error!");
- uapi_dev_runtime_set_train_storage_status(0); // 恢复状态
- return false;
- }
-
- if (isspecialsport_) {
- // 保存心率表
- if (!SaveHeartTable()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveHeartRateData is error!");
- // uapi_dev_runtime_set_train_storage_status(0); // 恢复状态
- // return false;
- }
- // 保存速度表
- if (!SavePaceTable()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveSpeedData is error!");
- // return false;
- }
- // 保存GPS数据
- if (!SaveCanvasData()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveCanvasData is error!");
- return false; //必须手表端存储成功
- }
- if (!SaveGPSData()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData is error!");
- // return false;
- }
- }
-
- // 如果所有操作成功,则恢复训练存储状态
- uapi_dev_runtime_set_train_storage_status(0);
- return true;
-}
-/*设置进入方式*/
-void TraingModel::SetEntryType(ENTEYTYPE type) { entey_type = type; }
-/*获取进入方式*/
-ENTEYTYPE TraingModel::GetEntryType() { return entey_type; }
-/*设置开启方式*/
-void TraingModel::SetSportOpenWay(bool way) { Openingway = way; }
-/*设置运动是否开始*/
-void TraingModel::SetSportOpenState(bool state) { Openstate = state; }
-/*获取运动是否开始*/
-bool TraingModel::GetSportOpenState() { return Openstate; }
-/*获取运动时长*/
-uint32_t TraingModel::GetSportTime() { return sportdata->duration; }
-/*获取训练量是否达标*/
-bool TraingModel::GetMomentum() {
-#if 0
- int caldata = TraingModel::GetInstance()->GetCal();
- /*判断是否产生至少1Kcald的运动量*/
- if (caldata >= 1)
+ uint8_t TraingModel::GetHeartRateData(void) {
+ uint8_t hrs = 0;
+ #ifdef SUPPORT_PPG_SERVICE
+ hrs = ppg_get_hrs();
+ #endif
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Heart rate data: %d", hrs);
+ return hrs;
+ }
+
+ static void sport_data_time_proc(int data) {
+ //增加训练runtime控制
+ if (uapi_dev_runtime_get_train_status() == 0 ||
+ TraingModel::GetInstance()->GetSportState() == ExerciseState::End) {
+ return;
+ }
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sport_data_time_proc");
+
+ // 获取并处理运动基础数据
+ TraingModel::GetInstance()->GetSportActivityData();
+
+ // 获取并处理运动额外数据 前提是特殊运动
+ if (TraingModel::GetInstance()->GetIsSpecial()) {
+ TraingModel::GetInstance()->GetSportExtraData();
+ }
+
+ // 获取并处理心率数据
+ uint8_t heartRate = TraingModel::GetInstance()->GetHeartRateData();
+ TraingModel::GetInstance()->AnalysisHeartData(heartRate);
+
+ // 更新UI
+ if (TraingCardPresenter::GetInstance()) {
+ TraingCardPresenter::GetInstance()->updataview();
+ }
+ }
+
+ static void sport_data_time_callback(int data) {
+ GraphicService::GetInstance()->PostGraphicEvent(
+ std::bind(sport_data_time_proc, data));
+ }
+
+ void TraingModel::sport_start_data_timer(uint32_t interval) {
+ WEARABLE_LOGE(
+ WEARABLE_LOG_MODULE_APP,
+ "TraingModel::sport_start_data_timer - Starting sport data timer");
+ if (g_sport_start_data_timer != nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sport Data Timer already exists!!");
+ return;
+ }
+ g_sport_start_data_timer = osTimerNew((osTimerFunc_t)sport_data_time_callback,
+ osTimerPeriodic, nullptr, nullptr);
+ if (g_sport_start_data_timer == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sport Data Timer create failed!!");
+ return;
+ }
+ int tick = osMs2Tick(interval);
+ osStatus_t ret = osTimerStart(g_sport_start_data_timer, tick);
+ if (ret != osOK) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Sport Data Timer start failed, ret = %lx!!", ret);
+ if (g_sport_start_data_timer != nullptr) {
+ ret = osTimerDelete(g_sport_start_data_timer);
+ }
+ g_sport_start_data_timer = nullptr;
+ return;
+ }
+ }
+
+ void TraingModel::sport_stop_data_timer(void) {
+ // 检查运动数据采集定时器是否存在
+ if (g_sport_start_data_timer != nullptr) {
+ WEARABLE_LOGE(
+ WEARABLE_LOG_MODULE_APP,
+ "TraingModel::sport_stop_data_timer - Stopping sport data timer");
+ osTimerStop(g_sport_start_data_timer);
+ // 删除定时器并置为 nullptr
+ osStatus_t ret = osTimerDelete(g_sport_start_data_timer);
+ if (ret != osOK) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Sport Data Timer delete failed, ret = %lx!!", ret);
+ }
+ g_sport_start_data_timer = nullptr;
+ }
+ }
+ #endif
+
+ #ifdef BLE_INSTRUCT
+
+ #include "bleEvent.h"
+ #include "bleInstruct.h"
+
+ //////////////////////////////ble////////////////////////////////////////////
+ #ifdef __cplusplus
+ #if __cplusplus
+ extern "C" {
+ #endif /* __cplusplus */
+ #endif /* __cplusplus */
+
+ bool get_training_spead_path_(char *path, uint32_t time, uint32_t max_len) {
+ std::string str = OHOS::TraingModel::GetInstance()->GetPaceTablePath(time);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "get_training_spead:%s\r\n",
+ str.c_str());
+ if (str.length() <= 3) {
+ return false;
+ }
+ memcpy_s(path, max_len, str.c_str(), str.length());
+ return true;
+ }
+
+ bool get_training_heart_path_(char *path, uint32_t time, uint32_t max_len) {
+ std::string str = OHOS::TraingModel::GetInstance()->GetHeartTablePath(time);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "get_training_heart:%s\r\n",
+ str.c_str());
+ if (str.length() <= 3) {
+ return false;
+ }
+ memcpy_s(path, max_len, str.c_str(), str.length());
+ return true;
+ }
+
+ //获取gps轨迹路径
+ bool get_training_gps_path_(char *path, uint32_t time, uint32_t max_len) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 获取GPS轨迹路径开始 ===");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "请求时间戳: %u, 最大路径长度: %u",
+ time, max_len);
+
+ std::string str = OHOS::TraingModel::GetInstance()->GetGPSDataPath(time);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "获取到的GPS路径: %s", str.c_str());
+
+ if (str.length() <= 3) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: GPS路径长度不足,长度: %zu",
+ str.length());
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 获取GPS轨迹路径结束(失败) ===");
+ return false;
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS路径长度: %zu", str.length());
+ memcpy_s(path, max_len, str.c_str(), str.length());
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS路径复制完成");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 获取GPS轨迹路径结束(成功) ===");
+ return true;
+ }
+
+ char *struct_to_json_buf_malloc_(uint8_t day) {
+ cJSON *dataJSON = cJSON_CreateObject();
+ cJSON *data_arryJSON = cJSON_CreateArray();
+ struct Trainingdata *data = NULL;
+ int i = 0;
+ // char file_path[128] = {0};
+ char timeday[12] = {0};
+ uapi_get_YYMMDD_Time_extend(timeday, '_', day);
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "%s\r\n", timeday);
+ // sprintf(file_path, "%s%s.bin", TRAINING_PATH_BLE, timeday);
+
+ while (data = read_Trainingdata_data(timeday, i)) {
+ cJSON *itemJSON = cJSON_CreateObject();
+ if (data != NULL) {
+ cJSON_AddNumberToObject(itemJSON, "src", data->src);
+ cJSON_AddNumberToObject(itemJSON, "bt", data->bt);
+ cJSON_AddNumberToObject(itemJSON, "ty", data->ty);
+ cJSON_AddNumberToObject(itemJSON, "st", data->st);
+ cJSON_AddNumberToObject(itemJSON, "sp", data->sp);
+ cJSON_AddNumberToObject(itemJSON, "tgt", data->tgt);
+ cJSON_AddNumberToObject(itemJSON, "tg", data->tg);
+ cJSON_AddNumberToObject(itemJSON, "pg", data->pg);
+ cJSON_AddNumberToObject(itemJSON, "du", data->du);
+ cJSON_AddNumberToObject(itemJSON, "ca", data->ca);
+ cJSON_AddNumberToObject(itemJSON, "di", data->di);
+ cJSON_AddNumberToObject(itemJSON, "hr", data->hr);
+ cJSON_AddNumberToObject(itemJSON, "stp", data->stp);
+ cJSON_AddNumberToObject(itemJSON, "ti", data->ti);
+ cJSON_AddNumberToObject(itemJSON, "cir", data->cir);
+ cJSON_AddNumberToObject(itemJSON, "alt", data->alt);
+ cJSON_AddNumberToObject(itemJSON, "apc", data->apc);
+ cJSON_AddNumberToObject(itemJSON, "fpc", data->fpc);
+ cJSON_AddNumberToObject(itemJSON, "spc", data->spc);
+ cJSON_AddNumberToObject(itemJSON, "std", data->std);
+ cJSON_AddNumberToObject(itemJSON, "cdc", data->cdc);
+ cJSON_AddItemReferenceToArray(data_arryJSON, itemJSON);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"*******************************");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"src:%d",data->src);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"bt:%d",data->bt);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ty:%d",data->ty);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"st:%d",data->st);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"sp:%d",data->sp);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"tgt:%d",data->tgt);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"tg:%d",data->tg);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"pg:%d",data->pg);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"du:%d",data->du);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ca:%d",data->ca);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"di:%d",data->di);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"hr:%d",data->hr);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"stp:%d",data->stp);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ti:%d",data->ti);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"cir:%d",data->cir);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"alt:%d",data->alt);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"apc:%d",data->apc);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"fpc:%d",data->fpc);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"spc:%d",data->spc);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"std:%d",data->std);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"cdc:%d",data->cdc);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"*******************************");
+ free(data);
+ data = NULL;
+ } else {
+ break;
+ }
+ i++;
+ }
+ cJSON_AddItemToObject(dataJSON, "recs", data_arryJSON);
+
+ char *json_format = cJSON_PrintUnformatted(dataJSON);
+
+ int pSize = cJSON_GetArraySize(dataJSON);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "训练历史数据:%s\r\n", json_format);
+ cJSON_Delete(dataJSON);
+ return json_format;
+ }
+
+ int read_gps_data_(char *path, uint8_t *out_buf) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 读取GPS数据开始 ===");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS文件路径: %s", path);
+
+ FILE *fp = fopen(path, "rb+");
+ if (fp == NULL) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: 无法打开GPS文件");
+ return 0;
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS文件打开成功");
+
+ uint32_t file_size = 0;
+ fseek(fp, 0, SEEK_END);
+ file_size = ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS文件大小: %u 字节", file_size);
+
+ int len = 0;
+ int point_count = 0;
+ OHOS::GPSData_t data = {0};
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始读取GPS轨迹点...");
+ while (1) {
+ int read_size = fread(&data, 1, sizeof(data), fp);
+ if (read_size == sizeof(data)) {
+ point_count++;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "读取GPS点 #%d: 时间戳=%u, 纬度=%.6f, 经度=%.6f",
+ point_count, data.timestamp, data.latitude, data.longitude);
+
+ int temp_data = data.timestamp;
+ memcpy_s(out_buf + len, 4, &temp_data, sizeof(data.timestamp));
+ len += 4;
+
+ int temp_latitude = (int)(data.latitude * 10000000);
+ memcpy_s(out_buf + len, 4, &temp_latitude, sizeof(temp_latitude));
+ len += 4;
+
+ int temp_longitude = (int)(data.longitude * 10000000);
+ memcpy_s(out_buf + len, 4, &temp_longitude, sizeof(temp_longitude));
+ len += 4;
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "GPS点数据转换: 时间戳=%u, 纬度=%d, 经度=%d", temp_data,
+ temp_latitude, temp_longitude);
+ } else {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS文件读取结束,读取大小: %d",
+ read_size);
+ break;
+ }
+ }
+
+ fclose(fp);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS文件关闭");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "总共读取 %d 个GPS点,输出数据长度: %d 字节", point_count, len);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 读取GPS数据结束 ===");
+
+ return len;
+ }
+
+ //通过时间戳获取训练数据
+ cJSON* ble_get_Trainingdata_by_timetamp_(uint32_t timetamp)
+ {
+ struct Trainingdata * data=NULL;
+ int i=0;
+
+ time_t currtime=timetamp;
+ struct tm *timeinfo;
+ timeinfo = localtime(&currtime);
+ int y=timeinfo->tm_year+ 1900;
+ int m=timeinfo->tm_mon+1;
+ int d=timeinfo->tm_mday;
+ int h=timeinfo->tm_hour;
+ int min=timeinfo->tm_min;
+
+ char timeday[24]={0};
+ sprintf_s(timeday,24,"%d_%02d_%02d",y,m,d);
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,"%s\r\n",timeday);
+
+ while(data=read_Trainingdata_data(timeday, i))
+ {
+ if(data!=NULL)
{
- MomentumFlag = true;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"%d,%d\r\n",timetamp,data->st);
+ // 打印data数据
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"*******************************");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"src:%d",data->src);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"bt:%d",data->bt);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ty:%d",data->ty);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"st:%d",data->st);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"sp:%d",data->sp);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"tgt:%d",data->tgt);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"tg:%d",data->tg);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"pg:%d",data->pg);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"du:%d",data->du);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ca:%d",data->ca);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"di:%d",data->di);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"hr:%d",data->hr);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"stp:%d",data->stp);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ti:%d",data->ti);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"cir:%d",data->cir);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"alt:%d",data->alt);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"apc:%d",data->apc);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"fpc:%d",data->fpc);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"spc:%d",data->spc);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"std:%d",data->std);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"cdc:%d",data->cdc);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"*******************************");
+ int time_error= timetamp>data->st ? timetamp-data->st:data->st-timetamp;
+ if(time_error<=2)
+ {
+ cJSON* itemJSON = cJSON_CreateObject();
+ cJSON_AddNumberToObject(itemJSON, "src", data->src);
+ cJSON_AddNumberToObject(itemJSON,"bt",data->bt);
+ cJSON_AddNumberToObject(itemJSON,"ty",data->ty);
+ cJSON_AddNumberToObject(itemJSON,"st",data->st);
+ cJSON_AddNumberToObject(itemJSON,"sp",data->sp);
+ cJSON_AddNumberToObject(itemJSON,"tgt",data->tgt);
+ cJSON_AddNumberToObject(itemJSON,"tg",data->tg);
+ cJSON_AddNumberToObject(itemJSON,"pg",data->pg);
+ cJSON_AddNumberToObject(itemJSON,"du",data->du);
+ cJSON_AddNumberToObject(itemJSON,"ca",data->ca);
+ cJSON_AddNumberToObject(itemJSON,"di",data->di);
+ cJSON_AddNumberToObject(itemJSON,"hr",data->hr);
+ cJSON_AddNumberToObject(itemJSON,"stp",data->stp);
+ cJSON_AddNumberToObject(itemJSON,"ti",data->ti);
+ cJSON_AddNumberToObject(itemJSON,"cir",data->cir);
+ cJSON_AddNumberToObject(itemJSON, "alt", data->alt);
+ cJSON_AddNumberToObject(itemJSON,"apc",data->apc);
+ cJSON_AddNumberToObject(itemJSON,"fpc",data->fpc);
+ cJSON_AddNumberToObject(itemJSON,"spc",data->spc);
+ cJSON_AddNumberToObject(itemJSON,"std",data->std);
+ cJSON_AddNumberToObject(itemJSON,"cdc",data->cdc);
+ free(data);
+ return itemJSON;
+ }
+
+ free(data);
+ data=NULL;
}
- else
- {
- MomentumFlag = false;
+ else{
+ break;
}
-#endif
- int duration = sportdata->duration;
- //判断运动时间是否大于1分钟
- if (duration >= 60) {
- MomentumFlag = true;
- } else {
- MomentumFlag = false;
- }
- return MomentumFlag;
-}
-
-//用户运动数据配置
-void TraingModel::SetUserSportdata() {
- // 获取用户的最高心率提醒
- user_maxHeart = ConfigModel::GetInstance()->get_configData_int(
- ConfigModel::CONFIGMODEL_ITEM_TRAINING_HEART_MAX);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "用户最高心率提醒: %d", user_maxHeart);
-
- // 获取用户年龄
- user_Age = ConfigModel::GetInstance()->GetUserAge();
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "用户年龄: %d", user_Age);
-
- // 获取自动训练设置
- user_autoget = ConfigModel::GetInstance()->get_configData_int(
- ConfigModel::CONFIGMODEL_ITEM_TRANING_AUTO);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "自动获取设置: %d", user_autoget);
-
- // 获取训练频率设置
- user_freq = ConfigModel::GetInstance()->get_configData_int(
- ConfigModel::CONFIGMODEL_ITEM_TRANING_FREQ);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "训练频率: %d", user_freq);
-
- user_Intensity = MAX_HEART_RATE_TRAINING - user_Age;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "用户运动强度心率: %d",
- user_Intensity);
-}
-//运动过程中更新心率配置
-void TraingModel::SetMaxHeart(int maxheart) {
- // 获取用户运动数据配置
- user_maxHeart = maxheart;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "用户最高心率: %d", user_maxHeart);
-}
-/*配速表与心率表相关*/
-//清空心率表
-void TraingModel::ClearHeartTable() {
- heartRateTable.clear(); // 清空心率数据表
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Heart Rate Table Cleared.\r\n");
-}
-//清空配速表
-void TraingModel::ClearPaceTable() {
- paceTable.clear(); // 清空配速数据表
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Pace Table Cleared.\r\n");
-}
-// 添加心率数据
-bool TraingModel::AddHeartRateData(uint32_t timestamp, uint8_t heartRate) {
- // 检查重复时间戳
- for (const auto &entry : heartRateTable) {
- if (entry.timestamp == timestamp) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "时间戳重复无法添加数据 AddHeartRateData is error!");
- return false; // 返回重复
- }
- }
-
- if (heartRateTable.size() >= MAX_ENTRIES) {
- WEARABLE_LOGE(
- WEARABLE_LOG_MODULE_APP,
- "达到最大心率数据条目限制 无法添加新的数据 AddHeartRateData is error!");
- return false; // 返回条目满了
- }
-
- HeartRateEntry newEntry = {timestamp, heartRate};
- heartRateTable.push_back(newEntry);
- return true; // 返回成功
-}
-
-// 添加配速数据
-bool TraingModel::AddPaceData(uint32_t timestamp, uint16_t pace) {
- // 检查重复时间戳
- for (const auto &entry : paceTable) {
- if (entry.timestamp == timestamp) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "时间戳重复无法添加数据 AddPaceData is error!"); // 修正这里
- return false; // 返回重复
- }
- }
-
- if (paceTable.size() >= MAX_ENTRIES) {
- WEARABLE_LOGE(
- WEARABLE_LOG_MODULE_APP,
- "达到配速数据条目限制 无法添加新的数据 AddPaceData is error!");
- return false; // 返回条目满了
- }
-
- PaceEntry newEntry = {timestamp, pace};
- paceTable.push_back(newEntry);
- return true; // 返回成功
-}
-//心率表打印
-void TraingModel::PrintHeartRateTable() {
- size_t heartRateCount = heartRateTable.size();
- if (heartRateCount == 0) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "心率表为空,无法打印。");
- return;
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Heart Rate Table:");
- for (const auto &entry : heartRateTable) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "时间戳: %u, 心率: %d",
- entry.timestamp, entry.heartRate);
- }
-}
-//配速表打印
-void TraingModel::PrintPaceTable() {
- size_t paceCount = paceTable.size();
- if (paceCount == 0) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "配速表为空,无法打印。");
- return;
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Pace Table:");
- for (const auto &entry : paceTable) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "时间戳: %u, 配速: %d",
- entry.timestamp, entry.pace);
- }
-}
-// 保存心率表
-bool TraingModel::SaveHeartTable() {
-
- //打印数组
- PrintHeartRateTable();
-
- CreateDirectoryIfNotExists(TRAINING_HEART_FILE);
-
- size_t heartRateCount = heartRateTable.size();
- if (heartRateCount == 0) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "心率表为空,无法保存。");
- return false;
- }
-
- std::string filePath =
- TRAINING_HEART_FILE + std::to_string(sportdata->starttime) + ".bin";
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveHeartTable filePath: %s",
- filePath.c_str());
- std::ofstream heartRateFile(filePath, std::ios::binary);
- if (!heartRateFile) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
- filePath.c_str());
- return false; // 返回失败
- }
-
- // 存储表数据
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "heartRateCount = %d", heartRateCount);
- if (!heartRateFile.write(
- reinterpret_cast<const char *>(heartRateTable.data()),
- heartRateCount * sizeof(HeartRateEntry))) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "写入心率数据失败。");
- return false; // 返回失败
- }
-
- heartRateFile.close();
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Heart Rate Table Saved.");
- return true; // 返回成功
-}
-
-// 保存配速表
-bool TraingModel::SavePaceTable() {
-
- //打印数组
- PrintPaceTable();
-
- CreateDirectoryIfNotExists(TRAINING_PACE_FILE);
-
- size_t paceCount = paceTable.size();
- if (paceCount == 0) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "配速表为空,无法保存。");
- return false;
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "paceCount = %d", paceCount);
-
- std::string filePath =
- TRAINING_PACE_FILE + std::to_string(sportdata->starttime) + ".bin";
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SavePaceTable filePath: %s",
- filePath.c_str());
-
- std::ofstream paceFile(filePath, std::ios::binary);
- if (!paceFile) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
- filePath.c_str());
- return false; // 返回失败
- }
- // 写入配速数据
- if (!paceFile.write(reinterpret_cast<const char *>(paceTable.data()),
- paceCount * sizeof(PaceEntry))) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "写入配速数据失败。");
- return false; // 返回失败
- }
-
- paceFile.close();
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SavePaceTable Success.");
- return true; // 返回成功
-}
-// 返回心率表路径
-std::string TraingModel::GetHeartTablePath(uint32_t timestamp) {
- // 首先检查完全匹配的时间戳
- std::string exactFilePath =
- TRAINING_HEART_FILE + std::to_string(timestamp) + ".bin";
- if (IsFileAccessibleAndNotEmpty(exactFilePath)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到心率表: %s",
- exactFilePath.c_str());
- return exactFilePath; // 返回完全匹配的文件路径
- }
-
- // 如果未找到,检查时间戳相差1到2秒的文件
- for (int i = -2; i <= 2; ++i) {
- uint32_t checkTimestamp = timestamp + i;
- std::string filePath =
- TRAINING_HEART_FILE + std::to_string(checkTimestamp) + ".bin";
- if (IsFileAccessibleAndNotEmpty(filePath)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到心率表: %s",
- filePath.c_str());
- return filePath; // 返回找到的文件路径
- }
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的心率表。");
- return ""; // 未找到文件
-}
-
-// 返回配速表路径
-std::string TraingModel::GetPaceTablePath(uint32_t timestamp) {
- // 首先检查完全匹配的时间戳
- std::string exactFilePath =
- TRAINING_PACE_FILE + std::to_string(timestamp) + ".bin";
- if (IsFileAccessibleAndNotEmpty(exactFilePath)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到配速表: %s",
- exactFilePath.c_str());
- return exactFilePath; // 返回完全匹配的文件路径
- }
-
- // 如果未找到,检查时间戳相差1到2秒的文件
- for (int i = -2; i <= 2; ++i) {
- uint32_t checkTimestamp = timestamp + i;
- std::string filePath =
- TRAINING_PACE_FILE + std::to_string(checkTimestamp) + ".bin";
- if (IsFileAccessibleAndNotEmpty(filePath)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到配速表: %s",
- filePath.c_str());
- return filePath; // 返回找到的文件路径
- }
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的配速表。");
- return ""; // 未找到文件
-}
-/*发送实时数据*/
-void TraingModel::SendLiveData() {
-#ifdef SUPPORT_BLE_SERVICE
- msg_get_train_data_info_t data;
- memset(&data, 0, sizeof(msg_get_train_data_info_t));
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata->starttime %d \r\n",
- sportdata->starttime);
- data.st = sportdata->starttime;
- data.hr = sportdata->heart;
- data.ca = sportdata->calories * 10; // app端需要乘10
- data.di = sportdata->distance;
- data.stp = sportdata->steps;
- data.ti = sportdata->intensity;
- data.pc = sportdata->pc;
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "SendLiveData Begin!");
- sendtraningdata(&data); // 发送数据函数
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "SendLiveData Stop!");
-#endif
-}
-/* -------------------------------- 运动识别发起处理
- * -------------------------------- */
-void TraingModel::set_sport_config(UIResourceTextId id) {
- if (id >= MAXSPORTID) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "id is error");
- return;
- }
- // 开始训练 ->不返回
- NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGCRAD);
- // 配置运动项
- SetSportEvent(id);
- // 获取运动项目
- ExerciseClass type = GetSportId();
- // 开启对应的运动
- TraingCardPresenter::GetInstance()->OpenSensor(type);
- // 设置为手表开启运动
- SetSportOpenWay(false);
-}
-
-/* -------------------------------------------------------------------------- */
-
-#include <chrono>
-#include <iomanip>
-#include <sstream>
-#include <string>
-
-std::string TraingModel::DisplayMotionDuration() {
- // 清空 motionDuration
- motionDuration.clear();
-
- // 重置运动次数
- if (g_motionTimes >= NUM_TIME_DAY) {
- g_motionTimes = 0;
- }
-
- // 计算和格式化运动时长
- int hours = g_motionTimes / NUM_TIME_HOUR;
- int minutes = (g_motionTimes % NUM_TIME_HOUR) / NUM_TIME_MIN;
- int seconds = g_motionTimes % NUM_TIME_MIN;
-
- std::ostringstream oss;
- oss << std::setfill('0') << std::setw(2) << hours << ":" << std::setw(2)
- << minutes << ":" << std::setw(2) << seconds;
-
- motionDuration = oss.str();
-
- // // 获取当前时间(以微秒为单位)
- // uint32_t currentTime = static_cast<uint32_t>(
- // std::chrono::duration_cast<std::chrono::microseconds>(
- // std::chrono::system_clock::now().time_since_epoch())
- // .count());
-
- // // 记录和检查上一次调用的时间
- // static uint32_t lastCallTime = 0;
- // if (currentTime - lastCallTime < 1000000) { // 小于1秒
- // return motionDuration;
- // }
- // lastCallTime = currentTime; // 更新最后调用时间
-
- // 如果 g_motionTimes 不为零,进行计算和数据存储
- if (g_motionTimes > 0) {
- sportdata->duration = g_motionTimes; // 设置训练时长
-
- // 每分钟进行配速和心率数据存储
- // StoreDataIfMinutePassed();
- }
-
- return motionDuration; // 返回格式化的运动时长
-}
-
-// 检查并发送实时数据
-void TraingModel::SendLiveDataIfNecessary() {
- // 检查自动获取的配置 app发起
- if (user_autoget && Openingway) {
- // 发送实时数据
- if (g_motionTimes % user_freq == 0) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "SendLiveData is OK\r\n");
- SendLiveData(); // 发送实时数据
- }
- }
-}
-
-// 每5秒进行配速和心率数据存储
-void TraingModel::StoreDataIfMinutePassed() {
- if (sportdata->duration % 5 == 0) {
- uint32_t timestamp;
- //进行数据的筛选
- if (uapi_get_timestampe_ss(×tamp) == ERRCODE_SUCC) {
- if (sportdata->heart != 0)
- AddHeartRateData(timestamp, sportdata->heart);
- if (sportdata->apc != 0)
- AddPaceData(timestamp, sportdata->apc);
- }
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "duration:[%d] g_motionTimes[%d]\r\n",
- sportdata->duration, g_motionTimes);
-}
-#ifdef __cplusplus
-extern "C" {
-#endif
-#include <stdbool.h> // 确保包含该头文件以使用布尔类型
-
-// 全局变量
-static bool isPaused = false; // 定时器暂停状态
-static uint32_t pauseStartTimestamp = 0; // 当前暂停开始的时间戳
-static uint32_t totalPausedDuration = 0; // 总暂停时长
-// static uint32_t g_motionTimes = 0; // 总运动时间
-// static TimerHandle_t g_startCounting = NULL; // 定时器句柄
-
-/* 日志错误信息 */
-void LogError(const char *msg, osStatus_t ret) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%s ret = %lx!!", msg, ret);
-}
-
-/* 定时器回调函数 */
-void TraingTimerCallback(void) {
- // 只在活动状态下进行更新
-
- if (pauseStartTimestamp == 0) {
- //开始时将该值设置为运动开始值
- pauseStartTimestamp = sportdata->starttime;
- }
-
- uint32_t currentTimestamp =
- OHOS::TraingModel::GetInstance()->get_current_time(); // 获取当前时间戳
- g_motionTimes = (currentTimestamp - pauseStartTimestamp) +
- totalPausedDuration; // 更新运动时间
- sportdata->duration = g_motionTimes; // 更新持续时间
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "更新运动时间。当前运动时间: %d %d %d %d", currentTimestamp,
- pauseStartTimestamp, totalPausedDuration, g_motionTimes);
-
- // 检查并发送实时数据
- OHOS::TraingModel::GetInstance()->SendLiveDataIfNecessary();
- OHOS::TraingModel::GetInstance()->CheckTimeTag();
-
- // 记录特殊活动的心率/配速数据
- if (OHOS::TraingModel::GetInstance()->GetIsSpecial()) {
- OHOS::TraingModel::GetInstance()->StoreDataIfMinutePassed();
- }
-
- // 振动反馈
- if ((sportdata->goaldegree >= 100) && (shock_count < 4)) {
- shock_count++;
- custom_VibratorFunc_int(0); // 触发振动反馈
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "触发了振动。振动计数: %d",
- shock_count);
- }
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "TraingTimerCallback 被执行。");
-}
-
-/* 启动定时器 */
-void StartTimer(void) {
- if (g_startCounting != NULL) {
- LogError("定时器已经存在", osError);
- return;
- }
-
- g_startCounting = osTimerNew((osTimerFunc_t)TraingTimerCallback,
- osTimerPeriodic, NULL, NULL);
- if (g_startCounting == NULL) {
- LogError("定时器创建失败", osError);
- return;
- }
-
- int tick = osMs2Tick(MS_ONE_SECONDS);
- osStatus_t retTimer = osTimerStart(g_startCounting, tick);
- if (retTimer != osOK) {
- LogError("定时器启动失败", retTimer);
- osTimerDelete(g_startCounting);
- g_startCounting = NULL;
- }
-}
-
-/* 停止并重置定时器 */
-void StopAndResetTimer(void) {
- if (g_startCounting != NULL) {
- osTimerStop(g_startCounting);
- osStatus_t ret = osTimerDelete(g_startCounting);
- if (ret != osOK) {
- LogError("定时器删除失败", ret);
- }
- g_startCounting = NULL;
- g_motionTimes = 0;
- pauseStartTimestamp = 0;
- totalPausedDuration = 0; // 重置总暂停时长
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "定时器已停止并重置。");
- }
-}
-
-/* 暂停定时器 */
-void PauseTimer(void) {
- if (g_startCounting != NULL) {
- osTimerStop(g_startCounting);
-
- totalPausedDuration = sportdata->duration; //记录暂停前的时间;后续加上
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "定时器已暂停。暂停开始时间戳: %u",
- pauseStartTimestamp);
- }
-}
-
-/* 恢复定时器 */
-void ResumeTimer(void) {
- if (g_startCounting != NULL) {
-
- uint32_t currentTimestamp =
- OHOS::TraingModel::GetInstance()->get_current_time(); // 获取当前时间戳
- pauseStartTimestamp = currentTimestamp; // 保存暂停开始的时间戳 将这个更新
-
- int tick = osMs2Tick(MS_ONE_SECONDS);
- if (osTimerStart(g_startCounting, tick) != osOK) {
- LogError("恢复定时器时启动失败", osError);
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "定时器已恢复。当前运动时间: %u %d",
- g_motionTimes, pauseStartTimestamp);
- }
-}
-
-#ifdef __cplusplus
-}
-#endif
-/* ---------------------------------- 数据存储
- * ---------------------------------- */
-static int32 SaveTraingDataToFile(void) {
- time_t now = time(nullptr);
- struct tm *timeinfo = localtime(&now);
- char filename[20];
- strftime(filename, sizeof(filename), "%Y%m%d.bin", timeinfo);
-
- uint32 size = sizeof(Trainingdata);
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "SaveTraingDataToFile !!");
-
- char filePath[20];
- sprintf(filePath, "%s%s", TRAINING_DATA_DIRECTORY, filename);
-
- std::ofstream fd(filePath, std::ios::binary);
- if (!fd.is_open()) {
- if (mkdir(TRAINING_DATA_DIRECTORY, S_IRWXU) != 0) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to create directory");
- return 0;
- }
-
- fd.open(filePath, std::ios::binary);
- if (!fd.is_open()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open file for write");
- return 0;
- }
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Directory created successfully!!");
- }
-
- fd.write(reinterpret_cast<const char *>(&sportdata), size);
- fd.close();
- return 1;
-}
-static std::vector<Trainingdata> LoadTrainingDataFromTodayFile() {
- std::vector<Trainingdata> trainingRecords;
-
- time_t now = time(nullptr);
- struct tm *timeinfo = localtime(&now);
- char filename[20];
- strftime(filename, sizeof(filename), "%Y%m%d.bin", timeinfo);
- // 找到训练文件夹里面的当天文件
- char filePath[20];
- sprintf(filePath, "%s%s", TRAINING_DATA_DIRECTORY, filename);
-
- std::ifstream fd(filePath, std::ios::binary);
- if (!fd.is_open()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open today's file");
- return trainingRecords; // Return an empty vector if file open fails
- }
-
- // 读取训练信息
- while (!fd.eof()) {
- Trainingdata record;
- fd.read(reinterpret_cast<char *>(&record), sizeof(Trainingdata));
- if (fd.gcount() == sizeof(Trainingdata)) {
- trainingRecords.push_back(record);
- }
- }
-
- fd.close();
-
- return trainingRecords;
-}
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/stat.h>
-
-// 获得数据个数
-static int get_Trainingdata_data_num(FILE *fp) {
- if (fp == NULL) {
- return 0; // 如果文件指针为空,返回0
- }
-
- fseek(fp, 0, SEEK_END); // 将文件指针移动到文件末尾
- long size = ftell(fp); // 获取当前文件指针位置,即文件大小
- fseek(fp, 0, SEEK_SET); // 将文件指针移回文件开头
-
- return (size / sizeof(Trainingdata));
-}
-
-// 写训练bin文件
-int write_Trainingdata_data(Trainingdata data) {
- char file_path[MAX_FILE_PATH_LENGTH] = {0};
- char timeday[12] = {0};
- uapi_get_YYMMDD_Time(timeday, '_');
- snprintf(file_path, sizeof(file_path), "%s%s.bin", TRAINING_PATH, timeday);
-
- // 检查目录是否存在,如果不存在则创建
- struct stat st = {0};
- if (stat(TRAINING_PATH, &st) == -1) {
- if (mkdir(TRAINING_PATH, 0700) != 0) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to create directory: %s",
- strerror(errno));
- return -1; // 返回错误码
- }
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "%s", file_path);
- FILE *fp = fopen(file_path, "ab+");
- if (fp == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open file for append: %s",
- strerror(errno));
- fp = fopen(file_path, "wb+");
- if (fp == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "Failed to open file for write: %s", strerror(errno));
- return -1; // 返回错误码
- }
- }
-
- int ret = fwrite((void *)&data, sizeof(Trainingdata), 1, fp);
- if (ret != 1) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "fwrite_data error! %d", ret);
- fclose(fp);
- return -1; // 返回错误码
- }
-
- fclose(fp); // 关闭文件
- return 1; // 返回成功码
-}
-
-// 读数据
-Trainingdata *read_Trainingdata_data(char *timedata, uint16_t offset) {
- char file_path[128] = {0};
- snprintf(file_path, sizeof(file_path), "%s%s.bin", TRAINING_PATH, timedata);
-
- FILE *fp = fopen(file_path, "rb");
- if (fp == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open file for read: %s",
- strerror(errno));
- return NULL;
- }
-
- Trainingdata *data = (Trainingdata *)malloc(sizeof(Trainingdata));
- if (data == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "Failed to allocate memory for Trainingdata");
- fclose(fp);
- return NULL;
- }
-
- int num_data = get_Trainingdata_data_num(fp);
- if (num_data <= offset) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "Offset %d exceeds number of data %d", offset, num_data);
- free(data); // 释放内存
- fclose(fp);
- return NULL;
- }
-
- fseek(fp, sizeof(Trainingdata) * offset, SEEK_SET);
- fread(data, sizeof(Trainingdata), 1, fp);
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Data src: %d", data->src);
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Offset: %d, Size: %zu", offset,
- sizeof(Trainingdata));
-
- fclose(fp); // 关闭文件
- return data; // 返回读取的数据
-}
-
-#ifdef __cplusplus
-}
-#endif
-
-/* -------------------------------------------------------------------------- */
-/*检查是否存在数据*/
-bool TraingModel::CheckDataJson() {
- // 读取record.json文件
- std::ifstream file(TRAINING_RECORD_FILE);
-
- // 检查文件是否打开成功
- if (!file.is_open()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Unable to open file");
- return false;
- }
-
- // 检查文件是否为空
- file.seekg(0, std::ios::end);
- if (file.tellg() == 0) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "File is empty");
- file.close();
- return false;
- }
-
- // 解析json
- std::vector<SportData> sportDataVector;
- if (!LoadDataFormJson(sportDataVector)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LoadDataFormJson is error");
- file.close();
- return false;
- }
-
- // 关闭文件
- file.close();
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "CheckDataJson is ok");
- return true;
-}
-
-bool TraingModel::SaveDataToJson(SportData *data) {
- FILE *fp;
- char *jsonStr = NULL;
-
- // 读取现有的 JSON 数据
- fp = fopen(TRAINING_RECORD_FILE, "r");
- cJSON *data_root = NULL;
- cJSON *dataArray = NULL;
-
- if (fp != NULL) {
- fseek(fp, 0, SEEK_END);
- long fileSize = ftell(fp);
- fseek(fp, 0, SEEK_SET);
-
- jsonStr = (char *)malloc(fileSize + 1);
- if (jsonStr != nullptr) {
- if (fread(jsonStr, fileSize, 1, fp) == 1) {
- data_root = cJSON_Parse(jsonStr);
- if (data_root != NULL) {
- dataArray = cJSON_GetObjectItem(data_root, "data");
- }
- }
- }
- fclose(fp);
- }
-
- if (dataArray == NULL) {
- data_root = cJSON_CreateObject();
- cJSON_AddItemToObject(data_root, "data", cJSON_CreateArray());
- dataArray = cJSON_GetObjectItem(data_root, "data");
- }
-
- // 创建或更新数据节点
- cJSON *sportData = cJSON_CreateObject();
- cJSON_AddNumberToObject(sportData, "sportTypeId",
- int(data->sportType)); // 训练项目类 Id
- cJSON_AddNumberToObject(sportData, "sportId", data->sportId); // 训练项目 Id
- cJSON_AddStringToObject(sportData, "startDate",
- (data->start_date).c_str()); // 训练开始日期字符串
- cJSON_AddStringToObject(sportData, "startTime",
- (data->start_time).c_str()); // 训练开始时间
- cJSON_AddStringToObject(sportData, "endTime",
- (data->end_time).c_str()); // 训练结束时间
- cJSON_AddNumberToObject(sportData, "goalDegree",
- data->goaldegree); // 训练目标完成度
- cJSON_AddNumberToObject(sportData, "duration",
- data->duration); // 训练时长(秒)
- cJSON_AddNumberToObject(sportData, "distance", data->distance); // 训练距离
- cJSON_AddNumberToObject(sportData, "avgHeartRate",
- data->avgheart); // 训练平均心率
- cJSON_AddNumberToObject(sportData, "steps", data->steps); // 训练步数
- cJSON_AddNumberToObject(sportData, "intensity", data->intensity); // 训练强度
- cJSON_AddNumberToObject(sportData, "apc", data->apc); // 训练平均配速
- cJSON_AddNumberToObject(sportData, "cdc", data->cdc); // 训练步频
- cJSON_AddNumberToObject(sportData, "std", data->std); // 训练步幅
- cJSON_AddNumberToObject(sportData, "calories", data->calories); //卡路里
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "starttime is %d", data->starttime);
- cJSON_AddNumberToObject(sportData, "start_time",
- data->starttime); // 开始时间戳
-
- // 添加运动数据节点到数组并限制数量
- cJSON_AddItemToArray(dataArray, sportData);
- while (cJSON_GetArraySize(dataArray) > MAX_DATA_COUNT) {
- cJSON_DeleteItemFromArray(dataArray, 0);
- }
-
- // 将更新后的 JSON 数据写入文件
- char *jsonStrNew = cJSON_Print(data_root);
- fp = fopen(TRAINING_RECORD_FILE, "w");
- if (fp != NULL) {
- fwrite(jsonStrNew, strlen(jsonStrNew), 1, fp);
- fclose(fp);
- }
-
- // 释放资源
- cJSON_Delete(data_root);
- free(jsonStr);
- cJSON_free(jsonStrNew);
-
- return true;
-}
-
-/* 加载训练数据函数 */
-/* 加载训练数据函数 */
-bool TraingModel::LoadDataFormJson(std::vector<SportData> &sportDataVector) {
- // 清空输出向量,避免旧数据干扰
- sportDataVector.clear();
-
- // 打开JSON文件
- std::ifstream file(TRAINING_RECORD_FILE);
- if (!file.is_open()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open file: %s",
- TRAINING_RECORD_FILE);
- return false;
- }
-
- // 读取文件内容到字符串
- std::string jsonData((std::istreambuf_iterator<char>(file)),
- std::istreambuf_iterator<char>());
- file.close();
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Read %zu bytes from JSON file",
- jsonData.size());
-
- // 解析JSON根对象
- cJSON *root = cJSON_Parse(jsonData.c_str());
- if (root == NULL) {
- const char *errorPtr = cJSON_GetErrorPtr();
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to parse JSON: %s",
- errorPtr ? errorPtr : "Unknown error");
- return false;
- }
-
- // 查找并验证data数组
- cJSON *dataArray = cJSON_GetObjectItem(root, "data");
- if (dataArray == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Missing 'data' array in JSON");
- cJSON_Delete(root);
- return false;
- }
- if (!cJSON_IsArray(dataArray)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "'data' is not an array in JSON");
- cJSON_Delete(root);
- return false;
- }
-
- // 检查数组大小
- int dataCount = cJSON_GetArraySize(dataArray);
- if (dataCount == 0) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Empty 'data' array in JSON");
- cJSON_Delete(root);
- return false;
- }
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Found %d items in 'data' array",
- dataCount);
-
- // 遍历解析每个数据项
- for (int i = 0; i < dataCount; i++) {
- cJSON *item = cJSON_GetArrayItem(dataArray, i);
- if (item == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "Null item at index %d in 'data' array", i);
- continue; // 跳过无效项,继续处理其他项
- }
-
- SportData sportData;
- bool isValid = true;
- std::string missingFields; // 记录缺失的字段
-
- // 解析sportTypeId字段
- if (cJSON *sportTypeIdItem = cJSON_GetObjectItem(item, "sportTypeId")) {
- if (cJSON_IsNumber(sportTypeIdItem)) {
- sportData.sportType = (Exercisetype)sportTypeIdItem->valueint;
- } else {
- isValid = false;
- missingFields += "sportTypeId ";
- }
- } else {
- isValid = false;
- missingFields += "sportTypeId ";
- }
-
- // 解析sportId字段
- if (cJSON *sportIdItem = cJSON_GetObjectItem(item, "sportId")) {
- if (cJSON_IsNumber(sportIdItem)) {
- sportData.sportId = (UIResourceTextId)sportIdItem->valueint;
- } else {
- isValid = false;
- missingFields += "sportId ";
- }
- } else {
- isValid = false;
- missingFields += "sportId ";
- }
-
- // 解析startDate字段
- if (cJSON *startDateItem = cJSON_GetObjectItem(item, "startDate")) {
- if (cJSON_IsString(startDateItem) && startDateItem->valuestring) {
- sportData.start_date = startDateItem->valuestring;
- } else {
- isValid = false;
- missingFields += "startDate ";
- }
- } else {
- isValid = false;
- missingFields += "startDate ";
- }
-
- // 解析startTime字段
- if (cJSON *startTimeItem = cJSON_GetObjectItem(item, "startTime")) {
- if (cJSON_IsString(startTimeItem) && startTimeItem->valuestring) {
- sportData.start_time = startTimeItem->valuestring;
- } else {
- isValid = false;
- missingFields += "startTime ";
- }
- } else {
- isValid = false;
- missingFields += "startTime ";
- }
-
- // 解析endTime字段
- if (cJSON *endTimeItem = cJSON_GetObjectItem(item, "endTime")) {
- if (cJSON_IsString(endTimeItem) && endTimeItem->valuestring) {
- sportData.end_time = endTimeItem->valuestring;
- } else {
- isValid = false;
- missingFields += "endTime ";
- }
- } else {
- isValid = false;
- missingFields += "endTime ";
- }
-
- // 解析goalDegree字段
- if (cJSON *goalDegreeItem = cJSON_GetObjectItem(item, "goalDegree")) {
- if (cJSON_IsNumber(goalDegreeItem)) {
- sportData.goaldegree = goalDegreeItem->valueint;
- } else {
- isValid = false;
- missingFields += "goalDegree ";
- }
- } else {
- isValid = false;
- missingFields += "goalDegree ";
- }
-
- // 解析duration字段
- if (cJSON *durationItem = cJSON_GetObjectItem(item, "duration")) {
- if (cJSON_IsNumber(durationItem)) {
- sportData.duration = durationItem->valueint;
- } else {
- isValid = false;
- missingFields += "duration ";
- }
- } else {
- isValid = false;
- missingFields += "duration ";
- }
-
- // 解析distance字段
- if (cJSON *distanceItem = cJSON_GetObjectItem(item, "distance")) {
- if (cJSON_IsNumber(distanceItem)) {
- sportData.distance = distanceItem->valueint;
- } else {
- isValid = false;
- missingFields += "distance ";
- }
- } else {
- isValid = false;
- missingFields += "distance ";
- }
-
- // 解析avgHeartRate字段
- if (cJSON *avgHeartRateItem = cJSON_GetObjectItem(item, "avgHeartRate")) {
- if (cJSON_IsNumber(avgHeartRateItem)) {
- sportData.avgheart = avgHeartRateItem->valueint;
- } else {
- isValid = false;
- missingFields += "avgHeartRate ";
- }
- } else {
- isValid = false;
- missingFields += "avgHeartRate ";
- }
-
- // 解析steps字段
- if (cJSON *stepsItem = cJSON_GetObjectItem(item, "steps")) {
- if (cJSON_IsNumber(stepsItem)) {
- sportData.steps = stepsItem->valueint;
- } else {
- isValid = false;
- missingFields += "steps ";
- }
- } else {
- isValid = false;
- missingFields += "steps ";
- }
-
- // 解析intensity字段
- if (cJSON *intensityItem = cJSON_GetObjectItem(item, "intensity")) {
- if (cJSON_IsNumber(intensityItem)) {
- sportData.intensity = intensityItem->valueint;
- } else {
- isValid = false;
- missingFields += "intensity ";
- }
- } else {
- isValid = false;
- missingFields += "intensity ";
- }
-
- // 解析apc字段
- if (cJSON *apcItem = cJSON_GetObjectItem(item, "apc")) {
- if (cJSON_IsNumber(apcItem)) {
- sportData.apc = apcItem->valueint;
- } else {
- isValid = false;
- missingFields += "apc ";
- }
- } else {
- isValid = false;
- missingFields += "apc ";
- }
-
- // 解析cdc字段
- if (cJSON *cdcItem = cJSON_GetObjectItem(item, "cdc")) {
- if (cJSON_IsNumber(cdcItem)) {
- sportData.cdc = cdcItem->valueint;
- } else {
- isValid = false;
- missingFields += "cdc ";
- }
- } else {
- isValid = false;
- missingFields += "cdc ";
- }
-
- // 解析std字段
- if (cJSON *stdItem = cJSON_GetObjectItem(item, "std")) {
- if (cJSON_IsNumber(stdItem)) {
- sportData.std = stdItem->valueint;
- } else {
- isValid = false;
- missingFields += "std ";
- }
- } else {
- isValid = false;
- missingFields += "std ";
- }
-
- // 解析calories字段
- if (cJSON *caloriesItem = cJSON_GetObjectItem(item, "calories")) {
- if (cJSON_IsNumber(caloriesItem)) {
- sportData.calories = caloriesItem->valueint;
- } else {
- isValid = false;
- missingFields += "calories ";
- }
- } else {
- isValid = false;
- missingFields += "calories ";
- }
-
- // 解析starttime字段(时间戳)
- if (cJSON *starttimeItem = cJSON_GetObjectItem(item, "start_time")) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Found starttime field. Type: %d",
- starttimeItem->type);
- if (cJSON_IsNumber(starttimeItem)) {
- sportData.starttime = starttimeItem->valueint;
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Parsed starttime: %d",
- sportData.starttime);
- } else {
- // 打印详细信息,帮助定位问题
- const char *valueStr =
- starttimeItem->valuestring ? starttimeItem->valuestring : "null";
- WEARABLE_LOGE(
- WEARABLE_LOG_MODULE_APP,
- "Invalid starttime type. Expected number, got type %d (value: %s)",
- starttimeItem->type, valueStr);
- isValid = false;
- }
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Missing starttime field");
- isValid = false;
- }
-
- // 处理解析结果
- if (isValid) {
- sportDataVector.push_back(sportData);
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Successfully parsed item %d", i);
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "Invalid data in item %d. Missing fields: %s", i,
- missingFields.c_str());
- }
- }
-
- cJSON_Delete(root);
-
- // 检查是否成功解析了任何数据
- if (sportDataVector.empty()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "No valid data items found in JSON");
- return false;
- }
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "Successfully loaded %zu valid data items",
- sportDataVector.size());
- return true;
-}
-
-//////////////////////////////////////GPS///////////////////////////////////////////////
-/*
- Latitude(纬度):范围:-90° 至 +90°(或南纬 90° 至北纬 90°);
- Longitude(经度):范围:-180° 至 +180°(或西经 180° 至东经 180°);
-
- -画布数据集最多是100个定位坐标,超过按比例进行拿取100个点位;
- -通过映射关系,将经纬度转换为400*400画布上的坐标;
- -但是不能靠近400,以免显示不全等问题,350*350;
- -如果处理负数,需要将其转换为正数,然后再进行映射?
- -如果有正数又有负数,应该怎么处理?
-*/
-
-static double getmin(double a, double b) { return a < b ? a : b; }
-static double getmax(double a, double b) { return a > b ? a : b; }
-// 计算点p到由p1和p2确定的线段的垂直距离(优化版)
-static double perpendicularDistance(const Point &p, const Point &p1,
- const Point &p2) {
- // 计算向量 p1->p2
- double dx = p2.x - p1.x;
- double dy = p2.y - p1.y;
-
- // 线段长度的平方
- double len2 = dx * dx + dy * dy;
-
- // 如果线段退化为一个点,直接计算点到点的距离
- if (len2 == 0) {
- return std::hypot(p.x - p1.x, p.y - p1.y);
- }
-
- // 计算投影比例 t
- double t = ((p.x - p1.x) * dx + (p.y - p1.y) * dy) / len2;
-
- if (t < 0) {
- // 投影在p1之前,返回p到p1的距离
- return std::hypot(p.x - p1.x, p.y - p1.y);
- }
-
- if (t > 1) {
- // 投影在p2之后,返回p到p2的距离
- return std::hypot(p.x - p2.x, p.y - p2.y);
- }
-
- // 投影在线段上,计算投影点并返回距离
- double projX = p1.x + t * dx;
- double projY = p1.y + t * dy;
- return std::hypot(p.x - projX, p.y - projY);
-}
-
-// Douglas-Peucker算法实现(优化版)
-static void douglasPeucker(const std::vector<Point> &points, double epsilon,
- std::vector<Point> &result) {
- if (points.size() <= 2) {
- // 点数量不足,直接返回
- result = points;
- return;
- }
-
- double dmax = 0;
- int index = 0;
- int end = points.size() - 1;
-
- // 找到距离最大的点
- for (int i = 1; i < end; i++) {
- double d = perpendicularDistance(points[i], points[0], points[end]);
- if (d > dmax) {
- index = i;
- dmax = d;
- }
- }
-
- // 如果最大距离大于阈值,则递归处理
- if (dmax > epsilon) {
- std::vector<Point> recResults1, recResults2;
-
- // 递归处理前半部分
- douglasPeucker(
- std::vector<Point>(points.begin(), points.begin() + index + 1), epsilon,
- recResults1);
-
- // 递归处理后半部分
- douglasPeucker(std::vector<Point>(points.begin() + index, points.end()),
- epsilon, recResults2);
-
- // 合并结果(避免重复添加端点)
- result.assign(recResults1.begin(), recResults1.end() - 1);
- result.insert(result.end(), recResults2.begin(), recResults2.end());
- } else {
- // 否则保留首尾点
- result = {points[0], points[end]};
- }
-}
-
-// 改进的GetPointData函数(优化版)
-void TraingModel::GetPointData() {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "Generating point data from %zu GPS points", gpsData.size());
-
- std::vector<GPSData> sampledData;
-
- // 处理GPS数据
- if (gpsData.size() > 100) {
- // 计算经纬度范围
- double minLat = 90.0, maxLat = -90.0;
- double minLon = 180.0, maxLon = -180.0;
-
- // 同时计算平均值用于判断数据分布
- double sumLat = 0, sumLon = 0;
-
- for (const auto &point : gpsData) {
- minLat = getmin(minLat, point.latitude);
- maxLat = getmax(maxLat, point.latitude);
- minLon = getmin(minLon, point.longitude);
- maxLon = getmax(maxLon, point.longitude);
- sumLat += point.latitude;
- sumLon += point.longitude;
- }
-
- // 计算经纬度范围和平均值
- double rangeLat = maxLat - minLat;
- double rangeLon = maxLon - minLon;
- double avgLat = sumLat / gpsData.size();
- double avgLon = sumLon / gpsData.size();
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "GPS range: lat=[%.6f, %.6f], lon=[%.6f, %.6f]", minLat,
- maxLat, minLon, maxLon);
-
- // 检查数据是否集中在极小区域或几乎相同
- const double MIN_SIGNIFICANT_RANGE = 0.01; // 根据实际情况调整
- bool isDataDegenerate =
- (rangeLat < MIN_SIGNIFICANT_RANGE && rangeLon < MIN_SIGNIFICANT_RANGE);
-
- if (isDataDegenerate) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "GPS data is degenerate (very small range)");
- // 使用均匀采样,避免算法失效
- double step = static_cast<double>(gpsData.size() - 1) / 99;
- for (int i = 0; i < 100; i++) {
- int index = static_cast<int>(i * step);
- sampledData.push_back(gpsData[index]);
- }
- } else {
- // 转换GPS坐标到屏幕坐标
- std::vector<Point> screenPoints;
- screenPoints.reserve(gpsData.size());
-
- for (const auto &point : gpsData) {
- double normLat = (point.latitude - minLat) / rangeLat;
- double normLon = (point.longitude - minLon) / rangeLon;
- screenPoints.push_back(
- {normLon * 300 + 50, (1.0 - normLat) * 300 + 50});
- }
-
- // 使用Douglas-Peucker算法简化轨迹
- std::vector<Point> simplifiedPoints;
- douglasPeucker(screenPoints, 0.5, simplifiedPoints);
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "After Douglas-Peucker: %zu points",
- simplifiedPoints.size());
-
- // 处理简化结果
- if (simplifiedPoints.size() <= 2) {
- WEARABLE_LOGE(
- WEARABLE_LOG_MODULE_APP,
- "Trajectory over-simplified! Using uniform sampling instead.");
- // 过度简化时回退到均匀采样
- double step = static_cast<double>(gpsData.size() - 1) / 99;
- for (int i = 0; i < 100; i++) {
- int index = static_cast<int>(i * step);
- sampledData.push_back(gpsData[index]);
- }
- } else if (simplifiedPoints.size() > 100) {
- // 简化后仍超过100点,再次均匀采样
- double step = static_cast<double>(simplifiedPoints.size() - 1) / 99;
- for (int i = 0; i < 100; i++) {
- int index = static_cast<int>(i * step);
- // 屏幕坐标反推回GPS坐标
- double normX = (simplifiedPoints[index].x - 50) / 300;
- double normY = (simplifiedPoints[index].y - 50) / 300;
- double lat = minLat + (1.0 - normY) * rangeLat;
- double lon = minLon + normX * rangeLon;
- sampledData.push_back({0, lat, lon});
- }
- } else {
- // 简化后点数量合适,直接使用
- for (const auto &point : simplifiedPoints) {
- double normX = (point.x - 50) / 300;
- double normY = (point.y - 50) / 300;
- double lat = minLat + (1.0 - normY) * rangeLat;
- double lon = minLon + normX * rangeLon;
- sampledData.push_back({0, lat, lon});
- }
- }
- }
- } else {
- // GPS点数不足100,直接使用全部数据
- sampledData = gpsData;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "Using all %zu GPS points (no sampling)", sampledData.size());
- }
-
- // 映射到画布坐标
- canvasData.clear();
- canvasData.reserve(sampledData.size());
-
- // 如果没有采样数据,直接返回
- if (sampledData.empty()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "No sampled data to map!");
- return;
- }
-
- // 基于采样数据重新计算经纬度范围
- double minLat = 90.0, maxLat = -90.0;
- double minLon = 180.0, maxLon = -180.0;
-
- for (const auto &point : sampledData) {
- minLat = getmin(minLat, point.latitude);
- maxLat = getmax(maxLat, point.latitude);
- minLon = getmin(minLon, point.longitude);
- maxLon = getmax(maxLon, point.longitude);
- }
-
- double rangeLat = maxLat - minLat;
- double rangeLon = maxLon - minLon;
-
- // 避免除零错误
- if (rangeLat == 0)
- rangeLat = 1e-10;
- if (rangeLon == 0)
- rangeLon = 1e-10;
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "Sampled GPS range: lat=[%.6f, %.6f], lon=[%.6f, %.6f]", minLat,
- maxLat, minLon, maxLon);
-
- // 映射每个点到画布
- for (const auto &point : sampledData) {
- double normLat = (point.latitude - minLat) / rangeLat;
- double normLon = (point.longitude - minLon) / rangeLon;
-
- // 转换到画布坐标
- double x = normLon * 300 + 50;
- double y = (1.0 - normLat) * 300 + 50;
-
- canvasData.push_back({x, y});
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "Mapped point: (%.6f, %.6f) -> (%.2f, %.2f)", point.latitude,
- point.longitude, x, y);
- }
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Generated %zu canvas points",
- canvasData.size());
-}
-
-// 添加经纬度数据集
-void TraingModel::AddGPSData(uint32_t timestamp, double latitude,
- double longitude) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== AddGPSData函数调用开始 ===");
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "输入参数: timestamp=%u, latitude=%.6f, longitude=%.6f",
- timestamp, latitude, longitude);
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "当前GPS数据点数量: %zu",
- gpsData.size());
-
- // 判断数据是否合理
- if (latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "错误: GPS数据无效 - 纬度=%.6f (范围: -90~90), 经度=%.6f "
- "(范围: -180~180)",
- latitude, longitude);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "=== AddGPSData函数调用结束(数据无效) ===");
- return;
- }
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS数据有效性检查通过");
-
- // 校验时间戳是否递增
- if (!gpsData.empty() && timestamp <= gpsData.back().timestamp) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "错误: 时间戳未递增 - 当前=%u, 上一个=%u, 差值=%d", timestamp,
- gpsData.back().timestamp,
- (int)timestamp - (int)gpsData.back().timestamp);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "=== AddGPSData函数调用结束(时间戳错误) ===");
- return;
- }
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "时间戳递增检查通过");
-
- // 添加数据
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "准备添加GPS数据到容器...");
- gpsData.push_back({timestamp, latitude, longitude});
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "成功添加GPS数据 #%zu: 时间戳=%u, 坐标=(%.6f, %.6f)",
- gpsData.size(), timestamp, latitude, longitude);
-
- // 打印当前GPS数据统计信息
- if (gpsData.size() > 1) {
- double lat_diff = latitude - gpsData[gpsData.size() - 2].latitude;
- double lon_diff = longitude - gpsData[gpsData.size() - 2].longitude;
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "与上一个点的距离差: 纬度差=%.6f, 经度差=%.6f", lat_diff,
- lon_diff);
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== AddGPSData函数调用结束 ===");
-}
-
-//保存canvas数据
-bool TraingModel::SaveCanvasData() {
-
- //打印一下gps的数据
- for (const auto &point : gpsData) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "GPS Data: Latitude = %f, Longitude = %f", point.latitude,
- point.longitude);
- }
-
- if (canvasData.size() == 0) {
- WEARABLE_LOGE(WEARING_LOG_MODULE_APP, "canvasData is empty");
- return false;
- }
-
- CreateDirectoryIfNotExists(TRAINING_CANVAS_FILE);
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "canvasData.size() = %zu",
- canvasData.size());
-
- std::string filePath =
- TRAINING_CANVAS_FILE + std::to_string(sportdata->starttime) + ".bin";
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveCanvasData filePath: %s",
- filePath.c_str());
-
- std::ofstream gpsFile(filePath, std::ios::binary);
- if (!gpsFile) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
- filePath.c_str());
- return false; // 返回失败
- }
- // 写入gps数据
- if (!gpsFile.write(reinterpret_cast<const char *>(canvasData.data()),
- canvasData.size() * sizeof(Point))) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "写入GPS数据失败。");
- return false; // 返回失败
- }
-
- gpsFile.close();
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData Success.");
- return true; // 返回成功
-}
-// 清除canvas数据
-void TraingModel::ClearCanvasData() { canvasData.clear(); }
-// 获取轨迹点集
-std::vector<Point> TraingModel::GetCanvasData() const { return canvasData; }
-// 获取canvas数据
-std::string TraingModel::GetCanvasDataPath(uint32_t timestamp) {
- // 首先检查完全匹配的时间戳
- std::string exactFilePath =
- TRAINING_CANVAS_FILE + std::to_string(timestamp) + ".bin";
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "canvas表: %s", exactFilePath.c_str());
- if (IsFileAccessibleAndNotEmpty(exactFilePath)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到canvas表: %s",
- exactFilePath.c_str());
- return exactFilePath; // 返回完全匹配的文件路径
- }
-
- // 如果未找到,检查时间戳相差1到2秒的文件
- for (int i = -2; i <= 2; ++i) {
- uint32_t checkTimestamp = timestamp + i;
- std::string filePath =
- TRAINING_CANVAS_FILE + std::to_string(checkTimestamp) + ".bin";
- if (IsFileAccessibleAndNotEmpty(filePath)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到canvas表: %s",
- filePath.c_str());
- return filePath; // 返回找到的文件路径
- }
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的找到canvas表。");
- return ""; // 未找到文件
-}
-// 加载GPS数据
-bool TraingModel::LoadCanvasData(const std::string &filePath) {
- std::ifstream gpsFile(filePath, std::ios::binary);
- if (!gpsFile) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
- filePath.c_str());
- return false;
- }
-
- // 清空现有数据
- canvasData.clear();
-
- // 动态读取数据(假设文件格式允许逐点读取)
- Point point;
- while (gpsFile.read(reinterpret_cast<char *>(&point), sizeof(Point))) {
- canvasData.push_back(point);
- }
-
- gpsFile.close();
-
- if (canvasData.empty()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未读取到有效数据: %s",
- filePath.c_str());
- return false;
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "LoadCanvasData Success. 加载了 %zu 个点", canvasData.size());
- return true;
-}
-//////////////////////////////////////gps存储///////////////////////////////////////
-//存储gps数据
-bool TraingModel::SaveGPSData() {
- //打印gps数据
- for (const auto &point : gpsData) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
- "GPS Data: Latitude = %f, Longitude = %f", point.`latitude,
- point.longitude);
- }
- CreateDirectoryIfNotExists(TRAINING_GPS_FILE);
- std::string filePath =
- TRAINING_GPS_FILE + std::to_string(sportdata->starttime) + ".bin";
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData filePath: %s",
- filePath.c_str());
-
- std::ofstream gpsFile(filePath, std::ios::binary);
- if (!gpsFile) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
- filePath.c_str());
- return false; // 返回失败
- }
- // 写入gps数据
- if (!gpsFile.write(reinterpret_cast<const char *>(gpsData.data()),
- gpsData.size() * sizeof(GPSData))) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "写入GPS数据失败。");
- return false; // 返回失败
- }
- gpsFile.close();
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData Success.");
- return true;
-}
-std::string TraingModel::GetGPSDataPath(uint32_t timestamp) {
- // 首先检查完全匹配的时间戳
- std::string exactFilePath =
- TRAINING_GPS_FILE + std::to_string(timestamp) + ".bin";
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "gps表: %s", exactFilePath.c_str());
- if (IsFileAccessibleAndNotEmpty(exactFilePath)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到gps表: %s",
- exactFilePath.c_str());
- return exactFilePath; // 返回完全匹配的文件路径
- }
-
- // 如果未找到,检查时间戳相差1到2秒的文件
- for (int i = -2; i <= 2; ++i) {
- uint32_t checkTimestamp = timestamp + i;
- std::string filePath =
- TRAINING_GPS_FILE + std::to_string(checkTimestamp) + ".bin";
- if (IsFileAccessibleAndNotEmpty(filePath)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到gps表: %s", filePath.c_str());
- return filePath; // 返回找到的文件路径
- }
- }
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的找到gps表。");
- return "";
-}
-
-//获取GPS连接状态
-bool TraingModel::GetGPSConnectState() { return gpsconnect; }
-//设置GPS连接状态
-void TraingModel::SetGPSConnectState(bool state) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS连接状态设置 ===");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "当前GPS连接状态: %d, 新状态: %d",
- gpsconnect, state);
-
- if (gpsconnect != state) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS连接状态发生变化: %d -> %d",
- gpsconnect, state);
- gpsconnect = state;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS连接状态已更新为: %d",
- gpsconnect);
-
- //状态改变记录
- // SystemLogModel::GetInstance()->save_gps_status_log_type(gpsconnect);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS状态变化记录完成");
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS连接状态未发生变化,保持: %d",
- gpsconnect);
- }
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS连接状态设置完成 ===");
-}
-//运动心率、gps开启准备
-void TraingModel::start_training_heart() {
- if (gpsopen == false) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始启动GPS定位服务...");
- //开启心率检测
-#ifdef SUPPORT_PPG_SERVICE
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "启动心率检测服务");
- ppg_hr_start();
-#endif
- gpsopen = true;
- }
-}
-//运动心率、gps关闭
-void TraingModel::stop_heart_gps() {
- if (gpsopen == true) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始关闭GPS定位服务...");
- // 关闭gps
-#if defined(SUPPORT_GNSS)
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "调用gnss_stop()关闭GPS服务");
- gnss_stop();
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "gnss_stop()调用完成");
-#endif
-
- //关闭心率检测
-#ifdef SUPPORT_PPG_SERVICE
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "关闭心率检测服务");
- ppg_hr_stop();
-#endif
-
- gpsopen = false;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "GPS定位服务关闭完成,gpsopen设置为false");
- }
-}
-
-/////////////////////////////////////位置服务定时器/////////////////////////////////////
-/*
- * 位置服务定时器说明:
- * - 功能:负责获取GPS定位数据,更新GPS连接状态,添加经纬度数据
- * - 频率:1秒间隔,保证位置数据的实时性
- * - 触发条件:仅在训练进行中且训练状态非结束时执行
- */
-static osTimerId_t g_location_timer = nullptr;
-static int location_timer_interval = 1; // 位置数据获取间隔,单位秒
-static int gps_timer_count = 0;
-
-/*位置服务定时器到期时的处理*/
-static void gnss_timer_callbackProc(int data) {
- /*
- GPS数据获取处理
- 1、目前gps注册有问题 ;需要延迟2s 去开启gps;
+ i++;
+ }
+ return NULL;
+ }
+
+ extern bleInstruct_t_ bleInstruct_t;
+
+ void ble_training_init(void) {
+ bleInstruct_t.get_training_spead_path_cb = get_training_spead_path_;
+ bleInstruct_t.get_training_heart_path_cb = get_training_heart_path_;
+ bleInstruct_t.get_training_gps_path_cb = get_training_gps_path_;
+ bleInstruct_t.struct_to_json_buf_malloc = struct_to_json_buf_malloc_;
+ bleInstruct_t.read_gps_data_cb = read_gps_data_;
+ bleInstruct_t.ble_get_Trainingdata_by_timetamp = ble_get_Trainingdata_by_timetamp_;
+ }
+
+ #ifdef __cplusplus
+ #if __cplusplus
+ }
+ #endif /* __cplusplus */
+ #endif /* __cplusplus */
+
+ #endif
+
+ /*
+ * 定时器分工总结:
+ *
+ * 1. 位置服务定时器 (g_location_timer)
+ * - 频率:1秒间隔
+ * - 功能:GPS定位、连接状态更新、轨迹数据采集
+ * - 特点:中等频率,保证位置数据实时性
+ *
+ * 2. 运动数据采集定时器 (g_sport_start_data_timer)
+ * - 频率:300毫秒间隔
+ * - 功能:运动数据采集、心率监测、目标完成度检查、UI更新
+ * - 特点:高频率,保证运动数据实时性和准确性
+ *
+ * 数据获取函数封装(职责单一,无耦合):
+ * - GetSportActivityData(): 获取运动基础数据,返回是否成功
+ * - GetSportExtraData(): 获取运动额外数据,返回是否成功
+ * - GetHeartRateData(): 获取心率数据,返回心率值
+ * - 数据分析和处理逻辑在定时器回调中协调完成
+ *
+ * 两个定时器独立运行,互不干扰,根据各自功能特点选择合适的执行频率
*/
- gps_timer_count++;
- if (gps_timer_count < 2) {
- return;
- }
- if (gps_timer_count == 2) {
-#if defined(SUPPORT_GNSS)
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开启gps");
- // gnss_start();
-#endif
- }
-#if defined(SUPPORT_GNSS)
- gps_data_t send_gps_data = {0};
- gps_data_t *gps_data = getPGPSGlobaldata();
-
- if (gps_data == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "错误: gps_data == NULL,GPS全局数据获取失败");
- return;
- }
-
- if (gps_data->last_gps_time_ms > 0) {
- memcpy_s(&send_gps_data, sizeof(gps_data_t), gps_data, sizeof(gps_data_t));
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS时间戳无效,跳过本次处理");
- return;
- }
-
- // 判断是否定位成功并设置连接状态
- bool isGpsConnected = (send_gps_data.status > 1);
- TraingModel::GetInstance()->SetGPSConnectState(isGpsConnected);
-
- // 判断是否在运动或训练已结束
- int train_status = uapi_dev_runtime_get_train_status();
- ExerciseState sport_state = TraingModel::GetInstance()->GetSportState();
-
- if (train_status == 0 || sport_state == ExerciseState::End) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "运动已结束或未开始,跳过GPS数据添加");
- return;
- }
-
- // 如果定位成功,添加经纬度数据
- if (isGpsConnected) {
- uint32_t time = TraingModel::GetInstance()->get_current_time();
- TraingModel::GetInstance()->AddGPSData(time, send_gps_data.location.lat,
- send_gps_data.location.lng);
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定位失败,跳过数据添加");
- }
-#endif
-}
-
-/*
- * @brief 位置服务定时器回调函数
- */
-static void gnss_timer_callback(int data) {
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "gnss_timer_callback %d", data);
- GraphicService::GetInstance()->PostGraphicEvent(
- std::bind(gnss_timer_callbackProc, data));
-}
-
-/*
- * @brief 开启位置服务定时器函数
- * @param interval 时间间隔,单位ms
- */
-void TraingModel::gnss_start_timer(uint32_t interval) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS定时器启动流程开始 ===");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "定时器间隔: %u ms", interval);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "当前定时器状态: %p",
- g_location_timer);
-
- /* -------------------------- 1.检查位置服务定时器是否已经存在,如果存在则返回
- * ------------------------- */
- if (g_location_timer != nullptr) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: GPS定时器已存在,跳过创建");
- return;
- }
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器不存在,开始创建...");
-
- /* --------------------------------- 2.创建位置服务定时器
- * -------------------------------- */
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "调用osTimerNew创建GPS定时器...");
- g_location_timer = osTimerNew((osTimerFunc_t)gnss_timer_callback,
- osTimerPeriodic, nullptr, nullptr);
- // 检查定时器创建是否成功
- if (g_location_timer == nullptr) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: GPS定时器创建失败!!");
- return;
- }
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器创建成功: %p",
- g_location_timer);
-
- /* ----------------------------- 3.将毫秒转换为系统时钟节拍数
- * ---------------------------- */
- int tick = osMs2Tick(interval);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "时间转换: %u ms -> %d ticks",
- interval, tick);
-
- /* --------------------------------- 4.启动位置服务定时器
- * -------------------------------- */
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "启动GPS定时器...");
- osStatus_t ret = osTimerStart(g_location_timer, tick);
- /* -------------------- 5.检查位置服务定时器启动是否成功,如果启动失败,删除定时器并置为
- * nullptr -------------------- */
- if (ret != osOK) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "错误: GPS定时器启动失败, ret = %lx!!", ret);
- if (g_location_timer != nullptr) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "删除失败的GPS定时器...");
- ret = osTimerDelete(g_location_timer);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器删除结果: %lx", ret);
- }
- g_location_timer = nullptr;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "=== GPS定时器启动流程结束(失败) ===");
- return;
- }
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器启动成功");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS定时器启动流程结束(成功) ===");
-}
-
-/*
- * @brief 停止位置服务定时器
- */
-void TraingModel::gnss_stop_timer(void) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS定时器停止流程开始 ===");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "当前定时器状态: %p",
- g_location_timer);
-
- // 检查位置服务定时器是否存在
- if (g_location_timer != nullptr) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器存在,开始停止流程...");
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "停止GPS定时器...");
- osTimerStop(g_location_timer);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器已停止");
-
- // 删除定时器并置为 nullptr
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "删除GPS定时器...");
- osStatus_t ret = osTimerDelete(g_location_timer);
- if (ret != osOK) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "错误: GPS定时器删除失败, ret = %lx!!", ret);
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器删除成功");
- }
- g_location_timer = nullptr;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器指针已置空");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== GPS定时器停止流程结束 ===");
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器不存在,跳过停止流程");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "=== GPS定时器停止流程结束(无需停止) ===");
- }
- gps_timer_count = 0;
-}
-
-///////////////////////////////////////运动数据采集定时器//////////////////////////////////
-/*
- * 运动数据采集定时器说明:
- * - 功能:负责获取运动数据(步数、距离、卡路里、心率等),更新训练目标完成度
- * - 频率:300ms间隔,保证运动数据的实时性和准确性
- * - 触发条件:仅在训练进行中且训练状态非结束时执行
- * - 包含服务:CYWEE运动数据、PPG心率数据、训练目标检查、UI更新
- */
-#if SPORT_TIMER_DATA
-// 声明运动数据采集定时器变量
-static osTimerId_t g_sport_start_data_timer = nullptr;
-static int sport_start_data_timer_interval = 300; // 运动数据获取间隔,单位毫秒
-
-/*
- * @brief 获取运动基础数据
- * @return 是否成功获取数据
- */
-bool TraingModel::GetSportActivityData(void) {
-#ifdef SUPPORT_CYWEE_SERVICE
- act_result_t cyweeInfo;
- cywee_get_act_data(&cyweeInfo);
- // 更新运动基础数据
- TraingModel::GetInstance()->AnalysisData(&cyweeInfo);
- // 更新其他目标完成度
- TraingModel::GetInstance()->CheckSportTag();
-#endif
- return true;
-}
-
-/*
- * @brief 获取运动额外数据
- * @return 是否成功获取数据
- */
-bool TraingModel::GetSportExtraData(void) {
-#ifdef SUPPORT_CYWEE_SERVICE
- act_ext_result_t cyweeInfo_ext;
- cywee_get_act_ext_data(&cyweeInfo_ext);
- TraingModel::GetInstance()->AnalysisExtraData(&cyweeInfo_ext);
-#endif
- return true;
-}
-
-/*
- * @brief 获取心率数据
- * @return 获取到的心率值,如果服务不支持则返回0
- */
-uint8_t TraingModel::GetHeartRateData(void) {
- uint8_t hrs = 0;
-#ifdef SUPPORT_PPG_SERVICE
- hrs = ppg_get_hrs();
-#endif
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Heart rate data: %d", hrs);
- return hrs;
-}
-
-static void sport_data_time_proc(int data) {
- //增加训练runtime控制
- if (uapi_dev_runtime_get_train_status() == 0 ||
- TraingModel::GetInstance()->GetSportState() == ExerciseState::End) {
- return;
- }
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sport_data_time_proc");
-
- // 获取并处理运动基础数据
- TraingModel::GetInstance()->GetSportActivityData();
-
- // 获取并处理运动额外数据 前提是特殊运动
- if (TraingModel::GetInstance()->GetIsSpecial()) {
- TraingModel::GetInstance()->GetSportExtraData();
- }
-
- // 获取并处理心率数据
- uint8_t heartRate = TraingModel::GetInstance()->GetHeartRateData();
- TraingModel::GetInstance()->AnalysisHeartData(heartRate);
-
- // 更新UI
- if (TraingCardPresenter::GetInstance()) {
- TraingCardPresenter::GetInstance()->updataview();
- }
-}
-
-static void sport_data_time_callback(int data) {
- GraphicService::GetInstance()->PostGraphicEvent(
- std::bind(sport_data_time_proc, data));
-}
-
-void TraingModel::sport_start_data_timer(uint32_t interval) {
- WEARABLE_LOGE(
- WEARABLE_LOG_MODULE_APP,
- "TraingModel::sport_start_data_timer - Starting sport data timer");
- if (g_sport_start_data_timer != nullptr) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sport Data Timer already exists!!");
- return;
- }
- g_sport_start_data_timer = osTimerNew((osTimerFunc_t)sport_data_time_callback,
- osTimerPeriodic, nullptr, nullptr);
- if (g_sport_start_data_timer == nullptr) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Sport Data Timer create failed!!");
- return;
- }
- int tick = osMs2Tick(interval);
- osStatus_t ret = osTimerStart(g_sport_start_data_timer, tick);
- if (ret != osOK) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "Sport Data Timer start failed, ret = %lx!!", ret);
- if (g_sport_start_data_timer != nullptr) {
- ret = osTimerDelete(g_sport_start_data_timer);
- }
- g_sport_start_data_timer = nullptr;
- return;
- }
-}
-
-void TraingModel::sport_stop_data_timer(void) {
- // 检查运动数据采集定时器是否存在
- if (g_sport_start_data_timer != nullptr) {
- WEARABLE_LOGE(
- WEARABLE_LOG_MODULE_APP,
- "TraingModel::sport_stop_data_timer - Stopping sport data timer");
- osTimerStop(g_sport_start_data_timer);
- // 删除定时器并置为 nullptr
- osStatus_t ret = osTimerDelete(g_sport_start_data_timer);
- if (ret != osOK) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "Sport Data Timer delete failed, ret = %lx!!", ret);
- }
- g_sport_start_data_timer = nullptr;
- }
-}
-#endif
-
-#ifdef BLE_INSTRUCT
-
-#include "bleEvent.h"
-#include "bleInstruct.h"
-
-//////////////////////////////ble////////////////////////////////////////////
-#ifdef __cplusplus
-#if __cplusplus
-extern "C" {
-#endif /* __cplusplus */
-#endif /* __cplusplus */
-
-bool get_training_spead_path_(char *path, uint32_t time, uint32_t max_len) {
- std::string str = OHOS::TraingModel::GetInstance()->GetPaceTablePath(time);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "get_training_spead:%s\r\n",
- str.c_str());
- if (str.length() <= 3) {
- return false;
- }
- memcpy_s(path, max_len, str.c_str(), str.length());
- return true;
-}
-
-bool get_training_heart_path_(char *path, uint32_t time, uint32_t max_len) {
- std::string str = OHOS::TraingModel::GetInstance()->GetHeartTablePath(time);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "get_training_heart:%s\r\n",
- str.c_str());
- if (str.length() <= 3) {
- return false;
- }
- memcpy_s(path, max_len, str.c_str(), str.length());
- return true;
-}
-
-//获取gps轨迹路径
-bool get_training_gps_path_(char *path, uint32_t time, uint32_t max_len) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 获取GPS轨迹路径开始 ===");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "请求时间戳: %u, 最大路径长度: %u",
- time, max_len);
-
- std::string str = OHOS::TraingModel::GetInstance()->GetGPSDataPath(time);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "获取到的GPS路径: %s", str.c_str());
-
- if (str.length() <= 3) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: GPS路径长度不足,长度: %zu",
- str.length());
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 获取GPS轨迹路径结束(失败) ===");
- return false;
- }
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS路径长度: %zu", str.length());
- memcpy_s(path, max_len, str.c_str(), str.length());
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS路径复制完成");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 获取GPS轨迹路径结束(成功) ===");
- return true;
-}
-
-char *struct_to_json_buf_malloc_(uint8_t day) {
- cJSON *dataJSON = cJSON_CreateObject();
- cJSON *data_arryJSON = cJSON_CreateArray();
- struct Trainingdata *data = NULL;
- int i = 0;
- // char file_path[128] = {0};
- char timeday[12] = {0};
- uapi_get_YYMMDD_Time_extend(timeday, '_', day);
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "%s\r\n", timeday);
- // sprintf(file_path, "%s%s.bin", TRAINING_PATH_BLE, timeday);
-
- while (data = read_Trainingdata_data(timeday, i)) {
- cJSON *itemJSON = cJSON_CreateObject();
- if (data != NULL) {
- cJSON_AddNumberToObject(itemJSON, "src", data->src);
- cJSON_AddNumberToObject(itemJSON, "bt", data->bt);
- cJSON_AddNumberToObject(itemJSON, "ty", data->ty);
- cJSON_AddNumberToObject(itemJSON, "st", data->st);
- cJSON_AddNumberToObject(itemJSON, "sp", data->sp);
- cJSON_AddNumberToObject(itemJSON, "tgt", data->tgt);
- cJSON_AddNumberToObject(itemJSON, "tg", data->tg);
- cJSON_AddNumberToObject(itemJSON, "pg", data->pg);
- cJSON_AddNumberToObject(itemJSON, "du", data->du);
- cJSON_AddNumberToObject(itemJSON, "ca", data->ca);
- cJSON_AddNumberToObject(itemJSON, "di", data->di);
- cJSON_AddNumberToObject(itemJSON, "hr", data->hr);
- cJSON_AddNumberToObject(itemJSON, "stp", data->stp);
- cJSON_AddNumberToObject(itemJSON, "ti", data->ti);
- cJSON_AddNumberToObject(itemJSON, "cir", data->cir);
- cJSON_AddNumberToObject(itemJSON, "alt", data->alt);
- cJSON_AddNumberToObject(itemJSON, "apc", data->apc);
- cJSON_AddNumberToObject(itemJSON, "fpc", data->fpc);
- cJSON_AddNumberToObject(itemJSON, "spc", data->spc);
- cJSON_AddNumberToObject(itemJSON, "std", data->std);
- cJSON_AddNumberToObject(itemJSON, "cdc", data->cdc);
- cJSON_AddItemReferenceToArray(data_arryJSON, itemJSON);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"*******************************");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"src:%d",data->src);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"bt:%d",data->bt);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ty:%d",data->ty);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"st:%d",data->st);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"sp:%d",data->sp);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"tgt:%d",data->tgt);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"tg:%d",data->tg);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"pg:%d",data->pg);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"du:%d",data->du);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ca:%d",data->ca);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"di:%d",data->di);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"hr:%d",data->hr);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"stp:%d",data->stp);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ti:%d",data->ti);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"cir:%d",data->cir);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"alt:%d",data->alt);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"apc:%d",data->apc);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"fpc:%d",data->fpc);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"spc:%d",data->spc);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"std:%d",data->std);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"cdc:%d",data->cdc);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"*******************************");
- free(data);
- data = NULL;
- } else {
- break;
- }
- i++;
- }
- cJSON_AddItemToObject(dataJSON, "recs", data_arryJSON);
-
- char *json_format = cJSON_PrintUnformatted(dataJSON);
-
- int pSize = cJSON_GetArraySize(dataJSON);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "训练历史数据:%s\r\n", json_format);
- cJSON_Delete(dataJSON);
- return json_format;
-}
-
-int read_gps_data_(char *path, uint8_t *out_buf) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 读取GPS数据开始 ===");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS文件路径: %s", path);
-
- FILE *fp = fopen(path, "rb+");
- if (fp == NULL) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: 无法打开GPS文件");
- return 0;
- }
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS文件打开成功");
-
- uint32_t file_size = 0;
- fseek(fp, 0, SEEK_END);
- file_size = ftell(fp);
- fseek(fp, 0, SEEK_SET);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS文件大小: %u 字节", file_size);
-
- int len = 0;
- int point_count = 0;
- OHOS::GPSData_t data = {0};
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始读取GPS轨迹点...");
- while (1) {
- int read_size = fread(&data, 1, sizeof(data), fp);
- if (read_size == sizeof(data)) {
- point_count++;
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "读取GPS点 #%d: 时间戳=%u, 纬度=%.6f, 经度=%.6f",
- point_count, data.timestamp, data.latitude, data.longitude);
-
- int temp_data = data.timestamp;
- memcpy_s(out_buf + len, 4, &temp_data, sizeof(data.timestamp));
- len += 4;
-
- int temp_latitude = (int)(data.latitude * 10000000);
- memcpy_s(out_buf + len, 4, &temp_latitude, sizeof(temp_latitude));
- len += 4;
-
- int temp_longitude = (int)(data.longitude * 10000000);
- memcpy_s(out_buf + len, 4, &temp_longitude, sizeof(temp_longitude));
- len += 4;
-
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "GPS点数据转换: 时间戳=%u, 纬度=%d, 经度=%d", temp_data,
- temp_latitude, temp_longitude);
- } else {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS文件读取结束,读取大小: %d",
- read_size);
- break;
- }
- }
-
- fclose(fp);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS文件关闭");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "总共读取 %d 个GPS点,输出数据长度: %d 字节", point_count, len);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 读取GPS数据结束 ===");
-
- return len;
-}
-
-//通过时间戳获取训练数据
-cJSON* ble_get_Trainingdata_by_timetamp_(uint32_t timetamp)
-{
- struct Trainingdata * data=NULL;
- int i=0;
-
- time_t currtime=timetamp;
- struct tm *timeinfo;
- timeinfo = localtime(&currtime);
- int y=timeinfo->tm_year+ 1900;
- int m=timeinfo->tm_mon+1;
- int d=timeinfo->tm_mday;
- int h=timeinfo->tm_hour;
- int min=timeinfo->tm_min;
-
- char timeday[24]={0};
- sprintf_s(timeday,24,"%d_%02d_%02d",y,m,d);
-
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,"%s\r\n",timeday);
-
- while(data=read_Trainingdata_data(timeday, i))
- {
- if(data!=NULL)
- {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"%d,%d\r\n",timetamp,data->st);
- // 打印data数据
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"*******************************");
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"src:%d",data->src);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"bt:%d",data->bt);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ty:%d",data->ty);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"st:%d",data->st);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"sp:%d",data->sp);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"tgt:%d",data->tgt);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"tg:%d",data->tg);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"pg:%d",data->pg);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"du:%d",data->du);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ca:%d",data->ca);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"di:%d",data->di);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"hr:%d",data->hr);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"stp:%d",data->stp);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ti:%d",data->ti);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"cir:%d",data->cir);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"alt:%d",data->alt);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"apc:%d",data->apc);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"fpc:%d",data->fpc);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"spc:%d",data->spc);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"std:%d",data->std);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"cdc:%d",data->cdc);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"*******************************");
- int time_error= timetamp>data->st ? timetamp-data->st:data->st-timetamp;
- if(time_error<=2)
- {
- cJSON* itemJSON = cJSON_CreateObject();
- cJSON_AddNumberToObject(itemJSON, "src", data->src);
- cJSON_AddNumberToObject(itemJSON,"bt",data->bt);
- cJSON_AddNumberToObject(itemJSON,"ty",data->ty);
- cJSON_AddNumberToObject(itemJSON,"st",data->st);
- cJSON_AddNumberToObject(itemJSON,"sp",data->sp);
- cJSON_AddNumberToObject(itemJSON,"tgt",data->tgt);
- cJSON_AddNumberToObject(itemJSON,"tg",data->tg);
- cJSON_AddNumberToObject(itemJSON,"pg",data->pg);
- cJSON_AddNumberToObject(itemJSON,"du",data->du);
- cJSON_AddNumberToObject(itemJSON,"ca",data->ca);
- cJSON_AddNumberToObject(itemJSON,"di",data->di);
- cJSON_AddNumberToObject(itemJSON,"hr",data->hr);
- cJSON_AddNumberToObject(itemJSON,"stp",data->stp);
- cJSON_AddNumberToObject(itemJSON,"ti",data->ti);
- cJSON_AddNumberToObject(itemJSON,"cir",data->cir);
- cJSON_AddNumberToObject(itemJSON, "alt", data->alt);
- cJSON_AddNumberToObject(itemJSON,"apc",data->apc);
- cJSON_AddNumberToObject(itemJSON,"fpc",data->fpc);
- cJSON_AddNumberToObject(itemJSON,"spc",data->spc);
- cJSON_AddNumberToObject(itemJSON,"std",data->std);
- cJSON_AddNumberToObject(itemJSON,"cdc",data->cdc);
- free(data);
- return itemJSON;
- }
-
- free(data);
- data=NULL;
- }
- else{
- break;
- }
- i++;
- }
- return NULL;
-}
-
-extern bleInstruct_t_ bleInstruct_t;
-
-void ble_training_init(void) {
- bleInstruct_t.get_training_spead_path_cb = get_training_spead_path_;
- bleInstruct_t.get_training_heart_path_cb = get_training_heart_path_;
- bleInstruct_t.get_training_gps_path_cb = get_training_gps_path_;
- bleInstruct_t.struct_to_json_buf_malloc = struct_to_json_buf_malloc_;
- bleInstruct_t.read_gps_data_cb = read_gps_data_;
- bleInstruct_t.ble_get_Trainingdata_by_timetamp = ble_get_Trainingdata_by_timetamp_;
-}
-
-#ifdef __cplusplus
-#if __cplusplus
-}
-#endif /* __cplusplus */
-#endif /* __cplusplus */
-
-#endif
-
-/*
- * 定时器分工总结:
- *
- * 1. 位置服务定时器 (g_location_timer)
- * - 频率:1秒间隔
- * - 功能:GPS定位、连接状态更新、轨迹数据采集
- * - 特点:中等频率,保证位置数据实时性
- *
- * 2. 运动数据采集定时器 (g_sport_start_data_timer)
- * - 频率:300毫秒间隔
- * - 功能:运动数据采集、心率监测、目标完成度检查、UI更新
- * - 特点:高频率,保证运动数据实时性和准确性
- *
- * 数据获取函数封装(职责单一,无耦合):
- * - GetSportActivityData(): 获取运动基础数据,返回是否成功
- * - GetSportExtraData(): 获取运动额外数据,返回是否成功
- * - GetHeartRateData(): 获取心率数据,返回心率值
- * - 数据分析和处理逻辑在定时器回调中协调完成
- *
- * 两个定时器独立运行,互不干扰,根据各自功能特点选择合适的执行频率
- */
-
-} // namespace OHOS
+
+ } // namespace OHOS
+
\ No newline at end of file