该示例实现了Modbus服务器应用程序。
该示例充当Modbus服务器。它接收标准的Modbus请求,根据请求调整其内部状态,并以适当的回复进行响应。
此示例必须与Modbus客户端示例结合使用。在启动Modbus客户端示例之前,应启动此示例并将其置于监听状态。两个示例之间的后续交互使用Modbus协议。
本例中使用的关键类:
>QModbusServer
>QModbusDataUnit
创造一个QModbusServer
执行任何通信都需要QModbusServer的实例。根据指定的连接类型,该示例可以实例化QModbusRtuSerialServer(用于串行通信)或QModbusTcpServer(用于基于TCP的通信)。
auto type = static_cast<ModbusConnection>(index);
if (type == Serial) {
#if QT_CONFIG(modbus_serialport)
modbusDevice = new QModbusRtuSerialServer(this);
// Try to fill in the first available serial port name if the line edit
// is empty, or contains a url (assume that ':' is only a part of url).
const auto ports = QSerialPortInfo::availablePorts();
const auto currentText = ui->portEdit->text();
if (!ports.isEmpty() && (currentText.isEmpty() || currentText.contains(u':')))
ui->portEdit->setText(ports.front().portName());
#endif
} else if (type == Tcp) {
modbusDevice = new QModbusTcpServer(this);
const QUrl currentUrl = QUrl::fromUserInput(ui->portEdit->text());
// Check if we already have <ip address>:<port>
if (currentUrl.port() <= 0)
ui->portEdit->setText(QLatin1String("127.0.0.1:50200"));
}
创建服务器后,使用QModbusServer::setMap()方法设置寄存器映射。客户端应用程序使用此寄存器映射来读取和写入服务器数据。
QModbusDataUnitMap reg;
reg.insert(QModbusDataUnit::Coils, { QModbusDataUnit::Coils, 0, 10 });
reg.insert(QModbusDataUnit::DiscreteInputs, { QModbusDataUnit::DiscreteInputs, 0, 10 });
reg.insert(QModbusDataUnit::InputRegisters, { QModbusDataUnit::InputRegisters, 0, 10 });
reg.insert(QModbusDataUnit::HoldingRegisters, { QModbusDataUnit::HoldingRegisters, 0, 10 });
modbusDevice->setMap(reg);
之后,指定通信参数和服务器地址。通信参数取决于通信类型:
if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) {
modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
ui->portEdit->text());
#if QT_CONFIG(modbus_serialport)
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
m_settingsDialog->settings().parity);
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
m_settingsDialog->settings().baud);
modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
m_settingsDialog->settings().dataBits);
modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
m_settingsDialog->settings().stopBits);
#endif
} else {
const QUrl url = QUrl::fromUserInput(ui->portEdit->text());
modbusDevice->setConnectionParameter(QModbusDevice::NetworkPortParameter, url.port());
modbusDevice->setConnectionParameter(QModbusDevice::NetworkAddressParameter, url.host());
}
modbusDevice->setServerAddress(ui->serverEdit->text().toInt());
创建服务器并指定所有参数后,使用QModbusServer::connectDevice()连接到Modbus网络。
改变本地值
该示例允许通过使用提供的组合框或行编辑来更改所有支持的寄存器类型的值。在UI中更新值后,QModbusServer::setData()方法用于更新服务器上的实际值:
void MainWindow::bitChanged(int id, QModbusDataUnit::RegisterType table, bool value)
{
if (!modbusDevice)
return;
if (!modbusDevice->setData(table, quint16(id), value))
statusBar()->showMessage(tr("Could not set data: ") + modbusDevice->errorString(), 5000);
}
void MainWindow::setRegister(const QString &value)
{
if (!modbusDevice)
return;
const QString objectName = QObject::sender()->objectName();
if (registers.contains(objectName)) {
bool ok = true;
const quint16 id = quint16(QObject::sender()->property("ID").toUInt());
if (objectName.startsWith(QStringLiteral("inReg"))) {
const auto uval = value.toUShort(&ok, 16);
if (ok)
ok = modbusDevice->setData(QModbusDataUnit::InputRegisters, id, uval);
} else if (objectName.startsWith(QStringLiteral("holdReg"))) {
const auto uval = value.toUShort(&ok, 16);
if (ok)
ok = modbusDevice->setData(QModbusDataUnit::HoldingRegisters, id, uval);
}
if (!ok)
statusBar()->showMessage(tr("Could not set register: ") + modbusDevice->errorString(),
5000);
}
}
处理远程写入
Modbus客户端能够通过发送写请求来更新线圈和保持寄存器。一旦在服务器端使用此类请求更新了值,就会发出QModbusServer::dataWritten()信号。
connect(modbusDevice, &QModbusServer::dataWritten,
this, &MainWindow::updateWidgets);
Modbus服务器示例连接到此信号,提取更新的值并相应地更新UI:
void MainWindow::updateWidgets(QModbusDataUnit::RegisterType table, int address, int size)
{
for (int i = 0; i < size; ++i) {
quint16 value;
QString text;
switch (table) {
case QModbusDataUnit::Coils:
modbusDevice->data(QModbusDataUnit::Coils, quint16(address + i), &value);
coilButtons.button(address + i)->setChecked(value);
break;
case QModbusDataUnit::HoldingRegisters:
modbusDevice->data(QModbusDataUnit::HoldingRegisters, quint16(address + i), &value);
registers.value(QStringLiteral("holdReg_%1").arg(address + i))->setText(text
.setNum(value, 16));
break;
default:
break;
}
}
}