diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 206d6ff..913ddf2 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -14,7 +14,7 @@ "cStandard": "c17", "cppStandard": "gnu++17", "intelliSenseMode": "windows-gcc-x64", - "compileCommands": "${workspaceFolder}/../build-camera_light_src_timing_controller_pc-Desktop_Qt_5_12_12_MinGW_64_bit-Debug/compile_commands.json" + "compileCommands": "${workspaceFolder}/build/compile_commands.json" } ], "version": 4 diff --git a/.vscode/settings.json b/.vscode/settings.json index e4c1bae..6fff06c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -87,7 +87,8 @@ "qserialportinfo": "cpp", "qmessagebox": "cpp", "qgridlayout": "cpp", - "qmenu": "cpp" + "qmenu": "cpp", + "qtconcurrent": "cpp" }, "files.autoGuessEncoding": false, } \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index dcb77d4..2996b55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,9 @@ set(PROJECT_SOURCES mainwindow.cpp mainwindow.h mainwindow.ui + + src/qt_serial_datachannel.cpp + src/electrocardiograph_tester.cpp ) diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user index 9784cd6..49a45c5 100644 --- a/CMakeLists.txt.user +++ b/CMakeLists.txt.user @@ -1,6 +1,6 @@ - + EnvironmentId @@ -8,7 +8,7 @@ ProjectExplorer.Project.ActiveTarget - 0 + 2 ProjectExplorer.Project.EditorSettings @@ -292,19 +292,500 @@ 2 - ProjectExplorer.CustomExecutableRunConfiguration - + electrocardiograph_upper + CMakeProjectManager.CMakeRunConfiguration.electrocardiograph_upper + electrocardiograph_upper false true + true false true + D:/workspace/nordic_wp/build-electrocardiograph_upper-unknown_279fbe-Debug + + 1 + + + + ProjectExplorer.Project.Target.1 + + Desktop + Desktop Qt 5.12.12 MSVC2017 64bit + Desktop Qt 5.12.12 MSVC2017 64bit + qt.qt5.51212.win64_msvc2017_64_kit + 0 + 0 + -1 + + Debug + -GMinGW Makefiles +-DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake +-DCMAKE_SYSROOT:PATH=C:/Qt/Qt5.12.12/Tools/mingw730_64 +-DCMAKE_C_COMPILER_TARGET:STRING=x86_64-w64-mingw32 +-DCMAKE_CXX_COMPILER_TARGET:STRING=x86_64-w64-mingw32 +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + D:\workspace\nordic_wp\build-electrocardiograph_upper-Desktop_Qt_5_12_12_MSVC2017_64bit-Debug + + + + all + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + Release + -GMinGW Makefiles +-DCMAKE_BUILD_TYPE:STRING=Release +-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake +-DCMAKE_SYSROOT:PATH=C:/Qt/Qt5.12.12/Tools/mingw730_64 +-DCMAKE_C_COMPILER_TARGET:STRING=x86_64-w64-mingw32 +-DCMAKE_CXX_COMPILER_TARGET:STRING=x86_64-w64-mingw32 +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + D:\workspace\nordic_wp\build-electrocardiograph_upper-Desktop_Qt_5_12_12_MSVC2017_64bit-Release + + + + all + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Release + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + -GMinGW Makefiles +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake +-DCMAKE_SYSROOT:PATH=C:/Qt/Qt5.12.12/Tools/mingw730_64 +-DCMAKE_C_COMPILER_TARGET:STRING=x86_64-w64-mingw32 +-DCMAKE_CXX_COMPILER_TARGET:STRING=x86_64-w64-mingw32 +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + D:\workspace\nordic_wp\build-electrocardiograph_upper-Desktop_Qt_5_12_12_MSVC2017_64bit-RelWithDebInfo + + + + all + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + + MinSizeRel + -GMinGW Makefiles +-DCMAKE_BUILD_TYPE:STRING=MinSizeRel +-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake +-DCMAKE_SYSROOT:PATH=C:/Qt/Qt5.12.12/Tools/mingw730_64 +-DCMAKE_C_COMPILER_TARGET:STRING=x86_64-w64-mingw32 +-DCMAKE_CXX_COMPILER_TARGET:STRING=x86_64-w64-mingw32 +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + D:\workspace\nordic_wp\build-electrocardiograph_upper-Desktop_Qt_5_12_12_MSVC2017_64bit-MinSizeRel + + + + all + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Minimum Size Release + CMakeProjectManager.CMakeBuildConfiguration + + 4 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + 0 + + + + ProjectExplorer.Project.Target.2 + + Desktop + Desktop Qt 5.12.12 MinGW 64-bit + Desktop Qt 5.12.12 MinGW 64-bit + qt.qt5.51212.win64_mingw73_kit + 4 + 0 + 0 + + Release + -GMinGW Makefiles +-DCMAKE_BUILD_TYPE:STRING=Release +-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + D:\workspace\nordic_wp\build-electrocardiograph_upper-Desktop_Qt_5_12_12_MinGW_64_bit-Release + + + + all + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Release + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + -GMinGW Makefiles +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + D:\workspace\nordic_wp\build-electrocardiograph_upper-Desktop_Qt_5_12_12_MinGW_64_bit-RelWithDebInfo + + + + all + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + + MinSizeRel + -GMinGW Makefiles +-DCMAKE_BUILD_TYPE:STRING=MinSizeRel +-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + D:\workspace\nordic_wp\build-electrocardiograph_upper-Desktop_Qt_5_12_12_MinGW_64_bit-MinSizeRel + + + + all + + true + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Minimum Size Release + CMakeProjectManager.CMakeBuildConfiguration + + + Debug + -GMinGW Makefiles +-DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + D:\workspace\nordic_wp\build-electrocardiograph_upper-Desktop_Qt_5_12_12_MinGW_64_bit-Debug + + + + all + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Debug2 + CMakeProjectManager.CMakeBuildConfiguration + + + Debug + -GMinGW Makefiles +-DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PROJECT_INCLUDE_BEFORE:PATH=%{IDE:ResourcePath}/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:STRING=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:STRING=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:STRING=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:STRING=%{Compiler:Executable:Cxx} + D:/workspace/nordic_wp/electrocardiograph_upper/build + + + + all + + true + Build + CMakeProjectManager.MakeStep + + 1 + Build + Build + ProjectExplorer.BuildSteps.Build + + + + + clean + + true + Build + CMakeProjectManager.MakeStep + + 1 + Clean + Clean + ProjectExplorer.BuildSteps.Clean + + 2 + false + + + Debug + CMakeProjectManager.CMakeBuildConfiguration + + 5 + + + 0 + Deploy + Deploy + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + true + + 2 + + electrocardiograph_upper + CMakeProjectManager.CMakeRunConfiguration.electrocardiograph_upper + electrocardiograph_upper + false + true + true + false + true + D:/workspace/nordic_wp/electrocardiograph_upper/build 1 ProjectExplorer.Project.TargetCount - 1 + 3 ProjectExplorer.Project.Updater.FileVersion diff --git a/libzqt/logger.cpp b/libzqt/logger.cpp index b9cef92..328798a 100644 --- a/libzqt/logger.cpp +++ b/libzqt/logger.cpp @@ -2,16 +2,26 @@ #include #include -// #include +#include #include -void zos_log(const char *fmt, ...) { +void zos_log(const char* fmt, ...) { va_list args; va_start(args, fmt); char buf[1024] = {0}; vsnprintf(buf, sizeof(buf), fmt, args); - // qInfo() << buf; + qInfo() << buf; va_end(args); } int32_t zos_get_ticket() { return (int32_t)QDateTime::currentMSecsSinceEpoch(); } + +std::string zhex2str(const uint8_t* hex, size_t len) { + std::string str; + for (size_t i = 0; i < len; i++) { + char buf[3] = {0}; + snprintf(buf, sizeof(buf), "%02x ", hex[i]); + str += buf; + } + return str; +} diff --git a/libzqt/logger.hpp b/libzqt/logger.hpp index b7bac68..45a2e4d 100644 --- a/libzqt/logger.hpp +++ b/libzqt/logger.hpp @@ -2,9 +2,13 @@ #pragma once #include -void zos_log(const char *fmt, ...); +#include + +void zos_log(const char* fmt, ...); int32_t zos_get_ticket(); +std::string zhex2str(const uint8_t* hex, size_t len); + #define ZLOGI(TAG, fmt, ...) zos_log("%08lu INFO [%-10s] " fmt "", zos_get_ticket(), TAG, ##__VA_ARGS__); #define ZLOGD(TAG, fmt, ...) zos_log("%08lu DEBU [%-10s] " fmt "", zos_get_ticket(), TAG, ##__VA_ARGS__); #define ZLOGE(TAG, fmt, ...) zos_log("%08lu ERRO [%-10s] " fmt "", zos_get_ticket(), TAG, ##__VA_ARGS__); @@ -14,4 +18,3 @@ int32_t zos_get_ticket(); ZLOGE("ASSERT", "condition: %s", #cond); \ exit(-1); \ } - diff --git a/libzqt/zexception.hpp b/libzqt/zexception.hpp new file mode 100644 index 0000000..2d88e7b --- /dev/null +++ b/libzqt/zexception.hpp @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace std _GLIBCXX_VISIBILITY(default) { + +class zexception : public exception { + string m_ecodeinfo = ""; + int32_t m_ecode = 0; + + public: + /** Takes a character string describing the error. */ + explicit zexception(int32_t ecode, const string& __arg) { + m_ecodeinfo = __arg; + m_ecode = ecode; + } + + const char* what() const noexcept override { return m_ecodeinfo.c_str(); } + int32_t ecode() const noexcept { return m_ecode; } +}; +} // namespace std _GLIBCXX_VISIBILITY(default) diff --git a/mainwindow.cpp b/mainwindow.cpp index f3e03e0..9a14bea 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -7,16 +7,17 @@ #include #include "./ui_mainwindow.h" -#include "camera_light_src_timing_controller/qt_serial_datachannel.hpp" +#include "electrocardiograph_tester.hpp" #include "logger.hpp" +#include "qt_serial_datachannel.hpp" +#include "zexception.hpp" -using namespace iflytop; -using namespace clst; using namespace std; +using namespace iflytop; + +static MainWindow *m_mainWindow; +static QTDataChannel G_QTDataChannel; -static MainWindow *m_mainWindow; -static CLSTControler *m_clstc; -ZQThread *m_zqthread; #define TAG "MainWindow" static const char *fmt(const char *fmt, ...) { @@ -33,53 +34,61 @@ void MainWindow::doinui_slot(QFunction func) { if (func.get()) func.get()(); } -void MainWindow::dumpLog(const char *fmt, ...) { +void MainWindow::instructionPreviewShow(const char *fmt, ...) { va_list args; va_start(args, fmt); char buf[1024] = {0}; vsnprintf(buf, sizeof(buf), fmt, args); - // qDebug() << buf; va_end(args); QString text(buf); - QString info; - // zos_get_ticket - info.append(QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz")); - info.append(" [Info]:"); + info.append(QDateTime::currentDateTime().toString("hh:mm:ss.zzz")); + info.append(" |"); info.append(text); + emit doinui_signal(QFunction([this, info]() { ui->instructionPreview->append(info); })); +} - static int id; +void MainWindow::instructionPreviewClear() { ui->instructionPreview->document()->clear(); } - // informationBrowser +void MainWindow::reportPreviewShow(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + char buf[1024] = {0}; + vsnprintf(buf, sizeof(buf), fmt, args); + va_end(args); + QString text(buf); + QString info; + info.append(QDateTime::currentDateTime().toString("hh:mm:ss.zzz")); + info.append(text); emit doinui_signal(QFunction([this, info]() { - if (ui->instructionPreview->document()->lineCount() > 100) { - ui->instructionPreview->document()->clear(); + if (ui->reportPreview->document()->lineCount() > 100) { + ui->reportPreview->document()->clear(); } - ui->instructionPreview->append(info); + ui->reportPreview->append(info); })); } void MainWindow::displayInfo(bool suc, QString info) { - emit doinui_signal(QFunction([this, info, suc]() { - static int cnt = 0; - cnt++; - - QString _info; - _info.append("["); - _info.append(QString::number(cnt)); - _info.append("] "); - _info.append(info); - - if (suc) { - ui->informationBrowser->setStyleSheet("color: green"); - } else { - ui->informationBrowser->setStyleSheet("color: red"); - } - - ui->informationBrowser->setText(_info); - })); + // emit doinui_signal(QFunction([this, info, suc]() { + // static int cnt = 0; + // cnt++; + + // QString _info; + // _info.append("["); + // _info.append(QString::number(cnt)); + // _info.append("] "); + // _info.append(info); + + // if (suc) { + // ui->informationBrowser->setStyleSheet("color: green"); + // } else { + // ui->informationBrowser->setStyleSheet("color: red"); + // } + + // ui->informationBrowser->setText(_info); + // })); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { @@ -94,16 +103,102 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi qRegisterMetaType>("function"); qRegisterMetaType("QFunction"); connect(this, SIGNAL(doinui_signal(QFunction)), this, SLOT(doinui_slot(QFunction))); + qInstallMessageHandler(log_output); /******************************************************************************* * 页面逻辑初始化 * *******************************************************************************/ constructUI(); + + /******************************************************************************* + * 业务逻辑构造 * + *******************************************************************************/ + G_QTDataChannel.init(); + ElectrocardiographTester::ins()->initialize(&G_QTDataChannel); } MainWindow::~MainWindow() { delete ui; } /******************************************************************************* * UI相关构造 * *******************************************************************************/ +void MainWindow::constructUI() { + /******************************************************************************* + * serialPortCB * + *******************************************************************************/ + const auto infos = QSerialPortInfo::availablePorts(); + for (const QSerialPortInfo &info : infos) { + ui->serialPortCB->addItem(info.portName()); + } + + /******************************************************************************* + * 波特率填?? * + *******************************************************************************/ + ui->serialBaudrateCB->addItem("9600"); + ui->serialBaudrateCB->addItem("14400"); + ui->serialBaudrateCB->addItem("19200"); + ui->serialBaudrateCB->addItem("38400"); + ui->serialBaudrateCB->addItem("57600"); + ui->serialBaudrateCB->addItem("115200"); + ui->serialBaudrateCB->addItem("460800"); + ui->serialBaudrateCB->addItem("500000"); + ui->serialBaudrateCB->setCurrentIndex(6); + + /******************************************************************************* + * 刷新串口 * + *******************************************************************************/ + connect(ui->serialPortRefreshKey, &QPushButton::clicked, this, [this](bool check) { + ui->serialPortCB->clear(); + const auto infos = QSerialPortInfo::availablePorts(); + for (const QSerialPortInfo &info : infos) { + ui->serialPortCB->addItem(info.portName()); + } + }); -void MainWindow::constructUI() {} + /******************************************************************************* + * 打开串口 * + *******************************************************************************/ + connect(ui->serialOpenKey, &QPushButton::clicked, this, [=](bool check) { + // 打开串口 + if (ui->serialOpenKey->text() == "打开") { + G_QTDataChannel.setPortName(ui->serialPortCB->currentText().toStdString()); + G_QTDataChannel.setBaudRate(ui->serialBaudrateCB->currentText().toInt()); + G_QTDataChannel.setDataBits(QSerialPort::Data8); + G_QTDataChannel.setParity(QSerialPort::NoParity); + G_QTDataChannel.setFlowControl(QSerialPort::NoFlowControl); + G_QTDataChannel.setStopBits(QSerialPort::OneStop); + + if (!G_QTDataChannel.open()) { + QMessageBox::about(NULL, "提示", "串口无法打开,串口不存在或已被占??"); + return; + } + ui->serialOpenKey->setText("关闭"); + // 下拉菜单控件使能 + ui->serialBaudrateCB->setEnabled(false); + ui->serialPortCB->setEnabled(false); + ui->serialPortRefreshKey->setEnabled(false); + } else { + G_QTDataChannel.close(); + ui->serialOpenKey->setText("打开"); + ui->serialBaudrateCB->setEnabled(true); + ui->serialPortCB->setEnabled(true); + ui->serialPortRefreshKey->setEnabled(true); + } + }); +} + +void MainWindow::processException(zexception &e) { instructionPreviewShow("%s:%d", e.what(), e.ecode()); } + +void MainWindow::on_readDeviceVersion_clicked() { + instructionPreviewClear(); + try { + device_version_info_receipt_t version; + ElectrocardiographTester::ins()->readDeviceVersion(&version); + instructionPreviewShow("-------------- version ----------------"); + instructionPreviewShow(" blestack_version: %d", version.blestack_version); + instructionPreviewShow(" bootloader_version: %d", version.bootloader_version); + instructionPreviewShow(" firmware_version: %d", version.firmware_version); + instructionPreviewShow(" hardware_version: %d", version.hardware_version); + } catch (zexception &exception) { + processException(exception); + } +} diff --git a/mainwindow.h b/mainwindow.h index f8084c5..0373a89 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -28,10 +28,9 @@ #include #include -// -#include "camera_light_src_timing_controller/clst_controler.hpp" #include "logger.hpp" #include "qfunction.hpp" +#include "zexception.hpp" #include "zqthread.hpp" QT_BEGIN_NAMESPACE @@ -78,12 +77,19 @@ class MainWindow : public QMainWindow { private slots: void doinui_slot(QFunction); + void on_readDeviceVersion_clicked(); + signals: void doinui_signal(QFunction); private: static void log_output(QtMsgType type, const QMessageLogContext &context, const QString &msg); - void dumpLog(const char *fmt, ...); + void instructionPreviewShow(const char *fmt, ...); + void instructionPreviewClear(); + void reportPreviewShow(const char *fmt, ...); void displayInfo(bool suc, QString info); + + private: + void processException(std::zexception &e); }; #endif // MAINWINDOW_H diff --git a/mainwindow.ui b/mainwindow.ui index a6457bf..f9e9206 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -739,40 +739,6 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - - - 信息栏 - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - false - - - - - - - @@ -783,4234 +749,155 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { 功能按键 - - - - - 刷新页面 - - - - - - - 提交配置 - - - - - - - 保存配置 - - - - - - - 重启设备 - - - - - - - 恢复出厂设置 - - - - - - - 触发(内部) - - - - - - - 停止(内部) - - - - + - + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + 0 + 0 + + + + + + + + 0 + 0 + + 0 - 0 + 300 - - 寄存器操作 + + + 16777215 + 300 + - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 读数值 - - - - - + + + - + 0 0 - 0 - 25 + 400 + 0 - - - 16777215 - 25 - - - - 地址 - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 写数值 - - - - - + + - + 0 0 - 0 - 25 - - - - - 16777215 - 25 + 400 + 0 - - 2 - - - - - - - 读寄存器 - - - - - - - 写寄存器 - - - - - - 0 - 25 - + + + + Qt::Horizontal - - - - - + - 0 - 25 + 40 + 20 - + - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - + - + 0 - 10 + 0 - - 0 + + + 0 + 500 + - - true + + + 16777215 + 500 + - - - 外部触发源配置 - - - - - - - 400 - 16777215 - - - - 外部触发3 - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发源选择 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 滤波系数(x10ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 模式 - - - - - - - - 0 - 120 - - - - - 16777215 - 120 - - - - TRIGGER_MODE配置 - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 倍频因子 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发边沿 - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 分频因子 - - - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输出频率探测(HZ) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 频率探测允许误差(x10ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输入频率探测(HZ) - - - - - - - - 0 - 25 - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 顺序触发最大计数 - - - - - - - - 0 - 25 - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 400 - 16777215 - - - - 外部触发2 - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发源选择 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输出频率探测(HZ) - - - - - - - - 0 - 120 - - - - - 16777215 - 120 - - - - TRIGGER_MODE配置 - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 倍频因子 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发边沿 - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 分频因子 - - - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 频率探测允许误差(x10ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输入频率探测(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 滤波系数(x10ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 模式 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 顺序触发最大计数 - - - - - - - - 0 - 25 - - - - - - - - - - - - 400 - 16777215 - - - - 外部触发1 - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 模式 - - - - - - - - 0 - 25 - - - - - - - - - 0 - 120 - - - - - 16777215 - 120 - - - - TRIGGER_MODE配置 - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 倍频因子 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发边沿 - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 分频因子 - - - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发源选择 - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 滤波系数(x10ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 频率探测允许误差(x10ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输出频率探测(HZ) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输入频率探测(HZ) - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 顺序触发最大计数 - - - - - - - - 0 - 25 - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 400 - 16777215 - - - - 外部触发4 - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 频率探测允许误差(x10ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发源选择 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 模式 - - - - - - - - 0 - 120 - - - - - 16777215 - 120 - - - - TRIGGER_MODE配置 - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 倍频因子 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发边沿 - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 分频因子 - - - - - - - - - - - 0 - 25 - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 滤波系数(x10ns) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输入频率探测(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输出频率探测(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 顺序触发最大计数 - - - - - - - - 0 - 25 - - - - - - - - - - - - 内部触发源配置 - - - - - 20 - 10 - 300 - 151 - - - - - 300 - 200 - - - - 内部触发源配置 - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发时钟(HZ) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发脉冲数量(0等于无限) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 顺序触发模式最大计数 - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输出频率探测(HZ) - - - - - - - - 0 - 25 - - - - - - - - - - 光源时序配置 - - - - - - - 400 - 16777215 - - - - 光源3 - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输入触发频率(HZ) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发源 - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 脉冲偏移(100ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 脉冲宽度(100ns) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源亮度(%) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源驱动器状态 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源驱动频率(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输出频率(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 400 - 16777215 - - - - 光源2 - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输入触发频率(HZ) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发源 - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 脉冲偏移(100ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 脉冲宽度(100ns) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源亮度(%) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源驱动器状态 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源驱动频率(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输出频率(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - - - - 400 - 16777215 - - - - 光源1 - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输入触发频率(HZ) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发源 - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 脉冲偏移(100ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 脉冲宽度(100ns) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源亮度(%) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源驱动器状态 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源驱动频率(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输出频率(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 400 - 16777215 - - - - 光源4 - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输入触发频率(HZ) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发源 - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 脉冲偏移(100ns) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 脉冲宽度(100ns) - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源亮度(%) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源驱动器状态 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 光源驱动频率(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 输出频率(HZ) - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - 0 - 25 - - - - - - - - - - - - 快门时序配置 - - - - - - - 400 - 16777215 - - - - 快门输出2 - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发模式 - - - - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 60 - - - - - - - - - 0 - 0 - - - - - 16777215 - 120 - - - - BIND模式配置 - - - - - - - 0 - 25 - - - - - - - - 光源2 - - - - - - - 光源3 - - - - - - - 光源4 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发延迟(100ns) - - - - - - - 光源1 - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 120 - - - - TRANSPARENT模式配置 - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 转发信号选择 - - - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 25 - - - - - - - - 输出极性翻转 - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 400 - 16777215 - - - - 快门输出4 - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发模式 - - - - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 60 - - - - - - - - - 0 - 0 - - - - - 16777215 - 120 - - - - BIND模式配置 - - - - - - - 0 - 25 - - - - - - - - 光源2 - - - - - - - 光源3 - - - - - - - 光源4 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发延迟(100ns) - - - - - - - 光源1 - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 120 - - - - TRANSPARENT模式配置 - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 转发信号选择 - - - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 25 - - - - - - - - 输出极性翻转 - - - - - - - - - - - 400 - 16777215 - - - - 快门输出1 - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发模式 - - - - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 60 - - - - - - - - - 0 - 0 - - - - - 16777215 - 120 - - - - BIND模式配置 - - - - - - - 0 - 25 - - - - - - - - 光源2 - - - - - - - 光源3 - - - - - - - 光源4 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发延迟(100ns) - - - - - - - 光源1 - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 120 - - - - TRANSPARENT模式配置 - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 转发信号选择 - - - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 25 - - - - - - - - 输出极性翻转 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - 400 - 16777215 - - - - 快门输出3 - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发模式 - - - - - - - Qt::Horizontal - - - - - - - Qt::Vertical - - - - 20 - 60 - - - - - - - - - 0 - 0 - - - - - 16777215 - 120 - - - - BIND模式配置 - - - - - - - 0 - 25 - - - - - - - - 光源2 - - - - - - - 光源3 - - - - - - - 光源4 - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 触发延迟(100ns) - - - - - - - 光源1 - - - - - - - - - - - 0 - 0 - - - - - 16777215 - 120 - - - - TRANSPARENT模式配置 - - - - - - - 0 - 25 - - - - - - - - - 0 - 0 - - - - - 0 - 25 - - - - - 16777215 - 25 - - - - 转发信号选择 - - - - - - - - - - Qt::Horizontal - - - - - - - - 0 - 25 - - - - - - - - 输出极性翻转 - - - - - - - + + + + 30 + 70 + 102 + 18 + + + + 读取设备版本信息 + - - - - 0 - 1 - + + + Qt::Vertical - + - 0 - 100 + 20 + 40 - + - content - sidebar diff --git a/src/electrocardiograph_tester.cpp b/src/electrocardiograph_tester.cpp new file mode 100644 index 0000000..ec6c099 --- /dev/null +++ b/src/electrocardiograph_tester.cpp @@ -0,0 +1,206 @@ +#include "electrocardiograph_tester.hpp" + +#include +#include + +#include "logger.hpp" +#include "zexception.hpp" +using namespace iflytop; + +#define TAG "ElectrocardiographTester" + +/******************************************************************************* + * UTILS_BEGIN * + *******************************************************************************/ + +static bool isHeader(uint8_t *data, uint8_t *headerType) { + if (data[0] == 0x5A && data[1] == 0xA5) { + *headerType = 1; + return true; + } + + if (data[0] == 0x4A && data[1] == 0xA4) { + *headerType = 2; + return true; + } + + return false; +} + +static bool isTail(uint8_t headerType, uint8_t *data) { + if (headerType == 1 && data[0] == 0x5B && data[1] == 0xB5) { + return true; + } + + if (headerType == 2 && data[0] == 0x4B && data[1] == 0xB4) { + return true; + } + + return false; +} +/******************************************************************************* + * UTILS_END * + *******************************************************************************/ +ElectrocardiographTester *ElectrocardiographTester::ins() { + static ElectrocardiographTester *ins = nullptr; + if (ins == nullptr) { + ins = new ElectrocardiographTester(); + } + return ins; +} +void ElectrocardiographTester::initialize(IDataChannel *channel) { // + m_channel = channel; + m_channel->regRxListener([this](uint8_t *data, size_t len) { + { + lock_guard lock(lock_); + if (len + m_rxlen > sizeof(m_rxcache)) { + m_rxlen = 0; + } + memcpy(m_rxcache + m_rxlen, data, len); + m_rxlen += len; + } + }); + m_thread.reset(new thread([this]() { + while (true) { + this_thread::sleep_for(chrono::milliseconds(2)); + + { + lock_guard lock(lock_); + // 1.找头部 + int32_t headerpos = -1; + uint8_t headerType = 0; + for (int32_t i = 0; i < m_rxlen - 1; i++) { + if (isHeader(m_rxcache + i, &headerType)) { + headerpos = i; + break; + } + } + + if (headerpos == -1) { + continue; + } + + // 2.找尾 + int32_t tailpos = -1; + for (int32_t i = headerpos + 2; i < m_rxlen - 1; i++) { + if (isTail(headerType, m_rxcache + i)) { + tailpos = i; + break; + } + } + + if (tailpos == -1) { + continue; + } + + // 3.处理数据 + + int32_t datalen = tailpos - headerpos + 2; + uint8_t *packet = &m_rxcache[headerpos]; + + if (packet[0] == 0x5A && packet[1] == 0xA5) { + processCh3RxData(packet, datalen); + } else { + processCh4RxData(packet, datalen); + } + + // 4. 移除已经处理的数据 + int32_t leftlen = m_rxlen - tailpos - 2; + if (leftlen > 0) { + memmove(m_rxcache, m_rxcache + tailpos + 2, leftlen); + } + m_rxlen = leftlen; + } + } + })); +} + +void ElectrocardiographTester::regReportCB(on_report_t cb) { m_on_report = cb; } +void ElectrocardiographTester::processCh4RxData(uint8_t *rx, int32_t rxlen) { + ZLOGI(TAG, "rx ch4: %s", zhex2str(rx, rxlen).c_str()); + return; +} +void ElectrocardiographTester::processCh3RxData(uint8_t *rx, int32_t rxlen) { + ify_hrs_packet_t *rxcmd = (ify_hrs_packet_t *)&rx[2]; + if (rxcmd->frame_type == kifyhrs_pt_cmd_receipt || rxcmd->frame_type == kifyhrs_pt_error_receipt) { + if (rxcmd->frame_index == m_rxReceiptContext.waittingIndex) { + lock_guard lock(m_rxReceiptContext_lock); + m_rxReceiptContext.receiptIsReady = true; + m_rxReceiptContext.receiptLen = rxlen; + memcpy(m_rxReceiptContext.receipt, &rx[2], rxlen - 2); + } else { + ZLOGE(TAG, "Rx index not match, %s", zhex2str(rx, rxlen).c_str()); + return; + } + } else if (rxcmd->frame_type == kifyhrs_pt_report) { + processRxReportPacket(rxcmd, rxlen); + } +} +void ElectrocardiographTester::processRxReportPacket(ify_hrs_packet_t *rx, int32_t rxlen) { + if (m_on_report == nullptr) { + return; + } + m_on_report(rx, rxlen); +} + +void ElectrocardiographTester::readDeviceVersion(device_version_info_receipt_t *version) { + lock_guard lock(m_tx_lock); + + m_txcmd->cmd = ify_hrs_cmd_read_device_version; + sendCmd(m_txcmd, sizeof(ify_hrs_packet_t), m_rxcmd, &m_rxsize, 100); + + device_version_info_receipt_t *receipt = (device_version_info_receipt_t *)m_rxcmd->data; + memcpy(version, receipt, sizeof(device_version_info_receipt_t)); + return; +} + +void ElectrocardiographTester::sendCmd(ify_hrs_packet_t *tx, int32_t txlen, ify_hrs_packet_t *rx, int32_t *rxlen, int32_t overtime) { + if (m_channel == nullptr || m_channel->isOpen() == false) { + throw zexception(kifyhrs_ecode_channle_is_close, "channel is not open"); + } + + uint8_t txindex = m_txindex++; + + ify_hrs_packet_t *txcmd = (ify_hrs_packet_t *)tx; + txcmd->frame_index = txindex; + txcmd->frame_type = kifyhrs_pt_cmd; + + { + lock_guard lock(m_rxReceiptContext_lock); + m_rxReceiptContext.waittingIndex = txindex; + m_rxReceiptContext.waittingForReceipt = true; + m_rxReceiptContext.receiptIsReady = false; + m_rxReceiptContext.receiptLen = 0; + } + + m_channel->send((uint8_t *)tx, txlen); + bool rxreceipt = false; + + for (int32_t i = 0; i < overtime; i++) { + { + lock_guard lock(m_rxReceiptContext_lock); + /** + * @brief 接收到回执 + */ + if (m_rxReceiptContext.receiptIsReady) { + memcpy(rx, m_rxReceiptContext.receipt, m_rxReceiptContext.receiptLen); + *rxlen = m_rxReceiptContext.receiptLen; + rxreceipt = true; + break; + } + } + this_thread::sleep_for(chrono::milliseconds(1)); + } + + if (!rxreceipt) { + throw zexception(kifyhrs_ecode_overtime, "overtime"); + } + + ify_hrs_packet_t *rxcmd = (ify_hrs_packet_t *)rx; + if (rxcmd->frame_type == kifyhrs_pt_error_receipt) { + error_receipt_t *receipt = (error_receipt_t *)rxcmd->data; + throw zexception(receipt->errorcode, "ecode"); + } + + return; +} diff --git a/src/electrocardiograph_tester.hpp b/src/electrocardiograph_tester.hpp new file mode 100644 index 0000000..f16765f --- /dev/null +++ b/src/electrocardiograph_tester.hpp @@ -0,0 +1,95 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "electrocardiograph_tester.hpp" +#include "heart_rate_sensor_protocol.h" + +#define SDK_VERSION 1 + +namespace iflytop { +using namespace std; + +typedef enum { + kuart_raw_tx, + kuart_raw_rx, +} uart_message_type_t; + +typedef function on_report_t; + +class IDataChannel { + public: + virtual ~IDataChannel(){}; + virtual bool isOpen() = 0; + virtual bool send(const uint8_t *data, size_t len) = 0; + virtual void regRxListener(function cb) = 0; +}; + +class RxReceiptContext { + public: + bool waittingForReceipt; + bool receiptIsReady; + uint16_t waittingIndex; + uint8_t receipt[1024]; + size_t receiptLen; +}; + +class ElectrocardiographTester { + ElectrocardiographTester() {} + + IDataChannel *m_channel = nullptr; + + uint8_t m_rxcache[1024]; + int32_t m_rxlen = 0; + bool m_rxcache_is_full = false; + + mutex lock_; + + unique_ptr m_thread; + + RxReceiptContext m_rxReceiptContext; + mutex m_rxReceiptContext_lock; + + /******************************************************************************* + * TX CONTEXT * + *******************************************************************************/ + mutex m_tx_lock; + uint8_t m_txbuf[1024]; + uint8_t m_rxbuf[1024]; + ify_hrs_packet_t *m_txcmd = (ify_hrs_packet_t *)m_txbuf; + ify_hrs_packet_t *m_rxcmd = (ify_hrs_packet_t *)m_rxbuf; + int32_t m_rxsize; + uint8_t m_txindex = 0; + + /******************************************************************************* + * ReportCB * + *******************************************************************************/ + on_report_t m_on_report; + + public: + static ElectrocardiographTester *ins(); + + void initialize(IDataChannel *channel); + void regReportCB(on_report_t cb); + + public: + void readDeviceVersion(device_version_info_receipt_t *version); + + private: + void sendCmd(ify_hrs_packet_t *tx, int32_t txlen, ify_hrs_packet_t *rx, int32_t *rxlen, int32_t overtime); + void processCh3RxData(uint8_t *rx, int32_t rxlen); // 指令通道 + void processCh4RxData(uint8_t *rx, int32_t rxlen); // 心电原始数据通道 + void processRxReportPacket(ify_hrs_packet_t *rx, int32_t rxlen); +}; + +} // namespace iflytop diff --git a/src/heart_rate_sensor_protocol.h b/src/heart_rate_sensor_protocol.h new file mode 100644 index 0000000..7d16d6d --- /dev/null +++ b/src/heart_rate_sensor_protocol.h @@ -0,0 +1,202 @@ +#include + +#ifndef HEART_RATE_SENSOR_PROTOCOL_H +#define HEART_RATE_SENSOR_PROTOCOL_H +#ifdef __cplusplus +extern "C" { +#endif + +#pragma pack(push, 1) + +typedef struct { + uint8_t frame_type; + uint8_t frame_index; + uint8_t cmd; + uint8_t data[]; +} ify_hrs_packet_t; + +typedef enum { + kifyhrs_ecode_success = 0, + kifyhrs_ecode_unkown_error = 1, + kifyhrs_ecode_cmd_not_support = 2, + kifyhrs_ecode_illegal_parameter = 3, + kifyhrs_ecode_device_busy = 4, + kifyhrs_ecode_hardware_error = 5, + kifyhrs_ecode_sensor_drop = 6, + kifyhrs_ecode_no_record_find = 7, + + kifyhrs_ecode_channle_is_close = 100, + kifyhrs_ecode_overtime = 101, + kifyhrs_ecode_logic_error = 102, + +} ify_hrs_error_code_t; + +typedef enum { + kifyhrs_pt_cmd = 1, + kifyhrs_pt_cmd_receipt = 2, + kifyhrs_pt_report = 3, + kifyhrs_pt_error_receipt = 4, +} ify_hrs_packet_type_t; + +typedef enum { + kifyhrs_sensor_pos_none = 0, // 无指定位置 + kifyhrs_sensor_pos_I = 1, // I + kifyhrs_sensor_pos_II = 2, // II + kifyhrs_sensor_pos_III = 3, // III + kifyhrs_sensor_pos_V1 = 4, // V1 + kifyhrs_sensor_pos_V2 = 5, // V2 + kifyhrs_sensor_pos_V3 = 6, // V3 + kifyhrs_sensor_pos_V4 = 7, // V4 + kifyhrs_sensor_pos_V5 = 8, // V5 + kifyhrs_sensor_pos_V6 = 9, // V6 + kifyhrs_sensor_pos_aVR = 10, // + kifyhrs_sensor_pos_aVL = 11, // + kifyhrs_sensor_pos_aVF = 12, // +} ify_hrs_sensor_pos_t; + +typedef enum { + ify_hrs_cmd_read_device_version = 1, + ify_hrs_cmd_read_sensor_info = 2, + ify_hrs_cmd_read_device_state = 3, + ify_hrs_cmd_read_time = 4, + ify_hrs_cmd_sync_time = 5, + ify_hrs_cmd_start_capture = 6, + ify_hrs_cmd_stop_capture = 7, + ify_hrs_cmd_start_realtime_report = 8, + ify_hrs_cmd_stop_realtime_report = 9, + ify_hrs_cmd_read_records_info = 10, + ify_hrs_cmd_del_record = 11, + ify_hrs_cmd_start_upload_record = 12, + ify_hrs_cmd_enter_ota = 13, + ify_hrs_cmd_read_sn = 14, + ify_hrs_cmd_reset = 15, + ify_hrs_cmd_stop_upload_record = 16, + ify_hrs_report_heartrate_data = 101, + ify_hrs_report_battery_level = 102, + ify_hrs_report_low_battey_level = 103, + ify_hrs_report_sample_finish_end = 104, + ify_hrs_report_sensor_drop_detect = 105, + ify_hrs_report_record_upload_end = 106, + +} ify_hrs_cmd_t; + +/******************************************************************************* + * packet_struct * + *******************************************************************************/ +typedef struct { + uint16_t placeholder; + uint16_t blestack_version; + uint16_t bootloader_version; + uint16_t firmware_version; + uint16_t hardware_version; +} device_version_info_receipt_t; + +typedef struct { + uint8_t sensor_num; // 数量 + uint8_t sensor_precision; // 精度 + uint8_t sensor_sample_rate; // 采样率 + uint8_t sensor0_pos; // 位置 + uint8_t sensor1_pos; // 位置 + uint8_t sensor2_pos; // 位置 +} sensor_info_receipt_t; + +typedef struct { + uint8_t drop_state0; + uint8_t drop_state1; + struct { + uint8_t sampling_state : 1; // 位置 + uint8_t report_state : 1; // 位置 + uint8_t low_battery : 1; // 位置 + uint8_t full_storge : 1; // 位置 + uint8_t holder : 4; // 位置 + } device_state0; + uint8_t device_state1; // 预留 + uint8_t powerlevel; // 电量 + uint8_t storage_item_num; // 记录存储条数 +} device_state_receipt_t; + +typedef struct { + uint8_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; +} read_time_receipt_t; + +typedef struct { + uint8_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; +} sync_time_cmd_t; + +typedef struct { + uint8_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; +} start_capture_cmd_t; + +typedef struct { + uint8_t record_index; // 最近第几条记录 +} read_record_info_cmd_t; + +typedef struct { + uint8_t record_id[6]; + uint32_t frameNum; + uint32_t dataSize; + uint8_t sensorNum; + uint8_t captureRate; // N*10HZ + uint8_t capturePrecision; + uint8_t compressAlgorithm; // 压缩算法 +} read_record_info_receipt_t; + +typedef struct { + uint8_t record_id[6]; +} del_record_cmd_t; + +typedef struct { + uint8_t record_id[6]; +} start_upload_record_cmd_t; + +typedef struct { + uint8_t sn[14]; +} read_sn_receipt_t; + +typedef struct { + uint8_t errorcode; +} error_receipt_t; + +/******************************************************************************* + * 上报相关结构体 * + *******************************************************************************/ + +typedef struct { + uint8_t frame_type; + uint8_t frame_index; + uint8_t cmd; + + uint32_t sample_data_index; + uint8_t data[]; // 上报的数据 +} heartrate_report_packet_t; + +typedef struct { + uint8_t frame_type; + uint8_t frame_index; + uint8_t cmd; + + uint8_t drop_state0; + uint8_t drop_state1; +} sensor_drop_event_report_packet_t; + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/qt_serial_datachannel.cpp b/src/qt_serial_datachannel.cpp new file mode 100644 index 0000000..b932aea --- /dev/null +++ b/src/qt_serial_datachannel.cpp @@ -0,0 +1,156 @@ +#include "qt_serial_datachannel.hpp" + +// + +#include +#include + +#pragma comment(lib, "ws2_32.lib") + +#include "logger.hpp" + +using namespace iflytop; +using namespace std; + +#define TAG "QTDataChannel" + +void QTDataChannel::init() { + m_thread.reset(new thread([this]() { + while (true) { + if (m_isOpen) { + uint8_t rx[1024] = {0}; + int rx_cnt = com_receive(rx, 1024); + if (rx_cnt != 0) { + // ZLOGI(TAG, "rx %s ", zhex2str(rx, rx_cnt).c_str()); + if (m_rxcb) m_rxcb(rx, rx_cnt); + } + this_thread::sleep_for(chrono::microseconds(100)); + } else { + this_thread::sleep_for(chrono::microseconds(10000)); + } + } + })); +} +bool QTDataChannel::open() { + char portnamebuf[256] = {0}; + sprintf(portnamebuf, "\\\\.\\%s", m_name.c_str()); + m_CommHandler = CreateFileA(portnamebuf, // port name + GENERIC_READ | GENERIC_WRITE, // Read/Write + 0, // No Sharing + NULL, // No Security + OPEN_EXISTING, // Open existing port only + 0, // Non Overlapped I/O + NULL); // Null for Comm Devices + if (m_CommHandler == INVALID_HANDLE_VALUE) { + ZLOGI(TAG, "Error in opening serial port"); + return false; + } + DCB p; + memset(&p, 0, sizeof(p)); + p.DCBlength = sizeof(p); + p.BaudRate = m_baudRate; // 波特率 + + switch (m_dataBits) { + case QSerialPort::Data5: + p.ByteSize = 5; + break; + case QSerialPort::Data6: + p.ByteSize = 6; + break; + case QSerialPort::Data7: + p.ByteSize = 7; + break; + case QSerialPort::Data8: + p.ByteSize = 8; + break; + default: + p.ByteSize = 8; + break; + } + + // QSerialPort::NoParity = 0, + // QSerialPort::EvenParity = 2, + // QSerialPort::OddParity = 3, + // QSerialPort::SpaceParity = 4, + // QSerialPort::MarkParity = 5, + // QSerialPort::UnknownParity = -1 + + switch (m_parity) // 校验位 + { + case QSerialPort::NoParity: + p.Parity = NOPARITY; // 无校验 + break; + case QSerialPort::EvenParity: + p.Parity = EVENPARITY; // 奇校验 + break; + case QSerialPort::OddParity: + p.Parity = ODDPARITY; // 偶校验 + break; + case QSerialPort::MarkParity: + p.Parity = MARKPARITY; // 标记校验 + break; + default: + p.Parity = NOPARITY; // 无校验 + } + + switch (m_stopBits) // 停止位 + { + case QSerialPort::OneStop: + p.StopBits = ONESTOPBIT; // 1位停止位 + break; + case QSerialPort::OneAndHalfStop: + p.StopBits = TWOSTOPBITS; // 2位停止位 + break; + case QSerialPort::TwoStop: + p.StopBits = ONE5STOPBITS; // 1.5位停止位 + break; + default: + p.StopBits = ONESTOPBIT; // 无校验 + } + + if (!SetCommState(m_CommHandler, &p)) { + // 设置参数失败 + CloseHandle(m_CommHandler); + return false; + } + m_isOpen = true; + return true; +} + +void QTDataChannel::close() { + CloseHandle(m_CommHandler); + m_isOpen = false; +} + +bool QTDataChannel::isOpen() { return m_isOpen; } +bool QTDataChannel::send(const uint8_t *data, size_t len) { + ZLOGI(TAG, "send %s", zhex2str(data, len).c_str()); + + DWORD dwBytesWrite = len; + BOOL bWriteStat = WriteFile(m_CommHandler, // 串口句柄 + (char *)data, // 数据首地址 + dwBytesWrite, // 要发送的数据字节数 + &dwBytesWrite, // DWORD*,用来接收返回成功发送的数据字节数 + NULL); // NULL为同步发送,OVERLAPPED*为异步发送 + return dwBytesWrite; +} +void QTDataChannel::regRxListener(function cb) { m_rxcb = cb; } + +int QTDataChannel::com_receive(uint8_t *rxbuf, int rxbufsize) { + COMMTIMEOUTS TimeOuts; + GetCommTimeouts(m_CommHandler, &TimeOuts); + TimeOuts.ReadIntervalTimeout = 0; // 读间隔超时 + TimeOuts.ReadTotalTimeoutMultiplier = 0; // 读时间系数 + TimeOuts.ReadTotalTimeoutConstant = 1; // 读时间常量 + SetCommTimeouts(m_CommHandler, &TimeOuts); + + // PurgeComm(m_CommHandler, PURGE_RXCLEAR); + + DWORD wCount = rxbufsize; // 成功读取的数据字节数 + BOOL bReadStat = ReadFile(m_CommHandler, // 串口句柄 + rxbuf, // 数据首地址 + wCount, // 要读取的数据最大字节数 + &wCount, // DWORD*,用来接收返回成功读取的数据字节数 + NULL); + return wCount; +} diff --git a/src/qt_serial_datachannel.hpp b/src/qt_serial_datachannel.hpp new file mode 100644 index 0000000..7d60f67 --- /dev/null +++ b/src/qt_serial_datachannel.hpp @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// + +#include +// +#include +// +#include +#include +// +#include "zqthread.hpp" +#include "electrocardiograph_tester.hpp" + +#define SDK_VERSION 1 + +namespace iflytop { +using namespace std; +// QT_CHARTS_USE_NAMESPACE + +typedef function device_state_cb_t; + +class QTDataChannel : public IDataChannel { + function m_rxcb; + + QSerialPort::DataBits m_dataBits; + QSerialPort::Parity m_parity; + QSerialPort::StopBits m_stopBits; + QSerialPort::FlowControl m_flowControl; + string m_name; + + unique_ptr m_thread; + + uint32_t m_baudRate; + HANDLE m_CommHandler; + + bool m_isOpen = false; + + public: + void init(); + + virtual bool isOpen() override; + virtual bool send(const uint8_t *data, size_t len) override; + virtual void regRxListener(function cb) override; + + bool open(); + void close(); + + void setBaudRate(qint32 baudRate) { m_baudRate = baudRate; } + qint32 baudRate() const { return m_baudRate; } + + void setPortName(string name) { m_name = name; } + void setDataBits(QSerialPort::DataBits dataBits) { m_dataBits = dataBits; } + void setParity(QSerialPort::Parity parity) { m_parity = parity; } + void setStopBits(QSerialPort::StopBits stopBits) { m_stopBits = stopBits; } + void setFlowControl(QSerialPort::FlowControl flowControl) { m_flowControl = flowControl; } + + QSerialPort::DataBits dataBits() const { return m_dataBits; } + QSerialPort::Parity parity() const { return m_parity; } + QSerialPort::StopBits stopBits() const { return m_stopBits; } + QSerialPort::FlowControl flowControl() const { return m_flowControl; } + + private: + int com_receive(uint8_t *rxbuf, int rxbufsize); +}; +} // namespace iflytop