移植gps运动相关
authorCaisking <Casiking@zhihuishixun.com>
Tue, 26 Aug 2025 10:12:35 +0000 (18:12 +0800)
committerCaisking <Casiking@zhihuishixun.com>
Tue, 26 Aug 2025 10:12:35 +0000 (18:12 +0800)
12 files changed:
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/include/TraingEnddataView.h
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/include/TraingModel.h
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/include/TraingNomalPresenter.h
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/include/TraingNomalView.h
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingEnddataView.cpp
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingModel.cpp
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingNomalPresenter.cpp
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/src/TraingNomalView.cpp
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/include/TraingCardPresenter.h
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/include/TrainingControlView.h
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/src/TraingCardPresenter.cpp
code/sdk/application/wearable/fd230/nativeapp/nativeui/traing/training_card/src/TrainingControlView.cpp

index 16fface..e987071 100644 (file)
 #include "traing/Training_UiConfig.h"
 #include "traing/include/TraingModel.h"
 #include "uicomponents/UIComponents.h"
+
+#include "components/ui_canvas.h"
+
+
 namespace OHOS {
 #define BUTTONBACK "button1"
 
+struct TrackSegment {
+  std::vector<Point> points; // 轨迹点
+  ColorType color;    // 段颜色
+  // ColorType endColor;      // 段结束颜色
+};
+// 轨迹数据:拆分成多个段(这里分2段:起→达,达→终)
+
+// 标记点(起、达、终)
+struct Marker {
+  Point pos;
+  const char *text;
+  ColorType color;
+};
+
 class TraingEnddataPresenter;
 class TraingEnddataView : public View<TraingEnddataPresenter> {
 
@@ -57,14 +75,16 @@ public:
   void InitData(void);
   void InitializeImageViews(void);
   void InitializeLabels(void);
-  void SetLabelsTextColor(const std::vector<UILabel *> &labels,
-                          OHOS::ColorType color);
-  void AddLabelsToScrollView(const std::vector<UILabel *> &labels);
   void InitializeExitControls(void);
 
   void BonusdataView();
   void ExitView();
 
+  void GenerateTrackPoints(void);
+  void DrawMarkers(UICanvas *canvas);
+  void DrawTrackSegment(UICanvas *canvas);
+  // ColorType GetGradientColor(const ColorType &start, const ColorType &end, float progress);
+
 private:
   UIView::OnDragListener *draglistener{nullptr};
   TraingEnddataView(TraingEnddataView &&) = delete;
@@ -113,7 +133,11 @@ private:
   UIImageView *imageLine11 = nullptr;
   UIImageView *imageLine12 = nullptr;
 
+   UICanvas *canvas = nullptr;
+
   bool viewiInitStatus{false};
+
+  std::vector<TrackSegment> trackSegments_;
 };
 } // namespace OHOS
 #endif // TraingEnddata_PRESENTER_H
index e0a35bc..bb848cb 100644 (file)
@@ -115,6 +115,8 @@ namespace OHOS {
 #define TRAINING_RECORD_FILE "user/userdata/sport/Record.json"
 #define TRAINING_HEART_FILE "user/userdata/sport/HeartTabel/"
 #define TRAINING_PACE_FILE "user/userdata/sport/PaceTabel/"
+#define TRAINING_CANVAS_FILE "user/userdata/sport/Canvas_Point/"
+#define TRAINING_GPS_FILE "user/userdata/sport/GPS_Point/"
 #define MAX_DATA_COUNT 10
 
 #define TIMER 1
@@ -249,6 +251,14 @@ struct SportData {
   }
 };
 
+//经纬度结构体
+typedef struct GPSData {
+  uint32_t timestamp; // 时间戳
+  double latitude;  // 纬度
+  double longitude; // 经度
+}GPSData_t;
+//画布点位结构体
+
 class TraingModel {
 public:
   TraingModel();
@@ -266,7 +276,7 @@ public:
   LoadDataFormJson(std::vector<SportData> &sportDataVector); /*加载json文件*/
   void RecordSportData();                                    /*保存数据*/
 
-// 训练数据相关配置
+  // 训练数据相关配置
 #ifdef SUPPORT_CYWEE_SERVICE
   void AnalysisData(act_result_t *cyweeInfo); /*解析传输过来的数据*/
   void AnalysisExtraData(act_ext_result_t *cyweeInfo); /*解析运动额外数据*/
@@ -362,8 +372,30 @@ public:
   void SetUserSportdata();
   void SetMaxHeart(int maxheart);
 
-  //获取数据定时器
-  void sport_data_timer(uint32_t interval = 1000);
+  //映射经纬度数据集映射画布绘制轨迹坐标集
+  void GetPointData();
+  //添加经纬度数据集
+  void add_gps_data();
+  void AddGPSData(uint32_t timestamp, double latitude, double longitude);
+  bool SaveCanvasData();
+  void ClearCanvasData();
+  bool LoadCanvasData(const std::string &filePath);
+  std::string GetCanvasDataPath(uint32_t timestamp);
+  std::vector<Point> GetCanvasData() const;
+  bool GetGPSConnectState();
+  void SetGPSConnectState(bool state);
+  void OpenGPS();
+  void CloseGPS();
+  bool SaveGPSData();
+  std::string GetGPSDataPath(uint32_t timestamp);
+  //运动心率、gps开启准备
+  void start_heart_gps();
+  //运动心率、gps关闭
+  void stop_heart_gps();
+  ///////////////////////////////////////////////
+  void Traing_StartTimer(uint32_t interval = 1 * 1000); // 1s定时器
+  void Traing_StopTimer(void);
+  void sport_data_timer(uint32_t interval = 1 * 1000);
   void sport_stop_data_timer();
 
 private:
@@ -411,6 +443,12 @@ private:
   int user_Intensity{160};
 
   uint32_t current_goal{0}; // 当前目标
+  ////////////////////GPS数据集配置/////////////////
+  double latitude{0};
+  double longitude{0};
+  std::vector<Point> canvasData; // 轨迹点集
+  std::vector<GPSData> gpsData;  // GPS数据集
+  bool gpsconnect{false};
 };
 } // namespace OHOS
 
index 3ef2ab4..b9c804b 100644 (file)
@@ -5,7 +5,7 @@
 #include "key/include/KeyInputListener.h"
 #include "Presenter.h"
 #include "TraingNomalView.h"
-
+#include "ModelListenerSample.h"
 namespace OHOS
 {
     class TraingNomalView;
@@ -13,6 +13,7 @@ namespace OHOS
     class TraingNomalPresenter : public Presenter<TraingNomalView>,
                                  public UIView::OnClickListener,
                                  public RootView::OnKeyActListener,
+                                 public ModelListenerSample, 
                                  public UIView::OnDragListener
     {
     public:
@@ -24,6 +25,7 @@ namespace OHOS
         void OnResume() override;
         void OnStop() override;
         void NavigateBack();
+        void Notify() override;
 
         static TraingNomalPresenter *GetInstance();
 
index 85b11f3..310f592 100644 (file)
@@ -57,6 +57,7 @@ namespace OHOS
         void PreLoad(void);
         void UpdateTraingTag(); // 更新训练目标
 
+        bool updateview();
     private:
         UIView::OnDragListener *draglistener{nullptr};
         TraingNomalView(TraingNomalView &&) = delete;
@@ -65,7 +66,15 @@ namespace OHOS
         UILabelExt *sporttitle_ = nullptr;
         UIButton *buttonGo_ = nullptr;
         UILabelButtonExt *buttonSet_ = nullptr;
+
+        /*心率与GPS*/
+        UIImageView *gps_image_ = nullptr;
+        UILabel *gps_label_ = nullptr;
+        UIImageView *heartrate_image_ = nullptr;
+        UILabel *heartrate_label_ = nullptr;
+
         bool viewiInitStatus{false};
+        bool updat_view_status{false};
     };
 } // namespace OHOS
 #endif // TraingNomal_PRESENTER_H
index c209ac3..3181ab8 100644 (file)
@@ -4,6 +4,8 @@
 #include "UiConfig.h"
 #include "common/image_cache_manager.h"
 #include "components/ui_button.h"
+#include "components/ui_canvas.h"
+#include "components/ui_label.h"
 #include "components/ui_label_ext.h"
 #include "traing/Training_UiConfig.h"
 #include "traing/include/TraingModel.h"
 #include <string>  // 包含字符串处理库,用于使用 std::to_string
 #include <ui_scroll_view.h>
 
-/*测试打印函数*/
-#define PRINT_LINE_INFO()                                                      \
-  WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "File: %s, Line: %d\r\n", __FILE__,   \
-                __LINE__)
-#define PRINT_FUNCTION_LINE()                                                  \
-  WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "Function: %s, Line: %d\r\n",         \
-                __FUNCTION__, __LINE__)
-
 /*
     普通运动:
                 时长,平均心率,训练强度,卡路里,目标完成度
                 距离,步数,平均配速,步幅,步频,目标完成度
 */
 #define UI_UPDATA 0
-namespace OHOS {
+#define CANVAS_SIZE 400
 
-static const int startY_Unit = 252;
-static const int startY_Value = 292;
-static const int labelHeight = 40;
-static const int labelInterval = 130;
-ColorType textColor = Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0x7f);
+namespace OHOS {
 
-static const int initialY = 132;
+static const int initialY = 132 + CANVAS_SIZE;
 static const int lineHeight = 130;
 static const int iconHeight = 80;
 static const int verticalOffset = 2;
-static const int baseY = 214;
+static const int baseY = 214 + CANVAS_SIZE;
+static const int startY_Unit = 252 + CANVAS_SIZE;
+static const int startY_Value = 292 + CANVAS_SIZE;
+static const int labelHeight = 40;
+static const int labelInterval = 130;
+
+ColorType textColor = Color::GetColorFromRGBA(0xff, 0xff, 0xff, 0x7f);
 
 static TraingEnddataView *g_pTraingEnddataView{nullptr};
 
@@ -52,7 +48,6 @@ TraingEnddataView::TraingEnddataView() { g_pTraingEnddataView = this; }
 
 TraingEnddataView::~TraingEnddataView() {
   DeInit();
-
   g_pTraingEnddataView = nullptr;
 }
 
@@ -109,10 +104,10 @@ 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, 240, 35, "label6",
+  InitRollLabel(labelsport, 147, 140 + CANVAS_SIZE, 240, 35, "label6",
                 STR_EXERCISE_OUTDOORRUNNING, DEFAULT_VECTOR_FONT_FILENAME, 30);
-  InitLabel(labelduration, 146, 179, 240, 40, "label7", "13:00:22-15:00:12",
-            DEFAULT_VECTOR_FONT_FILENAME, 28);
+  InitLabel(labelduration, 146, 179 + CANVAS_SIZE, 240, 40, "label7",
+            "13:00:22-15:00:12", DEFAULT_VECTOR_FONT_FILENAME, 28);
   // 初始化Label
   //时长
   InitRollLabel(labelutime, 146, startY_Unit + labelInterval * 0, 240, 40,
@@ -486,6 +481,11 @@ void TraingEnddataView::DeInit() {
     exitLabel = nullptr;
   }
 
+  if (canvas) {
+    delete canvas;
+    canvas = nullptr;
+  }
+
   if (scrollView_) {
     delete scrollView_;
     scrollView_ = nullptr;
@@ -656,8 +656,18 @@ void TraingEnddataView::InitData() {
       WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "Stride length set: %s\r\n",
                     strideStr.c_str()); // 打印步幅
     }
+#if defined(BOARD_W9) || defined(BOARD_N9)
+    //读取一下gps文件
+    std::string gpsFilePath =
+        TraingModel::GetInstance()->GetCanvasDataPath(enddata->starttime);
+    if (!gpsFilePath.empty()) {
+      if (TraingModel::GetInstance()->LoadCanvasData(gpsFilePath) == false) {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "gps文件加载失败");
+        return;
+      }
+    }
+#endif
   }
-
   WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,
                 "TraingEnddataView data initialization complete.\r\n");
 }
@@ -667,7 +677,7 @@ static void SetKeyActListener() {
   // 增加按键监听
   RootView::OnKeyActListener *keyactListener =
       (RootView::OnKeyActListener *)OHOS::TraingEnddataPresenter::GetInstance();
- RootView::GetInstance()->SetOnKeyActListener(keyactListener);
+  RootView::GetInstance()->SetOnKeyActListener(keyactListener);
 }
 void TraingEnddataView::OnStart() {
 
@@ -679,6 +689,97 @@ void TraingEnddataView::OnStart() {
   GetRootContainer()->SetOnDragListener(presenter_);
 
   SetKeyActListener();
+#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();
+  DrawTrackSegment(canvas);
+  DrawMarkers(canvas);
+#endif
+}
+
+
+//////////////////////////////gps轨迹测试/////////////////////////////////////
+
+std::vector<Marker> markers_ = {{{58, 18}, "起", Color::Green()},
+                                {{299, 146}, "终", Color::Red()}};
+
+void TraingEnddataView::GenerateTrackPoints() {
+  trackSegments_.clear();
+
+  TrackSegment seg1;
+  seg1.color = Color::Blue();
+
+  std::vector<Point> canvasData = TraingModel::GetInstance()->GetCanvasData();
+  //最小轨迹点数限制,防止绘制失败
+  if (canvasData.size() < 3) {
+    return;
+  }
+  //将点位赋值
+  for (size_t i = 0; i < canvasData.size(); ++i) {
+    seg1.points.push_back(canvasData[i]);
+  }
+  trackSegments_.push_back(seg1);
+
+  //修改markers_ 起终点坐标
+  markers_[0].pos = canvasData[0];
+  markers_[1].pos = canvasData.back();
+}
+void TraingEnddataView::DrawTrackSegment(UICanvas *canvas) {
+  // 绘制网格
+  // Paint debugPaint;
+  // debugPaint.SetStrokeColor(Color::Gray());
+  // for (int i = 0; i < CANVAS_SIZE; i += 5) {
+  //   canvas->DrawLine({i, 0}, {i, CANVAS_SIZE}, debugPaint);
+  //   canvas->DrawLine({0, i}, {CANVAS_SIZE, i}, debugPaint);
+  // }
+
+  // 绘制轨迹
+  for (const auto &seg : trackSegments_) {
+    if (seg.points.size() < 3) //至少需要 3 个点位
+      return;
+
+    Paint paint;
+    paint.SetStyle(Paint::PaintStyle::STROKE_STYLE);
+    paint.SetStrokeWidth(8);
+    paint.SetStrokeColor(seg.color);
+
+    canvas->BeginPath();
+    canvas->MoveTo(seg.points[0]);
+    for (size_t i = 1; i < seg.points.size(); ++i) {
+      canvas->LineTo(seg.points[i]);
+    }
+    canvas->DrawPath(paint);
+  }
+}
+void TraingEnddataView::DrawMarkers(UICanvas *canvas) {
+  Paint circlePaint;
+  circlePaint.SetStyle(Paint::PaintStyle::FILL_STYLE);
+
+  Paint textPaint;
+  textPaint.SetFillColor(Color::White());
+
+  for (const auto &marker : markers_) {
+    // 绘制标记点圆形
+    circlePaint.SetFillColor(marker.color);
+    canvas->DrawCircle(marker.pos, 12, circlePaint);
+
+    // 绘制文字,调整位置和排版
+    UICanvas::FontStyle fontStyle;
+    fontStyle.align = TEXT_ALIGNMENT_LEFT; // 文字居左,避免 RTL 反向
+    fontStyle.direct = TEXT_DIRECT_LTR;    // 从左到右排版
+    fontStyle.fontName = DEFAULT_VECTOR_FONT_FILENAME;
+    fontStyle.fontSize = 24; // 适配 300*300 画布
+    fontStyle.letterSpace = 2;
+
+    // 文字位置:圆点右侧偏移,避免覆盖
+    canvas->DrawLabel({marker.pos.x + 18, marker.pos.y - 6}, marker.text, 80,
+                      fontStyle, textPaint);
+  }
 }
 
 } // namespace OHOS
index e86deea..94ade42 100644 (file)
@@ -8,14 +8,13 @@
 #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" {
@@ -205,12 +210,13 @@ static void Training_Notify(void *args, const Topic topic, Request *request) {
 #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;
@@ -223,6 +229,9 @@ 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}; //震动提醒
 
@@ -656,8 +665,9 @@ void TraingModel::SetSportdata(SportData *data) {
     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);     // 配置训练特殊性
   }
 }
 /*获取训练卡路里*/
@@ -692,6 +702,9 @@ void TraingModel::ResetSharedTrainingData() {
   Openstate = true;     // 开启运动开始标志位
 
   shock_count = 0; //重置震动计数
+
+  gpsData.clear();    // 清空gps数据
+  canvasData.clear(); // 清空画布数据
 }
 
 uint32_t TraingModel::get_current_time() {
@@ -705,7 +718,6 @@ 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");
@@ -713,7 +725,6 @@ void TraingModel::SetTimestamp() {
   }
 
   ExerciseState currentState = GetSportState(); // 获取当前运动状态
-  WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "currentState:%d", currentState);
 
   // 增加标志位互斥保障"开始"和"结束"的唯一性操作
   if (currentState == ExerciseState::Start && !Startstate) {
@@ -755,9 +766,7 @@ void TraingModel::SetTimestamp() {
     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发起运动
@@ -769,11 +778,8 @@ void TraingModel::SetTimestamp() {
     } 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) {
@@ -793,20 +799,22 @@ void TraingModel::SetTimestamp() {
     //配置最后的训练强度(平均心率为标准)
     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();
@@ -815,7 +823,7 @@ void TraingModel::SetTimestamp() {
     else {
       sendtraningstatedata(0, sportdata->duration);
     }
-#endif
+
     WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "结束运动");
   }
 }
@@ -1020,7 +1028,6 @@ void TraingModel::SetSportTag(ExerciseTag tagtpye, int tag) {
 /* 设置训练强度 */
 void TraingModel::SetIntensity(int heart) {
   // sportdata->intensity = active_state;
-#if 1
   /*
       训练强度
       220-年龄=最高心率
@@ -1044,7 +1051,7 @@ void TraingModel::SetIntensity(int heart) {
       (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) {
     // 较低强度
@@ -1064,7 +1071,6 @@ void TraingModel::SetIntensity(int heart) {
   WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP,
                 "maxHeartRate: %d, heartRate: %d, intensityPercentage: %.2f",
                 maxHeartRate, heart, intensityPercentage);
-#endif
 }
 /*获取训练强度STR*/
 UIResourceTextId TraingModel::GetIntensity() {
@@ -1421,7 +1427,15 @@ bool TraingModel::SaveSportData() {
     // 保存速度表
     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;
     }
   }
@@ -1513,13 +1527,16 @@ 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!");
+      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; // 返回条目满了
   }
 
@@ -1633,8 +1650,10 @@ bool TraingModel::SavePaceTable() {
 
   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) {
@@ -1771,9 +1790,25 @@ std::string TraingModel::DisplayMotionDuration() {
 
   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; // 返回格式化的运动时长
@@ -1804,10 +1839,9 @@ void TraingModel::StoreDataIfMinutePassed() {
     }
   }
 
-  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
@@ -1828,13 +1862,16 @@ void LogError(const char *msg, osStatus_t ret) {
 /* 定时器回调函数 */
 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,
@@ -2196,6 +2233,9 @@ bool TraingModel::SaveDataToJson(SportData *data) {
   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);
@@ -2219,156 +2259,898 @@ bool TraingModel::SaveDataToJson(SportData *data) {
   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
 // 声明定时器变量
@@ -2558,4 +3340,4 @@ void ble_training_init(void) {
 
 #endif
 
-} // namespace OHOS
\ No newline at end of file
+} // namespace OHOS
index 795d6cb..09cc2e9 100644 (file)
@@ -47,13 +47,13 @@ namespace OHOS
 \r
     void TraingNomalPresenter::OnStart()\r
     {\r
-\r
+        TraingModel::GetInstance()->Traing_StartTimer(1000);\r
         Presenter<TraingNomalView>::OnStart();\r
     }\r
 \r
     void TraingNomalPresenter::OnPause(void)\r
     {\r
-\r
+        TraingModel::GetInstance()->Traing_StopTimer();\r
         Presenter<TraingNomalView>::OnPause();\r
     }\r
 \r
@@ -78,7 +78,9 @@ namespace OHOS
             3.[更多]->[具体训练项]进入\r
             4.[设置训练目标]进入 {不返回}\r
         */\r
-        NativeAbility::GetInstance().Back();\r
+        NativeAbility::GetInstance().Back(TransitionType::TRANSITION_BACK_TO_HEXAGONS);\r
+        // 关闭gps\r
+        TraingModel::GetInstance()->stop_heart_gps();\r
     }\r
     bool TraingNomalPresenter::OnDrag(UIView &view, const DragEvent &event)\r
     {\r
@@ -101,9 +103,17 @@ namespace OHOS
         }\r
         if (strcmp(view.GetViewId(), BUTTONGO) == 0)\r
         {\r
-            // 开始训练 ->不返回\r
-            NativeAbility::GetInstance().ChangeSlice(VIEW_TRAININGCRAD);\r
-\r
+            //默认前往运动卡片\r
+            AppViewId viewid = VIEW_TRAININGCRAD;\r
+        #if defined(BOARD_W9) || defined(BOARD_N9)\r
+            bool gps_status = TraingModel::GetInstance()->GetGPSConnectState();\r
+            if (!gps_status)\r
+            {\r
+               //未连接进入GPS提示界面\r
+               viewid = VIEW_TRAINING_GPSNOTE;\r
+            }\r
+        #endif\r
+            NativeAbility::GetInstance().ChangeSlice(viewid);\r
             // 获取运动项目\r
             ExerciseClass type = TraingModel::GetInstance()->GetSportId();\r
             // 开启对应的运动\r
@@ -124,4 +134,11 @@ namespace OHOS
         }\r
         return true;\r
     }\r
+\r
+    void TraingNomalPresenter::Notify()\r
+    {\r
+        //获取gps定位状态\r
+        TraingNomalView::GetInstance()->updateview();\r
+        \r
+    }\r
 }\r
index 1260685..91fc99a 100644 (file)
@@ -19,18 +19,19 @@ namespace OHOS
 
     TraingNomalView::TraingNomalView()
     {
+        TraingModel::GetInstance()->start_heart_gps();
         g_pTraingNomalView = this;
     }
 
     TraingNomalView::~TraingNomalView()
     {
         DeInit();
+        // TraingModel::GetInstance()->stop_heart_gps();
         g_pTraingNomalView = nullptr;
     }
 
     TraingNomalView *TraingNomalView::GetInstance()
     {
-
         return g_pTraingNomalView;
     }
 
@@ -60,8 +61,33 @@ namespace OHOS
 
         buttonGo_->SetOnClickListener(static_cast<UIView::OnClickListener *>(TraingNomalPresenter::GetInstance()));
         buttonSet_->SetOnClickListener(static_cast<UIView::OnClickListener *>(TraingNomalPresenter::GetInstance()));
-        AddViewToRootContainer(buttonGo_);
+   AddViewToRootContainer(buttonGo_);
         AddViewToRootContainer(buttonSet_);
+#if defined(BOARD_W9) || defined(BOARD_N9)
+        InitUILabel(heartrate_label_, -32, 230, 200, 60, "heartrate_label_", "--", DEFAULT_VECTOR_FONT_FILENAME, 30);
+        AddViewToRootContainer(heartrate_label_);
+        InitUILabel(gps_label_, 295, 230, 200, 60, "label_time", "GPS", DEFAULT_VECTOR_FONT_FILENAME, 30);
+        AddViewToRootContainer(gps_label_);
+        
+        // 获取gps状态
+        bool gps_status = TraingModel::GetInstance()->GetGPSConnectState();
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "gps状态: %d", gps_status);
+        if (gps_status)
+        {
+            //连接状态
+            InitImageView(gps_image_, 375, 193, 100, 100, "gps_image", GPS_1_IMAGE);
+            buttonGo_->SetImageSrc(GO_1_IMAGE, GO_1_IMAGE);
+        }
+        else
+        {
+            //断开状态
+            InitImageView(gps_image_, 375, 193, 100, 100, "gps_image", GPS_2_IMAGE);
+            buttonGo_->SetImageSrc(GO_2_IMAGE, GO_2_IMAGE);
+        }
+        AddViewToRootContainer(gps_image_);
+        InitImageView(heartrate_image_, 48, 193, 100, 100, "heartrate_image_", HEARTRATE_1_IMAGE);
+        AddViewToRootContainer(heartrate_image_);
+#endif
     }
     /* DeInit:删除组件*/
     void TraingNomalView::DeInit()
@@ -82,6 +108,28 @@ namespace OHOS
             delete buttonSet_;
             buttonSet_ = nullptr;
         }
+#if defined(BOARD_W9) || defined(BOARD_N9)
+        if (heartrate_label_ != nullptr)
+        {
+            delete heartrate_label_;
+            heartrate_label_ = nullptr;
+        }
+        if(gps_label_ != nullptr)
+        {
+            delete gps_label_;
+            gps_label_ = nullptr;
+        }
+        if(gps_image_ != nullptr)
+        {
+            delete gps_image_;
+            gps_image_ = nullptr;
+        }
+        if(heartrate_image_ != nullptr)
+        {
+            delete heartrate_image_;
+            heartrate_image_ = nullptr;
+        }
+#endif
     }
 
     void TraingNomalView::PreLoad(void)
@@ -98,7 +146,7 @@ namespace OHOS
         WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "SetKeyActListener");
         // 增加按键监听
         RootView::OnKeyActListener *keyactListener = (RootView::OnKeyActListener *)OHOS::TraingNomalPresenter::GetInstance();
-       RootView::GetInstance()->SetOnKeyActListener(keyactListener);
+        RootView::GetInstance()->SetOnKeyActListener(keyactListener);
     }
     void TraingNomalView::OnStart()
     {
@@ -127,4 +175,46 @@ namespace OHOS
     }
 
 
+      //更新gps状态
+    bool TraingNomalView::updateview()
+    {
+        if(gps_image_ == nullptr || buttonGo_ == nullptr)
+        {
+            return false;
+        }
+        // 更新GPS状态显示
+        bool gps_status = TraingModel::GetInstance()->GetGPSConnectState();
+        if(gps_status == updat_view_status)
+        {
+            return false;
+        }
+        updat_view_status = gps_status;
+        WEARABLE_LOGD(WEARABLE_LOG_MODULE_APP, "gps状态: %d", updat_view_status);
+        ImageInfo *image;
+        std::string button_image_path;
+        if (updat_view_status)
+        {
+            // 连接状态 - 加载并设置GPS连接图标
+            ImageCacheManager::GetInstance().UnloadSingleRes(GPS_2_IMAGE);
+            image = ImageCacheManager::GetInstance().LoadSingleRes(GPS_1_IMAGE, false);
+            if (image == nullptr) {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "加载GPS连接图标失败: %s", GPS_1_IMAGE);
+                return false;
+            }
+            button_image_path = GPS_1_IMAGE;
+        }
+        else
+        {
+            ImageCacheManager::GetInstance().UnloadSingleRes(GPS_1_IMAGE);
+            image = ImageCacheManager::GetInstance().LoadSingleRes(GPS_2_IMAGE, false);
+            if (image == nullptr) {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "加载GPS断开图标失败: %s", GPS_2_IMAGE);
+                return false;
+            }
+            button_image_path = GPS_2_IMAGE;
+        }
+        gps_image_->SetSrc(image);
+        buttonGo_->SetImageSrc(button_image_path.c_str(), button_image_path.c_str());
+        return true; 
+    }
 }
index 4791bc3..23e7f2a 100644 (file)
@@ -3,7 +3,6 @@
 #define _TraingCard_PRESENTER_H\r
 \r
 #include "ModelListenerSample.h"\r
-#include "key/include/KeyInputListener.h"\r
 #include "Presenter.h"\r
 #include "TraingCardView.h"\r
 #include "traing/training_card/include/TrainingControlView.h"\r
@@ -21,9 +20,8 @@
 #include "components/ui_swipe_view.h"\r
 #include "components/ui_view.h"\r
 \r
-// #include "cywee_hal.h"\r
-// #include "cywee_service.h"\r
-\r
+#include "cywee_hal.h"\r
+#include "cywee_service.h"\r
 #include "traing/include/TraingModel.h"\r
 #include "uicomponents/UIComponents.h"\r
 \r
@@ -34,11 +32,11 @@ class TrainingDataView;
 class TrainingDetailView;\r
 class TraingModel;\r
 class TraingCardPresenter : public Presenter<TraingCardView>,\r
-                            public ModelListenerSample,\r
                             public UIView::OnClickListener,\r
                             public UIView::OnDragListener,\r
-                            public ListScrollListener,\r
-                            public RootView::OnKeyActListener {\r
+                            public UISwipeView::OnSwipeListener,\r
+                            public RootView::OnKeyActListener,\r
+                            public ModelListenerSample {\r
 public:\r
   TraingCardPresenter();\r
   virtual ~TraingCardPresenter() override;\r
@@ -61,7 +59,7 @@ public:
   void UpdateTimeOfData();\r
   void UpdateDataView();\r
   void updataview();\r
-/*蓝牙开启运动*/\r
+  /*蓝牙开启运动*/\r
 #ifdef BLE_SERVER_ENABLE\r
   bool send_sport_cmd(training_ble *cmd);\r
 #endif\r
@@ -76,12 +74,12 @@ private:
   // 因为继承了OnClickListener,所以有OnClick属性,至于参数,原函数也这样直接继承;\r
   bool OnClick(UIView &view, const ClickEvent &event) override;\r
   bool OnDrag(UIView &view, const DragEvent &event) override;\r
+  void OnSwipe(UISwipeView &view) override;\r
   bool OnKeyAct(UIView &view, const KeyEvent &event) override; // 增加按键事件\r
 \r
   int tag_ = 0;\r
   bool opensensorstuats{false};\r
   // bool OnDragEnd(UIView& view, const DragEvent& event) override;\r
-  // void OnSwipe(UISwipeView &view) override;\r
 };\r
 } // namespace OHOS\r
 \r
index 4d67223..3b7db2d 100644 (file)
@@ -11,7 +11,6 @@
 #include "components/root_view.h"
 #include "UiConfig.h"
 #include "components/ui_card_page.h"
-
 #include "View.h"
 #include <ui_scroll_view.h>
 #include "components/ui_view_group.h"
@@ -47,6 +46,7 @@ namespace OHOS
         UIButton *GetButton();
 
         UILabelExt *GetTimeLabel();
+        UILabelExt *GetGpsStatusLabel();
 
     private:
         UIView::OnDragListener *draglistener{nullptr};
@@ -62,6 +62,8 @@ namespace OHOS
 
         UIImageView *imagepoint = nullptr;
 
+        UILabelExt *gpsstatus = nullptr;
+
         void ClearAll(void);
     };
 }
index 393c42a..f5169f9 100644 (file)
@@ -41,8 +41,7 @@ extern "C" void do_cywee_service_check_msg(int cmd, int data[]);
 #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
@@ -53,8 +52,7 @@ void ResumeTimer();
 #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
@@ -63,139 +61,130 @@ static TraingCardPresenter *g_pTraingCardPresenter = nullptr;
 \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
@@ -224,11 +213,9 @@ bool TraingCardPresenter::OpenSensor(ExerciseClass type)
 }\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
@@ -249,10 +236,8 @@ bool TraingCardPresenter::ControlSensor(ExerciseState state)
   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
@@ -262,16 +247,13 @@ void TraingCardPresenter::RefreshData(SportData *sportdata)
     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
@@ -280,8 +262,8 @@ void TraingCardPresenter::RefreshData(SportData *sportdata)
   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
@@ -307,33 +289,30 @@ void TraingCardPresenter::RefreshData(SportData *sportdata)
 }\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
@@ -342,26 +321,21 @@ void TraingCardPresenter::updataview(void)
   }\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
@@ -370,8 +344,7 @@ void TraingCardPresenter::Notify(void)
   }\r
 }\r
 #endif\r
-void TraingCardPresenter::UpdateDataView()\r
-{\r
+void TraingCardPresenter::UpdateDataView() {\r
   // 刷新计时\r
   UpdateTimeOfData();\r
 \r
@@ -386,9 +359,8 @@ void TraingCardPresenter::UpdateDataView()
   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
@@ -396,80 +368,68 @@ bool TraingCardPresenter::OnClick(UIView &view, const ClickEvent &event)
       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
@@ -480,19 +440,14 @@ bool TraingCardPresenter::send_sport_cmd(training_ble *cmd)
   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
@@ -513,18 +468,14 @@ bool TraingCardPresenter::send_sport_cmd(training_ble *cmd)
   }\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
@@ -546,20 +497,16 @@ bool TraingCardPresenter::send_sport_cmd(training_ble *cmd)
       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
@@ -569,8 +516,7 @@ bool TraingCardPresenter::send_sport_cmd(training_ble *cmd)
 \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
@@ -580,9 +526,8 @@ bool TraingCardPresenter::send_sport_cmd(training_ble *cmd)
   // 仅在运动状态为开始、继续或暂停时允许结束运动\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
@@ -602,10 +547,8 @@ bool TraingCardPresenter::send_sport_cmd(training_ble *cmd)
   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
@@ -616,21 +559,17 @@ void TraingCardPresenter::handleEndState()
   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
@@ -640,8 +579,7 @@ void TraingCardPresenter::handleEndState()
 }\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
@@ -652,8 +590,7 @@ void TraingCardPresenter::handlePauseState()
 }\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
@@ -664,8 +601,7 @@ void TraingCardPresenter::handleContinueState()
 }\r
 \r
 /*训练开始*/\r
-void TraingCardPresenter::handleStartState()\r
-{\r
+void TraingCardPresenter::handleStartState() {\r
   ControlSensor(ExerciseState::Start);\r
   TraingModel::GetInstance()->SetSportOpenWay(true);\r
 }\r
index 1ca79f2..b6709e4 100644 (file)
@@ -5,6 +5,7 @@
 #include "common/image_cache_manager.h"
 #include "components/ui_button.h"
 #include "components/ui_label_ext.h"
+#include "languagecode/LanguageInit.h"
 #include "string"
 #include "traing/Training_UiConfig.h"
 #include "traing/include/TraingModel.h"
@@ -14,7 +15,6 @@
 #include <chrono>
 #include <thread>
 #include <ui_scroll_view.h>
-#include "languagecode/LanguageInit.h"
 namespace OHOS {
 
 static TrainingControlView *g_pTrainingControlView{nullptr};
@@ -83,6 +83,13 @@ void TrainingControlView::InitView() {
   // 初始化 Image
   InitImageView(imagepoint, 193, 421, 80, 20, "image1", CONTROL_POINT_IMAGE);
   Add(imagepoint);
+
+#if defined(BOARD_W9) || defined(BOARD_N9)
+  InitLabel(gpsstatus, 83, 365, 300, 35, "label1", "gps:定位中...",
+            DEFAULT_VECTOR_FONT_FILENAME, 30);
+  gpsstatus->SetTextColor(Color::Red());
+  Add(gpsstatus);
+#endif
 }
 
 UILabelExt *TrainingControlView::GetLabel() { return labelstop; }
@@ -108,6 +115,10 @@ void TrainingControlView::DeInit() {
     delete labelstop;
     labelstop = nullptr;
   }
+  if (gpsstatus != nullptr) {
+    delete gpsstatus;
+    gpsstatus = nullptr;
+  }
 
   // 删除Button
   if (buttonEnd != nullptr) {
@@ -136,4 +147,5 @@ void TrainingControlView::PreLoad(void) {
 void TrainingControlView::UnLoad(void) {}
 
 UILabelExt *TrainingControlView::GetTimeLabel() { return labeltimer; }
+UILabelExt *TrainingControlView::GetGpsStatusLabel() { return gpsstatus; }
 } // namespace OHOS