From: Caisking Date: Mon, 13 Oct 2025 08:32:52 +0000 (+0800) Subject: 修复gps无轨迹的显示问题 X-Git-Tag: V1.0.9_20251128_194606_LN~212^2~30 X-Git-Url: http://git.snicker.com.cn/?a=commitdiff_plain;h=e7e632605f2d88fa62b054bcd730d4a60e5191f9;p=mirrors%2FFD230.git 修复gps无轨迹的显示问题 --- diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingEnddataView.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingEnddataView.cpp index f1bd8c385..45874c056 100644 --- a/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingEnddataView.cpp +++ b/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingEnddataView.cpp @@ -30,13 +30,13 @@ namespace OHOS { -static const int initialY = 132 ; +static int initialY = 132 ; static const int lineHeight = 130; static const int iconHeight = 80; static const int verticalOffset = 2; -static const int baseY = 214 ; -static const int startY_Unit = 252 ; -static const int startY_Value = 292 ; +static int baseY = 214 ; +static int startY_Unit = 252 ; +static int startY_Value = 292 ; static const int labelHeight = 40; static const int labelInterval = 130; @@ -205,12 +205,7 @@ void TraingEnddataView ::InitializeLabels() { scrollView_->Add(labelstrong); scrollView_->Add(labelcalories); - // 初始化GPS轨迹失败提示标签(默认隐藏) - InitLabel(gpsNoTrackLabel, 33, 100 + CANVAS_SIZE/2 - 20, CANVAS_SIZE, 40, - "gpsNoTrackLabel", "未定位成功,没有GPS轨迹", DEFAULT_VECTOR_FONT_FILENAME, 24); - gpsNoTrackLabel->SetTextColor(Color::Red()); - gpsNoTrackLabel->SetVisible(false); // 默认隐藏 - scrollView_->Add(gpsNoTrackLabel); + } // 增加额外配置 void TraingEnddataView::BonusdataView() { @@ -485,10 +480,6 @@ void TraingEnddataView::DeInit() { delete labelcalories; labelcalories = nullptr; } - if (gpsNoTrackLabel) { - delete gpsNoTrackLabel; - gpsNoTrackLabel = nullptr; - } /* 删除图片 label */ if (imageIcon) { @@ -773,22 +764,21 @@ void TraingEnddataView::OnStart() { } #if defined(BOARD_W9) || defined(BOARD_N9) ///////////////////////////gps轨迹测试////////////////////// - canvas = new UICanvas(); - canvas->SetHeight(CANVAS_SIZE); - canvas->SetWidth(CANVAS_SIZE); - canvas->SetPosition(33, 100); - scrollView_->Add(canvas); - GenerateTrackPoints(); - // 检查是否有足够的GPS数据来绘制轨迹 std::vector canvasData = TraingModel::GetInstance()->GetCanvasData(); if (canvasData.size() >= 3) { + canvas = new UICanvas(); + canvas->SetHeight(CANVAS_SIZE); + canvas->SetWidth(CANVAS_SIZE); + canvas->SetPosition(33, 100); + scrollView_->Add(canvas); + GenerateTrackPoints(); // 有足够数据,绘制轨迹 DrawTrackSegment(canvas); DrawMarkers(canvas); } else { - // 数据不足,只显示提示标签(已在GenerateTrackPoints中处理) - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS轨迹数据不足,不绘制轨迹"); + // 数据不足,只显示提示标签 + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS轨迹数据不足,不绘制轨迹"); } #endif } @@ -806,20 +796,6 @@ void TraingEnddataView::GenerateTrackPoints() { seg1.color = Color::Blue(); std::vector canvasData = TraingModel::GetInstance()->GetCanvasData(); - //最小轨迹点数限制,防止绘制失败 - if (canvasData.size() < 3) { - // GPS数据不足,显示提示标签 - if (gpsNoTrackLabel) { - gpsNoTrackLabel->SetVisible(true); - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS轨迹数据不足,显示提示标签"); - } - return; - } - - // GPS数据充足,隐藏提示标签 - if (gpsNoTrackLabel) { - gpsNoTrackLabel->SetVisible(false); - } //将点位赋值 for (size_t i = 0; i < canvasData.size(); ++i) { diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingModel.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingModel.cpp index aad12df03..4e8f79cd6 100644 --- a/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingModel.cpp +++ b/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingModel.cpp @@ -5,3634 +5,3701 @@ * @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 -#include -#include -#include -#include -#include -#include -#include - -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "chip_system_time.h" -#include "cmath" -#include "system/include/SystemLogModel.h" -#include - -#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, " 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(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 + #include + #include + #include + #include + #include + #include + #include + + #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 + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include "chip_system_time.h" + #include "cmath" + #include "system/include/SystemLogModel.h" + #include + + #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, " 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(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(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 &heartRates) { + if (heartRates.size() <= 2) { + // 如果心率数据点少于等于2,无法去掉最高和最低值 + return 0.0; // 或者返回心率的平均值 + } + + // 创建一个副本以避免修改原始数据 + std::vector 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, "开始运动"); + + 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(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(sportdata->duration) / current_goal) * 100; + break; + } + case ExerciseTag::Calories: { + // 计算目标完成度 + goaldegree = (static_cast(sportdata->calories) / current_goal) * 100; + break; + } + case ExerciseTag::Distance: { + // 计算目标完成度 + goaldegree = (static_cast(sportdata->distance) / current_goal) * 100; + break; + } + case ExerciseTag::Steps: { + // 计算目标完成度 + goaldegree = (static_cast(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(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(sportdata->tag * 60); + break; + } + case ExerciseTag::Calories: { + // 计算目标完成度 + return sportdata->tag; + break; + } + case ExerciseTag::Distance: { + return static_cast(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(sportdata->sportType); + data.ty = static_cast( + GetBleSportId(sportdata->sportType, sportdata->sportId)); + data.st = static_cast(sportdata->starttime); + data.sp = static_cast(sportdata->endtime); + data.tgt = static_cast(sportdata->sportTagType); + if (sportdata->sportTagType == ExerciseTag::Distance) { + data.tg = static_cast(sportdata->tag * 1000); + } else { + data.tg = static_cast(sportdata->tag); + } + data.pg = static_cast(sportdata->goaldegree); + data.du = static_cast(sportdata->duration); + data.ca = static_cast(sportdata->calories * 10); + data.di = static_cast(sportdata->distance); + data.hr = static_cast(sportdata->avgheart); + data.stp = static_cast(sportdata->steps); + data.ti = static_cast(sportdata->intensity); + /* 230 目前不支持【圈数】【最高海拔高度】 */ + data.cir = 0; + data.alt = 0; + /* 配速相关 */ + data.apc = static_cast(sportdata->apc); + data.fpc = static_cast(sportdata->fpc); + data.spc = static_cast(sportdata->spc); + data.std = static_cast(sportdata->std); + data.cdc = static_cast(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(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(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 + #include + #include + #include + + 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( + // std::chrono::duration_cast( + // 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 // 确保包含该头文件以使用布尔类型 + + // 全局变量 + 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(&sportdata), size); + fd.close(); + return 1; + } + static std::vector LoadTrainingDataFromTodayFile() { + std::vector 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(&record), sizeof(Trainingdata)); + if (fd.gcount() == sizeof(Trainingdata)) { + trainingRecords.push_back(record); + } + } + + fd.close(); + + return trainingRecords; + } + + #ifdef __cplusplus + extern "C" { + #endif + + #include + #include + #include + #include + #include + + // 获得数据个数 + 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 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 &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(file)), + std::istreambuf_iterator()); + 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 &points, double epsilon, + std::vector &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 recResults1, recResults2; + + // 递归处理前半部分 + douglasPeucker( + std::vector(points.begin(), points.begin() + index + 1), epsilon, + recResults1); + + // 递归处理后半部分 + douglasPeucker(std::vector(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 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(gpsData.size() - 1) / 99; + for (int i = 0; i < 100; i++) { + int index = static_cast(i * step); + sampledData.push_back(gpsData[index]); + } + } else { + // 转换GPS坐标到屏幕坐标 + std::vector 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 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(gpsData.size() - 1) / 99; + for (int i = 0; i < 100; i++) { + int index = static_cast(i * step); + sampledData.push_back(gpsData[index]); + } + } else if (simplifiedPoints.size() > 100) { + // 简化后仍超过100点,再次均匀采样 + double step = static_cast(simplifiedPoints.size() - 1) / 99; + for (int i = 0; i < 100; i++) { + int index = static_cast(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(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 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(&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(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更新 */ - - 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(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 &heartRates) { - if (heartRates.size() <= 2) { - // 如果心率数据点少于等于2,无法去掉最高和最低值 - return 0.0; // 或者返回心率的平均值 - } - - // 创建一个副本以避免修改原始数据 - std::vector 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, "开始运动"); - - 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 SPORT_TIMER_DATA + // 声明运动数据采集定时器变量 + static osTimerId_t g_sport_start_data_timer = nullptr; + static int sport_start_data_timer_interval = 300; // 运动数据获取间隔,单位毫秒 + + /* + * @brief 获取运动基础数据 + * @return 是否成功获取数据 */ - // 确保最大心率为正值 - if (user_Intensity <= 0) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "maxHeartRate %d is error", - user_Intensity); - return; - } - - // 将 heart 转换为 double 以避免整数除法 - double heartRateAsDouble = static_cast(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(sportdata->duration) / current_goal) * 100; - break; - } - case ExerciseTag::Calories: { - // 计算目标完成度 - goaldegree = (static_cast(sportdata->calories) / current_goal) * 100; - break; - } - case ExerciseTag::Distance: { - // 计算目标完成度 - goaldegree = (static_cast(sportdata->distance) / current_goal) * 100; - break; - } - case ExerciseTag::Steps: { - // 计算目标完成度 - goaldegree = (static_cast(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(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(sportdata->tag * 60); - break; - } - case ExerciseTag::Calories: { - // 计算目标完成度 - return sportdata->tag; - break; - } - case ExerciseTag::Distance: { - return static_cast(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(sportdata->sportType); - data.ty = static_cast( - GetBleSportId(sportdata->sportType, sportdata->sportId)); - data.st = static_cast(sportdata->starttime); - data.sp = static_cast(sportdata->endtime); - data.tgt = static_cast(sportdata->sportTagType); - if (sportdata->sportTagType == ExerciseTag::Distance) { - data.tg = static_cast(sportdata->tag * 1000); - } else { - data.tg = static_cast(sportdata->tag); - } - data.pg = static_cast(sportdata->goaldegree); - data.du = static_cast(sportdata->duration); - data.ca = static_cast(sportdata->calories * 10); - data.di = static_cast(sportdata->distance); - data.hr = static_cast(sportdata->avgheart); - data.stp = static_cast(sportdata->steps); - data.ti = static_cast(sportdata->intensity); - /* 230 目前不支持【圈数】【最高海拔高度】 */ - data.cir = 0; - data.alt = 0; - /* 配速相关 */ - data.apc = static_cast(sportdata->apc); - data.fpc = static_cast(sportdata->fpc); - data.spc = static_cast(sportdata->spc); - data.std = static_cast(sportdata->std); - data.cdc = static_cast(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) + 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); + 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); + 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(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(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 -#include -#include -#include - -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( - // std::chrono::duration_cast( - // 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 // 确保包含该头文件以使用布尔类型 - -// 全局变量 -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(&sportdata), size); - fd.close(); - return 1; -} -static std::vector LoadTrainingDataFromTodayFile() { - std::vector 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(&record), sizeof(Trainingdata)); - if (fd.gcount() == sizeof(Trainingdata)) { - trainingRecords.push_back(record); - } - } - - fd.close(); - - return trainingRecords; -} - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include - -// 获得数据个数 -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 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 &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(file)), - std::istreambuf_iterator()); - 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 &points, double epsilon, - std::vector &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 recResults1, recResults2; - - // 递归处理前半部分 - douglasPeucker( - std::vector(points.begin(), points.begin() + index + 1), epsilon, - recResults1); - - // 递归处理后半部分 - douglasPeucker(std::vector(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 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(gpsData.size() - 1) / 99; - for (int i = 0; i < 100; i++) { - int index = static_cast(i * step); - sampledData.push_back(gpsData[index]); - } - } else { - // 转换GPS坐标到屏幕坐标 - std::vector 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 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(gpsData.size() - 1) / 99; - for (int i = 0; i < 100; i++) { - int index = static_cast(i * step); - sampledData.push_back(gpsData[index]); - } - } else if (simplifiedPoints.size() > 100) { - // 简化后仍超过100点,再次均匀采样 - double step = static_cast(simplifiedPoints.size() - 1) / 99; - for (int i = 0; i < 100; i++) { - int index = static_cast(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(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 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(&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(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); - 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; -} - -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_; -} - -#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 diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp index fe7f65606..15446fa1e 100644 --- a/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp +++ b/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp @@ -50,7 +50,10 @@ namespace OHOS //判断是否是需要GPS的运动 if(TraingModel::GetInstance()->IsNeedGPS(TraingModel::GetInstance()->GetSportEvent()) == true) { + #if defined(SUPPORT_GNSS) + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开启gps定时器"); TraingModel::GetInstance()->gnss_start_timer(1000); + #endif } Presenter::OnStart(); } @@ -82,10 +85,12 @@ namespace OHOS NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS); //如果不是进入运动界面 if(enter_flag == false){ - // 关闭gps与心率 + // 关闭心率 TraingModel::GetInstance()->stop_heart_gps(); + #if defined(SUPPORT_GNSS) // 关闭gps定时器 TraingModel::GetInstance()->gnss_stop_timer(); + #endif } } bool TraingNomalPresenter::OnDrag(UIView &view, const DragEvent &event) diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp index b9115cb00..728ca373b 100644 --- a/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp +++ b/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp @@ -142,6 +142,13 @@ void TraingCardPresenter::UpdateTimeOfControl() { TimeStr.c_str()); } #if defined(BOARD_W9) || defined(BOARD_N9) +//需要判断是否需要运动是否需要gps + bool isNeedGPS = TraingModel::GetInstance()->IsNeedGPS(TraingModel::GetInstance()->GetSportEvent()); + if(isNeedGPS == false) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "该运动项不需要GPS"); + return; + } if (TrainingControlView::GetInstance()->GetGpsStatusLabel() != nullptr) { //获取GPS状态 bool gpsStatus = TraingModel::GetInstance()->GetGPSConnectState(); diff --git a/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp b/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp index b6709e41a..3a1692975 100644 --- a/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp +++ b/code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp @@ -84,11 +84,20 @@ void TrainingControlView::InitView() { InitImageView(imagepoint, 193, 421, 80, 20, "image1", CONTROL_POINT_IMAGE); Add(imagepoint); -#if defined(BOARD_W9) || defined(BOARD_N9) +#if defined(SUPPORT_GNSS) +//需要判断是否需要运动是否需要gps + bool isNeedGPS = TraingModel::GetInstance()->IsNeedGPS(TraingModel::GetInstance()->GetSportEvent()); + if(isNeedGPS == false) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "该运动项不需要GPS"); + return; + } + InitLabel(gpsstatus, 83, 365, 300, 35, "label1", "gps:定位中...", DEFAULT_VECTOR_FONT_FILENAME, 30); - gpsstatus->SetTextColor(Color::Red()); - Add(gpsstatus); + gpsstatus->SetTextColor(Color::Red()); + Add(gpsstatus); + #endif } diff --git a/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingEnddataView.cpp b/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingEnddataView.cpp index 968d2f8c8..45874c056 100644 --- a/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingEnddataView.cpp +++ b/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingEnddataView.cpp @@ -30,13 +30,13 @@ namespace OHOS { -static const int initialY = 132 + CANVAS_SIZE; +static int initialY = 132 ; static const int lineHeight = 130; static const int iconHeight = 80; static const int verticalOffset = 2; -static const int baseY = 214 + CANVAS_SIZE; -static const int startY_Unit = 252 + CANVAS_SIZE; -static const int startY_Value = 292 + CANVAS_SIZE; +static int baseY = 214 ; +static int startY_Unit = 252 ; +static int startY_Value = 292 ; static const int labelHeight = 40; static const int labelInterval = 130; @@ -52,12 +52,27 @@ TraingEnddataView::~TraingEnddataView() { } TraingEnddataView *TraingEnddataView::GetInstance() { - return g_pTraingEnddataView; } // 初始化图像 void TraingEnddataView ::InitializeImageViews() { + + std::vector canvasData = TraingModel::GetInstance()->GetCanvasData(); + int height_canvas = 0; + if(canvasData.size() > 0) + { + height_canvas = CANVAS_SIZE; + } + else + { + height_canvas = 0; + } + initialY = 132 + height_canvas; + baseY = 214 + height_canvas; + startY_Unit = 252 + height_canvas; + startY_Value = 292 + height_canvas; + // 初始化主图标 InitImageView(imageIcon, 80, initialY, 54, iconHeight, "image", WALKING_54_IMAGE); @@ -104,9 +119,26 @@ void TraingEnddataView ::InitializeLabels() { /*title and sport*/ InitRollLabel(labeltype, 83, 50, 300, 35, "label1", STR_EXERCISE_LIST, DEFAULT_VECTOR_FONT_FILENAME, 30); - InitRollLabel(labelsport, 147, 140 + CANVAS_SIZE, 240, 35, "label6", + // 如果是有gps轨迹才需要加CANVAS_SIZE + std::vector canvasData = TraingModel::GetInstance()->GetCanvasData(); + int height_canvas = 0; + if(canvasData.size() > 0) + { + height_canvas = CANVAS_SIZE; + } + else + { + height_canvas = 0; + } + + initialY = 132 + height_canvas; + baseY = 214 + height_canvas; + startY_Unit = 252 + height_canvas; + startY_Value = 292 + height_canvas; + + InitRollLabel(labelsport, 147, 140 + height_canvas, 240, 35, "label6", STR_EXERCISE_OUTDOORRUNNING, DEFAULT_VECTOR_FONT_FILENAME, 30); - InitLabel(labelduration, 146, 179 + CANVAS_SIZE, 240, 40, "label7", + InitLabel(labelduration, 146, 179 + height_canvas, 240, 40, "label7", "13:00:22-15:00:12", DEFAULT_VECTOR_FONT_FILENAME, 28); // 初始化Label //时长 @@ -173,18 +205,27 @@ void TraingEnddataView ::InitializeLabels() { scrollView_->Add(labelstrong); scrollView_->Add(labelcalories); - // 初始化GPS轨迹失败提示标签(默认隐藏) - InitLabel(gpsNoTrackLabel, 33, 100 + CANVAS_SIZE/2 - 20, CANVAS_SIZE, 40, - "gpsNoTrackLabel", "未定位成功,没有GPS轨迹", DEFAULT_VECTOR_FONT_FILENAME, 24); - gpsNoTrackLabel->SetTextColor(Color::Red()); - gpsNoTrackLabel->SetVisible(false); // 默认隐藏 - scrollView_->Add(gpsNoTrackLabel); + } // 增加额外配置 void TraingEnddataView::BonusdataView() { if (!TraingModel::GetInstance()->GetIsSpecial()) return; + std::vector canvasData = TraingModel::GetInstance()->GetCanvasData(); + int height_canvas = 0; + if(canvasData.size() > 0) + { + height_canvas = 400; + } + else + { + height_canvas = 0; + } + baseY = baseY + height_canvas; + startY_Unit = startY_Unit + height_canvas; + startY_Value = startY_Value + height_canvas; + bool isincludeTag = TraingModel::GetInstance()->GetSportTag(); int tagflge = isincludeTag ? 1 : 0; @@ -267,6 +308,18 @@ void TraingEnddataView::BonusdataView() { // 配置退出相关位置 void TraingEnddataView::ExitView() { + std::vector canvasData = TraingModel::GetInstance()->GetCanvasData(); + int height_canvas = 0; + if(canvasData.size() > 0) + { + height_canvas = CANVAS_SIZE; + } + else + { + height_canvas = 0; + } + startY_Value = startY_Value + height_canvas; + bool isincludeTag = TraingModel::GetInstance()->GetSportTag(); int tagflge = isincludeTag ? 1 : 0; @@ -427,10 +480,6 @@ void TraingEnddataView::DeInit() { delete labelcalories; labelcalories = nullptr; } - if (gpsNoTrackLabel) { - delete gpsNoTrackLabel; - gpsNoTrackLabel = nullptr; - } /* 删除图片 label */ if (imageIcon) { @@ -715,22 +764,21 @@ void TraingEnddataView::OnStart() { } #if defined(BOARD_W9) || defined(BOARD_N9) ///////////////////////////gps轨迹测试////////////////////// - canvas = new UICanvas(); - canvas->SetHeight(CANVAS_SIZE); - canvas->SetWidth(CANVAS_SIZE); - canvas->SetPosition(33, 100); - scrollView_->Add(canvas); - GenerateTrackPoints(); - // 检查是否有足够的GPS数据来绘制轨迹 std::vector canvasData = TraingModel::GetInstance()->GetCanvasData(); if (canvasData.size() >= 3) { + canvas = new UICanvas(); + canvas->SetHeight(CANVAS_SIZE); + canvas->SetWidth(CANVAS_SIZE); + canvas->SetPosition(33, 100); + scrollView_->Add(canvas); + GenerateTrackPoints(); // 有足够数据,绘制轨迹 DrawTrackSegment(canvas); DrawMarkers(canvas); } else { - // 数据不足,只显示提示标签(已在GenerateTrackPoints中处理) - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS轨迹数据不足,不绘制轨迹"); + // 数据不足,只显示提示标签 + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS轨迹数据不足,不绘制轨迹"); } #endif } @@ -748,20 +796,6 @@ void TraingEnddataView::GenerateTrackPoints() { seg1.color = Color::Blue(); std::vector canvasData = TraingModel::GetInstance()->GetCanvasData(); - //最小轨迹点数限制,防止绘制失败 - if (canvasData.size() < 3) { - // GPS数据不足,显示提示标签 - if (gpsNoTrackLabel) { - gpsNoTrackLabel->SetVisible(true); - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS轨迹数据不足,显示提示标签"); - } - return; - } - - // GPS数据充足,隐藏提示标签 - if (gpsNoTrackLabel) { - gpsNoTrackLabel->SetVisible(false); - } //将点位赋值 for (size_t i = 0; i < canvasData.size(); ++i) { diff --git a/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingModel.cpp b/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingModel.cpp index aad12df03..4e8f79cd6 100644 --- a/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingModel.cpp +++ b/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingModel.cpp @@ -5,3634 +5,3701 @@ * @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 -#include -#include -#include -#include -#include -#include -#include - -#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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "chip_system_time.h" -#include "cmath" -#include "system/include/SystemLogModel.h" -#include - -#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, " 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(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 + #include + #include + #include + #include + #include + #include + #include + + #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 + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include "chip_system_time.h" + #include "cmath" + #include "system/include/SystemLogModel.h" + #include + + #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, " 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(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(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 &heartRates) { + if (heartRates.size() <= 2) { + // 如果心率数据点少于等于2,无法去掉最高和最低值 + return 0.0; // 或者返回心率的平均值 + } + + // 创建一个副本以避免修改原始数据 + std::vector 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, "开始运动"); + + 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(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(sportdata->duration) / current_goal) * 100; + break; + } + case ExerciseTag::Calories: { + // 计算目标完成度 + goaldegree = (static_cast(sportdata->calories) / current_goal) * 100; + break; + } + case ExerciseTag::Distance: { + // 计算目标完成度 + goaldegree = (static_cast(sportdata->distance) / current_goal) * 100; + break; + } + case ExerciseTag::Steps: { + // 计算目标完成度 + goaldegree = (static_cast(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(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(sportdata->tag * 60); + break; + } + case ExerciseTag::Calories: { + // 计算目标完成度 + return sportdata->tag; + break; + } + case ExerciseTag::Distance: { + return static_cast(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(sportdata->sportType); + data.ty = static_cast( + GetBleSportId(sportdata->sportType, sportdata->sportId)); + data.st = static_cast(sportdata->starttime); + data.sp = static_cast(sportdata->endtime); + data.tgt = static_cast(sportdata->sportTagType); + if (sportdata->sportTagType == ExerciseTag::Distance) { + data.tg = static_cast(sportdata->tag * 1000); + } else { + data.tg = static_cast(sportdata->tag); + } + data.pg = static_cast(sportdata->goaldegree); + data.du = static_cast(sportdata->duration); + data.ca = static_cast(sportdata->calories * 10); + data.di = static_cast(sportdata->distance); + data.hr = static_cast(sportdata->avgheart); + data.stp = static_cast(sportdata->steps); + data.ti = static_cast(sportdata->intensity); + /* 230 目前不支持【圈数】【最高海拔高度】 */ + data.cir = 0; + data.alt = 0; + /* 配速相关 */ + data.apc = static_cast(sportdata->apc); + data.fpc = static_cast(sportdata->fpc); + data.spc = static_cast(sportdata->spc); + data.std = static_cast(sportdata->std); + data.cdc = static_cast(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(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(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 + #include + #include + #include + + 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( + // std::chrono::duration_cast( + // 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 // 确保包含该头文件以使用布尔类型 + + // 全局变量 + 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(&sportdata), size); + fd.close(); + return 1; + } + static std::vector LoadTrainingDataFromTodayFile() { + std::vector 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(&record), sizeof(Trainingdata)); + if (fd.gcount() == sizeof(Trainingdata)) { + trainingRecords.push_back(record); + } + } + + fd.close(); + + return trainingRecords; + } + + #ifdef __cplusplus + extern "C" { + #endif + + #include + #include + #include + #include + #include + + // 获得数据个数 + 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 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 &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(file)), + std::istreambuf_iterator()); + 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 &points, double epsilon, + std::vector &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 recResults1, recResults2; + + // 递归处理前半部分 + douglasPeucker( + std::vector(points.begin(), points.begin() + index + 1), epsilon, + recResults1); + + // 递归处理后半部分 + douglasPeucker(std::vector(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 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(gpsData.size() - 1) / 99; + for (int i = 0; i < 100; i++) { + int index = static_cast(i * step); + sampledData.push_back(gpsData[index]); + } + } else { + // 转换GPS坐标到屏幕坐标 + std::vector 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 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(gpsData.size() - 1) / 99; + for (int i = 0; i < 100; i++) { + int index = static_cast(i * step); + sampledData.push_back(gpsData[index]); + } + } else if (simplifiedPoints.size() > 100) { + // 简化后仍超过100点,再次均匀采样 + double step = static_cast(simplifiedPoints.size() - 1) / 99; + for (int i = 0; i < 100; i++) { + int index = static_cast(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(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 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(&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(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更新 */ - - 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(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 &heartRates) { - if (heartRates.size() <= 2) { - // 如果心率数据点少于等于2,无法去掉最高和最低值 - return 0.0; // 或者返回心率的平均值 - } - - // 创建一个副本以避免修改原始数据 - std::vector 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, "开始运动"); - - 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 SPORT_TIMER_DATA + // 声明运动数据采集定时器变量 + static osTimerId_t g_sport_start_data_timer = nullptr; + static int sport_start_data_timer_interval = 300; // 运动数据获取间隔,单位毫秒 + + /* + * @brief 获取运动基础数据 + * @return 是否成功获取数据 */ - // 确保最大心率为正值 - if (user_Intensity <= 0) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "maxHeartRate %d is error", - user_Intensity); - return; - } - - // 将 heart 转换为 double 以避免整数除法 - double heartRateAsDouble = static_cast(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(sportdata->duration) / current_goal) * 100; - break; - } - case ExerciseTag::Calories: { - // 计算目标完成度 - goaldegree = (static_cast(sportdata->calories) / current_goal) * 100; - break; - } - case ExerciseTag::Distance: { - // 计算目标完成度 - goaldegree = (static_cast(sportdata->distance) / current_goal) * 100; - break; - } - case ExerciseTag::Steps: { - // 计算目标完成度 - goaldegree = (static_cast(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(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(sportdata->tag * 60); - break; - } - case ExerciseTag::Calories: { - // 计算目标完成度 - return sportdata->tag; - break; - } - case ExerciseTag::Distance: { - return static_cast(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(sportdata->sportType); - data.ty = static_cast( - GetBleSportId(sportdata->sportType, sportdata->sportId)); - data.st = static_cast(sportdata->starttime); - data.sp = static_cast(sportdata->endtime); - data.tgt = static_cast(sportdata->sportTagType); - if (sportdata->sportTagType == ExerciseTag::Distance) { - data.tg = static_cast(sportdata->tag * 1000); - } else { - data.tg = static_cast(sportdata->tag); - } - data.pg = static_cast(sportdata->goaldegree); - data.du = static_cast(sportdata->duration); - data.ca = static_cast(sportdata->calories * 10); - data.di = static_cast(sportdata->distance); - data.hr = static_cast(sportdata->avgheart); - data.stp = static_cast(sportdata->steps); - data.ti = static_cast(sportdata->intensity); - /* 230 目前不支持【圈数】【最高海拔高度】 */ - data.cir = 0; - data.alt = 0; - /* 配速相关 */ - data.apc = static_cast(sportdata->apc); - data.fpc = static_cast(sportdata->fpc); - data.spc = static_cast(sportdata->spc); - data.std = static_cast(sportdata->std); - data.cdc = static_cast(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) + 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); + 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); + 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(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(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 -#include -#include -#include - -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( - // std::chrono::duration_cast( - // 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 // 确保包含该头文件以使用布尔类型 - -// 全局变量 -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(&sportdata), size); - fd.close(); - return 1; -} -static std::vector LoadTrainingDataFromTodayFile() { - std::vector 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(&record), sizeof(Trainingdata)); - if (fd.gcount() == sizeof(Trainingdata)) { - trainingRecords.push_back(record); - } - } - - fd.close(); - - return trainingRecords; -} - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include -#include - -// 获得数据个数 -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 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 &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(file)), - std::istreambuf_iterator()); - 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 &points, double epsilon, - std::vector &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 recResults1, recResults2; - - // 递归处理前半部分 - douglasPeucker( - std::vector(points.begin(), points.begin() + index + 1), epsilon, - recResults1); - - // 递归处理后半部分 - douglasPeucker(std::vector(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 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(gpsData.size() - 1) / 99; - for (int i = 0; i < 100; i++) { - int index = static_cast(i * step); - sampledData.push_back(gpsData[index]); - } - } else { - // 转换GPS坐标到屏幕坐标 - std::vector 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 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(gpsData.size() - 1) / 99; - for (int i = 0; i < 100; i++) { - int index = static_cast(i * step); - sampledData.push_back(gpsData[index]); - } - } else if (simplifiedPoints.size() > 100) { - // 简化后仍超过100点,再次均匀采样 - double step = static_cast(simplifiedPoints.size() - 1) / 99; - for (int i = 0; i < 100; i++) { - int index = static_cast(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(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 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(&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(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); - 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; -} - -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_; -} - -#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 diff --git a/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp b/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp index d435d1d70..15446fa1e 100644 --- a/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp +++ b/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp @@ -50,8 +50,11 @@ namespace OHOS //判断是否是需要GPS的运动 if(TraingModel::GetInstance()->IsNeedGPS(TraingModel::GetInstance()->GetSportEvent()) == true) { + #if defined(SUPPORT_GNSS) + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开启gps定时器"); TraingModel::GetInstance()->gnss_start_timer(1000); - } + #endif + } Presenter::OnStart(); } @@ -82,10 +85,12 @@ namespace OHOS NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS); //如果不是进入运动界面 if(enter_flag == false){ - // 关闭gps与心率 + // 关闭心率 TraingModel::GetInstance()->stop_heart_gps(); + #if defined(SUPPORT_GNSS) // 关闭gps定时器 TraingModel::GetInstance()->gnss_stop_timer(); + #endif } } bool TraingNomalPresenter::OnDrag(UIView &view, const DragEvent &event) diff --git a/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp b/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp index b9115cb00..97d25a051 100644 --- a/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp +++ b/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp @@ -13,598 +13,606 @@ * /XTS_FD230/FD230/code/sdk/application/wearable/fd230app/nativeui/traing/training_card/src/TraingCardPresenter.cpp */ -#include "AppViewIDs.h" -#include "NativeAbility.h" -#include "components/ui_label_ext.h" -#include "ui_resource_string.h" - -#include "ConfigModel.h" -#include "custom_utils.h" -#include "graphic_service.h" -#include "traing/include/TraingModel.h" -#include "traing/training_card/include/TraingCardPresenter.h" -#include "traing/training_card/include/TrainingControlView.h" -#include "uicomponents/UIComponents.h" -#include "vibrator_service.h" - -#include -#include -#include -#include -#include -#include - -extern "C" void do_cywee_service_check_msg(int cmd, int data[]); - -#define MAXSPORTID 176 - -#define DEFAULT_DASH "--" -#define DATATEST 0 - -extern "C" { -void StartTimer(); -void StopAndResetTimer(); -void PauseTimer(); -void ResumeTimer(); -} -#include "NativeRegisterManager.h" - -#ifdef SUPPORT_CYWEE_SERVICE -#include "cywee_service.h" -#endif -namespace OHOS { - -REGIST_SLICE(VIEW_TRAININGCRAD, TraingCardView, TraingCardPresenter); - -// 当前Presenter属性实例化的内存指针 -static TraingCardPresenter *g_pTraingCardPresenter = nullptr; - -TraingCardPresenter::TraingCardPresenter() { g_pTraingCardPresenter = this; } - -TraingCardPresenter::~TraingCardPresenter() { - // tag_ = 0; - g_pTraingCardPresenter = nullptr; -} - -TraingCardPresenter *TraingCardPresenter::GetInstance() { - static TraingCardPresenter instance; - return &instance; -} - -void TraingCardPresenter::OnStart() { - Presenter::OnStart(); - // ScreenAlwaysOnCtr(OPEN_SCREEN_ALWAYS_ON); // 长亮屏 -} - -void TraingCardPresenter::OnPause(void) { - - Presenter::OnPause(); -} - -void TraingCardPresenter::OnResume(void) { - - Presenter::OnResume(); - RootView::GetInstance()->SetOnKeyActListener(this); -} - -void TraingCardPresenter::OnStop(void) { - - Presenter::OnStop(); - // ScreenAlwaysOnCtr(CANCEL_SCREEN_ALWAYS_ON); // 长亮屏 -} - -bool TraingCardPresenter::OnDrag(UIView &view, const DragEvent &event) { - UNUSED(view); - return true; -} - -// 计时器更新timer -void TraingCardPresenter::ModifiesTime() { - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ModifiesTime begin"); - std ::string TimeStr = TraingModel::GetInstance()->DisplayMotionDuration(); - - /*灭屏状态不刷新UI*/ - if (uapi_get_screen_state() == 1) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "uapi_get_screen_state is off"); - return; - } - - if (TimeStr.empty()) { - WEARABLE_LOGE( - WEARABLE_LOG_MODULE_APP, - "TraingModel::GetInstance()->DisplayMotionDuration() nullptr"); - return; - } - if (TrainingControlView::GetInstance() != nullptr && - TrainingDataView::GetInstance() != nullptr) { - if (TrainingControlView::GetInstance()->GetTimeLabel() != nullptr) { - TrainingControlView::GetInstance()->GetTimeLabel()->SetText( - TimeStr.c_str()); - } - if (TrainingDataView::GetInstance()->GetTimeLabel() != nullptr) { - TrainingDataView::GetInstance()->GetTimeLabel()->SetText(TimeStr.c_str()); - } - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ModifiesTime end"); - } -} - -//计时器 -void TraingCardPresenter::UpdateTimeOfControl() { - std ::string TimeStr = TraingModel::GetInstance()->DisplayMotionDuration(); - if (TimeStr.empty()) { - WEARABLE_LOGE( - WEARABLE_LOG_MODULE_APP, - "TraingModel::GetInstance()->DisplayMotionDuration() nullptr"); - return; - } - if (TrainingControlView::GetInstance()->GetTimeLabel() != nullptr) { - TrainingControlView::GetInstance()->GetTimeLabel()->SetText( - TimeStr.c_str()); - } -#if defined(BOARD_W9) || defined(BOARD_N9) - if (TrainingControlView::GetInstance()->GetGpsStatusLabel() != nullptr) { - //获取GPS状态 - bool gpsStatus = TraingModel::GetInstance()->GetGPSConnectState(); - std::string gpsStatusStr; - if (gpsStatus) { - gpsStatusStr = "GPS定位成功"; - TrainingControlView::GetInstance()->GetGpsStatusLabel()->SetTextColor( - Color::Green()); - } else { - gpsStatusStr = "GPS未定位成功"; - TrainingControlView::GetInstance()->GetGpsStatusLabel()->SetTextColor( - Color::Red()); - } - TrainingControlView::GetInstance()->GetGpsStatusLabel()->SetText( - gpsStatusStr.c_str()); - } -#endif -} -void TraingCardPresenter::UpdateTimeOfData() { - std ::string TimeStr = TraingModel::GetInstance()->DisplayMotionDuration(); - if (TimeStr.empty()) { - WEARABLE_LOGE( - WEARABLE_LOG_MODULE_APP, - "TraingModel::GetInstance()->DisplayMotionDuration() nullptr"); - return; - } - if (TrainingDataView::GetInstance()->GetTimeLabel() != nullptr) { - TrainingDataView::GetInstance()->GetTimeLabel()->SetText(TimeStr.c_str()); - } -} - -/* --------------------------------- cywee库操作 - * -------------------------------- */ -/*开启cywee*/ -bool TraingCardPresenter::OpenSensor(ExerciseClass type) { - // 防止重复开启 - if (opensensorstuats) { - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "OpenSensor is opening!!"); - return false; - } - // 根据不同的运动项目进行不同的运动 - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TraingCardPresenter::OpenSensor!!"); -#ifdef SUPPORT_CYWEE_SERVICE - switch (type) { - case ExerciseClass::NORMAL: - cywee_sport_mode_change(CYWEE_SERVICE_CMD_NORMAL_MODE); - break; - case ExerciseClass::TREADMILL: - cywee_sport_mode_change(CYWEE_SERVICE_CMD_TREADMILL_MODE); - break; - case ExerciseClass::OUTDOOR: - cywee_sport_mode_change(CYWEE_SERVICE_CMD_OUTDOOR_RUNNING_MODE); - break; - case ExerciseClass::INDOOR: - cywee_sport_mode_change(CYWEE_SERVICE_CMD_INDOOR_RUNNING_MODE); - break; - case ExerciseClass::FITNESS_PEDOMETER: - cywee_sport_mode_change(CYWEE_SERVICE_CMD_FITNESS_PEDOMETER_MODE); - break; - case ExerciseClass::FREE_TRAINING: - cywee_sport_mode_change(CYWEE_SERVICE_CMD_FREE_TRAINING_MODE); - break; - default: - break; - } -#endif - opensensorstuats = true; - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "OpenSensor--%d!!", type); - return true; -} - -/*控制cywee*/ -bool TraingCardPresenter::ControlSensor(ExerciseState state) { -#ifdef SUPPORT_PPG_SERVICE - switch (state) { - case ExerciseState::End: - // 退出进入普通模式 - do_cywee_service_check_msg(CYWEE_SERVICE_CMD_NORMAL_MODE, NULL); - opensensorstuats = false; // 恢复标志位 - StopAndResetTimer(); // 关闭计时器 - break; - case ExerciseState::Pause: - do_cywee_service_check_msg(CYWEE_SERVICE_CMD_PAUSE, 0); - break; - case ExerciseState::Continue: - do_cywee_service_check_msg(CYWEE_SERVICE_CMD_RESUME, 0); - break; - default: - break; - } -#endif - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ControlSensor[%d]!!", state); - return true; -} - -void TraingCardPresenter::RefreshData(SportData *sportdata) { - if (sportdata == nullptr) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata is null"); - return; - } - - if (TrainingDetailView::GetInstance() == nullptr || - TrainingDataView::GetInstance() == nullptr) - return; - - std::string distanceStr; - if (sportdata->distance != 0) { - double distance_km = - sportdata->distance / 1000.0; // 转换为公里并保留两位小数 - std::stringstream ss; - ss << std::fixed << std::setprecision(2) << distance_km; - distanceStr = ss.str(); - } else { - distanceStr = DEFAULT_DASH; - } - - std::string heartStr = - (sportdata->heart != 0) ? std::to_string(sportdata->heart) : DEFAULT_DASH; - std::string stepsStr = - (sportdata->steps != 0) ? std::to_string(sportdata->steps) : DEFAULT_DASH; - std::string calorieStr = (sportdata->calories != 0) - ? std::to_string(sportdata->calories) - : DEFAULT_DASH; - // 训练强度刷新 - UIResourceTextId intensityid = TraingModel::GetInstance()->GetIntensity(); - // printf("intensityid %d \r\n", intensityid); - TrainingDetailView::GetInstance()->Get_Intensity_Button()->SetTextId( - intensityid); - - TrainingDataView::GetInstance()->GetProgress()->SetValue( - sportdata->goaldegree); // 目标完成度 - TrainingDataView::GetInstance()->GetHeartLabel()->SetText( - heartStr.c_str()); // 心率 - TrainingDetailView::GetInstance()->GetCalorieLabel()->SetText( - calorieStr.c_str()); // 卡路里 - TrainingDetailView::GetInstance()->GetMileLabel()->SetText( - distanceStr.c_str()); // 距离 - TrainingDetailView::GetInstance()->GetStepLabel()->SetText( - stepsStr.c_str()); // 步数 - - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, - "[Calories: %s, Distance: %s, Steps: %s Intensityid: %d]", - calorieStr.c_str(), distanceStr.c_str(), stepsStr.c_str(), - intensityid); - // 释放sportdata内存 -} -#if 1 -/*刷新 time 和 data */ -void TraingCardPresenter::updataview(void) { - // 如果未开启训练,直接退出 - if (TraingModel::GetInstance()->GetSportState() == ExerciseState::General) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, - "TraingModel::GetInstance()->GetSportState() == " - "ExerciseState::General "); - return; - } - - if (TraingCardView::GetInstance() == nullptr) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "view_ == nullptr"); - return; - } - - Control_Card cardId = static_cast( - TraingCardView::GetInstance()->GetCurrentPage()); - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "cardId %d", (int)cardId); - - switch (cardId) { - case Control_Card::CONTROL: { - UpdateTimeOfControl(); // 更新控制界面的时间 - break; - } - case Control_Card::DATA: { - UpdateDataView(); // 更新训练数据视图 - break; - } - default: - break; // 其他情况不处理 - } -} -#else -void TraingCardPresenter::Notify(void) { - // 如果未开启训练,直接退出 - if (TraingModel::GetInstance()->GetSportState() == ExerciseState::General || - view_ == nullptr) { - return; - } - - Control_Card cardId = static_cast(view_->GetCurrentPage()); - - switch (cardId) { - case Control_Card::CONTROL: { - UpdateTimeOfControl(); // 更新控制界面的时间 - break; - } - case Control_Card::DATA: { - UpdateDataView(); // 更新训练数据视图 - break; - } - default: - break; // 其他情况不处理 - } -} -#endif -void TraingCardPresenter::UpdateDataView() { - // 刷新计时 - UpdateTimeOfData(); - - // 刷新训练数据视图,显示当前时间 - GraphicService::GetInstance()->PostGraphicEvent(std::bind( - &TrainingDataView::UpdataTime, TrainingDataView::GetInstance())); - - TraingModel::GetInstance()->updata_goal(); - - // 刷新训练数据:心率、卡路里、距离、步数、训练强度、目标完成度 - SportData *sportdata = TraingModel::GetInstance()->GetSportdata(); - RefreshData(sportdata); -} -/* --------------------------------- 点击事件处理 - * --------------------------------- */ -bool TraingCardPresenter::OnClick(UIView &view, const ClickEvent &event) { - const std::string viewId = view.GetViewId(); - - // 定义点击事件映射 - static const std::unordered_map> - actionMap = { - // 暂停按键 - {BUTTONSTOP, - [&]() { - auto currentState = TraingModel::GetInstance()->GetSportState(); - if (currentState == ExerciseState::Continue || - currentState == ExerciseState::Start) { - handlePauseState(); - } else if (currentState == ExerciseState::Pause) { - handleContinueState(); - } - }}, - // 结束按键 - {BUTTONEND, - [&]() { - // ControlSensor(ExerciseState::Pause); - TraingCardView::GetInstance()->SetPopupViewStatus(EndPopup, true); - TraingCardView::GetInstance()->RefreshEndView(); - }}, - // 确定结束 - {BUTTONEXIT, [&]() { handleEndState(); }}, - // 返回 - {BUTTONBACK, - [&]() { - // ControlSensor(ExerciseState::Continue); - TraingCardView::GetInstance()->SetPopupViewStatus(EndPopup, false); - }}, - // 确定达到目标 - {BUTTONWINOK, - [&]() { - TraingCardView::GetInstance()->GetAnimator()->Stop(); - TraingCardView::GetInstance()->SetPopupViewStatus(GetTagPopup, - false); - }}, - // 确定心率提醒 - {BUTTONNOTE, - [&]() { - TraingCardView::GetInstance()->SetPopupViewStatus(HeartPopup, - false); - }}, - }; - - // 查找并执行对应的操作 - auto it = actionMap.find(viewId); - if (it != actionMap.end()) { - it->second(); // 执行操作 - return true; - } - - return false; // 没有匹配的操作 -} -void TraingCardPresenter::OnSwipe(UISwipeView &view) { - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "TempPresenter::OnSwipe "); - UNUSED(view); - return; -} -bool TraingCardPresenter::OnKeyAct(UIView &view, const KeyEvent &event) { - /*消耗按键事件*/ - return true; -} -/* -------------------------------- 协议发起运动处理 - * -------------------------------- */ -#ifdef SUPPORT_BLE_SERVICE -bool TraingCardPresenter::send_sport_cmd(training_ble *cmd) { - if (cmd == nullptr) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无效的运动命令 cmd 为空!"); - return false; - } - - // 获取当前的运动状态 - ExerciseState currentState = TraingModel::GetInstance()->GetSportState(); - ExerciseState cmdstate = static_cast(cmd->controlmd); - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "cmdstate:%d currentState:%d", - cmdstate, currentState); - int m_sportTag = 0; - switch (cmd->controlmd) { - case SETGOAL: - // 仅在一般状态下允许设置目标 - if (currentState == ExerciseState::General) { - if (cmd->sportTagType == 0) { - //训练目标->m 需要转换 km - m_sportTag = cmd->sportTag / 1000; - } else { - m_sportTag = cmd->sportTag; - } - TraingModel::GetInstance()->SetSportTag( - static_cast(cmd->sportTagType), m_sportTag); - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "目标设置成功!\r\n"); - return true; - } - break; - - case SENDDATA: - // 随时允许发送实时数据 - TraingModel::GetInstance()->SendLiveData(); - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "实时数据发送成功!\r\n"); - return true; - - default: - break; - } - - // 仅在一般状态下允许开始运动 - if (cmdstate == ExerciseState::Start) { - if ((currentState == ExerciseState::General)) { - if (TraingCardView::GetInstance() == nullptr) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 开启运动"); - uapi_set_screen_on_api(); // 亮屏 - NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGCRAD); - // 设置目标类型和项目有且只有一次 - switch (cmd->sportType) { - case 0: - TraingModel::GetInstance()->SetSportEvent( - STR_EXERCISE_OUTDOORWALKING); - OpenSensor(ExerciseClass::OUTDOOR); - break; - case 1: - TraingModel::GetInstance()->SetSportEvent( - STR_EXERCISE_OUTDOORRUNNING); - OpenSensor(ExerciseClass::OUTDOOR); - break; - case 2: - TraingModel::GetInstance()->SetSportEvent(STR_EXERCISE_OUTDOORBIKING); - OpenSensor(ExerciseClass::FREE_TRAINING); - break; - default: - break; - } - } - reply_APP_OpenSportModeCmd(true); - handleStartState(); - return true; - } else { - - reply_APP_OpenSportModeCmd(false); - // return false; - } - } - // 仅在运动状态为开始或继续时允许暂停 - if (cmdstate == ExerciseState::Pause) { - if (currentState == ExerciseState::Start || - currentState == ExerciseState::Continue) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 暂停运动"); - reply_APP_SportPasueResetCmd(TraingModel::GetInstance()->GetSportTime()); - handlePauseState(); - return true; - } - } - - // 仅在暂停状态下允许恢复运动 - if (cmdstate == ExerciseState::Continue && - currentState == ExerciseState::Pause) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 恢复运动"); - reply_APP_SportPasueResetCmd(TraingModel::GetInstance()->GetSportTime()); - handleContinueState(); - return true; - } - - // 仅在运动状态为开始、继续或暂停时允许结束运动 - if (cmdstate == ExerciseState::End && - (currentState == ExerciseState::Start || - currentState == ExerciseState::Continue || - currentState == ExerciseState::Pause)) { - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 结束运动"); - uint32_t sport_time = TraingModel::GetInstance()->GetSportTime(); - reply_APP_SportStopModeCmd(sport_time); - handleEndState(); - return true; - } - - // 如果到达这里,命令未执行 - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, - "cmd->sportType[%d],cmd->controlmd[%d],cmd->sportTagType[%d]," - "cmd->sportTag[%d]\r\n", - cmd->sportType, cmd->controlmd, cmd->sportTagType, - cmd->sportTag); - - WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, - "由于状态限制,命令未执行 当前状态 %d。\r\n", currentState); - return false; // 没有执行任何命令 -} -#endif -/*训练结束*/ -void TraingCardPresenter::handleEndState() { - TraingModel::GetInstance()->SetSportState(ExerciseState::End); - ControlSensor(ExerciseState::End); - uapi_set_screen_on_api(); // 亮屏 -#if DATATEST - // NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGENDDATA); - // 回退 - NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGNOMAL); - return; -#endif - // 是否达到训练目标 - if (TraingModel::GetInstance()->GetMomentum()) { - // 进入训练详情 - NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGENDDATA); - NativeAbility::GetInstance().ClearSliceIdStack(); // 清空所有SLiceId栈元素 - } else { - // 进入训练记录 - // NativeAbility::GetInstance().ChangeSlice(VIEW_TRAINING_RECORD); - if (TraingModel::GetInstance()->GetEntryType() == APPLIST_ENTER) - NativeAbility::GetInstance().ChangeSlice( - VIEW_TRAINING, TransitionType::TRANSITION_BACK_TO_HEXAGONS); - else { - NativeAbility::GetInstance().ChangeSlice( - VIEW_MAIN, TransitionType::TRANSITION_BACK_TO_HEXAGONS); - NativeAbility::GetInstance().ClearSliceIdStack(); // 清空所有SLiceId栈元素 - } - NativeAbility::GetInstance().ClearSliceIdStack(); // 清空所有SLiceId栈元素 - } -} - -/*训练暂停*/ -void TraingCardPresenter::handlePauseState() { - uapi_set_screen_on_api(); // 亮屏 - TrainingControlView::GetInstance()->GetLabel()->SetTextId(STR_RESUME); - TrainingControlView::GetInstance()->GetButton()->SetImageSrc( - SPORT_CONTINUE_IMAGE, SPORT_CONTINUE_IMAGE); - TraingModel::GetInstance()->SetSportState(ExerciseState::Pause); - PauseTimer(); - ControlSensor(ExerciseState::Pause); -} - -/*训练继续*/ -void TraingCardPresenter::handleContinueState() { - uapi_set_screen_on_api(); // 亮屏 - TrainingControlView::GetInstance()->GetLabel()->SetTextId(STR_PAUSE); - TrainingControlView::GetInstance()->GetButton()->SetImageSrc( - SPORT_PAUSE_IMAGE, SPORT_PAUSE_IMAGE); - TraingModel::GetInstance()->SetSportState(ExerciseState::Continue); - ResumeTimer(); - ControlSensor(ExerciseState::Continue); -} - -/*训练开始*/ -void TraingCardPresenter::handleStartState() { - ControlSensor(ExerciseState::Start); - TraingModel::GetInstance()->SetSportOpenWay(true); -} - -} // namespace OHOS + #include "AppViewIDs.h" + #include "NativeAbility.h" + #include "components/ui_label_ext.h" + #include "ui_resource_string.h" + + #include "ConfigModel.h" + #include "custom_utils.h" + #include "graphic_service.h" + #include "traing/include/TraingModel.h" + #include "traing/training_card/include/TraingCardPresenter.h" + #include "traing/training_card/include/TrainingControlView.h" + #include "uicomponents/UIComponents.h" + #include "vibrator_service.h" + + #include + #include + #include + #include + #include + #include + + extern "C" void do_cywee_service_check_msg(int cmd, int data[]); + + #define MAXSPORTID 176 + + #define DEFAULT_DASH "--" + #define DATATEST 0 + + extern "C" { + void StartTimer(); + void StopAndResetTimer(); + void PauseTimer(); + void ResumeTimer(); + } + #include "NativeRegisterManager.h" + + #ifdef SUPPORT_CYWEE_SERVICE + #include "cywee_service.h" + #endif + namespace OHOS { + + REGIST_SLICE(VIEW_TRAININGCRAD, TraingCardView, TraingCardPresenter); + + // 当前Presenter属性实例化的内存指针 + static TraingCardPresenter *g_pTraingCardPresenter = nullptr; + + TraingCardPresenter::TraingCardPresenter() { g_pTraingCardPresenter = this; } + + TraingCardPresenter::~TraingCardPresenter() { + // tag_ = 0; + g_pTraingCardPresenter = nullptr; + } + + TraingCardPresenter *TraingCardPresenter::GetInstance() { + static TraingCardPresenter instance; + return &instance; + } + + void TraingCardPresenter::OnStart() { + Presenter::OnStart(); + // ScreenAlwaysOnCtr(OPEN_SCREEN_ALWAYS_ON); // 长亮屏 + } + + void TraingCardPresenter::OnPause(void) { + + Presenter::OnPause(); + } + + void TraingCardPresenter::OnResume(void) { + + Presenter::OnResume(); + RootView::GetInstance()->SetOnKeyActListener(this); + } + + void TraingCardPresenter::OnStop(void) { + + Presenter::OnStop(); + // ScreenAlwaysOnCtr(CANCEL_SCREEN_ALWAYS_ON); // 长亮屏 + } + + bool TraingCardPresenter::OnDrag(UIView &view, const DragEvent &event) { + UNUSED(view); + return true; + } + + // 计时器更新timer + void TraingCardPresenter::ModifiesTime() { + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ModifiesTime begin"); + std ::string TimeStr = TraingModel::GetInstance()->DisplayMotionDuration(); + + /*灭屏状态不刷新UI*/ + if (uapi_get_screen_state() == 1) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "uapi_get_screen_state is off"); + return; + } + + if (TimeStr.empty()) { + WEARABLE_LOGE( + WEARABLE_LOG_MODULE_APP, + "TraingModel::GetInstance()->DisplayMotionDuration() nullptr"); + return; + } + if (TrainingControlView::GetInstance() != nullptr && + TrainingDataView::GetInstance() != nullptr) { + if (TrainingControlView::GetInstance()->GetTimeLabel() != nullptr) { + TrainingControlView::GetInstance()->GetTimeLabel()->SetText( + TimeStr.c_str()); + } + if (TrainingDataView::GetInstance()->GetTimeLabel() != nullptr) { + TrainingDataView::GetInstance()->GetTimeLabel()->SetText(TimeStr.c_str()); + } + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ModifiesTime end"); + } + } + + //计时器 + void TraingCardPresenter::UpdateTimeOfControl() { + std ::string TimeStr = TraingModel::GetInstance()->DisplayMotionDuration(); + if (TimeStr.empty()) { + WEARABLE_LOGE( + WEARABLE_LOG_MODULE_APP, + "TraingModel::GetInstance()->DisplayMotionDuration() nullptr"); + return; + } + if (TrainingControlView::GetInstance()->GetTimeLabel() != nullptr) { + TrainingControlView::GetInstance()->GetTimeLabel()->SetText( + TimeStr.c_str()); + } + #if defined(BOARD_W9) || defined(BOARD_N9) + //需要判断是否需要运动是否需要gps + bool isNeedGPS = TraingModel::GetInstance()->IsNeedGPS(TraingModel::GetInstance()->GetSportEvent()); + if(isNeedGPS == false) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "该运动项不需要GPS"); + return; + } + if (TrainingControlView::GetInstance()->GetGpsStatusLabel() != nullptr) { + //获取GPS状态 + bool gpsStatus = TraingModel::GetInstance()->GetGPSConnectState(); + std::string gpsStatusStr; + if (gpsStatus) { + gpsStatusStr = "GPS定位成功"; + TrainingControlView::GetInstance()->GetGpsStatusLabel()->SetTextColor( + Color::Green()); + } else { + gpsStatusStr = "GPS未定位成功"; + TrainingControlView::GetInstance()->GetGpsStatusLabel()->SetTextColor( + Color::Red()); + } + TrainingControlView::GetInstance()->GetGpsStatusLabel()->SetText( + gpsStatusStr.c_str()); + } + #endif + } + void TraingCardPresenter::UpdateTimeOfData() { + std ::string TimeStr = TraingModel::GetInstance()->DisplayMotionDuration(); + if (TimeStr.empty()) { + WEARABLE_LOGE( + WEARABLE_LOG_MODULE_APP, + "TraingModel::GetInstance()->DisplayMotionDuration() nullptr"); + return; + } + if (TrainingDataView::GetInstance()->GetTimeLabel() != nullptr) { + TrainingDataView::GetInstance()->GetTimeLabel()->SetText(TimeStr.c_str()); + } + } + + /* --------------------------------- cywee库操作 + * -------------------------------- */ + /*开启cywee*/ + bool TraingCardPresenter::OpenSensor(ExerciseClass type) { + // 防止重复开启 + if (opensensorstuats) { + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "OpenSensor is opening!!"); + return false; + } + // 根据不同的运动项目进行不同的运动 + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TraingCardPresenter::OpenSensor!!"); + #ifdef SUPPORT_CYWEE_SERVICE + switch (type) { + case ExerciseClass::NORMAL: + cywee_sport_mode_change(CYWEE_SERVICE_CMD_NORMAL_MODE); + break; + case ExerciseClass::TREADMILL: + cywee_sport_mode_change(CYWEE_SERVICE_CMD_TREADMILL_MODE); + break; + case ExerciseClass::OUTDOOR: + cywee_sport_mode_change(CYWEE_SERVICE_CMD_OUTDOOR_RUNNING_MODE); + break; + case ExerciseClass::INDOOR: + cywee_sport_mode_change(CYWEE_SERVICE_CMD_INDOOR_RUNNING_MODE); + break; + case ExerciseClass::FITNESS_PEDOMETER: + cywee_sport_mode_change(CYWEE_SERVICE_CMD_FITNESS_PEDOMETER_MODE); + break; + case ExerciseClass::FREE_TRAINING: + cywee_sport_mode_change(CYWEE_SERVICE_CMD_FREE_TRAINING_MODE); + break; + default: + break; + } + #endif + opensensorstuats = true; + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "OpenSensor--%d!!", type); + return true; + } + + /*控制cywee*/ + bool TraingCardPresenter::ControlSensor(ExerciseState state) { + #ifdef SUPPORT_PPG_SERVICE + switch (state) { + case ExerciseState::End: + // 退出进入普通模式 + do_cywee_service_check_msg(CYWEE_SERVICE_CMD_NORMAL_MODE, NULL); + opensensorstuats = false; // 恢复标志位 + StopAndResetTimer(); // 关闭计时器 + break; + case ExerciseState::Pause: + do_cywee_service_check_msg(CYWEE_SERVICE_CMD_PAUSE, 0); + break; + case ExerciseState::Continue: + do_cywee_service_check_msg(CYWEE_SERVICE_CMD_RESUME, 0); + break; + default: + break; + } + #endif + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ControlSensor[%d]!!", state); + return true; + } + + void TraingCardPresenter::RefreshData(SportData *sportdata) { + if (sportdata == nullptr) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata is null"); + return; + } + + if (TrainingDetailView::GetInstance() == nullptr || + TrainingDataView::GetInstance() == nullptr) + return; + + std::string distanceStr; + if (sportdata->distance != 0) { + double distance_km = + sportdata->distance / 1000.0; // 转换为公里并保留两位小数 + std::stringstream ss; + ss << std::fixed << std::setprecision(2) << distance_km; + distanceStr = ss.str(); + } else { + distanceStr = DEFAULT_DASH; + } + + std::string heartStr = + (sportdata->heart != 0) ? std::to_string(sportdata->heart) : DEFAULT_DASH; + std::string stepsStr = + (sportdata->steps != 0) ? std::to_string(sportdata->steps) : DEFAULT_DASH; + std::string calorieStr = (sportdata->calories != 0) + ? std::to_string(sportdata->calories) + : DEFAULT_DASH; + // 训练强度刷新 + UIResourceTextId intensityid = TraingModel::GetInstance()->GetIntensity(); + // printf("intensityid %d \r\n", intensityid); + TrainingDetailView::GetInstance()->Get_Intensity_Button()->SetTextId( + intensityid); + + TrainingDataView::GetInstance()->GetProgress()->SetValue( + sportdata->goaldegree); // 目标完成度 + TrainingDataView::GetInstance()->GetHeartLabel()->SetText( + heartStr.c_str()); // 心率 + TrainingDetailView::GetInstance()->GetCalorieLabel()->SetText( + calorieStr.c_str()); // 卡路里 + TrainingDetailView::GetInstance()->GetMileLabel()->SetText( + distanceStr.c_str()); // 距离 + TrainingDetailView::GetInstance()->GetStepLabel()->SetText( + stepsStr.c_str()); // 步数 + + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, + "[Calories: %s, Distance: %s, Steps: %s Intensityid: %d]", + calorieStr.c_str(), distanceStr.c_str(), stepsStr.c_str(), + intensityid); + // 释放sportdata内存 + } + #if 1 + /*刷新 time 和 data */ + void TraingCardPresenter::updataview(void) { + // 如果未开启训练,直接退出 + if (TraingModel::GetInstance()->GetSportState() == ExerciseState::General) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, + "TraingModel::GetInstance()->GetSportState() == " + "ExerciseState::General "); + return; + } + + if (TraingCardView::GetInstance() == nullptr) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "view_ == nullptr"); + return; + } + + Control_Card cardId = static_cast( + TraingCardView::GetInstance()->GetCurrentPage()); + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "cardId %d", (int)cardId); + + switch (cardId) { + case Control_Card::CONTROL: { + UpdateTimeOfControl(); // 更新控制界面的时间 + break; + } + case Control_Card::DATA: { + UpdateDataView(); // 更新训练数据视图 + break; + } + default: + break; // 其他情况不处理 + } + } + #else + void TraingCardPresenter::Notify(void) { + // 如果未开启训练,直接退出 + if (TraingModel::GetInstance()->GetSportState() == ExerciseState::General || + view_ == nullptr) { + return; + } + + Control_Card cardId = static_cast(view_->GetCurrentPage()); + + switch (cardId) { + case Control_Card::CONTROL: { + UpdateTimeOfControl(); // 更新控制界面的时间 + break; + } + case Control_Card::DATA: { + UpdateDataView(); // 更新训练数据视图 + break; + } + default: + break; // 其他情况不处理 + } + } + #endif + void TraingCardPresenter::UpdateDataView() { + // 刷新计时 + UpdateTimeOfData(); + + // 刷新训练数据视图,显示当前时间 + GraphicService::GetInstance()->PostGraphicEvent(std::bind( + &TrainingDataView::UpdataTime, TrainingDataView::GetInstance())); + + TraingModel::GetInstance()->updata_goal(); + + // 刷新训练数据:心率、卡路里、距离、步数、训练强度、目标完成度 + SportData *sportdata = TraingModel::GetInstance()->GetSportdata(); + RefreshData(sportdata); + } + /* --------------------------------- 点击事件处理 + * --------------------------------- */ + bool TraingCardPresenter::OnClick(UIView &view, const ClickEvent &event) { + const std::string viewId = view.GetViewId(); + + // 定义点击事件映射 + static const std::unordered_map> + actionMap = { + // 暂停按键 + {BUTTONSTOP, + [&]() { + auto currentState = TraingModel::GetInstance()->GetSportState(); + if (currentState == ExerciseState::Continue || + currentState == ExerciseState::Start) { + handlePauseState(); + } else if (currentState == ExerciseState::Pause) { + handleContinueState(); + } + }}, + // 结束按键 + {BUTTONEND, + [&]() { + // ControlSensor(ExerciseState::Pause); + TraingCardView::GetInstance()->SetPopupViewStatus(EndPopup, true); + TraingCardView::GetInstance()->RefreshEndView(); + }}, + // 确定结束 + {BUTTONEXIT, [&]() { handleEndState(); }}, + // 返回 + {BUTTONBACK, + [&]() { + // ControlSensor(ExerciseState::Continue); + TraingCardView::GetInstance()->SetPopupViewStatus(EndPopup, false); + }}, + // 确定达到目标 + {BUTTONWINOK, + [&]() { + TraingCardView::GetInstance()->GetAnimator()->Stop(); + TraingCardView::GetInstance()->SetPopupViewStatus(GetTagPopup, + false); + }}, + // 确定心率提醒 + {BUTTONNOTE, + [&]() { + TraingCardView::GetInstance()->SetPopupViewStatus(HeartPopup, + false); + }}, + }; + + // 查找并执行对应的操作 + auto it = actionMap.find(viewId); + if (it != actionMap.end()) { + it->second(); // 执行操作 + return true; + } + + return false; // 没有匹配的操作 + } + void TraingCardPresenter::OnSwipe(UISwipeView &view) { + WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "TempPresenter::OnSwipe "); + UNUSED(view); + return; + } + bool TraingCardPresenter::OnKeyAct(UIView &view, const KeyEvent &event) { + /*消耗按键事件*/ + return true; + } + /* -------------------------------- 协议发起运动处理 + * -------------------------------- */ + #ifdef SUPPORT_BLE_SERVICE + bool TraingCardPresenter::send_sport_cmd(training_ble *cmd) { + if (cmd == nullptr) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无效的运动命令 cmd 为空!"); + return false; + } + + // 获取当前的运动状态 + ExerciseState currentState = TraingModel::GetInstance()->GetSportState(); + ExerciseState cmdstate = static_cast(cmd->controlmd); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "cmdstate:%d currentState:%d", + cmdstate, currentState); + int m_sportTag = 0; + switch (cmd->controlmd) { + case SETGOAL: + // 仅在一般状态下允许设置目标 + if (currentState == ExerciseState::General) { + if (cmd->sportTagType == 0) { + //训练目标->m 需要转换 km + m_sportTag = cmd->sportTag / 1000; + } else { + m_sportTag = cmd->sportTag; + } + TraingModel::GetInstance()->SetSportTag( + static_cast(cmd->sportTagType), m_sportTag); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "目标设置成功!\r\n"); + return true; + } + break; + + case SENDDATA: + // 随时允许发送实时数据 + TraingModel::GetInstance()->SendLiveData(); + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "实时数据发送成功!\r\n"); + return true; + + default: + break; + } + + // 仅在一般状态下允许开始运动 + if (cmdstate == ExerciseState::Start) { + if ((currentState == ExerciseState::General)) { + if (TraingCardView::GetInstance() == nullptr) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 开启运动"); + uapi_set_screen_on_api(); // 亮屏 + NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGCRAD); + // 设置目标类型和项目有且只有一次 + switch (cmd->sportType) { + case 0: + TraingModel::GetInstance()->SetSportEvent( + STR_EXERCISE_OUTDOORWALKING); + OpenSensor(ExerciseClass::OUTDOOR); + break; + case 1: + TraingModel::GetInstance()->SetSportEvent( + STR_EXERCISE_OUTDOORRUNNING); + OpenSensor(ExerciseClass::OUTDOOR); + break; + case 2: + TraingModel::GetInstance()->SetSportEvent(STR_EXERCISE_OUTDOORBIKING); + OpenSensor(ExerciseClass::FREE_TRAINING); + break; + default: + break; + } + } + reply_APP_OpenSportModeCmd(true); + handleStartState(); + return true; + } else { + + reply_APP_OpenSportModeCmd(false); + // return false; + } + } + // 仅在运动状态为开始或继续时允许暂停 + if (cmdstate == ExerciseState::Pause) { + if (currentState == ExerciseState::Start || + currentState == ExerciseState::Continue) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 暂停运动"); + reply_APP_SportPasueResetCmd(TraingModel::GetInstance()->GetSportTime()); + handlePauseState(); + return true; + } + } + + // 仅在暂停状态下允许恢复运动 + if (cmdstate == ExerciseState::Continue && + currentState == ExerciseState::Pause) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 恢复运动"); + reply_APP_SportPasueResetCmd(TraingModel::GetInstance()->GetSportTime()); + handleContinueState(); + return true; + } + + // 仅在运动状态为开始、继续或暂停时允许结束运动 + if (cmdstate == ExerciseState::End && + (currentState == ExerciseState::Start || + currentState == ExerciseState::Continue || + currentState == ExerciseState::Pause)) { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 结束运动"); + uint32_t sport_time = TraingModel::GetInstance()->GetSportTime(); + reply_APP_SportStopModeCmd(sport_time); + handleEndState(); + return true; + } + + // 如果到达这里,命令未执行 + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, + "cmd->sportType[%d],cmd->controlmd[%d],cmd->sportTagType[%d]," + "cmd->sportTag[%d]\r\n", + cmd->sportType, cmd->controlmd, cmd->sportTagType, + cmd->sportTag); + + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, + "由于状态限制,命令未执行 当前状态 %d。\r\n", currentState); + return false; // 没有执行任何命令 + } + #endif + /*训练结束*/ + void TraingCardPresenter::handleEndState() { + TraingModel::GetInstance()->SetSportState(ExerciseState::End); + ControlSensor(ExerciseState::End); + uapi_set_screen_on_api(); // 亮屏 + #if DATATEST + // NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGENDDATA); + // 回退 + NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGNOMAL); + return; + #endif + // 是否达到训练目标 + if (TraingModel::GetInstance()->GetMomentum()) { + // 进入训练详情 + NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGENDDATA); + NativeAbility::GetInstance().ClearSliceIdStack(); // 清空所有SLiceId栈元素 + } else { + // 进入训练记录 + // NativeAbility::GetInstance().ChangeSlice(VIEW_TRAINING_RECORD); + if (TraingModel::GetInstance()->GetEntryType() == APPLIST_ENTER) + NativeAbility::GetInstance().ChangeSlice( + VIEW_TRAINING, TransitionType::TRANSITION_BACK_TO_HEXAGONS); + else { + NativeAbility::GetInstance().ChangeSlice( + VIEW_MAIN, TransitionType::TRANSITION_BACK_TO_HEXAGONS); + NativeAbility::GetInstance().ClearSliceIdStack(); // 清空所有SLiceId栈元素 + } + NativeAbility::GetInstance().ClearSliceIdStack(); // 清空所有SLiceId栈元素 + } + } + + /*训练暂停*/ + void TraingCardPresenter::handlePauseState() { + uapi_set_screen_on_api(); // 亮屏 + TrainingControlView::GetInstance()->GetLabel()->SetTextId(STR_RESUME); + TrainingControlView::GetInstance()->GetButton()->SetImageSrc( + SPORT_CONTINUE_IMAGE, SPORT_CONTINUE_IMAGE); + TraingModel::GetInstance()->SetSportState(ExerciseState::Pause); + PauseTimer(); + ControlSensor(ExerciseState::Pause); + } + + /*训练继续*/ + void TraingCardPresenter::handleContinueState() { + uapi_set_screen_on_api(); // 亮屏 + TrainingControlView::GetInstance()->GetLabel()->SetTextId(STR_PAUSE); + TrainingControlView::GetInstance()->GetButton()->SetImageSrc( + SPORT_PAUSE_IMAGE, SPORT_PAUSE_IMAGE); + TraingModel::GetInstance()->SetSportState(ExerciseState::Continue); + ResumeTimer(); + ControlSensor(ExerciseState::Continue); + } + + /*训练开始*/ + void TraingCardPresenter::handleStartState() { + ControlSensor(ExerciseState::Start); + TraingModel::GetInstance()->SetSportOpenWay(true); + } + + } // namespace OHOS + \ No newline at end of file diff --git a/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp b/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp index b6709e41a..3a1692975 100644 --- a/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp +++ b/code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp @@ -84,11 +84,20 @@ void TrainingControlView::InitView() { InitImageView(imagepoint, 193, 421, 80, 20, "image1", CONTROL_POINT_IMAGE); Add(imagepoint); -#if defined(BOARD_W9) || defined(BOARD_N9) +#if defined(SUPPORT_GNSS) +//需要判断是否需要运动是否需要gps + bool isNeedGPS = TraingModel::GetInstance()->IsNeedGPS(TraingModel::GetInstance()->GetSportEvent()); + if(isNeedGPS == false) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "该运动项不需要GPS"); + return; + } + InitLabel(gpsstatus, 83, 365, 300, 35, "label1", "gps:定位中...", DEFAULT_VECTOR_FONT_FILENAME, 30); - gpsstatus->SetTextColor(Color::Red()); - Add(gpsstatus); + gpsstatus->SetTextColor(Color::Red()); + Add(gpsstatus); + #endif } diff --git a/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingEnddataView.cpp b/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingEnddataView.cpp index c59372c58..45874c056 100644 --- a/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingEnddataView.cpp +++ b/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingEnddataView.cpp @@ -205,12 +205,7 @@ void TraingEnddataView ::InitializeLabels() { scrollView_->Add(labelstrong); scrollView_->Add(labelcalories); - // 初始化GPS轨迹失败提示标签(默认隐藏) - InitLabel(gpsNoTrackLabel, 33, 100 + CANVAS_SIZE/2 - 20, CANVAS_SIZE, 40, - "gpsNoTrackLabel", "未定位成功,没有GPS轨迹", DEFAULT_VECTOR_FONT_FILENAME, 24); - gpsNoTrackLabel->SetTextColor(Color::Red()); - gpsNoTrackLabel->SetVisible(false); // 默认隐藏 - scrollView_->Add(gpsNoTrackLabel); + } // 增加额外配置 void TraingEnddataView::BonusdataView() { @@ -485,10 +480,6 @@ void TraingEnddataView::DeInit() { delete labelcalories; labelcalories = nullptr; } - if (gpsNoTrackLabel) { - delete gpsNoTrackLabel; - gpsNoTrackLabel = nullptr; - } /* 删除图片 label */ if (imageIcon) { @@ -773,22 +764,21 @@ void TraingEnddataView::OnStart() { } #if defined(BOARD_W9) || defined(BOARD_N9) ///////////////////////////gps轨迹测试////////////////////// - canvas = new UICanvas(); - canvas->SetHeight(CANVAS_SIZE); - canvas->SetWidth(CANVAS_SIZE); - canvas->SetPosition(33, 100); - scrollView_->Add(canvas); - GenerateTrackPoints(); - // 检查是否有足够的GPS数据来绘制轨迹 std::vector canvasData = TraingModel::GetInstance()->GetCanvasData(); if (canvasData.size() >= 3) { + canvas = new UICanvas(); + canvas->SetHeight(CANVAS_SIZE); + canvas->SetWidth(CANVAS_SIZE); + canvas->SetPosition(33, 100); + scrollView_->Add(canvas); + GenerateTrackPoints(); // 有足够数据,绘制轨迹 DrawTrackSegment(canvas); DrawMarkers(canvas); } else { - // 数据不足,只显示提示标签(已在GenerateTrackPoints中处理) - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS轨迹数据不足,不绘制轨迹"); + // 数据不足,只显示提示标签 + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS轨迹数据不足,不绘制轨迹"); } #endif } @@ -806,20 +796,6 @@ void TraingEnddataView::GenerateTrackPoints() { seg1.color = Color::Blue(); std::vector canvasData = TraingModel::GetInstance()->GetCanvasData(); - //最小轨迹点数限制,防止绘制失败 - if (canvasData.size() < 3) { - // GPS数据不足,显示提示标签 - if (gpsNoTrackLabel) { - gpsNoTrackLabel->SetVisible(true); - WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS轨迹数据不足,显示提示标签"); - } - return; - } - - // GPS数据充足,隐藏提示标签 - if (gpsNoTrackLabel) { - gpsNoTrackLabel->SetVisible(false); - } //将点位赋值 for (size_t i = 0; i < canvasData.size(); ++i) { diff --git a/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingModel.cpp b/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingModel.cpp index aad12df03..1485333d7 100644 --- a/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingModel.cpp +++ b/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingModel.cpp @@ -3595,6 +3595,71 @@ int read_gps_data_(char *path, uint8_t *out_buf) { 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); + 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) { @@ -3603,6 +3668,7 @@ void ble_training_init(void) { 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 diff --git a/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp b/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp index fe7f65606..15446fa1e 100644 --- a/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp +++ b/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp @@ -50,7 +50,10 @@ namespace OHOS //判断是否是需要GPS的运动 if(TraingModel::GetInstance()->IsNeedGPS(TraingModel::GetInstance()->GetSportEvent()) == true) { + #if defined(SUPPORT_GNSS) + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开启gps定时器"); TraingModel::GetInstance()->gnss_start_timer(1000); + #endif } Presenter::OnStart(); } @@ -82,10 +85,12 @@ namespace OHOS NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS); //如果不是进入运动界面 if(enter_flag == false){ - // 关闭gps与心率 + // 关闭心率 TraingModel::GetInstance()->stop_heart_gps(); + #if defined(SUPPORT_GNSS) // 关闭gps定时器 TraingModel::GetInstance()->gnss_stop_timer(); + #endif } } bool TraingNomalPresenter::OnDrag(UIView &view, const DragEvent &event) diff --git a/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp b/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp index b9115cb00..c0ccf3b5b 100644 --- a/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp +++ b/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp @@ -141,7 +141,14 @@ void TraingCardPresenter::UpdateTimeOfControl() { TrainingControlView::GetInstance()->GetTimeLabel()->SetText( TimeStr.c_str()); } -#if defined(BOARD_W9) || defined(BOARD_N9) + //需要判断是否需要运动是否需要gps + bool isNeedGPS = TraingModel::GetInstance()->IsNeedGPS(TraingModel::GetInstance()->GetSportEvent()); + if(isNeedGPS == false) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "该运动项不需要GPS"); + return; + } + if (TrainingControlView::GetInstance()->GetGpsStatusLabel() != nullptr) { //获取GPS状态 bool gpsStatus = TraingModel::GetInstance()->GetGPSConnectState(); @@ -158,7 +165,6 @@ void TraingCardPresenter::UpdateTimeOfControl() { TrainingControlView::GetInstance()->GetGpsStatusLabel()->SetText( gpsStatusStr.c_str()); } -#endif } void TraingCardPresenter::UpdateTimeOfData() { std ::string TimeStr = TraingModel::GetInstance()->DisplayMotionDuration(); diff --git a/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp b/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp index b6709e41a..3a1692975 100644 --- a/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp +++ b/code/sdk/application/wearable/n9/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp @@ -84,11 +84,20 @@ void TrainingControlView::InitView() { InitImageView(imagepoint, 193, 421, 80, 20, "image1", CONTROL_POINT_IMAGE); Add(imagepoint); -#if defined(BOARD_W9) || defined(BOARD_N9) +#if defined(SUPPORT_GNSS) +//需要判断是否需要运动是否需要gps + bool isNeedGPS = TraingModel::GetInstance()->IsNeedGPS(TraingModel::GetInstance()->GetSportEvent()); + if(isNeedGPS == false) + { + WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "该运动项不需要GPS"); + return; + } + InitLabel(gpsstatus, 83, 365, 300, 35, "label1", "gps:定位中...", DEFAULT_VECTOR_FONT_FILENAME, 30); - gpsstatus->SetTextColor(Color::Red()); - Add(gpsstatus); + gpsstatus->SetTextColor(Color::Red()); + Add(gpsstatus); + #endif }