changed fronius to make use of QModbus

This commit is contained in:
Boernsman 2020-06-17 13:34:05 +02:00
parent 05e71c24f6
commit bdbb239c59
17 changed files with 319 additions and 858 deletions

View File

@ -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<ModbusRTUMaster *>(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<quint16> &values)
{
ModbusRTUMaster *modbus = static_cast<ModbusRTUMaster *>(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<ThingDescriptor> 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);
}
}
}

View File

@ -34,7 +34,7 @@
#include "integrations/integrationplugin.h"
#include "plugintimer.h"
#include "modbusrtumaster.h"
#include "../modbus/modbusrtumaster.h"
#include <QDateTime>
#include <QSerialPortInfo>
@ -71,10 +71,9 @@ private slots:
void onPluginConfigurationChanged(const ParamTypeId &paramTypeId, 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<quint16> &values);
void onWriteRequestFinished(QUuid requestId, bool success);
};

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "modbusrtumaster.h"
#include "extern-plugininfo.h"
#include <QSerialPortInfo>
ModbusRTUMaster::ModbusRTUMaster(QString serialPort, 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<QModbusReply *>(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<int>(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<QModbusReply *>(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<uint16_t>(value));
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [requestId, this] {
QModbusReply *reply = qobject_cast<QModbusReply *>(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<int>(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<uint16_t>(value));
if (QModbusReply *reply = m_modbusRtuSerialMaster->sendWriteRequest(request, slaveAddress)) {
if (!reply->isFinished()) {
connect(reply, &QModbusReply::finished, this, [requestId, this] {
QModbusReply *reply = qobject_cast<QModbusReply *>(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<int>(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);
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under
* contact@nymea.io or see our FAQ/Licensing Information on
* https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef MODBUSRTUMASTER_H
#define MODBUSRTUMASTER_H
#include <QObject>
#include <QtSerialBus>
#include <QSerialPort>
#include <QTimer>
#include <QUuid>
class ModbusRTUMaster : public QObject
{
Q_OBJECT
public:
explicit ModbusRTUMaster(QString serialPort, 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

View File

@ -12,7 +12,7 @@ SOURCES += \
froniusstorage.cpp \
froniusmeter.cpp \
sunspecthing.cpp \
../modbus/modbustcpmaster.h \
../modbus/modbustcpmaster.cpp \
HEADERS += \
integrationpluginfronius.h \

View File

@ -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());

View File

@ -38,6 +38,7 @@
#include <QHash>
#include <QNetworkReply>
#include <QTimer>
#include <QUuid>
class PluginTimer;
@ -67,7 +68,7 @@ private:
QHash<FroniusStorage *, Thing *> m_froniusStorages;
QHash<SunspecThing *, Thing *> m_sunspecThings;
QHash<ThingActionInfo *, Thing *> m_asyncActions;
QHash<QUuid, ThingActionInfo *> m_asyncActions;
void updateThingStates(Thing *thing);

View File

@ -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<StorageState>(data[11]));
m_thing->setStateValue(sunspecStorageStorageStateStateTypeId, storageStateToString(static_cast<StorageState>(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<quint16> &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<quint16> &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<StorageState>(data[11]));
m_thing->setStateValue(sunspecStorageStorageStateStateTypeId, storageStateToString(static_cast<StorageState>(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;
};
}

View File

@ -37,9 +37,9 @@
#include <QHostAddress>
#include <QByteArray>
#include <QBitArray>
#include <QUuid>
#include <modbus/modbus.h>
#include <modbus/modbus-tcp.h>
#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<quint16> &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<quint16> &values);
};
#endif // SUNSPECTHING_H

View File

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

View File

@ -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<quint16> &values);
void receivedInputRegister(uint slaveAddress, uint modbusRegister, uint value);
};

View File

@ -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<quint16> &values)
QUuid ModbusTCPMaster::writeHoldingRegisters(uint slaveAddress, uint registerAddress, const QVector<quint16> &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<quint16>() << value);
}
void ModbusTCPMaster::onModbusErrorOccurred(QModbusDevice::Error error)
{

View File

@ -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<quint16> &values);
QUuid writeHoldingRegister(uint slaveAddress, uint registerAddress, quint16 value);
QUuid writeHoldingRegisters(uint slaveAddress, uint registerAddress, const QVector<quint16> &values);
QHostAddress hostAddress();
uint port();

View File

@ -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<quint16> &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;
}

View File

@ -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<quint16> &values);
void onReceivedInputRegister(quint32 slaveAddress, quint32 modbusRegister, int value);
};

View File

@ -8,8 +8,10 @@ QT += \
SOURCES += \
integrationpluginmodbuscommander.cpp \
../modbus/modbustcpmaster.cpp \
../modbus/modbusrtumaster.cpp \
HEADERS += \
integrationpluginmodbuscommander.h \
../modbus/modbustcpmaster.h \
../modbus/modbusrtumaster.h \

View File

@ -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<quint16>() << 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<quint16>() << 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<ModbusTCPMaster *>(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<quint16> &value)
{
Q_UNUSED(slaveAddress)
ModbusTCPMaster *modbusTcpMaster = sender();
ModbusTCPMaster *modbusTcpMaster = static_cast<ModbusTCPMaster *>(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);