added modbus commander

This commit is contained in:
Boernsman 2020-06-16 13:28:52 +02:00
parent 2e38fae9a7
commit 5ffe45baff
14 changed files with 2008 additions and 0 deletions

18
debian/control vendored
View File

@ -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

View File

@ -0,0 +1 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginmodbuscommander.so

28
modbuscommander/README.md Normal file
View 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/

View 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 &paramTypeId, 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);});
}
}

View 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 &paramTypeId, 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

View 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
View 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"
]
}

View 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

View 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 \

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

View 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

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

View 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

View File

@ -3,6 +3,7 @@ TEMPLATE = subdirs
PLUGIN_DIRS = \
drexelundweiss \
fronius \
modbuscommander \
mypv \
wallbe \