From 33ab44bd08b533798412502441de6152838327b5 Mon Sep 17 00:00:00 2001 From: HwangKC Date: Sun, 16 Mar 2025 14:20:19 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E4=B8=BA=E9=80=9A=E8=BF=87js?= =?UTF-8?q?on=E6=96=87=E4=BB=B6=E5=AE=9A=E4=B9=89=E8=A7=A3=E7=A0=81?= =?UTF-8?q?=EF=BC=8C=E8=AF=A6=E7=BB=86=E9=85=8D=E7=BD=AE=E8=A7=81json?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- applications/EmsShower/customdisplaypanel.cpp | 50 ++- applications/EmsShower/emsshower.ini | 47 +-- applications/EmsShower/emsshower.json | 191 +++++++++++ applications/EmsShower/mainwindow.cpp | 307 +++++++++++++++++- applications/EmsShower/mainwindow.h | 15 + applications/EmsShower/slave_define.h | 110 ++++++- 6 files changed, 657 insertions(+), 63 deletions(-) create mode 100644 applications/EmsShower/emsshower.json diff --git a/applications/EmsShower/customdisplaypanel.cpp b/applications/EmsShower/customdisplaypanel.cpp index 9ec8c9b..1e40860 100644 --- a/applications/EmsShower/customdisplaypanel.cpp +++ b/applications/EmsShower/customdisplaypanel.cpp @@ -67,7 +67,7 @@ void CustomDisplayPanel::Build() //" font-family: 'Alimama DongFangDaKai';" // 需确保字体已加载 " font-family: 'Arial';" " font-size: 20px;" - " color: #13396E;" + " color: #20549E;" " font-weight: bold;" " font-style: black;" "}" @@ -139,6 +139,8 @@ void CustomDisplayPanel::Build() tableWidget->setShowGrid(true); // 默认显示网格线[3](@ref) tableWidget->setStyleSheet("QTableWidget { gridline-color: #e0e0e0; background-color: white; border-radius: 10px;}"); + tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + // **应用阴影效果** QGraphicsDropShadowEffect *shadowTable = new QGraphicsDropShadowEffect(this); shadowTable->setBlurRadius(10); @@ -169,7 +171,7 @@ void CustomDisplayPanel::UpdateData(OpenJson& json) void CustomDisplayPanel::UpdateTemperature(OpenJson &json) { auto& nodeLabel = json["text_panel"]; - qDebug() << nodeLabel.size(); + qDebug() << "text_panel: " << nodeLabel.size(); for(size_t i=0; isetStyleSheet( + "QLabel {" + //" font-family: 'Alimama DongFangDaKai';" // 需确保字体已加载 + " font-family: 'Arial';" + " font-size: 20px;" + " color: #2E86C1;" + " font-weight: bold;" + "}" + ); + } else + { disp = QString("Offline"); - + m_txtLabels[i]->setStyleSheet( + "QLabel {" + //" font-family: 'Alimama DongFangDaKai';" // 需确保字体已加载 + " font-family: 'Arial';" + " font-size: 20px;" + " color: #FF0000;" + " font-weight: bold;" + " font-style: black" + "}" + ); + } } else { @@ -193,7 +217,7 @@ void CustomDisplayPanel::UpdateTemperature(OpenJson &json) } auto& nodeTable = json["table"]; - qDebug() << nodeTable.size(); + qDebug() << "table: "<< nodeTable.size(); m_pTableWidget->clear(); m_pTableWidget->setRowCount(nodeTable.size()); @@ -255,6 +279,8 @@ void CustomDisplayPanel::UpdateAlarm(OpenJson &json) auto& nodeTable = json["alarm"]; qDebug() << nodeTable.size(); + + m_pTableWidget->setUpdatesEnabled(false); for(size_t i=0; iinsertRow(0); @@ -280,6 +306,22 @@ void CustomDisplayPanel::UpdateAlarm(OpenJson &json) m_pTableWidget->setItem(0, 1, item1); m_pTableWidget->setItem(0, 2, item2); } + + m_pTableWidget->setUpdatesEnabled(true); + + // 自动定位到首行 + if(m_pTableWidget->rowCount() > 0) + { + // 创建首行索引 + QModelIndex firstIndex = m_pTableWidget->model()->index(0, 0); + + // 滚动到首行并置顶 + m_pTableWidget->scrollTo(firstIndex, QAbstractItemView::PositionAtTop); + + // 选中整行(需提前设置选择模式) + m_pTableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + m_pTableWidget->setCurrentIndex(firstIndex); + } } //////// diff --git a/applications/EmsShower/emsshower.ini b/applications/EmsShower/emsshower.ini index ce76875..42582ae 100644 --- a/applications/EmsShower/emsshower.ini +++ b/applications/EmsShower/emsshower.ini @@ -1,67 +1,36 @@ -;通用设置 [version] -ver="1_0_0" +ver=1_0_0 [modbus] -;1=RTU 0=TCP type=1 - -;if type=1, ignore ip and port setting -ip="127.0.0.1" +ip=127.0.0.1 port=502 - -com="COM20" +com=COM20 baund=9600 data=8 - -;0=None 1=Odd 2=Even parity=0 - stop=1 [slaves] -;如果从机数量多于1,则从机地址slave_id以:分隔(英文冒号) -slave_id="1" - -;每一个从机的配置 -;由两个节组成:slave_%d 和 slave_%d_function_%d +slave_id=1 [slave_1] -;支持的功能码数量 function_code_counts=2 [slave_1_function_0] -;功能码 function_code=3 - -;起始地址,10进制 start_addr=40000 - -;读取数量 quantity=9 - -;扫描间隔,单位ms interval=1000 - -;serial_number与read_quantity的元素数量要一样 equipment_code=81 -serial_number="0:1:2:3:4:5:6:7:8:9:10:11:12:13:14" -read_quantity="1:1:1:1:1:1:1:1:1:1:1:1:1:1:1" +serial_number=0:1:2:3:4:5:6:7:8:9:10:11:12:13:14 +read_quantity=1:1:1:1:1:1:1:1:1:1:1:1:1:1:1 [slave_1_function_1] -;功能码 function_code=3 - -;起始地址,10进制 start_addr=40009 - -;读取数量 quantity=6 - -;扫描间隔,单位ms interval=1000 - -;serial_number与read_quantity的元素数量要一样 equipment_code=81 -serial_number="0:1:2:3:4:5:6:7:8:9:10:11:12:13:14" -read_quantity="1:1:1:1:1:1:1:1:1:1:1:1:1:1:1" \ No newline at end of file +serial_number=0:1:2:3:4:5:6:7:8:9:10:11:12:13:14 +read_quantity=1:1:1:1:1:1:1:1:1:1:1:1:1:1:1 diff --git a/applications/EmsShower/emsshower.json b/applications/EmsShower/emsshower.json new file mode 100644 index 0000000..b086efd --- /dev/null +++ b/applications/EmsShower/emsshower.json @@ -0,0 +1,191 @@ +{ + "modbus": + { + "type":"rs485", + "port":"COM1", + "baudrate":9600, + "parity":"N", + "stopbits":1, + "data":8, + "ip":"127.0.0.1", + "ip_port":502 + }, + "slaves":[ + { + "id":1, + "function_code":3, + "address":[ + { + "start_addr":40000, + "quantity":9, + "bytes_per_register":2, + "device_name_chn":"温湿度", + "device_name_eng":"Temperature", + "device_type":1, + "data": [ + { + "comment":"在线状态", + "order":0, + "precision":1, + "unit":"", + "title_chn":"在线状态", + "title_eng":"Online", + "display":1, + "skip":0 + }, + { + "comment":"温度", + "order":1, + "precision":10, + "unit":"℃", + "title_chn":"温度", + "title_eng":"T (℃) ", + "display":1, + "skip":0 + }, + { + "comment":"湿度", + "order":2, + "precision":10, + "unit":"%", + "title_chn":"湿度", + "title_eng":"RH (%)", + "display":1 , + "skip":0 + }, + { + "comment":"露点", + "order":3, + "precision":1, + "unit":"", + "title_chn":"露点", + "title_eng":"DewPoint", + "display":2, + "skip":0 + }, + { + "comment":"跳过", + "order":4, + "precision":1, + "unit":"", + "title_chn":"", + "title_eng":"", + "display":0, + "skip":1 + }, + { + "comment":"跳过", + "order":5, + "precision":1, + "unit":"", + "title_chn":"", + "title_eng":"", + "display":0, + "skip":1 + }, + { + "comment":"DO", + "order":6, + "precision":1, + "unit":"", + "title_chn":"DO", + "title_eng":"DO", + "display":2, + "skip":0 + }, + { + "comment":"DI1", + "order":7, + "precision":1, + "unit":"", + "title_chn":"DI1", + "title_eng":"DI1", + "display":2, + "skip":0 + }, + { + "comment":"DI2", + "order":8, + "precision":1, + "unit":"", + "title_chn":"DI2", + "title_eng":"DI2", + "display":2, + "skip":0 + } + ] + }, + { + "start_addr":40009, + "quantity":6, + "bytes_per_register":2, + "device_name_chn":"温湿度告警", + "device_name_eng":"Alarm", + "device_type":6, + "data": [ + { + "comment":"温度阈值", + "order":0, + "precision":10, + "unit":"", + "title_chn":"温度阈值", + "title_eng":"Temp_threshold", + "display":0, + "skip":0 + }, + { + "comment":"温度偏移", + "order":1, + "precision":1, + "unit":"", + "title_chn":"温度偏移", + "title_eng":"Temp_offset", + "display":0, + "skip":0 + }, + { + "comment":"跳过", + "order":2, + "precision":1, + "unit":"", + "title_chn":"", + "title_eng":"", + "display":0, + "skip":1 + }, + { + "comment":"高温告警", + "order":3, + "precision":1, + "unit":"", + "title_chn":"高温告警", + "title_eng":"High Temp.", + "display":1, + "skip":0 + }, + { + "comment":"高湿告警", + "order":4, + "precision":1, + "unit":"", + "title_chn":"高湿告警", + "title_eng":"High Humidity", + "display":1, + "skip":0 + }, + { + "comment":"低温告警", + "order":5, + "precision":1, + "unit":"", + "title_chn":"低温告警", + "title_eng":"Low Temp.", + "display":0, + "skip":0 + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/applications/EmsShower/mainwindow.cpp b/applications/EmsShower/mainwindow.cpp index 45aeca2..2b2d309 100644 --- a/applications/EmsShower/mainwindow.cpp +++ b/applications/EmsShower/mainwindow.cpp @@ -25,6 +25,10 @@ #define TH08D_TEMPERATURE_EQUIPMENT_81_00_09 40000 #define TH08D_TEMPERATURE_EQUIPMENT_81_09_06 40009 +void DecodeWorker::setSlaveAddress(const slaveAddress& sa) +{ + m_slaveAddress = sa; +} // 处理数组的槽函数 void DecodeWorker::processArray(const QVector& array,int slave_id,int start_addr,int quantity,DeviceData* pData) { @@ -88,6 +92,31 @@ void DecodeWorker::processArray(const QVector& array,int slave_id,int emit finished(); } +void DecodeWorker::processRegisterData(const QVector &array, const slaveAddress& sa, DeviceData *pData) +{ + GeneralDeviceData* pGeneralData = (GeneralDeviceData*)pData; + RegisterDataItems::const_iterator iter = sa.register_data_items.begin(); + int idx = 0; + for(; iter!=sa.register_data_items.end(); iter++) + { + if (iter->second.skip ==0) + { + DisplayDataItem ddi; + ddi.device_type = sa.device_type; + ddi.display_location = iter->second.display; + ddi.order = iter->second.order; + ddi.precision = iter->second.precision; + ddi.title = iter->second.title_chn; + ddi.unit = iter->second.unit; + ddi.value = array[idx]; + pGeneralData->m_PanelDisplayDataItems.emplace_back(ddi); + } + idx++; + } + + return; +} + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) @@ -153,6 +182,7 @@ void MainWindow::getConfiguration(QString iniFilePath) m_modbus_parity = m_pSettings->value("modbus/parity").toInt(); m_modbus_stop = m_pSettings->value("modbus/stop").toInt(); +#if 0 m_modbus_slave_id = m_pSettings->value("slaves/slave_id").toInt(); QString app = QString("slave_%1/function_code_counts").arg(m_modbus_slave_id); @@ -202,6 +232,61 @@ void MainWindow::getConfiguration(QString iniFilePath) m_SlaveData.push_back(pSlaveData); #endif } +#endif +} + +void MainWindow::ReadConfiguration(QString jsonFilePath) +{ + OpenJson json; + json.decodeFile(jsonFilePath.toStdString()); + auto& nodeSlaves = json["slaves"]; + qDebug() << nodeSlaves.size(); + + + for (size_t i = 0; i < nodeSlaves.size(); i++) + { + auto& node = nodeSlaves[i]; + + slaveItem slave_item; + + slave_item.slave_id = node["id"].i32(); + slave_item.function_code = node["function_code"].i32(); + + auto& nodeAddress = node["address"]; + for (size_t j = 0; j < nodeAddress.size(); j++) + { + auto& nodeAddrItem = nodeAddress[j]; + + slaveAddress address_item; + address_item.start_addr = nodeAddrItem["start_addr"].i32(); + address_item.quantity = nodeAddrItem["quantity"].i32(); + address_item.bytes_per_register = nodeAddrItem["bytes_per_register"].i32(); + address_item.device_name_chn = nodeAddrItem["device_name_chn"].s(); + address_item.device_name_eng = nodeAddrItem["device_name_eng"].s(); + address_item.device_type = nodeAddrItem["device_type"].i32(); + auto& nodeData = nodeAddrItem["data"]; + for (size_t k = 0; k < nodeData.size(); k++) + { + auto& nodeRegister = nodeData[k]; + + registryData rdata; + rdata.comment = nodeRegister["comment"].s(); + rdata.order = nodeRegister["order"].i32(); + rdata.precision = (float)nodeRegister["precision"].d(); + rdata.unit = nodeRegister["unit"].s(); + rdata.title_chn = nodeRegister["title_chn"].s(); + rdata.title_en = nodeRegister["title_eng"].s(); + rdata.display = nodeRegister["display"].i32(); + rdata.skip = nodeRegister["skip"].i32(); + + address_item.register_data_items.insert(std::make_pair(rdata.order,rdata)); + } + + slave_item.slave_address_items.emplace_back(address_item); + } + + m_total_slave_items.emplace_back(slave_item); + } } bool MainWindow::InitializeUI() @@ -249,10 +334,12 @@ bool MainWindow::InitializeUI() m_pTemperaturePanel->Build(); mainLayout->addWidget(m_pTemperaturePanel, 0, 0); + m_Panels.insert(std::make_pair(CustomDisplayPanel::PANEL_TEMPERATURE,m_pTemperaturePanel)); + //电源 m_pPowerPanel = new CustomDisplayPanel(this); m_pPowerPanel->setImage(":/icons/main_power.png"); - QStringList l2{QStringList{tr("Online"),tr("​Input Voltage"),tr("​Output Voltage"),tr("​Output Current"),tr("Module Temp.")}}; + QStringList l2{QStringList{tr("Online"),tr("Vin."),tr("Vout"),tr("Iout"),tr("T_mod")}}; m_pPowerPanel->setLableCount(l2.count()); m_pPowerPanel->setTableRowsCount(10); m_pPowerPanel->setLables(l2); @@ -261,10 +348,12 @@ bool MainWindow::InitializeUI() m_pPowerPanel->Build(); mainLayout->addWidget(m_pPowerPanel, 0, 1); + m_Panels.insert(std::make_pair(CustomDisplayPanel::PANEL_POWER,m_pPowerPanel)); + //电池 m_pBatteryPanel = new CustomDisplayPanel(this); m_pBatteryPanel->setImage(":/icons/main_battery.png"); - QStringList l3{QStringList{tr("Online"),tr("SOC"),tr("SOH"),tr("Group Voltage"),tr("Cell V.Avg"),tr("Cell V.Max"),tr("Cell V.Min")}}; + QStringList l3{QStringList{tr("Online"),tr("SOC"),tr("SOH"),tr("Vpack")}}; m_pBatteryPanel->setLableCount(l3.count()); m_pBatteryPanel->setTableRowsCount(10); m_pBatteryPanel->setLables(l3); @@ -273,6 +362,8 @@ bool MainWindow::InitializeUI() m_pBatteryPanel->Build(); mainLayout->addWidget(m_pBatteryPanel, 0, 2); + m_Panels.insert(std::make_pair(CustomDisplayPanel::PANEL_BATTERY,m_pBatteryPanel)); + //空调 m_pACPanel = new CustomDisplayPanel(this); m_pACPanel->setImage(":/icons/main_ac.png"); @@ -285,10 +376,12 @@ bool MainWindow::InitializeUI() m_pACPanel->Build(); mainLayout->addWidget(m_pACPanel, 0, 3); + m_Panels.insert(std::make_pair(CustomDisplayPanel::PANEL_AC,m_pACPanel)); + //交流配电 m_pInverterPanel = new CustomDisplayPanel(this); m_pInverterPanel->setImage(":/icons/main_invertor.png"); - QStringList l5{QStringList{tr("Online"),tr("​Input Voltage"),tr("​Input Current"),tr("Module Temp.")}}; + QStringList l5{QStringList{tr("Online"),tr("Vin"),tr("Iin"),tr("T_mod")}}; m_pInverterPanel->setLableCount(l5.count()); m_pInverterPanel->setTableRowsCount(10); m_pInverterPanel->setLables(l5); @@ -297,10 +390,12 @@ bool MainWindow::InitializeUI() m_pInverterPanel->Build(); mainLayout->addWidget(m_pInverterPanel, 1, 0); + m_Panels.insert(std::make_pair(CustomDisplayPanel::PANEL_INVERTER,m_pInverterPanel)); + //PV太阳能 m_pPVPanel = new CustomDisplayPanel(this); m_pPVPanel->setImage(":/icons/main_pv.png"); - QStringList l6{QStringList{tr("Online"),tr("​Output Voltage"),tr("​Output Current"),tr("Module Temp.")}}; + QStringList l6{QStringList{tr("Online"),tr("Vout"),tr("Iout"),tr("T_mod")}}; m_pPVPanel->setLableCount(l6.count()); m_pPVPanel->setTableRowsCount(10); m_pPVPanel->setLables(l6); @@ -309,10 +404,12 @@ bool MainWindow::InitializeUI() m_pPVPanel->Build(); mainLayout->addWidget(m_pPVPanel, 1, 1); + m_Panels.insert(std::make_pair(CustomDisplayPanel::PANEL_PV,m_pPVPanel)); + //门禁 m_pHomePanel = new CustomDisplayPanel(this); m_pHomePanel->setImage(":/icons/main_cab.png"); - QStringList l7{QStringList{tr("Online"),tr("​Output Voltage"),tr("​Output Current"),tr("Module Temp.")}}; + QStringList l7{QStringList{tr("Online"),tr("Vout"),tr("Iout"),tr("T_mod")}}; m_pHomePanel->setLableCount(l7.count()); m_pHomePanel->setTableRowsCount(10); m_pHomePanel->setLables(l7); @@ -321,6 +418,8 @@ bool MainWindow::InitializeUI() m_pHomePanel->Build(); mainLayout->addWidget(m_pHomePanel, 1, 2); + m_Panels.insert(std::make_pair(CustomDisplayPanel::PANEL_HOME,m_pHomePanel)); + //告警 m_pAlarmPanel = new CustomWarningPanel(this); m_pAlarmPanel->setImage(":/icons/main_alarm.png"); @@ -334,6 +433,8 @@ bool MainWindow::InitializeUI() m_pAlarmPanel->Build(); mainLayout->addWidget(m_pAlarmPanel, 1, 3); + m_Panels.insert(std::make_pair(CustomDisplayPanel::PANEL_ALARM,m_pAlarmPanel)); + // 设置布局的间距和边距 mainLayout->setSpacing(5); //mainLayout->setContentsMargins(10, 40, 10, 10); // 顶部留出工具栏空间 @@ -364,11 +465,7 @@ bool MainWindow::InitializeUI() QPixmap pixmap(":/icons/logo-en.png"); // 替换为实际图片路径 logoLabel->setPixmap(pixmap.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); toolBar->addWidget(logoLabel); - toolBar->addSeparator(); - - //添加按钮 - toolBar->addAction(actionRead); - toolBar->addSeparator(); + //toolBar->addSeparator(); // 添加间隔控件 QWidget *spacer = new QWidget(); @@ -388,6 +485,7 @@ bool MainWindow::InitializeUI() //customFont.setBold(true); //label->setFont(customFont); //label->setStyleSheet("color: #2E86C1;"); + label->setStyleSheet( "QLabel {" //" font-family: 'Alimama DongFangDaKai';" // 需确保字体已加载 @@ -404,6 +502,10 @@ bool MainWindow::InitializeUI() spacerWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolBar->addWidget(spacerWidget); + //添加按钮 + toolBar->addAction(actionRead); + toolBar->addSeparator(); + toolBar->addAction(actionSetting); toolBar->addSeparator(); @@ -429,6 +531,9 @@ bool MainWindow::InitializeModbus() getConfiguration(iniFilePath); + QString jsonFilePath = appDir + QString::fromStdString("/emsshower.json"); + ReadConfiguration(jsonFilePath); + m_bInitializeModbus = true; switch (m_modbus_type) { @@ -454,7 +559,7 @@ bool MainWindow::InitializeRtu() std::string com = std::string("\\\\.\\") + m_modbus_com.toStdString(); m_pModbus = modbus_new_rtu(com.c_str(), m_modbus_baund, 'N', m_modbus_data, m_modbus_stop); - +#if 1 int slave_id = m_modbus_slave_id; //modbus_set_debug(m_pModbus, true); modbus_set_slave(m_pModbus, slave_id); //设置modbus从机地址 @@ -468,7 +573,7 @@ bool MainWindow::InitializeRtu() t.tv_sec=0; t.tv_usec=1000000; //设置modbus超时时间为1000毫秒 modbus_set_response_timeout(m_pModbus, t.tv_usec,t.tv_usec); - +#endif return true; } @@ -559,6 +664,75 @@ bool MainWindow::readRegister(int addr,int nb,uint16_t* dest) return true; } +bool MainWindow::readRegister() +{ + if(!InitializeModbus()) //这里有问题,如果是虚拟串口,连接的通常会返回成功 + { + ui->statusbar->showMessage(tr("Failed to open Modbus device,Check modbus connection please!")); //打开MODBUS设备失败,请检查设备连接情况!")); + return false; + } + + //对每一个slave进行读取 + //枚举每一个slave id;这里因为实际上只有一个slave,所以实际上只会执行一次循环 + + SlaveItems::iterator iter = m_total_slave_items.begin(); + for(; iter!=m_total_slave_items.end(); iter++) + { + SlaveAddressItems::iterator iterAddress = iter->slave_address_items.begin(); + for(; iterAddress!=iter->slave_address_items.end(); iterAddress++) + { + uint16_t* tab_reg = new uint16_t[iterAddress->quantity]; + int regs = modbus_read_registers(m_pModbus, iterAddress->start_addr, iterAddress->quantity, tab_reg); + + QVector registers; + for (int i = 0; i < regs; ++i) + { + registers.push_back(tab_reg[i]); + } + + delete []tab_reg; + + DeviceData* pDevice = new GeneralDeviceData(); + + DecodeSync(registers, *iterAddress, pDevice); + + OpenJson json; + CreateJson2(pDevice,json); + + m_Panels[iterAddress->device_type]->UpdateData(json); + + delete pDevice; + + qDebug() << "decode an item"; + } + } + + return true; +} + +void MainWindow::DecodeSync(const QVector& array,const slaveAddress& sa,DeviceData* pData) +{ + GeneralDeviceData* pGeneralData = (GeneralDeviceData*)pData; + RegisterDataItems::const_iterator iter = sa.register_data_items.begin(); + int idx = 0; + for(; iter!=sa.register_data_items.end(); iter++) + { + if (iter->second.skip ==0) + { + DisplayDataItem ddi; + ddi.device_type = sa.device_type; + ddi.display_location = iter->second.display; + ddi.order = iter->second.order; + ddi.precision = iter->second.precision; + ddi.title = iter->second.title_en; + ddi.unit = iter->second.unit; + ddi.value = array[idx]; + pGeneralData->m_PanelDisplayDataItems.emplace_back(ddi); + } + idx++; + } +} + void MainWindow::startAsyncProcess(const QVector& array,int slave_id,int start_addr,int quantity, DeviceData* pDevice) { QThread* thread = new QThread; @@ -595,6 +769,42 @@ void MainWindow::startAsyncProcess(const QVector& array,int slave_id,i qDebug() << "同步读取完成"; } +void MainWindow::startAsyncProcess2(const QVector &array, const slaveAddress& sa,DeviceData *pData) +{ + QThread* thread = new QThread; + DecodeWorker* worker = new DecodeWorker; + worker->moveToThread(thread); + + worker->setSlaveAddress(sa); + + // 连接线程启动信号和槽函数 + connect(this, &MainWindow::startProcessing2, worker, &DecodeWorker::processRegisterData); + + // 工作完成后退出线程 + connect(worker, &DecodeWorker::finished, thread, &QThread::quit); + connect(worker, &DecodeWorker::finished, worker, &DecodeWorker::deleteLater); + connect(thread, &QThread::finished, thread, &QThread::deleteLater); + + //// 可选:连接进度信号更新UI + //connect(worker, &DecodeWorker::progress, this, [](int index) + //{ + // qDebug() << "进度更新,当前处理到索引:" << index; + //}); + + thread->start(); + + QEventLoop loop; + QObject::connect(worker, &DecodeWorker::finished, &loop, &QEventLoop::quit); + + // 发射信号传递参数 + emit startProcessing2(array,sa, pData); + + // 阻塞,等待线程结束 + loop.exec(); + + qDebug() << "同步读取完成"; +} + void MainWindow::ReadSerialPortData() { @@ -608,9 +818,9 @@ void MainWindow::ReadSerialPortData() } else { - m_pTimer->setInterval(10000); // 10秒 + m_pTimer->setInterval(10 * 1000); // 10秒 connect(m_pTimer, &QTimer::timeout, this, &MainWindow::onTimeout); - ui->statusbar->showMessage(tr("Begin Readding")); + ui->statusbar->showMessage(tr("Begin Reading")); m_pTimer->start(); onTimeout(); } @@ -618,7 +828,8 @@ void MainWindow::ReadSerialPortData() void MainWindow::onTimeout() { - readRegister(0,0,0); + //readRegister(0,0,0); + readRegister(); } void MainWindow::SettingSerialPort() @@ -633,6 +844,7 @@ void MainWindow::SettingSerialPort() // QMessageBox::information(this, "Cancel Clicked", "Cancel was clicked!"); } + bool MainWindow::CreateJson(DeviceData* pData,OpenJson& json) { int data_type = pData->m_device_type; @@ -648,10 +860,10 @@ bool MainWindow::CreateJson(DeviceData* pData,OpenJson& json) nodeLabel[0]["title"] = "Online"; nodeLabel[1]["value"] = pTempData->TempValue; - nodeLabel[1]["title"] = "Temp(℃)"; + nodeLabel[1]["title"] = "T (℃) "; nodeLabel[2]["value"] = pTempData->HumidityValue; - nodeLabel[2]["title"] = "RH (%) "; + nodeLabel[2]["title"] = "RH (%)"; auto& nodeTable = json["table"]; int i = 0; @@ -720,3 +932,64 @@ bool MainWindow::CreateJson(DeviceData* pData,OpenJson& json) return false; } + +bool MainWindow::CreateJson2(DeviceData* pData,OpenJson& json) +{ + assert(pData); + GeneralDeviceData* pDevice = (GeneralDeviceData*)pData; + DisplayDataItems::const_iterator iter = pDevice->m_PanelDisplayDataItems.begin(); + + int idx0=0; + int idx1=0; + json["panel_type"] = iter->device_type; + + if (iter->device_type == 6) //告警,需要特别处理 + { + + auto& nodeTable = json["alarm"]; + for(; iter!=pDevice->m_PanelDisplayDataItems.end(); iter++) + { + if(iter->display_location == 0) + continue; + nodeTable[idx0]["time"] = QDateTime::currentDateTime().toString("MM-dd HH:mm:ss").toStdString(); + nodeTable[idx0]["signal"] = iter->title; + nodeTable[idx0]["value"] = iter->value * 1.0f / iter->precision; + idx0++; + } + } + else + { + auto& nodeLabel = json["text_panel"]; + auto& nodeTable = json["table"]; + for(; iter!=pDevice->m_PanelDisplayDataItems.end(); iter++) + { + if(iter->display_location == 1) //显示在面板 + { + nodeLabel[idx0]["value"] = iter->value * 1.0f / iter->precision; + nodeLabel[idx0]["title"] = iter->title; + idx0++; + } + if (iter->display_location == 2) // 显示在表格 + { + if (idx1 == 0) + { + nodeTable[idx1]["value"] = QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss").toStdString(); + nodeTable[idx1]["signal"] = "Time"; + } + else + { + nodeTable[idx1]["value"] = iter->value * 1.0f / iter->precision; + nodeTable[idx1]["signal"] = iter->title; + } + idx1++; + } + + } + } + + std::string a = json.encode(); + + qDebug() << QString::fromStdString(a); + + return true; +} diff --git a/applications/EmsShower/mainwindow.h b/applications/EmsShower/mainwindow.h index 4232257..464b947 100644 --- a/applications/EmsShower/mainwindow.h +++ b/applications/EmsShower/mainwindow.h @@ -42,12 +42,15 @@ public: this->pDevice = pDevice; } + void setSlaveAddress(const slaveAddress& sa); + private: QVector array; int slave_id; int start_addr; int quantity; DeviceData* pDevice; + slaveAddress m_slaveAddress; signals: // 处理进度信号 @@ -59,6 +62,8 @@ public slots: // 处理数组的槽函数 // 根据从机id、开始地址、寄存器数量三个变量进行辨别和解码处理 void processArray(const QVector& array,int slave_id,int start_addr,int quantity,DeviceData* pData); + + void processRegisterData(const QVector& array,const slaveAddress& sa,DeviceData* pData); }; //处理主界面 @@ -72,12 +77,15 @@ public: protected: void getConfiguration(QString iniFilePath); + void ReadConfiguration(QString jsonFilePath); bool InitializeUI(); bool InitializeModbus(); bool InitializeRtu(); bool InitializeTcp(); bool readRegister(int addr,int nb,uint16_t* dest); + bool readRegister(); + private: Ui::MainWindow *ui; @@ -99,9 +107,12 @@ protected: QSettings* m_pSettings; bool m_bInitializeModbus; + SlaveItems m_total_slave_items; + protected: //异步处理数据 void startAsyncProcess(const QVector& array,int slave_id,int start_addr,int quantity,DeviceData* pData); + void startAsyncProcess2(const QVector& array,const slaveAddress& sa,DeviceData* pData); private slots: void ReadSerialPortData(); @@ -117,9 +128,12 @@ private: CustomDisplayPanel* m_pAlarmPanel; //告警 CustomDisplayPanel* m_pInverterPanel; //逆变器 CustomDisplayPanel* m_pPVPanel; //太阳能 + std::map m_Panels; private: bool CreateJson(DeviceData* pData,OpenJson& json); + bool CreateJson2(DeviceData* pData,OpenJson& json); + void DecodeSync(const QVector& array,const slaveAddress& sa,DeviceData* pData); //同步解码 signals: // 处理进度信号 @@ -129,5 +143,6 @@ signals: signals: void startProcessing(const QVector& array, int slave_id, int start_addr, int quantity, DeviceData* pData); + void startProcessing2(const QVector& array,const slaveAddress& sa,DeviceData* pData); }; #endif // MAINWINDOW_H diff --git a/applications/EmsShower/slave_define.h b/applications/EmsShower/slave_define.h index 2e27f36..abc7a3a 100644 --- a/applications/EmsShower/slave_define.h +++ b/applications/EmsShower/slave_define.h @@ -1,7 +1,9 @@ -#ifndef SLAVE_DEFINE_H +#ifndef SLAVE_DEFINE_H #define SLAVE_DEFINE_H #include +#include +#include typedef struct _tagSlave { @@ -14,6 +16,20 @@ typedef struct _tagSlave typedef std::vector SlaveData; +//存储解码后的数据 +typedef struct __DisplayDataItem +{ + int device_type; + int order; + std::string title; + int precision; + int value; + std::string unit; + int display_location; //1:board 2:table +} DisplayDataItem; + +typedef std::vector DisplayDataItems; + class DeviceData { public: @@ -29,6 +45,19 @@ public: int m_device_online_state; }; +class GeneralDeviceData: public DeviceData +{ +public: + GeneralDeviceData() + { + m_device_type = 8899; + m_device_online_state = 0; + } + virtual ~GeneralDeviceData() {} + +public: + DisplayDataItems m_PanelDisplayDataItems; +}; class TemperatureData : public DeviceData { @@ -43,8 +72,8 @@ public: virtual ~TemperatureData() {} public: - bool bDecodeAlarm; - bool bDecodeTemp; + bool bDecodeAlarm; + bool bDecodeTemp; float TempValue; float HumidityValue; float DewPointValue; @@ -56,4 +85,79 @@ public: int TempLowAlarm; }; +//////////////////////////////////////////////////////////////////// +// // +// 以下数据结构是为了存取json配置文件设置 // +// 保存串口的从机号、寄存器开始地址、数量、寄存器数据描述等等信息 // +//////////////////////////////////////////////////////////////////// + +/**************************************************************** +* 逻辑结构 +* + SlaveItems 定义有多少个从机 + slaveAddress 定义每一个从机有多少要读取的地址,每一个起始地址并不保证连续 + registryData 定义每一个起始地址需要读取的数据 +* +* +*****************************************************************/ + +//寄存器的数据,以及控制策略描述 +typedef struct __registryData +{ + __registryData(): + comment(""), precision(1.0f), unit(""), title_chn("中文"), title_en("eng"), display(1), skip(0) + {} + + std::string comment; // ":"在线状态", 注释字段,为了方便读,无实际意义 + int order; // 定义解码的顺序,从0开始,数量 =slaveAddress.quantity, + float precision; // " : 1, 精度,标注小数点用 + std::string unit; // " : "", 单位 + std::string title_chn; // " : 中文显示 + std::string title_en; // " : 英文显示 + int display; // " : 1, 是否显示,0不显示,1显示在board,2显示在表格 + int skip; //" : 0 //1跳过 0不跳过 +} registryData; + +//寄存器的数据数组 +typedef std::map RegisterDataItems; + +typedef struct __slaveAddress +{ + int start_addr; // ":40000, 寄存器地址 + unsigned short quantity; // " : 9, 连续读取多少个寄存器 + unsigned short bytes_per_register; // " : 2, 每个寄存器的字节数,缺省是uint16,两个字节 + std::string device_name_chn; // " : "温湿度", 该寄存器数组代表的设备名称 + std::string device_name_eng; // " : "温湿度", 该寄存器数组代表的设备名称,英文 + + //标识本地址的数据是哪一个类型的设备 + /* + PANEL_TEMPERATURE = 1, + PANEL_BATTERY = 2, + PANEL_POWER = 3, + PANEL_AC = 4, + PANEL_PV = 5, + PANEL_ALARM = 6, + PANEL_INVERTER = 7, + PANEL_OTHER = 88, + PANEL_HOME = 99, + * */ + int device_type; + + RegisterDataItems register_data_items; //寄存器数据,数组 +} slaveAddress; + +//需要读取的从机的地址信息 +typedef std::vector SlaveAddressItems; + +typedef struct __slaveItem +{ + int slave_id; //从机ID + int function_code; //功能码,缺省是3 + SlaveAddressItems slave_address_items; //该从机ID需要读取的地址信息描述 +} slaveItem; + +//需要处理的从机列表 +typedef std::vector SlaveItems; + + #endif // SLAVE_DEFINE_H