|
|
#include "mainwindow.h"
#include <QDateTime>
#include <QMessageBox>
#include <QtConcurrent>
#include <QtSerialPort/QSerialPort>
#include <QtSerialPort/QSerialPortInfo>
#include "./ui_mainwindow.h"
#include "electrocardiograph_tester.hpp"
#include "logger.hpp"
#include "qt_serial_datachannel.hpp"
#include "widgetplot2d.h"
#include "zexception.hpp"
WidgetPlot2D *wp2d;
using namespace std; using namespace iflytop; typedef enum { kone_lead_ecg, kthree_lead_ecg, kone_lead_ecg_v2,
} device_type_t;
static MainWindow *m_mainWindow; static QTDataChannel G_QTDataChannel; static device_type_t m_devicetype;
#define TAG "MainWindow"
static const char *fmt(const char *fmt, ...) { va_list args; va_start(args, fmt); static char buf[1024] = {0}; vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); return buf; }
void MainWindow::log_output(QtMsgType type, const QMessageLogContext &context, const QString &msg) {} void MainWindow::doinui_slot(QFunction func) { if (func.get()) func.get()(); }
void MainWindow::instructionPreviewShow(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(" |"); info.append(text); emit doinui_signal(QFunction([this, info]() { ui->instructionPreview->append(info); })); }
void MainWindow::instructionPreviewClear() { ui->instructionPreview->document()->clear(); }
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->reportPreview->document()->lineCount() > 1000) { ui->reportPreview->document()->clear(); } ui->reportPreview->append(info); })); }
void MainWindow::blockDataUploadPreviewShow(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->uploadDataPreview->document()->lineCount() > 1000) { ui->uploadDataPreview->document()->clear(); } ui->uploadDataPreview->append(info); })); } void MainWindow::rawDataPreviewShow(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->rawDataPreview->document()->lineCount() > 1000) { ui->rawDataPreview->document()->clear(); } ui->rawDataPreview->append(info); })); } // uploadDataPreview
#pragma pack(push, 1)
typedef struct { uint16_t header; int16_t wave1; int16_t wave2; int16_t wave3; int16_t wave4; int16_t wave5; uint8_t check; uint16_t tail; } Wave_t; #pragma pack(pop)
void MainWindow::displayInfo(bool suc, QString info) {}
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { /*******************************************************************************
* QT初始化 * *******************************************************************************/ ui->setupUi(this); m_mainWindow = this; qRegisterMetaType<int32_t>("int32_t"); qRegisterMetaType<uint32_t>("uint32_t"); qRegisterMetaType<float>("float"); qRegisterMetaType<function<void()>>("function<void()>"); qRegisterMetaType<QFunction>("QFunction"); connect(this, SIGNAL(doinui_signal(QFunction)), this, SLOT(doinui_slot(QFunction))); // qInstallMessageHandler(log_output);
wp2d = new WidgetPlot2D(); wp2d->initGraphName(QStringList() << "心电");
/*******************************************************************************
* 页面逻辑初始化 * *******************************************************************************/ constructUI();
/*******************************************************************************
* 业务逻辑构造 * *******************************************************************************/ G_QTDataChannel.init(); ElectrocardiographTester::ins()->initialize(&G_QTDataChannel); } MainWindow::~MainWindow() { delete ui; }
/*******************************************************************************
* UI相关构造 * *******************************************************************************/ void MainWindow::constructUI() { /*******************************************************************************
* 指令串口UI * *******************************************************************************/ { 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()); } });
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); } });
/*******************************************************************************
* 波形串口UI * *******************************************************************************/ { ui->deviceType->addItem("单导联一代(M1001)"); ui->deviceType->addItem("三导联一代(M1002)"); ui->deviceType->addItem("单导联二代(M1003)"); ui->deviceType->setCurrentIndex(0); connect(ui->deviceType, &QComboBox::currentTextChanged, this, [this](const QString &text) { if (text == "单导联一代(M1001)") { m_devicetype = kone_lead_ecg; } else if (text == "三导联一代(M1002)") { m_devicetype = kthree_lead_ecg; } else if (text == "单导联二代(M1003)") { m_devicetype = kone_lead_ecg_v2; } }); }
// 事件填充
ElectrocardiographTester::ins()->regReportCB([this](bool checkok, ify_hrs_packet_t *report_packet, size_t len) { 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; if ((lastpacket_index + 5) != (heartrate_report->sample_data_index)) { lostpacket++; } lastpacket_index = heartrate_report->sample_data_index; reportPreviewShow("[preview data ] lost:%d index %d", lostpacket, heartrate_report->sample_data_index);
if (checkok) { uint16_t data0 = (uint16_t)(heartrate_report->data[0]) + ((uint16_t)heartrate_report->data[1] << 8); uint16_t data1 = (uint16_t)(heartrate_report->data[2]) + ((uint16_t)heartrate_report->data[3] << 8); uint16_t data2 = (uint16_t)(heartrate_report->data[4]) + ((uint16_t)heartrate_report->data[5] << 8); uint16_t data3 = (uint16_t)(heartrate_report->data[6]) + ((uint16_t)heartrate_report->data[7] << 8); uint16_t data4 = (uint16_t)(heartrate_report->data[8]) + ((uint16_t)heartrate_report->data[9] << 8);
emit doinui_signal(QFunction([this, data0, data1, data2, data3, data4]() { //
wp2d->addData("心电", data0, 0); wp2d->addData("心电", data1, 2); 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); static uint32_t lastpacket_index = 0; static uint32_t lostpacket = 0; if ((lastpacket_index + 50) != (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; emit doinui_signal(QFunction([this, data, i]() { wp2d->addData("心电", data, i * 2); })); } } } break; } case ify_hrs_report_battery_level: { reportPreviewShow("[battery_level ]"); break; } case ify_hrs_report_low_battey_level: { reportPreviewShow("[low_battey ]"); break; } case ify_hrs_report_sample_finish_end: { reportPreviewShow("[sample_finish ]"); break; } case ify_hrs_report_sensor_drop_detect: { sensor_drop_event_report_packet_t *sensor_drop_report = (sensor_drop_event_report_packet_t *)report_packet->data; reportPreviewShow("[sensor_drop ] %s %s", zhex2binary(sensor_drop_report->drop_state0).c_str(), zhex2binary(sensor_drop_report->drop_state1).c_str()); break; } case ify_hrs_report_record_upload_end: { uint32_t checksum = *(uint32_t *)report_packet->data; reportPreviewShow("[upload end ] checksum: 0x%08x", checksum); break; } default: break; } });
ElectrocardiographTester::ins()->regCh4CheckSumPacketReport([this](uint32_t rxcnt, uint32_t report_packet_checksum) { //
blockDataUploadPreviewShow("RXCNT %d CHECKSUM 0x%08x", rxcnt, report_packet_checksum); }); ElectrocardiographTester::ins()->regRawDataCB([this](raw_data_type_t type, uint8_t *hex, uint32_t hexlen) { if (type == kcmd_cmd) { rawDataPreviewShow("[CMD ] %s", zhex2str(hex, hexlen).c_str()); } else if (type == kcmd_receipt) { rawDataPreviewShow("[RECEIPT] %s", zhex2str(hex, hexlen).c_str()); } else if (type == kcmd_report) { rawDataPreviewShow("[REPORT ] %s", zhex2str(hex, hexlen).c_str()); } else if (type == kcmd_ch4_data) { // rawDataPreviewShow("[CH4 ] %s", zhex2str(hex, hexlen).c_str());
} }); }
void MainWindow::processException(zexception &e) { instructionPreviewShow("%s:%s", e.what(), hrs_ecode2str((ify_hrs_error_code_t)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); } }
void MainWindow::on_readDeviceState_clicked() { instructionPreviewClear(); try { device_state_receipt_t state; ElectrocardiographTester::ins()->readDeviceState(&state); instructionPreviewShow("-------------- state ----------------"); instructionPreviewShow(" drop_state0: %s", zhex2binary(state.drop_state0).c_str()); instructionPreviewShow(" drop_state1: %s", zhex2binary(state.drop_state1).c_str());
instructionPreviewShow(" sampling_state: %d", state.device_state0.sampling_state); instructionPreviewShow(" report_state: %d", state.device_state0.report_state); instructionPreviewShow(" low_battery: %d", state.device_state0.low_battery); instructionPreviewShow(" full_storge: %d", state.device_state0.full_storge); instructionPreviewShow(" holder: %d", state.device_state0.holder); instructionPreviewShow(" powerlevel: %d", state.powerlevel); instructionPreviewShow(" storage_item_num: %d", state.storage_item_num); } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_readTime_clicked() { instructionPreviewClear(); try { read_time_receipt_t time; ElectrocardiographTester::ins()->readTime(&time); instructionPreviewShow("-------------- time ----------------"); instructionPreviewShow(" %d/%d/%d %d:%d:%d", time.year + 2000, time.month, time.day, time.hour, time.minute, time.second);
} catch (zexception &exception) { processException(exception); } }
void MainWindow::on_syncTime_clicked() { instructionPreviewClear(); try { // 获取系统时间
QDateTime now = QDateTime::currentDateTime(); ElectrocardiographTester::ins()->syncTime(now.date().year() - 2000, now.date().month(), now.date().day(), now.time().hour(), now.time().minute(), now.time().second()); instructionPreviewShow("sync time success!"); } catch (zexception &exception) { processException(exception); } }
std::string zhex2time(const uint8_t *hex, size_t len) { std::string str; if (len < 6) return str; str.append(fmt("%d/%d/%d %2d:%2d:%2d", hex[0] + 2000, hex[1], hex[2], hex[3], hex[4], hex[5])); return str; }
void MainWindow::on_readAllRecords_clicked() { // 读取当前有多少条记录
instructionPreviewClear(); try { device_state_receipt_t state; ElectrocardiographTester::ins()->readDeviceState(&state); instructionPreviewShow("index recordid frameNum dataSize sensorNum captRate captPrec compAlgo checksum"); for (int32_t i = 0; i < state.storage_item_num; i++) { read_record_info_receipt_t record; ElectrocardiographTester::ins()->readRecordsInfo(i, &record); instructionPreviewShow("%d %s %8d %8d %8d %8d %8d %8d 0x%08x", i, zhex2time(record.record_id, 6).c_str(), record.frameNum, record.dataSize, record.sensorNum, record.captureRate, record.capturePrecision, record.compressAlgorithm, record.checksum); } } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_startUploadRecord_clicked() { instructionPreviewClear(); int reportIndex = ui->startUploadRecord_p0->toPlainText().toInt(); try { read_record_info_receipt_t record; ElectrocardiographTester::ins()->readRecordsInfo(reportIndex, &record); ElectrocardiographTester::ins()->startUploadRecord(record.record_id); instructionPreviewShow("start upload record [%s] success", zhex2time(record.record_id, 6).c_str()); } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_readSensorInfo_clicked() { instructionPreviewClear(); try { sensor_info_receipt_t sensor; ElectrocardiographTester::ins()->readSensorInfo(&sensor); instructionPreviewShow("-------------- sensor ----------------"); instructionPreviewShow(" sensor_num: %d", sensor.sensor_num); instructionPreviewShow(" sensor_precision: %d", sensor.sensor_precision); instructionPreviewShow(" sensor_sample_rate:%d", sensor.sensor_sample_rate); instructionPreviewShow(" sensor0_pos: %d", sensor.sensor0_pos); instructionPreviewShow(" sensor1_pos: %d", sensor.sensor1_pos); instructionPreviewShow(" sensor2_pos: %d", sensor.sensor2_pos); } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_readSN_clicked() { instructionPreviewClear(); try { string sn; ElectrocardiographTester::ins()->readSn(sn); instructionPreviewShow("-------------- sn ----------------"); instructionPreviewShow(" sn: %s", sn.c_str()); } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_stopUploadRecord_clicked() { instructionPreviewClear(); try { ElectrocardiographTester::ins()->stopUploadRecord(); instructionPreviewShow("stop upload record success"); } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_delRecord_clicked() { instructionPreviewClear(); int reportIndex = ui->delRecord_p0->toPlainText().toInt(); try { read_record_info_receipt_t record; ElectrocardiographTester::ins()->readRecordsInfo(reportIndex, &record); ElectrocardiographTester::ins()->delRecord(record.record_id); instructionPreviewShow("delete record [%s] success", zhex2time(record.record_id, 6).c_str()); } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_startCapture_clicked() { instructionPreviewClear(); try { ElectrocardiographTester::ins()->startCapture(); instructionPreviewShow("start capture success"); } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_stopCapture_clicked() { instructionPreviewClear(); try { ElectrocardiographTester::ins()->stopCapture(); instructionPreviewShow("stop capture success"); } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_startRealtimeReport_clicked() { instructionPreviewClear(); try { ElectrocardiographTester::ins()->startRealtimeReport(); instructionPreviewShow("start realtime report success"); wp2d->show(); } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_stopRealtimeReport_clicked() { instructionPreviewClear(); try { ElectrocardiographTester::ins()->stopRealtimeReport(); instructionPreviewShow("stop realtime report success"); wp2d->hide(); } catch (zexception &exception) { processException(exception); } }
void MainWindow::on_clearPreview_clicked() { ui->reportPreview->document()->clear(); ui->uploadDataPreview->document()->clear(); ui->rawDataPreview->document()->clear(); ui->instructionPreview->document()->clear(); }
|