From 9f37eecde4aaa8be68e0de13a33b8303a9244974 Mon Sep 17 00:00:00 2001 From: zhaohe Date: Thu, 9 May 2024 09:38:25 +0800 Subject: [PATCH] update --- CMakeLists.txt | 1 + CMakeLists.txt.user | 2 +- ify_hrs_protocol | 2 +- libzqt/widgetplot2d.cpp | 30 +++--- libzqt/zcsv.cpp | 127 ++++++++++++++++++++++ libzqt/zcsv.hpp | 47 +++++++++ mainwindow.cpp | 63 +++++++---- mainwindow.h | 4 + mainwindow.ui | 217 ++++++++++++++++++++------------------ src/electrocardiograph_tester.cpp | 25 ++++- src/electrocardiograph_tester.hpp | 2 + 11 files changed, 372 insertions(+), 148 deletions(-) create mode 100644 libzqt/zcsv.cpp create mode 100644 libzqt/zcsv.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1cafae2..632ff68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ set(PROJECT_SOURCES libzqt/logger.cpp libzqt/zqthread.cpp libzqt/QFunction.cpp + libzqt/zcsv.cpp src/main.cpp mainwindow.cpp diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user index cb97507..064f247 100644 --- a/CMakeLists.txt.user +++ b/CMakeLists.txt.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/ify_hrs_protocol b/ify_hrs_protocol index 391a955..a1fda58 160000 --- a/ify_hrs_protocol +++ b/ify_hrs_protocol @@ -1 +1 @@ -Subproject commit 391a9551d21fd8b591e1eb4a5c8555ec2d59ff3c +Subproject commit a1fda58c30f6bb892c9f503280b3896c67e00dda diff --git a/libzqt/widgetplot2d.cpp b/libzqt/widgetplot2d.cpp index 9c47202..049e43c 100644 --- a/libzqt/widgetplot2d.cpp +++ b/libzqt/widgetplot2d.cpp @@ -3,6 +3,8 @@ #include #include "ui_widgetplot2d.h" +#include "zcsv.hpp" +using namespace iflytop; WidgetPlot2D::WidgetPlot2D(QWidget* parent) : QWidget(parent), ui(new Ui::WidgetPlot2D) { ui->setupUi(this); @@ -434,22 +436,22 @@ bool WidgetPlot2D::isDirExist(QString fullPath) { /* 保存绘图成图片 */ void WidgetPlot2D::savePlotPng() { - // 获取程序运行路径 - QString savePath = QCoreApplication::applicationDirPath() + "/output"; - // 判断文件路径 - if (isDirExist(savePath)) { - } else { - qDebug() << "保存文件目录未找到!"; - QMessageBox::warning(this, "warning", "保存文件目录未找到!", QMessageBox::Yes, QMessageBox::Yes); - } + QString savePath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); QString fileName = QFileDialog::getSaveFileName(this, - "保存波形数据", // 对话框的标题 - savePath, // 保存的默认路径为程序运行路径 - "Save Picture (*.png *jpg)"); // 打开文件的类型,;隔开 - // 如果用户点击了“取消”按钮 + "保存波形数据", // 对话框的标题 + savePath, // 保存的默认路径为程序运行路径 + "Save Picture (*.csv)"); // 打开文件的类型,;隔开 if (fileName.isNull()) return; - // 保存图片 - ui->customPlot->savePng(fileName, 1280, 800, 1.0, -1, 255); + + ZCSV csv; + int datacnt = ui->customPlot->graph(0)->dataCount(); + csv.setdata(1, 1, "pointIndex"); + csv.setdata(1, 2, "value"); + for (size_t i = 0; i < datacnt; i++) { + csv.setdata(i + 1, 1, QString::number(ui->customPlot->graph(0)->data()->at(i)->key).toStdString()); + csv.setdata(i + 1, 2, QString::number(ui->customPlot->graph(0)->data()->at(i)->value).toStdString()); + } + csv.dumpCSV(fileName.toStdString()); } /* 水平滚动条移动 */ diff --git a/libzqt/zcsv.cpp b/libzqt/zcsv.cpp new file mode 100644 index 0000000..c5cfd06 --- /dev/null +++ b/libzqt/zcsv.cpp @@ -0,0 +1,127 @@ + +#include "zcsv.hpp" +using namespace std; +using namespace iflytop; + +ZCSV::ZCSV() {} + +bool ZCSV::parseCSV(string filename) { + csvData.clear(); + ifstream file(filename); + string line; + + if (!file.is_open()) { + return false; + } + + int rowNum = 0; + while (getline(file, line)) { + rowNum = rowNum + 1; + stringstream linestream(line); + string cell; + ZCSVCell csvCell; + csvCell.rowNum = rowNum; + int colNum = 0; + + while (getline(linestream, cell, ',')) { + colNum = colNum + 1; + csvCell.colNum = colNum; + csvCell.data = cell; + csvData.push_back(csvCell); + } + + csvData.push_back(csvCell); + } + + // 找到最大行数 + int maxRowNum = 0; + for (list::iterator it = csvData.begin(); it != csvData.end(); it++) { + if (it->rowNum > maxRowNum) { + maxRowNum = it->rowNum; + } + } + + // 找到最大列数 + int maxColNum = 0; + for (list::iterator it = csvData.begin(); it != csvData.end(); it++) { + if (it->colNum > maxColNum) { + maxColNum = it->colNum; + } + } + + m_maxRowNum = maxRowNum; + m_maxColNum = maxColNum; + return true; +} + +ZCSVCell* ZCSV::findCell(int rowNum, int colNum) { + for (list::iterator it = csvData.begin(); it != csvData.end(); it++) { + if (it->rowNum == rowNum && it->colNum == colNum) { + return &(*it); + } + } + return NULL; +} + +void ZCSV::setdata(int rowNum, int colNum, string data) { + ZCSVCell* cell = findCell(rowNum, colNum); + if (cell != NULL) { + cell->data = data; + } else { + ZCSVCell newCell; + newCell.rowNum = rowNum; + newCell.colNum = colNum; + newCell.data = data; + + if (rowNum > m_maxRowNum) { + m_maxRowNum = rowNum; + } + + if (colNum > m_maxColNum) { + m_maxColNum = colNum; + } + + csvData.push_back(newCell); + } +} +string ZCSV::getdata(int rowNum, int colNum) { + ZCSVCell* cell = findCell(rowNum, colNum); + if (cell != NULL) { + return cell->data; + } else { + return ""; + } +} + +void ZCSV::dumpCSV(string filename) { + ofstream file; + file.open(filename, ios::out | ios::trunc); + + // 找到最大行数 + int maxRowNum = 0; + for (list::iterator it = csvData.begin(); it != csvData.end(); it++) { + if (it->rowNum > maxRowNum) { + maxRowNum = it->rowNum; + } + } + + // 找到最大列数 + int maxColNum = 0; + for (list::iterator it = csvData.begin(); it != csvData.end(); it++) { + if (it->colNum > maxColNum) { + maxColNum = it->colNum; + } + } + + // 足个点插入数据 + for (int i = 1; i <= maxRowNum; i++) { + for (int j = 1; j <= maxColNum; j++) { + ZCSVCell* cell = findCell(i, j); + if (cell != NULL) { + file << cell->data; + } + file << ","; + } + file << endl; + } +} \ No newline at end of file diff --git a/libzqt/zcsv.hpp b/libzqt/zcsv.hpp new file mode 100644 index 0000000..fe86d66 --- /dev/null +++ b/libzqt/zcsv.hpp @@ -0,0 +1,47 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace iflytop { +using namespace std; + +class ZCSVCell { + public: + int rowNum; + int colNum; + string data; +}; + +class ZCSV { + private: + list csvData; + + int m_maxRowNum = 0; + int m_maxColNum = 0; + + public: + ZCSV(); + + bool parseCSV(string filename); + + void setdata(int rowNum, int colNum, string data); + string getdata(int rowNum, int colNum); + + int maxRowNum() { return m_maxRowNum; } + int maxColNum() { return m_maxColNum; } + + void dumpCSV(string filename); + + private: + ZCSVCell* findCell(int rowNum, int colNum); +}; + +} // namespace iflytop \ No newline at end of file diff --git a/mainwindow.cpp b/mainwindow.cpp index 4189c9f..29e8484 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -243,12 +243,15 @@ void MainWindow::constructUI() { ui->serialBaudrateCB->setEnabled(false); ui->serialPortCB->setEnabled(false); ui->serialPortRefreshKey->setEnabled(false); + ui->deviceType->setEnabled(false); + } else { G_QTDataChannel.close(); ui->serialOpenKey->setText("打开"); ui->serialBaudrateCB->setEnabled(true); ui->serialPortCB->setEnabled(true); ui->serialPortRefreshKey->setEnabled(true); + ui->deviceType->setEnabled(true); } }); @@ -276,11 +279,10 @@ void MainWindow::constructUI() { int reportType = report_packet->cmd; switch (reportType) { case ify_hrs_report_heartrate_data: { - heartrate_report_packet_t *heartrate_report = (heartrate_report_packet_t *)report_packet; - if (m_devicetype == kone_lead_ecg) { - static uint32_t lastpacket_index = 0; - static uint32_t lostpacket = 0; + heartrate_report_packet_t *heartrate_report = (heartrate_report_packet_t *)report_packet; + static uint32_t lastpacket_index = 0; + static uint32_t lostpacket = 0; if ((lastpacket_index + 5) != (heartrate_report->sample_data_index)) { lostpacket++; } @@ -300,33 +302,28 @@ void MainWindow::constructUI() { wp2d->addData("心电", data2, 4); wp2d->addData("心电", data3, 6); wp2d->addData("心电", data4, 8); - // wp2d->addData("心电", data2); - // wp2d->addData("心电", data3); - // wp2d->addData("心电", data4); })); } } else if (m_devicetype == kthree_lead_ecg) { } else if (m_devicetype == kone_lead_ecg_v2) { - if (checkok) { - uint32_t packetlen = len - sizeof(heartrate_report_packet_t); + if (!checkok) return; + + m1003_heartrate_report_packet_t *heartrate_report = (m1003_heartrate_report_packet_t *)report_packet; + static uint32_t lostpacket = 0; + { static uint32_t lastpacket_index = 0; - static uint32_t lostpacket = 0; - if ((lastpacket_index + 50) != (heartrate_report->sample_data_index)) { + if ((lastpacket_index + heartrate_report->sample_data_num) != (heartrate_report->sample_data_index)) { lostpacket++; } lastpacket_index = heartrate_report->sample_data_index; - - uint32_t timestamp = *(uint32_t *)(&heartrate_report->data[50 * 4]); - - reportPreviewShow("[preview data ] timestamp:%10d lost:%d index %d packetlen %d", timestamp, lostpacket, heartrate_report->sample_data_index, packetlen); - int32_t *frame = (int32_t *)heartrate_report->data; - for (int i = 0; i < 50; i++) { - int32_t data = frame[i]; - int32_t frameIndex = heartrate_report->sample_data_index + i; - data = FilterAlgoMgr::ins()->processData("心电", data); - - emit doinui_signal(QFunction([this, data, i]() { wp2d->addData("心电", data, i * 2); })); - } + } + reportPreviewShow("[preview data ] lost:%d index %d leadoff 0x%x", // + lostpacket, heartrate_report->sample_data_index, heartrate_report->leadoff_state); + for (int i = 0; i < heartrate_report->sample_data_num; i++) { + int16_t data = heartrate_report->frame[i]; + int32_t frameIndex = heartrate_report->sample_data_index + i; + data = FilterAlgoMgr::ins()->processData("心电", data); + emit doinui_signal(QFunction([this, data, i]() { wp2d->addData("心电", data, i * 2); })); } } break; @@ -759,3 +756,23 @@ void MainWindow::on_TestCmd_writeSubICAllReg_clicked() { processException(exception); } } + +void MainWindow::on_setEcgInTestMode_clicked() { + instructionPreviewClear(); + try { + ElectrocardiographTester::ins()->set_ecg_in_test_mode(1); + ishow("set ECG in test mode success"); + } catch (zexception &exception) { + processException(exception); + } +} + +void MainWindow::on_setEcgInNormalMode_clicked() { + instructionPreviewClear(); + try { + ElectrocardiographTester::ins()->set_ecg_in_test_mode(0); + ishow("set ECG in test mode success"); + } catch (zexception &exception) { + processException(exception); + } +} diff --git a/mainwindow.h b/mainwindow.h index 921b5e7..c4692c5 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -128,6 +128,10 @@ class MainWindow : public QMainWindow { void on_TestCmd_writeSubICAllReg_clicked(); + void on_setEcgInTestMode_clicked(); + + void on_setEcgInNormalMode_clicked(); + signals: void doinui_signal(QFunction); diff --git a/mainwindow.ui b/mainwindow.ui index 69d6e91..ad10c45 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -614,7 +614,7 @@ QGroupBox:title { - 200 + 250 0 @@ -637,16 +637,6 @@ QGroupBox:title { 串口设置 - - - - - - - 打开 - - - @@ -657,6 +647,9 @@ QGroupBox:title { + + + @@ -664,32 +657,31 @@ QGroupBox:title { - + + + + 打开 + + + + 刷新 - - - - - - - - 16777215 - 16777215 - - - - 设备配置 - - - - + + + + + 2 + 0 + + + - + 型号 @@ -825,7 +817,11 @@ p, li { white-space: pre-wrap; } <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#ff0000;">M1003 单导心电记录仪-2代</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#ff0000;">注意事项:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#ff0000;">1.当检查上传数据的CHECKSUM(检验和)时,需要先复位下蓝牙主机开发板清空统计信息</span></p></body></html> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#ff0000;">1.当检查上传数据的CHECKSUM(检验和)时,需要先复位下蓝牙主机开发板清空统计信息</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; color:#ff0000;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#ff0000;">版本:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#ff0000;">V2:支持原始数据保存</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" color:#ff0000;"> </span></p></body></html> @@ -907,7 +903,7 @@ p, li { white-space: pre-wrap; } - 1 + 0 @@ -917,7 +913,7 @@ p, li { white-space: pre-wrap; } - 设备信息 + 设备基本操作 @@ -958,8 +954,8 @@ p, li { white-space: pre-wrap; } - - + + 1 @@ -973,12 +969,12 @@ p, li { white-space: pre-wrap; } - 读取设备时间 + 读取设备SN - - + + 1 @@ -992,7 +988,45 @@ p, li { white-space: pre-wrap; } - 读取设备SN + 读取传感器信息 + + + + + + + + 1 + 0 + + + + + 0 + 30 + + + + 设置ECG为正常模式 + + + + + + + + 1 + 0 + + + + + 0 + 30 + + + + 复位设备 @@ -1015,7 +1049,26 @@ p, li { white-space: pre-wrap; } - + + + + + 1 + 0 + + + + + 0 + 30 + + + + 读取设备时间 + + + + Qt::Horizontal @@ -1028,7 +1081,7 @@ p, li { white-space: pre-wrap; } - + Qt::Vertical @@ -1041,8 +1094,8 @@ p, li { white-space: pre-wrap; } - - + + 1 @@ -1056,26 +1109,7 @@ p, li { white-space: pre-wrap; } - 读取传感器信息 - - - - - - - - 1 - 0 - - - - - 0 - 30 - - - - 复位设备 + 设置ECG为正常模式 @@ -1262,27 +1296,8 @@ p, li { white-space: pre-wrap; } 采集&预览 - - - - - 1 - 0 - - - - - 0 - 30 - - - - 停止预览 - - - - - + + 1 @@ -1296,31 +1311,25 @@ p, li { white-space: pre-wrap; } - 停止采集 + 开始预览 - - - - - 1 - 0 - + + + + Qt::Vertical - + - 0 - 30 + 20 + 40 - - 开始采集 - - + - - + + 1 @@ -1334,7 +1343,7 @@ p, li { white-space: pre-wrap; } - 开始预览 + 停止预览 @@ -2167,7 +2176,7 @@ p, li { white-space: pre-wrap; } - 数据采样周期 + 数据采样周期(ms) @@ -2364,7 +2373,7 @@ p, li { white-space: pre-wrap; } - iflytop + 心电上位机V2 diff --git a/src/electrocardiograph_tester.cpp b/src/electrocardiograph_tester.cpp index 639f36f..d2717f2 100644 --- a/src/electrocardiograph_tester.cpp +++ b/src/electrocardiograph_tester.cpp @@ -375,28 +375,30 @@ void ElectrocardiographTester::reset() { void ElectrocardiographTester::testCmdStartCapture() { lock_guard lock(m_tx_lock); - - m_txcmd->cmd = ify_hrs_test_cmd_start_capture; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); + throw zexception(kifyhrs_ecode_cmd_not_support, "not implement"); } void ElectrocardiographTester::testCmdStopCapture() { lock_guard lock(m_tx_lock); - m_txcmd->cmd = ify_hrs_test_cmd_stop_capture; - sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); + throw zexception(kifyhrs_ecode_cmd_not_support, "not implement"); } uint8_t ElectrocardiographTester::testCmdReadReg(uint8_t addr) { lock_guard lock(m_tx_lock); + throw zexception(kifyhrs_ecode_cmd_not_support, "not implement"); +#if 0 m_txcmd->cmd = ify_hrs_test_cmd_read_reg; m_txcmd->data[0] = addr; m_txcmd->cmd = ify_hrs_test_cmd_read_reg; sendCmd(m_txcmd, sizeof(ify_hrs_packet_t) + 1, m_rxcmd, &m_rxsize, 100); return m_rxcmd->data[0]; +#endif } void ElectrocardiographTester::testCmdWriteReg(uint8_t addr, uint8_t val) { lock_guard lock(m_tx_lock); + throw zexception(kifyhrs_ecode_cmd_not_support, "not implement"); +#if 0 m_txcmd->cmd = ify_hrs_test_cmd_write_reg; m_txcmd->data[0] = addr; @@ -404,4 +406,17 @@ void ElectrocardiographTester::testCmdWriteReg(uint8_t addr, uint8_t val) { m_txcmd->cmd = ify_hrs_test_cmd_write_reg; sendCmd(m_txcmd, sizeof(ify_hrs_packet_t) + 2, m_rxcmd, &m_rxsize, 100); +#endif +} + +void ElectrocardiographTester::set_ecg_in_test_mode(int32_t testmode) { + lock_guard lock(m_tx_lock); + + + int32_t *para = (int32_t *)m_txcmd->data; + para[0] = testmode; + + m_txcmd->cmd = ify_hrs_cmd_set_ecg_in_test_mode; + sendCmd(m_txcmd, sizeof(ify_hrs_packet_t) + sizeof(int32_t), m_rxcmd, &m_rxsize, 100); + return; } diff --git a/src/electrocardiograph_tester.hpp b/src/electrocardiograph_tester.hpp index bc5a524..fcf5ded 100644 --- a/src/electrocardiograph_tester.hpp +++ b/src/electrocardiograph_tester.hpp @@ -107,6 +107,8 @@ class ElectrocardiographTester { void readSn(string &sn); void reset(); // 重置设备 + void set_ecg_in_test_mode(int32_t testmode); + void testCmdStartCapture(); void testCmdStopCapture(); uint8_t testCmdReadReg(uint8_t addr);