diff --git a/debian/changelog b/debian/changelog index e975cce..a07aa97 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,11 @@ +nymea-plugins-modbus (0.30.0+202201281421~c194a9f~buster+202201281425~2c664e6~buster) UNRELEASED; urgency=medium + + + [ ] + * New Package build + + -- Fri, 28 Jan 2022 14:25:13 +0000 + nymea-plugins-modbus (0.30.0) xenial; urgency=medium [ Michael Zanetti ] diff --git a/huawei/huawei-registers.json b/huawei/huawei-registers.json index c504fcb..0e559d1 100644 --- a/huawei/huawei-registers.json +++ b/huawei/huawei-registers.json @@ -118,24 +118,36 @@ "value": 40960 } ] + }, + { + "name": "BatteryDeviceStatus", + "values": [ + { + "key": "Offline", + "value": 0 + }, + { + "key": "Standby", + "value": 1 + }, + { + "key": "Running", + "value": 1 + }, + { + "key": "Fault", + "value": 1 + }, + { + "key": "SleepMode", + "value": 1 + } + ] } ], "blocks": [ ], "registers": [ - { - "id": "inverterDCPower", - "address": 32064, - "size": 2, - "type": "int32", - "registerType": "holdingRegister", - "readSchedule": "update", - "description": "Inverter DC power", - "unit": "kW", - "staticScaleFactor": -3, - "defaultValue": "0", - "access": "RO" - }, { "id": "inverterActivePower", "address": 32080, @@ -149,6 +161,31 @@ "defaultValue": "0", "access": "RO" }, + { + "id": "inverterDeviceStatus", + "address": 32089, + "size": 1, + "type": "uint16", + "registerType": "holdingRegister", + "readSchedule": "update", + "description": "Inverter device status", + "enum": "InverterDeviceStatus", + "defaultValue": "InverterDeviceStatusStandbyInitializing", + "access": "RO" + }, + { + "id": "inverterEnergyProduced", + "address": 32106, + "size": 2, + "type": "uint32", + "registerType": "holdingRegister", + "readSchedule": "update", + "description": "Inverter energy produced", + "unit": "kWh", + "staticScaleFactor": -2, + "defaultValue": "0", + "access": "RO" + }, { "id": "powerMeterActivePower", "address": 37113, @@ -161,6 +198,18 @@ "defaultValue": "0", "access": "RO" }, + { + "id": "lunaBattery1Status", + "address": 37000, + "size": 1, + "type": "uint16", + "registerType": "holdingRegister", + "readSchedule": "update", + "description": "Luna 2000 Battery 1 status", + "enum": "BatteryDeviceStatus", + "defaultValue": "BatteryDeviceStatusOffline", + "access": "RO" + }, { "id": "lunaBattery1Power", "address": 37001, @@ -186,6 +235,18 @@ "defaultValue": "0", "access": "RO" }, + { + "id": "lunaBattery2Status", + "address": 37741, + "size": 1, + "type": "uint16", + "registerType": "holdingRegister", + "readSchedule": "update", + "description": "Luna 2000 Battery 2 status", + "enum": "BatteryDeviceStatus", + "defaultValue": "BatteryDeviceStatusOffline", + "access": "RO" + }, { "id": "lunaBattery2Power", "address": 37743, diff --git a/huawei/huaweifusionsolar.cpp b/huawei/huaweifusionsolar.cpp index 23b1be0..b32867f 100644 --- a/huawei/huaweifusionsolar.cpp +++ b/huawei/huaweifusionsolar.cpp @@ -1,5 +1,8 @@ #include "huaweifusionsolar.h" #include "extern-plugininfo.h" +#include "loggingcategories.h" + +NYMEA_LOGGING_CATEGORY(dcHuaweiFusionSolar, "HuaweiFusionSolar") HuaweiFusionSolar::HuaweiFusionSolar(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent) : HuaweiModbusTcpConnection(hostAddress, port, slaveId, parent) @@ -20,16 +23,24 @@ void HuaweiFusionSolar::update() return; // Add the requests - m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterInverterDCPower); m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterInverterActivePower); - m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery1Power); - m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery1Soc); + m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterInverterDeviceStatus); + m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterInverterEnergyProduced); + m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery1Status); + if (m_battery1Available) { + m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery1Power); + m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery1Soc); + } m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterPowerMeterActivePower); - m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery2Soc); - m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery2Power); + m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery2Status); + if (m_battery2Available) { + m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery2Power); + m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery2Soc); + } // Note: since huawei can only process one request at the time, we need to queue the requests + m_currentRegisterRequest = -1; readNextRegister(); } @@ -39,11 +50,16 @@ void HuaweiFusionSolar::readNextRegister() if (m_currentRegisterRequest >= 0) return; + // Check if there is still a register to read + if (m_registersQueue.isEmpty()) + return; + + m_currentRegisterRequest = m_registersQueue.dequeue(); + switch (m_currentRegisterRequest) { - case HuaweiModbusTcpConnection::RegisterInverterDCPower: - // Update registers from Inverter DC power - qCDebug(dcHuawei()) << "--> Read \"Inverter DC power\" register:" << 32064 << "size:" << 2; - QModbusReply *reply = readInverterDCPower(); + case HuaweiModbusTcpConnection::RegisterInverterActivePower: { + qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Inverter active power\" register:" << 32080 << "size:" << 2; + QModbusReply *reply = readInverterActivePower(); if (reply) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); @@ -51,27 +67,364 @@ void HuaweiFusionSolar::readNextRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); const QVector values = unit.values(); - qCDebug(dcHuawei()) << "<-- Response from \"Inverter DC power\" register" << 32064 << "size:" << 2 << values; - float receivedInverterDCPower = ModbusDataUtils::convertToInt32(values, ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -3); - if (m_inverterDCPower != receivedInverterDCPower) { - m_inverterDCPower = receivedInverterDCPower; - emit inverterDCPowerChanged(m_inverterDCPower); + qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter active power\" register" << 32080 << "size:" << 2 << values; + float receivedInverterActivePower = ModbusDataUtils::convertToInt32(values, ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -3); + if (m_inverterActivePower != receivedInverterActivePower) { + m_inverterActivePower = receivedInverterActivePower; + emit inverterActivePowerChanged(m_inverterActivePower); } } + finishRequest(); }); connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ - qCWarning(dcHuawei()) << "Modbus reply error occurred while updating \"Inverter DC power\" registers from" << hostAddress().toString() << error << reply->errorString(); + qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter active power\" registers from" << hostAddress().toString() << error << reply->errorString(); emit reply->finished(); // To make sure it will be deleted }); } else { delete reply; // Broadcast reply returns immediatly + finishRequest(); } } else { - qCWarning(dcHuawei()) << "Error occurred while reading \"Inverter DC power\" registers from" << hostAddress().toString() << errorString(); + qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Inverter active power\" registers from" << hostAddress().toString() << errorString(); + finishRequest(); } - - break; } + case HuaweiModbusTcpConnection::RegisterInverterDeviceStatus: { + // Update registers from Inverter device status + qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Inverter device status\" register:" << 32089 << "size:" << 1; + QModbusReply *reply = readInverterDeviceStatus(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter device status\" register" << 32089 << "size:" << 1 << values; + InverterDeviceStatus receivedInverterDeviceStatus = static_cast(ModbusDataUtils::convertToUInt16(values)); + qCDebug(dcHuaweiFusionSolar()) << "Inverter status" << receivedInverterDeviceStatus; + if (m_inverterDeviceStatus != receivedInverterDeviceStatus) { + m_inverterDeviceStatus = receivedInverterDeviceStatus; + emit inverterDeviceStatusChanged(m_inverterDeviceStatus); + } + } + finishRequest(); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter device status\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + finishRequest(); + } + } else { + qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Inverter device status\" registers from" << hostAddress().toString() << errorString(); + finishRequest(); + } + break; + } + case HuaweiModbusTcpConnection::RegisterInverterEnergyProduced: { + // Update registers from Inverter energy produced + qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Inverter energy produced\" register:" << 32106 << "size:" << 2; + QModbusReply *reply = readInverterEnergyProduced(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter energy produced\" register" << 32106 << "size:" << 2 << values; + float receivedInverterEnergyProduced = ModbusDataUtils::convertToUInt32(values, ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -2); + if (m_inverterEnergyProduced != receivedInverterEnergyProduced) { + m_inverterEnergyProduced = receivedInverterEnergyProduced; + emit inverterEnergyProducedChanged(m_inverterEnergyProduced); + } + } + finishRequest(); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter energy produced\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + finishRequest(); + } + } else { + qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Inverter energy produced\" registers from" << hostAddress().toString() << errorString(); + finishRequest(); + } + break; + } + case HuaweiModbusTcpConnection::RegisterLunaBattery1Status: { + // Update registers from Luna 2000 Battery 1 status + qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Luna 2000 Battery 1 status\" register:" << 37000 << "size:" << 1; + QModbusReply *reply = readLunaBattery1Status(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 status\" register" << 37000 << "size:" << 1 << values; + BatteryDeviceStatus receivedLunaBattery1Status = static_cast(ModbusDataUtils::convertToUInt16(values)); + qCDebug(dcHuaweiFusionSolar()) << "Battery 1 status" << receivedLunaBattery1Status; + if (receivedLunaBattery1Status == BatteryDeviceStatusOffline) { + m_battery1Available = false; + } else { + m_battery1Available = true; + } + + if (m_lunaBattery1Status != receivedLunaBattery1Status) { + m_lunaBattery1Status = receivedLunaBattery1Status; + emit lunaBattery1StatusChanged(m_lunaBattery1Status); + } + } + finishRequest(); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 1 status\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + finishRequest(); + } + } else { + qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 1 status\" registers from" << hostAddress().toString() << errorString(); + finishRequest(); + } + break; + } + case HuaweiModbusTcpConnection::RegisterLunaBattery1Power: { + // Update registers from Luna 2000 Battery 1 power + qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Luna 2000 Battery 1 power\" register:" << 37001 << "size:" << 2; + QModbusReply *reply = readLunaBattery1Power(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 power\" register" << 37001 << "size:" << 2 << values; + qint32 receivedLunaBattery1Power = ModbusDataUtils::convertToInt32(values, ModbusDataUtils::ByteOrderBigEndian); + if (m_lunaBattery1Power != receivedLunaBattery1Power) { + m_lunaBattery1Power = receivedLunaBattery1Power; + emit lunaBattery1PowerChanged(m_lunaBattery1Power); + } + } + finishRequest(); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 1 power\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + finishRequest(); + } + } else { + qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 1 power\" registers from" << hostAddress().toString() << errorString(); + finishRequest(); + } + break; + } + case HuaweiModbusTcpConnection::RegisterLunaBattery1Soc: { + // Update registers from Luna 2000 Battery 1 state of charge + qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Luna 2000 Battery 1 state of charge\" register:" << 37004 << "size:" << 1; + QModbusReply *reply = readLunaBattery1Soc(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 state of charge\" register" << 37004 << "size:" << 1 << values; + float receivedLunaBattery1Soc = ModbusDataUtils::convertToUInt16(values) * 1.0 * pow(10, -1); + if (m_lunaBattery1Soc != receivedLunaBattery1Soc) { + m_lunaBattery1Soc = receivedLunaBattery1Soc; + emit lunaBattery1SocChanged(m_lunaBattery1Soc); + } + } + finishRequest(); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 1 state of charge\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + finishRequest(); + } + } else { + qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 1 state of charge\" registers from" << hostAddress().toString() << errorString(); + finishRequest(); + } + break; + } + case HuaweiModbusTcpConnection::RegisterPowerMeterActivePower: { + // Update registers from Power meter active power + qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Power meter active power\" register:" << 37113 << "size:" << 2; + QModbusReply *reply = readPowerMeterActivePower(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Power meter active power\" register" << 37113 << "size:" << 2 << values; + qint32 receivedPowerMeterActivePower = ModbusDataUtils::convertToInt32(values, ModbusDataUtils::ByteOrderBigEndian); + if (m_powerMeterActivePower != receivedPowerMeterActivePower) { + m_powerMeterActivePower = receivedPowerMeterActivePower; + emit powerMeterActivePowerChanged(m_powerMeterActivePower); + } + } + finishRequest(); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Power meter active power\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + finishRequest(); + } + } else { + qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Power meter active power\" registers from" << hostAddress().toString() << errorString(); + finishRequest(); + } + break; + } + case HuaweiModbusTcpConnection::RegisterLunaBattery2Status: { + // Update registers from Luna 2000 Battery 2 status + qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Luna 2000 Battery 2 status\" register:" << 37741 << "size:" << 1; + QModbusReply *reply = readLunaBattery2Status(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 status\" register" << 37741 << "size:" << 1 << values; + BatteryDeviceStatus receivedLunaBattery2Status = static_cast(ModbusDataUtils::convertToUInt16(values)); + qCDebug(dcHuaweiFusionSolar()) << "Battery 2 status" << receivedLunaBattery2Status; + if (receivedLunaBattery2Status == BatteryDeviceStatusOffline) { + m_battery2Available = false; + } else { + m_battery2Available = true; + } + if (m_lunaBattery2Status != receivedLunaBattery2Status) { + m_lunaBattery2Status = receivedLunaBattery2Status; + emit lunaBattery2StatusChanged(m_lunaBattery2Status); + } + } + finishRequest(); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 2 status\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + finishRequest(); + } + } else { + qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 2 status\" registers from" << hostAddress().toString() << errorString(); + finishRequest(); + } + break; + } + case HuaweiModbusTcpConnection::RegisterLunaBattery2Power: { + // Update registers from Luna 2000 Battery 2 power + qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Luna 2000 Battery 2 power\" register:" << 37743 << "size:" << 2; + QModbusReply *reply = readLunaBattery2Power(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 power\" register" << 37743 << "size:" << 2 << values; + qint32 receivedLunaBattery2Power = ModbusDataUtils::convertToInt32(values, ModbusDataUtils::ByteOrderBigEndian); + if (m_lunaBattery2Power != receivedLunaBattery2Power) { + m_lunaBattery2Power = receivedLunaBattery2Power; + emit lunaBattery2PowerChanged(m_lunaBattery2Power); + } + } + finishRequest(); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 2 power\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + finishRequest(); + } + } else { + qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 2 power\" registers from" << hostAddress().toString() << errorString(); + finishRequest(); + } + break; + } + case HuaweiModbusTcpConnection::RegisterLunaBattery2Soc: { + // Update registers from Luna 2000 Battery 2 state of charge + qCDebug(dcHuaweiFusionSolar()) << "--> Read \"Luna 2000 Battery 2 state of charge\" register:" << 37738 << "size:" << 1; + QModbusReply *reply = readLunaBattery2Soc(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 state of charge\" register" << 37738 << "size:" << 1 << values; + float receivedLunaBattery2Soc = ModbusDataUtils::convertToUInt16(values) * 1.0 * pow(10, -1); + if (m_lunaBattery2Soc != receivedLunaBattery2Soc) { + m_lunaBattery2Soc = receivedLunaBattery2Soc; + emit lunaBattery2SocChanged(m_lunaBattery2Soc); + } + } + finishRequest(); + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 2 state of charge\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + finishRequest(); + } + } else { + qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 2 state of charge\" registers from" << hostAddress().toString() << errorString(); + finishRequest(); + } + break; + } + } +} + +void HuaweiFusionSolar::finishRequest() +{ + m_currentRegisterRequest = -1; + QTimer::singleShot(1000, this, &HuaweiFusionSolar::readNextRegister); } diff --git a/huawei/huaweifusionsolar.h b/huawei/huaweifusionsolar.h index b0e16be..98e38d4 100644 --- a/huawei/huaweifusionsolar.h +++ b/huawei/huaweifusionsolar.h @@ -15,12 +15,19 @@ public: virtual void initialize() override; virtual void update() override; + private: QQueue m_registersQueue; int m_currentRegisterRequest = -1; + void finishRequest(); + bool m_battery1Available = true; + bool m_battery2Available = true; + +private slots: void readNextRegister(); + }; #endif // HUAWEIFUSIONSOLAR_H diff --git a/huawei/huaweimodbustcpconnection.cpp b/huawei/huaweimodbustcpconnection.cpp index fb2b6c5..cca8e02 100644 --- a/huawei/huaweimodbustcpconnection.cpp +++ b/huawei/huaweimodbustcpconnection.cpp @@ -41,21 +41,31 @@ HuaweiModbusTcpConnection::HuaweiModbusTcpConnection(const QHostAddress &hostAdd } -float HuaweiModbusTcpConnection::inverterDCPower() const -{ - return m_inverterDCPower; -} - float HuaweiModbusTcpConnection::inverterActivePower() const { return m_inverterActivePower; } +HuaweiModbusTcpConnection::InverterDeviceStatus HuaweiModbusTcpConnection::inverterDeviceStatus() const +{ + return m_inverterDeviceStatus; +} + +float HuaweiModbusTcpConnection::inverterEnergyProduced() const +{ + return m_inverterEnergyProduced; +} + qint32 HuaweiModbusTcpConnection::powerMeterActivePower() const { return m_powerMeterActivePower; } +HuaweiModbusTcpConnection::BatteryDeviceStatus HuaweiModbusTcpConnection::lunaBattery1Status() const +{ + return m_lunaBattery1Status; +} + qint32 HuaweiModbusTcpConnection::lunaBattery1Power() const { return m_lunaBattery1Power; @@ -66,6 +76,11 @@ float HuaweiModbusTcpConnection::lunaBattery1Soc() const return m_lunaBattery1Soc; } +HuaweiModbusTcpConnection::BatteryDeviceStatus HuaweiModbusTcpConnection::lunaBattery2Status() const +{ + return m_lunaBattery2Status; +} + qint32 HuaweiModbusTcpConnection::lunaBattery2Power() const { return m_lunaBattery2Power; @@ -84,48 +99,18 @@ void HuaweiModbusTcpConnection::initialize() void HuaweiModbusTcpConnection::update() { - updateInverterDCPower(); updateInverterActivePower(); + updateInverterDeviceStatus(); + updateInverterEnergyProduced(); updatePowerMeterActivePower(); + updateLunaBattery1Status(); updateLunaBattery1Power(); updateLunaBattery1Soc(); + updateLunaBattery2Status(); updateLunaBattery2Power(); updateLunaBattery2Soc(); } -void HuaweiModbusTcpConnection::updateInverterDCPower() -{ - // Update registers from Inverter DC power - qCDebug(dcHuaweiModbusTcpConnection()) << "--> Read \"Inverter DC power\" register:" << 32064 << "size:" << 2; - QModbusReply *reply = readInverterDCPower(); - if (reply) { - if (!reply->isFinished()) { - connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); - connect(reply, &QModbusReply::finished, this, [this, reply](){ - if (reply->error() == QModbusDevice::NoError) { - const QModbusDataUnit unit = reply->result(); - const QVector values = unit.values(); - qCDebug(dcHuaweiModbusTcpConnection()) << "<-- Response from \"Inverter DC power\" register" << 32064 << "size:" << 2 << values; - float receivedInverterDCPower = ModbusDataUtils::convertToInt32(values, ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -3); - if (m_inverterDCPower != receivedInverterDCPower) { - m_inverterDCPower = receivedInverterDCPower; - emit inverterDCPowerChanged(m_inverterDCPower); - } - } - }); - - connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ - qCWarning(dcHuaweiModbusTcpConnection()) << "Modbus reply error occurred while updating \"Inverter DC power\" registers from" << hostAddress().toString() << error << reply->errorString(); - emit reply->finished(); // To make sure it will be deleted - }); - } else { - delete reply; // Broadcast reply returns immediatly - } - } else { - qCWarning(dcHuaweiModbusTcpConnection()) << "Error occurred while reading \"Inverter DC power\" registers from" << hostAddress().toString() << errorString(); - } -} - void HuaweiModbusTcpConnection::updateInverterActivePower() { // Update registers from Inverter active power @@ -159,6 +144,72 @@ void HuaweiModbusTcpConnection::updateInverterActivePower() } } +void HuaweiModbusTcpConnection::updateInverterDeviceStatus() +{ + // Update registers from Inverter device status + qCDebug(dcHuaweiModbusTcpConnection()) << "--> Read \"Inverter device status\" register:" << 32089 << "size:" << 1; + QModbusReply *reply = readInverterDeviceStatus(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiModbusTcpConnection()) << "<-- Response from \"Inverter device status\" register" << 32089 << "size:" << 1 << values; + InverterDeviceStatus receivedInverterDeviceStatus = static_cast(ModbusDataUtils::convertToUInt16(values)); + if (m_inverterDeviceStatus != receivedInverterDeviceStatus) { + m_inverterDeviceStatus = receivedInverterDeviceStatus; + emit inverterDeviceStatusChanged(m_inverterDeviceStatus); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiModbusTcpConnection()) << "Modbus reply error occurred while updating \"Inverter device status\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcHuaweiModbusTcpConnection()) << "Error occurred while reading \"Inverter device status\" registers from" << hostAddress().toString() << errorString(); + } +} + +void HuaweiModbusTcpConnection::updateInverterEnergyProduced() +{ + // Update registers from Inverter energy produced + qCDebug(dcHuaweiModbusTcpConnection()) << "--> Read \"Inverter energy produced\" register:" << 32106 << "size:" << 2; + QModbusReply *reply = readInverterEnergyProduced(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiModbusTcpConnection()) << "<-- Response from \"Inverter energy produced\" register" << 32106 << "size:" << 2 << values; + float receivedInverterEnergyProduced = ModbusDataUtils::convertToUInt32(values, ModbusDataUtils::ByteOrderBigEndian) * 1.0 * pow(10, -2); + if (m_inverterEnergyProduced != receivedInverterEnergyProduced) { + m_inverterEnergyProduced = receivedInverterEnergyProduced; + emit inverterEnergyProducedChanged(m_inverterEnergyProduced); + } + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiModbusTcpConnection()) << "Modbus reply error occurred while updating \"Inverter energy produced\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcHuaweiModbusTcpConnection()) << "Error occurred while reading \"Inverter energy produced\" registers from" << hostAddress().toString() << errorString(); + } +} + void HuaweiModbusTcpConnection::updatePowerMeterActivePower() { // Update registers from Power meter active power @@ -192,6 +243,37 @@ void HuaweiModbusTcpConnection::updatePowerMeterActivePower() } } +void HuaweiModbusTcpConnection::updateLunaBattery1Status() +{ + // Update registers from Luna 2000 Battery 1 status + qCDebug(dcHuaweiModbusTcpConnection()) << "--> Read \"Luna 2000 Battery 1 status\" register:" << 37000 << "size:" << 1; + QModbusReply *reply = readLunaBattery1Status(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiModbusTcpConnection()) << "<-- Response from \"Luna 2000 Battery 1 status\" register" << 37000 << "size:" << 1 << values; + BatteryDeviceStatus receivedLunaBattery1Status = static_cast(ModbusDataUtils::convertToUInt16(values)); + m_lunaBattery1Status = receivedLunaBattery1Status; + emit lunaBattery1StatusChanged(m_lunaBattery1Status); + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiModbusTcpConnection()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 1 status\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcHuaweiModbusTcpConnection()) << "Error occurred while reading \"Luna 2000 Battery 1 status\" registers from" << hostAddress().toString() << errorString(); + } +} + void HuaweiModbusTcpConnection::updateLunaBattery1Power() { // Update registers from Luna 2000 Battery 1 power @@ -258,6 +340,37 @@ void HuaweiModbusTcpConnection::updateLunaBattery1Soc() } } +void HuaweiModbusTcpConnection::updateLunaBattery2Status() +{ + // Update registers from Luna 2000 Battery 2 status + qCDebug(dcHuaweiModbusTcpConnection()) << "--> Read \"Luna 2000 Battery 2 status\" register:" << 37741 << "size:" << 1; + QModbusReply *reply = readLunaBattery2Status(); + if (reply) { + if (!reply->isFinished()) { + connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); + connect(reply, &QModbusReply::finished, this, [this, reply](){ + if (reply->error() == QModbusDevice::NoError) { + const QModbusDataUnit unit = reply->result(); + const QVector values = unit.values(); + qCDebug(dcHuaweiModbusTcpConnection()) << "<-- Response from \"Luna 2000 Battery 2 status\" register" << 37741 << "size:" << 1 << values; + BatteryDeviceStatus receivedLunaBattery2Status = static_cast(ModbusDataUtils::convertToUInt16(values)); + m_lunaBattery2Status = receivedLunaBattery2Status; + emit lunaBattery2StatusChanged(m_lunaBattery2Status); + } + }); + + connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){ + qCWarning(dcHuaweiModbusTcpConnection()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 2 status\" registers from" << hostAddress().toString() << error << reply->errorString(); + emit reply->finished(); // To make sure it will be deleted + }); + } else { + delete reply; // Broadcast reply returns immediatly + } + } else { + qCWarning(dcHuaweiModbusTcpConnection()) << "Error occurred while reading \"Luna 2000 Battery 2 status\" registers from" << hostAddress().toString() << errorString(); + } +} + void HuaweiModbusTcpConnection::updateLunaBattery2Power() { // Update registers from Luna 2000 Battery 2 power @@ -324,24 +437,36 @@ void HuaweiModbusTcpConnection::updateLunaBattery2Soc() } } -QModbusReply *HuaweiModbusTcpConnection::readInverterDCPower() -{ - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 32064, 2); - return sendReadRequest(request, m_slaveId); -} - QModbusReply *HuaweiModbusTcpConnection::readInverterActivePower() { QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 32080, 2); return sendReadRequest(request, m_slaveId); } +QModbusReply *HuaweiModbusTcpConnection::readInverterDeviceStatus() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 32089, 1); + return sendReadRequest(request, m_slaveId); +} + +QModbusReply *HuaweiModbusTcpConnection::readInverterEnergyProduced() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 32106, 2); + return sendReadRequest(request, m_slaveId); +} + QModbusReply *HuaweiModbusTcpConnection::readPowerMeterActivePower() { QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 37113, 2); return sendReadRequest(request, m_slaveId); } +QModbusReply *HuaweiModbusTcpConnection::readLunaBattery1Status() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 37000, 1); + return sendReadRequest(request, m_slaveId); +} + QModbusReply *HuaweiModbusTcpConnection::readLunaBattery1Power() { QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 37001, 2); @@ -354,6 +479,12 @@ QModbusReply *HuaweiModbusTcpConnection::readLunaBattery1Soc() return sendReadRequest(request, m_slaveId); } +QModbusReply *HuaweiModbusTcpConnection::readLunaBattery2Status() +{ + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 37741, 1); + return sendReadRequest(request, m_slaveId); +} + QModbusReply *HuaweiModbusTcpConnection::readLunaBattery2Power() { QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 37743, 2); @@ -377,11 +508,14 @@ void HuaweiModbusTcpConnection::verifyInitFinished() QDebug operator<<(QDebug debug, HuaweiModbusTcpConnection *huaweiModbusTcpConnection) { debug.nospace().noquote() << "HuaweiModbusTcpConnection(" << huaweiModbusTcpConnection->hostAddress().toString() << ":" << huaweiModbusTcpConnection->port() << ")" << "\n"; - debug.nospace().noquote() << " - Inverter DC power:" << huaweiModbusTcpConnection->inverterDCPower() << " [kW]" << "\n"; debug.nospace().noquote() << " - Inverter active power:" << huaweiModbusTcpConnection->inverterActivePower() << " [kW]" << "\n"; + debug.nospace().noquote() << " - Inverter device status:" << huaweiModbusTcpConnection->inverterDeviceStatus() << "\n"; + debug.nospace().noquote() << " - Inverter energy produced:" << huaweiModbusTcpConnection->inverterEnergyProduced() << " [kWh]" << "\n"; debug.nospace().noquote() << " - Power meter active power:" << huaweiModbusTcpConnection->powerMeterActivePower() << " [W]" << "\n"; + debug.nospace().noquote() << " - Luna 2000 Battery 1 status:" << huaweiModbusTcpConnection->lunaBattery1Status() << "\n"; debug.nospace().noquote() << " - Luna 2000 Battery 1 power:" << huaweiModbusTcpConnection->lunaBattery1Power() << " [W]" << "\n"; debug.nospace().noquote() << " - Luna 2000 Battery 1 state of charge:" << huaweiModbusTcpConnection->lunaBattery1Soc() << " [%]" << "\n"; + debug.nospace().noquote() << " - Luna 2000 Battery 2 status:" << huaweiModbusTcpConnection->lunaBattery2Status() << "\n"; debug.nospace().noquote() << " - Luna 2000 Battery 2 power:" << huaweiModbusTcpConnection->lunaBattery2Power() << " [W]" << "\n"; debug.nospace().noquote() << " - Luna 2000 Battery 2 state of charge:" << huaweiModbusTcpConnection->lunaBattery2Soc() << " [%]" << "\n"; return debug.quote().space(); diff --git a/huawei/huaweimodbustcpconnection.h b/huawei/huaweimodbustcpconnection.h index 8a21117..40ce1b3 100644 --- a/huawei/huaweimodbustcpconnection.h +++ b/huawei/huaweimodbustcpconnection.h @@ -41,12 +41,15 @@ class HuaweiModbusTcpConnection : public ModbusTCPMaster Q_OBJECT public: enum Registers { - RegisterInverterDCPower = 32064, RegisterInverterActivePower = 32080, + RegisterInverterDeviceStatus = 32089, + RegisterInverterEnergyProduced = 32106, + RegisterLunaBattery1Status = 37000, RegisterLunaBattery1Power = 37001, RegisterLunaBattery1Soc = 37004, RegisterPowerMeterActivePower = 37113, RegisterLunaBattery2Soc = 37738, + RegisterLunaBattery2Status = 37741, RegisterLunaBattery2Power = 37743 }; Q_ENUM(Registers) @@ -83,24 +86,42 @@ public: }; Q_ENUM(InverterDeviceStatus) + enum BatteryDeviceStatus { + BatteryDeviceStatusOffline = 0, + BatteryDeviceStatusStandby = 1, + BatteryDeviceStatusRunning = 1, + BatteryDeviceStatusFault = 1, + BatteryDeviceStatusSleepMode = 1 + }; + Q_ENUM(BatteryDeviceStatus) + explicit HuaweiModbusTcpConnection(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr); ~HuaweiModbusTcpConnection() = default; - /* Inverter DC power [kW] - Address: 32064, Size: 2 */ - float inverterDCPower() const; - /* Inverter active power [kW] - Address: 32080, Size: 2 */ float inverterActivePower() const; + /* Inverter device status - Address: 32089, Size: 1 */ + InverterDeviceStatus inverterDeviceStatus() const; + + /* Inverter energy produced [kWh] - Address: 32106, Size: 2 */ + float inverterEnergyProduced() const; + /* Power meter active power [W] - Address: 37113, Size: 2 */ qint32 powerMeterActivePower() const; + /* Luna 2000 Battery 1 status - Address: 37000, Size: 1 */ + BatteryDeviceStatus lunaBattery1Status() const; + /* Luna 2000 Battery 1 power [W] - Address: 37001, Size: 2 */ qint32 lunaBattery1Power() const; /* Luna 2000 Battery 1 state of charge [%] - Address: 37004, Size: 1 */ float lunaBattery1Soc() const; + /* Luna 2000 Battery 2 status - Address: 37741, Size: 1 */ + BatteryDeviceStatus lunaBattery2Status() const; + /* Luna 2000 Battery 2 power [W] - Address: 37743, Size: 2 */ qint32 lunaBattery2Power() const; @@ -111,46 +132,58 @@ public: virtual void initialize(); virtual void update(); - void updateInverterDCPower(); void updateInverterActivePower(); + void updateInverterDeviceStatus(); + void updateInverterEnergyProduced(); void updatePowerMeterActivePower(); + void updateLunaBattery1Status(); void updateLunaBattery1Power(); void updateLunaBattery1Soc(); + void updateLunaBattery2Status(); void updateLunaBattery2Power(); void updateLunaBattery2Soc(); signals: void initializationFinished(); - void inverterDCPowerChanged(float inverterDCPower); void inverterActivePowerChanged(float inverterActivePower); + void inverterDeviceStatusChanged(InverterDeviceStatus inverterDeviceStatus); + void inverterEnergyProducedChanged(float inverterEnergyProduced); void powerMeterActivePowerChanged(qint32 powerMeterActivePower); + void lunaBattery1StatusChanged(BatteryDeviceStatus lunaBattery1Status); void lunaBattery1PowerChanged(qint32 lunaBattery1Power); void lunaBattery1SocChanged(float lunaBattery1Soc); + void lunaBattery2StatusChanged(BatteryDeviceStatus lunaBattery2Status); void lunaBattery2PowerChanged(qint32 lunaBattery2Power); void lunaBattery2SocChanged(float lunaBattery2Soc); protected: - QModbusReply *readInverterDCPower(); QModbusReply *readInverterActivePower(); + QModbusReply *readInverterDeviceStatus(); + QModbusReply *readInverterEnergyProduced(); QModbusReply *readPowerMeterActivePower(); + QModbusReply *readLunaBattery1Status(); QModbusReply *readLunaBattery1Power(); QModbusReply *readLunaBattery1Soc(); + QModbusReply *readLunaBattery2Status(); QModbusReply *readLunaBattery2Power(); QModbusReply *readLunaBattery2Soc(); + float m_inverterActivePower = 0; + InverterDeviceStatus m_inverterDeviceStatus = InverterDeviceStatusStandbyInitializing; + float m_inverterEnergyProduced = 0; + qint32 m_powerMeterActivePower = 0; + BatteryDeviceStatus m_lunaBattery1Status = BatteryDeviceStatusOffline; + qint32 m_lunaBattery1Power = 0; + float m_lunaBattery1Soc = 0; + BatteryDeviceStatus m_lunaBattery2Status = BatteryDeviceStatusOffline; + qint32 m_lunaBattery2Power = 0; + float m_lunaBattery2Soc = 0; + private: quint16 m_slaveId = 1; QVector m_pendingInitReplies; - float m_inverterDCPower = 0; - float m_inverterActivePower = 0; - qint32 m_powerMeterActivePower = 0; - qint32 m_lunaBattery1Power = 0; - float m_lunaBattery1Soc = 0; - qint32 m_lunaBattery2Power = 0; - float m_lunaBattery2Soc = 0; - void verifyInitFinished(); diff --git a/huawei/integrationpluginhuawei.cpp b/huawei/integrationpluginhuawei.cpp index a362181..5fe8ba7 100644 --- a/huawei/integrationpluginhuawei.cpp +++ b/huawei/integrationpluginhuawei.cpp @@ -59,7 +59,7 @@ void IntegrationPluginHuawei::discoverThings(ThingDiscoveryInfo *info) QString title; if (networkDeviceInfo.hostName().isEmpty()) { - title = networkDeviceInfo.address().toString(); + title = "Huawei FusionSolar"; } else { title = networkDeviceInfo.hostName() + " (" + networkDeviceInfo.address().toString() + ")"; } @@ -111,54 +111,193 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info) uint port = thing->paramValue(huaweiInverterThingPortParamTypeId).toUInt(); quint16 slaveId = thing->paramValue(huaweiInverterThingSlaveIdParamTypeId).toUInt(); - HuaweiModbusTcpConnection *connection = new HuaweiModbusTcpConnection(hostAddress, port, slaveId, this); - connect(connection, &HuaweiModbusTcpConnection::initializationFinished, this, [this, thing, connection, info]{ - qCDebug(dcHuawei()) << "Connection init" << connection; + HuaweiFusionSolar *connection = new HuaweiFusionSolar(hostAddress, port, slaveId, this); - // FIXME: check if success - - m_connections.insert(thing, connection); - info->finish(Thing::ThingErrorNoError); - - // Set connected true - thing->setStateValue(huaweiInverterConnectedStateTypeId, true); - - // Update registers - connection->update(); - }); - - connect(connection, &HuaweiModbusTcpConnection::connectionStateChanged, this, [thing, connection](bool status){ + connect(connection, &HuaweiFusionSolar::connectionStateChanged, this, [this, thing, connection](bool status){ qCDebug(dcHuawei()) << "Connected changed to" << status << "for" << thing; if (status) { // Connected true will be set after successfull init connection->initialize(); + thing->setStateValue(huaweiInverterConnectedStateTypeId, true); } else { thing->setStateValue(huaweiInverterConnectedStateTypeId, false); + } + foreach (Thing *childThing, myThings().filterByParentId(thing->id())) { + childThing->setStateValue("connected", status); } }); + connect(connection, &HuaweiFusionSolar::inverterActivePowerChanged, this, [thing](float inverterActivePower){ + qCDebug(dcHuawei()) << "Inverter power changed" << inverterActivePower * -1000.0 << "W"; + thing->setStateValue(huaweiInverterCurrentPowerStateTypeId, inverterActivePower * -1000.0); + }); + + connect(connection, &HuaweiFusionSolar::inverterDeviceStatusChanged, this, [thing](HuaweiFusionSolar::InverterDeviceStatus inverterDeviceStatus){ + qCDebug(dcHuawei()) << "Inverter device status changed" << inverterDeviceStatus; + Q_UNUSED(thing) + }); + + connect(connection, &HuaweiFusionSolar::inverterEnergyProducedChanged, this, [thing](float inverterEnergyProduced){ + qCDebug(dcHuawei()) << "Inverter total energy produced changed" << inverterEnergyProduced << "kWh"; + thing->setStateValue(huaweiInverterTotalEnergyProducedStateTypeId, inverterEnergyProduced); + }); + + // Meter + connect(connection, &HuaweiFusionSolar::powerMeterActivePowerChanged, this, [this, thing](qint32 powerMeterActivePower){ + Things meterThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiMeterThingClassId); + if (!meterThings.isEmpty()) { + qCDebug(dcHuawei()) << "Meter power changed" << powerMeterActivePower << "W"; + // Note: > 0 -> return, < 0 consume + meterThings.first()->setStateValue(huaweiMeterCurrentPowerStateTypeId, -powerMeterActivePower); + } + }); + + // Battery 1 + connect(connection, &HuaweiFusionSolar::lunaBattery1StatusChanged, this, [this, thing](HuaweiFusionSolar::BatteryDeviceStatus lunaBattery1Status){ + qCDebug(dcHuawei()) << "Battery 1 status changed" << lunaBattery1Status; + if (lunaBattery1Status != HuaweiFusionSolar::BatteryDeviceStatusOffline) { + // Check if w have to create the energy storage + Things batteryThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId); + bool alreadySetUp = false; + foreach (Thing *batteryThing, batteryThings) { + if (batteryThing->paramValue(huaweiBatteryThingUnitParamTypeId).toUInt() == 1) { + alreadySetUp = true; + } + } + + if (!alreadySetUp) { + qCDebug(dcHuawei()) << "Set up huawei energy storage 1 for" << thing; + ThingDescriptor descriptor(huaweiBatteryThingClassId, "Luna 2000 Battery", QString(), thing->id()); + ParamList params; + params.append(Param(huaweiBatteryThingUnitParamTypeId, 1)); + descriptor.setParams(params); + emit autoThingsAppeared(ThingDescriptors() << descriptor); + } + } + }); + + connect(connection, &HuaweiFusionSolar::lunaBattery1PowerChanged, this, [this, thing](qint32 lunaBattery1Power){ + qCDebug(dcHuawei()) << "Battery 1 power changed" << lunaBattery1Power << "W"; + Things batteryThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId).filterByParam(huaweiBatteryThingUnitParamTypeId, 1); + if (!batteryThings.isEmpty()) { + batteryThings.first()->setStateValue(huaweiBatteryCurrentPowerStateTypeId, lunaBattery1Power); + if (lunaBattery1Power < 0) { + batteryThings.first()->setStateValue(huaweiBatteryChargingStateStateTypeId, "discharging"); + } else if (lunaBattery1Power > 0) { + batteryThings.first()->setStateValue(huaweiBatteryChargingStateStateTypeId, "charging"); + } else { + batteryThings.first()->setStateValue(huaweiBatteryChargingStateStateTypeId, "idle"); + } + } + }); + + connect(connection, &HuaweiFusionSolar::lunaBattery1SocChanged, this, [this, thing](float lunaBattery1Soc){ + qCDebug(dcHuawei()) << "Battery 1 SOC changed" << lunaBattery1Soc << "%"; + Things batteryThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId).filterByParam(huaweiBatteryThingUnitParamTypeId, 1); + if (!batteryThings.isEmpty()) { + batteryThings.first()->setStateValue(huaweiBatteryBatteryLevelStateTypeId, lunaBattery1Soc); + batteryThings.first()->setStateValue(huaweiBatteryBatteryCriticalStateTypeId, lunaBattery1Soc < 10); + } + }); + + // Battery 2 + connect(connection, &HuaweiFusionSolar::lunaBattery2StatusChanged, this, [this, thing](HuaweiFusionSolar::BatteryDeviceStatus lunaBattery1Status){ + qCDebug(dcHuawei()) << "Battery 2 status changed" << lunaBattery1Status; + if (lunaBattery1Status != HuaweiFusionSolar::BatteryDeviceStatusOffline) { + // Check if w have to create the energy storage + Things batteryThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId); + bool alreadySetUp = false; + foreach (Thing *batteryThing, batteryThings) { + if (batteryThing->paramValue(huaweiBatteryThingUnitParamTypeId).toUInt() == 2) { + alreadySetUp = true; + } + } + + if (!alreadySetUp) { + qCDebug(dcHuawei()) << "Set up huawei energy storage 2 for" << thing; + ThingDescriptor descriptor(huaweiBatteryThingClassId, "Luna 2000 Battery", QString(), thing->id()); + ParamList params; + params.append(Param(huaweiBatteryThingUnitParamTypeId, 2)); + descriptor.setParams(params); + emit autoThingsAppeared(ThingDescriptors() << descriptor); + } + } + }); + + connect(connection, &HuaweiFusionSolar::lunaBattery2PowerChanged, this, [this, thing](qint32 lunaBattery2Power){ + qCDebug(dcHuawei()) << "Battery 2 power changed" << lunaBattery2Power << "W"; + Things batteryThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId).filterByParam(huaweiBatteryThingUnitParamTypeId, 2); + if (!batteryThings.isEmpty()) { + batteryThings.first()->setStateValue(huaweiBatteryCurrentPowerStateTypeId, lunaBattery2Power); + + if (lunaBattery2Power < 0) { + batteryThings.first()->setStateValue(huaweiBatteryChargingStateStateTypeId, "discharging"); + } else if (lunaBattery2Power > 0) { + batteryThings.first()->setStateValue(huaweiBatteryChargingStateStateTypeId, "charging"); + } else { + batteryThings.first()->setStateValue(huaweiBatteryChargingStateStateTypeId, "idle"); + } + } + }); + + connect(connection, &HuaweiFusionSolar::lunaBattery2SocChanged, this, [this, thing](float lunaBattery2Soc){ + qCDebug(dcHuawei()) << "Battery 2 SOC changed" << lunaBattery2Soc << "%"; + Things batteryThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId).filterByParam(huaweiBatteryThingUnitParamTypeId, 2); + if (!batteryThings.isEmpty()) { + batteryThings.first()->setStateValue(huaweiBatteryBatteryLevelStateTypeId, lunaBattery2Soc); + batteryThings.first()->setStateValue(huaweiBatteryBatteryCriticalStateTypeId, lunaBattery2Soc < 10); + } + }); + + m_connections.insert(thing, connection); connection->connectDevice(); // FIXME: make async and check if this is really a huawei info->finish(Thing::ThingErrorNoError); } + + if (thing->thingClassId() == huaweiMeterThingClassId) { + // Nothing to do here, we get all information from the inverter connection + info->finish(Thing::ThingErrorNoError); + Thing *parentThing = myThings().findById(thing->parentId()); + if (parentThing) { + thing->setStateValue("connected", parentThing->stateValue("connected").toBool()); + } + + } + + if (thing->thingClassId() == huaweiBatteryThingClassId) { + // Nothing to do here, we get all information from the inverter connection + info->finish(Thing::ThingErrorNoError); + Thing *parentThing = myThings().findById(thing->parentId()); + if (parentThing) { + thing->setStateValue("connected", parentThing->stateValue("connected").toBool()); + } + } } void IntegrationPluginHuawei::postSetupThing(Thing *thing) { - Q_UNUSED(thing) if (thing->thingClassId() == huaweiInverterThingClassId) { if (!m_pluginTimer) { qCDebug(dcHuawei()) << "Starting plugin timer..."; - m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(10); + m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(5); connect(m_pluginTimer, &PluginTimer::timeout, this, [this] { - foreach(HuaweiModbusTcpConnection *connection, m_connections) { + foreach(HuaweiFusionSolar *connection, m_connections) { if (connection->connected()) { connection->update(); } } }); + + m_pluginTimer->start(); + } + + // Check if w have to set up a child meter for this inverter connection + if (myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiMeterThingClassId).isEmpty()) { + qCDebug(dcHuawei()) << "Set up huawei meter for" << thing; + emit autoThingsAppeared(ThingDescriptors() << ThingDescriptor(huaweiMeterThingClassId, "Huawei Power Meter", QString(), thing->id())); } } } @@ -166,8 +305,7 @@ void IntegrationPluginHuawei::postSetupThing(Thing *thing) void IntegrationPluginHuawei::thingRemoved(Thing *thing) { if (thing->thingClassId() == huaweiInverterThingClassId && m_connections.contains(thing)) { - HuaweiModbusTcpConnection *connection = m_connections.take(thing); - delete connection; + m_connections.take(thing)->deleteLater(); } if (myThings().isEmpty() && m_pluginTimer) { diff --git a/huawei/integrationpluginhuawei.h b/huawei/integrationpluginhuawei.h index 5668d76..83e1915 100644 --- a/huawei/integrationpluginhuawei.h +++ b/huawei/integrationpluginhuawei.h @@ -33,7 +33,7 @@ #include "plugintimer.h" #include "integrations/integrationplugin.h" -#include "huaweimodbustcpconnection.h" +#include "huaweifusionsolar.h" class IntegrationPluginHuawei: public IntegrationPlugin { @@ -54,7 +54,7 @@ public: private: PluginTimer *m_pluginTimer = nullptr; - QHash m_connections; + QHash m_connections; }; diff --git a/huawei/integrationpluginhuawei.json b/huawei/integrationpluginhuawei.json index f3ee39b..22510d2 100644 --- a/huawei/integrationpluginhuawei.json +++ b/huawei/integrationpluginhuawei.json @@ -14,6 +14,7 @@ "id": "87e75ee0-d544-457b-add3-bd4e58160fcd", "createMethods": ["discovery", "user"], "interfaces": ["solarinverter", "connectable"], + "providedInterfaces": [ "solarinverter", "energymeter", "energystorage"], "paramTypes": [ { "id": "d93371db-0954-4dcd-a1a5-6881b78cb0ea", @@ -73,87 +74,6 @@ "type": "double", "unit": "KiloWattHour", "defaultValue": 0.00 - }, - { - "id": "6609a589-8f0f-4747-9240-c0d0e0d87f29", - "name": "phaseACurrent", - "displayName": "Phase A current", - "displayNameEvent": "Phase A current changed", - "type": "double", - "unit": "Ampere", - "defaultValue": 0.00 - }, - { - "id": "1bff8853-ec1e-4fe8-b148-c04d1db67e74", - "name": "phaseBCurrent", - "displayName": "Phase B current", - "displayNameEvent": "Phase B current changed", - "type": "double", - "unit": "Ampere", - "defaultValue": 0.00 - }, - { - "id": "0977c3bc-7798-4161-a75f-19858d44c463", - "name": "phaseCCurrent", - "displayName": "Phase C current", - "displayNameEvent": "Phase C current changed", - "type": "double", - "unit": "Ampere", - "defaultValue": 0.00 - }, - { - "id": "e91281b3-d89f-4d3e-b81e-96645a8de63f", - "name": "voltagePhaseA", - "displayName": "Voltage phase A", - "displayNameEvent": "Voltage phase A changed", - "type": "double", - "unit": "Volt", - "defaultValue": 0.00 - }, - { - "id": "ee921f23-ceeb-4818-adca-8c2fa1e75b17", - "name": "voltagePhaseB", - "displayName": "Voltage phase B", - "displayNameEvent": "Voltage phase B changed", - "type": "double", - "unit": "Volt", - "defaultValue": 0.00 - }, - { - "id": "5613cea9-11cb-4b81-9b61-0e9a7ec737a3", - "name": "voltagePhaseC", - "displayName": "Voltage phase C", - "displayNameEvent": "Voltage phase C changed", - "type": "double", - "unit": "Volt", - "defaultValue": 0.00 - }, - { - "id": "4db7c66c-7676-449d-94c5-1b8c0ddf368a", - "name": "currentPowerPhaseA", - "displayName": "Current power phase A", - "displayNameEvent": "Current power phase A changed", - "type": "double", - "unit": "Watt", - "defaultValue": 0.00 - }, - { - "id": "59026351-1cea-45e8-bffe-9ab65b44ddb2", - "name": "currentPowerPhaseB", - "displayName": "Current power phase B", - "displayNameEvent": "Current power phase B changed", - "type": "double", - "unit": "Watt", - "defaultValue": 0.00 - }, - { - "id": "89ccd9d7-fd26-431a-b198-5001564373dc", - "name": "currentPowerPhaseC", - "displayName": "Current power phase C", - "displayNameEvent": "Current power phase C changed", - "type": "double", - "unit": "Watt", - "defaultValue": 0.00 } ], "actionTypes": [ ] @@ -303,6 +223,13 @@ "createMethods": ["auto"], "interfaces": [ "energystorage", "connectable"], "paramTypes": [ + { + "id": "019287a6-c593-45a8-9695-2e1ad8e81c32", + "name":"unit", + "displayName": "Unit", + "type": "uint", + "defaultValue": 0 + } ], "stateTypes": [ {