增加ECG测试AT
authorxue <2144439972@qq.com>
Mon, 15 Jun 2026 03:10:11 +0000 (11:10 +0800)
committerxue <2144439972@qq.com>
Mon, 15 Jun 2026 03:10:11 +0000 (11:10 +0800)
code/sdk/application/brandy/brandy_standard/service_init.c
code/sdk/application/wearable/n9/nativeapp/nativeui/ecg/include/ECGAtCmd.h [new file with mode: 0644]
code/sdk/application/wearable/n9/nativeapp/nativeui/ecg/include/ECGMainPresenter.h
code/sdk/application/wearable/n9/nativeapp/nativeui/ecg/src/ECGAtCmd.cpp [new file with mode: 0644]
code/sdk/application/wearable/n9/nativeapp/nativeui/ecg/src/ECGMainPresenter.cpp

index 5fe4fcf..4987e94 100644 (file)
@@ -26,6 +26,7 @@
 #include "ohosfwk_at_process.h"\r
 #endif\r
 #include "bloodpressure/include/BPAtCmd.h"\r
+#include "ecg/include/ECGAtCmd.h"\r
 #ifdef SUPPORT_AT_APP\r
 #include "app_cmd_manager.h"\r
 #endif\r
@@ -137,6 +138,9 @@ static void register_at_service(void)
     uint32_t bp_ui_cmd_count;\r
     at_cmd_table_t *bp_ui_table = get_bp_ui_at_cmd_table(&bp_ui_cmd_count);\r
     at_cmd_add_moudle(bp_ui_table, bp_ui_cmd_count);\r
+    uint32_t ecg_ui_cmd_count;\r
+    at_cmd_table_t *ecg_ui_table = get_ecg_ui_at_cmd_table(&ecg_ui_cmd_count);\r
+    at_cmd_add_moudle(ecg_ui_table, ecg_ui_cmd_count);\r
 #ifdef SUPPORT_AT_APP\r
     uint32_t app_cmd_count;\r
     at_cmd_table_t *app_table = get_app_cmd_table(&app_cmd_count);\r
diff --git a/code/sdk/application/wearable/n9/nativeapp/nativeui/ecg/include/ECGAtCmd.h b/code/sdk/application/wearable/n9/nativeapp/nativeui/ecg/include/ECGAtCmd.h
new file mode 100644 (file)
index 0000000..4facd81
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) CompanyNameMagicTag. All rights reserved.
+ * Description: 心电 UI AT 调试命令
+ */
+#ifndef ECG_AT_CMD_H
+#define ECG_AT_CMD_H
+
+#include <stdint.h>
+#include "at_cmd_api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+at_cmd_table_t *get_ecg_ui_at_cmd_table(uint32_t *cmd_count);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
index 4ef9ce7..9e55fcf 100644 (file)
@@ -37,9 +37,11 @@ public:
   void OnMeasureTimerTick();
   void OnEnterMeasuringView();
   void FinishMeasuringFlow(ECGMainView *mainView);
+  void NotifyMeasureComplete(uint8_t bpm, uint8_t afibResult);
   bool IsMeasureSessionActive() const { return measureSessionActive_; }
 
 private:
+  void CompleteMeasureFlow(uint8_t bpm, uint8_t afibResult);
   void NavigateBack();
   bool HandleSwipeBack();
   bool IsFlowSubPage(int16_t viewId) const;
diff --git a/code/sdk/application/wearable/n9/nativeapp/nativeui/ecg/src/ECGAtCmd.cpp b/code/sdk/application/wearable/n9/nativeapp/nativeui/ecg/src/ECGAtCmd.cpp
new file mode 100644 (file)
index 0000000..c0bf769
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) CompanyNameMagicTag. All rights reserved.
+ * Description: 心电模块 AT 调试命令
+ *
+ * AT^ECGMEASVAL=bpm,afibResult  — 模拟测量完成,直接显示结果
+ *   afibResult: 0=窦性心律 1=房颤 2=无法判断
+ */
+#include "ecg/include/ECGAtCmd.h"
+#include "ecg/include/ECGMainPresenter.h"
+#include "at_cmd_api.h"
+#include "custom_utils.h"
+#include "debug_print.h"
+#include <cstdlib>
+#include <cstring>
+
+#define ECG_AT_MAX_ARGC 4
+#define ECG_AT_MAX_ARGV_LEN 16
+#define ECG_AT_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
+
+static int EcgAtConvertParam(uint8_t *para, uint32_t paraLen,
+                             uint8_t argv[ECG_AT_MAX_ARGC][ECG_AT_MAX_ARGV_LEN], uint32_t *argc) {
+  uint32_t pos = 0;
+  *argc = 0;
+  if (para == nullptr || argc == nullptr) {
+    return -1;
+  }
+  for (uint32_t i = 0; i < paraLen; i++) {
+    if ((para[i] == ' ' || para[i] == '\n' || para[i] == ',') && pos != 0) {
+      argv[*argc][pos] = '\0';
+      (*argc)++;
+      pos = 0;
+      if (*argc >= ECG_AT_MAX_ARGC) {
+        return -1;
+      }
+    } else if (para[i] != ' ' && para[i] != '\n' && para[i] != ',') {
+      argv[*argc][pos] = para[i];
+      pos++;
+      if (pos >= ECG_AT_MAX_ARGV_LEN) {
+        return -1;
+      }
+    }
+  }
+  if (pos != 0) {
+    (*argc)++;
+  }
+  return 0;
+}
+
+static uint32_t ecg_meas_val_at(uint8_t *para, uint32_t paraLen, char *strCommonCmd) {
+  (void)strCommonCmd;
+  if (paraLen == 0 || (para != nullptr && paraLen >= 1 && para[0] == '?')) {
+    PRINT("AT^ECGMEASVAL=bpm,afibResult\r\n");
+    PRINT("  afibResult: 0=sinus 1=AF 2=inconclusive\r\n");
+    return ERRCODE_SUCC;
+  }
+
+  uint8_t argv[ECG_AT_MAX_ARGC][ECG_AT_MAX_ARGV_LEN] = {{0}};
+  uint32_t argc = 0;
+  if (EcgAtConvertParam(para, paraLen, argv, &argc) != 0 || argc < 2) {
+    PRINT("ECGMEASVAL need bpm,afibResult\r\n");
+    return ERRCODE_FAIL;
+  }
+
+  int bpm = atoi(reinterpret_cast<char *>(argv[0]));
+  int afibResult = atoi(reinterpret_cast<char *>(argv[1]));
+  if (bpm < 0 || bpm > 255 || afibResult < 0 || afibResult > 2) {
+    PRINT("ECGMEASVAL invalid bpm/afibResult\r\n");
+    return ERRCODE_FAIL;
+  }
+
+  uapi_set_screen_on_api();
+  OHOS::ECGMainPresenter *presenter = OHOS::ECGMainPresenter::GetInstance();
+  if (presenter == nullptr) {
+    PRINT("ECGMEASVAL fail: presenter not ready\r\n");
+    return ERRCODE_FAIL;
+  }
+  presenter->NotifyMeasureComplete(static_cast<uint8_t>(bpm), static_cast<uint8_t>(afibResult));
+  PRINT("ECGMEASVAL ok: bpm=%d afib=%d\r\n", bpm, afibResult);
+  return ERRCODE_SUCC;
+}
+
+static const at_cmd_table_t g_ecg_ui_at_cmd[] = {
+    {(char *)"AT^ECGMEASVAL", ecg_meas_val_at, (char *)"bpm,afibResult"},
+};
+
+extern "C" at_cmd_table_t *get_ecg_ui_at_cmd_table(uint32_t *cmd_count) {
+  if (cmd_count != nullptr) {
+    *cmd_count = ECG_AT_ARRAY_COUNT(g_ecg_ui_at_cmd);
+  }
+  return const_cast<at_cmd_table_t *>(g_ecg_ui_at_cmd);
+}
index 3b76b7d..a0107fc 100644 (file)
@@ -372,6 +372,36 @@ void ECGMainPresenter::FinishMeasuringFlow(ECGMainView *mainView) {
   }
 }
 
+void ECGMainPresenter::CompleteMeasureFlow(uint8_t bpm, uint8_t afibResult) {
+  ECGModel *model = ECGModel::GetInstance();
+  model->SetMeasureResult(bpm, afibResult);
+  model->SaveMeasureRecord(bpm, afibResult);
+  ECGMainView *mainView = ECGMainView::GetInstance();
+  if (mainView == nullptr) {
+    return;
+  }
+  int16_t current = mainView->GetCurrentViewId();
+  if (current == ECG_MEASURING_VIEW) {
+    ECGMeasuringView *view = ECGMeasuringView::GetInstance();
+    if (view != nullptr) {
+      view->StopMeasurement();
+    }
+    mainView->ChangeView(ECG_MEASURING_VIEW, ECG_MEASURE_RESULT_VIEW);
+    return;
+  }
+  mainView->ChangeView(current, ECG_MEASURE_RESULT_VIEW);
+}
+
+void ECGMainPresenter::NotifyMeasureComplete(uint8_t bpm, uint8_t afibResult) {
+  if (measureDataReceived_) {
+    return;
+  }
+  measureDataReceived_ = true;
+  StopMeasureTimer();
+  StopContactGoodTimer();
+  CompleteMeasureFlow(bpm, afibResult);
+}
+
 bool ECGMainPresenter::OnDragStart(UIView &view, const DragEvent &event) {
   UNUSED(view);
   UNUSED(event);