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




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();
}