ui代码修改优化 3318/1 3318/head 3319/target/1
authorluozheng <1732036298@qq.com>
Mon, 30 Mar 2026 12:31:24 +0000 (20:31 +0800)
committerluozheng <1732036298@qq.com>
Mon, 30 Mar 2026 12:31:24 +0000 (20:31 +0800)
code/sdk/application/wearable/fd272/nativeapp/nativeui/applist/src/AppItemView.cpp
code/sdk/application/wearable/fd272/nativeapp/nativeui/heartrate/src/HeartRateCardView.cpp
code/sdk/application/wearable/fd272/nativeapp/nativeui/sport_countdown/src/CountdownView.cpp
code/sdk/application/wearable/fd272/nativeapp/nativeui/traing/src/TraingModel.cpp
code/sdk/zhihui/services/gnss/src/uart_gnss.c

index 87b35c5..2f11487 100644 (file)
@@ -173,6 +173,9 @@ namespace OHOS {
 
     void ArcScaleListItemView::SetItemInfo(const AppItem &itemInfo)
     {
+        if (image_ == nullptr || label_ == nullptr) {
+            return ;
+        }
         itemInfo_ = itemInfo;
         image_->SetSrc(itemInfo.iconSmall);
         if(VIEW_EXTERN_APP != itemInfo.id) {
index 5fc8c24..56fcd44 100644 (file)
@@ -23,7 +23,6 @@ void HeartRateCardView::ClearAll(void) {
     RemoveAll();
     delete heartRateCardView;
     heartRateCardView = nullptr;
-    heartRateGroupView = nullptr;
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "HeartRateCardView::ClearAll viewiInitStatus end");
   }
 }
index 207c95f..fb5e818 100644 (file)
@@ -202,6 +202,11 @@ CountdownView::CountdownView() {
 
 CountdownView::~CountdownView() {
   ScreenAlwaysOnCtr(CANCEL_SCREEN_ALWAYS_ON);  // 退出运动倒计时取消常亮屏
+  if (group_) {
+    group_->RemoveAll();
+    delete group_;
+    group_ = nullptr;
+  }
   if (countdownAnimator_) {
     countdownAnimator_->Stop();
     delete countdownAnimator_;
@@ -219,10 +224,6 @@ CountdownView::~CountdownView() {
     delete label_;
     label_ = nullptr;
   }
-  if (group_) {
-    delete group_;
-    group_ = nullptr;
-  }
   g_pCountdownView = nullptr;
 }
 
@@ -275,8 +276,11 @@ void CountdownView::StopCanvas() {
 }
 
 void CountdownView::OnStart() {
-  group_ = new UIViewGroup();
-  if (!group_) return;
+  if (group_ == nullptr) 
+  {
+    group_ = new UIViewGroup();
+    if (group_ == nullptr) return;
+  }
   group_->SetPosition(0, 0);
   group_->SetWidth(Screen::GetInstance().GetWidth());
   group_->SetHeight(Screen::GetInstance().GetHeight());
index 92b917c..6958117 100644 (file)
@@ -80,7 +80,6 @@
  #include "NativeAbility.h"
  #include "AppViewIDs.h"
  #include "sport_countdown/CountdownPresenter.h"
-#include "heartrate/include/HeartModel.h"
  
  #if defined(SUPPORT_GNSS)
  #include "NMEA0183.h"
  // 计算平均心率的函数
  double CalculateAverageHeartRate(const std::vector<int> &heartRates) {
    if (heartRates.size() <= 2) {
-     // 如果心率数据点少于等于2,无法去掉最高和最低值
+     // 如果心率数据点少于等于2,无法去掉最高和最低值
      return 0.0; // 或者返回心率的平均值
    }
  
@@ -561,7 +560,7 @@ typedef struct
                   "CalculatePace: distance=%d米, duration=%d秒, pace=%d秒/公里",
                   sportdata->distance, sportdata->duration, calculatedAvgPace);
   } else if (cyweeInfo->avg_pace != 0) {
-    // 如果距离或时间为0,使用CYWEE提供的平均配速作为后备
+    // 如果距离或时间为0,使用CYWEE提供的平均配速作为后备
     sportdata->pc = cyweeInfo->pace;
   }
 #else
@@ -569,7 +568,7 @@ typedef struct
 #endif
 
 
-  // 心率提醒 目前取值有问题,暂时关闭
+  // 心率提醒 目前取值有问题,暂时关闭
   if (sportdata->heart >= user_maxHeart && user_maxHeart != 0) {
     uapi_set_screen_on_api(); // 亮屏检测
     if (true != OHOS::ConfigModel::GetInstance()->GetDnotdisturb() && (!uapi_sleep_mode_flag_get())) {
@@ -581,7 +580,7 @@ typedef struct
           restshock_count++;
           shock_count = 0;
           if (restshock_count >= 5) {
-            restshock_count = 0; /* 休息满 5 次,下一轮可再震 */
+            restshock_count = 0; /* 休息满 5 次,下一轮可再震 */
           }
         }
     }
@@ -636,12 +635,12 @@ typedef struct
                   "CalculateAvgPace: distance=%d米, duration=%d秒, avgPace=%d秒/公里",
                   sportdata->distance, sportdata->duration, calculatedAvgPace);
   } else if (cyweeInfo->avg_pace != 0) {
-    // 如果距离或时间为0,使用CYWEE提供的平均配速作为后备
+    // 如果距离或时间为0,使用CYWEE提供的平均配速作为后备
     sportdata->apc = cyweeInfo->avg_pace;
   }
   
-  // 最快配速:配速越小表示速度越快,所以最快配速应该是最小的配速值
-  // 如果当前配速不为0且小于之前的最快配速(或最快配速为0),则更新最快配速
+  // 最快配速:配速越小表示速度越快,所以最快配速应该是最小的配速值
+  // 如果当前配速不为0且小于之前的最快配速(或最快配速为0),则更新最快配速
   if (sportdata->pc > 0) {
     if (sportdata->fpc == 0 || sportdata->pc < sportdata->fpc) {
       sportdata->fpc = sportdata->pc;
@@ -649,7 +648,7 @@ typedef struct
                     "UpdateFastestPace: fpc=%d秒/公里", sportdata->fpc);
     }
   } else if (cyweeInfo->max_pace != 0) {
-    // 如果当前配速为0,使用CYWEE提供的最快配速作为后备
+    // 如果当前配速为0,使用CYWEE提供的最快配速作为后备
     sportdata->fpc = cyweeInfo->max_pace;
   }
    //平均步频
@@ -717,7 +716,7 @@ void TraingModel::SetSportdata(SportData *data) {
     sportdata->starttime = data->starttime; //更新开始时间
     printf_data(data);                      //打印该运动数据
     IsSpecialSport(sportdata->sportId);     // 配置训练特殊性
-    // 更新sportId_,确保IsNeedGPS()能正确判断是否需要GPS
+    // 更新sportId_,确保IsNeedGPS()能正确判断是否需要GPS
     sportId_ = data->sportId;
     sportType_ = data->sportType;
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SetSportdata: 更新sportId_=%d, sportType_=%d", sportId_, (int)sportType_);
@@ -971,7 +970,7 @@ void TraingModel::SetSportdata(SportData *data) {
      WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "SaveSportData error!");
    }
  #else
-   //运动数据达标,存储运动数据
+   //运动数据达标,存储运动数据
    if (MomentumFlag) {
      // GetGoaldegree(); // 设置目标完成度
      if (SaveSportData()) {
@@ -1025,7 +1024,7 @@ void TraingModel::SetSportdata(SportData *data) {
    maxHeap.push(newHeartRate);
    minHeap.push(newHeartRate);
  
-   // 如果超过窗口大小,移除最早的心率
+   // 如果超过窗口大小,移除最早的心率
    if (heartRateData.size() > windowSize) {
      int oldestRate = heartRateData[0];
      totalHeartRate -= oldestRate;
@@ -1053,7 +1052,7 @@ void TraingModel::SetSportdata(SportData *data) {
                  "新心率: %d, 当前心率总和: %d, 心率计数: %d", newHeartRate,
                  totalHeartRate, countHeartRate);
  
-   // 如果心率数量少于 3,直接返回当前的平均值
+   // 如果心率数量少于 3,直接返回当前的平均值
    if (countHeartRate < 3) {
      return totalHeartRate / countHeartRate; // 无法去除最大最小值
    }
@@ -1077,7 +1076,7 @@ void TraingModel::SetSportdata(SportData *data) {
    // 将新的心率添加到最近心率列表
    recentHeartRates.push_back(newHeartRate);
  
-   // 如果超过窗口大小,移除最早的心率
+   // 如果超过窗口大小,移除最早的心率
    if (recentHeartRates.size() > windowSize) {
      recentHeartRates.erase(recentHeartRates.begin());
    }
@@ -1305,7 +1304,7 @@ void TraingModel::SetSportdata(SportData *data) {
  int TraingModel::GetGoaldegree() {
    if (sportdata->tag == 0) {
      WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "sportdata->tag is 0");
-     return 0; // 如果目标值为 0,返回 0%
+     return 0; // 如果目标值为 0,返回 0%
    }
  
    int goaldegree = GetSportTagData();
@@ -1433,27 +1432,27 @@ void TraingModel::SetSportdata(SportData *data) {
  }
  
  /*
-   uint32_t src; // 训练来源:0 - 手表开启的训练,1 - APP开启的训练
-   uint32_t bt;  // 运动类型:0 - 健身类,1 - 舞蹈类,2 - 休闲运动,3 - 球类,4 - 水上项目,5 - 冰上项目,6 - 极限运动,7 - 其他 
+   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 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 ti;  // 训练强度:0 - 较低强度,1 - 中等强度,2 - 高等强度,3 - 超高强度 
    uint32_t cir; // 圈数 
    uint32_t alt; // 最高海拔高度
  */
 /*保存数据*/
  bool TraingModel::SaveSportData() {
 
-   // 骑行等需要GPS的运动:CYWEE无距离时,在结束时用GPS轨迹一次性计算,不实时计
+   // 骑行等需要GPS的运动:CYWEE无距离时,在结束时用GPS轨迹一次性计算,不实时计
    if (sportdata->distance == 0 && IsNeedGPS(sportId_) && gpsData.size() >= 2) 
    {
       uint32_t gpsDist = CalculateDistanceFromGPSTrack();
@@ -1551,7 +1550,7 @@ void TraingModel::SetSportdata(SportData *data) {
      }
    }
  
-   // 如果所有操作成功,则恢复训练存储状态
+   // 如果所有操作成功,则恢复训练存储状态
    uapi_dev_runtime_set_train_storage_status(0);
    return true;
  }
@@ -1589,8 +1588,8 @@ bool TraingModel::GetMomentum() {
     MomentumFlag = false;
   }
   
-  // 如果是需要GPS定位的运动,需要判断移动距离是否大于100m,不然轨迹可能异常
-  // 但是目前轨迹没有距离,先不判断
+  // 如果是需要GPS定位的运动,需要判断移动距离是否大于100m,不然轨迹可能异常
+  // 但是目前轨迹没有距离,先不判断
   if ((IsNeedGPS(sportdata->sportId))&&(sportdata->sportId!=UIResourceTextId::STR_EXERCISE_OUTDOORBIKING)) {
     if (sportdata->distance < 100) {
       MomentumFlag = false;
@@ -1691,7 +1690,7 @@ bool TraingModel::GetMomentum() {
  void TraingModel::PrintHeartRateTable() {
    size_t heartRateCount = heartRateTable.size();
    if (heartRateCount == 0) {
-     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "心率表为空,无法打印。");
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "心率表为空,无法打印。");
      return;
    }
  
@@ -1705,7 +1704,7 @@ bool TraingModel::GetMomentum() {
  void TraingModel::PrintPaceTable() {
    size_t paceCount = paceTable.size();
    if (paceCount == 0) {
-     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "配速表为空,无法打印。");
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "配速表为空,无法打印。");
      return;
    }
  
@@ -1725,7 +1724,7 @@ bool TraingModel::GetMomentum() {
  
    size_t heartRateCount = heartRateTable.size();
    if (heartRateCount == 0) {
-     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "心率表为空,无法保存。");
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "心率表为空,无法保存。");
      return false;
    }
  
@@ -1764,7 +1763,7 @@ bool TraingModel::GetMomentum() {
  
    size_t paceCount = paceTable.size();
    if (paceCount == 0) {
-     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "配速表为空,无法保存。");
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "配速表为空,无法保存。");
      return false;
    }
  
@@ -1802,7 +1801,7 @@ bool TraingModel::GetMomentum() {
      return exactFilePath; // 返回完全匹配的文件路径
    }
  
-   // 如果未找到,检查时间戳相差5秒的文件
+   // 如果未找到,检查时间戳相差5秒的文件
    for (int i = -5; i <= 5; ++i) {
      uint32_t checkTimestamp = timestamp + i;
      std::string filePath = TRAINING_HEART_FILE + std::to_string(checkTimestamp) + ".bin";
@@ -1827,7 +1826,7 @@ bool TraingModel::GetMomentum() {
      return exactFilePath; // 返回完全匹配的文件路径
    }
  
-   // 如果未找到,检查时间戳相差5秒的文件
+   // 如果未找到,检查时间戳相差5秒的文件
    for (int i = -5; i <= 5; ++i) {
      uint32_t checkTimestamp = timestamp + i;
      std::string filePath =
@@ -1868,10 +1867,10 @@ bool TraingModel::GetMomentum() {
      WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "id is error");
      return;
    }
-    // 进入运动倒计时 3 秒,结束后跳转运动卡片
+    // 进入运动倒计时 3 秒,结束后跳转运动卡片
    CountdownPresenter::SetReturnSlice(VIEW_TRAININGCRAD);
    NativeAbility::GetInstance().EnterSlice(VIEW_SPORT_COUNTDOWN);
-   // 配置运动项(先完成配置,再进 3 秒倒计时,倒计时结束进入运动界面)
+   // 配置运动项(先完成配置,再进 3 秒倒计时,倒计时结束进入运动界面)
    SetSportEvent(id);
    // 获取运动项目
    ExerciseClass type = GetSportId();
@@ -1879,20 +1878,20 @@ bool TraingModel::GetMomentum() {
    TraingCardPresenter::GetInstance()->OpenSensor(type);
 
    TraingCardPresenter::GetInstance()->ControlSensor(ExerciseState::Start);
-   TraingModel::GetInstance()->SetSportOpenWay(false);  // 运动识别由手表开启,非用户点击
+   TraingModel::GetInstance()->SetSportOpenWay(false);  // 运动识别由手表开启,非用户点击
    //开启心率
    TraingModel::GetInstance()->start_training_heart();
    // 仅需要GPS的运动才开启GPS定时器(与TraingNomalPresenter::OnStart一致)
    if (TraingModel::GetInstance()->IsNeedGPS(TraingModel::GetInstance()->GetSportEvent())) {
-    #if defined(SUPPORT_GNSS)
-         WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "该运动需要GPS,开启gps定时器,频率:2秒/点");
-         TraingModel::GetInstance()->gnss_start_timer(2 * 1000); // 2秒一个GPS点              
-    #endif
+ #if defined(SUPPORT_GNSS)
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "该运动需要GPS,开启gps定时器,频率:2秒/点");
+     TraingModel::GetInstance()->gnss_start_timer(2 * 1000); // 2秒一个GPS点
+ #endif
    } else {
-     WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "该运动不需要GPS,不开启GPS定时器");
+     WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "该运动不需要GPS,不开启GPS定时器");
    }
    // 快捷训练都属于健身类
-   // 运动识别,跑步,走路,骑行,划船机,椭圆机,都属于健身类
+   // 运动识别,跑步,走路,骑行,划船机,椭圆机,都属于健身类
    SetSportType(Exercisetype::FITNESS);
  }
  
@@ -1936,7 +1935,7 @@ bool TraingModel::GetMomentum() {
    // }
    // lastCallTime = currentTime; // 更新最后调用时间
  
-   // 如果 g_motionTimes 不为零,进行计算和数据存储
+   // 如果 g_motionTimes 不为零,进行计算和数据存储
    if (g_motionTimes > 0) {
      sportdata->duration = g_motionTimes; // 设置训练时长
  
@@ -2186,11 +2185,11 @@ bool TraingModel::GetMomentum() {
  // 获得数据个数
  static int get_Trainingdata_data_num(FILE *fp) {
    if (fp == NULL) {
-     return 0; // 如果文件指针为空,返回0
+     return 0; // 如果文件指针为空,返回0
    }
  
    fseek(fp, 0, SEEK_END); // 将文件指针移动到文件末尾
-   long size = ftell(fp);  // 获取当前文件指针位置,即文件大小
+   long size = ftell(fp);  // 获取当前文件指针位置,即文件大小
    fseek(fp, 0, SEEK_SET); // 将文件指针移回文件开头
  
    return (size / sizeof(Trainingdata));
@@ -2203,7 +2202,7 @@ bool TraingModel::GetMomentum() {
    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) {
@@ -2399,7 +2398,7 @@ bool TraingModel::GetMomentum() {
  /* 加载训练数据函数 */
  /* 加载训练数据函数 */
  bool TraingModel::LoadDataFormJson(std::vector<SportData> &sportDataVector) {
-   // 清空输出向量,避免旧数据干扰
+   // 清空输出向量,避免旧数据干扰
    sportDataVector.clear();
  
    // 打开JSON文件
@@ -2457,7 +2456,7 @@ bool TraingModel::GetMomentum() {
      if (item == NULL) {
        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
                      "Null item at index %d in 'data' array", i);
-       continue; // 跳过无效项,继续处理其他项
+       continue; // 跳过无效项,继续处理其他项
      }
  
      SportData sportData;
@@ -2668,7 +2667,7 @@ bool TraingModel::GetMomentum() {
          WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Parsed starttime: %d",
                        sportData.starttime);
        } else {
-         // 打印详细信息,帮助定位问题
+         // 打印详细信息,帮助定位问题
          const char *valueStr =
              starttimeItem->valuestring ? starttimeItem->valuestring : "null";
          WEARABLE_LOGE(
@@ -2712,11 +2711,11 @@ bool TraingModel::GetMomentum() {
   Latitude(纬度):范围:-90° 至 +90°(或南纬 90° 至北纬 90°);
   Longitude(经度):范围:-180° 至 +180°(或西经 180° 至东经 180°);
 
-  -画布数据集使用所有GPS定位坐标,不进行点位限制,确保绘制完整;
-  -通过映射关系,将经纬度转换为400*400画布上的坐标;
-  -但是不能靠近400,以免显示不全等问题,350*350;
-  -如果处理负数,需要将其转换为正数,然后再进行映射?
-  -如果有正数又有负数,应该怎么处理?
+  -画布数据集使用所有GPS定位坐标,不进行点位限制,确保绘制完整;
+  -通过映射关系,将经纬度转换为400*400画布上的坐标;
+  -但是不能靠近400,以免显示不全等问题,350*350;
+  -如果处理负数,需要将其转换为正数,然后再进行映射?
+  -如果有正数又有负数,应该怎么处理?
  */
  
  static double getmin(double a, double b) { return a < b ? a : b; }
@@ -2731,7 +2730,7 @@ bool TraingModel::GetMomentum() {
    // 线段长度的平方
    double len2 = dx * dx + dy * dy;
  
-   // 如果线段退化为一个点,直接计算点到点的距离
+   // 如果线段退化为一个点,直接计算点到点的距离
    if (len2 == 0) {
      return std::hypot(p.x - p1.x, p.y - p1.y);
    }
@@ -2740,16 +2739,16 @@ bool TraingModel::GetMomentum() {
    double t = ((p.x - p1.x) * dx + (p.y - p1.y) * dy) / len2;
  
    if (t < 0) {
-     // 投影在p1之前,返回p到p1的距离
+     // 投影在p1之前,返回p到p1的距离
      return std::hypot(p.x - p1.x, p.y - p1.y);
    }
  
    if (t > 1) {
-     // 投影在p2之后,返回p到p2的距离
+     // 投影在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);
@@ -2759,7 +2758,7 @@ bool TraingModel::GetMomentum() {
  static void douglasPeucker(const std::vector<Point> &points, double epsilon,
                             std::vector<Point> &result) {
    if (points.size() <= 2) {
-     // 点数量不足,直接返回
+     // 点数量不足,直接返回
      result = points;
      return;
    }
@@ -2777,7 +2776,7 @@ bool TraingModel::GetMomentum() {
      }
    }
  
-   // 如果最大距离大于阈值,则递归处理
+   // 如果最大距离大于阈值,则递归处理
    if (dmax > epsilon) {
      std::vector<Point> recResults1, recResults2;
  
@@ -2799,11 +2798,11 @@ bool TraingModel::GetMomentum() {
    }
  }
  
-// 手表轨迹画布最大点数,控制内存与绘制性能
+// 手表轨迹画布最大点数,控制内存与绘制性能
 static const size_t MAX_CANVAS_POINTS = 300;
 
 /**
- * @brief 将画布点集均匀采样至最多 maxPoints 个点(保留首尾,中间等间隔取点)
+ * @brief 将画布点集均匀采样至最多 maxPoints 个点(保留首尾,中间等间隔取点)
  */
 static void uniformSampleToMax(std::vector<Point> &points, size_t maxPoints) {
   if (points.size() <= maxPoints) return;
@@ -2818,17 +2817,17 @@ static void uniformSampleToMax(std::vector<Point> &points, size_t maxPoints) {
 }
 
 /**
- * @brief GPS点位映射到画布坐标,并限制画布点数≤300以简化手表显示
+ * @brief GPS点位映射到画布坐标,并限制画布点数≤300以简化手表显示
  *
  * 1. 计算边界框并映射所有GPS点到画布
- * 2. 若点数>300则均匀采样至300点,保证轨迹形状不失真
+ * 2. 若点数>300则均匀采样至300点,保证轨迹形状不失真
  */
 void TraingModel::GetPointData() {
   WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
-                "开始GPS到画布映射,GPS点数: %zu", gpsData.size());
+                "开始GPS到画布映射,GPS点数: %zu", gpsData.size());
 
   if (gpsData.empty()) {
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS数据为空,无法映射");
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS数据为空,无法映射");
     canvasData.clear();
     return;
   }
@@ -2869,7 +2868,7 @@ void TraingModel::GetPointData() {
   if (canvasData.size() > MAX_CANVAS_POINTS) {
     uniformSampleToMax(canvasData, MAX_CANVAS_POINTS);
     WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
-                  "画布点超限,已均匀采样至 %zu 点(最多300点)", canvasData.size());
+                  "画布点超限,已均匀采样至 %zu 点(最多300点)", canvasData.size());
   }
 
   WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
@@ -2877,7 +2876,7 @@ void TraingModel::GetPointData() {
                 gpsData.size(), canvasData.size());
 }
 
-// 地球平均半径(米),用于Haversine公式
+// 地球平均半径(米),用于Haversine公式
 static const double EARTH_RADIUS_M = 6371000.0;
 static const double DEG_TO_RAD = 3.141592653589793 / 180.0;
 
@@ -2903,8 +2902,8 @@ static double haversineDistanceM(double lat1, double lon1, double lat2, double l
 
 /**
  * @brief 根据GPS轨迹点逐段累加计算总距离(米)
- * 用于骑行等CYWEE不输出距离时,用GPS点位补算
- * @return 总距离(米),点数<2时返回0
+ * 用于骑行等CYWEE不输出距离时,用GPS点位补算
+ * @return 总距离(米),点数<2时返回0
  */
 uint32_t TraingModel::CalculateDistanceFromGPSTrack(void) {
   if (gpsData.size() < 2) {
@@ -3033,7 +3032,7 @@ uint32_t TraingModel::CalculateDistanceFromGPSTrack(void) {
      return exactFilePath; // 返回完全匹配的文件路径
    }
  
-   // 如果未找到,检查时间戳相差1到2秒的文件
+   // 如果未找到,检查时间戳相差1到2秒的文件
    for (int i = -2; i <= 2; ++i) {
      uint32_t checkTimestamp = timestamp + i;
      std::string filePath =
@@ -3120,7 +3119,7 @@ uint32_t TraingModel::CalculateDistanceFromGPSTrack(void) {
      return exactFilePath; // 返回完全匹配的文件路径
    }
  
-   // 如果未找到,检查时间戳相差1到2秒的文件
+   // 如果未找到,检查时间戳相差1到2秒的文件
    for (int i = -2; i <= 2; ++i) {
      uint32_t checkTimestamp = timestamp + i;
      std::string filePath =
@@ -3188,46 +3187,19 @@ bool TraingModel::LoadGPSData(const std::string &filePath) {
      // SystemLogModel::GetInstance()->save_gps_status_log_type(gpsconnect);
      WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS状态变化记录完成");
    } else {
-     WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS连接状态未发生变化,保持: %d",
+     WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS连接状态未发生变化,保持: %d",
                    gpsconnect);
    }
    WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "=== GPS连接状态设置完成 ===");
  }
-// 延迟关闭 GNSS:10 秒内若训练重新开启,则取消关闭
-#if defined(SUPPORT_GNSS)
-static osTimerId_t g_delayed_gnss_stop_timer = nullptr;
-static uint32_t g_gnss_stop_request_token = 0;
-static uint32_t g_gnss_stop_execute_token = 0;
-
-static void gnss_delayed_stop_timer_callback(void *arg) {
-  unused(arg);
-  TraingModel *model = TraingModel::GetInstance();
-  if (model == nullptr) {
-    return;
-  }
-  // 仅当有新的 GPS 开启动作时取消关闭,避免非GPS运动误取消。
-  if (g_gnss_stop_execute_token != g_gnss_stop_request_token) {
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
-                  "取消本次GPS延迟关闭: execute_token=%u request_token=%u",
-                  g_gnss_stop_execute_token, g_gnss_stop_request_token);
-    return;
-  }
-  GraphicService::GetInstance()->PostGraphicEvent([]() {
-    TraingModel *m = TraingModel::GetInstance();
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "10s后异步执行gnss_stop()");
-    gnss_stop();
-    if (m != nullptr) {
-      m->SetGpsOpen(false);
-    }
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "gnss_stop()调用完成");
-  });
-}
-#endif
-
-//运动心率开启准备
+ //运动心率、gps开启准备
  void TraingModel::start_training_heart() {
-     // 心率统一通过 HeartModel 接口控制
-     HeartModel::GetInstance()->SwitchPpgHeartSensor(true);
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始启动GPS定位服务...");
+     //开启心率检测
+ #ifdef SUPPORT_PPG_SERVICE
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "启动心率检测服务");
+     ppg_hr_start();
+ #endif
      //提前设置训练状态为1
      uapi_dev_runtime_set_train_status(1);
  }
@@ -3235,37 +3207,24 @@ static void gnss_delayed_stop_timer_callback(void *arg) {
  void TraingModel::stop_heart_gps() {
    if (gpsopen == true) {
      WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始关闭GPS定位服务...");
+    // 关闭gps
 #if defined(SUPPORT_GNSS)
-    // 延迟 10 秒再真正执行 gnss_stop;若期间重新打开训练,则取消这次关闭。
-    g_gnss_stop_request_token++;
-    g_gnss_stop_execute_token = g_gnss_stop_request_token;
-
-    if (g_delayed_gnss_stop_timer == nullptr) {
-      g_delayed_gnss_stop_timer =
-          osTimerNew((osTimerFunc_t)gnss_delayed_stop_timer_callback,
-                     osTimerOnce, nullptr, nullptr);
-    }
-
-    if (g_delayed_gnss_stop_timer != nullptr) {
-      osTimerStop(g_delayed_gnss_stop_timer);
-      osStatus_t ret = osTimerStart(g_delayed_gnss_stop_timer, osMs2Tick(5 * 1000));
-      WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
-                    "已发起GPS延迟关闭(5s), token=%u, ret=%ld",
-                    g_gnss_stop_request_token, (long)ret);
-    } else {
-      // 定时器创建失败时兜底:立即关闭,避免资源悬挂
-      WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS延迟关闭定时器创建失败,立即执行gnss_stop()");
-      GraphicService::GetInstance()->PostGraphicEvent([]() {
-        gnss_stop();
-      });
-    }
-
-    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
-                  "GPS延迟关闭已安排,等待10s确认后再真正关闭");
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "调用gnss_stop()关闭GPS服务");
+    // GraphicService::GetInstance()->PostGraphicEvent([]() {
+    //   WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "异步执行gnss_stop()");
+    //   gnss_stop();
+    //   WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "gnss_stop()调用完成");
+    // });
+    gnss_stop();
+    gpsopen = false;
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定位服务关闭完成,gpsopen设置为false");
 #endif
     }
-     // 心率统一通过 HeartModel 接口控制,延迟 3 秒关闭防抖
-     HeartModel::GetInstance()->SwitchPpgHeartSensor(false);
+     //关闭心率检测
+ #ifdef SUPPORT_PPG_SERVICE
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "关闭心率检测服务");
+     ppg_hr_stop();
+ #endif
 
      //设置训练状态为0
      uapi_dev_runtime_set_train_status(0);
@@ -3282,34 +3241,33 @@ void TraingModel::SetGpsOpen(bool open) {
 /////////////////////////////////////位置服务定时器/////////////////////////////////////
  /*
   * 位置服务定时器说明:
-  * - 功能:负责获取GPS定位数据,更新GPS连接状态,添加经纬度数据
-  * - 频率:2秒间隔(GPS打点频率),平衡精度与功耗
-  *   * 2秒频率的优势:比1秒节省约50%电量,轨迹精度足够(跑步/骑行等运动)
-  *   * 参考:专业运动手表通常使用1-2秒频率,消费级手表2-5秒
+  * - 功能:负责获取GPS定位数据,更新GPS连接状态,添加经纬度数据
+  * - 频率:2秒间隔(GPS打点频率),平衡精度与功耗
+  *   * 2秒频率的优势:比1秒节省约50%电量,轨迹精度足够(跑步/骑行等运动)
+  *   * 参考:专业运动手表通常使用1-2秒频率,消费级手表2-5秒
   * - 触发条件:仅在训练进行中且训练状态非结束时执行
   */
  static osTimerId_t g_location_timer = nullptr;
- static int location_timer_interval = 2; // 位置数据获取间隔,单位秒(GPS打点频率)
+ static int location_timer_interval = 2; // 位置数据获取间隔,单位秒(GPS打点频率)
  static int gps_timer_count = 0;
  
  /*位置服务定时器到期时的处理*/
  static void gnss_timer_callbackProc(int data) {
    /*
        GPS数据获取处理
-       1、目前gps注册有问题 ;需要延迟2s 去开启gps;
+       1、目前gps注册有问题 ;需要延迟5s 去开启gps;  
    */
    gps_timer_count++;
-   if (gps_timer_count < 2) {
+   if (gps_timer_count < 5) {
      return;
    }
 
 #if defined(SUPPORT_GNSS)
    /* 延迟 2 秒后首次开启 GPS(规避 gps 注册时序问题) */
    TraingModel *model = TraingModel::GetInstance();
-   if (gps_timer_count == 2 && model != nullptr && !model->GetGpsOpen()) {
+   if (gps_timer_count == 5 && model != nullptr && !model->GetGpsOpen()) {
      WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开启gps");
      GraphicService::GetInstance()->PostGraphicEvent([]() {
-       WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "开始启动GPS定位服务...");
        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "异步执行gnss_start()");
        gnss_start();
      });
@@ -3345,7 +3303,7 @@ void TraingModel::SetGpsOpen(bool open) {
      return;
    }
  
-   // 如果定位成功,添加经纬度数据
+   // 如果定位成功,添加经纬度数据
    if (isGpsConnected) {
      uint32_t time = TraingModel::GetInstance()->get_current_time();
      TraingModel::GetInstance()->AddGPSData(time, send_gps_data.location.lat,
@@ -3358,13 +3316,13 @@ void TraingModel::SetGpsOpen(bool open) {
  
 /*
  * @brief 位置服务定时器回调函数
- * 倒计时期间定时器照常触发,但不投递到 GUI、不执行逻辑,避免与倒计时动画抢线程
+ * 倒计时期间定时器照常触发,但不投递到 GUI、不执行逻辑,避免与倒计时动画抢线程
  */
 static void gnss_timer_callback(int data) {
   WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "gnss_timer_callback %d", data);
 #if defined(SUPPORT_GNSS)
   if (NativeAbility::GetInstance().GetCurSliceId() == VIEW_SPORT_COUNTDOWN) {
-    return;  // 当前在运动倒计时 slice,只正常定时,不操作
+    return;  // 当前在运动倒计时 slice,只正常定时,不操作
   }
 #endif
   GraphicService::GetInstance()->PostGraphicEvent(
@@ -3373,30 +3331,21 @@ static void gnss_timer_callback(int data) {
  
  /*
   *  @brief 开启位置服务定时器函数
-  *  @param interval 时间间隔,单位ms
+  *  @param interval 时间间隔,单位ms
   */
  void TraingModel::gnss_start_timer(uint32_t interval) {
    WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "=== GPS定时器启动流程开始 ===");
    WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "定时器间隔: %u ms", interval);
    WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "当前定时器状态: %p",
                  g_location_timer);
-#if defined(SUPPORT_GNSS)
-  // 所有真实的 GPS 开启动作都在这里统一取消“延迟关闭”
-  g_gnss_stop_request_token++;
-  if (g_delayed_gnss_stop_timer != nullptr) {
-    osTimerStop(g_delayed_gnss_stop_timer);
-  }
-  WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "取消待执行的GPS延迟关闭请求, token=%u",
-                g_gnss_stop_request_token);
-#endif
  
-   /* -------------------------- 1.检查位置服务定时器是否已经存在,如果存在则返回
+   /* -------------------------- 1.检查位置服务定时器是否已经存在,如果存在则返回
     * ------------------------- */
    if (g_location_timer != nullptr) {
-     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: GPS定时器已存在,跳过创建");
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: GPS定时器已存在,跳过创建");
      return;
    }
-   WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS定时器不存在,开始创建...");
+   WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS定时器不存在,开始创建...");
  
    /* --------------------------------- 2.创建位置服务定时器
     * -------------------------------- */
@@ -3421,7 +3370,7 @@ static void gnss_timer_callback(int data) {
     * -------------------------------- */
    WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "启动GPS定时器...");
    osStatus_t ret = osTimerStart(g_location_timer, tick);
-   /* -------------------- 5.检查位置服务定时器启动是否成功,如果启动失败,删除定时器并置为
+   /* -------------------- 5.检查位置服务定时器启动是否成功,如果启动失败,删除定时器并置为
     * nullptr -------------------- */
    if (ret != osOK) {
      WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
@@ -3450,7 +3399,7 @@ static void gnss_timer_callback(int data) {
  
    // 检查位置服务定时器是否存在
    if (g_location_timer != nullptr) {
-     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器存在,开始停止流程...");
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "GPS定时器存在,开始停止流程...");
  
      WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "停止GPS定时器...");
      osTimerStop(g_location_timer);
@@ -3469,7 +3418,7 @@ static void gnss_timer_callback(int data) {
      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定时器不存在,跳过停止流程");
      WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
                    "=== GPS定时器停止流程结束(无需停止) ===");
    }
@@ -3479,15 +3428,15 @@ static void gnss_timer_callback(int data) {
  ///////////////////////////////////////运动数据采集定时器//////////////////////////////////
  /*
   * 运动数据采集定时器说明:
-  * - 功能:负责获取运动数据(步数、距离、卡路里、心率等),更新训练目标完成度
-  * - 频率:300ms间隔,保证运动数据的实时性和准确性
+  * - 功能:负责获取运动数据(步数、距离、卡路里、心率等),更新训练目标完成度
+  * - 频率: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; // 运动数据获取间隔,单位毫秒
+ static int sport_start_data_timer_interval = 300; // 运动数据获取间隔,单位毫秒
  
  /*
   * @brief 获取运动基础数据
@@ -3520,7 +3469,7 @@ static void gnss_timer_callback(int data) {
  
  /*
   * @brief 获取心率数据
-  * @return 获取到的心率值,如果服务不支持则返回0
+  * @return 获取到的心率值,如果服务不支持则返回0
   */
  uint8_t TraingModel::GetHeartRateData(void) {
    uint8_t hrs = 0;
@@ -3651,7 +3600,7 @@ static void gnss_timer_callback(int data) {
    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "获取到的GPS路径: %s", str.c_str());
  
    if (str.length() <= 3) {
-     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: GPS路径长度不足,长度: %zu",
+     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "错误: GPS路径长度不足,长度: %zu",
                    str.length());
      WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "=== 获取GPS轨迹路径结束(失败) ===");
      return false;
@@ -3786,7 +3735,7 @@ static void gnss_timer_callback(int data) {
                      "GPS点数据转换: 时间戳=%u, 纬度=%d, 经度=%d", temp_data,
                      temp_latitude, temp_longitude);
      } else {
-      WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS文件读取结束,读取大小: %d",
+      WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS文件读取结束,读取大小: %d",
                      read_size);
        break;
      }
@@ -3795,7 +3744,7 @@ static void gnss_timer_callback(int data) {
    fclose(fp);
    WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "GPS文件关闭");
    WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
-                 "总共读取 %d 个GPS点,输出数据长度: %d 字节", point_count, len);
+                 "总共读取 %d 个GPS点,输出数据长度: %d 字节", point_count, len);
    WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "=== 读取GPS数据结束 ===");
  
    return len;
@@ -3915,20 +3864,20 @@ static void gnss_timer_callback(int data) {
   * 1. 位置服务定时器 (g_location_timer)
   *    - 频率:1秒间隔
   *    - 功能:GPS定位、连接状态更新、轨迹数据采集
-  *    - 特点:中等频率,保证位置数据实时性
+  *    - 特点:中等频率,保证位置数据实时性
   *
   * 2. 运动数据采集定时器 (g_sport_start_data_timer)
   *    - 频率:300毫秒间隔
   *    - 功能:运动数据采集、心率监测、目标完成度检查、UI更新
-  *    - 特点:高频率,保证运动数据实时性和准确性
+  *    - 特点:高频率,保证运动数据实时性和准确性
   *
-  * 数据获取函数封装(职责单一,无耦合):
-  * - GetSportActivityData(): 获取运动基础数据,返回是否成功
-  * - GetSportExtraData(): 获取运动额外数据,返回是否成功
-  * - GetHeartRateData(): 获取心率数据,返回心率值
+  * 数据获取函数封装(职责单一,无耦合):
+  * - GetSportActivityData(): 获取运动基础数据,返回是否成功
+  * - GetSportExtraData(): 获取运动额外数据,返回是否成功
+  * - GetHeartRateData(): 获取心率数据,返回心率值
   * - 数据分析和处理逻辑在定时器回调中协调完成
   *
-  * 两个定时器独立运行,互不干扰,根据各自功能特点选择合适的执行频率
+  * 两个定时器独立运行,互不干扰,根据各自功能特点选择合适的执行频率
   */
  
  } // namespace OHOS
index 129d364..327d6df 100644 (file)
@@ -215,7 +215,10 @@ static void uart_event_task(void const *arg)
             }
         }
         if(mail.cmd == GNSS_CMD_READDATA)
+        {
             osal_kfree(mail.buffer); //只有接收的缓存需要free,发送的都是静态数组,无需申请缓存
+            mail.buffer = NULL;
+        }
     }
 }