From bdbb239c599c31bcd6caf2a4cfb391407f613829 Mon Sep 17 00:00:00 2001 From: Boernsman Date: Wed, 17 Jun 2020 13:34:05 +0200 Subject: [PATCH] changed fronius to make use of QModbus --- .../integrationplugindrexelundweiss.cpp | 178 ++++---- .../integrationplugindrexelundweiss.h | 9 +- drexelundweiss/modbusrtumaster.cpp | 363 ---------------- drexelundweiss/modbusrtumaster.h | 81 ---- fronius/fronius.pro | 2 +- fronius/integrationpluginfronius.cpp | 39 +- fronius/integrationpluginfronius.h | 3 +- fronius/sunspecthing.cpp | 397 +++++++----------- fronius/sunspecthing.h | 29 +- modbus/modbusrtumaster.cpp | 17 +- modbus/modbusrtumaster.h | 2 +- modbus/modbustcpmaster.cpp | 17 +- modbus/modbustcpmaster.h | 6 +- .../integrationpluginmodbuscommander.cpp | 6 +- .../integrationpluginmodbuscommander.h | 2 +- modbuscommander/modbuscommander.pro | 2 + mypv/integrationpluginmypv.cpp | 24 +- 17 files changed, 319 insertions(+), 858 deletions(-) delete mode 100644 drexelundweiss/modbusrtumaster.cpp delete mode 100644 drexelundweiss/modbusrtumaster.h diff --git a/drexelundweiss/integrationplugindrexelundweiss.cpp b/drexelundweiss/integrationplugindrexelundweiss.cpp index ed3bd60..4ff6714 100644 --- a/drexelundweiss/integrationplugindrexelundweiss.cpp +++ b/drexelundweiss/integrationplugindrexelundweiss.cpp @@ -70,20 +70,15 @@ void IntegrationPluginDrexelUndWeiss::discoverThings(ThingDiscoveryInfo *info) } info->finish(Thing::ThingErrorNoError); return; - } - - if (info->thingClassId() == x2luThingClassId) { + } else if (info->thingClassId() == x2luThingClassId) { info->finish(Thing::ThingErrorNoError); return; - } - - if (info->thingClassId() == x2wpThingClassId) { + } else if (info->thingClassId() == x2wpThingClassId) { info->finish(Thing::ThingErrorNoError); return; + } else { + Q_ASSERT_X(false, "discoverThings", QString("Unhandled thingClassId: %1").arg(info->thingClassId().toString()).toUtf8()); } - - info->finish(Thing::ThingErrorThingClassNotFound); - return; } void IntegrationPluginDrexelUndWeiss::setupThing(ThingSetupInfo *info) @@ -97,29 +92,21 @@ void IntegrationPluginDrexelUndWeiss::setupThing(ThingSetupInfo *info) ModbusRTUMaster *modbus = new ModbusRTUMaster(serialPort, baudRate, QSerialPort::Parity::NoParity, 8, 1, this); connect(modbus, &ModbusRTUMaster::connectionStateChanged, this, &IntegrationPluginDrexelUndWeiss::onConnectionStateChanged); - connect(modbus, &ModbusRTUMaster::receivedCoil, this, &IntegrationPluginDrexelUndWeiss::onReceivedCoil); - connect(modbus, &ModbusRTUMaster::receivedDiscreteInput, this, &IntegrationPluginDrexelUndWeiss::onReceivedDiscreteInput); connect(modbus, &ModbusRTUMaster::receivedHoldingRegister, this, &IntegrationPluginDrexelUndWeiss::onReceivedHoldingRegister); - connect(modbus, &ModbusRTUMaster::receivedInputRegister, this, &IntegrationPluginDrexelUndWeiss::onReceivedInputRegister); - m_modbusRTUMasters.insert(thing, modbus); m_usedSerialPorts.append(serialPort); info->finish(Thing::ThingErrorNoError); return; - } - - if (thing->thingClassId() == x2luThingClassId) { + } else if (thing->thingClassId() == x2luThingClassId) { info->finish(Thing::ThingErrorNoError); return; - } - - if (thing->thingClassId() == x2wpThingClassId) { + } else if (thing->thingClassId() == x2wpThingClassId) { info->finish(Thing::ThingErrorNoError); return; + } else { + Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); } - info->finish(Thing::ThingErrorThingClassNotFound); - return; } void IntegrationPluginDrexelUndWeiss::postSetupThing(Thing *thing) @@ -138,12 +125,10 @@ void IntegrationPluginDrexelUndWeiss::postSetupThing(Thing *thing) qCWarning(dcDrexelUndWeiss()) << "No modbus master available"; } thing->setStateValue(modbusConnectionConnectedStateTypeId, true); - } - - if ((thing->thingClassId() == x2luThingClassId) || (thing->thingClassId() == x2wpThingClassId)) { + } else if ((thing->thingClassId() == x2luThingClassId) || (thing->thingClassId() == x2wpThingClassId)) { Thing *parentThing = myThings().findById(thing->parentId()); if (!parentThing) { - qWarning(dcDrexelUndWeiss()) << "Could not find the parent Thing"; + qCWarning(dcDrexelUndWeiss()) << "Could not find the parent Thing"; return; } ModbusRTUMaster *modbus = m_modbusRTUMasters.value(parentThing); @@ -152,6 +137,8 @@ void IntegrationPluginDrexelUndWeiss::postSetupThing(Thing *thing) } updateStates(thing); // Update states + } else { + Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); } } @@ -243,7 +230,7 @@ void IntegrationPluginDrexelUndWeiss::executeAction(ThingActionInfo *info) info->finish(Thing::ThingErrorActionTypeNotFound); } } else { - info->finish(Thing::ThingErrorThingClassNotFound); + Q_ASSERT_X(false, "executeAction", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); } } @@ -329,24 +316,16 @@ void IntegrationPluginDrexelUndWeiss::onPluginConfigurationChanged(const ParamTy void IntegrationPluginDrexelUndWeiss::onConnectionStateChanged(bool status) { - Q_UNUSED(status) + ModbusRTUMaster *modbusRtuMaster = static_cast(sender()); + Thing *thing = m_modbusRTUMasters.key(modbusRtuMaster); + if (!thing) + return; + if (thing->thingClassId() == modbusConnectionThingClassId) { + thing->setStateValue(modbusConnectionConnectedStateTypeId, status); + } } -void IntegrationPluginDrexelUndWeiss::onReceivedCoil(int slaveAddress, int modbusRegister, bool value) -{ - Q_UNUSED(slaveAddress) - Q_UNUSED(modbusRegister) - Q_UNUSED(value) -} - -void IntegrationPluginDrexelUndWeiss::onReceivedDiscreteInput(int slaveAddress, int modbusRegister, bool value) -{ - Q_UNUSED(slaveAddress) - Q_UNUSED(modbusRegister) - Q_UNUSED(value) -} - -void IntegrationPluginDrexelUndWeiss::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, int value) +void IntegrationPluginDrexelUndWeiss::onReceivedHoldingRegister(uint slaveAddress, uint modbusRegister, const QVector &values) { ModbusRTUMaster *modbus = static_cast(sender()); @@ -357,29 +336,29 @@ void IntegrationPluginDrexelUndWeiss::onReceivedHoldingRegister(int slaveAddress if (thing->thingClassId() == x2luThingClassId && thing->paramValue(x2luThingSlaveAddressParamTypeId) == slaveAddress) { switch (modbusRegister) { case ModbusRegisterX2::Waermepumpe: - thing->setStateValue(x2wpPowerStateTypeId, value); + thing->setStateValue(x2wpPowerStateTypeId, values[0]); break; case ModbusRegisterX2::RaumSoll: - thing->setStateValue(x2wpTargetTemperatureStateTypeId, value/1000.00); + thing->setStateValue(x2wpTargetTemperatureStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::Raum: - thing->setStateValue(x2wpTemperatureStateTypeId, value/1000.00); + thing->setStateValue(x2wpTemperatureStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::TemperaturWarmwasserspeicherUnten: - thing->setStateValue(x2wpWaterTemperatureStateTypeId, value/1000.00); + thing->setStateValue(x2wpWaterTemperatureStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::BrauchwasserSolltermperatur: - thing->setStateValue(x2wpTargetWaterTemperatureStateTypeId, value/1000.00); + thing->setStateValue(x2wpTargetWaterTemperatureStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::Auszenluft: - thing->setStateValue(x2wpOutsideAirTemperatureStateTypeId, value/1000.00); + thing->setStateValue(x2wpOutsideAirTemperatureStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::Summenstoerung: - if (value != 0) { + if (values[0] != 0) { //get actual error } else { thing->setStateValue(x2wpErrorStateTypeId, "No Error"); @@ -387,139 +366,139 @@ void IntegrationPluginDrexelUndWeiss::onReceivedHoldingRegister(int slaveAddress break; case ModbusRegisterX2::StoerungAbluftventilator: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Exhaust fan"); break; case ModbusRegisterX2::StoerungBoilerfuehlerElektroheizstab: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Boiler sensor electric heating element"); break; case ModbusRegisterX2::StoerungBoilerfuehlerSolar: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Boiler sensor solar"); break; case ModbusRegisterX2::StoerungBoilerfuehlerWaermepumpe: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Boiler sensor heat pump"); break; case ModbusRegisterX2::StoerungBoileruebertemperatur: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Boiler overtemperature"); break; case ModbusRegisterX2::StoerungCO2Sensor: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "CO2-Sensor"); break; case ModbusRegisterX2::StoerungDruckverlustAbluftZuGrosz: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Pressure loss exhaust air too big"); break; case ModbusRegisterX2::StoerungDruckverlustZuluftZuGrosz: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Pressure loss supply air too large"); break; case ModbusRegisterX2::StoerungDurchflussmengeHeizgkreis: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Flow rate of heating circuit"); break; case ModbusRegisterX2::StoerungDurchflussmengeSolekreis: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Flow rate brine circuit"); break; case ModbusRegisterX2::StoerungTeilnehmerNichtErreichbar: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Participant not available"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerAuszenluft: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor outside air"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerHeizkreisVorlauf: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor heating circuit flow"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerRaum: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor room"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerSolarkollektor: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor solar collector"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerSole: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor brine"); break; case ModbusRegisterX2::StoerungTemperaturfuehlerSoleAuszenluft: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Temperature sensor brine outside air"); break; case ModbusRegisterX2::StoerungWaermepumpeHochdruck: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Heat pump high pressure"); break; case ModbusRegisterX2::StoerungWaermepumpeNiederdruck: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Heat pump low pressure"); break; case ModbusRegisterX2::StoerungWertNichtZulaessig: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Value not allowed"); break; case ModbusRegisterX2::StoerungZuluftventilator: - if (value != 0) + if (values[0] != 0) thing->setStateValue(x2wpErrorStateTypeId, "Supply air fan"); break; case ModbusRegisterX2::LeistungKompressor: - thing->setStateValue(x2wpPowerCompressorStateTypeId, value/1000.00); + thing->setStateValue(x2wpPowerCompressorStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::LeistungWarmwasser: - thing->setStateValue(x2wpPowerWaterHeatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpPowerWaterHeatingStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::LeistungRaumheizung: - thing->setStateValue(x2wpPowerRoomHeatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpPowerRoomHeatingStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::LeistungLuftvorwaermung: - thing->setStateValue(x2wpPowerAirPreheatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpPowerAirPreheatingStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::EnergieKompressor: - thing->setStateValue(x2wpEnergyCompressorStateTypeId, value/1000.00); + thing->setStateValue(x2wpEnergyCompressorStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::EnergieWarmwasser: - thing->setStateValue(x2wpEnergyWaterHeatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpEnergyWaterHeatingStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::EnergieRaumheizung: - thing->setStateValue(x2wpEnergyRoomHeatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpEnergyRoomHeatingStateTypeId, values[0]/1000.00); break; case ModbusRegisterX2::EnergieLuftvorerwarrmung: - thing->setStateValue(x2wpEnergyAirPreheatingStateTypeId, value/1000.00); + thing->setStateValue(x2wpEnergyAirPreheatingStateTypeId, values[0]/1000.00); break; default: break; @@ -528,32 +507,32 @@ void IntegrationPluginDrexelUndWeiss::onReceivedHoldingRegister(int slaveAddress switch (modbusRegister) { case ModbusRegisterX2::Betriebsart: - if (value == VentialtionMode::ManuellStufe0) { + if (values[0] == VentialtionMode::ManuellStufe0) { thing->setStateValue(x2luVentilationModeStateTypeId, "Manual level 0"); - } else if (value == VentialtionMode::ManuellStufe1) { + } else if (values[0] == VentialtionMode::ManuellStufe1) { thing->setStateValue(x2luVentilationModeStateTypeId, "Manual level 1"); - } else if (value == VentialtionMode::ManuellStufe2) { + } else if (values[0] == VentialtionMode::ManuellStufe2) { thing->setStateValue(x2luVentilationModeStateTypeId, "Manual level 2"); - } else if (value == VentialtionMode::ManuellStufe3) { + } else if (values[0] == VentialtionMode::ManuellStufe3) { thing->setStateValue(x2luVentilationModeStateTypeId, "Manual level 3"); - } else if (value == VentialtionMode::Automatikbetrieb) { + } else if (values[0] == VentialtionMode::Automatikbetrieb) { thing->setStateValue(x2luVentilationModeStateTypeId, "Automatic"); - } else if (value == VentialtionMode::Party) { + } else if (values[0] == VentialtionMode::Party) { thing->setStateValue(x2luVentilationModeStateTypeId, "Party"); } break; case ModbusRegisterX2::AktiveLuefterstufe: - thing->setStateValue(x2luActiveVentilationLevelStateTypeId, value); + thing->setStateValue(x2luActiveVentilationLevelStateTypeId, values[0]); break; case ModbusRegisterX2::CO2: - thing->setStateValue(x2luCo2StateTypeId, value); + thing->setStateValue(x2luCo2StateTypeId, values[0]); break; } } } if (modbusRegister == ModbusRegisterX2::Geraetetyp) { - switch (value) { + switch (values[0]) { case DeviceType::X2_WP: { qDebug(dcDrexelUndWeiss()) << "Discovered X2 heat pump"; QList thingDescriptors; @@ -587,29 +566,24 @@ void IntegrationPluginDrexelUndWeiss::onReceivedHoldingRegister(int slaveAddress qDebug(dcDrexelUndWeiss()) << "Discovered Aerosilent Bianco"; break; default: - qDebug(dcDrexelUndWeiss()) << "Unkown Thingtype" << value; + qDebug(dcDrexelUndWeiss()) << "Unkown Thingtype" << values[0]; } } } } -void IntegrationPluginDrexelUndWeiss::onReceivedInputRegister(int slaveAddress, int modbusRegister, int value) -{ - Q_UNUSED(slaveAddress) - Q_UNUSED(modbusRegister) - Q_UNUSED(value) -} - void IntegrationPluginDrexelUndWeiss::onWriteRequestFinished(QUuid requestId, bool success) { - ThingActionInfo *info = m_pendingActions.take(requestId); - if (!info) - return; + if (m_pendingActions.contains(requestId)) { + ThingActionInfo *info = m_pendingActions.take(requestId); + if (!info) + return; - if (success) { - info->finish(Thing::ThingErrorNoError); - } else { - info->finish(Thing::ThingErrorHardwareFailure); + if (success) { + info->finish(Thing::ThingErrorNoError); + } else { + info->finish(Thing::ThingErrorHardwareFailure); + } } } diff --git a/drexelundweiss/integrationplugindrexelundweiss.h b/drexelundweiss/integrationplugindrexelundweiss.h index aef6390..9ada4ff 100644 --- a/drexelundweiss/integrationplugindrexelundweiss.h +++ b/drexelundweiss/integrationplugindrexelundweiss.h @@ -34,7 +34,7 @@ #include "integrations/integrationplugin.h" #include "plugintimer.h" -#include "modbusrtumaster.h" +#include "../modbus/modbusrtumaster.h" #include #include @@ -71,10 +71,9 @@ private slots: void onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value); void onConnectionStateChanged(bool status); - void onReceivedCoil(int slaveAddress, int modbusRegister, bool value); - void onReceivedDiscreteInput(int slaveAddress, int modbusRegister, bool value); - void onReceivedHoldingRegister(int slaveAddress, int modbusRegister, int value); - void onReceivedInputRegister(int slaveAddress, int modbusRegister, int value); + void onReceivedCoil(uint slaveAddress, uint modbusRegister, bool value); + void onReceivedDiscreteInput(uint slaveAddress, uint modbusRegister, bool value); + void onReceivedHoldingRegister(uint slaveAddress, uint modbusRegister, const QVector &values); void onWriteRequestFinished(QUuid requestId, bool success); }; diff --git a/drexelundweiss/modbusrtumaster.cpp b/drexelundweiss/modbusrtumaster.cpp deleted file mode 100644 index ccf2e97..0000000 --- a/drexelundweiss/modbusrtumaster.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 . -* -* 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 - -ModbusRTUMaster::ModbusRTUMaster(QString serialPort, int baudrate, QSerialPort::Parity parity, int dataBits, int 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() -{ - qDebug(dcDrexelUndWeiss()) << "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::onReplyFinished() -{ - QModbusReply *reply = qobject_cast(sender()); - if (!reply) - return; - reply->deleteLater(); - - int modbusAddress = 0; - if (reply->error() == QModbusDevice::NoError) { - const QModbusDataUnit unit = reply->result(); - - for (int i = 0; i < static_cast(unit.valueCount()); i++) { - //qCDebug(dcUniPi()) << "Start Address:" << unit.startAddress() << "Register Type:" << unit.registerType() << "Value:" << unit.value(i); - modbusAddress = unit.startAddress() + i; - - switch (unit.registerType()) { - case QModbusDataUnit::RegisterType::Coils: - emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::DiscreteInputs: - emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::InputRegisters: - emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::HoldingRegisters: - emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::Invalid: - break; - } - } - - } else if (reply->error() == QModbusDevice::ProtocolError) { - qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->errorString() << reply->rawResult().exceptionCode(); - } else { - qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->error(); - } -} - -void ModbusRTUMaster::onReplyErrorOccured(QModbusDevice::Error error) -{ - qCWarning(dcDrexelUndWeiss()) << "Modbus replay error:" << error; - QModbusReply *reply = qobject_cast(sender()); - if (!reply) - return; - reply->finished(); //to make sure it will be deleted -} - -void ModbusRTUMaster::onReconnectTimer() -{ - if(!m_modbusRtuSerialMaster->connectDevice()) { - m_reconnectTimer->start(10000); - } -} - -bool ModbusRTUMaster::readCoil(int slaveAddress, int registerAddress) -{ - if (!m_modbusRtuSerialMaster) - return false; - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1); - - if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, this, &ModbusRTUMaster::onReplyFinished); - connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured); - QTimer::singleShot(200, reply, SLOT(deleteLater())); - } else { - delete reply; // broadcast replies return immediately - } - } else { - qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString(); - } - return true; -} - -QUuid ModbusRTUMaster::writeCoil(int slaveAddress, int registerAddress, bool value) -{ - QUuid requestId = QUuid::createUuid(); - if (!m_modbusRtuSerialMaster) - return requestId; - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, 1); - request.setValue(0, static_cast(value)); - - if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, this, [requestId, this] { - - QModbusReply *reply = qobject_cast(sender()); - if (!reply) - return; - reply->deleteLater(); - - int modbusAddress = 0; - if (reply->error() == QModbusDevice::NoError) { - const QModbusDataUnit unit = reply->result(); - emit writeRequestFinished(requestId, true); - - for (int i = 0; i < static_cast(unit.valueCount()); i++) { - //qCDebug(dcUniPi()) << "Start Address:" << unit.startAddress() << "Register Type:" << unit.registerType() << "Value:" << unit.value(i); - modbusAddress = unit.startAddress() + i; - - switch (unit.registerType()) { - case QModbusDataUnit::RegisterType::Coils: - emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::DiscreteInputs: - emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::InputRegisters: - emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::HoldingRegisters: - emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::Invalid: - break; - } - } - } else if (reply->error() == QModbusDevice::ProtocolError) { - emit writeRequestFinished(requestId, false); - qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->errorString() << reply->rawResult().exceptionCode(); - } else { - emit writeRequestFinished(requestId, false); - qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->error(); - } - }); - connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured); - QTimer::singleShot(200, reply, SLOT(deleteLater())); - } else { - delete reply; // broadcast replies return immediately - } - } else { - qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString(); - } - return requestId; -} - -QUuid ModbusRTUMaster::writeHoldingRegister(int slaveAddress, int registerAddress, int value) -{ - QUuid requestId = QUuid::createUuid(); - if (!m_modbusRtuSerialMaster){ - qCWarning(dcDrexelUndWeiss()) << "Modbus RTU interface not available"; - return requestId; - } - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1); - request.setValue(0, static_cast(value)); - - if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, this, [requestId, this] { - - QModbusReply *reply = qobject_cast(sender()); - if (!reply) - return; - reply->deleteLater(); - - int modbusAddress = 0; - if (reply->error() == QModbusDevice::NoError) { - const QModbusDataUnit unit = reply->result(); - emit writeRequestFinished(requestId, true); - - for (int i = 0; i < static_cast(unit.valueCount()); i++) { - //qCDebug(dcUniPi()) << "Start Address:" << unit.startAddress() << "Register Type:" << unit.registerType() << "Value:" << unit.value(i); - modbusAddress = unit.startAddress() + i; - - switch (unit.registerType()) { - case QModbusDataUnit::RegisterType::Coils: - emit receivedCoil(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::DiscreteInputs: - emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::InputRegisters: - emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::HoldingRegisters: - emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.value(i)); - break; - case QModbusDataUnit::RegisterType::Invalid: - break; - } - } - } else if (reply->error() == QModbusDevice::ProtocolError) { - emit writeRequestFinished(requestId, false); - qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->errorString() << reply->rawResult().exceptionCode(); - } else { - emit writeRequestFinished(requestId, false); - qCWarning(dcDrexelUndWeiss()) << "Read response error:" << reply->error(); - } - }); - connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured); - QTimer::singleShot(200, reply, SLOT(deleteLater())); - } else { - delete reply; // broadcast replies return immediately - } - } else { - qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString(); - } - return requestId; -} - -bool ModbusRTUMaster::readDiscreteInput(int slaveAddress, int registerAddress) -{ - if (!m_modbusRtuSerialMaster) - return false; - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, registerAddress, 1); - - if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, this, &ModbusRTUMaster::onReplyFinished); - connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured); - QTimer::singleShot(200, reply, SLOT(deleteLater())); - } else { - delete reply; // broadcast replies return immediately - } - } else { - qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString(); - } - return true; -} - -bool ModbusRTUMaster::readInputRegister(int slaveAddress, int registerAddress) -{ - if (!m_modbusRtuSerialMaster) - return false; - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, registerAddress, 1); - - if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, this, &ModbusRTUMaster::onReplyFinished); - connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured); - QTimer::singleShot(200, reply, SLOT(deleteLater())); - } else { - delete reply; // broadcast replies return immediately - } - } else { - qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString(); - } - return true; -} - -bool ModbusRTUMaster::readHoldingRegister(int slaveAddress, int registerAddress) -{ - if (!m_modbusRtuSerialMaster) - return false; - - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, 1); - - if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, this, &ModbusRTUMaster::onReplyFinished); - connect(reply, &QModbusReply::errorOccurred, this, &ModbusRTUMaster::onReplyErrorOccured); - QTimer::singleShot(200, reply, SLOT(deleteLater())); - } else { - delete reply; // broadcast replies return immediately - } - } else { - qCWarning(dcDrexelUndWeiss()) << "Read error: " << m_modbusRtuSerialMaster->errorString(); - } - return true; -} - - -void ModbusRTUMaster::onModbusErrorOccurred(QModbusDevice::Error error) -{ - qCWarning(dcDrexelUndWeiss()) << "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); -} diff --git a/drexelundweiss/modbusrtumaster.h b/drexelundweiss/modbusrtumaster.h deleted file mode 100644 index b7abc28..0000000 --- a/drexelundweiss/modbusrtumaster.h +++ /dev/null @@ -1,81 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* 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 . -* -* 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 -#include -#include -#include -#include - -class ModbusRTUMaster : public QObject -{ - Q_OBJECT -public: - explicit ModbusRTUMaster(QString serialPort, int baudrate, QSerialPort::Parity parity, int dataBits, int stopBits, QObject *parent = nullptr); - ~ModbusRTUMaster(); - - bool connectDevice(); - - bool readCoil(int slaveAddress, int registerAddress); - bool readDiscreteInput(int slaveAddress, int registerAddress); - bool readInputRegister(int slaveAddress, int registerAddress); - bool readHoldingRegister(int slaveAddress, int registerAddress); - - QUuid writeCoil(int slaveAddress, int registerAddress, bool status); - QUuid writeHoldingRegister(int slaveAddress, int registerAddress, int data); - - QString serialPort(); - -private: - QModbusRtuSerialMaster *m_modbusRtuSerialMaster; - QTimer *m_reconnectTimer = nullptr; - -private slots: - void onReplyFinished(); - void onReplyErrorOccured(QModbusDevice::Error error); - void onReconnectTimer(); - - void onModbusErrorOccurred(QModbusDevice::Error error); - void onModbusStateChanged(QModbusDevice::State state); - -signals: - void connectionStateChanged(bool status); - void receivedCoil(int slaveAddress, int modbusRegister, bool value); - void receivedDiscreteInput(int slaveAddress, int modbusRegister, bool value); - void receivedHoldingRegister(int slaveAddress, int modbusRegister, int value); - void receivedInputRegister(int slaveAddress, int modbusRegister, int value); - - void writeRequestFinished(QUuid requestId, bool success); -}; - -#endif // MODBUSRTUMASTER_H diff --git a/fronius/fronius.pro b/fronius/fronius.pro index 0a0729e..e213a6a 100644 --- a/fronius/fronius.pro +++ b/fronius/fronius.pro @@ -12,7 +12,7 @@ SOURCES += \ froniusstorage.cpp \ froniusmeter.cpp \ sunspecthing.cpp \ - ../modbus/modbustcpmaster.h \ + ../modbus/modbustcpmaster.cpp \ HEADERS += \ integrationpluginfronius.h \ diff --git a/fronius/integrationpluginfronius.cpp b/fronius/integrationpluginfronius.cpp index d4c7d1a..a83f8b9 100644 --- a/fronius/integrationpluginfronius.cpp +++ b/fronius/integrationpluginfronius.cpp @@ -265,7 +265,7 @@ void IntegrationPluginFronius::postSetupThing(Thing *thing) SunspecThing *sunspecThing = m_sunspecThings.key(thing); sunspecThing->update(); } else { - Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); + Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); } } @@ -302,7 +302,7 @@ void IntegrationPluginFronius::thingRemoved(Thing *thing) qCDebug(dcFronius()) << "Sunspec thing deleted"; return; } else { - Q_ASSERT_X(false, "thingRemoved", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); + Q_ASSERT_X(false, "thingRemoved", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); } if (myThings().isEmpty()) { @@ -335,36 +335,41 @@ void IntegrationPluginFronius::executeAction(ThingActionInfo *info) } if (action.actionTypeId() == sunspecStorageGridChargingActionTypeId) { - if (sunspecThing->setGridCharging(action.param(sunspecStorageGridChargingActionGridChargingParamTypeId).value().toBool())){ - info->finish(Thing::ThingErrorNoError); - } else { + QUuid requestId = sunspecThing->setGridCharging(action.param(sunspecStorageGridChargingActionGridChargingParamTypeId).value().toBool()); + if (requestId.isNull()) { info->finish(Thing::ThingErrorHardwareFailure); + } else { + m_asyncActions.insert(requestId, info); } } else if (action.actionTypeId() == sunspecStorageEnableChargingLimitActionTypeId) { int value = (action.param(sunspecStorageEnableChargingLimitActionEnableChargingLimitParamTypeId).value().toBool() << 1) | thing->stateValue(sunspecStorageEnableDischargingLimitStateTypeId).toBool(); - if (sunspecThing->setStorageControlMode(value)) { - info->finish(Thing::ThingErrorNoError); - } else { + QUuid requestId = sunspecThing->setStorageControlMode(value); + if (requestId.isNull()) { info->finish(Thing::ThingErrorHardwareFailure); + } else { + m_asyncActions.insert(requestId, info); } } else if (action.actionTypeId() == sunspecStorageChargingRateActionTypeId) { - if (sunspecThing->setChargingRate(action.param(sunspecStorageChargingRateActionChargingRateParamTypeId).value().toInt())) { - info->finish(Thing::ThingErrorNoError); - } else { + QUuid requestId = sunspecThing->setChargingRate(action.param(sunspecStorageChargingRateActionChargingRateParamTypeId).value().toInt()); + if (requestId.isNull()) { info->finish(Thing::ThingErrorHardwareFailure); + } else { + m_asyncActions.insert(requestId, info); } } else if (action.actionTypeId() == sunspecStorageEnableDischargingLimitActionTypeId) { int value = (action.param(sunspecStorageEnableDischargingLimitActionEnableDischargingLimitParamTypeId).value().toBool() << 1) | thing->stateValue(sunspecStorageEnableChargingLimitStateTypeId).toBool(); - if (sunspecThing->setStorageControlMode(value)) { - info->finish(Thing::ThingErrorNoError); - } else { + QUuid requestId = sunspecThing->setStorageControlMode(value); + if (requestId.isNull()) { info->finish(Thing::ThingErrorHardwareFailure); + } else { + m_asyncActions.insert(requestId, info); } } else if (action.actionTypeId() == sunspecStorageDischargingRateActionTypeId) { - if (sunspecThing->setDischargingRate(action.param(sunspecStorageDischargingRateActionDischargingRateParamTypeId).value().toInt())) { - info->finish(Thing::ThingErrorNoError); - } else { + QUuid requestId = sunspecThing->setDischargingRate(action.param(sunspecStorageDischargingRateActionDischargingRateParamTypeId).value().toInt()); + if (requestId.isNull()) { info->finish(Thing::ThingErrorHardwareFailure); + } else { + m_asyncActions.insert(requestId, info); } } else { Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8()); diff --git a/fronius/integrationpluginfronius.h b/fronius/integrationpluginfronius.h index 10d080b..1b9cccc 100644 --- a/fronius/integrationpluginfronius.h +++ b/fronius/integrationpluginfronius.h @@ -38,6 +38,7 @@ #include #include #include +#include class PluginTimer; @@ -67,7 +68,7 @@ private: QHash m_froniusStorages; QHash m_sunspecThings; - QHash m_asyncActions; + QHash m_asyncActions; void updateThingStates(Thing *thing); diff --git a/fronius/sunspecthing.cpp b/fronius/sunspecthing.cpp index e757836..4784d85 100644 --- a/fronius/sunspecthing.cpp +++ b/fronius/sunspecthing.cpp @@ -37,107 +37,32 @@ SunspecThing::SunspecThing(Thing *thing, QObject *parent) : QObject(parent), - m_thing(thing), - m_modbus(nullptr) + m_thing(thing) { } SunspecThing::~SunspecThing() { - modbus_free(m_modbus); - m_modbus = nullptr; -} -void SunspecThing::destroyModbus() -{ - if (m_thing) - m_thing->setStateValue(sunspecStorageConnectedStateTypeId, false); - - if (!m_modbus) - return; - - modbus_free(m_modbus); - m_modbus = nullptr; } void SunspecThing::readCommonBlock() { - if (!m_modbus) + if (!m_modbusTcpMaster) return; - qCDebug(dcFronius()) << "Successfully set slave to" << m_slaveId; - int address = 40001 - 1; - uint16_t data[70]; - // Read common block - if (modbus_read_registers(m_modbus, address, 70, data) < 0) { - qCWarning(dcFronius()) << "Could not read register address:" << modbus_strerror(errno); - destroyModbus(); - return; - } - - qCDebug(dcFronius()) << "Sunspec Identification:" << convertModbusRegisters(data, 0, 2); - - if (convertModbusRegisters(data, 0, 2) != "SunS") { - qCWarning(dcFronius()) << "Could not find SunS value at" << address << "on" << address; - destroyModbus(); - return; - } - - // ID: 40003 - qCDebug(dcFronius()) << "Id:" << data[2]; - - // Manufacturer: 40005 | 16 - qCDebug(dcFronius()) << "Manufacturer:" << QString::fromLatin1(convertModbusRegisters(data, 4, 16)); - - // Thing model: 40021 | 16 - qCDebug(dcFronius()) << "Thing model:" << QString::fromLatin1(convertModbusRegisters(data, 20, 16)); - - // Data manager version: 40037 | 8 - qCDebug(dcFronius()) << "Data manager version:" << QString::fromLatin1(convertModbusRegisters(data, 36, 8)); - - // Inverter Version: 40045 | 8 - qCDebug(dcFronius()) << "Inverter version:" << QString::fromLatin1(convertModbusRegisters(data, 44, 8)); - - // Serial Number: 40053 | 16 - qCDebug(dcFronius()) << "Serial number:" << QString::fromLatin1(convertModbusRegisters(data, 52, 16)); - - // Modbus thing address : 40069 | 1 - qCDebug(dcFronius()) << "Thing modbus address:" << data[67]; - - /*Sunspec Model Type - zum Auswählen des Datentyps von Datenmodellen für Wechselrichter - (3d) float - Darstellung als Gleitkommazahlen - SunSpec Inverter Model I111, I112 oder I113 - (3e) int+SF - Darstellung als ganze Zahlen mit Skalierungsfaktoren - SunSpec Inverter Model I101, I102 oder I103 - WICHTIG! Da die verschiedenen Modelle über unterschiedliche Anzahlen an Re- - gistern verfügen, ändern sich durch den Wechsel des Datentyps auch die Regis- - teradressen aller nachfolgenden Modelle.*/ - qCDebug(dcFronius()) << "SunSpec Inverter Modbus Map:" << data[69]; - if (data[69] > 110){ - m_floatingPointRepresentation = true; - } else { - m_floatingPointRepresentation = false; - } - - readStorageBlock(); + m_modbusTcpMaster->readHoldingRegister(m_slaveId, address, 70); } void SunspecThing::readStorageBlock() { - if (!m_modbus) + if (!m_modbusTcpMaster) return; - - qCDebug(dcFronius()) << "Storage"; - int address; - if (m_floatingPointRepresentation) { address = 40313; } else { @@ -147,68 +72,7 @@ void SunspecThing::readStorageBlock() - bei Einstellung „float“: 40313 - bei Einstellung „int+SF“: 40303 */ - uint16_t data[26]; - - if (modbus_read_registers(m_modbus, address, 26, data) < 0) { - qCWarning(dcFronius()) << "Could not read register address" << address << ":" << modbus_strerror(errno); - destroyModbus(); - return; - } - - // ID - qCDebug(dcFronius()) << "Id:" << data[0]; - - if (data[0] != 124) { - qCWarning(dcFronius()) << "Invalid id in register address" << address << ":" << data[0]; - destroyModbus(); - return; - } - - // L - qCDebug(dcFronius()) << "Register count:" << data[1]; - - // WchaMax - qCDebug(dcFronius()) << "Setpoint of maximum charge:" << data[2] << "W"; - - // WchaGra - qCDebug(dcFronius()) << "Setpoint for maximum charge:" << data[3] << "[s]"; - - // WdisChaGra - qCDebug(dcFronius()) << "Setpoint for maximum discharge rate:" << data[4] << "[s]"; - - // StorCtl_Mod: Activate hold/discharge/charge storage control mode. Bit0: charge, Bit 1: discharge - QBitArray storageControlBits = convertModbusRegisterBits(data[5]); - - qCDebug(dcFronius()) << "Charging control mode:" << (storageControlBits.testBit(0) ? "On" : "Off"); - m_thing->setStateValue(sunspecStorageEnableChargingLimitStateTypeId, storageControlBits.testBit(0)); - - qCDebug(dcFronius()) << "Discharging control mode:" << (storageControlBits.testBit(1) ? "On" : "Off"); - m_thing->setStateValue(sunspecStorageEnableDischargingLimitStateTypeId, storageControlBits.testBit(1)); - - // MinRsvPct - qCDebug(dcFronius()) << "Setpoint for minimum reserve:" << data[7] << "[%]"; - - // ChaState - qCDebug(dcFronius()) << "Current energy:" << ((double)data[8] / 100.0) << "[%]"; - m_thing->setStateValue(sunspecStorageEnergyStateTypeId, (double)data[8] / 100.0); - - // ChaSt - //Charge status of storage thing. Enumerated - qCDebug(dcFronius()) << "Charge state" << storageStateToString(static_cast(data[11])); - m_thing->setStateValue(sunspecStorageStorageStateStateTypeId, storageStateToString(static_cast(data[11]))); - - // OutWRte - m_thing->setStateValue(sunspecStorageDischargingRateStateTypeId, (int16_t)data[12] / 100); - qCDebug(dcFronius()) << "Percent of max. discharge rate:" << ((int16_t)data[12] / 100) << "[%]"; - - // InWRte - m_thing->setStateValue(sunspecStorageChargingRateStateTypeId, (int16_t)data[13] / 100); - qCDebug(dcFronius()) << "Percent of max. charge rate:" << ((int16_t)data[13] / 100) << "[%]"; - - // ChaGriSet - m_thing->setStateValue(sunspecStorageGridChargingStateTypeId, data[18]); - qCDebug(dcFronius()) << "Charging from grid:" << (data[18] == 0 ? "disabled" : "enabled"); - + m_modbusTcpMaster->readHoldingRegister(m_slaveId, address, 26); } QByteArray SunspecThing::convertModbusRegister(const uint16_t &modbusData) @@ -234,7 +98,7 @@ QBitArray SunspecThing::convertModbusRegisterBits(const uint16_t &modbusData) return bits; } -QByteArray SunspecThing::convertModbusRegisters(uint16_t *modbusData, const int &offset, const int &size) +QByteArray SunspecThing::convertModbusRegisters(const QVector &modbusData, int offset, int size) { QByteArray bytes; for (int i = offset; i < offset + size; i++) @@ -253,60 +117,30 @@ QString SunspecThing::storageStateToString(const SunspecThing::StorageState &sta bool SunspecThing::connectModbus() { - if (m_modbus) - destroyModbus(); - if (m_thing->paramValue(sunspecStorageThingModbusHostParamTypeId).toString().isEmpty()) { qCWarning(dcFronius()) << "Empty ip address"; return false; } - m_modbus = modbus_new_tcp(m_thing->paramValue(sunspecStorageThingModbusHostParamTypeId).toString().toStdString().c_str(), 502); - if (modbus_connect(m_modbus) == -1) { - qCWarning(dcFronius()) << "Connection failed:" << modbus_strerror(errno); - destroyModbus(); - return false; - } + m_modbusTcpMaster = new ModbusTCPMaster(QHostAddress(m_thing->paramValue(sunspecStorageThingModbusHostParamTypeId).toString()), 502, this); + connect(m_modbusTcpMaster, &ModbusTCPMaster::connectionStateChanged, this, &SunspecThing::connectionStateChanged); + connect(m_modbusTcpMaster, &ModbusTCPMaster::writeRequestExecuted, this, &SunspecThing::requestExecuted); + connect(m_modbusTcpMaster, &ModbusTCPMaster::receivedHoldingRegister, this, &SunspecThing::onReceivedHoldingRegister); - //TODO - //timeval response_timeout; - //response_timeout.tv_sec = 3; - //response_timeout.tv_usec = 0; - - //timeval byte_timeout; - //byte_timeout.tv_sec = 3; - //byte_timeout.tv_usec = 0; - //modbus_set_byte_timeout(m_modbus, &byte_timeout); - //modbus_set_response_timeout(m_modbus, &response_timeout); + m_modbusTcpMaster->setTimeout(3 * 1000); // 3 seconds + m_modbusTcpMaster->setNumberOfRetries(3); qCDebug(dcFronius()) << "Connected successfully" << m_thing->paramValue(sunspecStorageThingModbusHostParamTypeId).toString(); - m_thing->setStateValue(sunspecStorageConnectedStateTypeId, true); - - - if (modbus_set_slave(m_modbus, m_slaveId) != 0) { - qCWarning(dcFronius()) << "Could not set Slave Id to" << m_slaveId << ":" << modbus_strerror(errno); - destroyModbus(); - return false; - } readCommonBlock(); return true; } -void SunspecThing::disconnectModbus() +QUuid SunspecThing::setGridCharging(const bool &charging) { - if (!m_modbus) - return; - - destroyModbus(); -} - -bool SunspecThing::setGridCharging(const bool &charging) -{ - if (!m_modbus) { - connectModbus(); - return false; + if (!m_modbusTcpMaster) { + return ""; } /* Start address: @@ -327,42 +161,18 @@ bool SunspecThing::setGridCharging(const bool &charging) registerAddress = 40303 + 18 - 1; } - uint16_t value = 0; - if(modbus_read_registers(m_modbus, registerAddress, 1, &value) == -1){ - qCWarning(dcFronius()) << "Could not read register address:" << registerAddress << (int16_t)value << modbus_strerror(errno); - return false; - }else { - qDebug(dcFronius()) << "Succesfully read register:" << registerAddress << (int16_t)value ; - } - - - if (charging) { - value = 1; - if (modbus_write_register(m_modbus, registerAddress, value) == -1) { - qCWarning(dcFronius()) << "Could not write register address:" << registerAddress << value << modbus_strerror(errno); - return false; - } - } else { - value = 0; - if (modbus_write_register(m_modbus, registerAddress, value) == -1) { - qCWarning(dcFronius()) << "Could not write register address:" << registerAddress << value << modbus_strerror(errno); - return false; - } - } - - readStorageBlock(); - return true; + quint16 value = charging; + return m_modbusTcpMaster->writeHoldingRegister(m_slaveId, registerAddress, value); } -bool SunspecThing::setChargingRate(const int &charging) +QUuid SunspecThing::setChargingRate(const int &charging) { // 40313 + Offset 14 - 1 //Register Name InWRte /* Defines the maximum charge rate (charge limit). Default is 100% */ - if (!m_modbus) { - connectModbus(); - return false; + if (!m_modbusTcpMaster) { + return ""; } int registerAddress; @@ -373,29 +183,16 @@ bool SunspecThing::setChargingRate(const int &charging) } int16_t value = charging * 100; - if (modbus_write_register(m_modbus, registerAddress, value) == -1) { - qCWarning(dcFronius()) << "Could not write register address:" << registerAddress << value << modbus_strerror(errno); - return false; - } - - readStorageBlock(); - return true; + return m_modbusTcpMaster->writeHoldingRegister(m_slaveId, registerAddress, value); } -bool SunspecThing::setStorageControlMode(const int &charging) +QUuid SunspecThing::setStorageControlMode(const int &charging) { // 40313 + Offset 6 - 1 // Set charge bit to enable charge limit, set discharge bit to enable discharge limit, set both bits to enable both limits - if (!m_modbus) { - connectModbus(); - return false; - } - - if (modbus_set_slave(m_modbus, m_slaveId) != 0) { - qCWarning(dcFronius()) << "Could not set slave to" << m_slaveId << ":" << modbus_strerror(errno); - destroyModbus(); - return false; + if (!m_modbusTcpMaster) { + return ""; } int registerAddress; @@ -406,30 +203,17 @@ bool SunspecThing::setStorageControlMode(const int &charging) } uint16_t value = charging; - if (modbus_write_register(m_modbus, registerAddress, value) == -1) { - qCWarning(dcFronius()) << "Could not write register address:" << registerAddress << value << modbus_strerror(errno); - return false; - } - - readStorageBlock(); - return true; + return m_modbusTcpMaster->writeHoldingRegister(m_slaveId, registerAddress, value); } -bool SunspecThing::setDischargingRate(const int &charging) +QUuid SunspecThing::setDischargingRate(const int &charging) { // 40313 + Offset 13 - 1 //Register Name OutWRte /*Defines the maximum discharge rate (discharge limit). Default is 100% */ - if (!m_modbus) { - connectModbus(); - return false; - } - - if (modbus_set_slave(m_modbus, m_slaveId) != 0) { - qCWarning(dcFronius()) << "Could not set slave to" << m_slaveId << ":" << modbus_strerror(errno); - destroyModbus(); - return false; + if (!m_modbusTcpMaster) { + return ""; } int registerAddress; @@ -440,22 +224,129 @@ bool SunspecThing::setDischargingRate(const int &charging) } int16_t value = charging * 100; - if (modbus_write_register(m_modbus, registerAddress, value) == -1) { - qCWarning(dcFronius()) << "Could not write register address:" << registerAddress << value << modbus_strerror(errno); - return false; - } - - readStorageBlock(); - return true; + return m_modbusTcpMaster->writeHoldingRegister(m_slaveId, registerAddress, value); } void SunspecThing::update() { - if (!m_modbus) { - connectModbus(); + if (!m_modbusTcpMaster) { return; } readCommonBlock(); + readStorageBlock(); +} + +void SunspecThing::onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, const QVector &data) +{ + if (!m_thing) + return; + + switch (modbusRegister) { + case 40000: {// Common block, 70 registers long + + qCDebug(dcFronius()) << "Sunspec Identification:" << convertModbusRegisters(data, 0, 2); + + if (convertModbusRegisters(data, 0, 2) != "SunS") { + qCWarning(dcFronius()) << "Could not find SunS value at" << modbusRegister << "on" << slaveAddress; + return; + } + + // ID: 40003 + qCDebug(dcFronius()) << "Id:" << data[2]; + + // Manufacturer: 40005 | 16 + qCDebug(dcFronius()) << "Manufacturer:" << QString::fromLatin1(convertModbusRegisters(data, 4, 16)); + + // Thing model: 40021 | 16 + qCDebug(dcFronius()) << "Thing model:" << QString::fromLatin1(convertModbusRegisters(data, 20, 16)); + + // Data manager version: 40037 | 8 + qCDebug(dcFronius()) << "Data manager version:" << QString::fromLatin1(convertModbusRegisters(data, 36, 8)); + + // Inverter Version: 40045 | 8 + qCDebug(dcFronius()) << "Inverter version:" << QString::fromLatin1(convertModbusRegisters(data, 44, 8)); + + // Serial Number: 40053 | 16 + qCDebug(dcFronius()) << "Serial number:" << QString::fromLatin1(convertModbusRegisters(data, 52, 16)); + + // Modbus thing address : 40069 | 1 + qCDebug(dcFronius()) << "Thing modbus address:" << data[67]; + + /*Sunspec Model Type + zum Auswählen des Datentyps von Datenmodellen für Wechselrichter + (3d) float + Darstellung als Gleitkommazahlen + SunSpec Inverter Model I111, I112 oder I113 + (3e) int+SF + Darstellung als ganze Zahlen mit Skalierungsfaktoren + SunSpec Inverter Model I101, I102 oder I103 + WICHTIG! Da die verschiedenen Modelle über unterschiedliche Anzahlen an Re- + gistern verfügen, ändern sich durch den Wechsel des Datentyps auch die Regis- + teradressen aller nachfolgenden Modelle.*/ + qCDebug(dcFronius()) << "SunSpec Inverter Modbus Map:" << data[69]; + if (data[69] > 110){ + m_floatingPointRepresentation = true; + } else { + m_floatingPointRepresentation = false; + } + } break; + case 40303: + case 40313: { //Storage Block + // ID + qCDebug(dcFronius()) << "Id:" << data[0]; + + if (data[0] != 124) { + qCWarning(dcFronius()) << "Invalid id in register address" << modbusRegister << ":" << data[0]; + } + + // L + qCDebug(dcFronius()) << "Register count:" << data[1]; + + // WchaMax + qCDebug(dcFronius()) << "Setpoint of maximum charge:" << data[2] << "W"; + + // WchaGra + qCDebug(dcFronius()) << "Setpoint for maximum charge:" << data[3] << "[s]"; + + // WdisChaGra + qCDebug(dcFronius()) << "Setpoint for maximum discharge rate:" << data[4] << "[s]"; + + // StorCtl_Mod: Activate hold/discharge/charge storage control mode. Bit0: charge, Bit 1: discharge + QBitArray storageControlBits = convertModbusRegisterBits(data[5]); + + qCDebug(dcFronius()) << "Charging control mode:" << (storageControlBits.testBit(0) ? "On" : "Off"); + m_thing->setStateValue(sunspecStorageEnableChargingLimitStateTypeId, storageControlBits.testBit(0)); + + qCDebug(dcFronius()) << "Discharging control mode:" << (storageControlBits.testBit(1) ? "On" : "Off"); + m_thing->setStateValue(sunspecStorageEnableDischargingLimitStateTypeId, storageControlBits.testBit(1)); + + // MinRsvPct + qCDebug(dcFronius()) << "Setpoint for minimum reserve:" << data[7] << "[%]"; + + // ChaState + qCDebug(dcFronius()) << "Current energy:" << ((double)data[8] / 100.0) << "[%]"; + m_thing->setStateValue(sunspecStorageEnergyStateTypeId, (double)data[8] / 100.0); + + // ChaSt + //Charge status of storage thing. Enumerated + qCDebug(dcFronius()) << "Charge state" << storageStateToString(static_cast(data[11])); + m_thing->setStateValue(sunspecStorageStorageStateStateTypeId, storageStateToString(static_cast(data[11]))); + + // OutWRte + m_thing->setStateValue(sunspecStorageDischargingRateStateTypeId, (int16_t)data[12] / 100); + qCDebug(dcFronius()) << "Percent of max. discharge rate:" << ((int16_t)data[12] / 100) << "[%]"; + + // InWRte + m_thing->setStateValue(sunspecStorageChargingRateStateTypeId, (int16_t)data[13] / 100); + qCDebug(dcFronius()) << "Percent of max. charge rate:" << ((int16_t)data[13] / 100) << "[%]"; + + // ChaGriSet + m_thing->setStateValue(sunspecStorageGridChargingStateTypeId, data[18]); + qCDebug(dcFronius()) << "Charging from grid:" << (data[18] == 0 ? "disabled" : "enabled"); + } break; + default: + break; + }; } diff --git a/fronius/sunspecthing.h b/fronius/sunspecthing.h index 7025177..cac5329 100644 --- a/fronius/sunspecthing.h +++ b/fronius/sunspecthing.h @@ -37,9 +37,9 @@ #include #include #include +#include -#include -#include +#include "../modbus/modbustcpmaster.h" class SunspecThing : public QObject { @@ -61,31 +61,38 @@ public: private: Thing *m_thing; - modbus_t *m_modbus; + ModbusTCPMaster *m_modbusTcpMaster = nullptr; int m_slaveId = 1; bool m_floatingPointRepresentation = false; - void destroyModbus(); - void readCommonBlock(); void readStorageBlock(); QByteArray convertModbusRegister(const uint16_t &modbusData); QBitArray convertModbusRegisterBits(const uint16_t &modbusData); - QByteArray convertModbusRegisters(uint16_t *modbusData, const int &offset, const int &size); + QByteArray convertModbusRegisters(const QVector &modbusData, int offset, int size); QString storageStateToString(const StorageState &state); public slots: bool connectModbus(); - void disconnectModbus(); - bool setGridCharging(const bool &charging); - bool setDischargingRate(const int &charging); - bool setChargingRate(const int &charging); - bool setStorageControlMode(const int &charging); + QUuid setGridCharging(const bool &charging); + QUuid setDischargingRate(const int &charging); + QUuid setChargingRate(const int &charging); + QUuid setStorageControlMode(const int &charging); void update(); + +signals: + void connectionStateChanged(bool status); + void requestExecuted(QUuid requetId, bool success); + +private slots: + void onConnectionStateChanged(bool status); + void onRequestExecuted(QUuid requestId, bool success); + void onRequestError(QUuid requestId, const QString &error); + void onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, const QVector &values); }; #endif // SUNSPECTHING_H diff --git a/modbus/modbusrtumaster.cpp b/modbus/modbusrtumaster.cpp index 5ecbf61..b7b04b1 100644 --- a/modbus/modbusrtumaster.cpp +++ b/modbus/modbusrtumaster.cpp @@ -99,9 +99,9 @@ QUuid ModbusRTUMaster::readCoil(uint slaveAddress, uint registerAddress) if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) { if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); connect(reply, &QModbusReply::finished, this, [reply, requestId, this] { - if (reply->error() == QModbusDevice::NoError) { requestExecuted(requestId, true); const QModbusDataUnit unit = reply->result(); @@ -112,7 +112,6 @@ QUuid ModbusRTUMaster::readCoil(uint slaveAddress, uint registerAddress) requestExecuted(requestId, false); qCWarning(dcModbusRTU()) << "Read response error:" << reply->error(); } - reply->deleteLater(); }); connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ @@ -145,6 +144,7 @@ QUuid ModbusRTUMaster::writeCoil(uint slaveAddress, uint registerAddress, bool v if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) { if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); connect(reply, &QModbusReply::finished, this, [reply, requestId, this] { if (reply->error() == QModbusDevice::NoError) { @@ -157,7 +157,6 @@ QUuid ModbusRTUMaster::writeCoil(uint slaveAddress, uint registerAddress, bool v requestExecuted(requestId, false); qCWarning(dcModbusRTU()) << "Read response error:" << reply->error(); } - reply->deleteLater(); }); connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ @@ -189,19 +188,19 @@ QUuid ModbusRTUMaster::writeHoldingRegister(uint slaveAddress, uint registerAddr if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) { if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); 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)); + emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.values()); } else { requestExecuted(requestId, false); qCWarning(dcModbusRTU()) << "Read response error:" << reply->error(); } - reply->deleteLater(); }); connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ @@ -232,6 +231,7 @@ QUuid ModbusRTUMaster::readDiscreteInput(uint slaveAddress, uint registerAddress if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) { if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); connect(reply, &QModbusReply::finished, this, [reply, requestId, this] { if (reply->error() == QModbusDevice::NoError) { @@ -244,7 +244,6 @@ QUuid ModbusRTUMaster::readDiscreteInput(uint slaveAddress, uint registerAddress requestExecuted(requestId, false); qCWarning(dcModbusRTU()) << "Read response error:" << reply->error(); } - reply->deleteLater(); }); connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ @@ -276,6 +275,7 @@ QUuid ModbusRTUMaster::readInputRegister(uint slaveAddress, uint registerAddress if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) { if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); connect(reply, &QModbusReply::finished, this, [reply, requestId, this] { @@ -289,7 +289,6 @@ QUuid ModbusRTUMaster::readInputRegister(uint slaveAddress, uint registerAddress requestExecuted(requestId, false); qCWarning(dcModbusRTU()) << "Read response error:" << reply->error(); } - reply->deleteLater(); }); connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ @@ -321,19 +320,19 @@ QUuid ModbusRTUMaster::readHoldingRegister(uint slaveAddress, uint registerAddre if (QModbusReply *reply = m_modbusRtuSerialMaster->sendReadRequest(request, slaveAddress)) { if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); 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)); + emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.values()); } else { requestExecuted(requestId, false); qCWarning(dcModbusRTU()) << "Read response error:" << reply->error(); } - reply->deleteLater(); }); connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ diff --git a/modbus/modbusrtumaster.h b/modbus/modbusrtumaster.h index cc8c7fb..9218ac5 100644 --- a/modbus/modbusrtumaster.h +++ b/modbus/modbusrtumaster.h @@ -74,7 +74,7 @@ signals: 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 receivedHoldingRegister(uint slaveAddress, uint modbusRegister, const QVector &values); void receivedInputRegister(uint slaveAddress, uint modbusRegister, uint value); }; diff --git a/modbus/modbustcpmaster.cpp b/modbus/modbustcpmaster.cpp index 74db87b..7b92999 100644 --- a/modbus/modbustcpmaster.cpp +++ b/modbus/modbustcpmaster.cpp @@ -71,6 +71,16 @@ bool ModbusTCPMaster::connectDevice() { return m_modbusTcpClient->connectDevice(); } +void ModbusTCPMaster::setNumberOfRetries(int number) +{ + m_modbusTcpClient->setNumberOfRetries(number); +} + +void ModbusTCPMaster::setTimeout(int timeout) +{ + m_modbusTcpClient->setTimeout(timeout); +} + uint ModbusTCPMaster::port() { return m_modbusTcpClient->connectionParameter(QModbusDevice::NetworkPortParameter).toUInt(); @@ -143,7 +153,7 @@ QUuid ModbusTCPMaster::readCoil(uint slaveAddress, uint registerAddress) return requestId; } -QUuid ModbusTCPMaster::writeHoldingRegister(uint slaveAddress, uint registerAddress, const QVector &values) +QUuid ModbusTCPMaster::writeHoldingRegisters(uint slaveAddress, uint registerAddress, const QVector &values) { if (!m_modbusTcpClient) { return ""; @@ -363,6 +373,11 @@ QUuid ModbusTCPMaster::writeCoil(uint slaveAddress, uint registerAddress, bool v return requestId; } +QUuid ModbusTCPMaster::writeHoldingRegister(uint slaveAddress, uint registerAddress, quint16 value) +{ + return writeHoldingRegisters(slaveAddress, registerAddress, QVector() << value); +} + void ModbusTCPMaster::onModbusErrorOccurred(QModbusDevice::Error error) { diff --git a/modbus/modbustcpmaster.h b/modbus/modbustcpmaster.h index f824df6..c0312ee 100644 --- a/modbus/modbustcpmaster.h +++ b/modbus/modbustcpmaster.h @@ -45,6 +45,8 @@ public: ~ModbusTCPMaster(); bool connectDevice(); + void setNumberOfRetries(int number); + void setTimeout(int timeout); QUuid readCoil(uint slaveAddress, uint registerAddress); QUuid readDiscreteInput(uint slaveAddress, uint registerAddress); @@ -52,7 +54,9 @@ public: QUuid readHoldingRegister(uint slaveAddress, uint registerAddress, uint size = 1); QUuid writeCoil(uint slaveAddress, uint registerAddress, bool status); - QUuid writeHoldingRegister(uint slaveAddress, uint registerAddress, const QVector &values); + + QUuid writeHoldingRegister(uint slaveAddress, uint registerAddress, quint16 value); + QUuid writeHoldingRegisters(uint slaveAddress, uint registerAddress, const QVector &values); QHostAddress hostAddress(); uint port(); diff --git a/modbuscommander/integrationpluginmodbuscommander.cpp b/modbuscommander/integrationpluginmodbuscommander.cpp index b51ebd7..fffca4b 100644 --- a/modbuscommander/integrationpluginmodbuscommander.cpp +++ b/modbuscommander/integrationpluginmodbuscommander.cpp @@ -420,7 +420,7 @@ void IntegrationPluginModbusCommander::onReceivedDiscreteInput(quint32 slaveAddr } } -void IntegrationPluginModbusCommander::onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, int value) +void IntegrationPluginModbusCommander::onReceivedHoldingRegister(uint slaveAddress, uint modbusRegister, const QVector &values) { auto modbus = sender(); @@ -430,7 +430,7 @@ void IntegrationPluginModbusCommander::onReceivedHoldingRegister(quint32 slaveAd 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_valueStateTypeId.value(thing->thingClassId()), values[0]); thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true); return; } @@ -442,7 +442,7 @@ void IntegrationPluginModbusCommander::onReceivedHoldingRegister(quint32 slaveAd 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_valueStateTypeId.value(thing->thingClassId()), values[0]); thing->setStateValue(m_connectedStateTypeId.value(thing->thingClassId()), true); return; } diff --git a/modbuscommander/integrationpluginmodbuscommander.h b/modbuscommander/integrationpluginmodbuscommander.h index c9b4a9d..45b393e 100644 --- a/modbuscommander/integrationpluginmodbuscommander.h +++ b/modbuscommander/integrationpluginmodbuscommander.h @@ -86,7 +86,7 @@ private slots: 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 onReceivedHoldingRegister(uint slaveAddress, uint modbusRegister, const QVector &values); void onReceivedInputRegister(quint32 slaveAddress, quint32 modbusRegister, int value); }; diff --git a/modbuscommander/modbuscommander.pro b/modbuscommander/modbuscommander.pro index 276537d..9c7a28e 100644 --- a/modbuscommander/modbuscommander.pro +++ b/modbuscommander/modbuscommander.pro @@ -8,8 +8,10 @@ QT += \ SOURCES += \ integrationpluginmodbuscommander.cpp \ ../modbus/modbustcpmaster.cpp \ + ../modbus/modbusrtumaster.cpp \ HEADERS += \ integrationpluginmodbuscommander.h \ ../modbus/modbustcpmaster.h \ + ../modbus/modbusrtumaster.h \ diff --git a/mypv/integrationpluginmypv.cpp b/mypv/integrationpluginmypv.cpp index ee46197..65ffad5 100644 --- a/mypv/integrationpluginmypv.cpp +++ b/mypv/integrationpluginmypv.cpp @@ -160,12 +160,13 @@ void IntegrationPluginMyPv::executeAction(ThingActionInfo *info) if (action.actionTypeId() == elwaHeatingPowerActionTypeId) { int heatingPower = action.param(elwaHeatingPowerActionHeatingPowerParamTypeId).value().toInt(); - QUuid requestId = modbusTCPMaster->writeHoldingRegister(0xff, ElwaModbusRegisters::Power, QVector() << heatingPower)); + QUuid requestId = modbusTCPMaster->writeHoldingRegister(0xff, ElwaModbusRegisters::Power, heatingPower); m_asyncActions.insert(requestId, info); } else if (action.actionTypeId() == elwaPowerActionTypeId) { bool power = action.param(elwaHeatingPowerActionHeatingPowerParamTypeId).value().toBool(); if(power) { - QUuid requestId = modbusTCPMaster->writeHoldingRegister(0xff, ElwaModbusRegisters::ManuelStart, QVector() << 1)); + QUuid requestId = modbusTCPMaster->writeHoldingRegister(0xff, ElwaModbusRegisters::ManuelStart, 1); + m_asyncActions.insert(requestId, info); } } else { Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8()); @@ -184,7 +185,7 @@ void IntegrationPluginMyPv::onRefreshTimer(){ void IntegrationPluginMyPv::onConnectionStateChanged(bool status) { - ModbusTCPMaster *modbusTcpMaster = sender(); + ModbusTCPMaster *modbusTcpMaster = static_cast(sender()); Thing *thing = m_modbusTcpMasters.key(modbusTcpMaster); if (!thing) return; @@ -193,25 +194,32 @@ void IntegrationPluginMyPv::onConnectionStateChanged(bool status) void IntegrationPluginMyPv::onWriteRequestExecuted(QUuid requestId, bool success) { - + if (m_asyncActions.contains(requestId)) { + ThingActionInfo *info = m_asyncActions.value(requestId); + if (success) { + info->finish(Thing::ThingErrorNoError); + } else { + info->finish(Thing::ThingErrorHardwareNotAvailable); + } + } } void IntegrationPluginMyPv::onWriteRequestError(QUuid requestId, const QString &error) { Q_UNUSED(requestId) - qCWarning(dcMypv()) << "Error " + qCWarning(dcMypv()) << "Modbus error "<< error; } -void IntegrationPluginMyPv::onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, int value) +void IntegrationPluginMyPv::onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, const QVector &value) { Q_UNUSED(slaveAddress) - ModbusTCPMaster *modbusTcpMaster = sender(); + ModbusTCPMaster *modbusTcpMaster = static_cast(sender()); Thing *thing = m_modbusTcpMasters.key(modbusTcpMaster); if (!thing) return; if(modbusRegister == ElwaModbusRegisters::Status) { - switch (ElwaStatus(value)) { + switch (ElwaStatus(value[0])) { case ElwaStatus::Heating: { thing->setStateValue(elwaStatusStateTypeId, "Heating"); thing->setStateValue(elwaPowerStateTypeId, true);