Merge PR #77: Huawei: Adding modbus RTU functionality and update FusionSolar discovery

This commit is contained in:
jenkins 2022-08-24 17:19:26 +02:00
commit 2b058978ec
15 changed files with 1929 additions and 732 deletions

View File

@ -1,12 +1,42 @@
# Huawei FusionSolar
Connects to a Huawei FusionSolar using Modbus TCP.
Connects to a Huawei FusionSolar using Modbus RTU or TCP.
## Supported things
## Huawei FusionSolar
* Inverter
* Meter (only current power)
* Battery (on Unit 1 and 2)
The Huawei FusionSolar can be connected either via the Huawei SmartDongle or via a Modbus RTU (RS485) connection.
The following devices are supported:
* Huawei FusionSolar Inverter (all model supported by the SmartDongle)
* Huawei Meter (connected internally to the Inverter)
* Luna2000 battery units 1 and 2 if connected
The batteries will be shown in the system as informative things, but will not be considered in the energy view and and energy balance. Since the batteries are connected internally behind the inverter, power coming from the battery will be shown as power coming from the inverter. The inverter power represents power from the PV or from the battery.
### Huawei SmartDongle
The [SmartDongle](https://solar.huawei.com/-/media/Solar/attachment/pdf/apac/datasheet/SmartDongle-WLAN-FE.pdf) can be used to communicate
with the Huawei Solar Inverter. In order to allow nymea to read from the device please make sure following requirements are met:
* SmartDongle software version must be at least `SP130`.
* Connect the Huawei SmartDongle to the network
* Use the official FusonSolar App to enable Modbus TCP access for the Dongle (full access). You can find more informations [here](https://forum.huawei.com/enterprise/en/modbus-tcp-guide/thread/789585-100027?page=1#comments-area).
You can also contact the [official Huawei support](mailto:eu_inverter_support@huawei.com) in order to get the update files and instructions, or get it from [here](https://support.huawei.com/enterprise/en/digital-power/sdongle-pid-23826585/software).
> The SmartDongle provides only access to the registers specified in the Huawei `openAPI`. Full modbus register access requires a modbus RTU connection.
### Direct modbus RTU connection (RS485).
If you want to communicate directly with the inverter using modbus RTU, you need to wire up the connection correctly and set up the Modbus RTU interface in the nymea settings using the right configuations. Then you can discover the Modbus RTU interface while adding the Huawei Modbus RTU inverter in nymea.
The Modbus RTU offers a full access to all modbus registers available on the inverter.
Please contact your installer how to enable a modbus RTU connection for your setup.
## More
https://solar.huawei.com/eu/

View File

@ -0,0 +1,280 @@
{
"className": "HuaweiFusion",
"protocol": "TCP",
"endianness": "BigEndian",
"errorLimitUntilNotReachable": 15,
"checkReachableRegister": "inverterActivePower",
"enums": [
{
"name": "InverterDeviceStatus",
"values": [
{
"key": "StandbyInitializing",
"value": 0
},
{
"key": "StandbyDetectingInsulationResistance",
"value": 1
},
{
"key": "StandbyDetectingIrradiation",
"value": 2
},
{
"key": "StandbyDridDetecting",
"value": 3
},
{
"key": "Starting",
"value": 256
},
{
"key": "OnGrid",
"value": 512
},
{
"key": "PowerLimited",
"value": 513
},
{
"key": "SelfDerating",
"value": 514
},
{
"key": "ShutdownFault",
"value": 768
},
{
"key": "ShutdownCommand",
"value": 769
},
{
"key": "ShutdownOVGR",
"value": 770
},
{
"key": "ShutdownCommunicationDisconnected",
"value": 771
},
{
"key": "ShutdownPowerLimit",
"value": 772
},
{
"key": "ShutdownManualStartupRequired",
"value": 773
},
{
"key": "ShutdownInputUnderpower",
"value": 774
},
{
"key": "GridSchedulingPCurve",
"value": 1025
},
{
"key": "GridSchedulingQUCurve",
"value": 1026
},
{
"key": "GridSchedulingPFUCurve",
"value": 1027
},
{
"key": "GridSchedulingDryContact",
"value": 1028
},
{
"key": "GridSchedulingQPCurve",
"value": 1029
},
{
"key": "SpotCheckReady",
"value": 1280
},
{
"key": "SpotChecking",
"value": 1281
},
{
"key": "Inspecting",
"value": 1536
},
{
"key": "AfciSelfCheck",
"value": 1792
},
{
"key": "IVScanning",
"value": 2048
},
{
"key": "DCInputDetection",
"value": 2304
},
{
"key": "RunningOffGridCharging",
"value": 2560
},
{
"key": "StandbyNoIrradiation",
"value": 40960
}
]
},
{
"name": "BatteryDeviceStatus",
"values": [
{
"key": "Offline",
"value": 0
},
{
"key": "Standby",
"value": 1
},
{
"key": "Running",
"value": 2
},
{
"key": "Fault",
"value": 3
},
{
"key": "SleepMode",
"value": 4
}
]
}
],
"blocks": [
],
"registers": [
{
"id": "inverterActivePower",
"address": 32080,
"size": 2,
"type": "int32",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Inverter active power",
"unit": "kW",
"staticScaleFactor": -3,
"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,
"size": 2,
"type": "int32",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Power meter active power",
"unit": "W",
"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,
"size": 2,
"type": "int32",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Luna 2000 Battery 1 power",
"unit": "W",
"defaultValue": "0",
"access": "RO"
},
{
"id": "lunaBattery1Soc",
"address": 37004,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Luna 2000 Battery 1 state of charge",
"staticScaleFactor": -1,
"unit": "%",
"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,
"size": 2,
"type": "int32",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Luna 2000 Battery 2 power",
"unit": "W",
"defaultValue": "0",
"access": "RO"
},
{
"id": "lunaBattery2Soc",
"address": 37738,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"readSchedule": "update",
"description": "Luna 2000 Battery 2 state of charge",
"staticScaleFactor": -1,
"unit": "%",
"defaultValue": "0",
"access": "RO"
}
]
}

View File

@ -1,6 +1,6 @@
{
"className": "Huawei",
"protocol": "TCP",
"protocol": "RTU",
"endianness": "BigEndian",
"errorLimitUntilNotReachable": 15,
"checkReachableRegister": "inverterActivePower",
@ -135,20 +135,149 @@
},
{
"key": "Running",
"value": 1
"value": 2
},
{
"key": "Fault",
"value": 1
"value": 3
},
{
"key": "SleepMode",
"value": 1
"value": 4
}
]
}
],
"blocks": [
{
"id": "identifyer",
"readSchedule": "init",
"registers": [
{
"id": "model",
"address": 30000,
"size": 15,
"type": "string",
"registerType": "holdingRegister",
"description": "Model",
"access": "RO"
},
{
"id": "serialNumber",
"address": 30015,
"size": 10,
"type": "string",
"registerType": "holdingRegister",
"description": "Serial number",
"access": "RO"
},
{
"id": "productNumber",
"address": 30025,
"size": 10,
"type": "string",
"registerType": "holdingRegister",
"description": "Product number",
"access": "RO"
}
]
},
{
"id": "setup",
"readSchedule": "init",
"registers": [
{
"id": "modelId",
"address": 30070,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"description": "Model ID",
"defaultValue": "0",
"access": "RO"
},
{
"id": "numberOfPvStrings",
"address": 30071,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"description": "Number of PV strings",
"defaultValue": "0",
"access": "RO"
},
{
"id": "numberOfMppTracks",
"address": 30072,
"size": 1,
"type": "uint16",
"registerType": "holdingRegister",
"description": "Number of MPP tracks",
"defaultValue": "0",
"access": "RO"
},
{
"id": "ratedPower",
"address": 30073,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Rated power (Pn)",
"unit": "kW",
"staticScaleFactor": -3,
"defaultValue": "0",
"access": "RO"
},
{
"id": "maxActivePower",
"address": 30075,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Maximum active power (P max)",
"unit": "kW",
"staticScaleFactor": -3,
"defaultValue": "0",
"access": "RO"
},
{
"id": "maxApparentPower",
"address": 30077,
"size": 2,
"type": "uint32",
"registerType": "holdingRegister",
"description": "Maximum apparant power (S max)",
"unit": "kVA",
"staticScaleFactor": -3,
"defaultValue": "0",
"access": "RO"
},
{
"id": "maxReactivePowerToGrid",
"address": 30079,
"size": 2,
"type": "int32",
"registerType": "holdingRegister",
"description": "Maximum reactive power (Q max) - fed to grid",
"unit": "kVar",
"staticScaleFactor": -3,
"defaultValue": "0",
"access": "RO"
},
{
"id": "maxReactivePowerFromGrid",
"address": 30081,
"size": 2,
"type": "int32",
"registerType": "holdingRegister",
"description": "Maximum reactive power (Q max) - absorbed from grid",
"unit": "kVar",
"staticScaleFactor": -3,
"defaultValue": "0",
"access": "RO"
}
]
}
],
"registers": [
{

View File

@ -1,14 +1,16 @@
include(../plugins.pri)
# Generate modbus connection
MODBUS_CONNECTIONS += huawei-registers.json
#MODBUS_TOOLS_CONFIG += VERBOSE
MODBUS_CONNECTIONS += huawei-fusion-solar-registers.json huawei-registers.json
include(../modbus.pri)
HEADERS += \
huaweifusionsolar.h \
huaweifusionsolardiscovery.h \
integrationpluginhuawei.h
SOURCES += \
huaweifusionsolar.cpp \
huaweifusionsolardiscovery.cpp \
integrationpluginhuawei.cpp

View File

@ -1,4 +1,4 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, nymea GmbH
* Contact: contact@nymea.io
@ -35,15 +35,61 @@
NYMEA_LOGGING_CATEGORY(dcHuaweiFusionSolar, "HuaweiFusionSolar")
HuaweiFusionSolar::HuaweiFusionSolar(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent) :
HuaweiModbusTcpConnection(hostAddress, port, slaveId, parent)
HuaweiFusionModbusTcpConnection(hostAddress, port, slaveId, parent)
{
// Note: sometimes right after the discovery / setup the check fails the first time due to server busy error,
// this is a very slow or busy device since it returns quiet often that error. Don't faile with the first busy error...
setCheckReachableRetries(3);
connect(this, &HuaweiFusionModbusTcpConnection::connectionStateChanged, this, [=](bool connected){
if (!connected) {
m_registersQueue.clear();
}
});
}
bool HuaweiFusionSolar::initialize()
{
// No init registers defined. Nothing to be done and we are finished.
emit initializationFinished(true);
if (!reachable()) {
qCWarning(dcHuaweiFusionSolar()) << "Tried to initialize but the device is not to be reachable.";
return false;
}
if (m_initReply) {
qCWarning(dcHuaweiFusionSolar()) << "Tried to initialize but the init process is already running.";
return false;
}
qCDebug(dcHuaweiFusionSolar()) << "Initialize connection by reading \"Inverter active power\" register:" << 32080 << "size:" << 2;
m_initReply = readInverterActivePower();
if (!m_initReply) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while initializing connection and read \"Inverter active power\" register from" << hostAddress().toString() << errorString();
return false;
}
if (m_initReply->isFinished()) {
m_initReply->deleteLater(); // Broadcast reply returns immediatly
m_initReply = nullptr;
return false;
}
connect(m_initReply, &QModbusReply::finished, this, [this](){
if (m_initReply->error() == QModbusDevice::NoError) {
qCDebug(dcHuaweiFusionSolar()) << "Initialization finished of HuaweiFusionSolar" << hostAddress().toString() << "finished successfully";
emit initializationFinished(true);
} else {
qCWarning(dcHuaweiFusionSolar()) << "Initialization finished of HuaweiFusionSolar" << hostAddress().toString() << "failed.";
emit initializationFinished(false);
}
m_initReply->deleteLater();
m_initReply = nullptr;
});
connect(m_initReply, &QModbusReply::errorOccurred, this, [this] (QModbusDevice::Error error){
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while initializing connection and read \"Inverter active power\" registers from" << hostAddress().toString() << error << m_initReply->errorString();
});
return true;
}
@ -53,23 +99,29 @@ bool HuaweiFusionSolar::update()
if (!m_registersQueue.isEmpty())
return true;
// Add the requests
m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterInverterActivePower);
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::RegisterLunaBattery2Status);
if (m_battery2Available) {
m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery2Power);
m_registersQueue.enqueue(HuaweiModbusTcpConnection::RegisterLunaBattery2Soc);
}
// Add the requests to queue, begin with power values, since they are most important
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterInverterActivePower);
if (m_battery1Available)
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterLunaBattery1Power);
// Note: since huawei can only process one request at the time, we need to queue the requests
if (m_battery2Available)
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterLunaBattery2Power);
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterPowerMeterActivePower);
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterInverterEnergyProduced);
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterInverterDeviceStatus);
// Note: we constantly read the status in any case so we detect if a battery came online
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterLunaBattery1Status);
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterLunaBattery2Status);
if (m_battery1Available)
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterLunaBattery1Soc);
if (m_battery2Available)
m_registersQueue.enqueue(HuaweiFusionModbusTcpConnection::RegisterLunaBattery2Soc);
// Note: since huawei can only process one request at the time, we need to queue the requests and have some time between requests...
m_currentRegisterRequest = -1;
readNextRegister();
@ -89,323 +141,518 @@ void HuaweiFusionSolar::readNextRegister()
m_currentRegisterRequest = m_registersQueue.dequeue();
switch (m_currentRegisterRequest) {
case HuaweiModbusTcpConnection::RegisterInverterActivePower: {
case HuaweiFusionModbusTcpConnection::RegisterInverterActivePower: {
// Update registers from Inverter active power
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);
connect(reply, &QModbusReply::finished, this, [this, reply](){
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
const QVector<quint16> values = unit.values();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter active power\" register" << 32080 << "size:" << 2 << values;
processInverterActivePowerRegisterValues(values);
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter active power\" registers from" << hostAddress().toString() << error << reply->errorString();
});
} else {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
}
} else {
if (!reply) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Inverter active power\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter active power\" register" << 32080 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 2)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 2 << "but received" << unit.values();
} else {
processInverterActivePowerRegisterValues(unit.values());
}
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter active power\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter active power\" registers from" << hostAddress().toString() << error << reply->errorString();
}
});
break;
}
case HuaweiModbusTcpConnection::RegisterInverterDeviceStatus: {
case HuaweiFusionModbusTcpConnection::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<quint16> values = unit.values();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter device status\" register" << 32089 << "size:" << 1 << values;
processInverterDeviceStatusRegisterValues(values);
qCDebug(dcHuaweiFusionSolar()) << "Inverter status" << 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();
});
} else {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
}
} else {
if (!reply) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Inverter device status\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter device status\" register" << 32089 << "size:" << 1 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 1)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 1 << "but received" << unit.values();
} else {
processInverterDeviceStatusRegisterValues(unit.values());
}
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter device status\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter device status\" registers from" << hostAddress().toString() << error << reply->errorString();
}
});
break;
}
case HuaweiModbusTcpConnection::RegisterInverterEnergyProduced: {
case HuaweiFusionModbusTcpConnection::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<quint16> values = unit.values();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter energy produced\" register" << 32106 << "size:" << 2 << values;
processInverterEnergyProducedRegisterValues(values);
}
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();
});
} else {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
}
} else {
if (!reply) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Inverter energy produced\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
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<quint16> values = unit.values();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 status\" register" << 37000 << "size:" << 1 << values;
processLunaBattery1StatusRegisterValues(values);
qCDebug(dcHuaweiFusionSolar()) << "Battery 1 status" << m_lunaBattery1Status;
if (m_lunaBattery1Status == BatteryDeviceStatusOffline) {
m_battery1Available = false;
} else {
m_battery1Available = true;
}
}
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();
});
} else {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 1 status\" registers from" << hostAddress().toString() << errorString();
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
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<quint16> values = unit.values();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 power\" register" << 37001 << "size:" << 2 << values;
processLunaBattery1PowerRegisterValues(values);
}
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();
});
} else {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Inverter energy produced\" register" << 32106 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 2)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 2 << "but received" << unit.values();
} else {
processInverterEnergyProducedRegisterValues(unit.values());
}
}
} 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<quint16> values = unit.values();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 state of charge\" register" << 37004 << "size:" << 1 << values;
processLunaBattery1SocRegisterValues(values);
}
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();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter energy produced\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Inverter energy produced\" registers from" << hostAddress().toString() << error << reply->errorString();
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 1 state of charge\" registers from" << hostAddress().toString() << errorString();
finishRequest();
}
});
break;
}
case HuaweiModbusTcpConnection::RegisterPowerMeterActivePower: {
case HuaweiFusionModbusTcpConnection::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<quint16> values = unit.values();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Power meter active power\" register" << 37113 << "size:" << 2 << values;
processPowerMeterActivePowerRegisterValues(values);
}
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();
});
} else {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
}
} else {
if (!reply) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Power meter active power\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Power meter active power\" register" << 37113 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 2)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 2 << "but received" << unit.values();
} else {
processPowerMeterActivePowerRegisterValues(unit.values());
}
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Power meter active power\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Power meter active power\" registers from" << hostAddress().toString() << error << reply->errorString();
}
});
break;
}
case HuaweiModbusTcpConnection::RegisterLunaBattery2Status: {
case HuaweiFusionModbusTcpConnection::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) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 1 status\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 status\" register" << 37000 << "size:" << 1 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 1)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 1 << "but received" << unit.values();
} else {
processLunaBattery1StatusRegisterValues(unit.values());
}
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 1 status\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 1 status\" registers from" << hostAddress().toString() << error << reply->errorString();
}
});
break;
}
case HuaweiFusionModbusTcpConnection::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) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 1 power\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 power\" register" << 37001 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 2)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 2 << "but received" << unit.values();
} else {
processLunaBattery1PowerRegisterValues(unit.values());
}
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 1 power\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 1 power\" registers from" << hostAddress().toString() << error << reply->errorString();
}
});
break;
}
case HuaweiFusionModbusTcpConnection::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) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 1 state of charge\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 1 state of charge\" register" << 37004 << "size:" << 1 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 1)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 1 << "but received" << unit.values();
} else {
processLunaBattery1SocRegisterValues(unit.values());
}
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 1 state of charge\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 1 state of charge\" registers from" << hostAddress().toString() << error << reply->errorString();
}
});
break;
}
case HuaweiFusionModbusTcpConnection::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<quint16> values = unit.values();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 status\" register" << 37741 << "size:" << 1 << values;
processLunaBattery2StatusRegisterValues(values);
qCDebug(dcHuaweiFusionSolar()) << "Battery 2 status" << m_lunaBattery2Status;
if (m_lunaBattery2Status == BatteryDeviceStatusOffline) {
m_battery2Available = false;
} else {
m_battery2Available = true;
}
}
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();
});
} else {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
}
} else {
if (!reply) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 2 status\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 status\" register" << 37741 << "size:" << 1 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 1)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 1 << "but received" << unit.values();
} else {
processLunaBattery2StatusRegisterValues(unit.values());
}
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 2 status\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 2 status\" registers from" << hostAddress().toString() << error << reply->errorString();
}
});
break;
}
case HuaweiModbusTcpConnection::RegisterLunaBattery2Power: {
case HuaweiFusionModbusTcpConnection::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<quint16> values = unit.values();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 power\" register" << 37743 << "size:" << 2 << values;
processLunaBattery2PowerRegisterValues(values);
}
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();
});
} else {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
}
} else {
if (!reply) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 2 power\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 power\" register" << 37743 << "size:" << 2 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 2)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values count. Requested" << 2 << "but received" << unit.values();
} else {
processLunaBattery2PowerRegisterValues(unit.values());
}
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 2 power\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 2 power\" registers from" << hostAddress().toString() << error << reply->errorString();
}
});
break;
}
case HuaweiModbusTcpConnection::RegisterLunaBattery2Soc: {
case HuaweiFusionModbusTcpConnection::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<quint16> values = unit.values();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 state of charge\" register" << 37738 << "size:" << 1 << values;
processLunaBattery2SocRegisterValues(values);
}
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();
});
} else {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
}
} else {
if (!reply) {
qCWarning(dcHuaweiFusionSolar()) << "Error occurred while reading \"Luna 2000 Battery 2 state of charge\" registers from" << hostAddress().toString() << errorString();
finishRequest();
return;
}
if (reply->isFinished()) {
reply->deleteLater(); // Broadcast reply returns immediatly
finishRequest();
return;
}
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
connect(reply, &QModbusReply::finished, this, [this, reply](){
handleModbusError(reply->error());
if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result();
qCDebug(dcHuaweiFusionSolar()) << "<-- Response from \"Luna 2000 Battery 2 state of charge\" register" << 37738 << "size:" << 1 << "valueCount:" << unit.valueCount() << unit.values() << unit.values().count();
if (!valuesAreVaild(unit.values(), 1)) {
qCWarning(dcHuaweiFusionSolar()) << "<-- Received invalid values. Requested" << 1 << "but received" << unit.values();
} else {
processLunaBattery2SocRegisterValues(unit.values());
}
}
finishRequest();
});
connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){
if (reply->error() == QModbusDevice::ProtocolError) {
QModbusResponse response = reply->rawResult();
if (response.isException()) {
qCDebug(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 2 state of charge\" registers from" << hostAddress().toString() << exceptionToString(response.exceptionCode());
}
} else {
qCWarning(dcHuaweiFusionSolar()) << "Modbus reply error occurred while updating \"Luna 2000 Battery 2 state of charge\" registers from" << hostAddress().toString() << error << reply->errorString();
}
});
break;
}
}
}
bool HuaweiFusionSolar::valuesAreVaild(const QVector<quint16> &values, int readSize)
{
if (values.count() != readSize) {
qCDebug(dcHuaweiFusionSolar()) << "Invalid values. The received values count does not match the requested" << readSize << "registers.";
return false;
}
// According to the documentation from 2016:
// 0x7FFF: invalid value of the floating point type returned by one register
// 0xFFFF: invalid value of a type other than the floating point type returned by one register
// 0xFFFFFFFF: invalid value returned by two registers
if (values.count() == 2) {
bool floatingPointValid = (values != QVector<quint16>() << 0x7fff << 0xffff);
if (!floatingPointValid)
qCDebug(dcHuaweiFusionSolar()) << "Invalid values. The received values match the invalid for floating pointer:" << values;
bool otherTypesValid = (values != QVector<quint16>(2, 0xffff));
if (!otherTypesValid)
qCDebug(dcHuaweiFusionSolar()) << "Invalid values. The received values match the invalid registers values:" << values;
return floatingPointValid && otherTypesValid;
}
if (values.count() == 1)
return values.at(0) != 0x7fff && values.at(0) != 0xffff;
return true;
}
void HuaweiFusionSolar::finishRequest()
{
m_currentRegisterRequest = -1;
QTimer::singleShot(1000, this, &HuaweiFusionSolar::readNextRegister);
}
QString HuaweiFusionSolar::exceptionToString(QModbusPdu::ExceptionCode exception)
{
QString exceptionString;
switch (exception) {
case QModbusPdu::IllegalFunction:
exceptionString = "Illegal function";
break;
case QModbusPdu::IllegalDataAddress:
exceptionString = "Illegal data address";
break;
case QModbusPdu::IllegalDataValue:
exceptionString = "Illegal data value";
break;
case QModbusPdu::ServerDeviceFailure:
exceptionString = "Server device failure";
break;
case QModbusPdu::Acknowledge:
exceptionString = "Acknowledge";
break;
case QModbusPdu::ServerDeviceBusy:
exceptionString = "Server device busy";
break;
case QModbusPdu::NegativeAcknowledge:
exceptionString = "Negative acknowledge";
break;
case QModbusPdu::MemoryParityError:
exceptionString = "Memory parity error";
break;
case QModbusPdu::GatewayPathUnavailable:
exceptionString = "Gateway path unavailable";
break;
case QModbusPdu::GatewayTargetDeviceFailedToRespond:
exceptionString = "Gateway target device failed to respond";
break;
case QModbusPdu::ExtendedException:
exceptionString = "Extended exception";
break;
}
return exceptionString;
}

View File

@ -34,20 +34,21 @@
#include <QObject>
#include <QQueue>
#include "huaweimodbustcpconnection.h"
#include "huaweifusionmodbustcpconnection.h"
class HuaweiFusionSolar : public HuaweiModbusTcpConnection
class HuaweiFusionSolar : public HuaweiFusionModbusTcpConnection
{
Q_OBJECT
public:
explicit HuaweiFusionSolar(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr);
~HuaweiFusionSolar() = default;
virtual bool initialize() override;
virtual bool update() override;
private:
QQueue<HuaweiModbusTcpConnection::Registers> m_registersQueue;
QQueue<HuaweiFusionModbusTcpConnection::Registers> m_registersQueue;
QModbusReply *m_initReply = nullptr;
int m_currentRegisterRequest = -1;
void finishRequest();
@ -55,8 +56,13 @@ private:
bool m_battery1Available = true;
bool m_battery2Available = true;
double m_actualInverterPower = 0;
QString exceptionToString(QModbusPdu::ExceptionCode exception);
private slots:
void readNextRegister();
bool valuesAreVaild(const QVector<quint16> &values, int readSize);
};

View File

@ -0,0 +1,149 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, 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 "huaweifusionsolardiscovery.h"
#include "extern-plugininfo.h"
HuaweiFusionSolarDiscovery::HuaweiFusionSolarDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port, quint16 modbusAddress, QObject *parent) :
QObject(parent),
m_networkDeviceDiscovery(networkDeviceDiscovery),
m_port(port),
m_modbusAddress(modbusAddress)
{
}
void HuaweiFusionSolarDiscovery::startDiscovery()
{
qCInfo(dcHuawei()) << "Discovery: Start searching for Huawei FusionSolar SmartDongle in the network...";
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
// Check any already discovered infos..
foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) {
checkNetworkDevice(networkDeviceInfo);
}
// Immedialty check any new device gets discovered
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, &HuaweiFusionSolarDiscovery::checkNetworkDevice);
// Check what might be left on finished
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
qCDebug(dcHuawei()) << "Discovery: Network discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices";
m_networkDeviceInfos = discoveryReply->networkDeviceInfos();
qCDebug(dcHuawei()) << "Discovery: Network discovery finished. Start finishing discovery...";
// Send a report request to nework device info not sent already...
foreach (const NetworkDeviceInfo &networkDeviceInfo, m_networkDeviceInfos) {
if (!m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo)) {
checkNetworkDevice(networkDeviceInfo);
}
}
// Finish with some delay so the last added network device information objects still can be checked.
QTimer::singleShot(3000, this, [this](){
qCDebug(dcHuawei()) << "Discovery: Grace period timer triggered.";
finishDiscovery();
});
});
}
NetworkDeviceInfos HuaweiFusionSolarDiscovery::discoveryResults() const
{
return m_discoveryResults;
}
void HuaweiFusionSolarDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
{
if (m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo))
return;
// The dongle must have a huawei registered mac address
if (!networkDeviceInfo.macAddressManufacturer().toLower().contains("huawei"))
return;
HuaweiFusionSolar *connection = new HuaweiFusionSolar(networkDeviceInfo.address(), m_port, m_modbusAddress, this);
m_connections.append(connection);
m_verifiedNetworkDeviceInfos.append(networkDeviceInfo);
connect(connection, &HuaweiFusionSolar::reachableChanged, this, [=](bool reachable){
if (!reachable) {
// Disconnected ... done with this connection
cleanupConnection(connection);
return;
}
qCDebug(dcHuawei()) << "Discovery: --> Found" << networkDeviceInfo;
m_discoveryResults.append(networkDeviceInfo);
});
// If we get any error...skip this host...
connect(connection, &HuaweiFusionSolar::connectionErrorOccurred, this, [=](QModbusDevice::Error error){
if (error != QModbusDevice::NoError) {
qCDebug(dcHuawei()) << "Discovery: Connection error on" << networkDeviceInfo.address().toString() << "Continue...";;
cleanupConnection(connection);
}
});
// If check reachability failed...skip this host...
connect(connection, &HuaweiFusionSolar::checkReachabilityFailed, this, [=](){
qCDebug(dcHuawei()) << "Discovery: Check reachability failed on" << networkDeviceInfo.address().toString() << "Continue...";;
cleanupConnection(connection);
});
// Try to connect, maybe it works, maybe not,
// but retry only once to communicate with the device for reachability check...
connection->setCheckReachableRetries(1);
// Try to connect, maybe it works, maybe not...
connection->connectDevice();
}
void HuaweiFusionSolarDiscovery::cleanupConnection(HuaweiFusionSolar *connection)
{
if (m_connections.contains(connection)) {
m_connections.removeAll(connection);
connection->disconnectDevice();
connection->deleteLater();
}
}
void HuaweiFusionSolarDiscovery::finishDiscovery()
{
qint64 durationMilliSeconds = QDateTime::currentMSecsSinceEpoch() - m_startDateTime.toMSecsSinceEpoch();
// Cleanup any leftovers...we don't care any more
foreach (HuaweiFusionSolar *connection, m_connections)
cleanupConnection(connection);
qCInfo(dcHuawei()) << "Discovery: Finished the discovery process. Found" << m_discoveryResults.count()
<< "inverters in" << QTime::fromMSecsSinceStartOfDay(durationMilliSeconds).toString("mm:ss.zzz");
emit discoveryFinished();
}

View File

@ -0,0 +1,74 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2022, 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 HUAWEIFUSIONSOLARDISCOVERY_H
#define HUAWEIFUSIONSOLARDISCOVERY_H
#include <QObject>
#include <network/networkdevicediscovery.h>
#include "huaweifusionsolar.h"
class HuaweiFusionSolarDiscovery : public QObject
{
Q_OBJECT
public:
explicit HuaweiFusionSolarDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 port = 502, quint16 modbusAddress = 1, QObject *parent = nullptr);
void startDiscovery();
NetworkDeviceInfos discoveryResults() const;
signals:
void discoveryFinished();
private:
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
quint16 m_port;
quint16 m_modbusAddress;
QDateTime m_startDateTime;
NetworkDeviceInfos m_networkDeviceInfos;
NetworkDeviceInfos m_verifiedNetworkDeviceInfos;
QList<HuaweiFusionSolar *> m_connections;
NetworkDeviceInfos m_discoveryResults;
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
void cleanupConnection(HuaweiFusionSolar *connection);
void finishDiscovery();
};
#endif // HUAWEIFUSIONSOLARDISCOVERY_H

View File

@ -28,10 +28,11 @@
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "huaweifusionsolardiscovery.h"
#include "integrationpluginhuawei.h"
#include "plugininfo.h"
#include <network/networkdevicediscovery.h>
#include <hardware/modbus/modbusrtuhardwareresource.h>
#include <hardwaremanager.h>
IntegrationPluginHuawei::IntegrationPluginHuawei()
@ -41,59 +42,69 @@ IntegrationPluginHuawei::IntegrationPluginHuawei()
void IntegrationPluginHuawei::discoverThings(ThingDiscoveryInfo *info)
{
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
qCWarning(dcHuawei()) << "The network discovery is not available on this platform.";
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
return;
}
if (info->thingClassId() == huaweiFusionSolarInverterThingClassId) {
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
qCWarning(dcHuawei()) << "The network discovery is not available on this platform.";
info->finish(Thing::ThingErrorUnsupportedFeature, QT_TR_NOOP("The network device discovery is not available."));
return;
}
NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover();
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) {
// Create a discovery with the info as parent for auto deleting the object once the discovery info is done
HuaweiFusionSolarDiscovery *discovery = new HuaweiFusionSolarDiscovery(hardwareManager()->networkDeviceDiscovery(), 502, 1, info);
connect(discovery, &HuaweiFusionSolarDiscovery::discoveryFinished, info, [=](){
foreach (const NetworkDeviceInfo &networkDeviceInfo, discovery->discoveryResults()) {
qCDebug(dcHuawei()) << "Found" << networkDeviceInfo;
ThingDescriptor descriptor(huaweiFusionSolarInverterThingClassId, QT_TR_NOOP("Huawei Solar Inverter"), networkDeviceInfo.macAddress() + " - " + networkDeviceInfo.address().toString());
qCDebug(dcHuawei()) << "Discovered:" << descriptor.title() << descriptor.description();
// Filter for mac manufacturer
if (!networkDeviceInfo.macAddressManufacturer().contains("Huawei"))
// Check if we already have set up this device
Things existingThings = myThings().filterByParam(huaweiFusionSolarInverterThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
if (existingThings.count() == 1) {
qCDebug(dcHuawei()) << "This inverter already exists in the system:" << networkDeviceInfo;
descriptor.setThingId(existingThings.first()->id());
}
ParamList params;
params << Param(huaweiFusionSolarInverterThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
// Note: if we discover also the port and modbusaddress, we must fill them in from the discovery here, for now everywhere the defaults...
descriptor.setParams(params);
info->addThingDescriptor(descriptor);
}
info->finish(Thing::ThingErrorNoError);
});
// Start the discovery process
discovery->startDiscovery();
} else if (info->thingClassId() == huaweiRtuInverterThingClassId) {
qCDebug(dcHuawei()) << "Discovering modbus RTU resources...";
if (hardwareManager()->modbusRtuResource()->modbusRtuMasters().isEmpty()) {
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("No Modbus RTU interface available. Please set up a Modbus RTU interface first."));
return;
}
uint slaveAddress = info->params().paramValue(huaweiRtuInverterThingSlaveAddressParamTypeId).toUInt();
if (slaveAddress > 254 || slaveAddress == 0) {
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The Modbus slave address must be a value between 1 and 254."));
return;
}
foreach (ModbusRtuMaster *modbusMaster, hardwareManager()->modbusRtuResource()->modbusRtuMasters()) {
qCDebug(dcHuawei()) << "Found RTU master resource" << modbusMaster << "connected" << modbusMaster->connected();
if (!modbusMaster->connected())
continue;
QString title;
if (networkDeviceInfo.hostName().isEmpty()) {
title = "Huawei FusionSolar";
} else {
title = networkDeviceInfo.hostName() + " (" + networkDeviceInfo.address().toString() + ")";
}
QString description;
if (networkDeviceInfo.macAddressManufacturer().isEmpty()) {
description = networkDeviceInfo.macAddress();
} else {
description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")";
}
ThingDescriptor descriptor(huaweiInverterThingClassId, title, description);
ThingDescriptor descriptor(info->thingClassId(), "Huawei Inverter", QString::number(slaveAddress) + " " + modbusMaster->serialPort());
ParamList params;
params << Param(huaweiInverterThingIpAddressParamTypeId, networkDeviceInfo.address().toString());
params << Param(huaweiInverterThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
params << Param(huaweiRtuInverterThingSlaveAddressParamTypeId, slaveAddress);
params << Param(huaweiRtuInverterThingModbusMasterUuidParamTypeId, modbusMaster->modbusUuid());
descriptor.setParams(params);
// Check if we already have set up this device
Things existingThings = myThings().filterByParam(huaweiInverterThingMacAddressParamTypeId, networkDeviceInfo.macAddress());
if (existingThings.count() == 1) {
qCDebug(dcHuawei()) << "This connection already exists in the system:" << networkDeviceInfo;
descriptor.setThingId(existingThings.first()->id());
}
info->addThingDescriptor(descriptor);
}
info->finish(Thing::ThingErrorNoError);
});
}
void IntegrationPluginHuawei::startMonitoringAutoThings()
{
}
}
void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
@ -101,50 +112,113 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
Thing *thing = info->thing();
qCDebug(dcHuawei()) << "Setup" << thing << thing->params();
if (thing->thingClassId() == huaweiInverterThingClassId) {
QHostAddress hostAddress = QHostAddress(thing->paramValue(huaweiInverterThingIpAddressParamTypeId).toString());
if (hostAddress.isNull()) {
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("No IP address given"));
if (thing->thingClassId() == huaweiFusionSolarInverterThingClassId) {
// Handle reconfigure
if (m_connections.contains(thing))
delete m_connections.take(thing);
if (m_monitors.contains(thing))
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
// Make sure we have a valid mac address, otherwise no monitor and not auto searching is possible
MacAddress macAddress = MacAddress(thing->paramValue(huaweiFusionSolarInverterThingMacAddressParamTypeId).toString());
if (macAddress.isNull()) {
qCWarning(dcHuawei()) << "Failed to set up Fusion Solar because the MAC address is not valid:" << thing->paramValue(huaweiFusionSolarInverterThingMacAddressParamTypeId).toString() << macAddress.toString();
info->finish(Thing::ThingErrorInvalidParameter, QT_TR_NOOP("The MAC address is not vaild. Please reconfigure the device to fix this."));
return;
}
uint port = thing->paramValue(huaweiInverterThingPortParamTypeId).toUInt();
quint16 slaveId = thing->paramValue(huaweiInverterThingSlaveIdParamTypeId).toUInt();
// Create a monitor so we always get the correct IP in the network and see if the device is reachable without polling on our own
NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(macAddress);
m_monitors.insert(thing, monitor);
connect(info, &ThingSetupInfo::aborted, monitor, [=](){
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
});
HuaweiFusionSolar *connection = new HuaweiFusionSolar(hostAddress, port, slaveId, this);
// Continue with setup only if we know that the network device is reachable
if (monitor->reachable()) {
setupFusionSolar(info);
} else {
// otherwise wait until we reach the networkdevice before setting up the device
qCDebug(dcHuawei()) << "Network device" << thing->name() << "is not reachable yet. Continue with the setup once reachable.";
connect(monitor, &NetworkDeviceMonitor::reachableChanged, info, [=](bool reachable){
if (reachable) {
qCDebug(dcHuawei()) << "Network device" << thing->name() << "is now reachable. Continue with the setup...";
setupFusionSolar(info);
}
});
}
connect(connection, &HuaweiFusionSolar::connectionStateChanged, this, [this, thing, connection](bool status){
qCDebug(dcHuawei()) << "Connected changed to" << status << "for" << thing;
if (status) {
return;
}
if (thing->thingClassId() == huaweiRtuInverterThingClassId) {
uint address = thing->paramValue(huaweiRtuInverterThingSlaveAddressParamTypeId).toUInt();
if (address > 254 || address == 0) {
qCWarning(dcHuawei()) << "Setup failed, slave address is not valid" << address;
info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus address not valid. It must be a value between 1 and 254."));
return;
}
QUuid uuid = thing->paramValue(huaweiRtuInverterThingModbusMasterUuidParamTypeId).toUuid();
if (!hardwareManager()->modbusRtuResource()->hasModbusRtuMaster(uuid)) {
qCWarning(dcHuawei()) << "Setup failed, hardware manager not available";
info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("The Modbus RTU resource is not available."));
return;
}
if (m_rtuConnections.contains(thing)) {
qCDebug(dcHuawei()) << "Already have a Huawei connection for this thing. Cleaning up old connection and initializing new one...";
delete m_rtuConnections.take(thing);
}
ModbusRtuMaster *rtuMaster = hardwareManager()->modbusRtuResource()->getModbusRtuMaster(uuid);
HuaweiModbusRtuConnection *connection = new HuaweiModbusRtuConnection(rtuMaster, address, this);
connect(connection, &HuaweiModbusRtuConnection::reachableChanged, thing, [this, thing, connection](bool reachable){
qCDebug(dcHuawei()) << thing->name() << "reachable changed" << reachable;
if (reachable) {
// 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);
thing->setStateValue("connected", false);
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
childThing->setStateValue("connected", false);
}
}
});
connect(connection, &HuaweiFusionSolar::inverterActivePowerChanged, this, [thing](float inverterActivePower){
connect(connection, &HuaweiModbusRtuConnection::initializationFinished, thing, [this, thing, connection](bool success){
if (success) {
thing->setStateValue("connected", true);
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
childThing->setStateValue("connected", true);
}
connection->update();
}
});
connect(connection, &HuaweiModbusRtuConnection::inverterActivePowerChanged, thing, [thing](float inverterActivePower){
qCDebug(dcHuawei()) << "Inverter power changed" << inverterActivePower * -1000.0 << "W";
thing->setStateValue(huaweiInverterCurrentPowerStateTypeId, inverterActivePower * -1000.0);
thing->setStateValue(huaweiRtuInverterCurrentPowerStateTypeId, inverterActivePower * -1000.0);
});
connect(connection, &HuaweiFusionSolar::inverterDeviceStatusChanged, this, [thing](HuaweiFusionSolar::InverterDeviceStatus inverterDeviceStatus){
connect(connection, &HuaweiModbusRtuConnection::inverterDeviceStatusChanged, thing, [thing](HuaweiModbusRtuConnection::InverterDeviceStatus inverterDeviceStatus){
qCDebug(dcHuawei()) << "Inverter device status changed" << inverterDeviceStatus;
Q_UNUSED(thing)
});
connect(connection, &HuaweiFusionSolar::inverterEnergyProducedChanged, this, [thing](float inverterEnergyProduced){
connect(connection, &HuaweiModbusRtuConnection::inverterEnergyProducedChanged, thing, [thing](float inverterEnergyProduced){
qCDebug(dcHuawei()) << "Inverter total energy produced changed" << inverterEnergyProduced << "kWh";
thing->setStateValue(huaweiInverterTotalEnergyProducedStateTypeId, inverterEnergyProduced);
thing->setStateValue(huaweiRtuInverterTotalEnergyProducedStateTypeId, inverterEnergyProduced);
});
// Meter
connect(connection, &HuaweiFusionSolar::powerMeterActivePowerChanged, this, [this, thing](qint32 powerMeterActivePower){
connect(connection, &HuaweiModbusRtuConnection::powerMeterActivePowerChanged, thing, [this, thing](qint32 powerMeterActivePower){
Things meterThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiMeterThingClassId);
if (!meterThings.isEmpty()) {
qCDebug(dcHuawei()) << "Meter power changed" << powerMeterActivePower << "W";
@ -154,7 +228,264 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
});
// Battery 1
connect(connection, &HuaweiFusionSolar::lunaBattery1StatusChanged, this, [this, thing](HuaweiFusionSolar::BatteryDeviceStatus lunaBattery1Status){
connect(connection, &HuaweiModbusRtuConnection::lunaBattery1StatusChanged, thing, [this, thing](HuaweiModbusRtuConnection::BatteryDeviceStatus lunaBattery1Status){
qCDebug(dcHuawei()) << "Battery 1 status changed" << lunaBattery1Status;
if (lunaBattery1Status != HuaweiModbusRtuConnection::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, &HuaweiModbusRtuConnection::lunaBattery1PowerChanged, thing, [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, &HuaweiModbusRtuConnection::lunaBattery1SocChanged, thing, [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, &HuaweiModbusRtuConnection::lunaBattery2StatusChanged, thing, [this, thing](HuaweiModbusRtuConnection::BatteryDeviceStatus lunaBattery1Status){
qCDebug(dcHuawei()) << "Battery 2 status changed" << lunaBattery1Status;
if (lunaBattery1Status != HuaweiModbusRtuConnection::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, &HuaweiModbusRtuConnection::lunaBattery2PowerChanged, thing, [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, &HuaweiModbusRtuConnection::lunaBattery2SocChanged, thing, [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_rtuConnections.insert(thing, connection);
connection->initialize();
// FIXME: make async and check if this is really a huawei
info->finish(Thing::ThingErrorNoError);
return;
}
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());
}
return;
}
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());
}
return;
}
}
void IntegrationPluginHuawei::postSetupThing(Thing *thing)
{
if (thing->thingClassId() == huaweiFusionSolarInverterThingClassId || thing->thingClassId() == huaweiRtuInverterThingClassId) {
if (!m_pluginTimer) {
qCDebug(dcHuawei()) << "Starting plugin timer...";
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(2);
connect(m_pluginTimer, &PluginTimer::timeout, this, [this] {
foreach(HuaweiFusionSolar *connection, m_connections) {
if (connection->connected()) {
connection->update();
}
}
foreach(HuaweiModbusRtuConnection *connection, m_rtuConnections) {
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()));
}
}
}
void IntegrationPluginHuawei::thingRemoved(Thing *thing)
{
if (m_monitors.contains(thing)) {
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
}
if (m_connections.contains(thing)) {
HuaweiFusionSolar *connection = m_connections.take(thing);
connection->disconnectDevice();
delete connection;
}
if (m_rtuConnections.contains(thing)) {
m_rtuConnections.take(thing)->deleteLater();
}
if (myThings().isEmpty() && m_pluginTimer) {
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
m_pluginTimer = nullptr;
}
}
void IntegrationPluginHuawei::setupFusionSolar(ThingSetupInfo *info)
{
Thing *thing = info->thing();
NetworkDeviceMonitor *monitor = m_monitors.value(thing);
uint port = thing->paramValue(huaweiFusionSolarInverterThingPortParamTypeId).toUInt();
quint16 slaveId = thing->paramValue(huaweiFusionSolarInverterThingSlaveIdParamTypeId).toUInt();
qCDebug(dcHuawei()) << "Setup connection to fusion solar dongle" << monitor->networkDeviceInfo().address().toString() << port << slaveId;
HuaweiFusionSolar *connection = new HuaweiFusionSolar(monitor->networkDeviceInfo().address(), port, slaveId, this);
connect(info, &ThingSetupInfo::aborted, connection, &HuaweiFusionSolar::deleteLater);
connect(connection, &HuaweiFusionSolar::reachableChanged, info, [=](bool reachable){
if (!reachable) {
qCWarning(dcHuawei()) << "Connection init finished with errors" << thing->name() << connection->hostAddress().toString();
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(monitor);
connection->disconnectDevice();
connection->deleteLater();
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Could not initialize the communication with the SmartDongle."));
return;
}
m_connections.insert(thing, connection);
info->finish(Thing::ThingErrorNoError);
qCDebug(dcHuawei()) << "Setup huawei fusion solar smart dongle finished successfully" << monitor->networkDeviceInfo().address().toString() << port << slaveId;
// Set connected state
thing->setStateValue("connected", true);
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
childThing->setStateValue("connected", true);
}
connect(connection, &HuaweiFusionSolar::reachableChanged, thing, [=](bool reachable){
qCDebug(dcHuawei()) << "Reachable changed to" << reachable << "for" << thing;
thing->setStateValue("connected", reachable);
foreach (Thing *childThing, myThings().filterByParentId(thing->id())) {
childThing->setStateValue("connected", reachable);
}
});
connect(monitor, &NetworkDeviceMonitor::reachableChanged, thing, [=](bool reachable){
if (!thing->setupComplete())
return;
qCDebug(dcHuawei()) << "Network device monitor for" << thing->name() << (reachable ? "is now reachable" : "is not reachable any more" );
if (reachable && !thing->stateValue("connected").toBool()) {
connection->setHostAddress(monitor->networkDeviceInfo().address());
connection->connectDevice();
} else if (!reachable) {
// Note: We disable autoreconnect explicitly and we will
// connect the device once the monitor says it is reachable again
connection->disconnectDevice();
}
});
connect(connection, &HuaweiFusionSolar::inverterActivePowerChanged, thing, [thing](float inverterActivePower){
thing->setStateValue(huaweiFusionSolarInverterCurrentPowerStateTypeId, inverterActivePower * -1000.0);
});
connect(connection, &HuaweiFusionSolar::inverterDeviceStatusReadFinished, thing, [thing](HuaweiFusionSolar::InverterDeviceStatus inverterDeviceStatus){
qCDebug(dcHuawei()) << "Inverter device status changed" << inverterDeviceStatus;
Q_UNUSED(thing)
});
connect(connection, &HuaweiFusionSolar::inverterEnergyProducedReadFinished, thing, [thing](float inverterEnergyProduced){
qCDebug(dcHuawei()) << "Inverter total energy produced changed" << inverterEnergyProduced << "kWh";
thing->setStateValue(huaweiFusionSolarInverterTotalEnergyProducedStateTypeId, inverterEnergyProduced);
});
// Meter
connect(connection, &HuaweiFusionSolar::powerMeterActivePowerReadFinished, thing, [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::lunaBattery1StatusReadFinished, thing, [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
@ -177,7 +508,7 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
}
});
connect(connection, &HuaweiFusionSolar::lunaBattery1PowerChanged, this, [this, thing](qint32 lunaBattery1Power){
connect(connection, &HuaweiFusionSolar::lunaBattery1PowerReadFinished, thing, [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()) {
@ -192,7 +523,7 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
}
});
connect(connection, &HuaweiFusionSolar::lunaBattery1SocChanged, this, [this, thing](float lunaBattery1Soc){
connect(connection, &HuaweiFusionSolar::lunaBattery1SocReadFinished, thing, [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()) {
@ -202,9 +533,9 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
});
// Battery 2
connect(connection, &HuaweiFusionSolar::lunaBattery2StatusChanged, this, [this, thing](HuaweiFusionSolar::BatteryDeviceStatus lunaBattery1Status){
qCDebug(dcHuawei()) << "Battery 2 status changed" << lunaBattery1Status;
if (lunaBattery1Status != HuaweiFusionSolar::BatteryDeviceStatusOffline) {
connect(connection, &HuaweiFusionSolar::lunaBattery2StatusReadFinished, thing, [this, thing](HuaweiFusionSolar::BatteryDeviceStatus lunaBattery2Status){
qCDebug(dcHuawei()) << "Battery 2 status changed" << lunaBattery2Status;
if (lunaBattery2Status != HuaweiFusionSolar::BatteryDeviceStatusOffline) {
// Check if w have to create the energy storage
Things batteryThings = myThings().filterByParentId(thing->id()).filterByThingClassId(huaweiBatteryThingClassId);
bool alreadySetUp = false;
@ -225,7 +556,7 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
}
});
connect(connection, &HuaweiFusionSolar::lunaBattery2PowerChanged, this, [this, thing](qint32 lunaBattery2Power){
connect(connection, &HuaweiFusionSolar::lunaBattery2PowerReadFinished, thing, [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()) {
@ -241,7 +572,7 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
}
});
connect(connection, &HuaweiFusionSolar::lunaBattery2SocChanged, this, [this, thing](float lunaBattery2Soc){
connect(connection, &HuaweiFusionSolar::lunaBattery2SocReadFinished, thing, [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()) {
@ -249,74 +580,8 @@ void IntegrationPluginHuawei::setupThing(ThingSetupInfo *info)
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());
}
}
connection->connectDevice();
}
void IntegrationPluginHuawei::postSetupThing(Thing *thing)
{
if (thing->thingClassId() == huaweiInverterThingClassId) {
if (!m_pluginTimer) {
qCDebug(dcHuawei()) << "Starting plugin timer...";
m_pluginTimer = hardwareManager()->pluginTimerManager()->registerTimer(5);
connect(m_pluginTimer, &PluginTimer::timeout, this, [this] {
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()));
}
}
}
void IntegrationPluginHuawei::thingRemoved(Thing *thing)
{
if (thing->thingClassId() == huaweiInverterThingClassId && m_connections.contains(thing)) {
m_connections.take(thing)->deleteLater();
}
if (myThings().isEmpty() && m_pluginTimer) {
hardwareManager()->pluginTimerManager()->unregisterTimer(m_pluginTimer);
m_pluginTimer = nullptr;
}
}
void IntegrationPluginHuawei::executeAction(ThingActionInfo *info)
{
info->finish(Thing::ThingErrorNoError);
}

View File

@ -33,8 +33,11 @@
#include <plugintimer.h>
#include <integrations/integrationplugin.h>
#include <network/networkdevicediscovery.h>
#include "extern-plugininfo.h"
#include "huaweifusionsolar.h"
#include "huaweimodbusrtuconnection.h"
class IntegrationPluginHuawei: public IntegrationPlugin
{
@ -47,16 +50,18 @@ public:
explicit IntegrationPluginHuawei();
void discoverThings(ThingDiscoveryInfo *info) override;
void startMonitoringAutoThings() override;
void setupThing(ThingSetupInfo *info) override;
void postSetupThing(Thing *thing) override;
void thingRemoved(Thing *thing) override;
void executeAction(ThingActionInfo *info) override;
private:
PluginTimer *m_pluginTimer = nullptr;
QHash<Thing *, HuaweiFusionSolar *> m_connections;
QHash<Thing *, NetworkDeviceMonitor *> m_monitors;
QHash<Thing *, HuaweiFusionSolar *> m_connections;
QHash<Thing *, HuaweiModbusRtuConnection *> m_rtuConnections;
void setupFusionSolar(ThingSetupInfo *info);
};
#endif // INTEGRATIONPLUGINHUAWEI_H

View File

@ -9,21 +9,13 @@
"id": "f654c99d-a286-4abb-b33e-1a71843d8da0",
"thingClasses": [
{
"name": "huaweiInverter",
"displayName": "Huawei FusionSolar Inverter",
"name": "huaweiFusionSolarInverter",
"displayName": "Huawei FusionSolar Inverter (SmartDongle)",
"id": "87e75ee0-d544-457b-add3-bd4e58160fcd",
"createMethods": ["discovery", "user"],
"interfaces": ["solarinverter", "connectable"],
"providedInterfaces": [ "solarinverter", "energymeter", "energystorage"],
"paramTypes": [
{
"id": "d93371db-0954-4dcd-a1a5-6881b78cb0ea",
"name": "ipAddress",
"displayName": "IP address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": "127.0.0.1"
},
{
"id": "93517bff-1928-4c4a-8207-5fe596c86eba",
"name":"macAddress",
@ -65,7 +57,7 @@
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": false
"cached": true
},
{
"id": "e97fe328-6ca4-4fe4-86f7-fee6e9e406a5",
@ -74,7 +66,74 @@
"displayNameEvent": "AC energy changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0.00
"defaultValue": 0.00,
"cached": true
}
],
"actionTypes": [ ]
},
{
"name": "huaweiRtuInverter",
"displayName": "Huawei FusionSolar Inverter (Modbus RTU)",
"id": "77558007-5076-4ca6-bd46-169f215c3e29",
"createMethods": ["discovery"],
"interfaces": ["solarinverter", "connectable"],
"providedInterfaces": [ "solarinverter", "energymeter", "energystorage"],
"discoveryParamTypes": [
{
"id": "93a4d3a8-c7d0-470b-b6e3-d8fc43b8e8d0",
"name": "slaveAddress",
"displayName": "Slave address",
"type": "int",
"defaultValue": 1
}
],
"paramTypes": [
{
"id": "5c7b28b1-3691-452e-8f49-d80ae7bcbe2c",
"name": "modbusMasterUuid",
"displayName": "Modbus RTU master",
"type": "QUuid",
"defaultValue": "",
"readOnly": true
},
{
"id": "de06f027-7940-4c45-9c96-30930ac2796d",
"name": "slaveAddress",
"displayName": "Modbus slave address",
"type": "uint",
"defaultValue": 1
}
],
"stateTypes": [
{
"id": "191ffa22-de6f-4325-8698-56b817f78df5",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "6064d90e-1b6b-40fd-9da0-6ebc713efb7d",
"name": "currentPower",
"displayName": "Active power",
"displayNameEvent": "Active power changed",
"type": "double",
"unit": "Watt",
"defaultValue": 0,
"cached": false
},
{
"id": "49b92919-301c-4ff7-ae63-0c1a2184e3f4",
"name": "totalEnergyProduced",
"displayName": "AC energy",
"displayNameEvent": "AC energy changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0.00,
"cached": true
}
],
"actionTypes": [ ]
@ -114,7 +173,8 @@
"displayNameEvent": "AC energy changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0.00
"defaultValue": 0.00,
"cached": true
},
{
"id": "2cf8d885-37f7-478f-819e-c4e20f2dbe01",
@ -123,7 +183,8 @@
"displayNameEvent": "Total real energy imported changed",
"type": "double",
"unit": "KiloWattHour",
"defaultValue": 0.00
"defaultValue": 0.00,
"cached": true
},
{
"id": "af48ff45-11ba-401e-a812-bb1db0896449",
@ -233,7 +294,7 @@
"displayName": "Huawei Battery",
"id": "40104aac-0456-475d-8bd6-18f946597d96",
"createMethods": ["auto"],
"interfaces": [ "energystorage", "connectable"],
"interfaces": [ "battery", "connectable"],
"paramTypes": [
{
"id": "019287a6-c593-45a8-9695-2e1ad8e81c32",
@ -280,7 +341,7 @@
"type": "double",
"unit": "Watt",
"defaultValue": 0.00,
"cached": false
"cached": true
},
{
"id": "3eed974a-0acb-4e38-bcb8-0e3f6fbfd51a",

View File

@ -4,397 +4,259 @@
<context>
<name>Huawei</name>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="104"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="107"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="110"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="113"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="64"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="67"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="70"/>
<source>AC energy</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: totalEnergyProduced, ID: {759554dd-74c5-4836-9792-96e02eb816f0})
<extracomment>The name of the StateType ({759554dd-74c5-4836-9792-96e02eb816f0}) of ThingClass huaweiMeter
----------
The name of the StateType ({759554dd-74c5-4836-9792-96e02eb816f0}) of ThingClass huaweiMeter
The name of the StateType ({49b92919-301c-4ff7-ae63-0c1a2184e3f4}) of ThingClass huaweiRtuInverter
----------
The name of the ParamType (ThingClass: huaweiInverter, EventType: totalEnergyProduced, ID: {e97fe328-6ca4-4fe4-86f7-fee6e9e406a5})
----------
The name of the StateType ({e97fe328-6ca4-4fe4-86f7-fee6e9e406a5}) of ThingClass huaweiInverter</extracomment>
The name of the StateType ({e97fe328-6ca4-4fe4-86f7-fee6e9e406a5}) of ThingClass huaweiFusionSolarInverter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="116"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="119"/>
<source>AC energy changed</source>
<extracomment>The name of the EventType ({759554dd-74c5-4836-9792-96e02eb816f0}) of ThingClass huaweiMeter
----------
The name of the EventType ({e97fe328-6ca4-4fe4-86f7-fee6e9e406a5}) of ThingClass huaweiInverter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="122"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="125"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="73"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="76"/>
<source>Active power</source>
<extracomment>The name of the ParamType (ThingClass: huaweiInverter, EventType: currentPower, ID: {f463f36e-69f9-4614-b690-664ce22d76e0})
<extracomment>The name of the StateType ({6064d90e-1b6b-40fd-9da0-6ebc713efb7d}) of ThingClass huaweiRtuInverter
----------
The name of the StateType ({f463f36e-69f9-4614-b690-664ce22d76e0}) of ThingClass huaweiInverter</extracomment>
The name of the StateType ({f463f36e-69f9-4614-b690-664ce22d76e0}) of ThingClass huaweiFusionSolarInverter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="128"/>
<source>Active power changed</source>
<extracomment>The name of the EventType ({f463f36e-69f9-4614-b690-664ce22d76e0}) of ThingClass huaweiInverter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="131"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="134"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="79"/>
<source>Battery critical</source>
<extracomment>The name of the ParamType (ThingClass: huaweiBattery, EventType: batteryCritical, ID: {223ddf60-ff73-4acf-b8ab-6337aeb972e8})
----------
The name of the StateType ({223ddf60-ff73-4acf-b8ab-6337aeb972e8}) of ThingClass huaweiBattery</extracomment>
<extracomment>The name of the StateType ({223ddf60-ff73-4acf-b8ab-6337aeb972e8}) of ThingClass huaweiBattery</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="137"/>
<source>Battery critical changed</source>
<extracomment>The name of the EventType ({223ddf60-ff73-4acf-b8ab-6337aeb972e8}) of ThingClass huaweiBattery</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="140"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="143"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="82"/>
<source>Battery level</source>
<extracomment>The name of the ParamType (ThingClass: huaweiBattery, EventType: batteryLevel, ID: {94d609bf-1f67-47c4-a23d-2fd14e7c0b21})
----------
The name of the StateType ({94d609bf-1f67-47c4-a23d-2fd14e7c0b21}) of ThingClass huaweiBattery</extracomment>
<extracomment>The name of the StateType ({94d609bf-1f67-47c4-a23d-2fd14e7c0b21}) of ThingClass huaweiBattery</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="146"/>
<source>Battery level changed</source>
<extracomment>The name of the EventType ({94d609bf-1f67-47c4-a23d-2fd14e7c0b21}) of ThingClass huaweiBattery</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="149"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="152"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="85"/>
<source>Capacity</source>
<extracomment>The name of the ParamType (ThingClass: huaweiBattery, EventType: capacity, ID: {3eed974a-0acb-4e38-bcb8-0e3f6fbfd51a})
----------
The name of the StateType ({3eed974a-0acb-4e38-bcb8-0e3f6fbfd51a}) of ThingClass huaweiBattery</extracomment>
<extracomment>The name of the StateType ({3eed974a-0acb-4e38-bcb8-0e3f6fbfd51a}) of ThingClass huaweiBattery</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="155"/>
<source>Capacity changed</source>
<extracomment>The name of the EventType ({3eed974a-0acb-4e38-bcb8-0e3f6fbfd51a}) of ThingClass huaweiBattery</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="158"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="161"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="88"/>
<source>Charging state</source>
<extracomment>The name of the ParamType (ThingClass: huaweiBattery, EventType: chargingState, ID: {d9604513-d5a9-463a-ad18-d2f259a7a99d})
----------
The name of the StateType ({d9604513-d5a9-463a-ad18-d2f259a7a99d}) of ThingClass huaweiBattery</extracomment>
<extracomment>The name of the StateType ({d9604513-d5a9-463a-ad18-d2f259a7a99d}) of ThingClass huaweiBattery</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="164"/>
<source>Charging state changed</source>
<extracomment>The name of the EventType ({d9604513-d5a9-463a-ad18-d2f259a7a99d}) of ThingClass huaweiBattery</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="167"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="170"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="173"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="176"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="179"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="182"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="91"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="94"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="97"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="100"/>
<source>Connected</source>
<extracomment>The name of the ParamType (ThingClass: huaweiBattery, EventType: connected, ID: {917bc284-9d43-430c-a8c3-642d302448e6})
----------
The name of the StateType ({917bc284-9d43-430c-a8c3-642d302448e6}) of ThingClass huaweiBattery
----------
The name of the ParamType (ThingClass: huaweiMeter, EventType: connected, ID: {720ece7a-b0b3-4fa3-9f52-6f23042624a5})
<extracomment>The name of the StateType ({917bc284-9d43-430c-a8c3-642d302448e6}) of ThingClass huaweiBattery
----------
The name of the StateType ({720ece7a-b0b3-4fa3-9f52-6f23042624a5}) of ThingClass huaweiMeter
----------
The name of the ParamType (ThingClass: huaweiInverter, EventType: connected, ID: {a51f0ceb-bd2c-444f-8b39-77cf8a4e1bc6})
The name of the StateType ({191ffa22-de6f-4325-8698-56b817f78df5}) of ThingClass huaweiRtuInverter
----------
The name of the StateType ({a51f0ceb-bd2c-444f-8b39-77cf8a4e1bc6}) of ThingClass huaweiInverter</extracomment>
The name of the StateType ({a51f0ceb-bd2c-444f-8b39-77cf8a4e1bc6}) of ThingClass huaweiFusionSolarInverter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="185"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="188"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="191"/>
<source>Connected changed</source>
<extracomment>The name of the EventType ({917bc284-9d43-430c-a8c3-642d302448e6}) of ThingClass huaweiBattery
----------
The name of the EventType ({720ece7a-b0b3-4fa3-9f52-6f23042624a5}) of ThingClass huaweiMeter
----------
The name of the EventType ({a51f0ceb-bd2c-444f-8b39-77cf8a4e1bc6}) of ThingClass huaweiInverter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="194"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="197"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="103"/>
<source>Current power phase A</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: currentPowerPhaseA, ID: {ecc03e9b-88b1-424f-a179-66bbdebaaea9})
----------
The name of the StateType ({ecc03e9b-88b1-424f-a179-66bbdebaaea9}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({ecc03e9b-88b1-424f-a179-66bbdebaaea9}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="200"/>
<source>Current power phase A changed</source>
<extracomment>The name of the EventType ({ecc03e9b-88b1-424f-a179-66bbdebaaea9}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="203"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="206"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="106"/>
<source>Current power phase B</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: currentPowerPhaseB, ID: {7971cbde-b2ea-4474-b68a-71e040ed3b1d})
----------
The name of the StateType ({7971cbde-b2ea-4474-b68a-71e040ed3b1d}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({7971cbde-b2ea-4474-b68a-71e040ed3b1d}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="209"/>
<source>Current power phase B changed</source>
<extracomment>The name of the EventType ({7971cbde-b2ea-4474-b68a-71e040ed3b1d}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="212"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="215"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="109"/>
<source>Current power phase C</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: currentPowerPhaseC, ID: {7ca21c4d-6763-49e4-a056-4c9c76923971})
----------
The name of the StateType ({7ca21c4d-6763-49e4-a056-4c9c76923971}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({7ca21c4d-6763-49e4-a056-4c9c76923971}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="218"/>
<source>Current power phase C changed</source>
<extracomment>The name of the EventType ({7ca21c4d-6763-49e4-a056-4c9c76923971}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="221"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="224"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="112"/>
<source>Frequency</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: frequency, ID: {1e2252be-80b3-4e9a-97f7-105d6d1c50f9})
----------
The name of the StateType ({1e2252be-80b3-4e9a-97f7-105d6d1c50f9}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({1e2252be-80b3-4e9a-97f7-105d6d1c50f9}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="227"/>
<source>Frequency changed</source>
<extracomment>The name of the EventType ({1e2252be-80b3-4e9a-97f7-105d6d1c50f9}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="230"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="115"/>
<source>Huawei</source>
<extracomment>The name of the vendor ({f654c99d-a286-4abb-b33e-1a71843d8da0})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="233"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="118"/>
<source>Huawei Battery</source>
<extracomment>The name of the ThingClass ({40104aac-0456-475d-8bd6-18f946597d96})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="236"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="121"/>
<source>Huawei FusionSolar</source>
<extracomment>The name of the plugin Huawei ({fc3e4509-47f3-4622-9bc4-0a90fe2b6262})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="239"/>
<source>Huawei FusionSolar Inverter</source>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="124"/>
<source>Huawei FusionSolar Inverter ((Modbus RTU)</source>
<extracomment>The name of the ThingClass ({77558007-5076-4ca6-bd46-169f215c3e29})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="127"/>
<source>Huawei FusionSolar Inverter (SmartDongle)</source>
<extracomment>The name of the ThingClass ({87e75ee0-d544-457b-add3-bd4e58160fcd})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="242"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="130"/>
<source>Huawei Meter</source>
<extracomment>The name of the ThingClass ({529c2a19-ca6a-4df2-b56e-3fb2673fa95f})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="245"/>
<source>IP address</source>
<extracomment>The name of the ParamType (ThingClass: huaweiInverter, Type: thing, ID: {d93371db-0954-4dcd-a1a5-6881b78cb0ea})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="248"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="133"/>
<source>MAC address</source>
<extracomment>The name of the ParamType (ThingClass: huaweiInverter, Type: thing, ID: {93517bff-1928-4c4a-8207-5fe596c86eba})</extracomment>
<extracomment>The name of the ParamType (ThingClass: huaweiFusionSolarInverter, Type: thing, ID: {93517bff-1928-4c4a-8207-5fe596c86eba})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="251"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="254"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="136"/>
<source>Modbus RTU master</source>
<extracomment>The name of the ParamType (ThingClass: huaweiRtuInverter, Type: thing, ID: {5c7b28b1-3691-452e-8f49-d80ae7bcbe2c})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="139"/>
<source>Modbus slave address</source>
<extracomment>The name of the ParamType (ThingClass: huaweiRtuInverter, Type: thing, ID: {de06f027-7940-4c45-9c96-30930ac2796d})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="142"/>
<source>Phase A current</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: currentPhaseA, ID: {af48ff45-11ba-401e-a812-bb1db0896449})
----------
The name of the StateType ({af48ff45-11ba-401e-a812-bb1db0896449}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({af48ff45-11ba-401e-a812-bb1db0896449}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="257"/>
<source>Phase A current changed</source>
<extracomment>The name of the EventType ({af48ff45-11ba-401e-a812-bb1db0896449}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="260"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="263"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="145"/>
<source>Phase B current</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: currentPhaseB, ID: {fb5082e4-a2d8-4958-a47d-e80928795ece})
----------
The name of the StateType ({fb5082e4-a2d8-4958-a47d-e80928795ece}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({fb5082e4-a2d8-4958-a47d-e80928795ece}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="266"/>
<source>Phase B current changed</source>
<extracomment>The name of the EventType ({fb5082e4-a2d8-4958-a47d-e80928795ece}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="269"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="272"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="148"/>
<source>Phase C current</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: currentPhaseC, ID: {bdd9aa8b-93fe-4b6b-8a31-08e99d85a06c})
----------
The name of the StateType ({bdd9aa8b-93fe-4b6b-8a31-08e99d85a06c}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({bdd9aa8b-93fe-4b6b-8a31-08e99d85a06c}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="275"/>
<source>Phase C current changed</source>
<extracomment>The name of the EventType ({bdd9aa8b-93fe-4b6b-8a31-08e99d85a06c}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="278"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="151"/>
<source>Port</source>
<extracomment>The name of the ParamType (ThingClass: huaweiInverter, Type: thing, ID: {55c4ec99-6342-4309-84a8-d1615f19b2e8})</extracomment>
<extracomment>The name of the ParamType (ThingClass: huaweiFusionSolarInverter, Type: thing, ID: {55c4ec99-6342-4309-84a8-d1615f19b2e8})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="281"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="154"/>
<source>Slave ID</source>
<extracomment>The name of the ParamType (ThingClass: huaweiInverter, Type: thing, ID: {aa6e978e-a16b-4722-8330-e706f3c7c21e})</extracomment>
<extracomment>The name of the ParamType (ThingClass: huaweiFusionSolarInverter, Type: thing, ID: {aa6e978e-a16b-4722-8330-e706f3c7c21e})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="284"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="287"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="157"/>
<source>Slave address</source>
<extracomment>The name of the ParamType (ThingClass: huaweiRtuInverter, Type: discovery, ID: {93a4d3a8-c7d0-470b-b6e3-d8fc43b8e8d0})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="160"/>
<source>Total real energy imported</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: totalEnergyConsumed, ID: {2cf8d885-37f7-478f-819e-c4e20f2dbe01})
----------
The name of the StateType ({2cf8d885-37f7-478f-819e-c4e20f2dbe01}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({2cf8d885-37f7-478f-819e-c4e20f2dbe01}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="290"/>
<source>Total real energy imported changed</source>
<extracomment>The name of the EventType ({2cf8d885-37f7-478f-819e-c4e20f2dbe01}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="293"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="296"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="299"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="302"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="163"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="166"/>
<source>Total real power</source>
<extracomment>The name of the ParamType (ThingClass: huaweiBattery, EventType: currentPower, ID: {53ca1f8a-0267-40aa-b563-762a943c8f55})
----------
The name of the StateType ({53ca1f8a-0267-40aa-b563-762a943c8f55}) of ThingClass huaweiBattery
----------
The name of the ParamType (ThingClass: huaweiMeter, EventType: currentPower, ID: {f480dc82-68e2-44e2-839c-df38b9c10310})
<extracomment>The name of the StateType ({53ca1f8a-0267-40aa-b563-762a943c8f55}) of ThingClass huaweiBattery
----------
The name of the StateType ({f480dc82-68e2-44e2-839c-df38b9c10310}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="305"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="308"/>
<source>Total real power changed</source>
<extracomment>The name of the EventType ({53ca1f8a-0267-40aa-b563-762a943c8f55}) of ThingClass huaweiBattery
----------
The name of the EventType ({f480dc82-68e2-44e2-839c-df38b9c10310}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="311"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="169"/>
<source>Unit</source>
<extracomment>The name of the ParamType (ThingClass: huaweiBattery, Type: thing, ID: {019287a6-c593-45a8-9695-2e1ad8e81c32})</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="314"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="317"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="172"/>
<source>Voltage phase A</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: voltagePhaseA, ID: {ea5d7924-19a8-415c-aeeb-e04ce08bed33})
----------
The name of the StateType ({ea5d7924-19a8-415c-aeeb-e04ce08bed33}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({ea5d7924-19a8-415c-aeeb-e04ce08bed33}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="320"/>
<source>Voltage phase A changed</source>
<extracomment>The name of the EventType ({ea5d7924-19a8-415c-aeeb-e04ce08bed33}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="323"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="326"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="175"/>
<source>Voltage phase B</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: voltagePhaseB, ID: {f15856d1-645f-4d34-89a7-c1585ca329cc})
----------
The name of the StateType ({f15856d1-645f-4d34-89a7-c1585ca329cc}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({f15856d1-645f-4d34-89a7-c1585ca329cc}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="329"/>
<source>Voltage phase B changed</source>
<extracomment>The name of the EventType ({f15856d1-645f-4d34-89a7-c1585ca329cc}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="332"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="335"/>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="178"/>
<source>Voltage phase C</source>
<extracomment>The name of the ParamType (ThingClass: huaweiMeter, EventType: voltagePhaseC, ID: {aafb5de4-caa1-4a90-8149-cdf85ae5dc2b})
----------
The name of the StateType ({aafb5de4-caa1-4a90-8149-cdf85ae5dc2b}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../../build-nymea-plugins-modbus-Desktop-Debug/huawei/plugininfo.h" line="338"/>
<source>Voltage phase C changed</source>
<extracomment>The name of the EventType ({aafb5de4-caa1-4a90-8149-cdf85ae5dc2b}) of ThingClass huaweiMeter</extracomment>
<extracomment>The name of the StateType ({aafb5de4-caa1-4a90-8149-cdf85ae5dc2b}) of ThingClass huaweiMeter</extracomment>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>IntegrationPluginHuawei</name>
<message>
<location filename="../integrationpluginhuawei.cpp" line="46"/>
<location filename="../integrationpluginhuawei.cpp" line="48"/>
<source>The network device discovery is not available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginhuawei.cpp" line="107"/>
<source>No IP address given</source>
<location filename="../integrationpluginhuawei.cpp" line="57"/>
<source>Huawei Solar Inverter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginhuawei.cpp" line="83"/>
<source>No Modbus RTU interface available. Please set up a Modbus RTU interface first.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginhuawei.cpp" line="89"/>
<source>The Modbus slave address must be a value between 1 and 254.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginhuawei.cpp" line="129"/>
<source>The MAC address is not vaild. Please reconfigure the device to fix this.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginhuawei.cpp" line="162"/>
<source>The Modbus address not valid. It must be a value between 1 and 254.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginhuawei.cpp" line="169"/>
<source>The Modbus RTU resource is not available.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../integrationpluginhuawei.cpp" line="424"/>
<source>Could not initialize the communication with the SmartDongle.</source>
<translation type="unfinished"></translation>
</message>
</context>

View File

@ -278,35 +278,35 @@ def writeTestReachabilityImplementationsRtu(fileDescriptor, className, registerD
writeLine(fileDescriptor, 'void %s::testReachability()' % (className))
writeLine(fileDescriptor, '{')
writeLine(fileDescriptor, ' if (m_testRechableReply)')
writeLine(fileDescriptor, ' if (m_checkRechableReply)')
writeLine(fileDescriptor, ' return;')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' // Try to read the check reachability register %s in order to verify if the communication is working or not.' % checkReachableRegister['id'])
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Test reachability by reading \\"%s\\" register:" << %s << "size:" << %s;' % (className, checkReachableRegister['description'], checkReachableRegister['address'], checkReachableRegister['size']))
writeLine(fileDescriptor, ' m_testRechableReply = read%s();' % (propertyName[0].upper() + propertyName[1:]))
writeLine(fileDescriptor, ' if (!m_testRechableReply) {')
writeLine(fileDescriptor, ' m_checkRechableReply = read%s();' % (propertyName[0].upper() + propertyName[1:]))
writeLine(fileDescriptor, ' if (!m_checkRechableReply) {')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "Error occurred verifying reachability by reading \\"%s\\" register";' % (className, checkReachableRegister['description']))
writeLine(fileDescriptor, ' emit checkReachabilityFailed();')
writeLine(fileDescriptor, ' onReachabilityCheckFailed();')
writeLine(fileDescriptor, ' return;')
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' if (m_testRechableReply->isFinished()) {')
writeLine(fileDescriptor, ' m_testRechableReply = nullptr;')
writeLine(fileDescriptor, ' emit checkReachabilityFailed();')
writeLine(fileDescriptor, ' if (m_checkRechableReply->isFinished()) {')
writeLine(fileDescriptor, ' m_checkRechableReply = nullptr;')
writeLine(fileDescriptor, ' onReachabilityCheckFailed();')
writeLine(fileDescriptor, ' return;')
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' connect(m_testRechableReply, &ModbusRtuReply::finished, this, [this](){')
writeLine(fileDescriptor, ' connect(m_checkRechableReply, &ModbusRtuReply::finished, this, [this](){')
writeLine(fileDescriptor, ' // Note: we don\'t care about the result here, only the error')
writeLine(fileDescriptor, ' handleModbusError(m_testRechableReply->error());')
writeLine(fileDescriptor, ' if (m_testRechableReply->error() != ModbusRtuReply::NoError)')
writeLine(fileDescriptor, ' emit checkReachabilityFailed();')
writeLine(fileDescriptor, ' handleModbusError(m_checkRechableReply->error());')
writeLine(fileDescriptor, ' if (m_checkRechableReply->error() != ModbusRtuReply::NoError)')
writeLine(fileDescriptor, ' onReachabilityCheckFailed();')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' m_testRechableReply = nullptr;')
writeLine(fileDescriptor, ' m_checkRechableReply = nullptr;')
writeLine(fileDescriptor, ' });')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' connect(m_testRechableReply, &ModbusRtuReply::errorOccurred, this, [this] (ModbusRtuReply::Error error){')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "ModbusRtu reply error occurred while verifying reachability by reading \\"%s\\" register" << error << m_testRechableReply->errorString();' % (className, checkReachableRegister['description']))
writeLine(fileDescriptor, ' connect(m_checkRechableReply, &ModbusRtuReply::errorOccurred, this, [this] (ModbusRtuReply::Error error){')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "ModbusRtu reply error occurred while verifying reachability by reading \\"%s\\" register" << error << m_checkRechableReply->errorString();' % (className, checkReachableRegister['description']))
writeLine(fileDescriptor, ' });')
writeLine(fileDescriptor, '}')
writeLine(fileDescriptor)

View File

@ -280,36 +280,36 @@ def writeTestReachabilityImplementationsTcp(fileDescriptor, className, registerD
writeLine(fileDescriptor, 'void %s::testReachability()' % (className))
writeLine(fileDescriptor, '{')
writeLine(fileDescriptor, ' if (m_testRechableReply)')
writeLine(fileDescriptor, ' if (m_checkRechableReply)')
writeLine(fileDescriptor, ' return;')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' // Try to read the check reachability register %s in order to verify if the communication is working or not.' % checkReachableRegister['id'])
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Test reachability by reading \\"%s\\" register:" << %s << "size:" << %s;' % (className, checkReachableRegister['description'], checkReachableRegister['address'], checkReachableRegister['size']))
writeLine(fileDescriptor, ' m_testRechableReply = read%s();' % (propertyName[0].upper() + propertyName[1:]))
writeLine(fileDescriptor, ' if (!m_testRechableReply) {')
writeLine(fileDescriptor, ' m_checkRechableReply = read%s();' % (propertyName[0].upper() + propertyName[1:]))
writeLine(fileDescriptor, ' if (!m_checkRechableReply) {')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "Error occurred verifying reachability by reading \\"%s\\" register";' % (className, checkReachableRegister['description']))
writeLine(fileDescriptor, ' emit checkReachabilityFailed();')
writeLine(fileDescriptor, ' onReachabilityCheckFailed();')
writeLine(fileDescriptor, ' return;')
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' if (m_testRechableReply->isFinished()) {')
writeLine(fileDescriptor, ' m_testRechableReply->deleteLater(); // Broadcast reply returns immediatly')
writeLine(fileDescriptor, ' m_testRechableReply = nullptr;')
writeLine(fileDescriptor, ' emit checkReachabilityFailed();')
writeLine(fileDescriptor, ' if (m_checkRechableReply->isFinished()) {')
writeLine(fileDescriptor, ' m_checkRechableReply->deleteLater(); // Broadcast reply returns immediatly')
writeLine(fileDescriptor, ' m_checkRechableReply = nullptr;')
writeLine(fileDescriptor, ' onReachabilityCheckFailed();')
writeLine(fileDescriptor, ' return;')
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' connect(m_testRechableReply, &QModbusReply::finished, this, [this](){')
writeLine(fileDescriptor, ' handleModbusError(m_testRechableReply->error());')
writeLine(fileDescriptor, ' if (m_testRechableReply->error() != QModbusDevice::NoError)')
writeLine(fileDescriptor, ' emit checkReachabilityFailed();')
writeLine(fileDescriptor, ' connect(m_checkRechableReply, &QModbusReply::finished, this, [this](){')
writeLine(fileDescriptor, ' handleModbusError(m_checkRechableReply->error());')
writeLine(fileDescriptor, ' if (m_checkRechableReply->error() != QModbusDevice::NoError)')
writeLine(fileDescriptor, ' onReachabilityCheckFailed();')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' m_testRechableReply->deleteLater();')
writeLine(fileDescriptor, ' m_testRechableReply = nullptr;')
writeLine(fileDescriptor, ' m_checkRechableReply->deleteLater();')
writeLine(fileDescriptor, ' m_checkRechableReply = nullptr;')
writeLine(fileDescriptor, ' });')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' connect(m_testRechableReply, &QModbusReply::errorOccurred, this, [this] (QModbusDevice::Error error){')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "Modbus reply error occurred while verifying reachability by reading \\"%s\\" register" << error << m_testRechableReply->errorString();' % (className, checkReachableRegister['description']))
writeLine(fileDescriptor, ' connect(m_checkRechableReply, &QModbusReply::errorOccurred, this, [this] (QModbusDevice::Error error){')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "Modbus reply error occurred while verifying reachability by reading \\"%s\\" register" << error << m_checkRechableReply->errorString();' % (className, checkReachableRegister['description']))
writeLine(fileDescriptor, ' });')
writeLine(fileDescriptor, '}')
writeLine(fileDescriptor)

View File

@ -71,6 +71,9 @@ def writeTcpHeaderFile():
writeLine(headerFile, ' ModbusDataUtils::ByteOrder endianness() const;')
writeLine(headerFile, ' void setEndianness(ModbusDataUtils::ByteOrder endianness);')
writeLine(headerFile)
writeLine(headerFile, ' uint checkReachableRetries() const;')
writeLine(headerFile, ' void setCheckReachableRetries(uint checkReachableRetries);')
writeLine(headerFile)
# Write registers get method declarations
writePropertyGetSetMethodDeclarationsTcp(headerFile, registerJson['registers'])
@ -108,6 +111,7 @@ def writeTcpHeaderFile():
writeLine(headerFile, 'signals:')
writeLine(headerFile, ' void reachableChanged(bool reachable);')
writeLine(headerFile, ' void checkReachabilityFailed();')
writeLine(headerFile, ' void checkReachableRetriesChanged(uint checkReachableRetries);')
writeLine(headerFile)
writeLine(headerFile, ' void initializationFinished(bool success);')
writeLine(headerFile, ' void updateFinished();')
@ -145,7 +149,11 @@ def writeTcpHeaderFile():
writeLine(headerFile, 'private:')
writeLine(headerFile, ' ModbusDataUtils::ByteOrder m_endianness = ModbusDataUtils::ByteOrder%s;' % endianness)
writeLine(headerFile, ' quint16 m_slaveId = 1;')
writeLine(headerFile)
writeLine(headerFile, ' bool m_reachable = false;')
writeLine(headerFile, ' QModbusReply *m_checkRechableReply = nullptr;')
writeLine(headerFile, ' uint m_checkReachableRetries = 0;')
writeLine(headerFile, ' uint m_checkReachableRetriesCount = 0;')
writeLine(headerFile, ' bool m_communicationWorking = false;')
writeLine(headerFile, ' quint8 m_communicationFailedMax = %s;' % (errorLimitUntilNotReachable))
writeLine(headerFile, ' quint8 m_communicationFailedCounter = 0;')
@ -159,7 +167,7 @@ def writeTcpHeaderFile():
writeLine(headerFile)
writeLine(headerFile, ' void verifyUpdateFinished();')
writeLine(headerFile)
writeLine(headerFile, ' QModbusReply *m_testRechableReply = nullptr;')
writeLine(headerFile, ' void onReachabilityCheckFailed();')
writeLine(headerFile, ' void evaluateReachableState();')
# End of class
@ -180,6 +188,8 @@ def writeTcpSourceFile():
writeLine(sourceFile)
writeLine(sourceFile, '#include "%s"' % headerFileName)
writeLine(sourceFile, '#include <loggingcategories.h>')
writeLine(sourceFile, '#include <math.h>')
writeLine(sourceFile, '#include <QTimer>')
writeLine(sourceFile)
writeLine(sourceFile, 'NYMEA_LOGGING_CATEGORY(dc%s, "%s")' % (className, className))
writeLine(sourceFile)
@ -197,11 +207,13 @@ def writeTcpSourceFile():
writeLine(sourceFile, ' m_pendingUpdateReplies.clear();')
writeLine(sourceFile, ' m_communicationWorking = false;')
writeLine(sourceFile, ' m_communicationFailedCounter = 0;')
writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;')
writeLine(sourceFile, ' testReachability();')
writeLine(sourceFile, ' } else {')
writeLine(sourceFile, ' qCWarning(dc%s()) << "Modbus TCP connection diconnected from" << m_hostAddress.toString() << ". The connection is not reachable any more.";' % (className))
writeLine(sourceFile, ' m_communicationWorking = false;')
writeLine(sourceFile, ' m_communicationFailedCounter = 0;')
writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;')
writeLine(sourceFile, ' }')
writeLine(sourceFile)
writeLine(sourceFile, ' evaluateReachableState();')
@ -215,6 +227,22 @@ def writeTcpSourceFile():
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'uint %s::checkReachableRetries() const' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' return m_checkReachableRetries;')
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'void %s::setCheckReachableRetries(uint checkReachableRetries)' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' if (m_checkReachableRetries == checkReachableRetries)')
writeLine(sourceFile, ' return;')
writeLine(sourceFile)
writeLine(sourceFile, ' m_checkReachableRetries = checkReachableRetries;')
writeLine(sourceFile, ' emit checkReachableRetriesChanged(m_checkReachableRetries);')
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'ModbusDataUtils::ByteOrder %s::endianness() const' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' return m_endianness;')
@ -324,6 +352,21 @@ def writeTcpSourceFile():
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'void %s::onReachabilityCheckFailed()' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' m_checkReachableRetriesCount++;')
writeLine(sourceFile)
writeLine(sourceFile, ' if (m_checkReachableRetriesCount <= m_checkReachableRetries) {')
writeLine(sourceFile, ' qCDebug(dc%s()) << "Reachability test failed. Retry in on second" << m_checkReachableRetriesCount << "/" << m_checkReachableRetries;' % (className))
writeLine(sourceFile, ' QTimer::singleShot(1000, this, &%s::testReachability);' % (className))
writeLine(sourceFile, ' return;')
writeLine(sourceFile, ' }')
writeLine(sourceFile)
writeLine(sourceFile, ' // The test reachability method failed, not retrying any more')
writeLine(sourceFile, ' emit checkReachabilityFailed();')
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'void %s::evaluateReachableState()' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' bool reachable = m_communicationWorking && connected();')
@ -332,6 +375,7 @@ def writeTcpSourceFile():
writeLine(sourceFile)
writeLine(sourceFile, ' m_reachable = reachable;')
writeLine(sourceFile, ' emit reachableChanged(m_reachable);')
writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;')
writeLine(sourceFile, '}')
writeLine(sourceFile)
@ -395,6 +439,9 @@ def writeRtuHeaderFile():
writeLine(headerFile)
writeLine(headerFile, ' bool reachable() const;')
writeLine(headerFile)
writeLine(headerFile, ' uint checkReachableRetries() const;')
writeLine(headerFile, ' void setCheckReachableRetries(uint checkReachableRetries);')
writeLine(headerFile)
writeLine(headerFile, ' ModbusDataUtils::ByteOrder endianness() const;')
writeLine(headerFile, ' void setEndianness(ModbusDataUtils::ByteOrder endianness);')
writeLine(headerFile)
@ -433,6 +480,7 @@ def writeRtuHeaderFile():
writeLine(headerFile, 'signals:')
writeLine(headerFile, ' void reachableChanged(bool reachable);')
writeLine(headerFile, ' void checkReachabilityFailed();')
writeLine(headerFile, ' void checkReachableRetriesChanged(uint checkReachableRetries);')
writeLine(headerFile)
writeLine(headerFile, ' void initializationFinished(bool success);')
writeLine(headerFile, ' void updateFinished();')
@ -471,7 +519,11 @@ def writeRtuHeaderFile():
writeLine(headerFile, ' ModbusRtuMaster *m_modbusRtuMaster = nullptr;')
writeLine(headerFile, ' ModbusDataUtils::ByteOrder m_endianness = ModbusDataUtils::ByteOrder%s;' % endianness)
writeLine(headerFile, ' quint16 m_slaveId = 1;')
writeLine(headerFile)
writeLine(headerFile, ' bool m_reachable = false;')
writeLine(headerFile, ' ModbusRtuReply *m_checkRechableReply = nullptr;')
writeLine(headerFile, ' uint m_checkReachableRetries = 0;')
writeLine(headerFile, ' uint m_checkReachableRetriesCount = 0;')
writeLine(headerFile, ' bool m_communicationWorking = false;')
writeLine(headerFile, ' quint8 m_communicationFailedMax = %s;' % (errorLimitUntilNotReachable))
writeLine(headerFile, ' quint8 m_communicationFailedCounter = 0;')
@ -485,7 +537,7 @@ def writeRtuHeaderFile():
writeLine(headerFile)
writeLine(headerFile, ' void verifyUpdateFinished();')
writeLine(headerFile)
writeLine(headerFile, ' ModbusRtuReply *m_testRechableReply = nullptr;')
writeLine(headerFile, ' void onReachabilityCheckFailed();')
writeLine(headerFile, ' void evaluateReachableState();')
@ -508,6 +560,7 @@ def writeRtuSourceFile():
writeLine(sourceFile, '#include "%s"' % headerFileName)
writeLine(sourceFile, '#include <loggingcategories.h>')
writeLine(sourceFile, '#include <math.h>')
writeLine(sourceFile, '#include <QTimer>')
writeLine(sourceFile)
writeLine(sourceFile, 'NYMEA_LOGGING_CATEGORY(dc%s, "%s")' % (className, className))
writeLine(sourceFile)
@ -525,11 +578,13 @@ def writeRtuSourceFile():
writeLine(sourceFile, ' m_pendingUpdateReplies.clear();')
writeLine(sourceFile, ' m_communicationWorking = false;')
writeLine(sourceFile, ' m_communicationFailedCounter = 0;')
writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;')
writeLine(sourceFile, ' testReachability();')
writeLine(sourceFile, ' } else {')
writeLine(sourceFile, ' qCWarning(dc%s()) << "Modbus RTU resource" << m_modbusRtuMaster->serialPort() << "disconnected. The connection is not reachable any more.";' % (className))
writeLine(sourceFile, ' m_communicationWorking = false;')
writeLine(sourceFile, ' m_communicationFailedCounter = 0;')
writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;')
writeLine(sourceFile, ' }')
writeLine(sourceFile)
writeLine(sourceFile, ' evaluateReachableState();')
@ -554,6 +609,22 @@ def writeRtuSourceFile():
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'uint %s::checkReachableRetries() const' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' return m_checkReachableRetries;')
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'void %s::setCheckReachableRetries(uint checkReachableRetries)' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' if (m_checkReachableRetries == checkReachableRetries)')
writeLine(sourceFile, ' return;')
writeLine(sourceFile)
writeLine(sourceFile, ' m_checkReachableRetries = checkReachableRetries;')
writeLine(sourceFile, ' emit checkReachableRetriesChanged(m_checkReachableRetries);')
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'ModbusDataUtils::ByteOrder %s::endianness() const' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' return m_endianness;')
@ -664,6 +735,21 @@ def writeRtuSourceFile():
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'void %s::onReachabilityCheckFailed()' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' m_checkReachableRetriesCount++;')
writeLine(sourceFile)
writeLine(sourceFile, ' if (m_checkReachableRetriesCount <= m_checkReachableRetries) {')
writeLine(sourceFile, ' qCDebug(dc%s()) << "Reachability test failed. Retry in on second" << m_checkReachableRetriesCount << "/" << m_checkReachableRetries;' % (className))
writeLine(sourceFile, ' QTimer::singleShot(1000, this, &%s::testReachability);' % (className))
writeLine(sourceFile, ' return;')
writeLine(sourceFile, ' }')
writeLine(sourceFile)
writeLine(sourceFile, ' // The test reachability method failed, not retrying any more')
writeLine(sourceFile, ' emit checkReachabilityFailed();')
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'void %s::evaluateReachableState()' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' bool reachable = m_communicationWorking && m_modbusRtuMaster->connected();')
@ -672,6 +758,7 @@ def writeRtuSourceFile():
writeLine(sourceFile)
writeLine(sourceFile, ' m_reachable = reachable;')
writeLine(sourceFile, ' emit reachableChanged(m_reachable);')
writeLine(sourceFile, ' m_checkReachableRetriesCount = 0;')
writeLine(sourceFile, '}')
writeLine(sourceFile)