diff --git a/modbus/modbusrtumaster.cpp b/modbus/modbusrtumaster.cpp index 8a7aa5a..57a45af 100644 --- a/modbus/modbusrtumaster.cpp +++ b/modbus/modbusrtumaster.cpp @@ -45,8 +45,7 @@ ModbusRTUMaster::ModbusRTUMaster(QString serialPort, uint baudrate, QSerialPort: 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); @@ -70,7 +69,7 @@ ModbusRTUMaster::~ModbusRTUMaster() bool ModbusRTUMaster::connectDevice() { - qCDebug(dcModbusRTU()) << "Setting up TCP connecion"; + qCDebug(dcModbusRTU()) << "Setting up RTU client connecion"; if (!m_modbusRtuSerialMaster) return false; @@ -78,6 +77,11 @@ bool ModbusRTUMaster::connectDevice() return m_modbusRtuSerialMaster->connectDevice(); } +bool ModbusRTUMaster::connected() +{ + return (m_modbusRtuSerialMaster->state() == QModbusDevice::State::ConnectedState); +} + void ModbusRTUMaster::setNumberOfRetries(int number) { m_modbusRtuSerialMaster->setNumberOfRetries(number); @@ -88,6 +92,16 @@ void ModbusRTUMaster::setTimeout(int timeout) m_modbusRtuSerialMaster->setTimeout(timeout); } +int ModbusRTUMaster::timeout() +{ + return m_modbusRtuSerialMaster->timeout(); +} + +int ModbusRTUMaster::numberOfRetries() +{ + return m_modbusRtuSerialMaster->numberOfRetries(); +} + QString ModbusRTUMaster::serialPort() { return m_modbusRtuSerialMaster->connectionParameter(QModbusDevice::SerialPortNameParameter).toString(); @@ -380,15 +394,18 @@ QUuid ModbusRTUMaster::readHoldingRegister(uint slaveAddress, uint registerAddre void ModbusRTUMaster::onModbusErrorOccurred(QModbusDevice::Error error) { qCWarning(dcModbusRTU()) << "An error occured" << error; + if (error == QModbusDevice::Error::ConnectionError) { + emit connectionStateChanged(false); + } } void ModbusRTUMaster::onModbusStateChanged(QModbusDevice::State state) { - bool connected = (state != QModbusDevice::UnconnectedState); - if (!connected) { + qCWarning(dcModbusRTU()) << "State changed" << state; + if (state == QModbusDevice::UnconnectedState) { //try to reconnect in 10 seconds m_reconnectTimer->start(10000); } - emit connectionStateChanged(connected); + emit connectionStateChanged(state == QModbusDevice::ConnectedState); } diff --git a/modbus/modbusrtumaster.h b/modbus/modbusrtumaster.h index 3a9b04c..3a69db3 100644 --- a/modbus/modbusrtumaster.h +++ b/modbus/modbusrtumaster.h @@ -45,9 +45,13 @@ public: ~ModbusRTUMaster(); bool connectDevice(); + bool connected(); void setNumberOfRetries(int number); void setTimeout(int timeout); + int timeout(); + int numberOfRetries(); + QUuid readCoil(uint slaveAddress, uint registerAddress, uint size = 1); QUuid readDiscreteInput(uint slaveAddress, uint registerAddress, uint size = 1); QUuid readInputRegister(uint slaveAddress, uint registerAddress, uint size = 1); diff --git a/modbus/modbustcpmaster.cpp b/modbus/modbustcpmaster.cpp index d0a4125..aeeba5f 100644 --- a/modbus/modbustcpmaster.cpp +++ b/modbus/modbustcpmaster.cpp @@ -71,6 +71,11 @@ bool ModbusTCPMaster::connectDevice() { return m_modbusTcpClient->connectDevice(); } +bool ModbusTCPMaster::connected() +{ + return (m_modbusTcpClient->state() == QModbusDevice::State::ConnectedState); +} + void ModbusTCPMaster::setNumberOfRetries(int number) { m_modbusTcpClient->setNumberOfRetries(number); diff --git a/modbus/modbustcpmaster.h b/modbus/modbustcpmaster.h index 0166c88..ae3d9b2 100644 --- a/modbus/modbustcpmaster.h +++ b/modbus/modbustcpmaster.h @@ -45,6 +45,7 @@ public: ~ModbusTCPMaster(); bool connectDevice(); + bool connected(); void setNumberOfRetries(int number); void setTimeout(int timeout); diff --git a/modbuscommander/README.md b/modbuscommander/README.md index ed84d53..13f513c 100644 --- a/modbuscommander/README.md +++ b/modbuscommander/README.md @@ -1,27 +1,62 @@ # Modbus Commander -A nymea plugin to send Modbus commands. +A nymea integration plugin to send Modbus RTU or TCP requests. + +## Usage + +Set up one Modbus RTU or TCP client first. Afterwards a register **thing** can be set-up +and during the setup process the modbus client can be selected. ## Supported Things * Modbus TCP client - * Gateway + * Modbus TCP interface + * IP Address and Port of the target device are requried. * Modbus RTU client - * Gateway - * Discovery setup + * Modbus RTU interface + * All available serial ports will be discovered. + * Baudrate, data bits, stop bits and parity are required. * Coil * Writes and reads a single Modbus Coil + * Modbus device- and register address are required. * Discrete input * Reads a single Modbus discrete input + * Modbus device- and register address are required. * Input register * Reads a single Modbus input register + * Modbus device- and register address are required. * Holding register * Writes and reads a single Modbus holding register + * Modbus device- and register address are required. ## Requirements * The package 'nymea-plugin-modbuscommander' must be installed. -* For Modbus RTU a serial port must be available. +* For Modbus RTU, a serial port must be available. +* For Modbus TCP, the target deviecc must be in the same network. + +## Settings + +### Plug-In Configuration + +*Update interval* + +Defines the interval for register polling in seconds. +Minimum and default value is 1 second. + +### Modbus TCP & RTU Client Settings + +*Timeout* + +Default value is 100 ms, the minimum timeout is 10 ms. +The timeout is used by the Modbus client to determine how long it waits +for a response from the server. The request failes if the response +is not received within the given timeout. + +*Number of retries* + +Sets the number of retries a client will perform before a request fails. +The default value is set to 3. ## More diff --git a/modbuscommander/integrationpluginmodbuscommander.cpp b/modbuscommander/integrationpluginmodbuscommander.cpp index 7621fc1..0057354 100644 --- a/modbuscommander/integrationpluginmodbuscommander.cpp +++ b/modbuscommander/integrationpluginmodbuscommander.cpp @@ -40,7 +40,6 @@ 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); @@ -52,6 +51,8 @@ void IntegrationPluginModbusCommander::init() 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); @@ -63,94 +64,44 @@ void IntegrationPluginModbusCommander::init() m_valueStateTypeId.insert(holdingRegisterThingClassId, holdingRegisterValueStateTypeId); } - -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(); - - foreach (ModbusTCPMaster *modbusTCPMaster, m_modbusTCPMasters.values()) { - if ((modbusTCPMaster->hostAddress() == hostAddress) && (modbusTCPMaster->port() == port)){ - m_modbusTCPMasters.insert(thing, modbusTCPMaster); - return info->finish(Thing::ThingErrorNoError); - } - } - - 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); - modbusTCPMaster->connectDevice(); - m_modbusTCPMasters.insert(thing, modbusTCPMaster); - m_asyncTCPSetup.insert(modbusTCPMaster, info); - - } 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); - - } else if ((thing->thingClassId() == coilThingClassId) - || (thing->thingClassId() == discreteInputThingClassId) - || (thing->thingClassId() == holdingRegisterThingClassId) - || (thing->thingClassId() == inputRegisterThingClassId)) { - info->finish(Thing::ThingErrorNoError); - - } else { - Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); - } -} - 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); + qCDebug(dcModbusCommander()) << "Found serial port:" << port.systemLocation() << "manufacturer" << port.manufacturer() << "description" << port.description() << "serial number" << port.serialNumber(); + if (port.isBusy()) { + qCDebug(dcModbusCommander()) << "Serial port ist busy, skipping."; + continue; + } + QString manufacturer = port.manufacturer(); + if (manufacturer.isEmpty()) { + manufacturer = "unknown"; + } + QString description = port.description()+" Manufacturer: "+port.manufacturer(); + ThingDescriptor thingDescriptor(thingClassId, "Modbus RTU interface", description); ParamList parameters; QString serialPort = port.systemLocation(); - foreach (Thing *existingThing, myThings()) { - if (existingThing->paramValue(modbusRTUClientThingSerialPortParamTypeId).toString() == serialPort) { - thingDescriptor.setThingId(existingThing->id()); - break; - } + QString serialnumber = port.serialNumber(); + if (serialnumber.isEmpty()) { + serialnumber = port.manufacturer()+QString::number(port.productIdentifier(), 16); + + } + qCDebug(dcModbusCommander()) << " - Serial number" << serialnumber; + Q_FOREACH (Thing *exisingThing, myThings().filterByParam(modbusRTUClientThingClassId)) { + thingDescriptor.setThingId(exisingThing->id()); + // Rediscovery is broken because of a missing unique device id + // This is a workaround and doesnt work if multiple uart converters are attached. + // ThingDiscoveryInfo may be extended to distinquish between discovery and rediscovery + break; } parameters.append(Param(modbusRTUClientThingSerialPortParamTypeId, serialPort)); + parameters.append(Param(modbusRTUClientThingSerialnumberParamTypeId, serialnumber)); thingDescriptor.setParams(parameters); info->addThingDescriptor(thingDescriptor); } + + //FIXME missing info if it is a rediscovery info->finish(Thing::ThingErrorNoError); return; } else if (thingClassId == discreteInputThingClassId) { @@ -220,20 +171,160 @@ void IntegrationPluginModbusCommander::discoverThings(ThingDiscoveryInfo *info) } } -void IntegrationPluginModbusCommander::postSetupThing(Thing *info) +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 ¶mTypeId, 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) { + + QString serialPort = thing->paramValue(modbusRTUClientThingSerialPortParamTypeId).toString(); + uint baudrate = thing->paramValue(modbusRTUClientThingBaudRateParamTypeId).toUInt(); + uint stopBits = thing->paramValue(modbusRTUClientThingStopBitsParamTypeId).toUInt(); + uint dataBits = thing->paramValue(modbusRTUClientThingDataBitsParamTypeId).toUInt(); + uint numberOfRetries = thing->setting(modbusRTUClientSettingsNumberOfRetriesParamTypeId).toUInt(); + uint timeout = thing->setting(modbusRTUClientSettingsTimeoutParamTypeId).toUInt(); + QSerialPort::Parity parity = QSerialPort::Parity::NoParity; + QString parityString = thing->paramValue(modbusRTUClientThingParityParamTypeId).toString(); + if (parityString.contains("No")) { + parity = QSerialPort::Parity::NoParity; + } else if (parityString.contains("Even")) { + parity = QSerialPort::Parity::EvenParity; + } else if (parityString.contains("Odd")) { + parity = QSerialPort::Parity::OddParity; + } + qCDebug(dcModbusCommander()) << "Setting up RTU client" << thing->name(); + qCDebug(dcModbusCommander()) << " baud:" << baudrate; + qCDebug(dcModbusCommander()) << " stop bits:" << stopBits; + qCDebug(dcModbusCommander()) << " data bits:" << dataBits; + qCDebug(dcModbusCommander()) << " parity:" << parityString; + qCDebug(dcModbusCommander()) << " number of retries:" << numberOfRetries; + qCDebug(dcModbusCommander()) << " timeout:" << timeout; + + + if (m_modbusRTUMasters.contains(thing)) { + // In case of a rediscovery + m_modbusRTUMasters.take(thing)->deleteLater(); + } + + ModbusRTUMaster *modbusRTUMaster = new ModbusRTUMaster(serialPort, baudrate, parity, dataBits, stopBits, this); + modbusRTUMaster->setTimeout(timeout); + modbusRTUMaster->setNumberOfRetries(numberOfRetries); + 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); + connect(modbusRTUMaster, &ModbusRTUMaster::connectionStateChanged, info, [info, modbusRTUMaster, this] (bool connected) { + if (connected) { + info->finish(Thing::ThingErrorNoError); + m_modbusRTUMasters.insert(info->thing(), modbusRTUMaster); + } + }); + connect(thing, &Thing::settingChanged, thing, [thing, modbusRTUMaster] (const ParamTypeId ¶mTypeId, const QVariant &value) { + if (paramTypeId == modbusRTUClientSettingsNumberOfRetriesParamTypeId) { + qCDebug(dcModbusCommander()) << "Set number of retries" << thing->name() << value.toUInt(); + modbusRTUMaster->setNumberOfRetries(value.toUInt()); + } else if (paramTypeId == modbusRTUClientSettingsTimeoutParamTypeId) { + qCDebug(dcModbusCommander()) << "Set timeout " << thing->name() << value.toUInt(); + modbusRTUMaster->setTimeout(value.toUInt()); + } + }); + modbusRTUMaster->connectDevice(); + + } 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) { - // Refresh timer for TCP read 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, &IntegrationPluginModbusCommander::onRefreshTimer); + 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 ((info->thingClassId() == coilThingClassId) || - (info->thingClassId() == discreteInputThingClassId) || - (info->thingClassId() == holdingRegisterThingClassId) || - (info->thingClassId() == inputRegisterThingClassId)) { - readRegister(info); + 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()); } } @@ -241,27 +332,33 @@ void IntegrationPluginModbusCommander::postSetupThing(Thing *info) void IntegrationPluginModbusCommander::executeAction(ThingActionInfo *info) { Thing *thing = info->thing(); + Action action = info->action(); if (thing->thingClassId() == coilThingClassId) { - if (info->action().actionTypeId() == coilValueActionTypeId) { + 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 (info->action().actionTypeId() == holdingRegisterValueActionTypeId) { + 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()); } - qCWarning(dcModbusCommander()) << "Unhandled deviceclass/actiontype in executeAction!"; - info->finish(Thing::ThingErrorThingClassNotFound); } void IntegrationPluginModbusCommander::thingRemoved(Thing *thing) { + qCDebug(dcModbusCommander()) << "Removing thing" << thing->name(); if (thing->thingClassId() == modbusTCPClientThingClassId) { ModbusTCPMaster *modbus = m_modbusTCPMasters.take(thing); modbus->deleteLater(); @@ -271,31 +368,23 @@ void IntegrationPluginModbusCommander::thingRemoved(Thing *thing) } if (myThings().empty()) { + qCDebug(dcModbusCommander()) << "No more Modbus commander things, stopping timer"; hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer); m_refreshTimer = nullptr; } } -void IntegrationPluginModbusCommander::onRefreshTimer() -{ - foreach (Thing *thing, myThings()) { - if ((thing->thingClassId() == coilThingClassId) || - (thing->thingClassId() == discreteInputThingClassId) || - (thing->thingClassId() == holdingRegisterThingClassId) || - (thing->thingClassId() == inputRegisterThingClassId)) { - readRegister(thing); - } - } -} - void IntegrationPluginModbusCommander::onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value) { // Check refresh schedule if (paramTypeId == modbusCommanderPluginUpdateIntervalParamTypeId) {; + 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"; } } } @@ -304,20 +393,13 @@ void IntegrationPluginModbusCommander::onConnectionStateChanged(bool status) { auto modbus = sender(); - if (m_asyncRTUSetup.contains(static_cast(modbus))) { - ThingSetupInfo *info = m_asyncRTUSetup.take(static_cast(modbus)); - info->finish(Thing::ThingErrorNoError); - - } else if (m_asyncTCPSetup.contains(static_cast(modbus))) { - ThingSetupInfo *info = m_asyncTCPSetup.take(static_cast(modbus)); - info->finish(Thing::ThingErrorNoError); - } - if (m_modbusRTUMasters.values().contains(static_cast(modbus))) { Thing *thing = m_modbusRTUMasters.key(static_cast(modbus)); + qCDebug(dcModbusCommander()) << "Connections state changed" << thing->name() << status; thing->setStateValue(modbusRTUClientConnectedStateTypeId, status); } else if (m_modbusTCPMasters.values().contains(static_cast(modbus))) { Thing *thing = m_modbusTCPMasters.key(static_cast(modbus)); + qCDebug(dcModbusCommander()) << "Connections state changed" << thing->name() << status; thing->setStateValue(modbusTCPClientConnectedStateTypeId, status); } } @@ -496,6 +578,9 @@ void IntegrationPluginModbusCommander::readRegister(Thing *thing) 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) { @@ -511,6 +596,9 @@ void IntegrationPluginModbusCommander::readRegister(Thing *thing) 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) { diff --git a/modbuscommander/integrationpluginmodbuscommander.h b/modbuscommander/integrationpluginmodbuscommander.h index 7fed22f..4c7b5c0 100644 --- a/modbuscommander/integrationpluginmodbuscommander.h +++ b/modbuscommander/integrationpluginmodbuscommander.h @@ -65,9 +65,6 @@ private: QHash m_asyncActions; QHash m_readRequests; - QHash m_asyncRTUSetup; - QHash m_asyncTCPSetup; - void readRegister(Thing *thing); void writeRegister(Thing *thing, ThingActionInfo *info); @@ -77,8 +74,6 @@ private: QHash m_valueStateTypeId; private slots: - void onRefreshTimer(); - void onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value); void onConnectionStateChanged(bool status); diff --git a/modbuscommander/integrationpluginmodbuscommander.json b/modbuscommander/integrationpluginmodbuscommander.json index 579f712..15bfd37 100644 --- a/modbuscommander/integrationpluginmodbuscommander.json +++ b/modbuscommander/integrationpluginmodbuscommander.json @@ -24,6 +24,23 @@ "displayName": "Modbus TCP client", "createMethods": ["user"], "interfaces": ["connectable"], + "settingsTypes": [ + { + "id": "a6aa4eff-205b-426d-ad05-90971a122138", + "name": "timeout", + "displayName": "Timeout", + "type": "uint", + "minValue": 10, + "defaultValue": 100 + }, + { + "id": "b27c95c9-7584-46e1-9e62-89890c7bde67", + "name": "numberOfRetries", + "displayName": "Number of retries", + "type": "uint", + "defaultValue": 3 + } + ], "paramTypes": [ { "id": "2a3fcb23-931b-4ba1-b134-c49b656c76f7", @@ -57,6 +74,23 @@ "displayName": "Modbus RTU client", "createMethods": ["discovery", "user"], "interfaces": ["connectable"], + "settingsTypes": [ + { + "id": "b0af32f0-b8cc-4642-af5a-576732522b2c", + "name": "timeout", + "displayName": "Timeout", + "type": "uint", + "minValue": 10, + "defaultValue": 100 + }, + { + "id": "c4f16d6c-c1f2-4862-b0bd-6fae7193eaa8", + "name": "numberOfRetries", + "displayName": "Number of retries", + "type": "uint", + "defaultValue": 3 + } + ], "paramTypes": [ { "id": "ed49f7d8-ab18-4c37-9b80-1004b75dcb91", @@ -66,6 +100,13 @@ "inputType": "TextLine", "defaultValue": "ttyAMA0" }, + { + "id": "9908b01f-a76b-4b21-8242-b507c9252254", + "name": "serialnumber", + "displayName": "Serial number", + "type": "QString", + "defaultValue": "" + }, { "id": "45dfc828-f238-4263-89a3-9b35cf5dea39", "name": "baudRate", @@ -98,7 +139,7 @@ "Even Parity", "Odd Parity" ], - "defaultValue": "Even Parity" + "defaultValue": "No Parity" } ], "stateTypes": [ diff --git a/modbuscommander/translations/7dda1b6d-c37e-4c9f-a696-1666f9de66e6-de.ts b/modbuscommander/translations/7dda1b6d-c37e-4c9f-a696-1666f9de66e6-de.ts index 63a8f62..60ba3d6 100644 --- a/modbuscommander/translations/7dda1b6d-c37e-4c9f-a696-1666f9de66e6-de.ts +++ b/modbuscommander/translations/7dda1b6d-c37e-4c9f-a696-1666f9de66e6-de.ts @@ -4,30 +4,30 @@ ModbusCommander - + Baud rate The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {45dfc828-f238-4263-89a3-9b35cf5dea39}) Baudrate - + Coil The name of the ThingClass ({f53524ea-1d06-40a9-b7a4-041297b21e84}) Coil - - - - - - - - - - - - + + + + + + + + + + + + Connected The name of the ParamType (ThingClass: holdingRegister, EventType: connected, ID: {1f55b72a-5d13-4ae1-b136-bfd84fd9761f}) ---------- @@ -55,11 +55,11 @@ The name of the StateType ({725b541a-9e0c-4634-81eb-e415c0b8f012}) of ThingClass Verbunden - - - - - + + + + + Connection status changed The name of the EventType ({1f55b72a-5d13-4ae1-b136-bfd84fd9761f}) of ThingClass holdingRegister ---------- @@ -73,70 +73,79 @@ The name of the EventType ({725b541a-9e0c-4634-81eb-e415c0b8f012}) of ThingClass Verbunden geändert - + Data bits The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {a27c664b-9f43-4573-a2cc-f65a8fa1a069}) Datenbits - + Discrete input The name of the ThingClass ({d7a15b39-48d3-4591-bdad-ec5e799aa6e5}) Discrete input - + Holding register The name of the ThingClass ({61a2382c-3d9f-41a1-a2fd-27b2af203c56}) Holding Register - + IP Address The name of the ParamType (ThingClass: modbusTCPClient, Type: thing, ID: {2a3fcb23-931b-4ba1-b134-c49b656c76f7}) IP-Adresse - + Input register The name of the ThingClass ({e4c34050-d115-440f-b332-63d36e3e12b8}) Input Register - + Modbus Commander The name of the plugin ModbusCommander ({7dda1b6d-c37e-4c9f-a696-1666f9de66e6}) Modbus Commander - + Modbus RTU client The name of the ThingClass ({776df314-6186-4eb5-b824-f0d916f6d9c3}) Modbus RTU Client - + Modbus TCP client The name of the ThingClass ({35d3e7dc-1f33-4b8c-baa3-eb10b4f157a7}) Modbus TCP Client - + + + Number of retries + The name of the ParamType (ThingClass: modbusRTUClient, Type: settings, ID: {c4f16d6c-c1f2-4862-b0bd-6fae7193eaa8}) +---------- +The name of the ParamType (ThingClass: modbusTCPClient, Type: settings, ID: {b27c95c9-7584-46e1-9e62-89890c7bde67}) + + + + Parity The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {72de1b08-2a27-49c5-90e0-8788c3ea1da3}) Parity - + Port The name of the ParamType (ThingClass: modbusTCPClient, Type: thing, ID: {bee8b151-815a-4159-9d8a-42b76e99b42c}) Port - - - - + + + + Register address The name of the ParamType (ThingClass: holdingRegister, Type: thing, ID: {c771e09e-15fe-4ea9-9662-c44e2df556a8}) ---------- @@ -148,16 +157,22 @@ The name of the ParamType (ThingClass: coil, Type: thing, ID: {9d40c4ce-d251-43b Registeradresse - + + Serial number + The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {9908b01f-a76b-4b21-8242-b507c9252254}) + + + + Serial port The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {ed49f7d8-ab18-4c37-9b80-1004b75dcb91}) Serielle Schnittstelle - - - - + + + + Slave address The name of the ParamType (ThingClass: holdingRegister, Type: thing, ID: {35879cf9-631c-4fe0-95c0-a4bb2e9039e6}) ---------- @@ -169,28 +184,37 @@ The name of the ParamType (ThingClass: coil, Type: thing, ID: {d85977a2-4f9c-40f Slaveadresse - + Stop bits The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {4ea8bcdf-d4c5-45a4-a54f-f10ac3f08a78}) Stopbits - + + + Timeout + The name of the ParamType (ThingClass: modbusRTUClient, Type: settings, ID: {b0af32f0-b8cc-4642-af5a-576732522b2c}) +---------- +The name of the ParamType (ThingClass: modbusTCPClient, Type: settings, ID: {a6aa4eff-205b-426d-ad05-90971a122138}) + + + + Update interval The name of the ParamType (ThingClass: modbusCommander, Type: plugin, ID: {0606c221-b157-4086-885d-7e7b166540a1}) Updateinterval - - - - - - - - - - + + + + + + + + + + Value The name of the ParamType (ThingClass: holdingRegister, ActionType: value, ID: {585cc4fc-07da-415f-a176-12f3baeef025}) ---------- @@ -214,8 +238,8 @@ The name of the StateType ({1cd4cd53-3043-4ed9-9ba8-62985000c599}) of ThingClass Wert - - + + Value changed The name of the EventType ({585cc4fc-07da-415f-a176-12f3baeef025}) of ThingClass holdingRegister ---------- @@ -223,14 +247,14 @@ The name of the EventType ({1cd4cd53-3043-4ed9-9ba8-62985000c599}) of ThingClass Wert geändert - + Value received The name of the EventType ({eabe2d1b-abe5-4063-adab-3cdd8500b286}) of ThingClass inputRegister Wert empfangen - - + + Write value The name of the ActionType ({585cc4fc-07da-415f-a176-12f3baeef025}) of ThingClass holdingRegister ---------- @@ -238,19 +262,19 @@ The name of the ActionType ({1cd4cd53-3043-4ed9-9ba8-62985000c599}) of ThingClas Schreibe Wert - + connection status changed The name of the EventType ({dbe7c801-0888-4e7f-a88b-ba342efb11b6}) of ThingClass discreteInput Verbindung geändert - + nymea The name of the vendor ({2062d64d-3232-433c-88bc-0d33c0ba2ba6}) nymea - + value changed The name of the EventType ({c772bd7f-6e51-4b28-b182-3b979c1298ce}) of ThingClass discreteInput Wert geändert diff --git a/modbuscommander/translations/7dda1b6d-c37e-4c9f-a696-1666f9de66e6-en_US.ts b/modbuscommander/translations/7dda1b6d-c37e-4c9f-a696-1666f9de66e6-en_US.ts index 54e8350..f6f98e4 100644 --- a/modbuscommander/translations/7dda1b6d-c37e-4c9f-a696-1666f9de66e6-en_US.ts +++ b/modbuscommander/translations/7dda1b6d-c37e-4c9f-a696-1666f9de66e6-en_US.ts @@ -4,30 +4,30 @@ ModbusCommander - + Baud rate The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {45dfc828-f238-4263-89a3-9b35cf5dea39}) - + Coil The name of the ThingClass ({f53524ea-1d06-40a9-b7a4-041297b21e84}) - - - - - - - - - - - - + + + + + + + + + + + + Connected The name of the ParamType (ThingClass: holdingRegister, EventType: connected, ID: {1f55b72a-5d13-4ae1-b136-bfd84fd9761f}) ---------- @@ -55,11 +55,11 @@ The name of the StateType ({725b541a-9e0c-4634-81eb-e415c0b8f012}) of ThingClass - - - - - + + + + + Connection status changed The name of the EventType ({1f55b72a-5d13-4ae1-b136-bfd84fd9761f}) of ThingClass holdingRegister ---------- @@ -73,70 +73,79 @@ The name of the EventType ({725b541a-9e0c-4634-81eb-e415c0b8f012}) of ThingClass - + Data bits The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {a27c664b-9f43-4573-a2cc-f65a8fa1a069}) - + Discrete input The name of the ThingClass ({d7a15b39-48d3-4591-bdad-ec5e799aa6e5}) - + Holding register The name of the ThingClass ({61a2382c-3d9f-41a1-a2fd-27b2af203c56}) - + IP Address The name of the ParamType (ThingClass: modbusTCPClient, Type: thing, ID: {2a3fcb23-931b-4ba1-b134-c49b656c76f7}) - + Input register The name of the ThingClass ({e4c34050-d115-440f-b332-63d36e3e12b8}) - + Modbus Commander The name of the plugin ModbusCommander ({7dda1b6d-c37e-4c9f-a696-1666f9de66e6}) - + Modbus RTU client The name of the ThingClass ({776df314-6186-4eb5-b824-f0d916f6d9c3}) - + Modbus TCP client The name of the ThingClass ({35d3e7dc-1f33-4b8c-baa3-eb10b4f157a7}) - + + + Number of retries + The name of the ParamType (ThingClass: modbusRTUClient, Type: settings, ID: {c4f16d6c-c1f2-4862-b0bd-6fae7193eaa8}) +---------- +The name of the ParamType (ThingClass: modbusTCPClient, Type: settings, ID: {b27c95c9-7584-46e1-9e62-89890c7bde67}) + + + + Parity The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {72de1b08-2a27-49c5-90e0-8788c3ea1da3}) - + Port The name of the ParamType (ThingClass: modbusTCPClient, Type: thing, ID: {bee8b151-815a-4159-9d8a-42b76e99b42c}) - - - - + + + + Register address The name of the ParamType (ThingClass: holdingRegister, Type: thing, ID: {c771e09e-15fe-4ea9-9662-c44e2df556a8}) ---------- @@ -148,16 +157,22 @@ The name of the ParamType (ThingClass: coil, Type: thing, ID: {9d40c4ce-d251-43b - + + Serial number + The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {9908b01f-a76b-4b21-8242-b507c9252254}) + + + + Serial port The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {ed49f7d8-ab18-4c37-9b80-1004b75dcb91}) - - - - + + + + Slave address The name of the ParamType (ThingClass: holdingRegister, Type: thing, ID: {35879cf9-631c-4fe0-95c0-a4bb2e9039e6}) ---------- @@ -169,28 +184,37 @@ The name of the ParamType (ThingClass: coil, Type: thing, ID: {d85977a2-4f9c-40f - + Stop bits The name of the ParamType (ThingClass: modbusRTUClient, Type: thing, ID: {4ea8bcdf-d4c5-45a4-a54f-f10ac3f08a78}) - + + + Timeout + The name of the ParamType (ThingClass: modbusRTUClient, Type: settings, ID: {b0af32f0-b8cc-4642-af5a-576732522b2c}) +---------- +The name of the ParamType (ThingClass: modbusTCPClient, Type: settings, ID: {a6aa4eff-205b-426d-ad05-90971a122138}) + + + + Update interval The name of the ParamType (ThingClass: modbusCommander, Type: plugin, ID: {0606c221-b157-4086-885d-7e7b166540a1}) - - - - - - - - - - + + + + + + + + + + Value The name of the ParamType (ThingClass: holdingRegister, ActionType: value, ID: {585cc4fc-07da-415f-a176-12f3baeef025}) ---------- @@ -214,8 +238,8 @@ The name of the StateType ({1cd4cd53-3043-4ed9-9ba8-62985000c599}) of ThingClass - - + + Value changed The name of the EventType ({585cc4fc-07da-415f-a176-12f3baeef025}) of ThingClass holdingRegister ---------- @@ -223,14 +247,14 @@ The name of the EventType ({1cd4cd53-3043-4ed9-9ba8-62985000c599}) of ThingClass - + Value received The name of the EventType ({eabe2d1b-abe5-4063-adab-3cdd8500b286}) of ThingClass inputRegister - - + + Write value The name of the ActionType ({585cc4fc-07da-415f-a176-12f3baeef025}) of ThingClass holdingRegister ---------- @@ -238,19 +262,19 @@ The name of the ActionType ({1cd4cd53-3043-4ed9-9ba8-62985000c599}) of ThingClas - + connection status changed The name of the EventType ({dbe7c801-0888-4e7f-a88b-ba342efb11b6}) of ThingClass discreteInput - + nymea The name of the vendor ({2062d64d-3232-433c-88bc-0d33c0ba2ba6}) - + value changed The name of the EventType ({c772bd7f-6e51-4b28-b182-3b979c1298ce}) of ThingClass discreteInput