This repository has been archived on 2026-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
powersync-plugins-modbus/modbuscommander/integrationpluginmodbuscommander.cpp
Simon Stürz 352413563f Use new modbus RTU hardware resource for the modbus commander plugin
Use new modbus RTU hardware resource for the modbus commander plugin

Fix action abort for RTU actions and fix connected state fro child RTU things

Fix modbus rtu client and make it work with the basic resource component

Remove modbusrtu master from project file

Remove custom modbus rtu master include
2021-06-27 17:22:41 +02:00

680 lines
35 KiB
C++

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* 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 "hardwaremanager.h"
#include "hardware/modbus/modbusrtumaster.h"
#include "hardware/modbus/modbusrtuhardwareresource.h"
#include <QSerialPort>
IntegrationPluginModbusCommander::IntegrationPluginModbusCommander()
{
}
void IntegrationPluginModbusCommander::init()
{
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(modbusRTUClientThingClassId, modbusRTUClientConnectedStateTypeId);
m_connectedStateTypeId.insert(modbusTCPClientThingClassId, modbusTCPClientConnectedStateTypeId);
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);
// Plugin configuration
connect(this, &IntegrationPluginModbusCommander::configValueChanged, this, &IntegrationPluginModbusCommander::onPluginConfigurationChanged);
// Modbus RTU hardware resource
connect(hardwareManager()->modbusRtuResource(), &ModbusRtuHardwareResource::modbusRtuMasterRemoved, this, [=](const QUuid &modbusUuid){
qCDebug(dcModbusCommander()) << "Modbus RTU master has been removed" << modbusUuid.toString();
// Check if there is any device using this resource
foreach (Thing *thing, m_modbusRtuMasters.keys()) {
if (m_modbusRtuMasters.value(thing)->modbusUuid() == modbusUuid) {
qCWarning(dcModbusCommander()) << "Hardware resource removed for" << thing << ". The thing will not be functional any more until a new resource has been configured for it.";
m_modbusRtuMasters.remove(thing);
thing->setStateValue(m_connectedStateTypeId[thing->thingClassId()], false);
// Set all child things disconnected
foreach (Thing *childThing, myThings()) {
if (childThing->parentId() == thing->id()) {
thing->setStateValue(m_connectedStateTypeId[childThing->thingClassId()], false);
}
}
}
}
});
}
void IntegrationPluginModbusCommander::discoverThings(ThingDiscoveryInfo *info)
{
ThingClassId thingClassId = info->thingClassId();
if (thingClassId == modbusRTUClientThingClassId) {
foreach (ModbusRtuMaster *modbusMaster, hardwareManager()->modbusRtuResource()->modbusRtuMasters()) {
qCDebug(dcModbusCommander()) << "Found RTU master resource" << modbusMaster;
if (modbusMaster->connected()) {
ParamList parameters;
ThingDescriptor thingDescriptor(thingClassId, "Modbus RTU master", modbusMaster->serialPort());
parameters.append(Param(modbusRTUClientThingModbusMasterUuidParamTypeId, modbusMaster->modbusUuid()));
thingDescriptor.setParams(parameters);
info->addThingDescriptor(thingDescriptor);
} else {
qCWarning(dcModbusCommander()) << "Found configured resource" << modbusMaster << "but it is not connected. Skipping.";
}
}
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(modbusTCPClientThingIpAddressParamTypeId).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(modbusRTUClientThingModbusMasterUuidParamTypeId).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(modbusTCPClientThingIpAddressParamTypeId).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(modbusRTUClientThingModbusMasterUuidParamTypeId).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(modbusTCPClientThingIpAddressParamTypeId).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(modbusRTUClientThingModbusMasterUuidParamTypeId).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(modbusTCPClientThingIpAddressParamTypeId).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(modbusRTUClientThingModbusMasterUuidParamTypeId).toString());
descriptor.setParentId(clientThing->id());
info->addThingDescriptor(descriptor);
}
}
info->finish(Thing::ThingErrorNoError);
return;
} else {
Q_ASSERT_X(false, "discoverThings", QString("Unhandled thingClassId: %1").arg(info->thingClassId().toString()).toUtf8());
}
}
void IntegrationPluginModbusCommander::setupThing(ThingSetupInfo *info)
{
Thing *thing = info->thing();
if (thing->thingClassId() == modbusTCPClientThingClassId) {
QHostAddress hostAddress = QHostAddress(thing->paramValue(modbusTCPClientThingIpAddressParamTypeId).toString());
uint port = thing->paramValue(modbusTCPClientThingPortParamTypeId).toUInt();
uint numberOfRetries = thing->setting(modbusTCPClientSettingsNumberOfRetriesParamTypeId).toUInt();
uint timeout = thing->setting(modbusTCPClientSettingsTimeoutParamTypeId).toUInt();
if (m_modbusTCPMasters.contains(thing)) {
// In case of a rediscovery
m_modbusTCPMasters.take(thing)->deleteLater();
}
foreach (ModbusTCPMaster *modbusTCPMaster, m_modbusTCPMasters.values()) {
if ((modbusTCPMaster->hostAddress() == hostAddress) && (modbusTCPMaster->port() == port)) {
m_modbusTCPMasters.insert(thing, modbusTCPMaster);
return info->finish(Thing::ThingErrorNoError);
}
}
qCDebug(dcModbusCommander()) << "Setting up TCP client" << thing->name();
qCDebug(dcModbusCommander()) << " address:" << hostAddress.toString();
qCDebug(dcModbusCommander()) << " port:" << port;
qCDebug(dcModbusCommander()) << " number of retries:" << numberOfRetries;
qCDebug(dcModbusCommander()) << " timeout:" << timeout;
ModbusTCPMaster *modbusTCPMaster = new ModbusTCPMaster(hostAddress, port, this);
connect(modbusTCPMaster, &ModbusTCPMaster::connectionStateChanged, this, &IntegrationPluginModbusCommander::onConnectionStateChanged);
connect(modbusTCPMaster, &ModbusTCPMaster::writeRequestExecuted, this, &IntegrationPluginModbusCommander::onRequestExecuted);
connect(modbusTCPMaster, &ModbusTCPMaster::writeRequestError, 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);
connect(modbusTCPMaster, &ModbusTCPMaster::connectionStateChanged, info, [info, modbusTCPMaster, this] (bool connected) {
if (connected) {
info->finish(Thing::ThingErrorNoError);
m_modbusTCPMasters.insert(info->thing(), modbusTCPMaster);
}
});
connect(thing, &Thing::settingChanged, thing, [thing, modbusTCPMaster] (const ParamTypeId &paramTypeId, const QVariant &value) {
if (paramTypeId == modbusTCPClientSettingsNumberOfRetriesParamTypeId) {
qCDebug(dcModbusCommander()) << "Set number of retries" << thing->name() << value.toUInt();
modbusTCPMaster->setNumberOfRetries(value.toUInt());
} else if (paramTypeId == modbusTCPClientSettingsTimeoutParamTypeId) {
qCDebug(dcModbusCommander()) << "Set timeout " << thing->name() << value.toUInt();
modbusTCPMaster->setTimeout(value.toUInt());
}
});
modbusTCPMaster->connectDevice();
} else if (thing->thingClassId() == modbusRTUClientThingClassId) {
QUuid modbusUuid = thing->paramValue(modbusRTUClientThingModbusMasterUuidParamTypeId).toUuid();
if (!hardwareManager()->modbusRtuResource()->available()) {
qCWarning(dcModbusCommander()) << "Cannot set up thing" << thing << ". The modbus RTU hardware resource is not available.";
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The modbus RTU hardware resource is not available"));
return;
}
if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(modbusUuid)) {
qCWarning(dcModbusCommander()) << "Cannot set up thing" << thing << ". The modbus RTU hardware resource" << modbusUuid.toString() << "does not exist any more. Reconfiguration required.";
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Configured modbus RTU master could not be found. Please reconfigure the client and assign a new valid modbus RTU master."));
return;
}
ModbusRtuMaster *modbusMaster = hardwareManager()->modbusRtuResource()->getModbusRtuMaster(modbusUuid);
qCDebug(dcModbusCommander()) << "Setting up" << thing << "using" << modbusMaster;
m_modbusRtuMasters.insert(thing, modbusMaster);
connect(modbusMaster, &ModbusRtuMaster::connectedChanged, thing, [=](bool connected){
qCDebug(dcModbusCommander()) << "Modbus RTU client" << modbusMaster << "connected changed" << connected;
thing->setStateValue(modbusRTUClientConnectedStateTypeId, connected);
// Note: only set the connected state for the child things if disconnected.
// The child things will be evaluated upon read requests if the slave is connected or not.
if (!connected) {
foreach (Thing *childThing, myThings()) {
if (childThing->parentId() == thing->id()) {
thing->setStateValue(m_connectedStateTypeId[childThing->thingClassId()], connected);
}
}
}
});
info->finish(Thing::ThingErrorNoError);
} else if ((thing->thingClassId() == coilThingClassId)
|| (thing->thingClassId() == discreteInputThingClassId)
|| (thing->thingClassId() == holdingRegisterThingClassId)
|| (thing->thingClassId() == inputRegisterThingClassId)) {
qCDebug(dcModbusCommander()) << "Setting up modbus register" << thing->name();
info->finish(Thing::ThingErrorNoError);
} else {
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}
}
void IntegrationPluginModbusCommander::postSetupThing(Thing *thing)
{
qCDebug(dcModbusCommander()) << "Post setup thing" << thing->name();
if (!m_refreshTimer) {
int refreshTime = configValue(modbusCommanderPluginUpdateIntervalParamTypeId).toInt();
qCDebug(dcModbusCommander()) << "Starting refresh timer with interval" << refreshTime << "s";
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(refreshTime);
connect(m_refreshTimer, &PluginTimer::timeout, this, [this] {
foreach (Thing *thing, myThings()) {
if ((thing->thingClassId() == coilThingClassId) ||
(thing->thingClassId() == discreteInputThingClassId) ||
(thing->thingClassId() == holdingRegisterThingClassId) ||
(thing->thingClassId() == inputRegisterThingClassId)) {
readRegister(thing);
}
}
});
}
if ((thing->thingClassId() == modbusRTUClientThingClassId) ||
(thing->thingClassId() == modbusTCPClientThingClassId)) {
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
} else if ((thing->thingClassId() == coilThingClassId) ||
(thing->thingClassId() == discreteInputThingClassId) ||
(thing->thingClassId() == holdingRegisterThingClassId) ||
(thing->thingClassId() == inputRegisterThingClassId)) {
readRegister(thing);
} else {
Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}
}
void IntegrationPluginModbusCommander::executeAction(ThingActionInfo *info)
{
Thing *thing = info->thing();
Action action = info->action();
if (thing->thingClassId() == coilThingClassId) {
if (action.actionTypeId() == coilValueActionTypeId) {
writeRegister(thing, info);
return;
} else {
Q_ASSERT_X(false, "Execute action", QString("Unhandled action type id: %1").arg(action.actionTypeId().toString()).toUtf8());
}
} else if (thing->thingClassId() == holdingRegisterThingClassId) {
if (action.actionTypeId() == holdingRegisterValueActionTypeId) {
writeRegister(thing, info);
return;
} else {
Q_ASSERT_X(false, "Execute action", QString("Unhandled action type id: %1").arg(action.actionTypeId().toString()).toUtf8());
}
} else {
Q_ASSERT_X(false, "Execute action", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
}
}
void IntegrationPluginModbusCommander::thingRemoved(Thing *thing)
{
qCDebug(dcModbusCommander()) << "Removing thing" << thing->name();
if (thing->thingClassId() == modbusTCPClientThingClassId) {
ModbusTCPMaster *modbus = m_modbusTCPMasters.take(thing);
modbus->deleteLater();
}
if (myThings().empty()) {
qCDebug(dcModbusCommander()) << "No more Modbus commander things, stopping timer";
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
m_refreshTimer = nullptr;
}
}
void IntegrationPluginModbusCommander::onPluginConfigurationChanged(const ParamTypeId &paramTypeId, const QVariant &value)
{
// Check refresh schedule
if (paramTypeId == modbusCommanderPluginUpdateIntervalParamTypeId) {;
qCDebug(dcModbusCommander()) << "Update interval has changed to" << value.toUInt() << "s";
if (m_refreshTimer) {
uint refreshTime = value.toUInt();
m_refreshTimer->stop();
m_refreshTimer->startTimer(refreshTime);
} else {
qCWarning(dcModbusCommander()) << "Update interval changed but refresh timer is not initialized";
}
}
}
void IntegrationPluginModbusCommander::onConnectionStateChanged(bool status)
{
auto modbus = sender();
if (m_modbusTCPMasters.values().contains(static_cast<ModbusTCPMaster *>(modbus))) {
Thing *thing = m_modbusTCPMasters.key(static_cast<ModbusTCPMaster *>(modbus));
qCDebug(dcModbusCommander()) << "Connections state changed" << thing->name() << status;
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, const QVector<quint16> &values)
{
auto modbus = sender();
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()), values[0]);
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
return;
}
}
}
}
}
void IntegrationPluginModbusCommander::onReceivedDiscreteInput(quint32 slaveAddress, quint32 modbusRegister, const QVector<quint16> &values)
{
auto modbus = sender();
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()), values[0]);
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
return;
}
}
}
}
}
void IntegrationPluginModbusCommander::onReceivedHoldingRegister(uint slaveAddress, uint modbusRegister, const QVector<quint16> &values)
{
auto modbus = sender();
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()), values[0]);
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
return;
}
}
}
}
}
void IntegrationPluginModbusCommander::onReceivedInputRegister(uint slaveAddress, uint modbusRegister, const QVector<quint16> &values)
{
auto modbus = sender();
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()), values[0]);
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 (!modbus->connected())
return; // Send requests only if the modbus interface is connected
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 *modbusMaster = m_modbusRtuMasters.value(parent);
if (!modbusMaster)
return;
if (!modbusMaster->connected())
return; // Send requests only if the modbus interface is connected
if (thing->thingClassId() == coilThingClassId) {
ModbusRtuReply *reply = modbusMaster->readCoil(slaveAddress, registerAddress);
connect(reply, &ModbusRtuReply::finished, modbusMaster, [=](){
if (reply->error() != ModbusRtuReply::NoError) {
qCWarning(dcModbusCommander()) << "Failed to read coil from" << modbusMaster << "slave:" << slaveAddress << "register:" << registerAddress;
thing->setStateValue(m_connectedStateTypeId[thing->thingClassId()], false);
return;
}
if (!reply->result().isEmpty()) {
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), reply->result().at(0));
}
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
});
} else if (thing->thingClassId() == discreteInputThingClassId) {
ModbusRtuReply *reply = modbusMaster->readDiscreteInput(slaveAddress, registerAddress);
connect(reply, &ModbusRtuReply::finished, modbusMaster, [=](){
if (reply->error() != ModbusRtuReply::NoError) {
qCWarning(dcModbusCommander()) << "Failed to read discrete input from" << modbusMaster << "slave:" << slaveAddress << "register:" << registerAddress;
thing->setStateValue(m_connectedStateTypeId[thing->thingClassId()], false);
return;
}
if (!reply->result().isEmpty()) {
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), reply->result().at(0));
}
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
});
} else if (thing->thingClassId() == holdingRegisterThingClassId) {
ModbusRtuReply *reply = modbusMaster->readHoldingRegister(slaveAddress, registerAddress);
connect(reply, &ModbusRtuReply::finished, modbusMaster, [=](){
if (reply->error() != ModbusRtuReply::NoError) {
qCWarning(dcModbusCommander()) << "Failed to read holding register from" << modbusMaster << "slave:" << slaveAddress << "register:" << registerAddress;
thing->setStateValue(m_connectedStateTypeId[thing->thingClassId()], false);
return;
}
if (!reply->result().isEmpty()) {
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), reply->result().at(0));
}
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
});
} else if (thing->thingClassId() == inputRegisterThingClassId) {
ModbusRtuReply *reply = modbusMaster->readInputRegister(slaveAddress, registerAddress);
connect(reply, &ModbusRtuReply::finished, modbusMaster, [=](){
if (reply->error() != ModbusRtuReply::NoError) {
qCWarning(dcModbusCommander()) << "Failed to read input register from" << modbusMaster << "slave:" << slaveAddress << "register:" << registerAddress;
thing->setStateValue(m_connectedStateTypeId[thing->thingClassId()], false);
return;
}
if (!reply->result().isEmpty()) {
thing->setStateValue(m_valueStateTypeId.value(thing->thingClassId()), reply->result().at(0));
}
thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true);
});
}
// Note: we don't want proceed with the method here, since we are not
// working with the requestId any more on RTU
return;
}
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 parent device" << thing->name();
info->finish(Thing::ThingErrorHardwareNotAvailable);
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) {
qCWarning(dcModbusCommander()) << "Could not find modbus TCP master for" << thing;
info->finish(Thing::ThingErrorHardwareNotAvailable);
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 *modbusMaster = m_modbusRtuMasters.value(parent);
if (!modbusMaster) {
qCWarning(dcModbusCommander()) << "Could not find modbus RTU master for" << thing;
info->finish(Thing::ThingErrorHardwareNotAvailable);
return;
}
if (thing->thingClassId() == coilThingClassId) {
QVector<quint16> values;
values.append(static_cast<quint16>(action.param(coilValueActionValueParamTypeId).value().toBool()));
ModbusRtuReply *reply = modbusMaster->writeCoils(slaveAddress, registerAddress, values);
connect(info, &ThingActionInfo::aborted, reply, &ModbusRtuReply::deleteLater);
connect(reply, &ModbusRtuReply::finished, modbusMaster, [=](){
if (reply->error() != ModbusRtuReply::NoError) {
qCWarning(dcModbusCommander()) << "Failed to write coils from" << modbusMaster << "slave:" << slaveAddress << "register:" << registerAddress << values << reply->errorString();
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
info->finish(Thing::ThingErrorNoError);
});
} else if (thing->thingClassId() == holdingRegisterThingClassId) {
QVector<quint16> values;
values.append(static_cast<quint16>(action.param(holdingRegisterValueActionValueParamTypeId).value().toUInt()));
ModbusRtuReply *reply = modbusMaster->writeHoldingRegisters(slaveAddress, registerAddress, values);
connect(info, &ThingActionInfo::aborted, reply, &ModbusRtuReply::deleteLater);
connect(reply, &ModbusRtuReply::finished, modbusMaster, [=](){
if (reply->error() != ModbusRtuReply::NoError) {
qCWarning(dcModbusCommander()) << "Failed to write holding registers from" << modbusMaster << "slave:" << slaveAddress << "register:" << registerAddress << values << reply->errorString();
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
info->finish(Thing::ThingErrorNoError);
});
}
// Note: we don't want proceed with the method here, since we are not
// working with the requestId any more on RTU
return;
}
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);});
}
}