diff --git a/sunspec/README.md b/sunspec/README.md index f9ad5a5..11999ea 100644 --- a/sunspec/README.md +++ b/sunspec/README.md @@ -5,12 +5,19 @@ Connect to SunSpec devices over Modbus TCP ## Supported Things * SunSpec Inverter + * 101 & 111 Single phase inverter + * 102 & 112 Split phase inverter + * 103 & 113 Three phase inverter * SunSpec Meter -* SunSpec Storage + * Single phase meter + * Split phase meter + * 203 Three phase meter +* SunSpec Storage [124] ## Requirements * The package 'nymea-plugin-sunspec' must be installed. +* The SunSpec device must support SunSpec over modbus TCP ## More https://sunspec.org diff --git a/sunspec/integrationpluginsunspec.cpp b/sunspec/integrationpluginsunspec.cpp index 2da3264..cabaa94 100644 --- a/sunspec/integrationpluginsunspec.cpp +++ b/sunspec/integrationpluginsunspec.cpp @@ -49,13 +49,13 @@ void IntegrationPluginSunSpec::init() m_connectedStateTypeIds.insert(sunspecSplitPhaseMeterThingClassId, sunspecSplitPhaseMeterConnectedStateTypeId); m_connectedStateTypeIds.insert(sunspecThreePhaseMeterThingClassId, sunspecThreePhaseMeterConnectedStateTypeId); - m_mapIdParamTypeIds.insert(sunspecSinglePhaseInverterThingClassId, sunspecSinglePhaseInverterThingMapIdParamTypeId); - m_mapIdParamTypeIds.insert(sunspecSplitPhaseInverterThingClassId, sunspecSplitPhaseInverterThingMapIdParamTypeId); - m_mapIdParamTypeIds.insert(sunspecThreePhaseInverterThingClassId, sunspecThreePhaseInverterThingMapIdParamTypeId); - m_mapIdParamTypeIds.insert(sunspecStorageThingClassId, sunspecStorageThingMapIdParamTypeId); - m_mapIdParamTypeIds.insert(sunspecSinglePhaseMeterThingClassId, sunspecSinglePhaseMeterThingMapIdParamTypeId); - m_mapIdParamTypeIds.insert(sunspecSplitPhaseMeterThingClassId, sunspecSplitPhaseMeterThingMapIdParamTypeId); - m_mapIdParamTypeIds.insert(sunspecThreePhaseMeterThingClassId, sunspecThreePhaseMeterThingMapIdParamTypeId); + m_modelIdParamTypeIds.insert(sunspecSinglePhaseInverterThingClassId, sunspecSinglePhaseInverterThingModelIdParamTypeId); + m_modelIdParamTypeIds.insert(sunspecSplitPhaseInverterThingClassId, sunspecSplitPhaseInverterThingModelIdParamTypeId); + m_modelIdParamTypeIds.insert(sunspecThreePhaseInverterThingClassId, sunspecThreePhaseInverterThingModelIdParamTypeId); + m_modelIdParamTypeIds.insert(sunspecStorageThingClassId, sunspecStorageThingModelIdParamTypeId); + m_modelIdParamTypeIds.insert(sunspecSinglePhaseMeterThingClassId, sunspecSinglePhaseMeterThingModelIdParamTypeId); + m_modelIdParamTypeIds.insert(sunspecSplitPhaseMeterThingClassId, sunspecSplitPhaseMeterThingModelIdParamTypeId); + m_modelIdParamTypeIds.insert(sunspecThreePhaseMeterThingClassId, sunspecThreePhaseMeterThingModelIdParamTypeId); m_modbusAddressParamTypeIds.insert(sunspecSinglePhaseInverterThingClassId, sunspecSinglePhaseInverterThingModbusAddressParamTypeId); m_modbusAddressParamTypeIds.insert(sunspecSplitPhaseInverterThingClassId, sunspecSplitPhaseInverterThingModbusAddressParamTypeId); @@ -98,17 +98,17 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecConnectionThingIpAddressParamTypeId).toString()); int port = info->thing()->paramValue(sunspecConnectionThingPortParamTypeId).toInt(); - //int slaveId = info->thing()->paramValue(sunspecConnectionThingSlaveIdParamTypeId).toInt(); + int slaveId = info->thing()->paramValue(sunspecConnectionThingSlaveIdParamTypeId).toInt(); - SunSpec *sunSpec; if (m_sunSpecConnections.contains(thing->id())) { qCDebug(dcSunSpec()) << "Reconfigure SunSpec connection with new address" << address; - sunSpec = m_sunSpecConnections.value(thing->id()); - sunSpec->setHostAddress(address); - } else { - sunSpec = new SunSpec(address, port, this); - m_sunSpecConnections.insert(thing->id(), sunSpec); + m_sunSpecConnections.take(thing->id())->deleteLater(); } + SunSpec *sunSpec; + sunSpec = new SunSpec(address, port, slaveId, this); + sunSpec->setTimeout(configValue(sunSpecPluginTimeoutParamTypeId).toUInt()); + sunSpec->setNumberOfRetries(configValue(sunSpecPluginNumberOfRetriesParamTypeId).toUInt()); + m_sunSpecConnections.insert(thing->id(), sunSpec); if (!sunSpec->connectModbus()) { qCWarning(dcSunSpec()) << "Error connecting to SunSpec device"; @@ -127,9 +127,9 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) connect(sunSpec, &SunSpec::destroyed, [this, thing] { m_sunSpecConnections.remove(thing->id()); }); - connect(sunSpec, &SunSpec::foundModbusMap, this, &IntegrationPluginSunSpec::onFoundModbusMap); - connect(sunSpec, &SunSpec::modbusMapSearchFinished, this, &IntegrationPluginSunSpec::onModbusMapSearchFinished); - connect(sunSpec, &SunSpec::commonMapReceived, thing, [thing] (const QString &manufacturer, const QString &deviceModel, const QString &serielNumber) { + connect(sunSpec, &SunSpec::foundSunSpecModel, this, &IntegrationPluginSunSpec::onFoundSunSpecModel); + connect(sunSpec, &SunSpec::sunspecModelSearchFinished, this, &IntegrationPluginSunSpec::onSunSpecModelSearchFinished); + connect(sunSpec, &SunSpec::commonModelReceived, thing, [thing] (const QString &manufacturer, const QString &deviceModel, const QString &serielNumber) { thing->setStateValue(sunspecConnectionConnectedStateTypeId, true); thing->setStateValue(sunspecConnectionManufacturerStateTypeId, manufacturer); thing->setStateValue(sunspecConnectionDeviceModelStateTypeId, deviceModel); @@ -140,14 +140,14 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) thing->thingClassId() == sunspecSplitPhaseInverterThingClassId || thing->thingClassId() == sunspecSinglePhaseInverterThingClassId ) { - uint mapId = thing->paramValue(m_mapIdParamTypeIds.value(thing->thingClassId())).toInt(); + uint modelId = thing->paramValue(m_modelIdParamTypeIds.value(thing->thingClassId())).toInt(); int modbusAddress = thing->paramValue(m_modbusAddressParamTypeIds.value(thing->thingClassId())).toInt(); SunSpec *connection = m_sunSpecConnections.value(thing->parentId()); if (!connection) { qCWarning(dcSunSpec()) << "Could not find SunSpec connection"; return info->finish(Thing::ThingErrorHardwareNotAvailable); } - SunSpecInverter *sunSpecInverter = new SunSpecInverter(connection, SunSpec::BlockId(mapId), modbusAddress); + SunSpecInverter *sunSpecInverter = new SunSpecInverter(connection, SunSpec::ModelId(modelId), modbusAddress); sunSpecInverter->init(); connect(sunSpecInverter, &SunSpecInverter::initFinished, info, [this, sunSpecInverter, info] (bool success){ qCDebug(dcSunSpec()) << "Modbus Inverter init finished, success:" << success; @@ -167,14 +167,14 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) thing->thingClassId() == sunspecSplitPhaseMeterThingClassId || thing->thingClassId() == sunspecThreePhaseMeterThingClassId) { - uint mapId = thing->paramValue(m_mapIdParamTypeIds.value(thing->thingClassId())).toInt(); + uint modelId = thing->paramValue(m_modelIdParamTypeIds.value(thing->thingClassId())).toInt(); int modbusAddress = thing->paramValue(m_modbusAddressParamTypeIds.value(thing->thingClassId())).toInt(); SunSpec *connection = m_sunSpecConnections.value(thing->parentId()); if (!connection) { qCWarning(dcSunSpec()) << "Could not find SunSpec connection"; return info->finish(Thing::ThingErrorHardwareNotAvailable); } - SunSpecMeter *sunSpecMeter = new SunSpecMeter(connection, SunSpec::BlockId(mapId), modbusAddress); + SunSpecMeter *sunSpecMeter = new SunSpecMeter(connection, SunSpec::ModelId(modelId), modbusAddress); sunSpecMeter->init(); connect(sunSpecMeter, &SunSpecMeter::initFinished, info, [this, sunSpecMeter, info] (bool success){ qCDebug(dcSunSpec()) << "Modbus meter init finished, success:" << success; @@ -192,6 +192,28 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) } else if (info->thing()->thingClassId() == sunspecStorageThingClassId) { + uint modelId = thing->paramValue(m_modelIdParamTypeIds.value(thing->thingClassId())).toInt(); + int modbusAddress = thing->paramValue(m_modbusAddressParamTypeIds.value(thing->thingClassId())).toInt(); + SunSpec *connection = m_sunSpecConnections.value(thing->parentId()); + if (!connection) { + qCWarning(dcSunSpec()) << "Could not find SunSpec connection"; + return info->finish(Thing::ThingErrorHardwareNotAvailable); + } + SunSpecStorage *sunSpecStorage = new SunSpecStorage(connection, SunSpec::ModelId(modelId), modbusAddress); + sunSpecStorage->init(); + connect(sunSpecStorage, &SunSpecStorage::initFinished, info, [this, sunSpecStorage, info] (bool success){ + qCDebug(dcSunSpec()) << "Modbus storage init finished, success:" << success; + if (success) { + m_sunSpecStorages.insert(info->thing(), sunSpecStorage); + info->finish(Thing::ThingErrorNoError); + } else { + info->finish(Thing::ThingErrorHardwareNotAvailable); + } + }); + + connect(info, &ThingSetupInfo::aborted, sunSpecStorage, &SunSpecStorage::deleteLater); + connect(sunSpecStorage, &SunSpecStorage::destroyed, thing, [thing, this] {m_sunSpecStorages.remove(thing);}); + connect(sunSpecStorage, &SunSpecStorage::storageDataReceived, this, &IntegrationPluginSunSpec::onStorageDataReceived); } else { Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(info->thing()->thingClassId().toString()).toUtf8()); @@ -215,8 +237,8 @@ void IntegrationPluginSunSpec::postSetupThing(Thing *thing) qCDebug(dcSunSpec()) << "SunSpecConnection not found"; return; } - connection->readCommonMap(); - connection->findModbusMap(QList()); // Discover all maps, without filter + connection->readCommonModel(); + connection->findSunSpecModels(QList()); // Discover all models, without filter } else if (thing->thingClassId() == sunspecSinglePhaseInverterThingClassId || thing->thingClassId() == sunspecSplitPhaseInverterThingClassId || @@ -227,7 +249,7 @@ void IntegrationPluginSunSpec::postSetupThing(Thing *thing) qCDebug(dcSunSpec()) << "SunSpecInverter not found"; return; } - sunSpecInverter->getInverterMap(); + sunSpecInverter->getInverterModelDataBlock(); } else if (thing->thingClassId() == sunspecSinglePhaseMeterThingClassId || thing->thingClassId() == sunspecSplitPhaseMeterThingClassId || @@ -238,7 +260,7 @@ void IntegrationPluginSunSpec::postSetupThing(Thing *thing) qCDebug(dcSunSpec()) << "SunSpecMeter not found"; return; } - sunSpecMeter->getMeterMap(); + sunSpecMeter->getMeterModelDataBlock(); } else if (thing->thingClassId() == sunspecStorageThingClassId) { SunSpecStorage *sunSpecStorage = m_sunSpecStorages.value(thing); @@ -246,7 +268,7 @@ void IntegrationPluginSunSpec::postSetupThing(Thing *thing) qCDebug(dcSunSpec()) << "SunSpecStorage not found"; return; } - sunSpecStorage->getStorageMap(); + sunSpecStorage->getStorageModelDataBlock(); } else { Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); @@ -373,11 +395,11 @@ void IntegrationPluginSunSpec::executeAction(ThingActionInfo *info) } } -bool IntegrationPluginSunSpec::checkIfThingExists(uint mapId, uint modbusAddress) +bool IntegrationPluginSunSpec::checkIfThingExists(uint modelId, uint modbusAddress) { Q_FOREACH(Thing *thing, myThings()) { - if (thing->paramValue(m_mapIdParamTypeIds.value(thing->thingClassId())).toUInt() == mapId && - thing->paramValue(m_modbusAddressParamTypeIds.value(thing->thingClassId())).toUInt() == modbusAddress) { + if (thing->paramValue(m_modelIdParamTypeIds.value(thing->thingClassId())).toUInt() == modelId && + thing->paramValue(m_modbusAddressParamTypeIds.value(thing->thingClassId())).toUInt() == modbusAddress) { return true; } } @@ -387,16 +409,16 @@ bool IntegrationPluginSunSpec::checkIfThingExists(uint mapId, uint modbusAddress void IntegrationPluginSunSpec::onRefreshTimer() { foreach (SunSpec *connection, m_sunSpecConnections) { - connection->readCommonMap(); //check connection + connection->readCommonModel(); //check connection } foreach (SunSpecInverter *inverter, m_sunSpecInverters) { - inverter->getInverterMap(); + inverter->getInverterModelDataBlock(); } foreach (SunSpecMeter *meter, m_sunSpecMeters) { - meter->getMeterMap(); + meter->getMeterModelDataBlock(); } foreach (SunSpecStorage *storage, m_sunSpecStorages) { - storage->getStorageMap(); + storage->getStorageModelDataBlock(); } } @@ -410,6 +432,18 @@ void IntegrationPluginSunSpec::onPluginConfigurationChanged(const ParamTypeId &p m_refreshTimer->stop(); m_refreshTimer->startTimer(refreshTime); } + } else if (paramTypeId == sunSpecPluginNumberOfRetriesParamTypeId) { + qCDebug(dcSunSpec()) << "Updating number of retires" << value.toUInt(); + Q_FOREACH(SunSpec *connection, m_sunSpecConnections) { + connection->setNumberOfRetries(value.toUInt()); + } + + } else if (paramTypeId == sunSpecPluginTimeoutParamTypeId) { + qCDebug(dcSunSpec()) << "Updating timeout" << value.toUInt() << "[ms]"; + Q_FOREACH(SunSpec *connection, m_sunSpecConnections) { + connection->setTimeout(value.toUInt()); + } + } else { qCWarning(dcSunSpec()) << "Unknown plugin configuration" << paramTypeId << "Value" << value; } @@ -431,23 +465,7 @@ void IntegrationPluginSunSpec::onConnectionStateChanged(bool status) } } -void IntegrationPluginSunSpec::onMapHeaderReceived(uint modbusAddress, SunSpec::BlockId mapId, uint mapLength) -{ - qCDebug(dcSunSpec()) << "On map header received" << modbusAddress << mapId << mapLength; -} - -void IntegrationPluginSunSpec::onMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector data) -{ - Q_UNUSED(data) - SunSpec *connection = static_cast(sender()); - Thing *thing = myThings().findById(m_sunSpecConnections.key(connection)); - if (!thing) - return; - - qCDebug(dcSunSpec()) << "On map received" << mapId << "Map length" << mapLength << "Thing:" << thing->name(); -} - -void IntegrationPluginSunSpec::onFoundModbusMap(SunSpec::BlockId mapId, int modbusStartRegister) +void IntegrationPluginSunSpec::onFoundSunSpecModel(SunSpec::ModelId modelId, int modbusStartRegister) { SunSpec *connection = static_cast(sender()); Thing *thing = myThings().findById(m_sunSpecConnections.key(connection)); @@ -456,71 +474,71 @@ void IntegrationPluginSunSpec::onFoundModbusMap(SunSpec::BlockId mapId, int modb return; } - qCDebug(dcSunSpec()) << "On map received" << mapId << "Map length" << modbusStartRegister << "Thing:" << thing->name(); - if (checkIfThingExists(mapId, modbusStartRegister)) { + qCDebug(dcSunSpec()) << "On model received" << modelId << "length" << modbusStartRegister << "Thing:" << thing->name(); + if (checkIfThingExists(modelId, modbusStartRegister)) { return; } QString model = thing->stateValue(sunspecConnectionDeviceModelStateTypeId).toString(); - switch (mapId) { - case SunSpec::BlockId::BlockIdInverterSinglePhase: - case SunSpec::BlockId::BlockIdInverterSinglePhaseFloat: { + switch (modelId) { + case SunSpec::ModelId::ModelIdInverterSinglePhase: + case SunSpec::ModelId::ModelIdInverterSinglePhaseFloat: { ThingDescriptor descriptor(sunspecSinglePhaseInverterThingClassId, model+tr(" single phase inverter"), "", thing->id()); ParamList params; - params.append(Param(sunspecSinglePhaseInverterThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecSinglePhaseInverterThingModelIdParamTypeId, modelId)); params.append(Param(sunspecSinglePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); descriptor.setParams(params); emit autoThingsAppeared({descriptor}); } break; - case SunSpec::BlockId::BlockIdInverterSplitPhase: - case SunSpec::BlockId::BlockIdInverterSplitPhaseFloat: { + case SunSpec::ModelId::ModelIdInverterSplitPhase: + case SunSpec::ModelId::ModelIdInverterSplitPhaseFloat: { ThingDescriptor descriptor(sunspecSplitPhaseInverterThingClassId, model+tr(" split phase inverter"), "", thing->id()); ParamList params; - params.append(Param(sunspecSplitPhaseInverterThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecSplitPhaseInverterThingModelIdParamTypeId, modelId)); params.append(Param(sunspecSplitPhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); descriptor.setParams(params); emit autoThingsAppeared({descriptor}); } break; - case SunSpec::BlockId::BlockIdInverterThreePhase: - case SunSpec::BlockId::BlockIdInverterThreePhaseFloat: { + case SunSpec::ModelId::ModelIdInverterThreePhase: + case SunSpec::ModelId::ModelIdInverterThreePhaseFloat: { ThingDescriptor descriptor(sunspecThreePhaseInverterThingClassId, model+tr(" three phase inverter"), "", thing->id()); ParamList params; - params.append(Param(sunspecThreePhaseInverterThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecThreePhaseInverterThingModelIdParamTypeId, modelId)); params.append(Param(sunspecThreePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); descriptor.setParams(params); emit autoThingsAppeared({descriptor}); } break; - case SunSpec::BlockIdSinglePhaseMeter: - case SunSpec::BlockIdSinglePhaseMeterFloat: { + case SunSpec::ModelIdSinglePhaseMeter: + case SunSpec::ModelIdSinglePhaseMeterFloat: { ThingDescriptor descriptor(sunspecSinglePhaseMeterThingClassId, model+tr(" Meter"), "", thing->id()); ParamList params; - params.append(Param(sunspecSinglePhaseMeterThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecSinglePhaseMeterThingModelIdParamTypeId, modelId)); params.append(Param(sunspecSinglePhaseMeterThingModbusAddressParamTypeId, modbusStartRegister)); descriptor.setParams(params); emit autoThingsAppeared({descriptor}); } break; - case SunSpec::BlockIdSplitSinglePhaseMeter: - case SunSpec::BlockIdSplitSinglePhaseMeterFloat: { + case SunSpec::ModelIdSplitSinglePhaseMeter: + case SunSpec::ModelIdSplitSinglePhaseMeterFloat: { ThingDescriptor descriptor(sunspecSplitPhaseMeterThingClassId, model+tr(" Meter"), "", thing->id()); ParamList params; - params.append(Param(sunspecSplitPhaseMeterThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecSplitPhaseMeterThingModelIdParamTypeId, modelId)); params.append(Param(sunspecSplitPhaseMeterThingModbusAddressParamTypeId, modbusStartRegister)); descriptor.setParams(params); emit autoThingsAppeared({descriptor}); } break; - case SunSpec::BlockIdWyeConnectThreePhaseMeterFloat: - case SunSpec::BlockIdDeltaConnectThreePhaseMeterFloat: { + case SunSpec::ModelIdWyeConnectThreePhaseMeterFloat: + case SunSpec::ModelIdDeltaConnectThreePhaseMeterFloat: { ThingDescriptor descriptor(sunspecThreePhaseMeterThingClassId, model+" Meter", "", thing->id()); ParamList params; - params.append(Param(sunspecThreePhaseMeterThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecThreePhaseMeterThingModelIdParamTypeId, modelId)); params.append(Param(sunspecThreePhaseMeterThingModbusAddressParamTypeId, modbusStartRegister)); descriptor.setParams(params); emit autoThingsAppeared({descriptor}); } break; - case SunSpec::BlockIdStorage: { + case SunSpec::ModelIdStorage: { ThingDescriptor descriptor(sunspecStorageThingClassId, model+" Storage", "", thing->id()); ParamList params; - params.append(Param(sunspecStorageThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecStorageThingModelIdParamTypeId, modelId)); params.append(Param(sunspecThreePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); descriptor.setParams(params); emit autoThingsAppeared({descriptor}); @@ -530,7 +548,7 @@ void IntegrationPluginSunSpec::onFoundModbusMap(SunSpec::BlockId mapId, int modb } } -void IntegrationPluginSunSpec::onModbusMapSearchFinished(const QHash &mapIds) +void IntegrationPluginSunSpec::onSunSpecModelSearchFinished(const QHash &modelIds) { SunSpec *connection = static_cast(sender()); Thing *thing = myThings().findById(m_sunSpecConnections.key(connection)); @@ -539,7 +557,7 @@ void IntegrationPluginSunSpec::onModbusMapSearchFinished(const QHashsetStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), true); thing->setStateValue(sunspecStorageStorageStateStateTypeId, storageData.chargingState); } @@ -688,8 +707,8 @@ void IntegrationPluginSunSpec::onMeterDataReceived(const SunSpecMeter::MeterData SunSpecMeter *meter = static_cast(sender()); Thing *thing = m_sunSpecMeters.key(meter); - if(!thing) { + if (!thing) { return; } - //thing->setStateValue(sunspecMeterStorageStateStateTypeId, meterData.event); + thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), true); } diff --git a/sunspec/integrationpluginsunspec.h b/sunspec/integrationpluginsunspec.h index 41ff90b..fedbb21 100644 --- a/sunspec/integrationpluginsunspec.h +++ b/sunspec/integrationpluginsunspec.h @@ -59,7 +59,7 @@ public: void executeAction(ThingActionInfo *info) override; private: - QHash m_mapIdParamTypeIds; + QHash m_modelIdParamTypeIds; QHash m_modbusAddressParamTypeIds; QHash m_connectedStateTypeIds; @@ -77,7 +77,7 @@ private: QHash m_sunSpecStorages; QHash m_sunSpecMeters; - bool checkIfThingExists(uint mapId, uint modbusAddress); + bool checkIfThingExists(uint modelId, uint modbusAddress); private slots: void onRefreshTimer(); @@ -86,11 +86,8 @@ private slots: void onConnectionStateChanged(bool status); - void onMapHeaderReceived(uint modbusAddress, SunSpec::BlockId mapId, uint mapLength); - void onMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector data); - - void onFoundModbusMap(SunSpec::BlockId mapId, int modbusStartRegister); - void onModbusMapSearchFinished(const QHash &mapIds); + void onFoundSunSpecModel(SunSpec::ModelId modelId, int modbusStartRegister); + void onSunSpecModelSearchFinished(const QHash &modelIds); void onWriteRequestExecuted(QUuid requestId, bool success); void onWriteRequestError(QUuid requestId, const QString &error); @@ -99,6 +96,5 @@ private slots: void onStorageDataReceived(const SunSpecStorage::StorageData &storageData); void onMeterDataReceived(const SunSpecMeter::MeterData &meterData); }; - #endif // INTEGRATIONPLUGINSUNSPEC_H diff --git a/sunspec/integrationpluginsunspec.json b/sunspec/integrationpluginsunspec.json index d719318..bb081cc 100644 --- a/sunspec/integrationpluginsunspec.json +++ b/sunspec/integrationpluginsunspec.json @@ -37,7 +37,7 @@ "thingClasses": [ { "name": "sunspecConnection", - "displayName": "SunSpec Device", + "displayName": "SunSpec connection", "id": "f51853f3-8815-4cf3-b337-45cda1f3e6d5", "createMethods": [ "User" ], "interfaces": ["connectable"], @@ -109,8 +109,8 @@ "paramTypes": [ { "id": "41715d00-a947-4f43-a475-cea05790e01d", - "name":"mapId", - "displayName": "Map id", + "name":"modelId", + "displayName": "Model", "type": "int", "readOnly": true }, @@ -243,8 +243,8 @@ "paramTypes": [ { "id": "c42fb50e-210f-4b53-88eb-fa216e15f88f", - "name":"mapId", - "displayName": "Map id", + "name":"modelId", + "displayName": "Model", "type": "int", "readOnly": true }, @@ -404,8 +404,8 @@ "paramTypes": [ { "id": "8d5b2b58-ce46-406d-844e-f53136afcf09", - "name":"mapId", - "displayName": "Map id", + "name":"modelId", + "displayName": "Model", "type": "int", "readOnly": true }, @@ -583,8 +583,8 @@ "paramTypes": [ { "id": "7d6fcafb-c62e-4a21-aae2-f4041c487149", - "name":"mapId", - "displayName": "Map id", + "name":"modelId", + "displayName": "Model", "type": "int", "readOnly": true, "defaultValue": 0 @@ -682,8 +682,8 @@ "paramTypes": [ { "id": "89aeec6d-abeb-48b5-9594-214ad5db2d03", - "name":"mapId", - "displayName": "Map id", + "name":"modelId", + "displayName": "Model", "type": "int", "readOnly": true, "defaultValue": 0 @@ -808,8 +808,8 @@ "paramTypes": [ { "id": "a1960821-155c-4176-86fa-974429039182", - "name":"mapId", - "displayName": "Map id", + "name":"modelId", + "displayName": "Model", "type": "int", "readOnly": true, "defaultValue": 0 @@ -952,8 +952,8 @@ "paramTypes": [ { "id": "219beb96-b9fe-4dd2-a386-ecfbbab8786d", - "name":"mapId", - "displayName": "Map id", + "name":"modelId", + "displayName": "Model", "type": "int", "readOnly": true, "defaultValue": 0 diff --git a/sunspec/sunspec.cpp b/sunspec/sunspec.cpp index 2ebf713..0dd3401 100644 --- a/sunspec/sunspec.cpp +++ b/sunspec/sunspec.cpp @@ -32,10 +32,11 @@ #include "extern-plugininfo.h" #include -SunSpec::SunSpec(const QHostAddress &hostAddress, uint port, QObject *parent) : +SunSpec::SunSpec(const QHostAddress &hostAddress, uint port, uint slaveId, QObject *parent) : QObject(parent), m_hostAddress(hostAddress), - m_port(port) + m_port(port), + m_slaveId(slaveId) { qCDebug(dcSunSpec()) << "SunSpec: Creating SunSpec connection"; m_modbusTcpClient = new QModbusTcpClient(this); @@ -143,8 +144,8 @@ void SunSpec::findBaseRegister() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); if ((unit.value(0) << 16 | unit.value(1)) == 0x53756e53) { - //Well-known value. Uniquely identifies this as a SunSpec Modbus Map - qCDebug(dcSunSpec()) << "SunSpec: Found start of modbus map" << baseRegister; + //Well-known value. Uniquely identifies this as a SunSpec Modbus model + qCDebug(dcSunSpec()) << "SunSpec: Found start of modbus model" << baseRegister; m_baseRegister = baseRegister; emit foundBaseRegister(baseRegister); } else { @@ -162,9 +163,9 @@ void SunSpec::findBaseRegister() } } -void SunSpec::findModbusMap(const QList &ids, uint modbusAddressOffset) +void SunSpec::findSunSpecModels(const QList &ids, uint modbusAddressOffset) { - qCDebug(dcSunSpec()) << "SunSpec: Find modbus map. Start register" << m_baseRegister+modbusAddressOffset; + qCDebug(dcSunSpec()) << "SunSpec: Find modbus model. Start register" << m_baseRegister+modbusAddressOffset; QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_baseRegister+modbusAddressOffset, 2); if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) { @@ -175,23 +176,23 @@ void SunSpec::findModbusMap(const QList &ids, uint modbusAddressOffset) if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); uint modbusAddress = unit.startAddress(); - BlockId blockId = BlockId(unit.value(0)); - int blockLength = unit.value(1); - if (blockId == BlockIdEnd) { - qCDebug(dcSunSpec()) << "SunSpec: Block Id End"; - modbusMapSearchFinished(m_mapList); + ModelId modelId = ModelId(unit.value(0)); + int modelLength = unit.value(1); + if (modelId == ModelIdEnd) { + qCDebug(dcSunSpec()) << "SunSpec: Model Id End"; + sunspecModelSearchFinished(m_modelList); return; } - if (ids.isEmpty() || ids.contains(blockId)) { - // If ids is empty then emit all blocks - qCDebug(dcSunSpec()) << "SunSpec: Found block" << BlockId(blockId) << "with block length" << blockLength; - m_mapList.insert(BlockId(blockId), modbusAddress); - foundModbusMap(BlockId(blockId), modbusAddress); + if (ids.isEmpty() || ids.contains(modelId)) { + // If ids is empty then emit all models + qCDebug(dcSunSpec()) << "SunSpec: Found model" << ModelId(modelId) << "with length" << modelLength; + m_modelList.insert(ModelId(modelId), modbusAddress); + foundSunSpecModel(ModelId(modelId), modbusAddress); } - findModbusMap(ids, modbusAddress+2+blockLength-m_baseRegister); //read next block + findSunSpecModels(ids, modbusAddress+2+modelLength-m_baseRegister); //read next model } else { - qCWarning(dcSunSpec()) << "SunSpec: Find modbus map, read response error:" << reply->error(); + qCWarning(dcSunSpec()) << "SunSpec: Find modbus model, read response error:" << reply->error(); } }); } else { @@ -204,9 +205,9 @@ void SunSpec::findModbusMap(const QList &ids, uint modbusAddressOffset) } } -void SunSpec::readMapHeader(uint modbusAddress) +void SunSpec::readModelHeader(uint modbusAddress) { - qCDebug(dcSunSpec()) << "SunSpec: Read map header, modbus address:" << modbusAddress << "Slave ID" << m_slaveId; + qCDebug(dcSunSpec()) << "SunSpec: Read model header, modbus address:" << modbusAddress << "Slave ID" << m_slaveId; if (modbusAddress == 0 || modbusAddress == 40000 || modbusAddress == 50000) modbusAddress += 2; @@ -220,34 +221,34 @@ void SunSpec::readMapHeader(uint modbusAddress) if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); uint modbusAddress = unit.startAddress(); - BlockId mapId = BlockId(unit.value(0)); - int mapLength = unit.value(1); - qCDebug(dcSunSpec()) << "Received block header response. Map ID:" << mapId << "Map length" << mapLength; - mapHeaderReceived(modbusAddress, mapId, mapLength); + ModelId modelId = ModelId(unit.value(0)); + int length = unit.value(1); + qCDebug(dcSunSpec()) << "SunSpec: Received model header response. Model ID:" << modelId << "length" << length; + modelHeaderReceived(modbusAddress, modelId, length); } else { - qCWarning(dcSunSpec()) << "Read response error:" << reply->error(); + qCWarning(dcSunSpec()) << "SunSpec: Read response error:" << reply->error(); } }); connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error) { - qCWarning(dcSunSpec()) << "Modbus reply error:" << error; + qCWarning(dcSunSpec()) << "SunSpec: Modbus reply error:" << error; reply->finished(); // To make sure it will be deleted }); } else { - qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); + qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString(); delete reply; // broadcast replies return immediately return; } } else { - qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); + qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString(); return; } } -void SunSpec::readMap(uint modbusAddress, uint modelLength) +void SunSpec::readModelDataBlock(uint modbusAddress, uint length) { - qCDebug(dcSunSpec()) << "SunSpec: Read map, modbus address" << modbusAddress << "model length" << modelLength << ", Slave ID" << m_slaveId; + qCDebug(dcSunSpec()) << "SunSpec: Read model, modbus address" << modbusAddress << "length" << length << ", Slave ID" << m_slaveId; - QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, modbusAddress, modelLength+2); + QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, modbusAddress, length+2); if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) { if (!reply->isFinished()) { @@ -257,32 +258,32 @@ void SunSpec::readMap(uint modbusAddress, uint modelLength) if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); uint modbusAddress = unit.startAddress(); - BlockId mapId = BlockId(unit.value(0)); - uint mapLength = unit.value(1); - qCDebug(dcSunSpec()) << "Received map. Modbus address" << modbusAddress << "Map ID" << mapId << "Map Length" << mapLength; - emit mapReceived(mapId, mapLength, unit.values().mid(2)); + ModelId modelId = ModelId(unit.value(0)); + uint length = unit.value(1); + qCDebug(dcSunSpec()) << "SunSpec: Received model. Modbus address" << modbusAddress << "model ID" << modelId << "length" << length; + emit modelDataBlockReceived(modelId, length, unit.values().mid(2)); } else { - qCWarning(dcSunSpec()) << "Read response error:" << reply->error(); + qCWarning(dcSunSpec()) << "SunSpec: Read response error:" << reply->error(); } }); connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error) { - qCWarning(dcSunSpec()) << "Modbus reply error:" << error; + qCWarning(dcSunSpec()) << "SunSpec: Modbus reply error:" << error; reply->finished(); // To make sure it will be deleted }); } else { - qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); + qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString(); delete reply; // broadcast replies return immediately return; } } else { - qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); + qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString(); return; } } -void SunSpec::readCommonMap() +void SunSpec::readCommonModel() { - qCDebug(dcSunSpec()) << "SunSpec: Read common block header. Modbus Address" << m_baseRegister+2 << ", Slave ID" << m_slaveId; + qCDebug(dcSunSpec()) << "SunSpec: Read common model header. Modbus Address" << m_baseRegister+2 << ", Slave ID" << m_slaveId; QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_baseRegister+2, 66); @@ -293,9 +294,6 @@ void SunSpec::readCommonMap() if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); - //uint modbusAddress = unit.startAddress(); - //BlockId mapId = BlockId(unit.value(0)); - //int mapLength = unit.value(1); m_manufacturer = convertModbusRegisters(unit.values(), MandatoryRegistersModel1::Manufacturer, 16); m_manufacturer.remove('\x00'); m_deviceModel = convertModbusRegisters(unit.values(), MandatoryRegistersModel1::Model, 16); @@ -303,60 +301,22 @@ void SunSpec::readCommonMap() m_serialNumber = convertModbusRegisters(unit.values(), MandatoryRegistersModel1::SerialNumber, 16); m_serialNumber.remove('\x00'); qCDebug(dcSunSpec()) << "SunSpec: Received common block response. Manufacturer" << m_manufacturer << "Model" << m_deviceModel << "Serial number" << m_serialNumber; - commonMapReceived(m_manufacturer, m_deviceModel, m_serialNumber); + commonModelReceived(m_manufacturer, m_deviceModel, m_serialNumber); } else { - qCWarning(dcSunSpec()) << "SunSpec: Read common map, read response error:" << reply->error(); + qCWarning(dcSunSpec()) << "SunSpec: Read common model, read response error:" << reply->error(); } }); } else { - qCWarning(dcSunSpec()) << "Sunspec: Read common map read error: " << m_modbusTcpClient->errorString(); + qCWarning(dcSunSpec()) << "Sunspec: Read common model read error: " << m_modbusTcpClient->errorString(); delete reply; // broadcast replies return immediately return; } } else { - qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); + qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString(); return; } } -void SunSpec::onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, const QVector &data) -{ - if (modbusRegister == 00000 || modbusRegister == 40000 || modbusRegister == 50000) { - // Common block, 66 registers long + 2 header registers - - qCDebug(dcSunSpec()) << "Sunspec Identification:" << convertModbusRegisters(data, 0, 2); - - if (convertModbusRegisters(data, 0, 2) != "SunS") { - qCWarning(dcSunSpec()) << "Could not find SunS value at" << modbusRegister << "at Slave" << slaveAddress; - return; - } - m_baseRegister = modbusRegister; - - //Mandatory SunSpec Registers - // ID: 40003 - qCDebug(dcSunSpec()) << "Id:" << data[MandatoryRegistersModel1::Manufacturer]; - - // Manufacturer: 40005 | 16 - qCDebug(dcSunSpec()) << "Manufacturer:" << QString::fromLatin1(convertModbusRegisters(data, MandatoryRegistersModel1::Manufacturer, 16)); - - // Thing model: 40021 | 16 - qCDebug(dcSunSpec()) << "Thing model:" << QString::fromLatin1(convertModbusRegisters(data, MandatoryRegistersModel1::Model, 16)); - - // Data manager version: 40037 | 8 - qCDebug(dcSunSpec()) << "Data manager version:" << QString::fromLatin1(convertModbusRegisters(data, 36, 8)); - - // Inverter Version: 40045 | 8 - qCDebug(dcSunSpec()) << "Inverter version:" << QString::fromLatin1(convertModbusRegisters(data, 44, 8)); - - // Serial Number: 40053 | 16 - qCDebug(dcSunSpec()) << "Serial number:" << QString::fromLatin1(convertModbusRegisters(data, MandatoryRegistersModel1::SerialNumber, 16)); - - // Modbus thing address : 40069 | 1 - qCDebug(dcSunSpec()) << "Thing modbus address:" << data[67]; - }; -} - - QByteArray SunSpec::convertModbusRegister(const uint16_t &modbusData) { uint8_t data[2]; @@ -438,22 +398,18 @@ QUuid SunSpec::writeHoldingRegisters(uint registerAddress, const QVectorerror() != QModbusDevice::NoError) { - qCWarning(dcSunSpec()) << "Read response error:" << reply->error(); + qCWarning(dcSunSpec()) << "SunSpec: Read response error:" << reply->error(); + emit requestExecuted(requestId, false); + return; } - reply->deleteLater(); + emit requestExecuted(requestId, true); }); - connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){ - - qCWarning(dcSunSpec()) << "Modbus reply error:" << error; - reply->finished(); // To make sure it will be deleted - }); - QTimer::singleShot(200, reply, &QModbusReply::deleteLater); } else { delete reply; // broadcast replies return immediately return ""; } } else { - qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); + qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString(); return ""; } return requestId; diff --git a/sunspec/sunspec.h b/sunspec/sunspec.h index bb5ec18..2c6a038 100644 --- a/sunspec/sunspec.h +++ b/sunspec/sunspec.h @@ -87,89 +87,89 @@ public: }; Q_ENUM(SunSpecEvent1) - enum BlockId { - BlockIdCommon = 1, - BlockIdBasicAggregator = 2, - BlockIdSecureDatasetReadRequest = 3, - BlockIdSecureDatasetReadResponse = 4, - BlockIdSecureWriteRequest = 5, - BlockIdSecureWriteSequentialRequest = 6, - BlockIdSecureWriteResponseModel = 7, - BlockIdGetDeviceSecurityCertificate = 8, - BlockIdSetOperatorSecurityCertificate = 9, - BlockIdCommunicationInterfaceHeader = 10, - BlockIdEthernetLinkLayer = 11, - BlockIdIPv4 = 12, - BlockIdIPv6 = 13, - BlockIdProxyServer = 14, - BlockIdInterfaceCountersModel = 15, - BlockIdSimpleIpNetwork = 16, - BlockIdSerialInterface = 17, - BlockIdCellularLink = 18, - BlockIdPPPLink = 19, - BlockIdInverterSinglePhase = 101, - BlockIdInverterSplitPhase = 102, - BlockIdInverterThreePhase = 103, - BlockIdInverterSinglePhaseFloat = 111, - BlockIdInverterSplitPhaseFloat = 112, - BlockIdInverterThreePhaseFloat = 113, - BlockIdNameplate = 120, - BlockIdBasicSettings = 121, - BlockIdMeasurementsStatus = 122, - BlockIdImmediateControls = 123, - BlockIdStorage = 124, - BlockIdPricing = 125, - BlockIdStaticVoltVAR = 126, - BlockIdFreqWattParam = 127, - BlockIdDynamicReactiveCurrent = 128, - BlockIdLVRTD = 129, - BlockIdHVRTD = 130, - BlockIdWattPF = 131, - BlockIdVoltWatt = 132, - BlockIdBasicScheduling = 133, - BlockIdFreqWattCrv = 134, - BlockIdLFRT = 135, - BlockIdHFRT = 136, - BlockIdLVRTC = 137, - BlockIdHVRTC = 138, - BlockIdMultipleMPPTInverterExtensionModel = 160, - BlockIdSinglePhaseMeter = 201, - BlockIdSplitSinglePhaseMeter = 202, - BlockIdWyeConnectThreePhaseMeter = 203, - BlockIdDeltaConnectThreePhaseMeter = 204, - BlockIdSinglePhaseMeterFloat = 211, - BlockIdSplitSinglePhaseMeterFloat = 212, - BlockIdWyeConnectThreePhaseMeterFloat = 213, - BlockIdDeltaConnectThreePhaseMeterFloat = 214, - BlockIdSecureACMeterSelectedReadings = 220, - BlockIdIrradianceModel = 302, - BlockIdBackOfModuleTemperatureModel = 303, - BlockIdInclinometerModel = 304, - BlockIdGPS = 305, - BlockIdReferencePointModel = 306, - BlockIdBaseMet = 307, - BlockIdMiniMetModel = 308, - BlockIdStringCombiner = 401, - BlockIdStringCombinerAdvanced = 402, - BlockIdStringCombinerCurrent = 403, - BlockIdStringCombinerCurrentAdvanced = 404, - BlockIdSolarModuleFloat = 501, - BlockIdSolarModule = 502, - BlockIdTrackerController = 601, - BlockIdEnergyStorageBaseModel = 801, - BlockIdBatteryBaseModel = 802, - BlockIdLithiumIonBatteryModel = 803, - BlockIdVerisStatusConfiguration = 64001, - BlockIdMersenGreenString = 64020, - BlockIdEltekInverterExtension = 64101, - BlockIdOutBackAXSDevice = 64110, - BlockIdBasicChargeController = 64111, - BlockIdOutBackFMChargeController = 64112, - BlockIdEnd = 65535 + enum ModelId { + ModelIdCommon = 1, + ModelIdBasicAggregator = 2, + ModelIdSecureDatasetReadRequest = 3, + ModelIdSecureDatasetReadResponse = 4, + ModelIdSecureWriteRequest = 5, + ModelIdSecureWriteSequentialRequest = 6, + ModelIdSecureWriteResponseModel = 7, + ModelIdGetDeviceSecurityCertificate = 8, + ModelIdSetOperatorSecurityCertificate = 9, + ModelIdCommunicationInterfaceHeader = 10, + ModelIdEthernetLinkLayer = 11, + ModelIdIPv4 = 12, + ModelIdIPv6 = 13, + ModelIdProxyServer = 14, + ModelIdInterfaceCountersModel = 15, + ModelIdSimpleIpNetwork = 16, + ModelIdSerialInterface = 17, + ModelIdCellularLink = 18, + ModelIdPPPLink = 19, + ModelIdInverterSinglePhase = 101, + ModelIdInverterSplitPhase = 102, + ModelIdInverterThreePhase = 103, + ModelIdInverterSinglePhaseFloat = 111, + ModelIdInverterSplitPhaseFloat = 112, + ModelIdInverterThreePhaseFloat = 113, + ModelIdNameplate = 120, + ModelIdBasicSettings = 121, + ModelIdMeasurementsStatus = 122, + ModelIdImmediateControls = 123, + ModelIdStorage = 124, + ModelIdPricing = 125, + ModelIdStaticVoltVAR = 126, + ModelIdFreqWattParam = 127, + ModelIdDynamicReactiveCurrent = 128, + ModelIdLVRTD = 129, + ModelIdHVRTD = 130, + ModelIdWattPF = 131, + ModelIdVoltWatt = 132, + ModelIdBasicScheduling = 133, + ModelIdFreqWattCrv = 134, + ModelIdLFRT = 135, + ModelIdHFRT = 136, + ModelIdLVRTC = 137, + ModelIdHVRTC = 138, + ModelIdMultipleMPPTInverterExtensionModel = 160, + ModelIdSinglePhaseMeter = 201, + ModelIdSplitSinglePhaseMeter = 202, + ModelIdWyeConnectThreePhaseMeter = 203, + ModelIdDeltaConnectThreePhaseMeter = 204, + ModelIdSinglePhaseMeterFloat = 211, + ModelIdSplitSinglePhaseMeterFloat = 212, + ModelIdWyeConnectThreePhaseMeterFloat = 213, + ModelIdDeltaConnectThreePhaseMeterFloat = 214, + ModelIdSecureACMeterSelectedReadings = 220, + ModelIdIrradianceModel = 302, + ModelIdBackOfModuleTemperatureModel = 303, + ModelIdInclinometerModel = 304, + ModelIdGPS = 305, + ModelIdReferencePointModel = 306, + ModelIdBaseMet = 307, + ModelIdMiniMetModel = 308, + ModelIdStringCombiner = 401, + ModelIdStringCombinerAdvanced = 402, + ModelIdStringCombinerCurrent = 403, + ModelIdStringCombinerCurrentAdvanced = 404, + ModelIdSolarModuleFloat = 501, + ModelIdSolarModule = 502, + ModelIdTrackerController = 601, + ModelIdEnergyStorageBaseModel = 801, + ModelIdBatteryBaseModel = 802, + ModelIdLithiumIonBatteryModel = 803, + ModelIdVerisStatusConfiguration = 64001, + ModelIdMersenGreenString = 64020, + ModelIdEltekInverterExtension = 64101, + ModelIdOutBackAXSDevice = 64110, + ModelIdBasicChargeController = 64111, + ModelIdOutBackFMChargeController = 64112, + ModelIdEnd = 65535 }; - Q_ENUM(BlockId) + Q_ENUM(ModelId) - explicit SunSpec(const QHostAddress &hostAddress, uint port = 502, QObject *parent = 0); + explicit SunSpec(const QHostAddress &hostAddress, uint port = 502, uint slaveId = 1, QObject *parent = 0); ~SunSpec(); bool connectModbus(); void setHostAddress(const QHostAddress &hostAddress); @@ -194,14 +194,14 @@ public: QString m_manufacturer = "Unknown"; QString m_deviceModel = "Unknown"; QString m_serialNumber = "Unknown"; - QHash m_mapList; + QHash m_modelList; void findBaseRegister(); - void findModbusMap(const QList &mapIds, uint modbusAddressOffset = 2); + void findSunSpecModels(const QList &modelIds, uint modbusAddressOffset = 2); - void readCommonMap(); - void readMapHeader(uint modbusAddress); - void readMap(uint modbusAddress, uint modelLength); //modbusAddress = model start address, model length is without header + void readCommonModel(); + void readModelHeader(uint modbusAddress); + void readModelDataBlock(uint modbusAddress, uint modelLength); //modbusAddress = model start address, model length is without header float convertValueWithSSF(quint32 rawValue, quint16 sunssf); float convertFloatValues(quint16 rawValue0, quint16 rawValue1); @@ -214,20 +214,19 @@ public: signals: void connectionStateChanged(bool status); - void requestExecuted(QUuid requetId, bool success); + void requestExecuted(const QUuid &requestId, bool success); void foundBaseRegister(int modbusAddress); - void commonMapReceived(const QString &manufacturer, const QString &deviceModel, const QString &serialNumber); + void commonModelReceived(const QString &manufacturer, const QString &deviceModel, const QString &serialNumber); - void foundModbusMap(BlockId mapId, int modbusStartRegister); - void modbusMapSearchFinished(const QHash &mapIds); + void foundSunSpecModel(ModelId modelId, int modbusStartRegister); + void sunspecModelSearchFinished(const QHash &mapIds); - void mapHeaderReceived(uint modbusAddress, BlockId mapId, uint mapLength); - void mapReceived(BlockId mapId, uint mapLength, QVector data); + void modelHeaderReceived(uint modbusAddress, ModelId mapId, uint mapLength); + void modelDataBlockReceived(ModelId id, uint ength, QVector data); private slots: void onModbusStateChanged(QModbusDevice::State state); - void onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, const QVector &values); }; #endif // SUNSPEC_H diff --git a/sunspec/sunspecinverter.cpp b/sunspec/sunspecinverter.cpp index 5b7cb9d..bee5c8d 100644 --- a/sunspec/sunspecinverter.cpp +++ b/sunspec/sunspecinverter.cpp @@ -33,17 +33,17 @@ #include -SunSpecInverter::SunSpecInverter(SunSpec *sunspec, SunSpec::BlockId mapId, int modbusAddress) : +SunSpecInverter::SunSpecInverter(SunSpec *sunspec, SunSpec::ModelId modelId, int modbusAddress) : QObject(sunspec), m_connection(sunspec), - m_id(mapId), - m_mapModbusStartRegister(modbusAddress) + m_id(modelId), + m_modelModbusStartRegister(modbusAddress) { qCDebug(dcSunSpec()) << "SunSpecInverter: Setting up inverter"; - connect(m_connection, &SunSpec::mapReceived, this, &SunSpecInverter::onModbusMapReceived); + connect(m_connection, &SunSpec::modelDataBlockReceived, this, &SunSpecInverter::onModelDataBlockReceived); } -SunSpec::BlockId SunSpecInverter::blockId() +SunSpec::ModelId SunSpecInverter::modelId() { return m_id; } @@ -51,10 +51,10 @@ SunSpec::BlockId SunSpecInverter::blockId() void SunSpecInverter::init() { qCDebug(dcSunSpec()) << "SunSpecInverter: Init"; - m_connection->readMapHeader(m_mapModbusStartRegister); - connect(m_connection, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, SunSpec::BlockId mapId, uint mapLength) { - qCDebug(dcSunSpec()) << "SunSpecInverter: Map Header received, modbus address:" << modbusAddress << "map Id:" << mapId << "map length:" << mapLength; - m_mapLength = mapLength; + m_connection->readModelHeader(m_modelModbusStartRegister); + connect(m_connection, &SunSpec::modelHeaderReceived, this, [this] (uint modbusAddress, SunSpec::ModelId modelId, uint length) { + qCDebug(dcSunSpec()) << "SunSpecInverter: Model Header received, modbus address:" << modbusAddress << "model Id:" << modelId << "length:" << length; + m_modelLength = length; emit initFinished(true); m_initFinishedSuccess = true; }); @@ -65,33 +65,33 @@ void SunSpecInverter::init() }); } -void SunSpecInverter::getInverterMap() +void SunSpecInverter::getInverterModelDataBlock() { // TODO check map length to modbus max value - m_connection->readMap(m_mapModbusStartRegister, m_mapLength); + m_connection->readModelDataBlock(m_modelModbusStartRegister, m_modelLength); } -void SunSpecInverter::readInverterBlockHeader() +void SunSpecInverter::getInverterModelHeader() { - m_connection->readMapHeader(m_mapModbusStartRegister); + m_connection->readModelHeader(m_modelModbusStartRegister); } -void SunSpecInverter::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector data) +void SunSpecInverter::onModelDataBlockReceived(SunSpec::ModelId mapId, uint mapLength, QVector data) { Q_UNUSED(mapLength) if (mapId != m_id) { return; } - if (mapLength < m_mapLength) { + if (mapLength < m_modelLength) { qCDebug(dcSunSpec()) << "SunSpecInverter: on modbus map received, map length ist too short" << mapLength; //return; } InverterData inverterData; switch (mapId) { - case SunSpec::BlockIdInverterSinglePhase: - case SunSpec::BlockIdInverterSplitPhase: - case SunSpec::BlockIdInverterThreePhase: { + case SunSpec::ModelIdInverterSinglePhase: + case SunSpec::ModelIdInverterSplitPhase: + case SunSpec::ModelIdInverterThreePhase: { inverterData.acCurrent= m_connection->convertValueWithSSF(data[Model10X::Model10XAcCurrent], data[Model10X::Model10XAmpereScaleFactor]); inverterData.acPower = m_connection->convertValueWithSSF(data[Model10X::Model10XACPower], data[Model10X::Model10XWattScaleFactor]); @@ -105,14 +105,7 @@ void SunSpecInverter::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength inverterData.phaseVoltageBN = m_connection->convertValueWithSSF(data[Model10X::Model10XPhaseVoltageBN], data[Model10X::Model10XVoltageScaleFactor]); inverterData.phaseVoltageCN = m_connection->convertValueWithSSF(data[Model10X::Model10XPhaseVoltageCN], data[Model10X::Model10XVoltageScaleFactor]); - - qCDebug(dcSunSpec()) << "Energy with SSF values:"; - qCDebug(dcSunSpec()) << " - AC Energy 1:" << data[Model10X::Model10XAcEnergy]; - qCDebug(dcSunSpec()) << " - AC Energy 2" << data[Model10X::Model10XAcEnergy+1]; quint32 acEnergy = ((static_cast(data.value(Model10X::Model10XAcEnergy))<<16)|static_cast(data.value(Model10X::Model10XAcEnergy+1))); - qCDebug(dcSunSpec()) << " - AC Energy combined" << acEnergy; - qCDebug(dcSunSpec()) << " - Scale factor:" << data[Model10X::Model10XWattHoursScaleFactor]; - inverterData.acEnergy = m_connection->convertValueWithSSF(acEnergy, data[Model10X::Model10XWattHoursScaleFactor]); inverterData.cabinetTemperature = m_connection->convertValueWithSSF(data[Model10X::Model10XCabinetTemperature], data[Model10X::Model10XTemperatureScaleFactor]); @@ -121,9 +114,9 @@ void SunSpecInverter::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength emit inverterDataReceived(inverterData); } break; - case SunSpec::BlockIdInverterThreePhaseFloat: - case SunSpec::BlockIdInverterSplitPhaseFloat: - case SunSpec::BlockIdInverterSinglePhaseFloat: { + case SunSpec::ModelIdInverterThreePhaseFloat: + case SunSpec::ModelIdInverterSplitPhaseFloat: + case SunSpec::ModelIdInverterSinglePhaseFloat: { inverterData.acCurrent = m_connection->convertFloatValues(data[Model11X::Model11XAcCurrent], data[Model11X::Model11XAcCurrent+1]); diff --git a/sunspec/sunspecinverter.h b/sunspec/sunspecinverter.h index 2c6ca04..918cc75 100644 --- a/sunspec/sunspecinverter.h +++ b/sunspec/sunspecinverter.h @@ -96,22 +96,22 @@ public: SunSpec::SunSpecOperatingState operatingState; }; - SunSpecInverter(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress); - SunSpec::BlockId blockId(); + SunSpecInverter(SunSpec *sunspec, SunSpec::ModelId modelId, int modbusAddress); + SunSpec::ModelId modelId(); void init(); - void getInverterMap(); + void getInverterModelDataBlock(); private: SunSpec *m_connection = nullptr; - SunSpec::BlockId m_id; //e.g. 103 for three phase inverter, 113 for three phase inverter with floating point representation - uint m_mapLength = 0; - uint m_mapModbusStartRegister = 40000; + SunSpec::ModelId m_id; //e.g. 103 for three phase inverter, 113 for three phase inverter with floating point representation + uint m_modelLength = 0; + uint m_modelModbusStartRegister = 40000; bool m_initFinishedSuccess = false; - void readInverterBlockHeader(); + void getInverterModelHeader(); private slots: - void onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector data); + void onModelDataBlockReceived(SunSpec::ModelId mapId, uint mapLength, QVector data); signals: void initFinished(bool success); diff --git a/sunspec/sunspecmeter.cpp b/sunspec/sunspecmeter.cpp index d7d6481..e154d1b 100644 --- a/sunspec/sunspecmeter.cpp +++ b/sunspec/sunspecmeter.cpp @@ -31,28 +31,29 @@ #include "sunspecmeter.h" #include "extern-plugininfo.h" -SunSpecMeter::SunSpecMeter(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress) : +SunSpecMeter::SunSpecMeter(SunSpec *sunspec, SunSpec::ModelId modelId, int modbusAddress) : QObject(sunspec), m_connection(sunspec), - m_id(blockId), - m_mapModbusStartRegister(modbusAddress) + m_id(modelId), + m_modelModbusStartRegister(modbusAddress) { qCDebug(dcSunSpec()) << "SunSpecMeter: Setting up meter"; - connect(m_connection, &SunSpec::mapReceived, this, &SunSpecMeter::onModbusMapReceived); + connect(m_connection, &SunSpec::modelDataBlockReceived, this, &SunSpecMeter::onModelDataBlockReceived); } -SunSpec::BlockId SunSpecMeter::blockId() +SunSpec::ModelId SunSpecMeter::modelId() { + return m_id; } void SunSpecMeter::init() { - qCDebug(dcSunSpec()) << "SunSpecInverter: Init"; - m_connection->readMapHeader(m_mapModbusStartRegister); - connect(m_connection, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, SunSpec::BlockId mapId, uint mapLength) { - qCDebug(dcSunSpec()) << "SunSpecInverter: Map Header received, modbus address:" << modbusAddress << "map Id:" << mapId << "map length:" << mapLength; - m_mapLength = mapLength; + qCDebug(dcSunSpec()) << "SunSpecMeter: Init"; + m_connection->readModelHeader(m_modelModbusStartRegister); + connect(m_connection, &SunSpec::modelHeaderReceived, this, [this] (uint modbusAddress, SunSpec::ModelId modelId, uint length) { + qCDebug(dcSunSpec()) << "SunSpecMeter: Model Header received, modbus address:" << modbusAddress << "model Id:" << modelId << "length:" << length; + m_modelLength = length; emit initFinished(true); m_initFinishedSuccess = true; }); @@ -63,21 +64,37 @@ void SunSpecMeter::init() }); } -void SunSpecMeter::getMeterMap() +void SunSpecMeter::getMeterModelDataBlock() { - m_connection->readMap(m_mapModbusStartRegister, m_mapLength); + m_connection->readModelDataBlock(m_modelModbusStartRegister, m_modelLength); } -void SunSpecMeter::readMeterBlockHeader() +void SunSpecMeter::getMeterModelHeader() { } -void SunSpecMeter::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector data) +void SunSpecMeter::onModelDataBlockReceived(SunSpec::ModelId modelId, uint length, QVector data) { - Q_UNUSED(mapLength) + if (modelId != m_id) { + return; + } + Q_UNUSED(length) Q_UNUSED(data) - switch (mapId) { + switch (modelId) { + + case SunSpec::ModelIdSinglePhaseMeter: + case SunSpec::ModelIdSinglePhaseMeterFloat: { + + } break; + case SunSpec::ModelIdSplitSinglePhaseMeter: + case SunSpec::ModelIdSplitSinglePhaseMeterFloat: { + + } break; + case SunSpec::ModelIdWyeConnectThreePhaseMeterFloat: + case SunSpec::ModelIdDeltaConnectThreePhaseMeterFloat: { + + } break; default: break; } diff --git a/sunspec/sunspecmeter.h b/sunspec/sunspecmeter.h index f7f81b8..1c0f2df 100644 --- a/sunspec/sunspecmeter.h +++ b/sunspec/sunspecmeter.h @@ -68,22 +68,21 @@ public: SunSpec::SunSpecOperatingState operatingState; }; - SunSpecMeter(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress); - SunSpec::BlockId blockId(); + SunSpecMeter(SunSpec *sunspec, SunSpec::ModelId modelId, int modbusAddress); + SunSpec::ModelId modelId(); void init(); - void getMeterMap(); + void getMeterModelHeader(); + void getMeterModelDataBlock(); private: SunSpec *m_connection = nullptr; - SunSpec::BlockId m_id = SunSpec::BlockIdDeltaConnectThreePhaseMeter; - uint m_mapLength = 0; - uint m_mapModbusStartRegister = 40000; + SunSpec::ModelId m_id = SunSpec::ModelIdDeltaConnectThreePhaseMeter; + uint m_modelLength = 0; + uint m_modelModbusStartRegister = 40000; bool m_initFinishedSuccess = false; - void readMeterBlockHeader(); - private slots: - void onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector data); + void onModelDataBlockReceived(SunSpec::ModelId modelId, uint length, QVector data); signals: void initFinished(bool success); diff --git a/sunspec/sunspecstorage.cpp b/sunspec/sunspecstorage.cpp index 426f63e..28600cc 100644 --- a/sunspec/sunspecstorage.cpp +++ b/sunspec/sunspecstorage.cpp @@ -31,29 +31,28 @@ #include "sunspecstorage.h" #include "extern-plugininfo.h" -SunSpecStorage::SunSpecStorage(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress) : +SunSpecStorage::SunSpecStorage(SunSpec *sunspec, SunSpec::ModelId modelId, int modbusAddress) : QObject(sunspec), m_connection(sunspec), - m_id(blockId), - m_mapModbusStartRegister(modbusAddress) + m_id(modelId), + m_modelModbusStartRegister(modbusAddress) { qCDebug(dcSunSpec()) << "SunSpecStorage: Setting up storage"; - connect(m_connection, &SunSpec::mapReceived, this, &SunSpecStorage::onModbusMapReceived); + connect(m_connection, &SunSpec::modelDataBlockReceived, this, &SunSpecStorage::onModelDataBlockReceived); } -SunSpec::BlockId SunSpecStorage::blockId() +SunSpec::ModelId SunSpecStorage::modelId() { return m_id; - } void SunSpecStorage::init() { - qCDebug(dcSunSpec()) << "SunSpecInverter: Init"; - m_connection->readMapHeader(m_mapModbusStartRegister); - connect(m_connection, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, SunSpec::BlockId mapId, uint mapLength) { - qCDebug(dcSunSpec()) << "SunSpecInverter: Map Header received, modbus address:" << modbusAddress << "map Id:" << mapId << "map length:" << mapLength; - m_mapLength = mapLength; + qCDebug(dcSunSpec()) << "SunSpecStorage: Init"; + m_connection->readModelHeader(m_modelModbusStartRegister); + connect(m_connection, &SunSpec::modelHeaderReceived, this, [this] (uint modbusAddress, SunSpec::ModelId modelId, uint length) { + qCDebug(dcSunSpec()) << "SunSpecStorager: Model header received, modbus address:" << modbusAddress << "model Id:" << modelId << "length:" << length; + m_modelLength = length; emit initFinished(true); m_initFinishedSuccess = true; }); @@ -64,14 +63,14 @@ void SunSpecStorage::init() }); } -void SunSpecStorage::getStorageMap() +void SunSpecStorage::getStorageModelDataBlock() { - m_connection->readMap(m_mapModbusStartRegister, m_mapLength); + m_connection->readModelDataBlock(m_modelModbusStartRegister, m_modelLength); } -void SunSpecStorage::readStorageBlockHeader() +void SunSpecStorage::getStorageModelHeader() { - m_connection->readMapHeader(m_mapModbusStartRegister); + m_connection->readModelHeader(m_modelModbusStartRegister); } QUuid SunSpecStorage::setGridCharging(bool enabled) @@ -82,7 +81,7 @@ QUuid SunSpecStorage::setGridCharging(bool enabled) PV (charging from grid 0 disabled) GRID (charging from 1 grid enabled*/ - uint registerAddress = m_mapModbusStartRegister + Model124::Model124ChaGriSet; + uint registerAddress = m_modelModbusStartRegister + Model124::Model124ChaGriSet; quint16 value = enabled; return m_connection->writeHoldingRegister(registerAddress, value); } @@ -93,7 +92,7 @@ QUuid SunSpecStorage::setStorageControlMode(bool chargingEnabled, bool dischargi quint16 value = ((static_cast(chargingEnabled) << StorageControlBitFieldCharge) | (static_cast(dischargingEnabled) << StorageControlBitFieldDischarge)) ; - uint modbusRegister = m_mapModbusStartRegister + Model124::Model124ActivateStorageControlMode; + uint modbusRegister = m_modelModbusStartRegister + Model124::Model124ActivateStorageControlMode; return m_connection->writeHoldingRegister(modbusRegister, value); } @@ -102,7 +101,7 @@ QUuid SunSpecStorage::setChargingRate(int rate) //Register Name InWRte /* Defines the maximum charge rate (charge limit). Default is 100% */ - uint modbusRegister = m_mapModbusStartRegister + Model124::Model124SetpointMaximumChargingRate; + uint modbusRegister = m_modelModbusStartRegister + Model124::Model124SetpointMaximumChargingRate; int16_t value = rate * 100; return m_connection->writeHoldingRegister(modbusRegister, value); } @@ -111,20 +110,37 @@ QUuid SunSpecStorage::setDischargingRate(int charging) { //Register Name OutWRte /* Defines the maximum discharge rate (discharge limit). Default is 100% */ - uint modbusRegister = m_mapModbusStartRegister + Model124::Model124SetpointMaximumDischargeRate; + uint modbusRegister = m_modelModbusStartRegister + Model124::Model124SetpointMaximumDischargeRate; quint16 value = charging * 100; return m_connection->writeHoldingRegister(modbusRegister, value); } -void SunSpecStorage::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, const QVector &data) +void SunSpecStorage::onModelDataBlockReceived(SunSpec::ModelId modelId, uint length, const QVector &data) { - Q_UNUSED(mapLength) - switch (mapId) { - case SunSpec::BlockIdStorage: { + Q_UNUSED(length) + if (modelId != m_id) { + return; + } + + switch (modelId) { + case SunSpec::ModelIdStorage: { StorageData storageData; + qCDebug(dcSunSpec()) << "SunSpecStorage: Storage model received:"; + qCDebug(dcSunSpec()) << " - Setpoint maximum charge" << data[Model124SetpointMaximumCharge]; + qCDebug(dcSunSpec()) << " - Setpoint maximum charging rate" << data[Model124SetpointMaximumChargingRate]; + qCDebug(dcSunSpec()) << " - Setpoint maximum discharge rate" << data[Model124SetpointMaximumDischargeRate]; + qCDebug(dcSunSpec()) << " - Active storage control mode" << data[Model124ActivateStorageControlMode]; + qCDebug(dcSunSpec()) << " - Currently available energy" << data[Model124CurrentlyAvailableEnergy]; storageData.chargingState = ChargingState(data[Model124::Model124ChargeStatus]); + qCDebug(dcSunSpec()) << " - Setpoint maximum charging rate" << data[Model124ChaGriSet]; + qCDebug(dcSunSpec()) << " - Setpoint maximum charging rate" << data[Model124ScaleFactorMaximumCharge]; + qCDebug(dcSunSpec()) << " - Setpoint maximum charging rate" << data[Model124ScaleFactorMaximumChargeDischargeRate]; + qCDebug(dcSunSpec()) << " - Setpoint maximum charging rate" << data[Model124ScaleFactorAvailableEnergyPercent]; emit storageDataReceived(storageData); } break; + case SunSpec::ModelIdBatteryBaseModel: + case SunSpec::ModelIdLithiumIonBatteryModel: { + } default: break; } diff --git a/sunspec/sunspecstorage.h b/sunspec/sunspecstorage.h index 3d2cd3e..1cb7f1c 100644 --- a/sunspec/sunspecstorage.h +++ b/sunspec/sunspecstorage.h @@ -38,11 +38,12 @@ class SunSpecStorage : public QObject { Q_OBJECT public: - SunSpecStorage(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress); + SunSpecStorage(SunSpec *sunspec, SunSpec::ModelId modelId, int modbusAddress); - SunSpec::BlockId blockId(); + SunSpec::ModelId modelId(); void init(); - void getStorageMap(); + void getStorageModelHeader(); + void getStorageModelDataBlock(); QUuid setGridCharging(bool enabled); QUuid setDischargingRate(int rate); @@ -93,15 +94,13 @@ public: private: SunSpec *m_connection = nullptr; - SunSpec::BlockId m_id = SunSpec::BlockIdEnergyStorageBaseModel; - uint m_mapLength = 0; - uint m_mapModbusStartRegister = 40000; + SunSpec::ModelId m_id = SunSpec::ModelIdEnergyStorageBaseModel; + uint m_modelLength = 0; + uint m_modelModbusStartRegister = 40000; bool m_initFinishedSuccess = false; - void readStorageBlockHeader(); - private slots: - void onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, const QVector &data); + void onModelDataBlockReceived(SunSpec::ModelId modelId, uint length, const QVector &data); signals: void initFinished(bool success); diff --git a/sunspec/sunspectracker.h b/sunspec/sunspectracker.h index 2be9c84..88b81a8 100644 --- a/sunspec/sunspectracker.h +++ b/sunspec/sunspectracker.h @@ -60,7 +60,7 @@ public: void getTrackerMap(); private: - BlockId m_id = BlockIdTrackerController; + ModelId m_id = ModelIdTrackerController; uint m_mapLength = 0; uint m_mapModbusStartRegister = 40000;