added modbus commander
This commit is contained in:
parent
2e38fae9a7
commit
5ffe45baff
18
debian/control
vendored
18
debian/control
vendored
@ -5,6 +5,7 @@ Maintainer: Bernhard Trinnes <bernhard.trinnes@nymea.io>
|
||||
Build-depends: debhelper (>= 0.0.0),
|
||||
libnymea1-dev (>= 0.17),
|
||||
libnymea-mqtt-dev,
|
||||
libqt5serialbus5-dev,
|
||||
libqt5serialport5-dev,
|
||||
nymea-dev-tools:native,
|
||||
pkg-config,
|
||||
@ -46,6 +47,22 @@ Description: nymea.io plugin for Fronius PV inverters
|
||||
This package will install the nymea.io plugin for Fronius
|
||||
|
||||
|
||||
Package: nymea-plugin-modbuscommander
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Section: libs
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
Description: nymea.io plugin to send and receive generic modbus commands
|
||||
The nymea daemon is a plugin based IoT (Internet of Things) server. The
|
||||
server works like a translator for devices, things and services and
|
||||
allows them to interact.
|
||||
With the powerful rule engine you are able to connect any device available
|
||||
in the system and create individual scenes and behaviors for your environment.
|
||||
.
|
||||
This package will install the nymea.io modbus commander plug-in
|
||||
|
||||
|
||||
Package: nymea-plugin-mypv
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends},
|
||||
@ -97,6 +114,7 @@ Section: libs
|
||||
Architecture: all
|
||||
Depends: nymea-plugin-drexelundweiss,
|
||||
nymea-plugin-fronius,
|
||||
nymea-plugin-modbuscommander,
|
||||
nymea-plugin-mypv
|
||||
nymea-plugin-wallbe,
|
||||
Description: Plugins for nymea IoT server - the modbus plugin collection
|
||||
|
||||
1
debian/nymea-plugin-modbuscommander.install.in
vendored
Normal file
1
debian/nymea-plugin-modbuscommander.install.in
vendored
Normal file
@ -0,0 +1 @@
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginmodbuscommander.so
|
||||
28
modbuscommander/README.md
Normal file
28
modbuscommander/README.md
Normal file
@ -0,0 +1,28 @@
|
||||
# Modbus Commander
|
||||
|
||||
A nymea plugin to send Modbus commands.
|
||||
|
||||
## Supported Things
|
||||
|
||||
* Modbus TCP client
|
||||
* Gateway
|
||||
* Modbus RTU client
|
||||
* Gateway
|
||||
* Discovery setup
|
||||
* Coil
|
||||
* Writes and reads a single Modbus Coil
|
||||
* Discrete input
|
||||
* Reads a single Modbus discrete input
|
||||
* Input register
|
||||
* Reads a single Modbus input register
|
||||
* Holding register
|
||||
* Writes and reads a single Modbus holding register
|
||||
|
||||
## Requirements
|
||||
|
||||
* The package 'nymea-plugin-modbuscommander' must be installed.
|
||||
* For Modbus RTU a serial port must be available.
|
||||
|
||||
## More
|
||||
|
||||
http://www.modbus.org/
|
||||
580
modbuscommander/integrationpluginmodbuscommander.cpp
Normal file
580
modbuscommander/integrationpluginmodbuscommander.cpp
Normal file
@ -0,0 +1,580 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
|
||||
#include "integrationpluginmodbuscommander.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include <QSerialPort>
|
||||
|
||||
IntegrationPluginModbusCommander::IntegrationPluginModbusCommander()
|
||||
{
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::init()
|
||||
{
|
||||
connect(this, &IntegrationPluginModbusCommander::configValueChanged, this, &IntegrationPluginModbusCommander::onPluginConfigurationChanged);
|
||||
//QLoggingCategory::setFilterRules(QStringLiteral("qt.modbus* = false"));
|
||||
|
||||
m_slaveAddressParamTypeId.insert(coilThingClassId, coilThingSlaveAddressParamTypeId);
|
||||
m_slaveAddressParamTypeId.insert(inputRegisterThingClassId, inputRegisterThingSlaveAddressParamTypeId);
|
||||
m_slaveAddressParamTypeId.insert(discreteInputThingClassId, discreteInputThingSlaveAddressParamTypeId);
|
||||
m_slaveAddressParamTypeId.insert(holdingRegisterThingClassId, holdingRegisterThingSlaveAddressParamTypeId);
|
||||
|
||||
m_registerAddressParamTypeId.insert(coilThingClassId, coilThingRegisterAddressParamTypeId);
|
||||
m_registerAddressParamTypeId.insert(inputRegisterThingClassId, inputRegisterThingRegisterAddressParamTypeId);
|
||||
m_registerAddressParamTypeId.insert(discreteInputThingClassId, discreteInputThingRegisterAddressParamTypeId);
|
||||
m_registerAddressParamTypeId.insert(holdingRegisterThingClassId, holdingRegisterThingRegisterAddressParamTypeId);
|
||||
|
||||
m_connectedStateTypeId.insert(coilThingClassId, coilConnectedStateTypeId);
|
||||
m_connectedStateTypeId.insert(inputRegisterThingClassId, inputRegisterConnectedStateTypeId);
|
||||
m_connectedStateTypeId.insert(discreteInputThingClassId, discreteInputConnectedStateTypeId);
|
||||
m_connectedStateTypeId.insert(holdingRegisterThingClassId, holdingRegisterConnectedStateTypeId);
|
||||
|
||||
m_valueStateTypeId.insert(coilThingClassId, coilValueStateTypeId);
|
||||
m_valueStateTypeId.insert(inputRegisterThingClassId, inputRegisterValueStateTypeId);
|
||||
m_valueStateTypeId.insert(discreteInputThingClassId, discreteInputValueStateTypeId);
|
||||
m_valueStateTypeId.insert(holdingRegisterThingClassId, holdingRegisterValueStateTypeId);
|
||||
}
|
||||
|
||||
|
||||
void IntegrationPluginModbusCommander::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
|
||||
if (thing->thingClassId() == modbusTCPClientThingClassId) {
|
||||
QString ipAddress = thing->paramValue(modbusTCPClientThingIpv4addressParamTypeId).toString();
|
||||
uint port = thing->paramValue(modbusTCPClientThingPortParamTypeId).toUInt();
|
||||
|
||||
foreach (ModbusTCPMaster *modbusTCPMaster, m_modbusTCPMasters.values()) {
|
||||
if ((modbusTCPMaster->ipv4Address() == ipAddress) && (modbusTCPMaster->port() == port)){
|
||||
m_modbusTCPMasters.insert(thing, modbusTCPMaster);
|
||||
return info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
}
|
||||
|
||||
ModbusTCPMaster *modbusTCPMaster = new ModbusTCPMaster(ipAddress, port, this);
|
||||
connect(modbusTCPMaster, &ModbusTCPMaster::connectionStateChanged, this, &IntegrationPluginModbusCommander::onConnectionStateChanged);
|
||||
connect(modbusTCPMaster, &ModbusTCPMaster::requestExecuted, this, &IntegrationPluginModbusCommander::onRequestExecuted);
|
||||
connect(modbusTCPMaster, &ModbusTCPMaster::requestError, this, &IntegrationPluginModbusCommander::onRequestError);
|
||||
connect(modbusTCPMaster, &ModbusTCPMaster::receivedCoil, this, &IntegrationPluginModbusCommander::onReceivedCoil);
|
||||
connect(modbusTCPMaster, &ModbusTCPMaster::receivedDiscreteInput, this, &IntegrationPluginModbusCommander::onReceivedDiscreteInput);
|
||||
connect(modbusTCPMaster, &ModbusTCPMaster::receivedHoldingRegister, this, &IntegrationPluginModbusCommander::onReceivedHoldingRegister);
|
||||
connect(modbusTCPMaster, &ModbusTCPMaster::receivedInputRegister, this, &IntegrationPluginModbusCommander::onReceivedInputRegister);
|
||||
modbusTCPMaster->connectDevice();
|
||||
m_modbusTCPMasters.insert(thing, modbusTCPMaster);
|
||||
m_asyncTCPSetup.insert(modbusTCPMaster, info);
|
||||
return;
|
||||
|
||||
} else if (thing->thingClassId() == modbusRTUClientThingClassId) {
|
||||
|
||||
QString serialPort = thing->paramValue(modbusRTUClientThingSerialPortParamTypeId).toString();
|
||||
uint baudrate = thing->paramValue(modbusRTUClientThingBaudRateParamTypeId).toUInt();
|
||||
uint stopBits = thing->paramValue(modbusRTUClientThingStopBitsParamTypeId).toUInt();
|
||||
uint dataBits = thing->paramValue(modbusRTUClientThingDataBitsParamTypeId).toUInt();
|
||||
QSerialPort::Parity parity = QSerialPort::Parity::NoParity;
|
||||
if (thing->paramValue(modbusRTUClientThingParityParamTypeId).toString().contains("No")) {
|
||||
parity = QSerialPort::Parity::NoParity;
|
||||
} else if (thing->paramValue(modbusRTUClientThingParityParamTypeId).toString().contains("Even")) {
|
||||
parity = QSerialPort::Parity::EvenParity;
|
||||
} else if (thing->paramValue(modbusRTUClientThingParityParamTypeId).toString().contains("Odd")) {
|
||||
parity = QSerialPort::Parity::OddParity;
|
||||
}
|
||||
|
||||
ModbusRTUMaster *modbusRTUMaster = new ModbusRTUMaster(serialPort, baudrate, parity, dataBits, stopBits, this);
|
||||
connect(modbusRTUMaster, &ModbusRTUMaster::connectionStateChanged, this, &IntegrationPluginModbusCommander::onConnectionStateChanged);
|
||||
connect(modbusRTUMaster, &ModbusRTUMaster::requestExecuted, this, &IntegrationPluginModbusCommander::onRequestExecuted);
|
||||
connect(modbusRTUMaster, &ModbusRTUMaster::requestError, this, &IntegrationPluginModbusCommander::onRequestError);
|
||||
connect(modbusRTUMaster, &ModbusRTUMaster::receivedCoil, this, &IntegrationPluginModbusCommander::onReceivedCoil);
|
||||
connect(modbusRTUMaster, &ModbusRTUMaster::receivedDiscreteInput, this, &IntegrationPluginModbusCommander::onReceivedDiscreteInput);
|
||||
connect(modbusRTUMaster, &ModbusRTUMaster::receivedHoldingRegister, this, &IntegrationPluginModbusCommander::onReceivedHoldingRegister);
|
||||
connect(modbusRTUMaster, &ModbusRTUMaster::receivedInputRegister, this, &IntegrationPluginModbusCommander::onReceivedInputRegister);
|
||||
modbusRTUMaster->connectDevice();
|
||||
m_modbusRTUMasters.insert(thing, modbusRTUMaster);
|
||||
m_asyncRTUSetup.insert(modbusRTUMaster, info);
|
||||
return;
|
||||
|
||||
} else if ((thing->thingClassId() == coilThingClassId)
|
||||
|| (thing->thingClassId() == discreteInputThingClassId)
|
||||
|| (thing->thingClassId() == holdingRegisterThingClassId)
|
||||
|| (thing->thingClassId() == inputRegisterThingClassId)) {
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
qCWarning(dcModbusCommander()) << "Unhandled thing class in setupDevice!";
|
||||
info->finish(Thing::ThingErrorSetupFailed);
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::discoverThings(ThingDiscoveryInfo *info)
|
||||
{
|
||||
ThingClassId thingClassId = info->thingClassId();
|
||||
|
||||
if (thingClassId == modbusRTUClientThingClassId) {
|
||||
Q_FOREACH(QSerialPortInfo port, QSerialPortInfo::availablePorts()) {
|
||||
//Serial port is not yet used, create now a new one
|
||||
qCDebug(dcModbusCommander()) << "Found serial port:" << port.systemLocation();
|
||||
QString description = port.manufacturer() + " " + port.description();
|
||||
ThingDescriptor thingDescriptor(thingClassId, port.portName(), description);
|
||||
ParamList parameters;
|
||||
QString serialPort = port.systemLocation();
|
||||
foreach (Thing *existingThing, myThings()) {
|
||||
if (existingThing->paramValue(modbusRTUClientThingSerialPortParamTypeId).toString() == serialPort) {
|
||||
thingDescriptor.setThingId(existingThing->id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
parameters.append(Param(modbusRTUClientThingSerialPortParamTypeId, serialPort));
|
||||
thingDescriptor.setParams(parameters);
|
||||
info->addThingDescriptor(thingDescriptor);
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
} else if (thingClassId == discreteInputThingClassId) {
|
||||
Q_FOREACH(Thing *clientThing, myThings()){
|
||||
if (clientThing->thingClassId() == modbusTCPClientThingClassId) {
|
||||
ThingDescriptor descriptor(thingClassId, "Discrete input", clientThing->name() + " " + clientThing->paramValue(modbusTCPClientThingIpv4addressParamTypeId).toString() + " Port: " + clientThing->paramValue(modbusTCPClientThingPortParamTypeId).toString());
|
||||
descriptor.setParentId(clientThing->id());
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
if (clientThing->thingClassId() == modbusRTUClientThingClassId) {
|
||||
ThingDescriptor descriptor(thingClassId, "Discrete input", clientThing->name() + " " + clientThing->paramValue(modbusRTUClientThingSerialPortParamTypeId).toString());
|
||||
descriptor.setParentId(clientThing->id());
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
|
||||
} else if (thingClassId == coilThingClassId) {
|
||||
Q_FOREACH(Thing *clientThing, myThings()){
|
||||
if (clientThing->thingClassId() == modbusTCPClientThingClassId) {
|
||||
ThingDescriptor descriptor(thingClassId, "Coil", clientThing->name() + " " + clientThing->paramValue(modbusTCPClientThingIpv4addressParamTypeId).toString() + " Port: " + clientThing->paramValue(modbusTCPClientThingPortParamTypeId).toString());
|
||||
descriptor.setParentId(clientThing->id());
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
if (clientThing->thingClassId() == modbusRTUClientThingClassId) {
|
||||
ThingDescriptor descriptor(thingClassId, "Coil", clientThing->name() + " " + clientThing->paramValue(modbusRTUClientThingSerialPortParamTypeId).toString());
|
||||
descriptor.setParentId(clientThing->id());
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
} else if (thingClassId == holdingRegisterThingClassId) {
|
||||
Q_FOREACH(Thing *clientThing, myThings()){
|
||||
if (clientThing->thingClassId() == modbusTCPClientThingClassId) {
|
||||
ThingDescriptor descriptor(thingClassId, "Holding register", clientThing->name() + " " + clientThing->paramValue(modbusTCPClientThingIpv4addressParamTypeId).toString() + " Port: " + clientThing->paramValue(modbusTCPClientThingPortParamTypeId).toString());
|
||||
descriptor.setParentId(clientThing->id());
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
if (clientThing->thingClassId() == modbusRTUClientThingClassId) {
|
||||
ThingDescriptor descriptor(thingClassId, "Holding register", clientThing->name() + " " + clientThing->paramValue(modbusRTUClientThingSerialPortParamTypeId).toString());
|
||||
descriptor.setParentId(clientThing->id());
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
|
||||
} else if (thingClassId == inputRegisterThingClassId) {
|
||||
Q_FOREACH(Thing *clientThing, myThings()){
|
||||
if (clientThing->thingClassId() == modbusTCPClientThingClassId) {
|
||||
ThingDescriptor descriptor(thingClassId, "Input register", clientThing->name() + " " + clientThing->paramValue(modbusTCPClientThingIpv4addressParamTypeId).toString() + " Port: " + clientThing->paramValue(modbusTCPClientThingPortParamTypeId).toString());
|
||||
descriptor.setParentId(clientThing->id());
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
if (clientThing->thingClassId() == modbusRTUClientThingClassId) {
|
||||
ThingDescriptor descriptor(thingClassId, "Input register", clientThing->name() + " " + clientThing->paramValue(modbusRTUClientThingSerialPortParamTypeId).toString());
|
||||
descriptor.setParentId(clientThing->id());
|
||||
info->addThingDescriptor(descriptor);
|
||||
}
|
||||
}
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
info->finish(Thing::ThingErrorThingClassNotFound);
|
||||
qCWarning(dcModbusCommander()) << "Unhandled device class in discovery!";
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::postSetupThing(Thing *info)
|
||||
{
|
||||
if (!m_refreshTimer) {
|
||||
// Refresh timer for TCP read
|
||||
int refreshTime = configValue(modbusCommanderPluginUpdateIntervalParamTypeId).toInt();
|
||||
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(refreshTime);
|
||||
connect(m_refreshTimer, &PluginTimer::timeout, this, &IntegrationPluginModbusCommander::onRefreshTimer);
|
||||
}
|
||||
|
||||
if ((info->thingClassId() == coilThingClassId) ||
|
||||
(info->thingClassId() == discreteInputThingClassId) ||
|
||||
(info->thingClassId() == holdingRegisterThingClassId) ||
|
||||
(info->thingClassId() == inputRegisterThingClassId)) {
|
||||
readRegister(info);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IntegrationPluginModbusCommander::executeAction(ThingActionInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
|
||||
if (thing->thingClassId() == coilThingClassId) {
|
||||
|
||||
if (info->action().actionTypeId() == coilValueActionTypeId) {
|
||||
writeRegister(thing, info);
|
||||
return;
|
||||
}
|
||||
} else if (thing->thingClassId() == holdingRegisterThingClassId) {
|
||||
|
||||
if (info->action().actionTypeId() == holdingRegisterValueActionTypeId) {
|
||||
writeRegister(thing, info);
|
||||
return;
|
||||
}
|
||||
}
|
||||
qCWarning(dcModbusCommander()) << "Unhandled deviceclass/actiontype in executeAction!";
|
||||
info->finish(Thing::ThingErrorThingClassNotFound);
|
||||
}
|
||||
|
||||
|
||||
void IntegrationPluginModbusCommander::thingRemoved(Thing *thing)
|
||||
{
|
||||
if (thing->thingClassId() == modbusTCPClientThingClassId) {
|
||||
ModbusTCPMaster *modbus = m_modbusTCPMasters.take(thing);
|
||||
modbus->deleteLater();
|
||||
}
|
||||
|
||||
if (thing->thingClassId() == modbusRTUClientThingClassId) {
|
||||
ModbusRTUMaster *modbus = m_modbusRTUMasters.take(thing);
|
||||
modbus->deleteLater();
|
||||
}
|
||||
|
||||
if (myThings().empty()) {
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
|
||||
m_refreshTimer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::onRefreshTimer()
|
||||
{
|
||||
foreach (Thing *thing, myThings()) {
|
||||
if ((thing->thingClassId() == coilThingClassId) ||
|
||||
(thing->thingClassId() == discreteInputThingClassId) ||
|
||||
(thing->thingClassId() == holdingRegisterThingClassId) ||
|
||||
(thing->thingClassId() == inputRegisterThingClassId)) {
|
||||
readRegister(thing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value)
|
||||
{
|
||||
// Check refresh schedule
|
||||
if (paramTypeId == modbusCommanderPluginUpdateIntervalParamTypeId) {;
|
||||
if (m_refreshTimer) {
|
||||
uint refreshTime = value.toUInt();
|
||||
m_refreshTimer->stop();
|
||||
m_refreshTimer->startTimer(refreshTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::onConnectionStateChanged(bool status)
|
||||
{
|
||||
auto modbus = sender();
|
||||
|
||||
if (m_asyncRTUSetup.contains(static_cast<ModbusRTUMaster *>(modbus))) {
|
||||
ThingSetupInfo *info = m_asyncRTUSetup.take(static_cast<ModbusRTUMaster *>(modbus));
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
|
||||
} else if (m_asyncTCPSetup.contains(static_cast<ModbusTCPMaster *>(modbus))) {
|
||||
ThingSetupInfo *info = m_asyncTCPSetup.take(static_cast<ModbusTCPMaster *>(modbus));
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
if (m_modbusRTUMasters.values().contains(static_cast<ModbusRTUMaster *>(modbus))) {
|
||||
Thing *thing = m_modbusRTUMasters.key(static_cast<ModbusRTUMaster *>(modbus));
|
||||
thing->setStateValue(modbusRTUClientConnectedStateTypeId, status);
|
||||
} else if (m_modbusTCPMasters.values().contains(static_cast<ModbusTCPMaster *>(modbus))) {
|
||||
Thing *thing = m_modbusTCPMasters.key(static_cast<ModbusTCPMaster *>(modbus));
|
||||
thing->setStateValue(modbusTCPClientConnectedStateTypeId, status);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::onRequestExecuted(QUuid requestId, bool success)
|
||||
{
|
||||
if (m_asyncActions.contains(requestId)){
|
||||
ThingActionInfo *info = m_asyncActions.take(requestId);
|
||||
if (success){
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
} else {
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
}
|
||||
info->thing()->setStateValue(m_connectedStateTypeId.value(info->thing()->thingClassId()), success);
|
||||
}
|
||||
|
||||
if (m_readRequests.contains(requestId)){
|
||||
Thing *thing = m_readRequests.take(requestId);
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), success);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::onRequestError(QUuid requestId, const QString &error)
|
||||
{
|
||||
if (m_asyncActions.contains(requestId)){
|
||||
ThingActionInfo *info = m_asyncActions.take(requestId);
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable, error);
|
||||
info->thing()->setStateValue(m_connectedStateTypeId.value(info->thing()->thingClassId()), false);
|
||||
}
|
||||
|
||||
if (m_readRequests.contains(requestId)){
|
||||
Thing *thing = m_readRequests.take(requestId);
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), false);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::onReceivedCoil(quint32 slaveAddress, quint32 modbusRegister, bool value)
|
||||
{
|
||||
auto modbus = sender();
|
||||
|
||||
if (m_modbusRTUMasters.values().contains(static_cast<ModbusRTUMaster *>(modbus))) {
|
||||
Thing *parent = m_modbusRTUMasters.key(static_cast<ModbusRTUMaster *>(modbus));
|
||||
foreach (Thing *thing, myThings().filterByParentId(parent->id())) {
|
||||
if (thing->thingClassId() == coilThingClassId) {
|
||||
if ((thing->paramValue(m_slaveAddressParamTypeId.value(thing->thingClassId())) == slaveAddress)
|
||||
&& (thing->paramValue(m_registerAddressParamTypeId.value(thing->thingClassId())) == modbusRegister)) {
|
||||
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), value);
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (m_modbusTCPMasters.values().contains(static_cast<ModbusTCPMaster *>(modbus))) {
|
||||
Thing *parent = m_modbusTCPMasters.key(static_cast<ModbusTCPMaster *>(modbus));
|
||||
foreach (Thing *thing, myThings().filterByParentId(parent->id())) {
|
||||
if (thing->thingClassId() == coilThingClassId) {
|
||||
if ((thing->paramValue(m_slaveAddressParamTypeId.value(thing->thingClassId())) == slaveAddress)
|
||||
&& (thing->paramValue(m_registerAddressParamTypeId.value(thing->thingClassId())) == modbusRegister)) {
|
||||
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), value);
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::onReceivedDiscreteInput(quint32 slaveAddress, quint32 modbusRegister, bool value)
|
||||
{
|
||||
auto modbus = sender();
|
||||
|
||||
if (m_modbusRTUMasters.values().contains(static_cast<ModbusRTUMaster *>(modbus))) {
|
||||
Thing *parent = m_modbusRTUMasters.key(static_cast<ModbusRTUMaster *>(modbus));
|
||||
foreach (Thing *thing, myThings().filterByParentId(parent->id())) {
|
||||
if (thing->thingClassId() == discreteInputThingClassId) {
|
||||
if ((thing->paramValue(m_slaveAddressParamTypeId.value(thing->thingClassId())) == slaveAddress)
|
||||
&& (thing->paramValue(m_registerAddressParamTypeId.value(thing->thingClassId())) == modbusRegister)) {
|
||||
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), value);
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (m_modbusTCPMasters.values().contains(static_cast<ModbusTCPMaster *>(modbus))) {
|
||||
Thing *parent = m_modbusTCPMasters.key(static_cast<ModbusTCPMaster *>(modbus));
|
||||
foreach (Thing *thing, myThings().filterByParentId(parent->id())) {
|
||||
if (thing->thingClassId() == discreteInputThingClassId) {
|
||||
if ((thing->paramValue(m_slaveAddressParamTypeId.value(thing->thingClassId())) == slaveAddress)
|
||||
&& (thing->paramValue(m_registerAddressParamTypeId.value(thing->thingClassId())) == modbusRegister)) {
|
||||
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), value);
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, int value)
|
||||
{
|
||||
auto modbus = sender();
|
||||
|
||||
if (m_modbusRTUMasters.values().contains(static_cast<ModbusRTUMaster *>(modbus))) {
|
||||
Thing *parent = m_modbusRTUMasters.key(static_cast<ModbusRTUMaster *>(modbus));
|
||||
foreach (Thing *thing, myThings().filterByParentId(parent->id())) {
|
||||
if (thing->thingClassId() == holdingRegisterThingClassId) {
|
||||
if ((thing->paramValue(m_slaveAddressParamTypeId.value(thing->thingClassId())) == slaveAddress)
|
||||
&& (thing->paramValue(m_registerAddressParamTypeId.value(thing->thingClassId())) == modbusRegister)) {
|
||||
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), value);
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (m_modbusTCPMasters.values().contains(static_cast<ModbusTCPMaster *>(modbus))) {
|
||||
Thing *parent = m_modbusTCPMasters.key(static_cast<ModbusTCPMaster *>(modbus));
|
||||
foreach (Thing *thing, myThings().filterByParentId(parent->id())) {
|
||||
if (thing->thingClassId() == holdingRegisterThingClassId) {
|
||||
if ((thing->paramValue(m_slaveAddressParamTypeId.value(thing->thingClassId())) == slaveAddress)
|
||||
&& (thing->paramValue(m_registerAddressParamTypeId.value(thing->thingClassId())) == modbusRegister)) {
|
||||
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), value);
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::onReceivedInputRegister(uint slaveAddress, uint modbusRegister, int value)
|
||||
{
|
||||
auto modbus = sender();
|
||||
|
||||
if (m_modbusRTUMasters.values().contains(static_cast<ModbusRTUMaster *>(modbus))) {
|
||||
Thing *parent = m_modbusRTUMasters.key(static_cast<ModbusRTUMaster *>(modbus));
|
||||
foreach (Thing *thing, myThings().filterByParentId(parent->id())) {
|
||||
if (thing->thingClassId() == inputRegisterThingClassId) {
|
||||
if ((thing->paramValue(m_slaveAddressParamTypeId.value(thing->thingClassId())) == slaveAddress)
|
||||
&& (thing->paramValue(m_registerAddressParamTypeId.value(thing->thingClassId())) == modbusRegister)) {
|
||||
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), value);
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (m_modbusTCPMasters.values().contains(static_cast<ModbusTCPMaster *>(modbus))) {
|
||||
Thing *parent = m_modbusTCPMasters.key(static_cast<ModbusTCPMaster *>(modbus));
|
||||
foreach (Thing *thing, myThings().filterByParentId(parent->id())) {
|
||||
if (thing->thingClassId() == inputRegisterThingClassId) {
|
||||
if ((thing->paramValue(m_slaveAddressParamTypeId.value(thing->thingClassId())) == slaveAddress)
|
||||
&& (thing->paramValue(m_registerAddressParamTypeId.value(thing->thingClassId())) == modbusRegister)) {
|
||||
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), value);
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::readRegister(Thing *thing)
|
||||
{
|
||||
Thing *parent = myThings().findById(thing->parentId());
|
||||
if (!parent) {
|
||||
qCWarning(dcModbusCommander()) << "Could not find parent device" << thing->name();
|
||||
return;
|
||||
}
|
||||
|
||||
uint registerAddress = thing->paramValue(m_registerAddressParamTypeId.value(thing->thingClassId())).toUInt();;
|
||||
uint slaveAddress = thing->paramValue(m_slaveAddressParamTypeId.value(thing->thingClassId())).toUInt();
|
||||
|
||||
QUuid requestId;
|
||||
|
||||
if (parent->thingClassId() == modbusTCPClientThingClassId) {
|
||||
ModbusTCPMaster *modbus = m_modbusTCPMasters.value(parent);
|
||||
if (!modbus)
|
||||
return;
|
||||
|
||||
if (thing->thingClassId() == coilThingClassId) {
|
||||
requestId = modbus->readCoil(slaveAddress, registerAddress);
|
||||
} else if (thing->thingClassId() == discreteInputThingClassId) {
|
||||
requestId = modbus->readDiscreteInput(slaveAddress, registerAddress);
|
||||
} else if (thing->thingClassId() == holdingRegisterThingClassId) {
|
||||
requestId = modbus->readHoldingRegister(slaveAddress, registerAddress);
|
||||
} else if (thing->thingClassId() == inputRegisterThingClassId) {
|
||||
requestId = modbus->readInputRegister(slaveAddress, registerAddress);
|
||||
}
|
||||
} else if (parent->thingClassId() == modbusRTUClientThingClassId) {
|
||||
|
||||
ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parent);
|
||||
if (!modbus)
|
||||
return;
|
||||
|
||||
if (thing->thingClassId() == coilThingClassId) {
|
||||
requestId = modbus->readCoil(slaveAddress, registerAddress);
|
||||
} else if (thing->thingClassId() == discreteInputThingClassId) {
|
||||
requestId = modbus->readDiscreteInput(slaveAddress, registerAddress);
|
||||
} else if (thing->thingClassId() == holdingRegisterThingClassId) {
|
||||
requestId = modbus->readHoldingRegister(slaveAddress, registerAddress);
|
||||
} else if (thing->thingClassId() == inputRegisterThingClassId) {
|
||||
requestId = modbus->readInputRegister(slaveAddress, registerAddress);
|
||||
}
|
||||
}
|
||||
if (!requestId.isNull()) {
|
||||
m_readRequests.insert(requestId, thing);
|
||||
QTimer::singleShot(5000, this, [requestId, this] {m_readRequests.remove(requestId);});
|
||||
} else {
|
||||
// Request returned without an id
|
||||
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), false);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginModbusCommander::writeRegister(Thing *thing, ThingActionInfo *info)
|
||||
{
|
||||
Thing *parent = myThings().findById(thing->parentId());
|
||||
if (!parent) {
|
||||
qCWarning(dcModbusCommander()) << "Could not find parente device" << thing->name();
|
||||
return;
|
||||
}
|
||||
uint registerAddress = thing->paramValue(m_registerAddressParamTypeId.value(thing->thingClassId())).toUInt();;
|
||||
uint slaveAddress = thing->paramValue(m_slaveAddressParamTypeId.value(thing->thingClassId())).toUInt();
|
||||
|
||||
QUuid requestId;
|
||||
Action action = info->action();
|
||||
|
||||
if (parent->thingClassId() == modbusTCPClientThingClassId) {
|
||||
ModbusTCPMaster *modbus = m_modbusTCPMasters.value(parent);
|
||||
if (!modbus)
|
||||
return;
|
||||
|
||||
if (thing->thingClassId() == coilThingClassId) {
|
||||
requestId = modbus->writeCoil(slaveAddress, registerAddress, action.param(coilValueActionValueParamTypeId).value().toBool());
|
||||
} else if (thing->thingClassId() == holdingRegisterThingClassId) {
|
||||
requestId = modbus->writeHoldingRegister(slaveAddress, registerAddress, action.param(holdingRegisterValueActionValueParamTypeId).value().toUInt());
|
||||
}
|
||||
|
||||
} else if (parent->thingClassId() == modbusRTUClientThingClassId) {
|
||||
ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parent);
|
||||
if (!modbus)
|
||||
return;
|
||||
|
||||
if (thing->thingClassId() == coilThingClassId) {
|
||||
requestId = modbus->writeCoil(slaveAddress, registerAddress, action.param(coilValueActionValueParamTypeId).value().toBool());
|
||||
} else if (thing->thingClassId() == holdingRegisterThingClassId) {
|
||||
requestId = modbus->writeHoldingRegister(slaveAddress, registerAddress, action.param(holdingRegisterValueActionValueParamTypeId).value().toUInt());
|
||||
}
|
||||
}
|
||||
|
||||
if (requestId.toString().isNull()){
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
} else {
|
||||
m_asyncActions.insert(requestId, info);
|
||||
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
|
||||
}
|
||||
}
|
||||
92
modbuscommander/integrationpluginmodbuscommander.h
Normal file
92
modbuscommander/integrationpluginmodbuscommander.h
Normal file
@ -0,0 +1,92 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef INTEGRATIONPLUGINMODBUSCOMMANDER_H
|
||||
#define INTEGRATIONPLUGINMODBUSCOMMANDER_H
|
||||
|
||||
#include "integrations/integrationplugin.h"
|
||||
#include "plugintimer.h"
|
||||
#include "modbustcpmaster.h"
|
||||
#include "modbusrtumaster.h"
|
||||
|
||||
#include <QSerialPortInfo>
|
||||
#include <QUuid>
|
||||
|
||||
class IntegrationPluginModbusCommander: public IntegrationPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginmodbuscommander.json")
|
||||
Q_INTERFACES(IntegrationPlugin)
|
||||
|
||||
public:
|
||||
explicit IntegrationPluginModbusCommander();
|
||||
|
||||
void init() override;
|
||||
void discoverThings(ThingDiscoveryInfo *info) override;
|
||||
void setupThing(ThingSetupInfo *info) override;
|
||||
void postSetupThing(Thing *info) override;
|
||||
void executeAction(ThingActionInfo *info) override;
|
||||
void thingRemoved(Thing *thing) override;
|
||||
|
||||
private:
|
||||
PluginTimer *m_refreshTimer = nullptr;
|
||||
|
||||
QHash<Thing*, ModbusRTUMaster*> m_modbusRTUMasters;
|
||||
QHash<Thing*, ModbusTCPMaster*> m_modbusTCPMasters;
|
||||
QHash<QUuid, ThingActionInfo*> m_asyncActions;
|
||||
QHash<QUuid, Thing*> m_readRequests;
|
||||
|
||||
QHash<ModbusRTUMaster*, ThingSetupInfo *> m_asyncRTUSetup;
|
||||
QHash<ModbusTCPMaster*, ThingSetupInfo *> m_asyncTCPSetup;
|
||||
|
||||
void readRegister(Thing *thing);
|
||||
void writeRegister(Thing *thing, ThingActionInfo *info);
|
||||
|
||||
QHash<ThingClassId, ParamTypeId> m_slaveAddressParamTypeId;
|
||||
QHash<ThingClassId, ParamTypeId> m_registerAddressParamTypeId;
|
||||
QHash<ThingClassId, StateTypeId> m_connectedStateTypeId;
|
||||
QHash<ThingClassId, StateTypeId> m_valueStateTypeId;
|
||||
|
||||
private slots:
|
||||
void onRefreshTimer();
|
||||
|
||||
void onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value);
|
||||
|
||||
void onConnectionStateChanged(bool status);
|
||||
void onRequestExecuted(QUuid requestId, bool success);
|
||||
void onRequestError(QUuid requestId, const QString &error);
|
||||
void onReceivedCoil(quint32 slaveAddress, quint32 modbusRegister, bool value);
|
||||
void onReceivedDiscreteInput(quint32 slaveAddress, quint32 modbusRegister, bool value);
|
||||
void onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, int value);
|
||||
void onReceivedInputRegister(quint32 slaveAddress, quint32 modbusRegister, int value);
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINMODBUSCOMMANDER_H
|
||||
287
modbuscommander/integrationpluginmodbuscommander.json
Normal file
287
modbuscommander/integrationpluginmodbuscommander.json
Normal file
@ -0,0 +1,287 @@
|
||||
{
|
||||
"displayName": "Modbus Commander",
|
||||
"name": "ModbusCommander",
|
||||
"id": "7dda1b6d-c37e-4c9f-a696-1666f9de66e6",
|
||||
"paramTypes":[
|
||||
{
|
||||
"id": "0606c221-b157-4086-885d-7e7b166540a1",
|
||||
"name": "updateInterval",
|
||||
"displayName": "Update interval",
|
||||
"type": "uint",
|
||||
"unit": "Seconds",
|
||||
"defaultValue": 1
|
||||
}
|
||||
],
|
||||
"vendors": [
|
||||
{
|
||||
"name": "nymea",
|
||||
"displayName": "nymea",
|
||||
"id": "2062d64d-3232-433c-88bc-0d33c0ba2ba6",
|
||||
"thingClasses": [
|
||||
{
|
||||
"id": "35d3e7dc-1f33-4b8c-baa3-eb10b4f157a7",
|
||||
"name": "modbusTCPClient",
|
||||
"displayName": "Modbus TCP client",
|
||||
"createMethods": ["user"],
|
||||
"interfaces": ["connectable"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "2a3fcb23-931b-4ba1-b134-c49b656c76f7",
|
||||
"name": "ipv4address",
|
||||
"displayName": "IPv4 address",
|
||||
"type": "QString",
|
||||
"inputType": "IPv4Address",
|
||||
"defaultValue": "127.0.0.1"
|
||||
},
|
||||
{
|
||||
"id": "bee8b151-815a-4159-9d8a-42b76e99b42c",
|
||||
"name": "port",
|
||||
"displayName": "Port",
|
||||
"type": "uint",
|
||||
"defaultValue": 502
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "725b541a-9e0c-4634-81eb-e415c0b8f012",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connection status changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "776df314-6186-4eb5-b824-f0d916f6d9c3",
|
||||
"name": "modbusRTUClient",
|
||||
"displayName": "Modbus RTU client",
|
||||
"createMethods": ["discovery", "user"],
|
||||
"interfaces": ["connectable"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "ed49f7d8-ab18-4c37-9b80-1004b75dcb91",
|
||||
"name": "serialPort",
|
||||
"displayName": "Serial port",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"defaultValue": "ttyAMA0"
|
||||
},
|
||||
{
|
||||
"id": "45dfc828-f238-4263-89a3-9b35cf5dea39",
|
||||
"name": "baudRate",
|
||||
"displayName": "Baud rate",
|
||||
"type": "uint",
|
||||
"defaultValue": 9600
|
||||
},
|
||||
{
|
||||
"id": "a27c664b-9f43-4573-a2cc-f65a8fa1a069",
|
||||
"name": "dataBits",
|
||||
"displayName": "Data bits",
|
||||
"type": "uint",
|
||||
"defaultValue": 8
|
||||
},
|
||||
{
|
||||
"id": "4ea8bcdf-d4c5-45a4-a54f-f10ac3f08a78",
|
||||
"name": "stopBits",
|
||||
"displayName": "Stop bits",
|
||||
"type": "uint",
|
||||
"defaultValue": 1
|
||||
},
|
||||
{
|
||||
"id": "72de1b08-2a27-49c5-90e0-8788c3ea1da3",
|
||||
"name": "parity",
|
||||
"displayName": "Parity",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"allowedValues": [
|
||||
"No Parity",
|
||||
"Even Parity",
|
||||
"Odd Parity"
|
||||
],
|
||||
"defaultValue": "Even Parity"
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "dffc59fe-b230-4345-81d6-0a55f9e16520",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connection status changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "f53524ea-1d06-40a9-b7a4-041297b21e84",
|
||||
"name": "coil",
|
||||
"displayName": "Coil",
|
||||
"createMethods": ["discovery"],
|
||||
"interfaces": ["connectable"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "d85977a2-4f9c-40f8-9aff-76cea7bd17a3",
|
||||
"name": "slaveAddress",
|
||||
"displayName": "Slave address",
|
||||
"type": "uint",
|
||||
"defaultValue": 180
|
||||
},
|
||||
{
|
||||
"id": "9d40c4ce-d251-43bb-a55e-a8780567bbac",
|
||||
"name": "registerAddress",
|
||||
"displayName": "Register address",
|
||||
"type": "uint",
|
||||
"defaultValue": 100
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "9b3852ac-1518-4417-8a0a-452fcfec8963",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connection status changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "1cd4cd53-3043-4ed9-9ba8-62985000c599",
|
||||
"name": "value",
|
||||
"displayName": "Value",
|
||||
"displayNameAction": "Write value",
|
||||
"displayNameEvent": "Value changed",
|
||||
"type": "bool",
|
||||
"writable": true,
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "d7a15b39-48d3-4591-bdad-ec5e799aa6e5",
|
||||
"name": "discreteInput",
|
||||
"displayName": "Discrete input",
|
||||
"createMethods": ["discovery"],
|
||||
"interfaces": ["connectable"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "044d951d-7b58-4099-a9a6-a6dff61746a8",
|
||||
"name": "slaveAddress",
|
||||
"displayName": "Slave address",
|
||||
"type": "uint",
|
||||
"defaultValue": 180
|
||||
},
|
||||
{
|
||||
"id": "d37be0cc-6155-4894-b70f-cbc9adfbe48b",
|
||||
"name": "registerAddress",
|
||||
"displayName": "Register address",
|
||||
"type": "uint",
|
||||
"defaultValue": 100
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "dbe7c801-0888-4e7f-a88b-ba342efb11b6",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"displayNameEvent": "connection status changed"
|
||||
},
|
||||
{
|
||||
"id": "c772bd7f-6e51-4b28-b182-3b979c1298ce",
|
||||
"name": "value",
|
||||
"displayName": "Value",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"displayNameEvent": "value changed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "e4c34050-d115-440f-b332-63d36e3e12b8",
|
||||
"name": "inputRegister",
|
||||
"displayName": "Input register",
|
||||
"createMethods": ["discovery"],
|
||||
"interfaces": ["connectable"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "f66956ac-07cb-45ab-90e0-61c2a950b85a",
|
||||
"name": "slaveAddress",
|
||||
"displayName": "Slave address",
|
||||
"type": "uint",
|
||||
"defaultValue": 180
|
||||
},
|
||||
{
|
||||
"id": "264e381c-d259-4e11-b4b3-332b518ebba3",
|
||||
"name": "registerAddress",
|
||||
"displayName": "Register address",
|
||||
"type": "uint",
|
||||
"defaultValue": 100
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "0f3768cf-5fb2-4fbf-8614-8389f65f1e9d",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"displayNameEvent": "Connection status changed"
|
||||
},
|
||||
{
|
||||
"id": "eabe2d1b-abe5-4063-adab-3cdd8500b286",
|
||||
"name": "Value",
|
||||
"displayName": "Value",
|
||||
"type": "int",
|
||||
"defaultValue": 0,
|
||||
"displayNameEvent": "Value received"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "61a2382c-3d9f-41a1-a2fd-27b2af203c56",
|
||||
"name": "holdingRegister",
|
||||
"displayName": "Holding register",
|
||||
"createMethods": ["discovery"],
|
||||
"interfaces": ["connectable"],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "35879cf9-631c-4fe0-95c0-a4bb2e9039e6",
|
||||
"name": "slaveAddress",
|
||||
"displayName": "Slave address",
|
||||
"type": "uint",
|
||||
"defaultValue": 180
|
||||
},
|
||||
{
|
||||
"id": "c771e09e-15fe-4ea9-9662-c44e2df556a8",
|
||||
"name": "registerAddress",
|
||||
"displayName": "Register address",
|
||||
"type": "uint",
|
||||
"defaultValue": 100
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "1f55b72a-5d13-4ae1-b136-bfd84fd9761f",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connection status changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "585cc4fc-07da-415f-a176-12f3baeef025",
|
||||
"name": "value",
|
||||
"displayName": "Value",
|
||||
"displayNameAction": "Write value",
|
||||
"displayNameEvent": "Value changed",
|
||||
"type": "int",
|
||||
"writable": true,
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
14
modbuscommander/meta.json
Normal file
14
modbuscommander/meta.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"title": "Modbus Commander",
|
||||
"tagline": "Write and read Modbus register.",
|
||||
"icon": "modbus.svg",
|
||||
"stability": "community",
|
||||
"offline": true,
|
||||
"technologies": [
|
||||
"serial-port",
|
||||
"network"
|
||||
],
|
||||
"categories": [
|
||||
"tool"
|
||||
]
|
||||
}
|
||||
59
modbuscommander/modbus.svg
Normal file
59
modbuscommander/modbus.svg
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="layer" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 652 652" style="enable-background:new 0 0 652 652;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#F49500;}
|
||||
.st1{fill:#FCC000;}
|
||||
.st2{fill:#03A239;}
|
||||
.st3{fill:#1192CF;}
|
||||
</style>
|
||||
<path class="st0" d="M189.4,323.1c0-36.5-31-66.1-69.3-66.1c-38.3,0-69.3,29.6-69.3,66.1c0,36.5,31,66.1,69.3,66.1
|
||||
C158.4,389.1,189.4,359.6,189.4,323.1"/>
|
||||
<path class="st1" d="M127.3,251.6c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8
|
||||
C115.7,225.8,127.3,237.4,127.3,251.6"/>
|
||||
<path class="st1" d="M198.9,273.5c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8
|
||||
C187.4,247.7,198.9,259.3,198.9,273.5"/>
|
||||
<path class="st1" d="M223.5,341.5c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8
|
||||
C212,315.8,223.5,327.3,223.5,341.5"/>
|
||||
<path class="st1" d="M168.9,391.3c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8
|
||||
C157.3,365.5,168.9,377,168.9,391.3"/>
|
||||
<path class="st1" d="M94.3,373.4c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8
|
||||
C82.7,347.6,94.3,359.2,94.3,373.4"/>
|
||||
<path class="st1" d="M73.5,303.1c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8
|
||||
C61.9,277.3,73.5,288.9,73.5,303.1"/>
|
||||
<polygon class="st2" points="125.3,308 123.4,301.4 119.4,309 105.1,259.6 112.3,263.8 110.5,257.5 100.5,252 94.9,262 96.8,268.3
|
||||
100.6,260.9 115,310.3 107.6,306 109.5,312.6 119.6,318 "/>
|
||||
<polygon class="st2" points="137.7,320.1 142.1,316 134.1,315.4 167.7,284.3 167.7,292.1 171.9,288.2 171.7,277.3 160.9,276.3
|
||||
156.7,280.3 164.5,280.8 130.9,312 131,303.9 126.5,308.1 126.8,319 "/>
|
||||
<polygon class="st2" points="82.1,373.4 86.6,368.9 78.4,368.7 112.1,335.5 112.2,343.5 116.5,339.3 116.2,328.2 105.1,327.7
|
||||
100.9,331.9 108.8,332.1 75.1,365.4 75,357.2 70.5,361.6 71,372.7 "/>
|
||||
<polygon class="st2" points="108.7,312.2 102.3,310.2 106.4,317.6 58.1,302.8 65.4,299 59.3,297.1 49.3,302.5 54.6,312.6
|
||||
60.7,314.5 56.8,307.3 105,322.1 97.5,325.9 103.9,327.9 113.9,322.4 "/>
|
||||
<polygon class="st2" points="147.9,378.9 145.9,373 142.1,380.2 127.4,336 134.6,339.3 132.7,333.7 122.6,329.3 117.3,338.8
|
||||
119.2,344.4 122.9,337.5 137.7,381.7 130.3,378.2 132.3,384.1 142.4,388.5 "/>
|
||||
<polygon class="st2" points="183.6,332.6 177.6,330.8 181.3,338.1 136.5,325.1 143.3,321.1 137.7,319.5 128.3,325.2 133.2,335
|
||||
138.8,336.7 135.2,329.6 180,342.5 173,346.6 179,348.4 188.3,342.5 "/>
|
||||
<polygon class="st3" points="129.6,357.5 143.8,286.8 176.2,286.8 190.8,329.3 222.5,286.8 255,286.8 240.6,357.5 220.6,357.5
|
||||
230.4,307.5 193.3,357.5 177.6,357.5 159.9,307.6 150.1,357.5 "/>
|
||||
<path class="st3" d="M320.1,310.8c-2.8-2.8-12.6-4-24-4c-17.8,0-26.6,2.9-31.9,8.2c-5.3,5.3-7.9,25.3-7.9,27.5
|
||||
c0,2.1-0.8,7.8,3.1,11.7c3.9,3.9,13.3,4.2,27.8,4.2c14.5,0,22.5-2.5,27.9-6.2c5.4-3.6,8.6-22.7,9.2-28.6
|
||||
C324.8,317.6,322.9,313.5,320.1,310.8 M305.9,333.1c-1.5,5.3-2.1,7.5-3.9,8.8c-1.8,1.3-11,2.2-13,2.2c-2,0-11.8-0.2-13.8-2.2
|
||||
c-2-2,1-16.9,3.9-18.5c2.9-1.6,8.9-2.1,13.2-2c6.6,0.1,11.6,0.3,13,1.7C306.7,324.7,307.4,327.8,305.9,333.1"/>
|
||||
<path class="st3" d="M389,286.8l-5.5,28.1c-2.6-6.2-7.5-8.1-18-8.1c-17.8,0-20.9,2.9-26.2,8.2s-7.9,25.3-7.9,27.5
|
||||
c0,2.1-0.8,7.8,3.1,11.7c3.9,3.9,7,4.2,21.1,4.2c13.6,0,18.5-4.6,20.9-7.7l-1.3,6.8h17.8l13.8-70.7H389z M376,342
|
||||
c-1.8,1.3-11,2.2-13.1,2.2s-11.8-0.2-13.8-2.2c-2-2,1-16.9,3.9-18.5c2.9-1.6,8.9-2.1,13.2-2c6.6,0.1,11.6,0.3,13.1,1.7
|
||||
c0.9,0.9,1.5,2.6,1.4,5.1l-1.2,5.9C378.3,338.8,377.7,340.8,376,342"/>
|
||||
<path class="st3" d="M472.3,309.8c-2.2-1.9-7.8-3-18.4-3c-13.3,0-18.8,2.7-23,8.4l5.5-28.3H419L405,357.5h17.7l1.4-7.2
|
||||
c2.1,4.9,5.3,8.1,20,8.1c14.5,0,18.4-0.7,23-5.4c4.9-4.9,8.7-23.4,9.2-29.4C476.8,317.7,476,312.9,472.3,309.8 M458.4,333.1
|
||||
c-1.5,5.3-2.1,7.5-3.9,8.8c-1.8,1.3-11,2.2-13,2.2c-2,0-11.8-0.2-13.8-2.2c-2-2,1-16.9,3.9-18.5c2.9-1.6,8.9-2.1,13.2-2
|
||||
c6.6,0.1,11.6,0.3,13,1.7C459.3,324.7,459.9,327.8,458.4,333.1"/>
|
||||
<path class="st3" d="M536.2,307.4h-0.7c0,0-4.7,27.5-9,31.8c-4.3,4.3-10.6,4.6-12.8,4.6c-2.1,0-8.2-0.9-10.4-3.1
|
||||
c-2.7-2.8,3.2-28.7,4.3-33.3h-17.3c0,0-7,31.7-7,36.6c0,3.9,0.4,11.1,11.2,13.6c2.5,0.6,5.5,0.9,9.2,0.9c2.9,0,5.6-0.3,8-0.9
|
||||
c8.4-2,13.7-6.8,16.6-10.4l-2,10.4h16.5l9.8-50.1h0H536.2z"/>
|
||||
<path class="st3" d="M605.6,323h16.1c0,0,0.3-3.9,0.3-5.5c0-1.7,0.3-3.9-3.1-7.3c-3.4-3.4-21.8-3.3-24.9-3.3
|
||||
c-3.1,0-19.8-0.3-24.5,3.6c-4,3.2-7.3,7.4-7.3,15.6c0,8.3,5.5,11.5,16.4,11.5h19.5c2.7,0,6.1,0.8,6.1,3.3c0,2.5-1.9,5.9-8.7,5.9
|
||||
h-12.9c-3.1,0-8.7-0.5-8.7-4v-1.6h-16.1c0,0-0.8,2.8-0.8,5.2c0,2.4,0.4,5.5,3.1,8.2c2.7,2.7,14,3.9,25.6,3.9
|
||||
c11.6,0,24.1-0.5,28.3-4.8c4.3-4.3,6.2-12.3,6.2-16.5c0-4.2-1.4-6.2-2.8-7.6c-1.3-1.3-5.3-3.7-23-3.7c-15.8,0-16.3-1.5-16.3-3
|
||||
c0-1.2,0.2-4.2,12.7-4.2C603.3,318.6,605.6,319.2,605.6,323"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.9 KiB |
16
modbuscommander/modbuscommander.pro
Normal file
16
modbuscommander/modbuscommander.pro
Normal file
@ -0,0 +1,16 @@
|
||||
include(../plugin.pri)
|
||||
|
||||
QT += \
|
||||
serialport \
|
||||
network \
|
||||
serialbus \
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginmodbuscommander.cpp \
|
||||
modbustcpmaster.cpp \
|
||||
modbusrtumaster.cpp \
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginmodbuscommander.h \
|
||||
modbustcpmaster.h \
|
||||
modbusrtumaster.h \
|
||||
371
modbuscommander/modbusrtumaster.cpp
Normal file
371
modbuscommander/modbusrtumaster.cpp
Normal file
@ -0,0 +1,371 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "modbusrtumaster.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QSerialPortInfo>
|
||||
|
||||
ModbusRTUMaster::ModbusRTUMaster(QString serialPort, uint baudrate, QSerialPort::Parity parity, uint dataBits, uint stopBits, QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
m_modbusRtuSerialMaster = new QModbusRtuSerialMaster(this);
|
||||
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialPortNameParameter, serialPort);
|
||||
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, baudrate);
|
||||
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, dataBits);
|
||||
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, stopBits);
|
||||
m_modbusRtuSerialMaster->setConnectionParameter(QModbusDevice::SerialParityParameter, parity);
|
||||
//m_modbusRtuSerialMaster->setTimeout(100);
|
||||
//m_modbusRtuSerialMaster->setNumberOfRetries(1);
|
||||
connect(m_modbusRtuSerialMaster, &QModbusTcpClient::stateChanged, this, &ModbusRTUMaster::onModbusStateChanged);
|
||||
connect(m_modbusRtuSerialMaster, &QModbusRtuSerialMaster::errorOccurred, this, &ModbusRTUMaster::onModbusErrorOccurred);
|
||||
|
||||
m_reconnectTimer = new QTimer(this);
|
||||
m_reconnectTimer->setSingleShot(true);
|
||||
connect(m_reconnectTimer, &QTimer::timeout, this, &ModbusRTUMaster::onReconnectTimer);
|
||||
}
|
||||
|
||||
|
||||
ModbusRTUMaster::~ModbusRTUMaster()
|
||||
{
|
||||
if (!m_modbusRtuSerialMaster) {
|
||||
m_modbusRtuSerialMaster->disconnectDevice();
|
||||
m_modbusRtuSerialMaster->deleteLater();
|
||||
}
|
||||
if (!m_reconnectTimer) {
|
||||
m_reconnectTimer->stop();
|
||||
m_reconnectTimer->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
bool ModbusRTUMaster::connectDevice()
|
||||
{
|
||||
qCDebug(dcModbusCommander()) << "Setting up TCP connecion";
|
||||
|
||||
if (!m_modbusRtuSerialMaster)
|
||||
return false;
|
||||
|
||||
return m_modbusRtuSerialMaster->connectDevice();
|
||||
}
|
||||
|
||||
QString ModbusRTUMaster::serialPort()
|
||||
{
|
||||
return m_modbusRtuSerialMaster->connectionParameter(QModbusDevice::SerialPortNameParameter).toString();
|
||||
}
|
||||
|
||||
void ModbusRTUMaster::onReconnectTimer()
|
||||
{
|
||||
if(!m_modbusRtuSerialMaster->connectDevice()) {
|
||||
m_reconnectTimer->start(10000);
|
||||
}
|
||||
}
|
||||
|
||||
QUuid ModbusRTUMaster::readCoil(uint slaveAddress, uint registerAddress)
|
||||
{
|
||||
if (!m_modbusRtuSerialMaster) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1);
|
||||
|
||||
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
|
||||
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
|
||||
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
QUuid ModbusRTUMaster::writeCoil(uint slaveAddress, uint registerAddress, bool value)
|
||||
{
|
||||
if (!m_modbusRtuSerialMaster) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1);
|
||||
request.setValue(0, static_cast<uint16_t>(value));
|
||||
|
||||
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
QUuid ModbusRTUMaster::writeHoldingRegister(uint slaveAddress, uint registerAddress, uint value)
|
||||
{
|
||||
if (!m_modbusRtuSerialMaster) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1);
|
||||
request.setValue(0, static_cast<uint16_t>(value));
|
||||
|
||||
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
QUuid ModbusRTUMaster::readDiscreteInput(uint slaveAddress, uint registerAddress)
|
||||
{
|
||||
if (!m_modbusRtuSerialMaster) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, registerAddress, 1);
|
||||
|
||||
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
|
||||
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
QUuid ModbusRTUMaster::readInputRegister(uint slaveAddress, uint registerAddress)
|
||||
{
|
||||
if (!m_modbusRtuSerialMaster) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, registerAddress, 1);
|
||||
|
||||
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
|
||||
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
|
||||
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
QUuid ModbusRTUMaster::readHoldingRegister(uint slaveAddress, uint registerAddress)
|
||||
{
|
||||
if (!m_modbusRtuSerialMaster) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1);
|
||||
|
||||
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
|
||||
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusRtuSerialMaster->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
|
||||
void ModbusRTUMaster::onModbusErrorOccurred(QModbusDevice::Error error)
|
||||
{
|
||||
qCWarning(dcModbusCommander()) << "An error occured" << error;
|
||||
}
|
||||
|
||||
|
||||
void ModbusRTUMaster::onModbusStateChanged(QModbusDevice::State state)
|
||||
{
|
||||
bool connected = (state != QModbusDevice::UnconnectedState);
|
||||
if (!connected) {
|
||||
//try to reconnect in 10 seconds
|
||||
m_reconnectTimer->start(10000);
|
||||
}
|
||||
emit connectionStateChanged(connected);
|
||||
}
|
||||
81
modbuscommander/modbusrtumaster.h
Normal file
81
modbuscommander/modbusrtumaster.h
Normal file
@ -0,0 +1,81 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef MODBUSRTUMASTER_H
|
||||
#define MODBUSRTUMASTER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtSerialBus>
|
||||
#include <QSerialPort>
|
||||
#include <QTimer>
|
||||
#include <QUuid>
|
||||
|
||||
class ModbusRTUMaster : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModbusRTUMaster(QString serialPort, uint baudrate, QSerialPort::Parity parity, uint dataBits, uint stopBits, QObject *parent = nullptr);
|
||||
~ModbusRTUMaster();
|
||||
|
||||
bool connectDevice();
|
||||
|
||||
QUuid readCoil(uint slaveAddress, uint registerAddress);
|
||||
QUuid readDiscreteInput(uint slaveAddress, uint registerAddress);
|
||||
QUuid readInputRegister(uint slaveAddress, uint registerAddress);
|
||||
QUuid readHoldingRegister(uint slaveAddress, uint registerAddress);
|
||||
|
||||
QUuid writeCoil(uint slaveAddress, uint registerAddress, bool status);
|
||||
QUuid writeHoldingRegister(uint slaveAddress, uint registerAddress, uint data);
|
||||
|
||||
QString serialPort();
|
||||
|
||||
private:
|
||||
QModbusRtuSerialMaster *m_modbusRtuSerialMaster;
|
||||
QTimer *m_reconnectTimer = nullptr;
|
||||
|
||||
private slots:
|
||||
void onReconnectTimer();
|
||||
|
||||
void onModbusErrorOccurred(QModbusDevice::Error error);
|
||||
void onModbusStateChanged(QModbusDevice::State state);
|
||||
|
||||
signals:
|
||||
void connectionStateChanged(bool status);
|
||||
|
||||
void requestExecuted(QUuid requestId, bool success);
|
||||
void requestError(QUuid requestId, const QString &error);
|
||||
|
||||
void receivedCoil(uint slaveAddress, uint modbusRegister, bool value);
|
||||
void receivedDiscreteInput(uint slaveAddress, uint modbusRegister, bool value);
|
||||
void receivedHoldingRegister(uint slaveAddress, uint modbusRegister, uint value);
|
||||
void receivedInputRegister(uint slaveAddress, uint modbusRegister, uint value);
|
||||
};
|
||||
|
||||
#endif // MODBUSRTUMASTER_H
|
||||
375
modbuscommander/modbustcpmaster.cpp
Normal file
375
modbuscommander/modbustcpmaster.cpp
Normal file
@ -0,0 +1,375 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "modbustcpmaster.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
ModbusTCPMaster::ModbusTCPMaster(QString IPv4Address, uint port, QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
m_modbusTcpClient = new QModbusTcpClient(this);
|
||||
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);
|
||||
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, IPv4Address);
|
||||
//m_modbusTcpClient->setTimeout(100);
|
||||
//m_modbusTcpClient->setNumberOfRetries(1);
|
||||
|
||||
connect(m_modbusTcpClient, &QModbusTcpClient::stateChanged, this, &ModbusTCPMaster::onModbusStateChanged);
|
||||
connect(m_modbusTcpClient, &QModbusRtuSerialMaster::errorOccurred, this, &ModbusTCPMaster::onModbusErrorOccurred);
|
||||
|
||||
m_reconnectTimer = new QTimer(this);
|
||||
m_reconnectTimer->setSingleShot(true);
|
||||
connect(m_reconnectTimer, &QTimer::timeout, this, &ModbusTCPMaster::onReconnectTimer);
|
||||
}
|
||||
|
||||
ModbusTCPMaster::~ModbusTCPMaster()
|
||||
{
|
||||
if (!m_modbusTcpClient) {
|
||||
m_modbusTcpClient->disconnectDevice();
|
||||
m_modbusTcpClient->deleteLater();
|
||||
}
|
||||
if (!m_reconnectTimer) {
|
||||
m_reconnectTimer->stop();
|
||||
m_reconnectTimer->deleteLater();
|
||||
}
|
||||
}
|
||||
|
||||
bool ModbusTCPMaster::connectDevice() {
|
||||
// TCP connction to target device
|
||||
qCDebug(dcModbusCommander()) << "Setting up TCP connecion";
|
||||
|
||||
if (!m_modbusTcpClient)
|
||||
return false;
|
||||
|
||||
return m_modbusTcpClient->connectDevice();
|
||||
}
|
||||
|
||||
uint ModbusTCPMaster::port()
|
||||
{
|
||||
return m_modbusTcpClient->connectionParameter(QModbusDevice::NetworkPortParameter).toUInt();
|
||||
}
|
||||
|
||||
bool ModbusTCPMaster::setIPv4Address(QString ipv4Address)
|
||||
{
|
||||
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, ipv4Address);
|
||||
return connectDevice();
|
||||
}
|
||||
|
||||
bool ModbusTCPMaster::setPort(uint port)
|
||||
{
|
||||
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);
|
||||
return connectDevice();
|
||||
}
|
||||
|
||||
void ModbusTCPMaster::onReconnectTimer()
|
||||
{
|
||||
if(!m_modbusTcpClient->connectDevice()) {
|
||||
m_reconnectTimer->start(10000);
|
||||
}
|
||||
}
|
||||
|
||||
QString ModbusTCPMaster::ipv4Address()
|
||||
{
|
||||
return m_modbusTcpClient->connectionParameter(QModbusDevice::NetworkAddressParameter).toString();
|
||||
}
|
||||
|
||||
QUuid ModbusTCPMaster::readCoil(uint slaveAddress, uint registerAddress)
|
||||
{
|
||||
if (!m_modbusTcpClient) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1);
|
||||
|
||||
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
reply->deleteLater();
|
||||
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus reply error:" << error;
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
QUuid ModbusTCPMaster::writeHoldingRegister(uint slaveAddress, uint registerAddress, uint value)
|
||||
{
|
||||
if (!m_modbusTcpClient) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1);
|
||||
request.setValue(0, static_cast<uint16_t>(value));
|
||||
|
||||
if (QModbusReply *reply = m_modbusTcpClient->sendWriteRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
QUuid ModbusTCPMaster::readDiscreteInput(uint slaveAddress, uint registerAddress)
|
||||
{
|
||||
if (!m_modbusTcpClient) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, registerAddress, 1);
|
||||
|
||||
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
reply->deleteLater();
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
|
||||
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
QUuid ModbusTCPMaster::readInputRegister(uint slaveAddress, uint registerAddress)
|
||||
{
|
||||
if (!m_modbusTcpClient) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, registerAddress, 1);
|
||||
|
||||
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
reply->deleteLater();
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus reply error:" << error;
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
QUuid ModbusTCPMaster::readHoldingRegister(uint slaveAddress, uint registerAddress)
|
||||
{
|
||||
if (!m_modbusTcpClient) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1);
|
||||
|
||||
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus replay error:" << error;
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
QUuid ModbusTCPMaster::writeCoil(uint slaveAddress, uint registerAddress, bool value)
|
||||
{
|
||||
if (!m_modbusTcpClient) {
|
||||
return "";
|
||||
}
|
||||
QUuid requestId = QUuid::createUuid();
|
||||
|
||||
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1);
|
||||
request.setValue(0, static_cast<uint16_t>(value));
|
||||
|
||||
if (QModbusReply *reply = m_modbusTcpClient->sendWriteRequest(request, slaveAddress)) {
|
||||
if (!reply->isFinished()) {
|
||||
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] () {
|
||||
|
||||
if (reply->error() == QModbusDevice::NoError) {
|
||||
requestExecuted(requestId, true);
|
||||
const QModbusDataUnit unit = reply->result();
|
||||
uint modbusAddress = unit.startAddress();
|
||||
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(0));
|
||||
|
||||
} else {
|
||||
requestExecuted(requestId, false);
|
||||
qCWarning(dcModbusCommander()) << "Read response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusCommander()) << "Modbus reply error:" << error;
|
||||
emit requestError(requestId, reply->errorString());
|
||||
reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
QTimer::singleShot(200, reply, &QModbusReply::deleteLater);
|
||||
} else {
|
||||
delete reply; // broadcast replies return immediately
|
||||
return "";
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusCommander()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return "";
|
||||
}
|
||||
return requestId;
|
||||
}
|
||||
|
||||
|
||||
void ModbusTCPMaster::onModbusErrorOccurred(QModbusDevice::Error error)
|
||||
{
|
||||
qCWarning(dcModbusCommander()) << "An error occured" << error;
|
||||
}
|
||||
|
||||
|
||||
void ModbusTCPMaster::onModbusStateChanged(QModbusDevice::State state)
|
||||
{
|
||||
bool connected = (state != QModbusDevice::UnconnectedState);
|
||||
if (!connected) {
|
||||
//try to reconnect in 10 seconds
|
||||
m_reconnectTimer->start(10000);
|
||||
}
|
||||
emit connectionStateChanged(connected);
|
||||
}
|
||||
85
modbuscommander/modbustcpmaster.h
Normal file
85
modbuscommander/modbustcpmaster.h
Normal file
@ -0,0 +1,85 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2020, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
* This project including source code and documentation is protected by
|
||||
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||
* reproduction, publication, editing and translation, are reserved. The use of
|
||||
* this project is subject to the terms of a license agreement to be concluded
|
||||
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||
* under https://nymea.io/license
|
||||
*
|
||||
* GNU Lesser General Public License Usage
|
||||
* Alternatively, this project may be redistributed and/or modified under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation; version 3. This project is distributed in the hope that
|
||||
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* For any further details and any questions please contact us under
|
||||
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||
* https://nymea.io/license/faq
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef MODBUSTCPMASTER_H
|
||||
#define MODBUSTCPMASTER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
#include <QtSerialBus>
|
||||
#include <QTimer>
|
||||
#include <QUuid>
|
||||
|
||||
class ModbusTCPMaster : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ModbusTCPMaster(QString ipAddress, uint port, QObject *parent = nullptr);
|
||||
~ModbusTCPMaster();
|
||||
|
||||
bool connectDevice();
|
||||
|
||||
QUuid readCoil(uint slaveAddress, uint registerAddress);
|
||||
QUuid readDiscreteInput(uint slaveAddress, uint registerAddress);
|
||||
QUuid readInputRegister(uint slaveAddress, uint registerAddress);
|
||||
QUuid readHoldingRegister(uint slaveAddress, uint registerAddress);
|
||||
|
||||
QUuid writeCoil(uint slaveAddress, uint registerAddress, bool status);
|
||||
QUuid writeHoldingRegister(uint slaveAddress, uint registerAddress, uint data);
|
||||
|
||||
QString ipv4Address();
|
||||
uint port();
|
||||
bool setIPv4Address(QString ipAddress);
|
||||
bool setPort(uint port);
|
||||
|
||||
|
||||
private:
|
||||
QTimer *m_reconnectTimer = nullptr;
|
||||
QModbusTcpClient *m_modbusTcpClient;
|
||||
|
||||
private slots:
|
||||
void onReconnectTimer();
|
||||
|
||||
void onModbusErrorOccurred(QModbusDevice::Error error);
|
||||
void onModbusStateChanged(QModbusDevice::State state);
|
||||
|
||||
signals:
|
||||
void connectionStateChanged(bool status);
|
||||
|
||||
void requestExecuted(QUuid requestId, bool success);
|
||||
void requestError(QUuid requestId, const QString &error);
|
||||
|
||||
void receivedCoil(uint slaveAddress, uint modbusRegister, bool value);
|
||||
void receivedDiscreteInput(uint slaveAddress, uint modbusRegister, bool value);
|
||||
void receivedHoldingRegister(uint slaveAddress, uint modbusRegister, uint value);
|
||||
void receivedInputRegister(uint slaveAddress, uint modbusRegister, uint value);
|
||||
};
|
||||
|
||||
#endif // MODBUSTCPMASTER_H
|
||||
@ -3,6 +3,7 @@ TEMPLATE = subdirs
|
||||
PLUGIN_DIRS = \
|
||||
drexelundweiss \
|
||||
fronius \
|
||||
modbuscommander \
|
||||
mypv \
|
||||
wallbe \
|
||||
|
||||
|
||||
Reference in New Issue
Block a user