336 lines
13 KiB
C++
336 lines
13 KiB
C++
#include "union_modbus_slave.h"
|
|
#include "ui_union_modbus_slave.h"
|
|
|
|
#include <QtSerialBus/QModbusRtuSerialSlave>
|
|
#include <QtSerialBus/QModbusTcpServer>
|
|
#include <QRegularExpression>
|
|
#include <QRegularExpressionValidator>
|
|
#include <QStatusBar>
|
|
#include <QUrl>
|
|
|
|
enum ModbusConnection {
|
|
Serial,
|
|
Tcp
|
|
};
|
|
|
|
union_modbus_slave::union_modbus_slave(QWidget *parent) :
|
|
QWidget(parent),
|
|
ui(new Ui::union_modbus_slave)
|
|
{
|
|
ui->setupUi(this);
|
|
setupWidgetContainers();
|
|
|
|
#if QT_CONFIG(modbus_serialport)
|
|
ui->connectType->setCurrentIndex(0);
|
|
onCurrentConnectTypeChanged(0);
|
|
#else
|
|
// lock out the serial port option
|
|
ui->connectType->setCurrentIndex(1);
|
|
onCurrentConnectTypeChanged(1);
|
|
ui->connectType->setEnabled(false);
|
|
#endif
|
|
|
|
//m_settingsDialog = new SettingsDialog(this);
|
|
initActions();
|
|
}
|
|
|
|
union_modbus_slave::~union_modbus_slave()
|
|
{
|
|
if (modbusDevice)
|
|
modbusDevice->disconnectDevice();
|
|
delete modbusDevice;
|
|
|
|
delete ui;
|
|
}
|
|
|
|
void union_modbus_slave::statusBarClear()
|
|
{
|
|
statusBarTimeOut.stop();
|
|
ui->StatusBar->clear();
|
|
}
|
|
|
|
void union_modbus_slave::initActions()
|
|
{
|
|
// ui->actionConnect->setEnabled(true);
|
|
// ui->actionDisconnect->setEnabled(false);
|
|
// ui->actionExit->setEnabled(true);
|
|
// ui->actionOptions->setEnabled(true);
|
|
|
|
connect(ui->connectButton, &QPushButton::clicked,
|
|
this, &union_modbus_slave::onConnectButtonClicked);
|
|
// connect(ui->actionConnect, &QAction::triggered,
|
|
// this, &union_modbus_slave::onConnectButtonClicked);
|
|
// connect(ui->actionDisconnect, &QAction::triggered,
|
|
// this, &union_modbus_slave::onConnectButtonClicked);
|
|
connect(ui->connectType, QOverload<int>::of(&QComboBox::currentIndexChanged),
|
|
this, &union_modbus_slave::onCurrentConnectTypeChanged);
|
|
|
|
// connect(ui->actionExit, &QAction::triggered, this, &union_modbus_slave::close);
|
|
// connect(ui->actionOptions, &QAction::triggered, m_settingsDialog, &QDialog::show);
|
|
|
|
statusBarTimeOut.setSingleShot(true);
|
|
connect(&statusBarTimeOut, &QTimer::timeout, this, &union_modbus_slave::statusBarClear);
|
|
}
|
|
|
|
void union_modbus_slave::onCurrentConnectTypeChanged(int index)
|
|
{
|
|
if (modbusDevice) {
|
|
modbusDevice->disconnect();
|
|
delete modbusDevice;
|
|
modbusDevice = nullptr;
|
|
}
|
|
|
|
auto type = static_cast<ModbusConnection>(index);
|
|
if (type == Serial) {
|
|
#if QT_CONFIG(modbus_serialport)
|
|
modbusDevice = new QModbusRtuSerialSlave(this);
|
|
#endif
|
|
} else if (type == Tcp) {
|
|
modbusDevice = new QModbusTcpServer(this);
|
|
if (ui->portEdit->text().isEmpty())
|
|
ui->portEdit->setText(QLatin1String("127.0.0.1:502"));
|
|
}
|
|
ui->listenOnlyBox->setEnabled(type == Serial);
|
|
|
|
if (!modbusDevice) {
|
|
ui->connectButton->setDisabled(true);
|
|
if (type == Serial)
|
|
{
|
|
//statusBar()->showMessage(tr("Could not create Modbus ."), 5000);
|
|
ui->StatusBar->setText(tr("Could not create Modbus slave."));
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
else
|
|
{
|
|
ui->StatusBar->setText(tr("Could not create Modbus server."));
|
|
statusBarTimeOut.start(5000);
|
|
//statusBar()->showMessage(tr("Could not create Modbus ."), 5000);
|
|
}
|
|
} else {
|
|
QModbusDataUnitMap reg;
|
|
reg.insert(QModbusDataUnit::Coils, { QModbusDataUnit::Coils, 0, 100 });
|
|
reg.insert(QModbusDataUnit::DiscreteInputs, { QModbusDataUnit::DiscreteInputs, 0, 100 });
|
|
reg.insert(QModbusDataUnit::InputRegisters, { QModbusDataUnit::InputRegisters, 0, 100 });
|
|
reg.insert(QModbusDataUnit::HoldingRegisters, { QModbusDataUnit::HoldingRegisters, 0, 100 });
|
|
modbusDevice->setMap(reg);
|
|
|
|
connect(modbusDevice, &QModbusServer::dataWritten,
|
|
this, &union_modbus_slave::updateWidgets);
|
|
connect(modbusDevice, &QModbusServer::stateChanged,
|
|
this, &union_modbus_slave::onStateChanged);
|
|
connect(modbusDevice, &QModbusServer::errorOccurred,
|
|
this, &union_modbus_slave::handleDeviceError);
|
|
|
|
connect(ui->listenOnlyBox, &QCheckBox::toggled, this, [this](bool toggled) {
|
|
if (modbusDevice)
|
|
modbusDevice->setValue(QModbusServer::ListenOnlyMode, toggled);
|
|
});
|
|
emit ui->listenOnlyBox->toggled(ui->listenOnlyBox->isChecked());
|
|
connect(ui->setBusyBox, &QCheckBox::toggled, this, [this](bool toggled) {
|
|
if (modbusDevice)
|
|
modbusDevice->setValue(QModbusServer::DeviceBusy, toggled ? 0xffff : 0x0000);
|
|
});
|
|
emit ui->setBusyBox->toggled(ui->setBusyBox->isChecked());
|
|
|
|
setupDeviceData();
|
|
}
|
|
}
|
|
|
|
void union_modbus_slave::handleDeviceError(QModbusDevice::Error newError)
|
|
{
|
|
if (newError == QModbusDevice::NoError || !modbusDevice)
|
|
return;
|
|
|
|
//statusBar()->showMessage(modbusDevice->errorString(), 5000);
|
|
ui->StatusBar->setText(tr("Could not create Modbus master."));
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
|
|
void union_modbus_slave::onConnectButtonClicked()
|
|
{
|
|
bool intendToConnect = (modbusDevice->state() == QModbusDevice::UnconnectedState);
|
|
|
|
//statusBar()->clearMessage();
|
|
statusBarClear();
|
|
|
|
if (intendToConnect) {
|
|
if (static_cast<ModbusConnection>(ui->connectType->currentIndex()) == Serial) {
|
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter,
|
|
ui->portEdit->text());
|
|
#if QT_CONFIG(modbus_serialport)
|
|
short parityStep = ui->parityCombo->currentIndex();
|
|
if (parityStep>0) parityStep++;
|
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter,
|
|
parityStep);
|
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
|
|
ui->baudCombo->currentText().toInt());
|
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
|
|
ui->dataBitsCombo->currentText().toInt());
|
|
modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
|
|
ui->stopBitsCombo->currentText().toInt());
|
|
#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());
|
|
if (!modbusDevice->connectDevice()) {
|
|
//statusBar()->showMessage(tr("Connect failed: ") + modbusDevice->errorString(), 5000);
|
|
ui->StatusBar->setText(tr("Connect failed: ") + modbusDevice->errorString());
|
|
statusBarTimeOut.start(5000);
|
|
} else {
|
|
ui->serverFrame->setEnabled(false);
|
|
ui->modbusSettings->setEnabled(false);
|
|
// ui->actionConnect->setEnabled(false);
|
|
// ui->actionDisconnect->setEnabled(true);
|
|
}
|
|
} else {
|
|
modbusDevice->disconnectDevice();
|
|
ui->serverFrame->setEnabled(true);
|
|
ui->modbusSettings->setEnabled(true);
|
|
// ui->actionConnect->setEnabled(true);
|
|
// ui->actionDisconnect->setEnabled(false);
|
|
}
|
|
}
|
|
|
|
void union_modbus_slave::onStateChanged(int state)
|
|
{
|
|
bool connected = (state != QModbusDevice::UnconnectedState);
|
|
// ui->actionConnect->setEnabled(!connected);
|
|
// ui->actionDisconnect->setEnabled(connected);
|
|
|
|
if (state == QModbusDevice::UnconnectedState)
|
|
ui->connectButton->setText(tr("Connect"));
|
|
else if (state == QModbusDevice::ConnectedState)
|
|
ui->connectButton->setText(tr("Disconnect"));
|
|
}
|
|
|
|
void union_modbus_slave::coilChanged(int id)
|
|
{
|
|
QAbstractButton *button = coilButtons.button(id);
|
|
bitChanged(id, QModbusDataUnit::Coils, button->isChecked());
|
|
}
|
|
|
|
void union_modbus_slave::discreteInputChanged(int id)
|
|
{
|
|
QAbstractButton *button = discreteButtons.button(id);
|
|
bitChanged(id, QModbusDataUnit::DiscreteInputs, button->isChecked());
|
|
}
|
|
|
|
void union_modbus_slave::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);
|
|
ui->StatusBar->setText(tr("Could not set data: ") + modbusDevice->errorString());
|
|
statusBarTimeOut.start(5000);
|
|
}
|
|
}
|
|
|
|
void union_modbus_slave::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")))
|
|
ok = modbusDevice->setData(QModbusDataUnit::InputRegisters, id, value.toUShort(&ok, 16));
|
|
else if (objectName.startsWith(QStringLiteral("holdReg")))
|
|
ok = modbusDevice->setData(QModbusDataUnit::HoldingRegisters, id, value.toUShort(&ok, 16));
|
|
|
|
if (!ok)
|
|
{
|
|
ui->StatusBar->setText(tr("Could not set register: ") + modbusDevice->errorString());
|
|
statusBarTimeOut.start(5000);
|
|
//statusBar()->showMessage(tr("Could not set register: ") + modbusDevice->errorString(),
|
|
// 5000);
|
|
}
|
|
}
|
|
}
|
|
|
|
void union_modbus_slave::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;
|
|
}
|
|
}
|
|
}
|
|
|
|
// -- private
|
|
|
|
void union_modbus_slave::setupDeviceData()
|
|
{
|
|
if (!modbusDevice)
|
|
return;
|
|
|
|
for (quint16 i = 0; i < coilButtons.buttons().count(); ++i)
|
|
modbusDevice->setData(QModbusDataUnit::Coils, i, coilButtons.button(i)->isChecked());
|
|
|
|
for (quint16 i = 0; i < discreteButtons.buttons().count(); ++i) {
|
|
modbusDevice->setData(QModbusDataUnit::DiscreteInputs, i,
|
|
discreteButtons.button(i)->isChecked());
|
|
}
|
|
|
|
bool ok;
|
|
for (QLineEdit *widget : qAsConst(registers)) {
|
|
if (widget->objectName().startsWith(QStringLiteral("inReg"))) {
|
|
modbusDevice->setData(QModbusDataUnit::InputRegisters, quint16(widget->property("ID").toUInt()),
|
|
widget->text().toUShort(&ok, 16));
|
|
} else if (widget->objectName().startsWith(QStringLiteral("holdReg"))) {
|
|
modbusDevice->setData(QModbusDataUnit::HoldingRegisters, quint16(widget->property("ID").toUInt()),
|
|
widget->text().toUShort(&ok, 16));
|
|
}
|
|
}
|
|
}
|
|
|
|
void union_modbus_slave::setupWidgetContainers()
|
|
{
|
|
coilButtons.setExclusive(false);
|
|
discreteButtons.setExclusive(false);
|
|
//Массив указателей на чекбоксы.
|
|
//При установке значения в бокс, вызывается функция
|
|
//которая высчитывает номер регистра (Значение спинбокса + номер от 0 до 4)
|
|
//И передаёт в функцию bitChanged
|
|
QRegularExpression regexp(QStringLiteral("coils_(?<ID>\\d+)"));
|
|
const QList<QCheckBox *> coils = findChildren<QCheckBox *>(regexp);
|
|
for (QCheckBox *cbx : coils)
|
|
coilButtons.addButton(cbx, regexp.match(cbx->objectName()).captured("ID").toInt());
|
|
connect(&coilButtons, SIGNAL(buttonClicked(int)), this, SLOT(coilChanged(int)));
|
|
|
|
regexp.setPattern(QStringLiteral("disc_(?<ID>\\d+)"));
|
|
const QList<QCheckBox *> discs = findChildren<QCheckBox *>(regexp);
|
|
for (QCheckBox *cbx : discs)
|
|
discreteButtons.addButton(cbx, regexp.match(cbx->objectName()).captured("ID").toInt());
|
|
connect(&discreteButtons, SIGNAL(buttonClicked(int)), this, SLOT(discreteInputChanged(int)));
|
|
|
|
regexp.setPattern(QLatin1String("(in|hold)Reg_(?<ID>\\d+)"));
|
|
const QList<QLineEdit *> qle = findChildren<QLineEdit *>(regexp);
|
|
for (QLineEdit *lineEdit : qle) {
|
|
registers.insert(lineEdit->objectName(), lineEdit);
|
|
lineEdit->setProperty("ID", regexp.match(lineEdit->objectName()).captured("ID").toInt());
|
|
lineEdit->setValidator(new QRegularExpressionValidator(QRegularExpression(QStringLiteral("[0-9a-f]{0,4}"),
|
|
QRegularExpression::CaseInsensitiveOption), this));
|
|
connect(lineEdit, &QLineEdit::textChanged, this, &union_modbus_slave::setRegister);
|
|
}
|
|
}
|