#include "traing/include/TraingModel.h"
#include "cmsis_os.h"
#include "custom_utils.h"
-
#include "safemodemanager/include/SafeModeManagerModelC.h"
-
#include "thread_adapter.h"
#include "traing/training_card/include/TraingCardPresenter.h"
#include "uicomponents/UIComponents.h"
#include "wearable_log.h"
#include <cmsis_os2.h>
+#include <cstdint>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>
#include "chip_system_time.h"
+#include "cmath"
+#include "system/include/SystemLogModel.h"
#include <custom_save_runtime.h>
-extern "C" errcode_t uapi_get_timestampe_ms(uint32_t *tm);
-extern "C" errcode_t uapi_get_timestampe_ss(uint32_t *tm);
+#if defined(SUPPORT_GNSS)
+#include "NMEA0183.h"
+#include "gnss_ctl.h"
+#include "graphic_service.h"
+#include "pgnss_sample.h"
+#endif
#ifdef __cplusplus
extern "C" {
#endif
void store_sport_data() {
- printf("运动数据存储!\r\n");
+ // printf("运动数据存储!\r\n");
OHOS::TraingModel::GetInstance()->RecordSportData();
return;
}
-#if TIMER
+
namespace OHOS {
+#if TIMER
// 声明定时器变量
static osTimerId_t g_startCounting = NULL; // 定时器
static constexpr uint64_t MS_ONE_SECONDS = 1000;
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}; //震动提醒
sportdata->spc = data->spc;
sportdata->std = data->std;
sportdata->cdc = data->cdc;
- printf_data(data); //打印该运动数据
- IsSpecialSport(sportdata->sportId); // 配置训练特殊性
+ sportdata->starttime = data->starttime; //更新开始时间
+ printf_data(data); //打印该运动数据
+ IsSpecialSport(sportdata->sportId); // 配置训练特殊性
}
}
/*获取训练卡路里*/
Openstate = true; // 开启运动开始标志位
shock_count = 0; //重置震动计数
+
+ gpsData.clear(); // 清空gps数据
+ canvasData.clear(); // 清空画布数据
}
uint32_t TraingModel::get_current_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");
}
ExerciseState currentState = GetSportState(); // 获取当前运动状态
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "currentState:%d", currentState);
// 增加标志位互斥保障"开始"和"结束"的唯一性操作
if (currentState == ExerciseState::Start && !Startstate) {
Endstate = false; // 确保"开始"和"结束"状态互斥
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始运动");
- sport_data_timer(300); //开启数据获取定时器
-#ifdef SYSTEM_LOG_SERVICE
//日志记录
if (Openingway) {
uapi_save_runtime_log(RUNTIME_LOG_APP_SPORT_LAUNCH); // app发起运动
} 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) {
//配置最后的训练强度(平均心率为标准)
SetIntensity(sportdata->avgheart);
+ //更新gps点位
+ GetPointData();
+
do_custom_fileoperate_service_check_msg(
CUSTOM_FILEOPERATE_MSG_TYPE_SPORTDATA_SAVE, nullptr, 0);
- // store_sport_data();
-
Startstate = false;
Endstate = true;
+ stop_heart_gps(); // 停止心率和gps数据采集
+
#if 0
// 重置训练目标 [发生在训练前]
sportTag_ = 0;
sportTagType_ = ExerciseTag::UNKNOW;
#endif
-#ifdef BLE_SERVER_ENABLE
/*运动结束并且是手表发起的运动*/
if (Openingway == false) {
sendtraningstateend();
else {
sendtraningstatedata(0, sportdata->duration);
}
-#endif
+
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "结束运动");
}
}
/* 设置训练强度 */
void TraingModel::SetIntensity(int heart) {
// sportdata->intensity = active_state;
-#if 1
/*
训练强度
220-年龄=最高心率
(heartRateAsDouble / (double)user_Intensity) * 100;
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
"heart: %d, maxHeartRate: %d, intensityPercentage: %.2f", heart,
- TraingModel, intensityPercentage);
+ HeartModel, intensityPercentage);
if (intensityPercentage < LOW_INTENSITY_THRESHOLD) {
// 较低强度
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
"maxHeartRate: %d, heartRate: %d, intensityPercentage: %.2f",
maxHeartRate, heart, intensityPercentage);
-#endif
}
/*获取训练强度STR*/
UIResourceTextId TraingModel::GetIntensity() {
// 保存速度表
if (!SavePaceTable()) {
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveSpeedData is error!");
- // uapi_dev_runtime_set_train_storage_status(0); // 恢复状态
+ // 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;
}
}
// 检查重复时间戳
for (const auto &entry : heartRateTable) {
if (entry.timestamp == timestamp) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"时间戳重复无法添加数据 AddHeartRateData is error!");
+ 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!");
+ WEARABLE_LOGE(
+ WEARABLE_LOG_MODULE_APP,
+ "达到最大心率数据条目限制 无法添加新的数据 AddHeartRateData is error!");
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::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) {
motionDuration = oss.str();
+ // // 获取当前时间(以微秒为单位)
+ // uint32_t currentTime = static_cast<uint32_t>(
+ // std::chrono::duration_cast<std::chrono::microseconds>(
+ // std::chrono::system_clock::now().time_since_epoch())
+ // .count());
+
+ // // 记录和检查上一次调用的时间
+ // static uint32_t lastCallTime = 0;
+ // if (currentTime - lastCallTime < 1000000) { // 小于1秒
+ // return motionDuration;
+ // }
+ // lastCallTime = currentTime; // 更新最后调用时间
+
+ // 如果 g_motionTimes 不为零,进行计算和数据存储
if (g_motionTimes > 0) {
sportdata->duration = g_motionTimes; // 设置训练时长
+ // 每分钟进行配速和心率数据存储
+ // StoreDataIfMinutePassed();
}
return motionDuration; // 返回格式化的运动时长
}
}
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "duration:[%d] g_motionTimes[%d]\r\n",
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "duration:[%d] g_motionTimes[%d]\r\n",
sportdata->duration, g_motionTimes);
}
-
#ifdef __cplusplus
extern "C" {
#endif
/* 定时器回调函数 */
void TraingTimerCallback(void) {
// 只在活动状态下进行更新
+
if (pauseStartTimestamp == 0) {
//开始时将该值设置为运动开始值
pauseStartTimestamp = sportdata->starttime;
}
- uint32_t currentTimestamp =OHOS::TraingModel::GetInstance()->get_current_time(); // 获取当前时间戳
- g_motionTimes = (currentTimestamp - pauseStartTimestamp) + totalPausedDuration; // 更新运动时间
+ uint32_t currentTimestamp =
+ OHOS::TraingModel::GetInstance()->get_current_time(); // 获取当前时间戳
+ g_motionTimes = (currentTimestamp - pauseStartTimestamp) +
+ totalPausedDuration; // 更新运动时间
sportdata->duration = g_motionTimes; // 更新持续时间
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
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);
return true;
}
+/* 加载训练数据函数 */
/* 加载训练数据函数 */
bool TraingModel::LoadDataFormJson(std::vector<SportData> &sportDataVector) {
+ // 清空输出向量,避免旧数据干扰
+ sportDataVector.clear();
+
+ // 打开JSON文件
std::ifstream file(TRAINING_RECORD_FILE);
if (!file.is_open()) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open file");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to open file: %s",
+ TRAINING_RECORD_FILE);
return false;
}
+ // 读取文件内容到字符串
std::string jsonData((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
file.close();
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Read %zu bytes from JSON file",
+ jsonData.size());
+
+ // 解析JSON根对象
cJSON *root = cJSON_Parse(jsonData.c_str());
if (root == NULL) {
+ const char *errorPtr = cJSON_GetErrorPtr();
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Failed to parse JSON: %s",
- cJSON_GetErrorPtr());
+ errorPtr ? errorPtr : "Unknown error");
return false;
}
+ // 查找并验证data数组
cJSON *dataArray = cJSON_GetObjectItem(root, "data");
- if (dataArray == NULL || !cJSON_IsArray(dataArray)) {
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
- "Invalid or missing 'data' array in JSON");
+ 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, "error dataCount is 0");
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Empty 'data' array in JSON");
cJSON_Delete(root);
return false;
}
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "dataCount is %d", dataCount);
+ 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, "Invalid item at index %d", i);
- continue; // 跳过无效项目
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Null item at index %d in 'data' array", i);
+ continue; // 跳过无效项,继续处理其他项
}
SportData sportData;
- bool isValid = true; // 用于标记 sportData 是否有效
+ bool isValid = true;
+ std::string missingFields; // 记录缺失的字段
- // 检查并获取每个字段,确保其存在且类型正确
- if (cJSON *sportTypeIdItem = cJSON_GetObjectItem(item, "sportTypeId");
- sportTypeIdItem && cJSON_IsNumber(sportTypeIdItem))
- sportData.sportType = (Exercisetype)sportTypeIdItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *sportIdItem = cJSON_GetObjectItem(item, "sportId");
- sportIdItem && cJSON_IsNumber(sportIdItem))
- sportData.sportId = (UIResourceTextId)sportIdItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *startDateItem = cJSON_GetObjectItem(item, "startDate");
- startDateItem && cJSON_IsString(startDateItem))
- sportData.start_date = startDateItem->valuestring;
- else
+ // 解析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 ";
+ }
- if (cJSON *startTimeItem = cJSON_GetObjectItem(item, "startTime");
- startTimeItem && cJSON_IsString(startTimeItem))
- sportData.start_time = startTimeItem->valuestring;
- else
+ // 解析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 ";
+ }
- if (cJSON *endTimeItem = cJSON_GetObjectItem(item, "endTime");
- endTimeItem && cJSON_IsString(endTimeItem))
- sportData.end_time = endTimeItem->valuestring;
- else
+ // 解析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 ";
+ }
- if (cJSON *goalDegreeItem = cJSON_GetObjectItem(item, "goalDegree");
- goalDegreeItem && cJSON_IsNumber(goalDegreeItem))
- sportData.goaldegree = goalDegreeItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *durationItem = cJSON_GetObjectItem(item, "duration");
- durationItem && cJSON_IsNumber(durationItem))
- sportData.duration = durationItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *distanceItem = cJSON_GetObjectItem(item, "distance");
- distanceItem && cJSON_IsNumber(distanceItem))
- sportData.distance = distanceItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *avgHeartRateItem = cJSON_GetObjectItem(item, "avgHeartRate");
- avgHeartRateItem && cJSON_IsNumber(avgHeartRateItem))
- sportData.avgheart = avgHeartRateItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *stepsItem = cJSON_GetObjectItem(item, "steps");
- stepsItem && cJSON_IsNumber(stepsItem))
- sportData.steps = stepsItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *intensityItem = cJSON_GetObjectItem(item, "intensity");
- intensityItem && cJSON_IsNumber(intensityItem))
- sportData.intensity = intensityItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *apcItem = cJSON_GetObjectItem(item, "apc");
- apcItem && cJSON_IsNumber(apcItem))
- sportData.apc = apcItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *cdcItem = cJSON_GetObjectItem(item, "cdc");
- cdcItem && cJSON_IsNumber(cdcItem))
- sportData.cdc = cdcItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *stdItem = cJSON_GetObjectItem(item, "std");
- stdItem && cJSON_IsNumber(stdItem))
- sportData.std = stdItem->valueint;
- else
+ // 解析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 ";
+ }
- if (cJSON *caloriesItem = cJSON_GetObjectItem(item, "calories");
- caloriesItem && cJSON_IsNumber(caloriesItem))
- sportData.calories = caloriesItem->valueint;
- else
+ // 解析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 ";
+ }
- // 只有在所有字段都有效的情况下才添加 sportData 到向量中
+ // 解析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", i);
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Invalid data in item %d. Missing fields: %s", i,
+ missingFields.c_str());
}
}
cJSON_Delete(root);
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "LoadDataFormJson is ok");
+
+ // 检查是否成功解析了任何数据
+ if (sportDataVector.empty()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "No valid data items found in JSON");
+ return false;
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "Successfully loaded %zu valid data items",
+ sportDataVector.size());
return true;
}
+//////////////////////////////////////GPS///////////////////////////////////////////////
+/*
+ Latitude(纬度):范围:-90° 至 +90°(或南纬 90° 至北纬 90°);
+ Longitude(经度):范围:-180° 至 +180°(或西经 180° 至东经 180°);
+
+ -画布数据集最多是100个定位坐标,超过按比例进行拿取100个点位;
+ -通过映射关系,将经纬度转换为400*400画布上的坐标;
+ -但是不能靠近400,以免显示不全等问题,350*350;
+ -如果处理负数,需要将其转换为正数,然后再进行映射?
+ -如果有正数又有负数,应该怎么处理?
+*/
+
+static double getmin(double a, double b) { return a < b ? a : b; }
+static double getmax(double a, double b) { return a > b ? a : b; }
+// 计算点p到由p1和p2确定的线段的垂直距离(优化版)
+static double perpendicularDistance(const Point &p, const Point &p1,
+ const Point &p2) {
+ // 计算向量 p1->p2
+ double dx = p2.x - p1.x;
+ double dy = p2.y - p1.y;
+
+ // 线段长度的平方
+ double len2 = dx * dx + dy * dy;
+
+ // 如果线段退化为一个点,直接计算点到点的距离
+ if (len2 == 0) {
+ return std::hypot(p.x - p1.x, p.y - p1.y);
+ }
+
+ // 计算投影比例 t
+ double t = ((p.x - p1.x) * dx + (p.y - p1.y) * dy) / len2;
+
+ if (t < 0) {
+ // 投影在p1之前,返回p到p1的距离
+ return std::hypot(p.x - p1.x, p.y - p1.y);
+ }
+
+ if (t > 1) {
+ // 投影在p2之后,返回p到p2的距离
+ return std::hypot(p.x - p2.x, p.y - p2.y);
+ }
+
+ // 投影在线段上,计算投影点并返回距离
+ double projX = p1.x + t * dx;
+ double projY = p1.y + t * dy;
+ return std::hypot(p.x - projX, p.y - projY);
+}
+
+// Douglas-Peucker算法实现(优化版)
+static void douglasPeucker(const std::vector<Point> &points, double epsilon,
+ std::vector<Point> &result) {
+ if (points.size() <= 2) {
+ // 点数量不足,直接返回
+ result = points;
+ return;
+ }
+
+ double dmax = 0;
+ int index = 0;
+ int end = points.size() - 1;
+
+ // 找到距离最大的点
+ for (int i = 1; i < end; i++) {
+ double d = perpendicularDistance(points[i], points[0], points[end]);
+ if (d > dmax) {
+ index = i;
+ dmax = d;
+ }
+ }
+
+ // 如果最大距离大于阈值,则递归处理
+ if (dmax > epsilon) {
+ std::vector<Point> recResults1, recResults2;
+
+ // 递归处理前半部分
+ douglasPeucker(
+ std::vector<Point>(points.begin(), points.begin() + index + 1), epsilon,
+ recResults1);
+
+ // 递归处理后半部分
+ douglasPeucker(std::vector<Point>(points.begin() + index, points.end()),
+ epsilon, recResults2);
+
+ // 合并结果(避免重复添加端点)
+ result.assign(recResults1.begin(), recResults1.end() - 1);
+ result.insert(result.end(), recResults2.begin(), recResults2.end());
+ } else {
+ // 否则保留首尾点
+ result = {points[0], points[end]};
+ }
+}
+
+// 改进的GetPointData函数(优化版)
+void TraingModel::GetPointData() {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "Generating point data from %zu GPS points", gpsData.size());
+
+ std::vector<GPSData> sampledData;
+
+ // 处理GPS数据
+ if (gpsData.size() > 100) {
+ // 计算经纬度范围
+ double minLat = 90.0, maxLat = -90.0;
+ double minLon = 180.0, maxLon = -180.0;
+
+ // 同时计算平均值用于判断数据分布
+ double sumLat = 0, sumLon = 0;
+
+ for (const auto &point : gpsData) {
+ minLat = getmin(minLat, point.latitude);
+ maxLat = getmax(maxLat, point.latitude);
+ minLon = getmin(minLon, point.longitude);
+ maxLon = getmax(maxLon, point.longitude);
+ sumLat += point.latitude;
+ sumLon += point.longitude;
+ }
+
+ // 计算经纬度范围和平均值
+ double rangeLat = maxLat - minLat;
+ double rangeLon = maxLon - minLon;
+ double avgLat = sumLat / gpsData.size();
+ double avgLon = sumLon / gpsData.size();
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "GPS range: lat=[%.6f, %.6f], lon=[%.6f, %.6f]", minLat,
+ maxLat, minLon, maxLon);
+
+ // 检查数据是否集中在极小区域或几乎相同
+ const double MIN_SIGNIFICANT_RANGE = 0.01; // 根据实际情况调整
+ bool isDataDegenerate =
+ (rangeLat < MIN_SIGNIFICANT_RANGE && rangeLon < MIN_SIGNIFICANT_RANGE);
+
+ if (isDataDegenerate) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "GPS data is degenerate (very small range)");
+ // 使用均匀采样,避免算法失效
+ double step = static_cast<double>(gpsData.size() - 1) / 99;
+ for (int i = 0; i < 100; i++) {
+ int index = static_cast<int>(i * step);
+ sampledData.push_back(gpsData[index]);
+ }
+ } else {
+ // 转换GPS坐标到屏幕坐标
+ std::vector<Point> screenPoints;
+ screenPoints.reserve(gpsData.size());
+
+ for (const auto &point : gpsData) {
+ double normLat = (point.latitude - minLat) / rangeLat;
+ double normLon = (point.longitude - minLon) / rangeLon;
+ screenPoints.push_back(
+ {normLon * 300 + 50, (1.0 - normLat) * 300 + 50});
+ }
+
+ // 使用Douglas-Peucker算法简化轨迹
+ std::vector<Point> simplifiedPoints;
+ douglasPeucker(screenPoints, 0.5, simplifiedPoints);
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "After Douglas-Peucker: %zu points",
+ simplifiedPoints.size());
+
+ // 处理简化结果
+ if (simplifiedPoints.size() <= 2) {
+ WEARABLE_LOGE(
+ WEARABLE_LOG_MODULE_APP,
+ "Trajectory over-simplified! Using uniform sampling instead.");
+ // 过度简化时回退到均匀采样
+ double step = static_cast<double>(gpsData.size() - 1) / 99;
+ for (int i = 0; i < 100; i++) {
+ int index = static_cast<int>(i * step);
+ sampledData.push_back(gpsData[index]);
+ }
+ } else if (simplifiedPoints.size() > 100) {
+ // 简化后仍超过100点,再次均匀采样
+ double step = static_cast<double>(simplifiedPoints.size() - 1) / 99;
+ for (int i = 0; i < 100; i++) {
+ int index = static_cast<int>(i * step);
+ // 屏幕坐标反推回GPS坐标
+ double normX = (simplifiedPoints[index].x - 50) / 300;
+ double normY = (simplifiedPoints[index].y - 50) / 300;
+ double lat = minLat + (1.0 - normY) * rangeLat;
+ double lon = minLon + normX * rangeLon;
+ sampledData.push_back({0, lat, lon});
+ }
+ } else {
+ // 简化后点数量合适,直接使用
+ for (const auto &point : simplifiedPoints) {
+ double normX = (point.x - 50) / 300;
+ double normY = (point.y - 50) / 300;
+ double lat = minLat + (1.0 - normY) * rangeLat;
+ double lon = minLon + normX * rangeLon;
+ sampledData.push_back({0, lat, lon});
+ }
+ }
+ }
+ } else {
+ // GPS点数不足100,直接使用全部数据
+ sampledData = gpsData;
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Using all %zu GPS points (no sampling)", sampledData.size());
+ }
+
+ // 映射到画布坐标
+ canvasData.clear();
+ canvasData.reserve(sampledData.size());
+
+ // 如果没有采样数据,直接返回
+ if (sampledData.empty()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "No sampled data to map!");
+ return;
+ }
+
+ // 基于采样数据重新计算经纬度范围
+ double minLat = 90.0, maxLat = -90.0;
+ double minLon = 180.0, maxLon = -180.0;
+
+ for (const auto &point : sampledData) {
+ minLat = getmin(minLat, point.latitude);
+ maxLat = getmax(maxLat, point.latitude);
+ minLon = getmin(minLon, point.longitude);
+ maxLon = getmax(maxLon, point.longitude);
+ }
+
+ double rangeLat = maxLat - minLat;
+ double rangeLon = maxLon - minLon;
+
+ // 避免除零错误
+ if (rangeLat == 0)
+ rangeLat = 1e-10;
+ if (rangeLon == 0)
+ rangeLon = 1e-10;
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "Sampled GPS range: lat=[%.6f, %.6f], lon=[%.6f, %.6f]", minLat,
+ maxLat, minLon, maxLon);
+
+ // 映射每个点到画布
+ for (const auto &point : sampledData) {
+ double normLat = (point.latitude - minLat) / rangeLat;
+ double normLon = (point.longitude - minLon) / rangeLon;
+
+ // 转换到画布坐标
+ double x = normLon * 300 + 50;
+ double y = (1.0 - normLat) * 300 + 50;
+
+ canvasData.push_back({x, y});
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "Mapped point: (%.6f, %.6f) -> (%.2f, %.2f)", point.latitude,
+ point.longitude, x, y);
+ }
+
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Generated %zu canvas points",
+ canvasData.size());
+}
+
+// 添加经纬度数据集
+void TraingModel::AddGPSData(uint32_t timestamp, double latitude,
+ double longitude) {
+ // 判断数据是否合理
+ if (latitude < -90 || latitude > 90 || longitude < -180 || longitude > 180) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Invalid GPS data: lat=%.6f, lon=%.6f", latitude, longitude);
+ return;
+ }
+
+ // 校验时间戳是否递增
+ if (!gpsData.empty() && timestamp <= gpsData.back().timestamp) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Timestamp not increasing: current=%u, last=%u", timestamp,
+ gpsData.back().timestamp);
+ return;
+ }
+
+ // 添加数据
+ gpsData.push_back({timestamp, latitude, longitude});
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "Added GPS data #%zu: ts=%u, (%.6f, %.6f)", gpsData.size(),
+ timestamp, latitude, longitude);
+}
+
+//保存canvas数据
+bool TraingModel::SaveCanvasData() {
+
+ //打印一下gps的数据
+ for (const auto &point : gpsData) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "GPS Data: Latitude = %f, Longitude = %f", point.latitude,
+ point.longitude);
+ }
+
+ if (canvasData.size() == 0) {
+ WEARABLE_LOGE(WEARING_LOG_MODULE_APP, "canvasData is empty");
+ return false;
+ }
+
+ CreateDirectoryIfNotExists(TRAINING_CANVAS_FILE);
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "canvasData.size() = %zu",
+ canvasData.size());
+
+ std::string filePath =
+ TRAINING_CANVAS_FILE + std::to_string(sportdata->starttime) + ".bin";
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveCanvasData filePath: %s",
+ filePath.c_str());
+
+ std::ofstream gpsFile(filePath, std::ios::binary);
+ if (!gpsFile) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
+ filePath.c_str());
+ return false; // 返回失败
+ }
+ // 写入gps数据
+ if (!gpsFile.write(reinterpret_cast<const char *>(canvasData.data()),
+ canvasData.size() * sizeof(Point))) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "写入GPS数据失败。");
+ return false; // 返回失败
+ }
+
+ gpsFile.close();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData Success.");
+ return true; // 返回成功
+}
+// 清除canvas数据
+void TraingModel::ClearCanvasData() { canvasData.clear(); }
+// 获取轨迹点集
+std::vector<Point> TraingModel::GetCanvasData() const { return canvasData; }
+// 获取canvas数据
+std::string TraingModel::GetCanvasDataPath(uint32_t timestamp) {
+ // 首先检查完全匹配的时间戳
+ std::string exactFilePath =
+ TRAINING_CANVAS_FILE + std::to_string(timestamp) + ".bin";
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "canvas表: %s", exactFilePath.c_str());
+ if (IsFileAccessibleAndNotEmpty(exactFilePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到canvas表: %s",
+ exactFilePath.c_str());
+ return exactFilePath; // 返回完全匹配的文件路径
+ }
+
+ // 如果未找到,检查时间戳相差1到2秒的文件
+ for (int i = -2; i <= 2; ++i) {
+ uint32_t checkTimestamp = timestamp + i;
+ std::string filePath =
+ TRAINING_CANVAS_FILE + std::to_string(checkTimestamp) + ".bin";
+ if (IsFileAccessibleAndNotEmpty(filePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到canvas表: %s",
+ filePath.c_str());
+ return filePath; // 返回找到的文件路径
+ }
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的找到canvas表。");
+ return ""; // 未找到文件
+}
+// 加载GPS数据
+bool TraingModel::LoadCanvasData(const std::string &filePath) {
+ std::ifstream gpsFile(filePath, std::ios::binary);
+ if (!gpsFile) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
+ filePath.c_str());
+ return false;
+ }
+
+ // 清空现有数据
+ canvasData.clear();
+
+ // 动态读取数据(假设文件格式允许逐点读取)
+ Point point;
+ while (gpsFile.read(reinterpret_cast<char *>(&point), sizeof(Point))) {
+ canvasData.push_back(point);
+ }
+
+ gpsFile.close();
+
+ if (canvasData.empty()) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未读取到有效数据: %s",
+ filePath.c_str());
+ return false;
+ }
+
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "LoadCanvasData Success. 加载了 %zu 个点", canvasData.size());
+ return true;
+}
+//////////////////////////////////////gps存储///////////////////////////////////////
+//存储gps数据
+bool TraingModel::SaveGPSData() {
+ //打印gps数据
+ for (const auto &point : gpsData) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
+ "GPS Data: Latitude = %f, Longitude = %f", point.`latitude,
+ point.longitude);
+ }
+ CreateDirectoryIfNotExists(TRAINING_GPS_FILE);
+ std::string filePath =
+ TRAINING_GPS_FILE + std::to_string(sportdata->starttime) + ".bin";
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData filePath: %s",
+ filePath.c_str());
+
+ std::ofstream gpsFile(filePath, std::ios::binary);
+ if (!gpsFile) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无法打开文件: %s",
+ filePath.c_str());
+ return false; // 返回失败
+ }
+ // 写入gps数据
+ if (!gpsFile.write(reinterpret_cast<const char *>(gpsData.data()),
+ gpsData.size() * sizeof(GPSData))) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "写入GPS数据失败。");
+ return false; // 返回失败
+ }
+ gpsFile.close();
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveGPSData Success.");
+ return true;
+}
+std::string TraingModel::GetGPSDataPath(uint32_t timestamp) {
+ // 首先检查完全匹配的时间戳
+ std::string exactFilePath =
+ TRAINING_GPS_FILE + std::to_string(timestamp) + ".bin";
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "gps表: %s", exactFilePath.c_str());
+ if (IsFileAccessibleAndNotEmpty(exactFilePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到gps表: %s",
+ exactFilePath.c_str());
+ return exactFilePath; // 返回完全匹配的文件路径
+ }
+
+ // 如果未找到,检查时间戳相差1到2秒的文件
+ for (int i = -2; i <= 2; ++i) {
+ uint32_t checkTimestamp = timestamp + i;
+ std::string filePath = TRAINING_GPS_FILE + std::to_string(checkTimestamp) + ".bin";
+ if (IsFileAccessibleAndNotEmpty(filePath)) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "找到gps表: %s", filePath.c_str());
+ return filePath; // 返回找到的文件路径
+ }
+ }
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "未找到与时间戳相关的找到gps表。");
+ return "";
+}
+
+//获取GPS连接状态
+bool TraingModel::GetGPSConnectState() { return gpsconnect; }
+//设置GPS连接状态
+void TraingModel::SetGPSConnectState(bool state) {
+ if (gpsconnect != state) {
+ gpsconnect = state;
+ //状态改变记录
+ // SystemLogModel::GetInstance()->save_gps_status_log_type(gpsconnect);
+ }
+}
+//开启gps
+void TraingModel::OpenGPS() {
+// 开启GPS的代码逻辑
+#if defined(SUPPORT_GNSS)
+ gnss_start();
+#endif
+}
+//关闭gps
+void TraingModel::CloseGPS() {
+// 关闭GPS的代码逻辑
+#if defined(SUPPORT_GNSS)
+ gnss_stop();
+#endif
+}
+//运动心率、gps开启准备
+void TraingModel::start_heart_gps() { OpenGPS(); }
+//运动心率、gps关闭
+void TraingModel::stop_heart_gps() { CloseGPS(); }
+
+/////////////////////////////////////gps定时器/////////////////////////////////////
+static osTimerId_t g_timer = nullptr;
+static int Time = 30;
+/*定时器到期时的处理*/
+/*定时器到期时的处理*/
+static void Traing_TimerCallbackProc(int data) {
+ /*
+ 定时倒计时 后续实现
+ */
+#if defined(SUPPORT_GNSS)
+ gps_data_t send_gps_data = {0};
+ gps_data_t *gps_data = getPGPSGlobaldata();
+
+ if (gps_data == NULL) {
+ PRINT("gps_data == NULL \n");
+ return;
+ }
+
+ // 判断是否为第一次定位并复制GPS数据
+ if (gps_data->last_gps_time_ms > 0) {
+ memcpy_s(&send_gps_data, sizeof(gps_data_t), gps_data, sizeof(gps_data_t));
+ }
+ else
+ {
+ return;
+ }
+
+ // 判断是否定位成功并设置连接状态
+ bool isGpsConnected = (send_gps_data.status > 1);
+ TraingModel::GetInstance()->SetGPSConnectState(isGpsConnected);
+
+ // 判断是否在运动或训练已结束
+ if (uapi_dev_runtime_get_train_status() == 0 ||
+ TraingModel::GetInstance()->GetSportState() == ExerciseState::End) {
+ 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);
+ }
+#endif
+}
+/*
+ * @brief 回调函数
+ */
+static void Traing_TimerCallback(int data) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Traing_TimerCallback %d", data);
+ GraphicService::GetInstance()->PostGraphicEvent(
+ std::bind(Traing_TimerCallbackProc, data));
+}
+
+/*
+ * @brief 开启定时器函数
+ * @param interval 时间间隔,单位ms
+ */
+void TraingModel::Traing_StartTimer(uint32_t interval) {
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "TraingModel::Traing_StartTimer");
+ /* -------------------------- 1.检查定时器是否已经存在,如果存在则返回
+ * ------------------------- */
+ if (g_timer != nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Timer already exists!!");
+ return;
+ }
+
+ /* --------------------------------- 2.创建定时器
+ * -------------------------------- */
+ g_timer = osTimerNew((osTimerFunc_t)Traing_TimerCallback, osTimerPeriodic,
+ nullptr, nullptr);
+ // 检查定时器创建是否成功
+ if (g_timer == nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Timer create failed!!");
+ return;
+ }
+
+ /* ----------------------------- 3.将毫秒转换为系统时钟节拍数
+ * ---------------------------- */
+ int tick = osMs2Tick(interval);
+
+ /* --------------------------------- 4.启动定时器
+ * -------------------------------- */
+ osStatus_t ret = osTimerStart(g_timer, tick);
+ /* -------------------- 5.检查定时器启动是否成功,如果启动失败,删除定时器并置为
+ * nullptr -------------------- */
+ if (ret != osOK) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "osTimerDelete failed, ret = %lx!!",
+ ret);
+ if (g_timer != nullptr) {
+ ret = osTimerDelete(g_timer);
+ }
+ g_timer = nullptr;
+ return;
+ }
+}
+/*
+ * @brief 停止定时器
+ */
+void TraingModel::Traing_StopTimer(void) {
+ // 检查定时器是否存在
+ if (g_timer != nullptr) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TraingModel::Traing_StopTimer");
+ osTimerStop(g_timer);
+ // 删除定时器并置为 nullptr
+ osStatus_t ret = osTimerDelete(g_timer);
+ if (ret != osOK) {
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
+ "osTimerDelete failed, ret = %lx!!", ret);
+ }
+ g_timer = nullptr;
+ }
+}
+
///////////////////////////////////////datatimer//////////////////////////////////
#if SPORT_TIMER_DATA
// 声明定时器变量
#endif
-} // namespace OHOS
\ No newline at end of file
+} // namespace OHOS
#define DEFAULT_DASH "--"\r
#define DATATEST 0\r
\r
-extern "C"\r
-{\r
+extern "C" {\r
void StartTimer();\r
void StopAndResetTimer();\r
void PauseTimer();\r
#ifdef SUPPORT_CYWEE_SERVICE\r
#include "cywee_service.h"\r
#endif\r
-namespace OHOS\r
-{\r
+namespace OHOS {\r
\r
REGIST_SLICE(VIEW_TRAININGCRAD, TraingCardView, TraingCardPresenter);\r
\r
\r
TraingCardPresenter::TraingCardPresenter() { g_pTraingCardPresenter = this; }\r
\r
-TraingCardPresenter::~TraingCardPresenter()\r
-{\r
+TraingCardPresenter::~TraingCardPresenter() {\r
// tag_ = 0;\r
g_pTraingCardPresenter = nullptr;\r
}\r
\r
-TraingCardPresenter *TraingCardPresenter::GetInstance()\r
-{\r
+TraingCardPresenter *TraingCardPresenter::GetInstance() {\r
static TraingCardPresenter instance;\r
return &instance;\r
}\r
\r
-void TraingCardPresenter::OnStart()\r
-{\r
+void TraingCardPresenter::OnStart() {\r
Presenter<TraingCardView>::OnStart();\r
// ScreenAlwaysOnCtr(OPEN_SCREEN_ALWAYS_ON); // 长亮屏\r
}\r
\r
-void TraingCardPresenter::OnPause(void)\r
-{\r
+void TraingCardPresenter::OnPause(void) {\r
+\r
Presenter<TraingCardView>::OnPause();\r
}\r
\r
-void TraingCardPresenter::OnResume(void)\r
-{\r
+void TraingCardPresenter::OnResume(void) {\r
\r
Presenter<TraingCardView>::OnResume();\r
}\r
\r
-void TraingCardPresenter::OnStop(void)\r
-{\r
+void TraingCardPresenter::OnStop(void) {\r
\r
Presenter<TraingCardView>::OnStop();\r
// ScreenAlwaysOnCtr(CANCEL_SCREEN_ALWAYS_ON); // 长亮屏\r
}\r
\r
-bool TraingCardPresenter::OnDrag(UIView &view, const DragEvent &event)\r
-{\r
+bool TraingCardPresenter::OnDrag(UIView &view, const DragEvent &event) {\r
UNUSED(view);\r
return true;\r
}\r
\r
// 计时器更新timer\r
-void TraingCardPresenter::ModifiesTime()\r
-{\r
+void TraingCardPresenter::ModifiesTime() {\r
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ModifiesTime begin");\r
std ::string TimeStr = TraingModel::GetInstance()->DisplayMotionDuration();\r
\r
/*灭屏状态不刷新UI*/\r
- if (uapi_get_screen_state() == 1)\r
- {\r
+ if (uapi_get_screen_state() == 1) {\r
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "uapi_get_screen_state is off");\r
return;\r
}\r
\r
- if (TimeStr.empty())\r
- {\r
+ if (TimeStr.empty()) {\r
WEARABLE_LOGE(\r
WEARABLE_LOG_MODULE_APP,\r
"TraingModel::GetInstance()->DisplayMotionDuration() nullptr");\r
return;\r
}\r
if (TrainingControlView::GetInstance() != nullptr &&\r
- TrainingDataView::GetInstance() != nullptr)\r
- {\r
- if (TrainingControlView::GetInstance()->GetTimeLabel() != nullptr)\r
- {\r
+ TrainingDataView::GetInstance() != nullptr) {\r
+ if (TrainingControlView::GetInstance()->GetTimeLabel() != nullptr) {\r
TrainingControlView::GetInstance()->GetTimeLabel()->SetText(\r
TimeStr.c_str());\r
}\r
- if (TrainingDataView::GetInstance()->GetTimeLabel() != nullptr)\r
- {\r
+ if (TrainingDataView::GetInstance()->GetTimeLabel() != nullptr) {\r
TrainingDataView::GetInstance()->GetTimeLabel()->SetText(TimeStr.c_str());\r
}\r
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ModifiesTime end");\r
}\r
- WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "ModifiesTime end");\r
}\r
\r
-// 计时器\r
-void TraingCardPresenter::UpdateTimeOfControl()\r
-{\r
+//计时器\r
+void TraingCardPresenter::UpdateTimeOfControl() {\r
std ::string TimeStr = TraingModel::GetInstance()->DisplayMotionDuration();\r
- if (TimeStr.empty())\r
- {\r
+ if (TimeStr.empty()) {\r
WEARABLE_LOGE(\r
WEARABLE_LOG_MODULE_APP,\r
"TraingModel::GetInstance()->DisplayMotionDuration() nullptr");\r
return;\r
}\r
- if (TrainingControlView::GetInstance() != nullptr)\r
- {\r
- if (TrainingControlView::GetInstance()->GetTimeLabel() != nullptr)\r
- {\r
- TrainingControlView::GetInstance()->GetTimeLabel()->SetText(\r
- TimeStr.c_str());\r
+ if (TrainingControlView::GetInstance()->GetTimeLabel() != nullptr) {\r
+ TrainingControlView::GetInstance()->GetTimeLabel()->SetText(\r
+ TimeStr.c_str());\r
+ }\r
+#if defined(BOARD_W9) || defined(BOARD_N9)\r
+ if (TrainingControlView::GetInstance()->GetGpsStatusLabel() != nullptr) {\r
+ //获取GPS状态\r
+ bool gpsStatus = TraingModel::GetInstance()->GetGPSConnectState();\r
+ std::string gpsStatusStr;\r
+ if (gpsStatus) {\r
+ gpsStatusStr = "GPS定位成功";\r
+ TrainingControlView::GetInstance()->GetGpsStatusLabel()->SetTextColor(\r
+ Color::Green());\r
+ } else {\r
+ gpsStatusStr = "GPS未定位成功";\r
+ TrainingControlView::GetInstance()->GetGpsStatusLabel()->SetTextColor(\r
+ Color::Red());\r
}\r
+ TrainingControlView::GetInstance()->GetGpsStatusLabel()->SetText(\r
+ gpsStatusStr.c_str());\r
}\r
+#endif\r
}\r
-void TraingCardPresenter::UpdateTimeOfData()\r
-{\r
+void TraingCardPresenter::UpdateTimeOfData() {\r
std ::string TimeStr = TraingModel::GetInstance()->DisplayMotionDuration();\r
- if (TimeStr.empty())\r
- {\r
+ if (TimeStr.empty()) {\r
WEARABLE_LOGE(\r
WEARABLE_LOG_MODULE_APP,\r
"TraingModel::GetInstance()->DisplayMotionDuration() nullptr");\r
return;\r
}\r
- if (TrainingDataView::GetInstance() != nullptr)\r
- {\r
- if (TrainingDataView::GetInstance()->GetTimeLabel() != nullptr)\r
- {\r
- TrainingDataView::GetInstance()->GetTimeLabel()->SetText(TimeStr.c_str());\r
- }\r
+ if (TrainingDataView::GetInstance()->GetTimeLabel() != nullptr) {\r
+ TrainingDataView::GetInstance()->GetTimeLabel()->SetText(TimeStr.c_str());\r
}\r
}\r
\r
/* --------------------------------- cywee库操作\r
- * -------------------------------- */\r
+ * -------------------------------- */\r
/*开启cywee*/\r
-bool TraingCardPresenter::OpenSensor(ExerciseClass type)\r
-{\r
+bool TraingCardPresenter::OpenSensor(ExerciseClass type) {\r
// 防止重复开启\r
- if (opensensorstuats)\r
- {\r
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "OpenSensor is opening!!");\r
+ if (opensensorstuats) {\r
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "OpenSensor is opening!!");\r
return false;\r
}\r
// 根据不同的运动项目进行不同的运动\r
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TraingCardPresenter::OpenSensor!!");\r
#ifdef SUPPORT_CYWEE_SERVICE\r
- switch (type)\r
- {\r
+ switch (type) {\r
case ExerciseClass::NORMAL:\r
cywee_sport_mode_change(CYWEE_SERVICE_CMD_NORMAL_MODE);\r
break;\r
}\r
\r
/*控制cywee*/\r
-bool TraingCardPresenter::ControlSensor(ExerciseState state)\r
-{\r
+bool TraingCardPresenter::ControlSensor(ExerciseState state) {\r
#ifdef SUPPORT_PPG_SERVICE\r
- switch (state)\r
- {\r
+ switch (state) {\r
case ExerciseState::End:\r
// 退出进入普通模式\r
do_cywee_service_check_msg(CYWEE_SERVICE_CMD_NORMAL_MODE, NULL);\r
return true;\r
}\r
\r
-void TraingCardPresenter::RefreshData(SportData *sportdata)\r
-{\r
- if (sportdata == nullptr)\r
- {\r
+void TraingCardPresenter::RefreshData(SportData *sportdata) {\r
+ if (sportdata == nullptr) {\r
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "sportdata is null");\r
return;\r
}\r
return;\r
\r
std::string distanceStr;\r
- if (sportdata->distance != 0)\r
- {\r
+ if (sportdata->distance != 0) {\r
double distance_km =\r
sportdata->distance / 1000.0; // 转换为公里并保留两位小数\r
std::stringstream ss;\r
ss << std::fixed << std::setprecision(2) << distance_km;\r
distanceStr = ss.str();\r
- }\r
- else\r
- {\r
+ } else {\r
distanceStr = DEFAULT_DASH;\r
}\r
\r
std::string stepsStr =\r
(sportdata->steps != 0) ? std::to_string(sportdata->steps) : DEFAULT_DASH;\r
std::string calorieStr = (sportdata->calories != 0)\r
- ? std::to_string(sportdata->calories)\r
- : DEFAULT_DASH;\r
+ ? std::to_string(sportdata->calories)\r
+ : DEFAULT_DASH;\r
// 训练强度刷新\r
UIResourceTextId intensityid = TraingModel::GetInstance()->GetIntensity();\r
// printf("intensityid %d \r\n", intensityid);\r
}\r
#if 1\r
/*刷新 time 和 data */\r
-void TraingCardPresenter::updataview(void)\r
-{\r
+void TraingCardPresenter::updataview(void) {\r
// 如果未开启训练,直接退出\r
- if (TraingModel::GetInstance()->GetSportState() == ExerciseState::General)\r
- {\r
- WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "TraingModel::GetInstance()->GetSportState() == ExerciseState::General ");\r
+ if (TraingModel::GetInstance()->GetSportState() == ExerciseState::General) {\r
+ WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,\r
+ "TraingModel::GetInstance()->GetSportState() == "\r
+ "ExerciseState::General ");\r
return;\r
}\r
\r
- if (TraingCardView::GetInstance() == nullptr)\r
- {\r
+ if (TraingCardView::GetInstance() == nullptr) {\r
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "view_ == nullptr");\r
return;\r
}\r
\r
- Control_Card cardId = static_cast<Control_Card>(TraingCardView::GetInstance()->GetCurrentPage());\r
+ Control_Card cardId = static_cast<Control_Card>(\r
+ TraingCardView::GetInstance()->GetCurrentPage());\r
WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "cardId %d", (int)cardId);\r
\r
- switch (cardId)\r
- {\r
- case Control_Card::CONTROL:\r
- {\r
+ switch (cardId) {\r
+ case Control_Card::CONTROL: {\r
UpdateTimeOfControl(); // 更新控制界面的时间\r
break;\r
}\r
- case Control_Card::DATA:\r
- {\r
+ case Control_Card::DATA: {\r
UpdateDataView(); // 更新训练数据视图\r
break;\r
}\r
}\r
}\r
#else\r
-void TraingCardPresenter::Notify(void)\r
-{\r
+void TraingCardPresenter::Notify(void) {\r
// 如果未开启训练,直接退出\r
if (TraingModel::GetInstance()->GetSportState() == ExerciseState::General ||\r
- view_ == nullptr)\r
- {\r
+ view_ == nullptr) {\r
return;\r
}\r
\r
Control_Card cardId = static_cast<Control_Card>(view_->GetCurrentPage());\r
\r
- switch (cardId)\r
- {\r
- case Control_Card::CONTROL:\r
- {\r
+ switch (cardId) {\r
+ case Control_Card::CONTROL: {\r
UpdateTimeOfControl(); // 更新控制界面的时间\r
break;\r
}\r
- case Control_Card::DATA:\r
- {\r
+ case Control_Card::DATA: {\r
UpdateDataView(); // 更新训练数据视图\r
break;\r
}\r
}\r
}\r
#endif\r
-void TraingCardPresenter::UpdateDataView()\r
-{\r
+void TraingCardPresenter::UpdateDataView() {\r
// 刷新计时\r
UpdateTimeOfData();\r
\r
RefreshData(sportdata);\r
}\r
/* --------------------------------- 点击事件处理\r
- * --------------------------------- */\r
-bool TraingCardPresenter::OnClick(UIView &view, const ClickEvent &event)\r
-{\r
+ * --------------------------------- */\r
+bool TraingCardPresenter::OnClick(UIView &view, const ClickEvent &event) {\r
const std::string viewId = view.GetViewId();\r
\r
// 定义点击事件映射\r
actionMap = {\r
// 暂停按键\r
{BUTTONSTOP,\r
- [&]()\r
- {\r
- auto currentState = TraingModel::GetInstance()->GetSportState();\r
- if (currentState == ExerciseState::Continue ||\r
- currentState == ExerciseState::Start)\r
- {\r
- handlePauseState();\r
- }\r
- else if (currentState == ExerciseState::Pause)\r
- {\r
- handleContinueState();\r
- }\r
- }},\r
+ [&]() {\r
+ auto currentState = TraingModel::GetInstance()->GetSportState();\r
+ if (currentState == ExerciseState::Continue ||\r
+ currentState == ExerciseState::Start) {\r
+ handlePauseState();\r
+ } else if (currentState == ExerciseState::Pause) {\r
+ handleContinueState();\r
+ }\r
+ }},\r
// 结束按键\r
{BUTTONEND,\r
- [&]()\r
- {\r
- // ControlSensor(ExerciseState::Pause);\r
- TraingCardView::GetInstance()->SetPopupViewStatus(EndPopup, true);\r
- TraingCardView::GetInstance()->RefreshEndView();\r
- }},\r
+ [&]() {\r
+ // ControlSensor(ExerciseState::Pause);\r
+ TraingCardView::GetInstance()->SetPopupViewStatus(EndPopup, true);\r
+ TraingCardView::GetInstance()->RefreshEndView();\r
+ }},\r
// 确定结束\r
- {BUTTONEXIT, [&]()\r
- { handleEndState(); }},\r
+ {BUTTONEXIT, [&]() { handleEndState(); }},\r
// 返回\r
{BUTTONBACK,\r
- [&]()\r
- {\r
- // ControlSensor(ExerciseState::Continue);\r
- TraingCardView::GetInstance()->SetPopupViewStatus(EndPopup, false);\r
- }},\r
+ [&]() {\r
+ // ControlSensor(ExerciseState::Continue);\r
+ TraingCardView::GetInstance()->SetPopupViewStatus(EndPopup, false);\r
+ }},\r
// 确定达到目标\r
{BUTTONWINOK,\r
- [&]()\r
- {\r
- TraingCardView::GetInstance()->GetAnimator()->Stop();\r
- TraingCardView::GetInstance()->SetPopupViewStatus(GetTagPopup,\r
- false);\r
- }},\r
+ [&]() {\r
+ TraingCardView::GetInstance()->GetAnimator()->Stop();\r
+ TraingCardView::GetInstance()->SetPopupViewStatus(GetTagPopup,\r
+ false);\r
+ }},\r
// 确定心率提醒\r
{BUTTONNOTE,\r
- [&]()\r
- {\r
- TraingCardView::GetInstance()->SetPopupViewStatus(HeartPopup,\r
- false);\r
- }},\r
+ [&]() {\r
+ TraingCardView::GetInstance()->SetPopupViewStatus(HeartPopup,\r
+ false);\r
+ }},\r
};\r
\r
// 查找并执行对应的操作\r
auto it = actionMap.find(viewId);\r
- if (it != actionMap.end())\r
- {\r
+ if (it != actionMap.end()) {\r
it->second(); // 执行操作\r
return true;\r
}\r
\r
return false; // 没有匹配的操作\r
}\r
-// void TraingCardPresenter::OnSwipe(UISwipeView &view) {\r
-// WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "TempPresenter::OnSwipe ");\r
-// UNUSED(view);\r
-// return;\r
-// }\r
-bool TraingCardPresenter::OnKeyAct(UIView &view, const KeyEvent &event)\r
-{\r
+void TraingCardPresenter::OnSwipe(UISwipeView &view) {\r
+ WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "TempPresenter::OnSwipe ");\r
+ UNUSED(view);\r
+ return;\r
+}\r
+bool TraingCardPresenter::OnKeyAct(UIView &view, const KeyEvent &event) {\r
/*消耗按键事件*/\r
return true;\r
}\r
-\r
+/* -------------------------------- 协议发起运动处理\r
+ * -------------------------------- */\r
#ifdef BLE_SERVER_ENABLE\r
-bool TraingCardPresenter::send_sport_cmd(training_ble *cmd)\r
-{\r
- if (cmd == nullptr)\r
- {\r
+bool TraingCardPresenter::send_sport_cmd(training_ble *cmd) {\r
+ if (cmd == nullptr) {\r
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "无效的运动命令 cmd 为空!");\r
return false;\r
}\r
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "cmdstate:%d currentState:%d",\r
cmdstate, currentState);\r
int m_sportTag = 0;\r
- switch (cmd->controlmd)\r
- {\r
+ switch (cmd->controlmd) {\r
case SETGOAL:\r
// 仅在一般状态下允许设置目标\r
- if (currentState == ExerciseState::General)\r
- {\r
- if (cmd->sportTagType == 0)\r
- {\r
- // 训练目标->m 需要转换 km\r
+ if (currentState == ExerciseState::General) {\r
+ if (cmd->sportTagType == 0) {\r
+ //训练目标->m 需要转换 km\r
m_sportTag = cmd->sportTag / 1000;\r
- }\r
- else\r
- {\r
+ } else {\r
m_sportTag = cmd->sportTag;\r
}\r
TraingModel::GetInstance()->SetSportTag(\r
}\r
\r
// 仅在一般状态下允许开始运动\r
- if (cmdstate == ExerciseState::Start)\r
- {\r
- if ((currentState == ExerciseState::General))\r
- {\r
- if (TraingCardView::GetInstance() == nullptr)\r
- {\r
+ if (cmdstate == ExerciseState::Start) {\r
+ if ((currentState == ExerciseState::General)) {\r
+ if (TraingCardView::GetInstance() == nullptr) {\r
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 开启运动");\r
uapi_set_screen_on_api(); // 亮屏\r
NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGCRAD);\r
// 设置目标类型和项目有且只有一次\r
- switch (cmd->sportType)\r
- {\r
+ switch (cmd->sportType) {\r
case 0:\r
TraingModel::GetInstance()->SetSportEvent(\r
STR_EXERCISE_OUTDOORWALKING);\r
reply_APP_OpenSportModeCmd(true);\r
handleStartState();\r
return true;\r
- }\r
- else\r
- {\r
+ } else {\r
\r
reply_APP_OpenSportModeCmd(false);\r
// return false;\r
}\r
}\r
// 仅在运动状态为开始或继续时允许暂停\r
- if (cmdstate == ExerciseState::Pause)\r
- {\r
+ if (cmdstate == ExerciseState::Pause) {\r
if (currentState == ExerciseState::Start ||\r
- currentState == ExerciseState::Continue)\r
- {\r
+ currentState == ExerciseState::Continue) {\r
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 暂停运动");\r
reply_APP_SportPasueResetCmd(TraingModel::GetInstance()->GetSportTime());\r
handlePauseState();\r
\r
// 仅在暂停状态下允许恢复运动\r
if (cmdstate == ExerciseState::Continue &&\r
- currentState == ExerciseState::Pause)\r
- {\r
+ currentState == ExerciseState::Pause) {\r
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 恢复运动");\r
reply_APP_SportPasueResetCmd(TraingModel::GetInstance()->GetSportTime());\r
handleContinueState();\r
// 仅在运动状态为开始、继续或暂停时允许结束运动\r
if (cmdstate == ExerciseState::End &&\r
(currentState == ExerciseState::Start ||\r
- currentState == ExerciseState::Continue ||\r
- currentState == ExerciseState::Pause))\r
- {\r
+ currentState == ExerciseState::Continue ||\r
+ currentState == ExerciseState::Pause)) {\r
WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "ble 结束运动");\r
uint32_t sport_time = TraingModel::GetInstance()->GetSportTime();\r
reply_APP_SportStopModeCmd(sport_time);\r
return false; // 没有执行任何命令\r
}\r
#endif\r
-\r
/*训练结束*/\r
-void TraingCardPresenter::handleEndState()\r
-{\r
+void TraingCardPresenter::handleEndState() {\r
TraingModel::GetInstance()->SetSportState(ExerciseState::End);\r
ControlSensor(ExerciseState::End);\r
uapi_set_screen_on_api(); // 亮屏\r
return;\r
#endif\r
// 是否达到训练目标\r
- if (TraingModel::GetInstance()->GetMomentum())\r
- {\r
+ if (TraingModel::GetInstance()->GetMomentum()) {\r
// 进入训练详情\r
NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGENDDATA);\r
NativeAbility::GetInstance().ClearSliceIdStack(); // 清空所有SLiceId栈元素\r
- }\r
- else\r
- {\r
+ } else {\r
// 进入训练记录\r
// NativeAbility::GetInstance().ChangeSlice(VIEW_TRAINING_RECORD);\r
if (TraingModel::GetInstance()->GetEntryType() == APPLIST_ENTER)\r
NativeAbility::GetInstance().ChangeSlice(\r
VIEW_TRAINING, TransitionType::TRANSITION_BACK_TO_HEXAGONS);\r
- else\r
- {\r
+ else {\r
NativeAbility::GetInstance().ChangeSlice(\r
VIEW_MAIN, TransitionType::TRANSITION_BACK_TO_HEXAGONS);\r
NativeAbility::GetInstance().ClearSliceIdStack(); // 清空所有SLiceId栈元素\r
}\r
\r
/*训练暂停*/\r
-void TraingCardPresenter::handlePauseState()\r
-{\r
+void TraingCardPresenter::handlePauseState() {\r
uapi_set_screen_on_api(); // 亮屏\r
TrainingControlView::GetInstance()->GetLabel()->SetTextId(STR_RESUME);\r
TrainingControlView::GetInstance()->GetButton()->SetImageSrc(\r
}\r
\r
/*训练继续*/\r
-void TraingCardPresenter::handleContinueState()\r
-{\r
+void TraingCardPresenter::handleContinueState() {\r
uapi_set_screen_on_api(); // 亮屏\r
TrainingControlView::GetInstance()->GetLabel()->SetTextId(STR_PAUSE);\r
TrainingControlView::GetInstance()->GetButton()->SetImageSrc(\r
}\r
\r
/*训练开始*/\r
-void TraingCardPresenter::handleStartState()\r
-{\r
+void TraingCardPresenter::handleStartState() {\r
ControlSensor(ExerciseState::Start);\r
TraingModel::GetInstance()->SetSportOpenWay(true);\r
}\r