UnionComDLL/union_modbus_slave.cpp

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