发布时间:2026-07-21阅读(1)
本系统专门配备了设备模拟工具,用来在没有外接真实设备的时候,模拟modbus协议数据,支持多个设备,支持串口和网络方式,可切换正常数据和报警数据,反应到主程序上。对应主程序中两种端口,一种是串口端口(这个可以用虚拟串口工具 Virtual Serial Port 虚拟一对串口用于测试),一种是网络端口(注意选择的监听主机地址和端口)。数据库模拟对应程序中的数据库采集运行模式,可以勾选自动模拟复选框。随机生成状态字段数据。
除了自身自带的设备模拟工具外,作为国际知名的modbus模拟仿真工具Modbus Slave,本系统也提供了对应的数据配置文件iotsystem.mbs,在db目录下,对应添加FC-1003-8控制器。两边的通信方式必须一致,比如软件上设置的串口则两边都是串口。具体Modbus Slave工具详细使用可以自行搜索,比如这篇文章 https://blog.csdn.net/xuw_xy/article/details/81166305 。
有了这几种模拟工具,一方面方便用户在没有真实硬件的情况下测试软件,一方面也为了自己做压力测试,比如模拟多个串口和网口端口,模拟几百个几千个设备,测试整个软件各个模块的反应度响应度,是否能够支持多少个设备报警,存储的记录数,记录是否超过了自动清理,报警短信转发和报警邮件转发功能是否正常等。
二、功能特点2.1 软件模块



void frmTool::checkData(const QbyteArray &data, bool isCom){ int dataLen = data.length(); if (dataLen <= 0) { return; } QByteArray body; QString type = ui->cboxDeviceType->currentText(); if (type == "FC-1003-1") { for (int i = 0; i < 1; i) { body.append(QUIHelper::ushortToByte(spinBoxs.at(i)->value())); } } else if (type == "FC-1003-8") { for (int i = 0; i < 8; i) { body.append(QUIHelper::ushortToByte(spinBoxs.at(i)->value())); } } else if (type == "FC-1003-16") { for (int i = 0; i < 16; i) { body.append(QUIHelper::ushortToByte(spinBoxs.at(i)->value())); } } else { for (int i = 0; i < 4; i) { body.append(QUIHelper::ushortToByte(spinBoxs.at(i)->value())); } } //模拟回复数据 01 03 08 00 14 03 12 00 00 00 00 79 E6 QByteArray buffer; buffer.append(ui->cboxDeviceAddr->currentText().toInt()); buffer.append(0x03); buffer.append(body.length()); buffer.append(body); buffer.append(QUIHelper::getCrcCode(buffer)); if (isCom) { com->write(buffer); append(1, QUIHelper::byteArrayToHexStr(data)); append(0, QUIHelper::byteArrayToHexStr(buffer)); } else { tcpSocket->write(buffer); append(3, QUIHelper::byteArrayToHexStr(data)); append(2, QUIHelper::byteArrayToHexStr(buffer)); }}void frmTool::append(int type, const QString &data, bool clear){ if (ui->btnShowData->text() == "继续") { return; } static int currentCount = 0; static int maxCount = 100; QUIHelper::appendMsg(ui->txtMain, type, data, maxCount, currentCount, clear);}void frmTool::on_btnOpen_clicked(){ if (ui->btnOpen->text() == "打开") { com = new QextSerialPort(AppConfig::PortName, QextSerialPort::Polling); isComOk = com->open(QIODevice::ReadWrite); if (isComOk) { com->setBaudRate((BaudRateType)AppConfig::BaudRate); com->setTimeout(10); ui->btnOpen->setText("关闭"); timerRead->start(); append(4, QString("打开串口 %1 成功").arg(AppConfig::PortName)); } else { append(3, QString("打开串口 %1 失败: %2").arg(AppConfig::PortName).arg(com->errorString())); } } else { timerRead->stop(); com->close(); com->deleteLater(); ui->btnOpen->setText("打开"); on_btnClearData_clicked(); isComOk = false; }}void frmTool::on_btnListen_clicked(){ if (ui->btnListen->text() == "监听") { isTcpOk = tcpServer->listen(QHostAddress(AppConfig::ListenIP), AppConfig::ListenPort); if (isTcpOk) { ui->btnListen->setText("停止"); append(4, QString("监听端口 %1 成功").arg(AppConfig::ListenPort)); } else { append(3, QString("监听端口 %1 失败: %2").arg(AppConfig::ListenPort).arg(tcpServer->errorString())); } } else { if (tcpSocket != 0) { tcpSocket->disconnectFromHost(); } tcpServer->close(); ui->btnListen->setText("监听"); on_btnClearData_clicked(); isTcpOk = false; }}void frmTool::on_btnClearData_clicked(){ append(0, "", true);}void frmTool::on_btnShowData_clicked(){ if (ui->btnShowData->text() == "暂停") { ui->btnShowData->setText("继续"); } else { ui->btnShowData->setText("暂停"); }}void frmTool::on_btnSetNormal_clicked(){ foreach (QSpinBox *spinBox, spinBoxs) { spinBox->setValue(QUIHelper::getRandValue(0, 20)); }}void frmTool::on_btnSetAlarm_clicked(){ foreach (QSpinBox *spinBox, spinBoxs) { spinBox->setValue(QUIHelper::getRandValue(0, 200)); }}void frmTool::on_btnAdd_clicked(){ int count = model->rowCount(); model->insertRow(count); int row = count - 1; QString positionID = model->index(row, 0).data().toString(); float nodeValue = model->index(row, 1).data().toFloat(); int nodeStatus = model->index(row, 2).data().toInt(); QString saveTime = model->index(row, 3).data().toString(); //位号末尾数字的话自动递增 int id = positionID.right(3).toInt(); if (id > 0) { positionID = QString("%1%2").arg(positionID.left(positionID.length() - 3)).arg(id 1, 3, 10, QChar(0)); } //当前为第一行时候的默认值 if (count == 0) { positionID = AppData::DefaultPositionID; nodeValue = 25; nodeStatus = 1; saveTime = DATETIME; } //设置新增加的行默认值 model->setData(model->index(count, 0), positionID); model->setData(model->index(count, 1), nodeValue); model->setData(model->index(count, 2), nodeStatus); model->setData(model->index(count, 3), saveTime); //立即选中当前新增加的行 ui->tableView->setCurrentIndex(model->index(count, 0));}void frmTool::on_btnSave_clicked(){ //重新设置下焦点避免MAC系统上当单元格处于编辑状态保存不成功的BUG ui->tableView->setFocus(); //开启数据库事务提交数据 model->database().transaction(); if (model->submitAll()) { model->database().commit(); } else { model->database().rollback(); QUIHelper::showMessageBoxError("保存信息失败, 请重新填写!"); } //有些数据库需要主动查询一下不然是空白的比如odbc数据源 model->select();}
Copyright © 2024 有趣生活 All Rights Reserve吉ICP备19000289号-5 TXT地图HTML地图XML地图