#include "mainwindow.h" #include "ui_mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "libmodbus/modbus.h" #include "customdisplaypanel.h" #include "formserialportsettingdialog.h" #define _DEBUG_VSPD_ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) ,m_pModbus(nullptr) ,m_pTimer(nullptr) ,m_pSettings(nullptr) ,m_bInitializeModbus(false) ,m_timer_interval(10) { ui->setupUi(this); m_version= "1.0.0"; m_modbus_type = 1; m_modbus_ip = "127.0.0.1"; m_modbus_port = 502; m_modbus_com = "COM1"; m_modbus_baund = 9600; m_modbus_data = 1; m_modbus_parity = 0; m_modbus_stop = 1; m_modbus_slave_id = 1; InitializeUI(); } MainWindow::~MainWindow() { if (m_pModbus) { modbus_close(m_pModbus); modbus_free(m_pModbus); m_pModbus = nullptr; } for (SlaveItem* ptr : m_SlaveData) { delete ptr; ptr = nullptr; } m_SlaveData.clear(); if (m_pSettings!=nullptr) { delete m_pSettings; m_pSettings = nullptr; } delete ui; } void MainWindow::getConfiguration(QString iniFilePath) { if (m_pSettings == nullptr) m_pSettings = new QSettings(iniFilePath, QSettings::IniFormat); m_version = m_pSettings->value("version/ver").toString(); m_modbus_type = m_pSettings->value("modbus/type").toInt(); m_modbus_ip = m_pSettings->value("modbus/ip").toString(); m_modbus_port = m_pSettings->value("modbus/port").toInt(); m_modbus_com = m_pSettings->value("modbus/com").toString(); m_modbus_baund = m_pSettings->value("modbus/baund").toInt(); m_modbus_data = m_pSettings->value("modbus/data").toInt(); m_modbus_parity = m_pSettings->value("modbus/parity").toInt(); m_modbus_stop = m_pSettings->value("modbus/stop").toInt(); m_timer_interval = m_pSettings->value("modbus/interval").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); int functions_count = m_pSettings->value(app).toInt(); for(int i=0; ivalue(app2 + "/equipment_code").toInt(); equipmentCode = equipmentCode << 9; equipmentCode &= 0xFE00; // 二进制掩码: 1111 1110 0000 0000(保留高7位) QStringList sn_list = m_pSettings->value(app2 + "/serial_number").toString().split(":", Qt::SkipEmptyParts); QStringList quantity_list = m_pSettings->value(app2 + "/read_quantity").toString().split(":", Qt::SkipEmptyParts); assert(sn_list.size() == quantity_list.size()); int j = 0; foreach(const QString sn, sn_list) { SlaveItem* pSlaveData = new SlaveItem(); pSlaveData->id = m_modbus_slave_id; pSlaveData->function_code = m_pSettings->value(app2 + "/function_code").toInt(); pSlaveData->interval = m_pSettings->value(app2 + "/interval").toInt(); unsigned short serialNumber = sn.toInt(); serialNumber &= 0x01FF; // 二进制掩码: 0000 0001 1111 1111(保留低9位) // 组合操作:高7位左移9位,低9位直接填充 unsigned short address = pSlaveData->start_address = (equipmentCode ) | serialNumber; // 关键位操作 pSlaveData->start_address = address; pSlaveData->quantity = quantity_list.at(j).toInt(); m_SlaveData.push_back(pSlaveData); j++; } #else SlaveItem* pSlaveData = new SlaveItem(); pSlaveData->id = m_modbus_slave_id; pSlaveData->function_code = m_pSettings->value(app2 + "/function_code").toInt(); pSlaveData->interval = m_pSettings->value(app2 + "/interval").toInt(); pSlaveData->quantity = m_pSettings->value(app2 + "/quantity").toInt(); pSlaveData->start_address = m_pSettings->value(app2 + "/start_addr").toInt(); 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.lang = nodeAddrItem["language"].s(); 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() { #ifndef NDEBUG //this->showMaximized(); #endif ui->statusbar->showMessage(tr("Ready"), 0); //初始化窗口边框 QWidget *centralWidget = this->centralWidget(); // new QWidget(this); QGridLayout *mainLayout = new QGridLayout(centralWidget); this->setStyleSheet("background-color: white;"); this->setProperty("canMove",true); //设置窗体透明 this->setAttribute(Qt::WA_TranslucentBackground, true); //设置无边框 this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); //实例阴影shadow QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this); //设置阴影距离 shadow->setOffset(0, 0); //设置阴影颜色 shadow->setColor(QColor(44,44,44)); //设置阴影圆角 shadow->setBlurRadius(16); //给嵌套QDialog设置阴影 ui->centralwidget->setGraphicsEffect(shadow); //给垂直布局器设置边距(此步很重要, 设置宽度为阴影的宽度) //->setMargin(12); //温度 m_pTemperaturePanel = new CustomDisplayPanel(this); m_pTemperaturePanel->setImage(":/icons/main_temp.png"); QStringList l1{tr("Online"),tr("Temperature"),tr("Humidity")}; m_pTemperaturePanel->setLableCount(l1.count()); m_pTemperaturePanel->setTableRowsCount(10); m_pTemperaturePanel->setLables(l1); m_pTemperaturePanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); m_pTemperaturePanel->setMainLabel(tr("Temp&RH")); 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("Vin."),tr("Vout"),tr("Iout"),tr("T_mod")}}; m_pPowerPanel->setLableCount(l2.count()); m_pPowerPanel->setTableRowsCount(10); m_pPowerPanel->setLables(l2); m_pPowerPanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); m_pPowerPanel->setMainLabel(tr("Power")); 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("Vpack")}}; m_pBatteryPanel->setLableCount(l3.count()); m_pBatteryPanel->setTableRowsCount(10); m_pBatteryPanel->setLables(l3); m_pBatteryPanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); m_pBatteryPanel->setMainLabel(tr("​Battery Pack")); 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"); QStringList l4{QStringList{tr("Online"),tr("Machine"),tr("IFM"),tr("Compressor"),tr("OFM"),tr("Discharge Temp."),tr("Room Temp.")}}; m_pACPanel->setLableCount(l4.count()); m_pACPanel->setTableRowsCount(10); m_pACPanel->setLables(l4); m_pACPanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); m_pACPanel->setMainLabel(tr("HVACR")); 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("Vin"),tr("Iin"),tr("T_mod")}}; m_pInverterPanel->setLableCount(l5.count()); m_pInverterPanel->setTableRowsCount(10); m_pInverterPanel->setLables(l5); m_pInverterPanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); m_pInverterPanel->setMainLabel(tr("AC Power")); 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("Vout"),tr("Iout"),tr("T_mod")}}; m_pPVPanel->setLableCount(l6.count()); m_pPVPanel->setTableRowsCount(10); m_pPVPanel->setLables(l6); m_pPVPanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); m_pPVPanel->setMainLabel(tr("​PV Module")); 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("Vout"),tr("Iout"),tr("T_mod")}}; m_pHomePanel->setLableCount(l7.count()); m_pHomePanel->setTableRowsCount(10); m_pHomePanel->setLables(l7); m_pHomePanel->setRowItems(QStringList{tr("露点"),tr("DO"),tr("DI1"),tr("DI2"),tr("高温"),tr("低温"),tr("高湿")}); m_pHomePanel->setMainLabel(tr("Sensors")); 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"); QStringList l8{QStringList{tr("Warning Board")}}; m_pAlarmPanel->setLableCount(l8.count()); m_pAlarmPanel->setTableRowsCount(10); m_pAlarmPanel->setTableRolCount(3); m_pAlarmPanel->setLables(l8); m_pAlarmPanel->setRowItems(QStringList{tr("")}); m_pAlarmPanel->setMainLabel(tr("Warning")); 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); // 顶部留出工具栏空间 //setCentralWidget(centralWidget); QToolBar *toolBar = new QToolBar(this); toolBar->setMovable(false); toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); toolBar->setIconSize(QSize(64,64)); // 设置按钮悬停效果(通过样式表) // toolBar->setStyleSheet( // "QToolButton { background: transparent; border: none; }" // "QToolButton:hover { background: #e0e0e0; border-radius: 4px; }" // ); // 添加工具栏按钮 QAction *actionRead = new QAction(QIcon(":/icons/modular.png"), tr("Read"), this); actionRead->setToolTip(tr("Read Data")); QAction *actionSetting = new QAction(QIcon(":/icons/setting.png"), tr("Setting"), this); actionSetting->setToolTip(tr("Setting Modbud Port")); QAction *actionClose = new QAction(QIcon(":/icons/close.png"), tr("Close"), this); actionClose->setToolTip(tr("Close Application")); //添加logo QLabel *logoLabel = new QLabel(this); QPixmap pixmap(":/icons/logo-en.png"); // 替换为实际图片路径 logoLabel->setPixmap(pixmap.scaled(64, 64, Qt::KeepAspectRatio, Qt::SmoothTransformation)); toolBar->addWidget(logoLabel); //toolBar->addSeparator(); // 添加间隔控件 QWidget *spacer = new QWidget(); spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolBar->addWidget(spacer); // 添加中央标签 QLabel *label = new QLabel(tr("​Integrated Power Cabinet Monitoring"), this); // // 加载字体 // int fontId = QFontDatabase::addApplicationFont(":/fonts/Alimama_DongFangDaKai_Regular.ttf"); // if (fontId == -1) // { // ui->statusbar->showMessage(tr("Failed to load font")); // } //QFont customFont("Alimama DongFangDaKai", 48); //customFont.setBold(true); //label->setFont(customFont); //label->setStyleSheet("color: #2E86C1;"); label->setStyleSheet( "QLabel {" //" font-family: 'Alimama DongFangDaKai';" // 需确保字体已加载 " font-family: 'Arial';" // 需确保字体已加载 " font-size: 52px;" " color: #2E86C1;" " font-weight: bold;" " font-style: black;" "}" ); toolBar->addWidget(label); QWidget *spacerWidget = new QWidget(); spacerWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); toolBar->addWidget(spacerWidget); //添加按钮 toolBar->addAction(actionRead); toolBar->addSeparator(); toolBar->addAction(actionSetting); toolBar->addSeparator(); toolBar->addAction(actionClose); // 将工具栏定位到窗口顶部· addToolBar(Qt::TopToolBarArea, toolBar); connect(actionClose, &QAction::triggered, this, &QMainWindow::close); connect(actionSetting, &QAction::triggered, this, &MainWindow::SettingSerialPort); connect(actionRead, &QAction::triggered, this, &MainWindow::ReadSerialPortData); //读一下配置 QString appDir = QCoreApplication::applicationDirPath(); QString iniFilePath = appDir + QString::fromStdString("/emsshower.ini"); getConfiguration(iniFilePath); return true; } bool MainWindow::InitializeModbus() { if (m_bInitializeModbus) return true; QString appDir = QCoreApplication::applicationDirPath(); QString iniFilePath = appDir + QString::fromStdString("/emsshower.ini"); getConfiguration(iniFilePath); QString jsonFilePath = appDir + QString::fromStdString("/emsshower.json"); ReadConfiguration(jsonFilePath); m_bInitializeModbus = true; switch (m_modbus_type) { case 0: return InitializeTcp(); break; default: case 1: return InitializeRtu(); break; } } bool MainWindow::InitializeRtu() { if(m_pModbus) { modbus_close(m_pModbus); m_pModbus = nullptr; } 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从机地址 modbus_rtu_set_serial_mode(m_pModbus, MODBUS_RTU_RS485); int rc = modbus_connect(m_pModbus); if (rc == -1) return false; struct timeval t; 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; } bool MainWindow::InitializeTcp() { if(m_pModbus) { modbus_close(m_pModbus); m_pModbus = nullptr; } std::string ip = m_modbus_ip.toStdString(); int port = m_modbus_port; m_pModbus = modbus_new_tcp(ip.c_str(), port); //由于是tcp client连接,在同一个程序中相同的端口可以连接多次。 if (m_pModbus == nullptr) { return false; } int slave_id = m_modbus_slave_id; modbus_set_slave(m_pModbus, slave_id); //从机地址 int rc = modbus_connect(m_pModbus); if (rc == -1) return false; struct timeval t; t.tv_sec=0; t.tv_usec=1000000; //设置modbus超时时间为1000毫秒 modbus_set_response_timeout(m_pModbus, t.tv_usec,t.tv_usec); 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; if(CreateJson2(pDevice,json)) { m_Panels[iterAddress->device_type]->UpdateData(json); } else { ui->statusbar->showMessage(tr("Failed to decode temperaure data")); //解析温度数据失败!")); } 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; if (QString::fromStdString(sa.lang.c_str()).toLower() == "chn") ddi.title = iter->second.title_chn; else ddi.title = iter->second.title_en; ddi.unit = iter->second.unit; ddi.value = array[idx]; pGeneralData->m_PanelDisplayDataItems.emplace_back(ddi); } idx++; } } void MainWindow::ReadSerialPortData() { if (!m_pTimer) m_pTimer = new QTimer(this); if (m_pTimer->isActive()) { m_pTimer->stop(); ui->statusbar->showMessage(tr("Stop Reading")); } else { m_pTimer->setInterval(m_timer_interval * 1000); connect(m_pTimer, &QTimer::timeout, this, &MainWindow::onTimeout); ui->statusbar->showMessage(tr("Begin Reading")); m_pTimer->start(); onTimeout(); } } void MainWindow::onTimeout() { readRegister(); } void MainWindow::SettingSerialPort() { FormSerialPortSettingDialog* dlg = new FormSerialPortSettingDialog(this); dlg->setWindowFlags(dlg->windowFlags()&~(Qt::WindowMinMaxButtonsHint|Qt::WindowContextHelpButtonHint)); dlg->exec(); // if(dlg->exec() == QDialog::Accepted) // QMessageBox::information(this, "OK Clicked", "Button 1 was clicked!"); // else // QMessageBox::information(this, "Cancel Clicked", "Cancel was clicked!"); } 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; }