UnionComDLL/union_modbus.cpp

446 lines
16 KiB
C++

#include "union_modbus.h"
#include "ui_union_modbus.h"
#include "writeregistermodel.h"
#include <QtSerialBus/QModbusTcpClient>
#include <QtSerialBus/QModbusRtuSerialMaster>
#include <QStandardItemModel>
#include <QUrl>
enum ModbusConnection {
Serial,
Tcp
};
union_modbus *this_modbus;
static void stepToPeriodRequest()
{
this_modbus->readModbus();
}
union_modbus::union_modbus(QWidget *parent) :
QWidget(parent),
ui(new Ui::union_modbus)
{
ui->setupUi(this);
initActions();
writeModel = new WriteRegisterModel(this);
writeModel->setStartAddress(ui->spinTXStartAddress->value());
writeModel->setNumberOfValues(ui->comboTXNumberOfValues->currentText());
ui->writeValueTable->setModel(writeModel);
ui->writeValueTable->hideColumn(2);
connect(writeModel, &WriteRegisterModel::updateViewport,
ui->writeValueTable->viewport(), QOverload<>::of(&QWidget::update));
ui->comboTable->addItem(tr("Coils"), QModbusDataUnit::Coils);
ui->comboTable->addItem(tr("Discrete Inputs"), QModbusDataUnit::DiscreteInputs);
ui->comboTable->addItem(tr("Input Registers"), QModbusDataUnit::InputRegisters);
ui->comboTable->addItem(tr("Holding Registers"), QModbusDataUnit::HoldingRegisters);
this_modbus=this;
RequestPeriodTimer.setSingleShot(true);
connect(&RequestPeriodTimer, &QTimer::timeout, this, &stepToPeriodRequest);
#if QT_CONFIG(modbus_serialport)
ui->connectType->setCurrentIndex(0);
onConnectTypeChanged(0);
#else
// lock out the serial port option
ui->connectType->setCurrentIndex(1);
onConnectTypeChanged(1);
ui->connectType->setEnabled(false);
#endif
auto model = new QStandardItemModel(10, 1, this);
for (int i = 0; i < 100; ++i)
model->setItem(i, new QStandardItem(QStringLiteral("%1").arg(i + 1)));
ui->comboTXNumberOfValues->setModel(model);
connect(ui->comboTXNumberOfValues, &QComboBox::currentTextChanged,
writeModel, &WriteRegisterModel::setNumberOfValues);
ui->comboTXNumberOfValues->setCurrentText("10");
auto valueChanged = QOverload<int>::of(&QSpinBox::valueChanged);
connect(ui->spinTXStartAddress, valueChanged, writeModel, &WriteRegisterModel::setStartAddress);
connect(ui->spinTXStartAddress, valueChanged, this, [this, model](int i) {
int lastPossibleIndex = 0;
const int currentIndex = ui->comboTXNumberOfValues->currentIndex();
for (int ii = 0; ii < 10; ++ii) {
if (ii < (10 - i)) {
lastPossibleIndex = ii;
model->item(ii)->setEnabled(true);
} else {
model->item(ii)->setEnabled(false);
}
}
if (currentIndex > lastPossibleIndex)
ui->comboTXNumberOfValues->setCurrentIndex(lastPossibleIndex);
});
}
union_modbus::~union_modbus()
{
if(modbusDevice->state()==QModbusDevice::ConnectedState)
{
modbusDevice->disconnectDevice();
}
delete ui;
}
void union_modbus::statusBarClear()
{
ui->StatusBar->clear();
}
void union_modbus::initActions()
{
connect(ui->buttonConnectOrDisconnect, &QPushButton::clicked,
this, &union_modbus::onConnectButtonClicked);
connect(ui->buttonRead, &QPushButton::clicked,
this, &union_modbus::onReadButtonClicked);
connect(ui->buttonWrite, &QPushButton::clicked,
this, &union_modbus::onWriteButtonClicked);
connect(ui->buttonReadWrite, &QPushButton::clicked,
this, &union_modbus::onReadWriteButtonClicked);
connect(ui->comboTable, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &union_modbus::onWriteTableChanged);
connect(ui->connectType, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, &union_modbus::onConnectTypeChanged);
statusBarTimeOut.setSingleShot(true);
connect(&statusBarTimeOut, &QTimer::timeout, this, &union_modbus::statusBarClear);
}
void union_modbus::onConnectTypeChanged(int index)
{
if (modbusDevice) {
modbusDevice->disconnectDevice();
delete modbusDevice;
modbusDevice = nullptr;
}
auto type = static_cast<ModbusConnection>(index);
if (type == Serial) {
#if QT_CONFIG(modbus_serialport)
ui->portEdit->clear();
modbusDevice = new QModbusRtuSerialMaster(this);
#endif
} else if (type == Tcp) {
modbusDevice = new QModbusTcpClient(this);
if (ui->portEdit->text().isEmpty())
ui->portEdit->setText(QLatin1String("127.0.0.1:502"));
}
connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
ui->StatusBar->setText(modbusDevice->errorString());
statusBarTimeOut.start(5000);
});
if (!modbusDevice) {
ui->buttonConnectOrDisconnect->setDisabled(true);
if (type == Serial)
{
ui->StatusBar->setText(tr("Could not create Modbus master."));
statusBarTimeOut.start(5000);
}
else
{
ui->StatusBar->setText(tr("Could not create Modbus client."));
statusBarTimeOut.start(5000);
}
} else {
connect(modbusDevice, &QModbusClient::stateChanged,
this, &union_modbus::onModbusStateChanged);
}
}
void union_modbus::ApplySettings()
{
#if QT_CONFIG(modbus_serialport)
m_settings.parity = ui->conboParity->currentIndex();
if (m_settings.parity > 0)
m_settings.parity++;
m_settings.baud = ui->comboBaudRate->currentText().toInt();
m_settings.dataBits = ui->comboDataBits->currentText().toInt();
m_settings.stopBits = ui->comboStopBits->currentText().toInt();
#endif
m_settings.responseTime = ui->spinResponseTimeout->value();
m_settings.numberOfRetries = ui->spinNumberOfRetries->value();
}
void union_modbus::onConnectButtonClicked()
{
if (!modbusDevice)
return;
statusBarTimeOut.stop();
ui->StatusBar->clear();
RequestPeriodTimer.stop();
if (modbusDevice->state() != QModbusDevice::ConnectedState) {
ApplySettings();
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_settings.parity);
modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter,
m_settings.baud);
modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter,
m_settings.dataBits);
modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter,
m_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->setTimeout(m_settings.responseTime);
modbusDevice->setNumberOfRetries(m_settings.numberOfRetries);
if (!modbusDevice->connectDevice()) {
ui->StatusBar->setText(tr("Connect failed: ") + modbusDevice->errorString());
statusBarTimeOut.start(5000);
}
else
{
ui->frameSettings->setEnabled(false);
ui->frameSerialTCPPort->setEnabled(false);
}
} else {
modbusDevice->disconnectDevice();
ui->frameSettings->setEnabled(true);
ui->frameSerialTCPPort->setEnabled(true);
}
}
void union_modbus::onModbusStateChanged(int state)
{
bool connected = (state != QModbusDevice::UnconnectedState);
if (state == QModbusDevice::UnconnectedState)
ui->buttonConnectOrDisconnect->setText(tr("Connect"));
else if (state == QModbusDevice::ConnectedState)
ui->buttonConnectOrDisconnect->setText(tr("Disconnect"));
}
void union_modbus::onReadButtonClicked()
{
if(CurrentReadButtonPosition)
{
if(ui->checkPeriodRequest->checkState()==Qt::Checked)
{
ui->buttonRead->setText("Stop Read");
CurrentReadButtonPosition = false;
readModbus();
}
else
{
readModbus();
}
}
else
{
ui->buttonRead->setText("Read");
CurrentReadButtonPosition = true;
RequestPeriodTimer.stop();
}
}
void union_modbus::readModbus()
{
if (!modbusDevice)
return;
statusBarTimeOut.stop();
ui->StatusBar->clear();
if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) {
if (!reply->isFinished())
connect(reply, &QModbusReply::finished, this, &union_modbus::onReadReady);
else
delete reply; // broadcast replies return immediately
} else {
ui->StatusBar->setText(tr("Read error: ") + modbusDevice->errorString());
statusBarTimeOut.start(5000);
}
// if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) {
// if (!reply->isFinished())
// connect(reply, &QModbusReply::finished, this, &union_modbus::onReadReady);
// else
// delete reply; // broadcast replies return immediately
// } else {
// ui->StatusBar->setText(tr("Read error: ") + modbusDevice->errorString());
// statusBarTimeOut.start(5000);
// }
// if (auto *reply = modbusDevice->sendReadRequest(readRequest(), ui->serverEdit->value())) {
// if (!reply->isFinished())
// connect(reply, &QModbusReply::finished, this, &union_modbus::onReadReady);
// else
// delete reply; // broadcast replies return immediately
// } else {
// ui->StatusBar->setText(tr("Read error: ") + modbusDevice->errorString());
// statusBarTimeOut.start(5000);
// }
}
void union_modbus::onReadReady()
{
auto reply = qobject_cast<QModbusReply *>(sender());
if (!reply)
return;
ui->readValue->clear();
if (reply->error() == QModbusDevice::NoError) {
ui->StatusBar->clear();
const QModbusDataUnit unit = reply->result();
for (int i = 0, total = int(unit.valueCount()); i < total; ++i) {
const QString entry = tr("Address: %1, Value: %2").arg(unit.startAddress() + i)
.arg(QString::number(unit.value(i),
unit.registerType() <= QModbusDataUnit::Coils ? 10 : 16));
ui->readValue->addItem(entry);
}
if(CurrentReadButtonPosition == false)
{
RequestPeriodTimer.start(ui->spinPeriodTime->value());
}
reply->deleteLater();
return;
} else if (reply->error() == QModbusDevice::ProtocolError) {
ui->StatusBar->setText(tr("Read response error: %1 (Mobus exception: 0x%2)").
arg(reply->errorString()).
arg(reply->rawResult().exceptionCode(), -1, 16));
statusBarTimeOut.start(5000);
} else {
ui->StatusBar->setText(tr("Read response error: %1 (code: 0x%2)").
arg(reply->errorString()).
arg(reply->error(), -1, 16));
statusBarTimeOut.start(5000);
}
if(CurrentReadButtonPosition==false)
onReadButtonClicked();
reply->deleteLater();
}
void union_modbus::onWriteButtonClicked()
{
if (!modbusDevice)
return;
statusBarTimeOut.stop();
ui->StatusBar->clear();
QModbusDataUnit writeUnit = writeRequest();
QModbusDataUnit::RegisterType table = writeUnit.registerType();
for (int i = 0, total = int(writeUnit.valueCount()); i < total; ++i) {
if (table == QModbusDataUnit::Coils)
writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]);
else
writeUnit.setValue(i, writeModel->m_holdingRegisters[i + writeUnit.startAddress()]);
}
if (auto *reply = modbusDevice->sendWriteRequest(writeUnit, ui->serverEdit->value())) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [this, reply]() {
if (reply->error() == QModbusDevice::ProtocolError) {
ui->StatusBar->setText(tr("Write response error: %1 (Mobus exception: 0x%2)")
.arg(reply->errorString()).arg(reply->rawResult().exceptionCode(), -1, 16));
statusBarTimeOut.start(5000);
} else if (reply->error() != QModbusDevice::NoError) {
ui->StatusBar->setText(tr("Write response error: %1 (code: 0x%2)").
arg(reply->errorString()).arg(reply->error(), -1, 16));
statusBarTimeOut.start(5000);
}
reply->deleteLater();
});
} else {
// broadcast replies return immediately
reply->deleteLater();
}
} else {
ui->StatusBar->setText(tr("Write error: ") + modbusDevice->errorString());
statusBarTimeOut.start(5000);
}
}
void union_modbus::onReadWriteButtonClicked()
{
if (!modbusDevice)
return;
ui->readValue->clear();
ui->StatusBar->clear();
statusBarTimeOut.stop();
QModbusDataUnit writeUnit = writeRequest();
QModbusDataUnit::RegisterType table = writeUnit.registerType();
for (int i = 0, total = int(writeUnit.valueCount()); i < total; ++i) {
if (table == QModbusDataUnit::Coils)
writeUnit.setValue(i, writeModel->m_coils[i + writeUnit.startAddress()]);
else
writeUnit.setValue(i, writeModel->m_holdingRegisters[i + writeUnit.startAddress()]);
}
if (auto *reply = modbusDevice->sendReadWriteRequest(readRequest(), writeUnit,
ui->serverEdit->value())) {
if (!reply->isFinished())
connect(reply, &QModbusReply::finished, this, &union_modbus::onReadReady);
else
delete reply; // broadcast replies return immediately
} else {
ui->StatusBar->setText(tr("Read error: ") + modbusDevice->errorString());
statusBarTimeOut.start(5000);
}
}
void union_modbus::onWriteTableChanged(int index)
{
const bool coilsOrHolding = index == 0 || index == 3;
if (coilsOrHolding) {
ui->writeValueTable->setColumnHidden(1, index != 0);
ui->writeValueTable->setColumnHidden(2, index != 3);
ui->writeValueTable->resizeColumnToContents(0);
}
ui->buttonReadWrite->setEnabled(index == 3);
ui->buttonWrite->setEnabled(coilsOrHolding);
ui->layoutTX->setEnabled(coilsOrHolding);
}
QModbusDataUnit union_modbus::readRequest() const
{
const auto table =
static_cast<QModbusDataUnit::RegisterType>(ui->comboTable->currentData().toInt());
int startAddress = ui->spinRXStartAddress->value();
Q_ASSERT(startAddress >= 0 && startAddress < 340);
// do not go beyond 10 entries
quint16 numberOfEntries = qMin(ui->comboRXNumberOfValues->currentText().toUShort(), quint16(340 - startAddress));
return QModbusDataUnit(table, startAddress, numberOfEntries);
}
QModbusDataUnit union_modbus::writeRequest() const
{
const auto table =
static_cast<QModbusDataUnit::RegisterType>(ui->comboTable->currentData().toInt());
int startAddress = ui->spinTXStartAddress->value();
Q_ASSERT(startAddress >= 0 && startAddress < 340);
// do not go beyond 10 entries
quint16 numberOfEntries = qMin(ui->comboTXNumberOfValues->currentText().toUShort(), quint16(340 - startAddress));
return QModbusDataUnit(table, startAddress, numberOfEntries);
}
void union_modbus::on_checkPeriodRequest_stateChanged(int arg1)
{
if (ui->checkPeriodRequest->checkState()==Qt::Checked)
{
ui->spinPeriodTime->setEnabled(true);
}
else
{
ui->spinPeriodTime->setEnabled(false);
RequestPeriodTimer.stop();
}
}