diff --git a/sunspec/README.md b/sunspec/README.md index 0d3b491..f9ad5a5 100644 --- a/sunspec/README.md +++ b/sunspec/README.md @@ -1,13 +1,11 @@ # SunSpec -Connect to SunSpec devices. +Connect to SunSpec devices over Modbus TCP ## Supported Things * SunSpec Inverter -* SunSpec PV Modules * SunSpec Meter -* SunSpec Tracker * SunSpec Storage ## Requirements diff --git a/sunspec/integrationpluginsunspec.cpp b/sunspec/integrationpluginsunspec.cpp index 127adb5..9481dbc 100644 --- a/sunspec/integrationpluginsunspec.cpp +++ b/sunspec/integrationpluginsunspec.cpp @@ -38,6 +38,34 @@ IntegrationPluginSunSpec::IntegrationPluginSunSpec() } +void IntegrationPluginSunSpec::init() +{ + m_connectedStateTypeIds.insert(sunspecConnectionThingClassId, sunspecConnectionConnectedStateTypeId); + m_connectedStateTypeIds.insert(sunspecSinglePhaseInverterThingClassId, sunspecSinglePhaseInverterConnectedStateTypeId); + m_connectedStateTypeIds.insert(sunspecSplitPhaseInverterThingClassId, sunspecSplitPhaseInverterConnectedStateTypeId); + m_connectedStateTypeIds.insert(sunspecThreePhaseInverterThingClassId, sunspecThreePhaseInverterConnectedStateTypeId); + m_connectedStateTypeIds.insert(sunspecStorageThingClassId, sunspecStorageConnectedStateTypeId); + m_connectedStateTypeIds.insert(sunspecSinglePhaseMeterThingClassId, sunspecSinglePhaseMeterConnectedStateTypeId); + 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_modbusAddressParamTypeIds.insert(sunspecSinglePhaseInverterThingClassId, sunspecSinglePhaseInverterThingModbusAddressParamTypeId); + m_modbusAddressParamTypeIds.insert(sunspecSplitPhaseInverterThingClassId, sunspecSplitPhaseInverterThingModbusAddressParamTypeId); + m_modbusAddressParamTypeIds.insert(sunspecThreePhaseInverterThingClassId, sunspecThreePhaseInverterThingModbusAddressParamTypeId); + m_modbusAddressParamTypeIds.insert(sunspecStorageThingClassId, sunspecStorageThingModbusAddressParamTypeId); + m_modbusAddressParamTypeIds.insert(sunspecSinglePhaseMeterThingClassId, sunspecSinglePhaseMeterThingModbusAddressParamTypeId); + m_modbusAddressParamTypeIds.insert(sunspecSplitPhaseMeterThingClassId, sunspecSplitPhaseMeterThingModbusAddressParamTypeId); + m_modbusAddressParamTypeIds.insert(sunspecThreePhaseMeterThingClassId, sunspecThreePhaseMeterThingModbusAddressParamTypeId); +} + void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) { Thing *thing = info->thing(); @@ -49,13 +77,13 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) //int slaveId = info->thing()->paramValue(sunspecConnectionThingSlaveIdParamTypeId).toInt(); SunSpec *sunSpec; - if (m_sunSpecConnections.contains(thing)) { + if (m_sunSpecConnections.contains(thing->id())) { qCDebug(dcSunSpec()) << "Reconfigure SunSpec connection with new address" << address; - sunSpec = m_sunSpecConnections.value(thing); + sunSpec = m_sunSpecConnections.value(thing->id()); sunSpec->setHostAddress(address); } else { sunSpec = new SunSpec(address, port, this); - m_sunSpecConnections.insert(info->thing(), sunSpec); + m_sunSpecConnections.insert(thing->id(), sunSpec); } if (!sunSpec->connectModbus()) { @@ -72,71 +100,74 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) }); connect(info, &ThingSetupInfo::aborted, sunSpec, &SunSpec::deleteLater); - connect(sunSpec, &SunSpec::destroyed, [this, info] { - m_sunSpecConnections.remove(info->thing()); + connect(sunSpec, &SunSpec::destroyed, [this, thing] { + m_sunSpecConnections.remove(thing->id()); }); - connect(sunSpec, &SunSpec::connectionStateChanged, this, [thing] (bool status) { - thing->setStateValue(sunspecConnectionThingClassId, status); + 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) { + thing->setStateValue(sunspecConnectionConnectedStateTypeId, true); + thing->setStateValue(sunspecConnectionManufacturerStateTypeId, manufacturer); + thing->setStateValue(sunspecConnectionDeviceModelStateTypeId, deviceModel); + thing->setStateValue(sunspecConnectionSerialNumberStateTypeId, serielNumber); }); - connect(sunSpec, &SunSpec::mapHeaderReceived, this, &IntegrationPluginSunSpec::onMapHeaderReceived); - connect(sunSpec, &SunSpec::mapReceived, this, &IntegrationPluginSunSpec::onMapReceived); - } else if (thing->thingClassId() == sunspecInverterThingClassId) { - QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecInverterThingIpAddressParamTypeId).toString()); - SunSpecInverter *sunSpecInverter = new SunSpecInverter(address, 502, this); + } else if (thing->thingClassId() == sunspecThreePhaseInverterThingClassId || + thing->thingClassId() == sunspecSplitPhaseInverterThingClassId || + thing->thingClassId() == sunspecSinglePhaseInverterThingClassId ) { - if (!sunSpecInverter->connectModbus()) { - qCWarning(dcSunSpec()) << "Error connecting to SunSpec device"; + uint mapId = thing->paramValue(m_mapIdParamTypeIds.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); } - connect(sunSpecInverter, &SunSpecInverter::initFinished, info, [this, sunSpecInverter, info] { - qCDebug(dcSunSpec()) << "Modbus Inverter init finished"; - m_sunSpecInverters.insert(info->thing(), sunSpecInverter); - info->finish(Thing::ThingErrorNoError); + SunSpecInverter *sunSpecInverter = new SunSpecInverter(connection, SunSpec::BlockId(mapId), modbusAddress); + sunSpecInverter->init(); + connect(sunSpecInverter, &SunSpecInverter::initFinished, info, [this, sunSpecInverter, info] (bool success){ + qCDebug(dcSunSpec()) << "Modbus Inverter init finished, success:" << success; + if (success) { + m_sunSpecInverters.insert(info->thing(), sunSpecInverter); + info->finish(Thing::ThingErrorNoError); + } else { + info->finish(Thing::ThingErrorHardwareNotAvailable); + } }); connect(info, &ThingSetupInfo::aborted, sunSpecInverter, &SunSpecInverter::deleteLater); - connect(sunSpecInverter, &SunSpecInverter::destroyed, [info, this] {m_sunSpecInverters.remove(info->thing());}); + connect(sunSpecInverter, &SunSpecInverter::destroyed, thing, [thing, this] {m_sunSpecInverters.remove(thing);}); connect(sunSpecInverter, &SunSpecInverter::inverterDataReceived, this, &IntegrationPluginSunSpec::onInverterDataReceived); - } else if (thing->thingClassId() == sunspecMeterThingClassId) { - QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecMeterThingIpAddressParamTypeId).toString()); - SunSpecMeter *sunSpecMeter = new SunSpecMeter(address, 502, this); + } else if (thing->thingClassId() == sunspecSinglePhaseMeterThingClassId || + thing->thingClassId() == sunspecSplitPhaseMeterThingClassId || + thing->thingClassId() == sunspecThreePhaseMeterThingClassId) { - if (!sunSpecMeter->connectModbus()) { - sunSpecMeter->deleteLater(); - qCDebug(dcSunSpec()) << "Could not connect"; + uint mapId = thing->paramValue(m_mapIdParamTypeIds.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); } - m_sunSpecMeters.insert(info->thing(), sunSpecMeter); - connect(info, &ThingSetupInfo::aborted, sunSpecMeter, [info, this] { - m_sunSpecMeters.take(info->thing())->deleteLater(); + SunSpecMeter *sunSpecMeter = new SunSpecMeter(connection, SunSpec::BlockId(mapId), modbusAddress); + sunSpecMeter->init(); + connect(sunSpecMeter, &SunSpecMeter::initFinished, info, [this, sunSpecMeter, info] (bool success){ + qCDebug(dcSunSpec()) << "Modbus meter init finished, success:" << success; + if (success) { + m_sunSpecMeters.insert(info->thing(), sunSpecMeter); + info->finish(Thing::ThingErrorNoError); + } else { + info->finish(Thing::ThingErrorHardwareNotAvailable); + } }); - } else if (info->thing()->thingClassId() == sunspecTrackerThingClassId) { - QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecTrackerThingIpAddressParamTypeId).toString()); - SunSpecTracker *sunSpecTracker = new SunSpecTracker(address, 502, this); - if (!sunSpecTracker->connectModbus()) { - sunSpecTracker->deleteLater(); - qCDebug(dcSunSpec()) << "Could not connect"; - return info->finish(Thing::ThingErrorHardwareNotAvailable); - } - connect(info, &ThingSetupInfo::aborted, sunSpecTracker, [info, this] { - m_sunSpecTrackers.take(info->thing())->deleteLater(); - }); + connect(info, &ThingSetupInfo::aborted, sunSpecMeter, &SunSpecMeter::deleteLater); + connect(sunSpecMeter, &SunSpecMeter::destroyed, thing, [thing, this] {m_sunSpecMeters.remove(thing);}); + connect(sunSpecMeter, &SunSpecMeter::meterDataReceived, this, &IntegrationPluginSunSpec::onMeterDataReceived); } else if (info->thing()->thingClassId() == sunspecStorageThingClassId) { - QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecStorageThingIpAddressParamTypeId).toString()); - SunSpecStorage *sunSpecStorage = new SunSpecStorage(address, 502, this); - if (!sunSpecStorage->connectModbus()) { - sunSpecStorage->deleteLater(); - qCDebug(dcSunSpec()) << "Could not connect"; - return info->finish(Thing::ThingErrorHardwareNotAvailable); - } - m_sunSpecStorages.insert(info->thing(), sunSpecStorage); - connect(info, &ThingSetupInfo::aborted, sunSpecStorage, [info, this] { - m_sunSpecStorages.take(info->thing())->deleteLater(); - }); + } else { Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(info->thing()->thingClassId().toString()).toUtf8()); @@ -155,50 +186,43 @@ void IntegrationPluginSunSpec::postSetupThing(Thing *thing) } if (thing->thingClassId() == sunspecConnectionThingClassId) { - SunSpec *connection = m_sunSpecConnections.take(thing); - if (!connection) + SunSpec *connection = m_sunSpecConnections.value(thing->id()); + if (!connection) { + qCDebug(dcSunSpec()) << "SunSpecConnection not found"; return; + } connection->readCommonMap(); - thing->setParamValue(sunspecConnectionManufacturerStateTypeId, connection->manufacturer()); - thing->setParamValue(sunspecInverterThingSerialNumberParamTypeId, connection->serialNumber()); - thing->setParamValue(sunspecInverterThingDeviceModelParamTypeId, connection->deviceModel()); + connection->findModbusMap(QList()); // Discover all maps, without filter + } else if (thing->thingClassId() == sunspecSinglePhaseInverterThingClassId || + thing->thingClassId() == sunspecSplitPhaseInverterThingClassId || + thing->thingClassId() == sunspecThreePhaseInverterThingClassId) { - } else if (thing->thingClassId() == sunspecInverterThingClassId) { - SunSpecInverter *sunSpecInverter = m_sunSpecInverters.take(thing); - if (!sunSpecInverter) + SunSpecInverter *sunSpecInverter = m_sunSpecInverters.value(thing); + if (!sunSpecInverter) { + qCDebug(dcSunSpec()) << "SunSpecInverter not found"; return; + } sunSpecInverter->getInverterMap(); - thing->setParamValue(sunspecInverterThingManufacturerParamTypeId, sunSpecInverter->manufacturer()); - thing->setParamValue(sunspecInverterThingSerialNumberParamTypeId, sunSpecInverter->serialNumber()); - thing->setParamValue(sunspecInverterThingDeviceModelParamTypeId, sunSpecInverter->deviceModel()); - } else if (thing->thingClassId() == sunspecMeterThingClassId) { - SunSpecMeter *sunSpecMeter = m_sunSpecMeters.take(thing); - if (!sunSpecMeter) - return; - //TODO upate data - thing->setParamValue(sunspecMeterThingManufacturerParamTypeId, sunSpecMeter->manufacturer()); - thing->setParamValue(sunspecMeterThingSerialNumberParamTypeId, sunSpecMeter->serialNumber()); - thing->setParamValue(sunspecMeterThingDeviceModelParamTypeId, sunSpecMeter->deviceModel()); + } else if (thing->thingClassId() == sunspecSinglePhaseMeterThingClassId || + thing->thingClassId() == sunspecSplitPhaseMeterThingClassId || + thing->thingClassId() == sunspecThreePhaseMeterThingClassId) { - } else if (thing->thingClassId() == sunspecTrackerThingClassId) { - SunSpecTracker *sunSpecTracker = m_sunSpecTrackers.take(thing); - if (!sunSpecTracker) + SunSpecMeter *sunSpecMeter = m_sunSpecMeters.value(thing); + if (!sunSpecMeter) { + qCDebug(dcSunSpec()) << "SunSpecMeter not found"; return; - //TODO upate data - thing->setParamValue(sunspecTrackerThingManufacturerParamTypeId, sunSpecTracker->manufacturer()); - thing->setParamValue(sunspecTrackerThingSerialNumberParamTypeId, sunSpecTracker->serialNumber()); - thing->setParamValue(sunspecTrackerThingDeviceModelParamTypeId, sunSpecTracker->deviceModel()); + } + sunSpecMeter->getMeterMap(); } else if (thing->thingClassId() == sunspecStorageThingClassId) { - SunSpecStorage *sunSpecStorage = m_sunSpecStorages.take(thing); - if (!sunSpecStorage) + SunSpecStorage *sunSpecStorage = m_sunSpecStorages.value(thing); + if (!sunSpecStorage) { + qCDebug(dcSunSpec()) << "SunSpecStorage not found"; return; - //TODO upate data - thing->setParamValue(sunspecStorageThingManufacturerParamTypeId, sunSpecStorage->manufacturer()); - thing->setParamValue(sunspecStorageThingSerialNumberParamTypeId, sunSpecStorage->serialNumber()); - thing->setParamValue(sunspecStorageThingDeviceModelParamTypeId, sunSpecStorage->deviceModel()); + } + sunSpecStorage->getStorageMap(); } else { Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); @@ -210,21 +234,25 @@ void IntegrationPluginSunSpec::thingRemoved(Thing *thing) { qCDebug(dcSunSpec()) << "Thing removed" << thing->name(); - if (thing->thingClassId() == sunspecInverterThingClassId) { + if (thing->thingClassId() == sunspecConnectionThingClassId) { + SunSpec *sunSpecConnection = m_sunSpecConnections.take(thing->id()); + if (sunSpecConnection) + sunSpecConnection->deleteLater(); + + } else if (thing->thingClassId() == sunspecSinglePhaseInverterThingClassId || + thing->thingClassId() == sunspecSplitPhaseInverterThingClassId || + thing->thingClassId() == sunspecThreePhaseInverterThingClassId) { SunSpecInverter *sunSpecInverter = m_sunSpecInverters.take(thing); if (sunSpecInverter) sunSpecInverter->deleteLater(); - } else if (thing->thingClassId() == sunspecMeterThingClassId) { + } else if (thing->thingClassId() == sunspecSinglePhaseMeterThingClassId || + thing->thingClassId() == sunspecSplitPhaseMeterThingClassId || + thing->thingClassId() == sunspecThreePhaseMeterThingClassId) { SunSpecMeter *sunSpecMeter = m_sunSpecMeters.take(thing); if (sunSpecMeter) sunSpecMeter->deleteLater(); - } else if (thing->thingClassId() == sunspecTrackerThingClassId) { - SunSpecTracker *sunSpecTracker = m_sunSpecTrackers.take(thing); - if (sunSpecTracker) - sunSpecTracker->deleteLater(); - } else if (thing->thingClassId() == sunspecStorageThingClassId) { SunSpecStorage *sunSpecStorage = m_sunSpecStorages.take(thing); if (sunSpecStorage) @@ -246,7 +274,10 @@ void IntegrationPluginSunSpec::executeAction(ThingActionInfo *info) Thing *thing = info->thing(); Action action = info->action(); - if (thing->thingClassId() == sunspecInverterThingClassId) { + if (thing->thingClassId() == sunspecSinglePhaseInverterThingClassId || + thing->thingClassId() == sunspecSplitPhaseInverterThingClassId || + thing->thingClassId() == sunspecThreePhaseInverterThingClassId) { + SunSpecInverter *sunSpecInverter = m_sunSpecInverters.value(thing); if (!sunSpecInverter) { qWarning(dcSunSpec()) << "Could not find SunSpec inverter for thing" << thing->name(); @@ -255,10 +286,9 @@ void IntegrationPluginSunSpec::executeAction(ThingActionInfo *info) } info->finish(Thing::ThingErrorActionTypeNotFound); - } else if (thing->thingClassId() == sunspecMeterThingClassId) { - info->finish(Thing::ThingErrorActionTypeNotFound); - - } else if (thing->thingClassId() == sunspecTrackerThingClassId) { + } else if (thing->thingClassId() == sunspecSinglePhaseMeterThingClassId || + thing->thingClassId() == sunspecSplitPhaseMeterThingClassId || + thing->thingClassId() == sunspecThreePhaseMeterThingClassId) { info->finish(Thing::ThingErrorActionTypeNotFound); } else if (thing->thingClassId() == sunspecStorageThingClassId) { @@ -319,24 +349,30 @@ void IntegrationPluginSunSpec::executeAction(ThingActionInfo *info) } } +bool IntegrationPluginSunSpec::checkIfThingExists(uint mapId, 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) { + return true; + } + } + return false; +} + void IntegrationPluginSunSpec::onRefreshTimer() { - qCDebug(dcSunSpec()) << "On refresh timer"; - //get data - foreach (SunSpec *connections, m_sunSpecConnections) { - connections->readCommonMap(); + foreach (SunSpec *connection, m_sunSpecConnections) { + connection->readCommonMap(); //check connection } foreach (SunSpecInverter *inverter, m_sunSpecInverters) { inverter->getInverterMap(); } foreach (SunSpecMeter *meter, m_sunSpecMeters) { - Q_UNUSED(meter) //TODO - } - foreach (SunSpecTracker *tracker, m_sunSpecTrackers) { - Q_UNUSED(tracker) //TODO + meter->getMeterMap(); } foreach (SunSpecStorage *storage, m_sunSpecStorages) { - Q_UNUSED(storage) //TODO + storage->getStorageMap(); } } @@ -357,7 +393,18 @@ void IntegrationPluginSunSpec::onPluginConfigurationChanged(const ParamTypeId &p void IntegrationPluginSunSpec::onConnectionStateChanged(bool status) { - qCDebug(dcSunSpec()) << "Connection state changed" << status; + SunSpec *connection = static_cast(sender()); + Thing *thing = myThings().findById(m_sunSpecConnections.key(connection)); + if (!thing) + return; + qCDebug(dcSunSpec()) << "Connection state changed" << status << thing->name(); + if (thing->thingClassId() == sunspecConnectionConnectedStateTypeId) { + thing->setStateValue(sunspecConnectionConnectedStateTypeId, status); + } + + Q_FOREACH(Thing *child, myThings().filterByParentId(thing->id())) { + child->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), status); + } } void IntegrationPluginSunSpec::onMapHeaderReceived(uint modbusAddress, SunSpec::BlockId mapId, uint mapLength) @@ -369,16 +416,93 @@ void IntegrationPluginSunSpec::onMapReceived(SunSpec::BlockId mapId, uint mapLen { Q_UNUSED(data) SunSpec *connection = static_cast(sender()); - Thing *thing = m_sunSpecConnections.key(connection); + Thing *thing = myThings().findById(m_sunSpecConnections.key(connection)); if (!thing) return; - qCDebug(dcSunSpec()) << "On map received" << mapId << mapLength; - if (mapId == SunSpec::BlockIdCommon && thing->thingClassId() == sunspecConnectionThingClassId) { - thing->setStateValue(sunspecConnectionManufacturerStateTypeId, connection->manufacturer()); - thing->setStateValue(sunspecConnectionSerialNumberStateTypeId, connection->serialNumber()); - thing->setStateValue(sunspecConnectionDeviceModelStateTypeId, connection->deviceModel()); - } + qCDebug(dcSunSpec()) << "On map received" << mapId << "Map length" << mapLength << "Thing:" << thing->name(); +} + +void IntegrationPluginSunSpec::onFoundModbusMap(SunSpec::BlockId mapId, int modbusStartRegister) +{ + SunSpec *connection = static_cast(sender()); + Thing *thing = myThings().findById(m_sunSpecConnections.key(connection)); + if (!thing) { + qCWarning(dcSunSpec()) << "Thing not found for SunSpec connection" << connection->deviceModel() << connection->serialNumber(); + return; + } + + qCDebug(dcSunSpec()) << "On map received" << mapId << "Map length" << modbusStartRegister << "Thing:" << thing->name(); + if (checkIfThingExists(mapId, modbusStartRegister)) { + return; + } + + switch (mapId) { + case SunSpec::BlockId::BlockIdInverterSinglePhase: + case SunSpec::BlockId::BlockIdInverterSinglePhaseFloat: { + QString model = thing->stateValue(sunspecConnectionDeviceModelStateTypeId).toString(); + ThingDescriptor descriptor(sunspecSinglePhaseInverterThingClassId, model+tr(" single phase inverter"), "", thing->id()); + ParamList params; + params.append(Param(sunspecSinglePhaseInverterThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecSinglePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); + descriptor.setParams(params); + emit autoThingsAppeared({descriptor}); + } break; + case SunSpec::BlockId::BlockIdInverterSplitPhase: + case SunSpec::BlockId::BlockIdInverterSplitPhaseFloat: { + QString model = thing->stateValue(sunspecConnectionDeviceModelStateTypeId).toString(); + ThingDescriptor descriptor(sunspecSplitPhaseInverterThingClassId, model+tr(" split phase inverter"), "", thing->id()); + ParamList params; + params.append(Param(sunspecSplitPhaseInverterThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecSplitPhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); + descriptor.setParams(params); + emit autoThingsAppeared({descriptor}); + } break; + case SunSpec::BlockId::BlockIdInverterThreePhase: + case SunSpec::BlockId::BlockIdInverterThreePhaseFloat: { + QString model = thing->stateValue(sunspecConnectionDeviceModelStateTypeId).toString(); + ThingDescriptor descriptor(sunspecThreePhaseInverterThingClassId, model+tr(" three phase inverter"), "", thing->id()); + ParamList params; + params.append(Param(sunspecThreePhaseInverterThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecThreePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); + descriptor.setParams(params); + emit autoThingsAppeared({descriptor}); + } break; + + case SunSpec::BlockIdSinglePhaseMeter: + case SunSpec::BlockIdSinglePhaseMeterFloat: { + } break; + case SunSpec::BlockIdSplitSinglePhaseMeter: + case SunSpec::BlockIdSplitSinglePhaseMeterFloat: { + } break; + case SunSpec::BlockIdWyeConnectThreePhaseMeterFloat: + case SunSpec::BlockIdDeltaConnectThreePhaseMeterFloat: { + } break; + case SunSpec::BlockIdStorage: { + QString model = thing->stateValue(sunspecConnectionDeviceModelStateTypeId).toString(); + ThingDescriptor descriptor(sunspecStorageThingClassId, model+" Storage", "Storage", thing->id()); + ParamList params; + params.append(Param(sunspecStorageThingMapIdParamTypeId, mapId)); + params.append(Param(sunspecThreePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); + descriptor.setParams(params); + emit autoThingsAppeared({descriptor}); + } break; + default: + qCDebug(dcSunSpec()) << "Block Id not handled"; + } +} + +void IntegrationPluginSunSpec::onModbusMapSearchFinished(const QHash &mapIds) +{ + SunSpec *connection = static_cast(sender()); + Thing *thing = myThings().findById(m_sunSpecConnections.key(connection)); + if (!thing) { + qCWarning(dcSunSpec()) << "Thing not found for SunSpec connection" << connection->deviceModel() << connection->serialNumber(); + return; + } + + qCDebug(dcSunSpec()) << "On modbus map search finished, maps:" << mapIds.count(); + } void IntegrationPluginSunSpec::onWriteRequestExecuted(QUuid requestId, bool success) @@ -401,95 +525,96 @@ void IntegrationPluginSunSpec::onInverterDataReceived(const SunSpecInverter::Inv if(!thing) { return; } - thing->setStateValue(sunspecInverterAcPowerStateTypeId, inverterData.acPower); - thing->setStateValue(sunspecInverterAcEnergyStateTypeId, inverterData.acEnergy/1000.00); - thing->setStateValue(sunspecInverterLineFrequencyStateTypeId, inverterData.lineFrequency); - thing->setStateValue(sunspecInverterTotalCurrentStateTypeId, inverterData.acCurrent); - thing->setStateValue(sunspecInverterCabinetTemperatureStateTypeId, inverterData.cabinetTemperature); + thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), true); + thing->setStateValue(sunspecThreePhaseInverterAcPowerStateTypeId, inverterData.acPower/1000.00); + thing->setStateValue(sunspecThreePhaseInverterAcEnergyStateTypeId, inverterData.acEnergy/1000.00); + thing->setStateValue(sunspecThreePhaseInverterLineFrequencyStateTypeId, inverterData.lineFrequency); + thing->setStateValue(sunspecThreePhaseInverterTotalCurrentStateTypeId, inverterData.acCurrent); + thing->setStateValue(sunspecThreePhaseInverterCabinetTemperatureStateTypeId, inverterData.cabinetTemperature); - thing->setStateValue(sunspecInverterPhaseACurrentStateTypeId, inverterData.phaseACurrent); - thing->setStateValue(sunspecInverterPhaseBCurrentStateTypeId, inverterData.phaseBCurrent); - thing->setStateValue(sunspecInverterPhaseCCurrentStateTypeId, inverterData.phaseCCurrent); + thing->setStateValue(sunspecThreePhaseInverterPhaseACurrentStateTypeId, inverterData.phaseACurrent); + thing->setStateValue(sunspecThreePhaseInverterPhaseBCurrentStateTypeId, inverterData.phaseBCurrent); + thing->setStateValue(sunspecThreePhaseInverterPhaseCCurrentStateTypeId, inverterData.phaseCCurrent); - thing->setStateValue(sunspecInverterPhaseANVoltageStateTypeId, inverterData.phaseVoltageAN); - thing->setStateValue(sunspecInverterPhaseBNVoltageStateTypeId, inverterData.phaseVoltageBN); - thing->setStateValue(sunspecInverterPhaseCNVoltageStateTypeId, inverterData.phaseVoltageCN); + thing->setStateValue(sunspecThreePhaseInverterPhaseANVoltageStateTypeId, inverterData.phaseVoltageAN); + thing->setStateValue(sunspecThreePhaseInverterPhaseBNVoltageStateTypeId, inverterData.phaseVoltageBN); + thing->setStateValue(sunspecThreePhaseInverterPhaseCNVoltageStateTypeId, inverterData.phaseVoltageCN); switch(inverterData.operatingState) { case SunSpec::SunSpecOperatingState::Off: - thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Off"); + thing->setStateValue(sunspecThreePhaseInverterOperatingStateStateTypeId, "Off"); break; case SunSpec::SunSpecOperatingState::MPPT: - thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "MPPT"); + thing->setStateValue(sunspecThreePhaseInverterOperatingStateStateTypeId, "MPPT"); break; case SunSpec::SunSpecOperatingState::Fault: - thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Fault"); + thing->setStateValue(sunspecThreePhaseInverterOperatingStateStateTypeId, "Fault"); break; case SunSpec::SunSpecOperatingState::Standby: - thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Standby"); + thing->setStateValue(sunspecThreePhaseInverterOperatingStateStateTypeId, "Standby"); break; case SunSpec::SunSpecOperatingState::Sleeping: - thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Sleeping"); + thing->setStateValue(sunspecThreePhaseInverterOperatingStateStateTypeId, "Sleeping"); break; case SunSpec::SunSpecOperatingState::Starting: - thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Starting"); + thing->setStateValue(sunspecThreePhaseInverterOperatingStateStateTypeId, "Starting"); break; case SunSpec::SunSpecOperatingState::Throttled: - thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Throttled"); + thing->setStateValue(sunspecThreePhaseInverterOperatingStateStateTypeId, "Throttled"); break; case SunSpec::SunSpecOperatingState::ShuttingDown: - thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Shutting down"); + thing->setStateValue(sunspecThreePhaseInverterOperatingStateStateTypeId, "Shutting down"); break; } switch(inverterData.event) { case SunSpec::SunSpecEvent1::OVER_TEMP: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Over temperature"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Over temperature"); break; case SunSpec::SunSpecEvent1::UNDER_TEMP: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Under temperature"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Under temperature"); break; case SunSpec::SunSpecEvent1::GroundFault: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Ground fault"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Ground fault"); break; case SunSpec::SunSpecEvent1::MEMORY_LOSS: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Memory loss"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Memory loss"); break; case SunSpec::SunSpecEvent1::AC_OVER_VOLT: - thing->setStateValue(sunspecInverterErrorStateTypeId, "AC voltage above limit"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "AC voltage above limit"); break; case SunSpec::SunSpecEvent1::CABINET_OPEN: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Cabinet open"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Cabinet open"); break; case SunSpec::SunSpecEvent1::AC_DISCONNECT: - thing->setStateValue(sunspecInverterErrorStateTypeId, "AC disconnect open"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "AC disconnect open"); break; case SunSpec::SunSpecEvent1::AC_UNDER_VOLT: - thing->setStateValue(sunspecInverterErrorStateTypeId, "AC voltage under limit"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "AC voltage under limit"); break; case SunSpec::SunSpecEvent1::DC_DISCONNECT: - thing->setStateValue(sunspecInverterErrorStateTypeId, "DC disconnect open"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "DC disconnect open"); break; case SunSpec::SunSpecEvent1::DcOverVolatage: - thing->setStateValue(sunspecInverterErrorStateTypeId, "DC over voltage"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "DC over voltage"); break; case SunSpec::SunSpecEvent1::OVER_FREQUENCY: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Frequency above limit"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Frequency above limit"); break; case SunSpec::SunSpecEvent1::GRID_DISCONNECT: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Grid disconnect"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Grid disconnect"); break; case SunSpec::SunSpecEvent1::HW_TEST_FAILURE: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Hardware test failure"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Hardware test failure"); break; case SunSpec::SunSpecEvent1::MANUAL_SHUTDOWN: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Manual shutdown"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Manual shutdown"); break; case SunSpec::SunSpecEvent1::UNDER_FREQUENCY: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Frequency under limit"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Frequency under limit"); break; case SunSpec::SunSpecEvent1::BLOWN_STRING_FUSE: - thing->setStateValue(sunspecInverterErrorStateTypeId, "Blown string fuse on input"); + thing->setStateValue(sunspecThreePhaseInverterErrorStateTypeId, "Blown string fuse on input"); break; } } @@ -516,15 +641,3 @@ void IntegrationPluginSunSpec::onMeterDataReceived(const SunSpecMeter::MeterData } //thing->setStateValue(sunspecMeterStorageStateStateTypeId, meterData.event); } - -void IntegrationPluginSunSpec::onTrackerDataReceived(const SunSpecTracker::TrackerData &trackerData) -{ - Q_UNUSED(trackerData) - SunSpecTracker *tracker = static_cast(sender()); - Thing *thing = m_sunSpecTrackers.key(tracker); - - if(!thing) { - return; - } - //thing->setStateValue(sunspecStorageStorageStateStateTypeId, storageData.chargingState); -} diff --git a/sunspec/integrationpluginsunspec.h b/sunspec/integrationpluginsunspec.h index c4aad1f..a5d328a 100644 --- a/sunspec/integrationpluginsunspec.h +++ b/sunspec/integrationpluginsunspec.h @@ -52,20 +52,26 @@ class IntegrationPluginSunSpec: public IntegrationPlugin public: explicit IntegrationPluginSunSpec(); + void init() override; void setupThing(ThingSetupInfo *info) override; void postSetupThing(Thing *thing) override; void thingRemoved(Thing *thing) override; void executeAction(ThingActionInfo *info) override; private: + QHash m_mapIdParamTypeIds; + QHash m_modbusAddressParamTypeIds; + + QHash m_connectedStateTypeIds; PluginTimer *m_refreshTimer = nullptr; QHash m_asyncActions; - QHash m_sunSpecConnections; + QHash m_sunSpecConnections; QHash m_sunSpecInverters; QHash m_sunSpecStorages; QHash m_sunSpecMeters; - QHash m_sunSpecTrackers; + + bool checkIfThingExists(uint mapId, uint modbusAddress); private slots: void onRefreshTimer(); @@ -77,13 +83,15 @@ private slots: 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 onWriteRequestExecuted(QUuid requestId, bool success); void onWriteRequestError(QUuid requestId, const QString &error); void onInverterDataReceived(const SunSpecInverter::InverterData &inverterData); void onStorageDataReceived(const SunSpecStorage::StorageData &storageData); void onMeterDataReceived(const SunSpecMeter::MeterData &meterData); - void onTrackerDataReceived(const SunSpecTracker::TrackerData &trackerData); }; #endif // INTEGRATIONPLUGINSUNSPEC_H diff --git a/sunspec/integrationpluginsunspec.json b/sunspec/integrationpluginsunspec.json index 209da62..a0c1a72 100644 --- a/sunspec/integrationpluginsunspec.json +++ b/sunspec/integrationpluginsunspec.json @@ -46,7 +46,8 @@ "id": "6be6abc4-e2b2-4687-9343-0e5164ed0ab2", "name":"ipAddress", "displayName": "IP address", - "type": "QString" + "type": "QString", + "defaultValue": "127.0.0.1" }, { "id": "1fa4fc9c-f6be-47c7-928a-bcefc1142eec", @@ -100,41 +101,96 @@ ] }, { - "name": "sunspecInverter", - "displayName": "SunSpec Inverter", + "name": "sunspecSinglePhaseInverter", + "displayName": "SunSpec single phase inverter", + "id": "c5d5204e-3375-4b92-8128-fab77a671fed", + "createMethods": [ "Auto" ], + "interfaces": ["connectable"], + "paramTypes": [ + { + "id": "41715d00-a947-4f43-a475-cea05790e01d", + "name":"mapId", + "displayName": "Map id", + "type": "int", + "readOnly": true + }, + { + "id": "26ae9050-7090-453a-85a3-307bfebe6fed", + "name":"modbusAddress", + "displayName": "Modbus address", + "type": "uint", + "readOnly": true, + "defaultValue": 0 + } + ], + "stateTypes":[ + { + "id": "48bf31c4-fda7-41e5-a3ef-3011bf96e104", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + } + ] + }, + { + "name": "sunspecSplitPhaseInverter", + "displayName": "SunSpec split phase inverter", + "id": "61b38f93-d331-42bf-b1ef-d3fb16ad1230", + "createMethods": [ "Auto" ], + "interfaces": ["connectable"], + "paramTypes": [ + { + "id": "c42fb50e-210f-4b53-88eb-fa216e15f88f", + "name":"mapId", + "displayName": "Map id", + "type": "int", + "readOnly": true + }, + { + "id": "37582a96-f2f2-4845-abef-973c7dd0ad57", + "name":"modbusAddress", + "displayName": "Modbus address", + "type": "uint", + "readOnly": true, + "defaultValue": 0 + } + ], + "stateTypes":[ + { + "id": "27b49640-f58b-466e-a225-a4663cf3ed96", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + } + ] + }, + { + "name": "sunspecThreePhaseInverter", + "displayName": "SunSpec three phase inverter", "id": "2e4122ea-96a5-415c-b5e2-7d6012265a83", "createMethods": [ "Auto" ], "interfaces": ["connectable"], "paramTypes": [ { - "id": "6ffaa694-a4f2-4936-b043-37679449a34b", - "name":"ipAddress", - "displayName": "IP address", - "type": "QString" + "id": "8d5b2b58-ce46-406d-844e-f53136afcf09", + "name":"mapId", + "displayName": "Map id", + "type": "int", + "readOnly": true }, { - "id": "418d90eb-16e9-47e4-83e3-a116a8fa4de7", - "name":"manufacturer", - "displayName": "Manufacturer", - "type": "QString", + "id": "e5465ede-9d3d-4558-b614-40dda743ddae", + "name":"modbusAddress", + "displayName": "Modbus address", + "type": "uint", "readOnly": true, - "defaultValue": "Unkown" - }, - { - "id": "edf1bc0f-d143-4b75-a11f-635339fb30bf", - "name":"deviceModel", - "displayName": "Device model", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" - }, - { - "id": "d5181377-c746-4352-8683-6d90a4a83e6e", - "name":"serialNumber", - "displayName": "Serial number", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" + "defaultValue": 0 } ], "stateTypes":[ @@ -162,54 +218,54 @@ "name": "totalCurrent", "displayName": "Total AC current", "displayNameEvent": "Total AC current changed", - "type": "int", + "type": "double", "unit": "Ampere", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "3140ccd3-40cf-46c8-8bb2-8c3ea4582f84", "name": "phaseACurrent", "displayName": "Phase A current", "displayNameEvent": "Phase A current changed", - "type": "int", + "type": "double", "unit": "Ampere", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "7ea1a53a-6fd9-4914-8283-b57aa1aaaebf", "name": "phaseBCurrent", "displayName": "Phase B current", "displayNameEvent": "Phase B current changed", - "type": "int", + "type": "double", "unit": "Ampere", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "aa4b4cf5-43d0-4be5-9505-403918b5371d", "name": "phaseCCurrent", "displayName": "Phase C current", "displayNameEvent": "Phase C current changed", - "type": "int", + "type": "double", "unit": "Ampere", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "f08521aa-9c38-4c31-95e1-acb616f6e9c6", "name": "phaseANVoltage", "displayName": "Phase AN voltage", "displayNameEvent": "Phase AN volatage changed", - "type": "int", + "type": "double", "unit": "Volt", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "739b8805-d522-4406-bede-d1e4200a3aa9", "name": "phaseBNVoltage", "displayName": "Phase BN voltage", "displayNameEvent": "Phase BN voltage changed", - "type": "int", + "type": "double", "unit": "Volt", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "949b797d-5566-4667-8982-e430d23548e2", @@ -225,36 +281,36 @@ "name": "acPower", "displayName": "AC power", "displayNameEvent": "AC power changed", - "type": "int", + "type": "double", "unit": "KiloWatt", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "faa45cae-ed28-4150-9036-fceddf9d6776", "name": "lineFrequency", "displayName": "Line frequency", "displayNameEvent": "Line frequency changed", - "type": "int", + "type": "double", "unit": "Hertz", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "d493880d-eb58-4530-8010-8ea4f6d63387", "name": "acEnergy", "displayName": "AC energy", "displayNameEvent": "AC energy changed", - "type": "int", + "type": "double", "unit": "KiloWattHour", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "44b0320f-89a7-4248-bad4-288ef898a5cc", "name": "cabinetTemperature", "displayName": "Cabinet temperature", "displayNameEvent": "Cabinet temperature changed", - "type": "int", + "type": "double", "unit": "DegreeCelsius", - "defaultValue": 0 + "defaultValue": 0.00 }, { "id": "cebdce98-42d1-4a28-8834-8960efc0e83f", @@ -304,41 +360,99 @@ ] }, { - "name": "sunspecMeter", - "displayName": "SunSpec Meter", + "name": "sunspecSinglePhaseMeter", + "displayName": "SunSpec single phase meter", + "id": "7ffa43b8-b56f-4435-8509-980e9d81dfa8", + "createMethods": [ "Auto" ], + "interfaces": ["connectable"], + "paramTypes": [ + { + "id": "7d6fcafb-c62e-4a21-aae2-f4041c487149", + "name":"mapId", + "displayName": "Map id", + "type": "int", + "readOnly": true, + "defaultValue": 0 + }, + { + "id": "30b90ec0-429b-4e6c-88e9-155aa4bcad47", + "name":"modbusAddress", + "displayName": "Modbus address", + "type": "uint", + "readOnly": true, + "defaultValue": 0 + } + ], + "stateTypes":[ + { + "id": "d960e7b1-d4aa-4cab-8f54-6bcfdbb8be36", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + } + ] + }, + { + "name": "sunspecSplitPhaseMeter", + "displayName": "SunSpec split phase meter", + "id": "b8a18e45-5ff5-4f43-915f-04ee216c809d", + "createMethods": [ "Auto" ], + "interfaces": ["connectable"], + "paramTypes": [ + { + "id": "89aeec6d-abeb-48b5-9594-214ad5db2d03", + "name":"mapId", + "displayName": "Map id", + "type": "int", + "readOnly": true, + "defaultValue": 0 + }, + { + "id": "a56f198d-ed86-429f-b839-8e11a32da8c1", + "name":"modbusAddress", + "displayName": "Modbus address", + "type": "uint", + "readOnly": true, + "defaultValue": 0 + } + ], + "stateTypes":[ + { + "id": "34e34ec9-dab0-438c-9493-a3068bc401de", + "name": "connected", + "displayName": "Connected", + "displayNameEvent": "Connected changed", + "type": "bool", + "defaultValue": false, + "cached": false + } + ] + }, + { + "name": "sunspecThreePhaseMeter", + "displayName": "SunSpec three phase meter", "id": "68f822f9-ff30-4275-b229-39a3674fead7", "createMethods": [ "Auto" ], "interfaces": ["connectable"], "paramTypes": [ { - "id": "66bdb89b-bbd4-4edf-96df-649014d94c42", - "name":"ipAddress", - "displayName": "IP address", - "type": "QString" + "id": "a1960821-155c-4176-86fa-974429039182", + "name":"mapId", + "displayName": "Map id", + "type": "int", + "readOnly": true, + "defaultValue": 0 }, { - "id": "aa1ca51d-5e9c-4a1a-a4ec-e479d4f702d5", - "name":"manufacturer", - "displayName": "Manufacturer", - "type": "QString", + "id": "6d5dbd35-1bf6-46db-bee9-90c679421b89", + "name":"modbusAddress", + "displayName": "Modbus address", + "type": "uint", "readOnly": true, - "defaultValue": "Unkown" - }, - { - "id": "52da5388-28d7-48a2-8ad6-4c1e852a4348", - "name":"deviceModel", - "displayName": "Device model", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" - }, - { - "id": "9fa7288c-e7a1-4163-baa1-6f484e1feb55", - "name":"serialNumber", - "displayName": "Serial number", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" + "defaultValue": 0 } ], "stateTypes":[ @@ -442,8 +556,7 @@ "defaultValue": 0 }, { - "id": "73ebf57f-1ad2-4d19-bfd9-9e0a514c1243 -", + "id": "73ebf57f-1ad2-4d19-bfd9-9e0a514c1243", "name": "energyExported", "displayName": "Total real energy exported", "displayNameEvent": "Total real energy exported changed", @@ -462,66 +575,6 @@ } ] }, - { - "name": "sunspecTracker", - "displayName": "SunSpec Tracker", - "id": "9941da30-a6d6-475d-8244-3c2145b419e6", - "createMethods": [ "Auto" ], - "interfaces": ["connectable"], - "paramTypes": [ - { - "id": "ccdcca78-7efa-4253-8fdb-8731e6880e9b", - "name":"ipAddress", - "displayName": "IP address", - "type": "QString" - }, - { - "id": "2795052c-5d63-423d-b44e-fdf02dfaac37", - "name":"manufacturer", - "displayName": "Manufacturer", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" - }, - { - "id": "7528c840-da8c-40ba-ab82-914cfc686a49", - "name":"deviceModel", - "displayName": "Device model", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" - }, - { - "id": "ce9cc598-947c-4efc-b04f-21e784c5d2df", - "name":"serialNumber", - "displayName": "Serial number", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" - } - ], - "stateTypes":[ - { - "id": "fe57e465-e49e-4d50-b880-6d9a243783ff", - "name": "connected", - "displayName": "Connected", - "displayNameEvent": "Connected changed", - "type": "bool", - "defaultValue": false, - "cached": false - }, - { - "id": "d2bbba4b-998f-444e-935f-f958927afddd", - "name": "power", - "displayName": "Power", - "displayNameEvent": "Power changed", - "displayNameAction": "Change power", - "type": "bool", - "defaultValue": 0, - "writable": true - } - ] - }, { "name": "sunspecStorage", "displayName": "SunSpec Storage", @@ -530,34 +583,20 @@ "interfaces": ["connectable"], "paramTypes": [ { - "id": "421cacb9-a94d-4ba9-923c-3bc30d8b94c8", - "name":"ipAddress", - "displayName": "IP address", - "type": "QString" + "id": "219beb96-b9fe-4dd2-a386-ecfbbab8786d", + "name":"mapId", + "displayName": "Map id", + "type": "int", + "readOnly": true, + "defaultValue": 0 }, { - "id": "1d8f1ef7-33ad-40ed-99dd-ec54f87c2f17", - "name":"manufacturer", - "displayName": "Manufacturer", - "type": "QString", + "id": "3f107844-00c5-4f39-86e5-485b3d1f5c1a", + "name":"modbusAddress", + "displayName": "Modbus address", + "type": "uint", "readOnly": true, - "defaultValue": "Unkown" - }, - { - "id": "2a7f8464-6f03-4113-ac1d-d8ebd8705695", - "name":"deviceModel", - "displayName": "Device model", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" - }, - { - "id": "2003acdb-38ad-4184-baf8-9ef8e67fc452", - "name":"serialNumber", - "displayName": "Serial number", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" + "defaultValue": 0 } ], "stateTypes":[ @@ -663,56 +702,6 @@ "displayNameAction": "Set discharging rate" } ] - }, - { - "name": "sunspecStringCombiner", - "displayName": "SunSpec String Combiner", - "id": "7787f238-f5a3-4ba5-b3ec-7d513b87bf71", - "createMethods": [ "Auto" ], - "interfaces": ["connectable"], - "paramTypes": [ - { - "id": "226697c3-37bd-49c7-8c0e-40537573c18e", - "name":"ipAddress", - "displayName": "IP address", - "type": "QString" - }, - { - "id": "3f126e65-05e2-46a3-aeeb-bfacc0bdd9b0", - "name":"manufacturer", - "displayName": "Manufacturer", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" - }, - { - "id": "40666030-3609-467f-b26c-3a42d1c6dca1", - "name":"deviceModel", - "displayName": "Device model", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" - }, - { - "id": "f5046138-9271-4a91-a3eb-c2ecfa755ef5", - "name":"serialNumber", - "displayName": "Serial number", - "type": "QString", - "readOnly": true, - "defaultValue": "Unkown" - } - ], - "stateTypes":[ - { - "id": "3f791612-50fd-4a3c-ac19-27e01dd8c63a", - "name": "connected", - "displayName": "Connected", - "displayNameEvent": "Connected changed", - "type": "bool", - "defaultValue": false, - "cached": false - } - ] } ] } diff --git a/sunspec/sunspec.cpp b/sunspec/sunspec.cpp index 1947a8a..40b5950 100644 --- a/sunspec/sunspec.cpp +++ b/sunspec/sunspec.cpp @@ -37,6 +37,7 @@ SunSpec::SunSpec(const QHostAddress &hostAddress, uint port, QObject *parent) : m_hostAddress(hostAddress), m_port(port) { + qCDebug(dcSunSpec()) << "SunSpec: Creating SunSpec connection"; m_modbusTcpClient = new QModbusTcpClient(this); m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_port); m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_hostAddress.toString()); @@ -48,16 +49,19 @@ SunSpec::SunSpec(const QHostAddress &hostAddress, uint port, QObject *parent) : SunSpec::~SunSpec() { + qCDebug(dcSunSpec()) << "SunSpec: Deleting SunSpec connection"; } bool SunSpec::connectModbus() { + qCDebug(dcSunSpec()) << "SunSpec: Connect modbus"; return m_modbusTcpClient->connectDevice(); } void SunSpec::setHostAddress(const QHostAddress &hostAddress) { if (m_hostAddress != hostAddress) { + qCDebug(dcSunSpec()) << "SunSpec: Set host address" << hostAddress.toString(); m_hostAddress = hostAddress; m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_hostAddress.toString()); } @@ -65,25 +69,46 @@ void SunSpec::setHostAddress(const QHostAddress &hostAddress) void SunSpec::setPort(uint port) { - m_port = port; - m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, port); + if (port != m_port) { + qCDebug(dcSunSpec()) << "SunSpec: Set Port" << port; + m_port = port; + m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, port); + } } void SunSpec::setSlaveId(uint slaveId) { + qCDebug(dcSunSpec()) << "SunSpec: Set slave id" << slaveId; m_slaveId = slaveId; } void SunSpec::setTimeout(uint milliSeconds) { + qCDebug(dcSunSpec()) << "SunSpec: Set timeout" << milliSeconds << "[ms]"; m_modbusTcpClient->setTimeout(milliSeconds); } void SunSpec::setNumberOfRetries(uint retries) { + qCDebug(dcSunSpec()) << "SunSpec: Set number of retries" << retries; m_modbusTcpClient->setNumberOfRetries(retries); } +QHostAddress SunSpec::hostAddress() const +{ + return m_hostAddress; +} + +uint SunSpec::port() +{ + return m_port; +} + +uint SunSpec::slaveId() +{ + return m_slaveId; +} + QString SunSpec::manufacturer() { return m_manufacturer; @@ -101,49 +126,45 @@ QString SunSpec::serialNumber() void SunSpec::findBaseRegister() { - qCDebug(dcSunSpec()) << "Find base register"; + qCDebug(dcSunSpec()) << "SunSpec: Find base register"; QList validBaseRegisters; validBaseRegisters.append(0); validBaseRegisters.append(40000); validBaseRegisters.append(50000); Q_FOREACH (int baseRegister, validBaseRegisters) { + qCDebug(dcSunSpec()) << " - Searching address" << baseRegister; QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, baseRegister, 2); if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); - connect(reply, &QModbusReply::finished, this, [reply, this] { + connect(reply, &QModbusReply::finished, this, [reply, baseRegister, this] { if (reply->error() == QModbusDevice::NoError) { const QModbusDataUnit unit = reply->result(); - uint modbusAddress = unit.startAddress(); if ((unit.value(0) << 16 | unit.value(1)) == 0x53756e53) { //Well-known value. Uniquely identifies this as a SunSpec Modbus Map - qCDebug(dcSunSpec()) << "Found start of modbus map" << modbusAddress; - m_baseRegister = modbusAddress; - emit foundBaseRegister(modbusAddress); + qCDebug(dcSunSpec()) << "SunSpec: Found start of modbus map" << baseRegister; + m_baseRegister = baseRegister; + emit foundBaseRegister(baseRegister); } else { - qCWarning(dcSunSpec()) << "Got reply on base register, but value didn't mach 0x53756e53"; + qCWarning(dcSunSpec()) << "SunSpec: Got reply on base register" << baseRegister << ", but value didn't mach 0x53756e53"; } } else { - qCWarning(dcSunSpec()) << "Find base register read response error:" << reply->error(); + qCDebug(dcSunSpec()) << "SunSpec: Find base register not found at:" << baseRegister; } }); } else { - qCWarning(dcSunSpec()) << "Find base register eead error: " << m_modbusTcpClient->errorString(); delete reply; // broadcast replies return immediately return; } - } else { - qCWarning(dcSunSpec()) << "Find base register read error: " << m_modbusTcpClient->errorString(); - return; } } } void SunSpec::findModbusMap(const QList &ids, uint modbusAddressOffset) { - qCDebug(dcSunSpec()) << "Find modbus map. Start register" << m_baseRegister+modbusAddressOffset; + qCDebug(dcSunSpec()) << "SunSpec: Find modbus map. Start register" << m_baseRegister+modbusAddressOffset; QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_baseRegister+modbusAddressOffset, 2); if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) { @@ -156,41 +177,37 @@ void SunSpec::findModbusMap(const QList &ids, uint modbusAddressOffset) uint modbusAddress = unit.startAddress(); BlockId blockId = BlockId(unit.value(0)); int blockLength = unit.value(1); - if (blockId > 800 || blockId == BlockIdEnd) { - qCDebug(dcSunSpec()) << "Block id not found, Id:" << ids; - modbusMapSearchFinished(ids, 0, "Ids not found"); + if (blockId == BlockIdEnd) { + qCDebug(dcSunSpec()) << "SunSpec: Block Id End"; + modbusMapSearchFinished(m_mapList); return; } - if (ids.contains(blockId)) { - qCDebug(dcSunSpec()) << "Found block" << BlockId(blockId); + + 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); - } else { - //read next block header - qCDebug(dcSunSpec()) << "Found Block" << blockId << "with block length" << blockLength; - findModbusMap(ids, modbusAddress+2+blockLength-m_baseRegister); //read next block } + findModbusMap(ids, modbusAddress+2+blockLength-m_baseRegister); //read next block } else { - qCWarning(dcSunSpec()) << "Read response error:" << reply->error(); + qCWarning(dcSunSpec()) << "SunSpec: Find modbus map, read response error:" << reply->error(); } }); - connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error) { - qCWarning(dcSunSpec()) << "Modbus replay error:" << error; - reply->finished(); // To make sure it will be deleted - }); } 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; } } void SunSpec::readMapHeader(uint modbusAddress) { - qCDebug(dcSunSpec()) << "Read block header. Modbus Address:" << modbusAddress << "Slave ID" << m_slaveId; - if (modbusAddress == 40000) + qCDebug(dcSunSpec()) << "SunSpec: Read map header, modbus address:" << modbusAddress << "Slave ID" << m_slaveId; + if (modbusAddress == 0 || modbusAddress == 40000 || modbusAddress == 50000) modbusAddress += 2; QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, modbusAddress, 2); @@ -228,7 +245,7 @@ void SunSpec::readMapHeader(uint modbusAddress) void SunSpec::readMap(uint modbusAddress, uint modelLength) { - qCDebug(dcSunSpec()) << "Read map. Modbus Address" << modbusAddress << ", Slave ID" << m_slaveId; + qCDebug(dcSunSpec()) << "SunSpec: Read map, modbus address" << modbusAddress << "model length" << modelLength << ", Slave ID" << m_slaveId; QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, modbusAddress, modelLength+2); @@ -265,18 +282,7 @@ void SunSpec::readMap(uint modbusAddress, uint modelLength) void SunSpec::readCommonMap() { - qCDebug(dcSunSpec()) << "Read common block"; - - // Base and Alternate Base Register Addresses - // DeviceModbus maps begin at one of three well-­known Modbus base addresses. - // Preferred Base Register: 40000 - // Alternate Base Register: 50000 - // Alternate Base Register: 00000 - - // Common Model model-length is 66 - // First two registers are the SunSpec Modbus Map identifier - - qCDebug(dcSunSpec()) << "Read common block header. Modbus Address" << m_baseRegister+2 << ", Slave ID" << m_slaveId; + qCDebug(dcSunSpec()) << "SunSpec: Read common block header. Modbus Address" << m_baseRegister+2 << ", Slave ID" << m_slaveId; QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_baseRegister+2, 66); @@ -287,27 +293,23 @@ 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); + //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); m_deviceModel.remove('\x00'); m_serialNumber = convertModbusRegisters(unit.values(), MandatoryRegistersModel1::SerialNumber, 16); m_serialNumber.remove('\x00'); - qCDebug(dcSunSpec()) << "Received common block response. Manufacturer" << m_manufacturer << "Model" << m_deviceModel << "Serial number" << m_serialNumber; - mapHeaderReceived(modbusAddress, mapId, mapLength); + qCDebug(dcSunSpec()) << "SunSpec: Received common block response. Manufacturer" << m_manufacturer << "Model" << m_deviceModel << "Serial number" << m_serialNumber; + commonMapReceived(m_manufacturer, m_deviceModel, m_serialNumber); } else { - qCWarning(dcSunSpec()) << "Read response error:" << reply->error(); + qCWarning(dcSunSpec()) << "SunSpec: Read common map, read response error:" << reply->error(); } }); - connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error) { - qCWarning(dcSunSpec()) << "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 common map read error: " << m_modbusTcpClient->errorString(); delete reply; // broadcast replies return immediately return; } @@ -390,7 +392,7 @@ QByteArray SunSpec::convertModbusRegisters(const QVector &modbusData, i float SunSpec::convertValueWithSSF(quint16 rawValue, quint16 sunssf) { float value; - value = rawValue * (10^static_cast(sunssf)); + value = rawValue * pow(10, static_cast(sunssf)); return value; } @@ -404,26 +406,24 @@ float SunSpec::convertFloatValues(quint16 rawValue0, quint16 rawValue1) void SunSpec::onModbusStateChanged(QModbusDevice::State state) { - bool connected = (state != QModbusDevice::UnconnectedState); + bool connected = (state == QModbusDevice::ConnectedState); if (!connected) { //try to reconnect in 10 seconds QTimer::singleShot(10000, m_modbusTcpClient, [this] { if (m_modbusTcpClient->connectDevice()) { - qCDebug(dcSunSpec()) << "Could not reconnect"; + qCDebug(dcSunSpec()) << "SunSpec: Could not reconnect"; } }); - } else { - readCommonMap(); } emit connectionStateChanged(connected); } -QUuid SunSpec::writeHoldingRegister(uint slaveAddress, uint registerAddress, quint16 value) +QUuid SunSpec::writeHoldingRegister(uint registerAddress, quint16 value) { - return writeHoldingRegisters(slaveAddress, registerAddress, QVector() << value); + return writeHoldingRegisters(registerAddress, QVector() << value); } -QUuid SunSpec::writeHoldingRegisters(uint slaveAddress, uint registerAddress, const QVector &values) +QUuid SunSpec::writeHoldingRegisters(uint registerAddress, const QVector &values) { if (!m_modbusTcpClient) { return ""; @@ -432,7 +432,7 @@ QUuid SunSpec::writeHoldingRegisters(uint slaveAddress, uint registerAddress, co QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, values.length()); request.setValues(values); - if (QModbusReply *reply = m_modbusTcpClient->sendWriteRequest(request, slaveAddress)) { + if (QModbusReply *reply = m_modbusTcpClient->sendWriteRequest(request, m_slaveId)) { if (!reply->isFinished()) { connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater); connect(reply, &QModbusReply::finished, this, [reply, requestId, this] { diff --git a/sunspec/sunspec.h b/sunspec/sunspec.h index 73d75de..56ebff3 100644 --- a/sunspec/sunspec.h +++ b/sunspec/sunspec.h @@ -178,6 +178,9 @@ public: void setTimeout(uint milliSeconds); void setNumberOfRetries(uint retries); + QHostAddress hostAddress() const; + uint port(); + uint slaveId(); QString manufacturer(); QString deviceModel(); QString serialNumber(); @@ -191,13 +194,14 @@ public: QString m_manufacturer = "Unknown"; QString m_deviceModel = "Unknown"; QString m_serialNumber = "Unknown"; + QHash m_mapList; void findBaseRegister(); - void findModbusMap(const QList &mapIds, uint modbusAddressOffset = 69); + void findModbusMap(const QList &mapIds, 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 readCommonMap(); float convertValueWithSSF(quint16 rawValue, quint16 sunssf); float convertFloatValues(quint16 rawValue0, quint16 rawValue1); @@ -205,16 +209,18 @@ public: QBitArray convertModbusRegisterBits(const uint16_t &modbusData); QByteArray convertModbusRegisters(const QVector &modbusData, int offset, int size); - QUuid writeHoldingRegister(uint slaveAddress, uint registerAddress, quint16 value); - QUuid writeHoldingRegisters(uint slaveAddress, uint registerAddress, const QVector &values); + QUuid writeHoldingRegister(uint registerAddress, quint16 value); + QUuid writeHoldingRegisters(uint registerAddress, const QVector &values); signals: void connectionStateChanged(bool status); void requestExecuted(QUuid requetId, bool success); void foundBaseRegister(int modbusAddress); + void commonMapReceived(const QString &manufacturer, const QString &deviceModel, const QString &serialNumber); + void foundModbusMap(BlockId mapId, int modbusStartRegister); - void modbusMapSearchFinished(const QList &mapIds, uint modbusStartRegister, const QString &error); + void modbusMapSearchFinished(const QHash &mapIds); void mapHeaderReceived(uint modbusAddress, BlockId mapId, uint mapLength); void mapReceived(BlockId mapId, uint mapLength, QVector data); diff --git a/sunspec/sunspec.pro b/sunspec/sunspec.pro index f005cb0..060a701 100644 --- a/sunspec/sunspec.pro +++ b/sunspec/sunspec.pro @@ -9,15 +9,11 @@ SOURCES += \ sunspec.cpp \ sunspecinverter.cpp \ sunspecmeter.cpp \ - sunspecstorage.cpp \ - sunspectracker.cpp \ - sunspecstringcombiner.cpp \ + sunspecstorage.cpp HEADERS += \ integrationpluginsunspec.h \ sunspec.h \ sunspecinverter.h \ sunspecmeter.h \ - sunspecstorage.h \ - sunspectracker.h \ - sunspecstringcombiner.h \ + sunspecstorage.h diff --git a/sunspec/sunspecinverter.cpp b/sunspec/sunspecinverter.cpp index 295d636..9dc9adb 100644 --- a/sunspec/sunspecinverter.cpp +++ b/sunspec/sunspecinverter.cpp @@ -31,95 +31,121 @@ #include "sunspecinverter.h" #include "extern-plugininfo.h" -SunSpecInverter::SunSpecInverter(const QHostAddress &hostAddress, uint port, QObject *parent) : SunSpec(hostAddress, port, parent) +#include + +SunSpecInverter::SunSpecInverter(SunSpec *sunspec, SunSpec::BlockId mapId, int modbusAddress) : + QObject(sunspec), + m_connection(sunspec), + m_id(mapId), + m_mapModbusStartRegister(modbusAddress) { - connect(m_modbusTcpClient, &QModbusClient::stateChanged, this, [this] (QModbusDevice::State state) { - if (state == QModbusDevice::ConnectedState) { - qCDebug(dcSunSpec()) << "Inverter connected successfully"; - QList mapIds; - mapIds.append(BlockIdInverterSinglePhase); - mapIds.append(BlockIdInverterSplitPhase); - mapIds.append(BlockIdInverterThreePhase); - mapIds.append(BlockIdInverterSinglePhaseFloat); - mapIds.append(BlockIdInverterSplitPhaseFloat); - mapIds.append(BlockIdInverterThreePhaseFloat); - findModbusMap(mapIds); + qCDebug(dcSunSpec()) << "SunSpecInverter: Setting up inverter"; + connect(m_connection, &SunSpec::mapReceived, this, &SunSpecInverter::onModbusMapReceived); +} + +SunSpec::BlockId SunSpecInverter::blockId() +{ + return m_id; +} + +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; + emit initFinished(true); + m_initFinishedSuccess = true; + }); + QTimer::singleShot(10000, this,[this] { + if (!m_initFinishedSuccess) { + emit initFinished(false); } }); - connect(this, &SunSpec::foundModbusMap, this, [this] (BlockId mapId, uint modbusRegisterAddress) { - qCDebug(dcSunSpec()) << "Read map header for mapId" << mapId << "and modbus register" << modbusRegisterAddress; - readMapHeader(modbusRegisterAddress); - }); - - connect(this, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, BlockId mapId, uint mapLength) { - m_id = mapId; - m_mapLength = mapLength; - m_mapModbusStartRegister = modbusAddress; - readMap(modbusAddress, mapLength); - }); - - connect(this, &SunSpec::mapReceived, this, &SunSpecInverter::onModbusMapReceived); } void SunSpecInverter::getInverterMap() { - readMap(m_mapModbusStartRegister, m_mapLength); + // TODO check map length to modbus max value + m_connection->readMap(m_mapModbusStartRegister, m_mapLength); } void SunSpecInverter::readInverterBlockHeader() { - readMapHeader(m_mapModbusStartRegister); + m_connection->readMapHeader(m_mapModbusStartRegister); } void SunSpecInverter::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector data) { Q_UNUSED(mapLength) + if (mapId != m_id) { + return; + } + if (mapLength < m_mapLength) { + qCDebug(dcSunSpec()) << "SunSpecInverter: on modbus map received, map length ist too short" << mapLength; + //return; + } + InverterData inverterData; + switch (mapId) { - case BlockIdInverterSinglePhase: - case BlockIdInverterSplitPhase: - case BlockIdInverterThreePhase: { - InverterData inverterData; - inverterData.acCurrent= convertValueWithSSF(data[Model10X::Model10XAcCurrent], data[Model10X::Model10XAmpereScaleFactor]); - inverterData.acPower = convertValueWithSSF(data[Model10X::Model10XACPower], data[Model10X::Model10XWattScaleFactor]); - inverterData.lineFrequency = convertValueWithSSF(data[Model10X::Model10XLineFrequency], data[Model10X::Model10XHerzScaleFactor]); + case SunSpec::BlockIdInverterSinglePhase: + case SunSpec::BlockIdInverterSplitPhase: + case SunSpec::BlockIdInverterThreePhase: { - inverterData.phaseACurrent = convertValueWithSSF(data[Model10X::Model10XPhaseACurrent], data[Model10X::Model10XAmpereScaleFactor]); - inverterData.phaseBCurrent = convertValueWithSSF(data[Model10X::Model10XPhaseBCurrent], data[Model10X::Model10XAmpereScaleFactor]); - inverterData.phaseCCurrent = convertValueWithSSF(data[Model10X::Model10XPhaseCCurrent], data[Model10X::Model10XAmpereScaleFactor]); + qCDebug(dcSunSpec()) << "Inverter with SSF values:"; + qCDebug(dcSunSpec()) << " - AC Current:" << data[Model10X::Model10XAcCurrent]; + qCDebug(dcSunSpec()) << " - Phase A Current:" << data[Model10X::Model10XPhaseACurrent]; + qCDebug(dcSunSpec()) << " - Phase B Current:" << data[Model10X::Model10XPhaseBCurrent]; + qCDebug(dcSunSpec()) << " - Phase C Current:" << data[Model10X::Model10XPhaseCCurrent]; + qCDebug(dcSunSpec()) << " - Ampere scale factor:" << static_cast(data[Model10X::Model10XAmpereScaleFactor]); - inverterData.phaseVoltageAN = convertValueWithSSF(data[Model10X::Model10XPhaseVoltageAN], data[Model10X::Model10XVoltageScaleFactor]); - inverterData.phaseVoltageBN = convertValueWithSSF(data[Model10X::Model10XPhaseVoltageBN], data[Model10X::Model10XVoltageScaleFactor]); - inverterData.phaseVoltageCN = convertValueWithSSF(data[Model10X::Model10XPhaseVoltageCN], data[Model10X::Model10XVoltageScaleFactor]); + inverterData.acCurrent= m_connection->convertValueWithSSF(data[Model10X::Model10XAcCurrent], data[Model10X::Model10XAmpereScaleFactor]); + inverterData.acPower = m_connection->convertValueWithSSF(data[Model10X::Model10XACPower], data[Model10X::Model10XWattScaleFactor]); + inverterData.lineFrequency = m_connection->convertValueWithSSF(data[Model10X::Model10XLineFrequency], data[Model10X::Model10XHerzScaleFactor]); - inverterData.acEnergy = convertValueWithSSF(data[Model10X::Model10XAcEnergy], data[Model10X::Model10XWattHoursScaleFactor]); + inverterData.phaseACurrent = m_connection->convertValueWithSSF(data[Model10X::Model10XPhaseACurrent], data[Model10X::Model10XAmpereScaleFactor]); + inverterData.phaseBCurrent = m_connection->convertValueWithSSF(data[Model10X::Model10XPhaseBCurrent], data[Model10X::Model10XAmpereScaleFactor]); + inverterData.phaseCCurrent = m_connection->convertValueWithSSF(data[Model10X::Model10XPhaseCCurrent], data[Model10X::Model10XAmpereScaleFactor]); - inverterData.cabinetTemperature = convertValueWithSSF(data[Model10X::Model10XCabinetTemperature], data[Model10X::Model10XTemperatureScaleFactor]); - inverterData.event = SunSpecEvent1(data[Model10X::Model10XEvent1]); - inverterData.operatingState = SunSpecOperatingState(data[Model10X::Model10XOperatingState]); + inverterData.phaseVoltageAN = m_connection->convertValueWithSSF(data[Model10X::Model10XPhaseVoltageAN], data[Model10X::Model10XVoltageScaleFactor]); + inverterData.phaseVoltageBN = m_connection->convertValueWithSSF(data[Model10X::Model10XPhaseVoltageBN], data[Model10X::Model10XVoltageScaleFactor]); + inverterData.phaseVoltageCN = m_connection->convertValueWithSSF(data[Model10X::Model10XPhaseVoltageCN], data[Model10X::Model10XVoltageScaleFactor]); + + qCDebug(dcSunSpec()) << "AC energy converting:"; + qCDebug(dcSunSpec()) << " - Origin" << data[Model10X::Model10XAcEnergy]; + qCDebug(dcSunSpec()) << " - SSF" << static_cast(data[Model10X::Model10XWattHoursScaleFactor]); + qCDebug(dcSunSpec()) << " - Converted "<< m_connection->convertValueWithSSF(data[Model10X::Model10XAcEnergy], data[Model10X::Model10XWattHoursScaleFactor]); + inverterData.acEnergy = m_connection->convertValueWithSSF(data[Model10X::Model10XAcEnergy], data[Model10X::Model10XWattHoursScaleFactor]); + + inverterData.cabinetTemperature = m_connection->convertValueWithSSF(data[Model10X::Model10XCabinetTemperature], data[Model10X::Model10XTemperatureScaleFactor]); + inverterData.event = SunSpec::SunSpecEvent1(data[Model10X::Model10XEvent1]); + inverterData.operatingState = SunSpec::SunSpecOperatingState(data[Model10X::Model10XOperatingState]); emit inverterDataReceived(inverterData); } break; - case BlockIdInverterSinglePhaseFloat: - case BlockIdInverterSplitPhaseFloat: - case BlockIdInverterThreePhaseFloat: { - InverterData inverterData; - inverterData.acCurrent = convertFloatValues(data[Model11X::Model11XAcCurrent], data[Model11X::Model11XAcCurrent+1]); + case SunSpec::BlockIdInverterThreePhaseFloat: + case SunSpec::BlockIdInverterSplitPhaseFloat: + case SunSpec::BlockIdInverterSinglePhaseFloat: { - inverterData.phaseACurrent = convertFloatValues(data[Model11X::Model11XPhaseACurrent], data[Model11X::Model11XPhaseACurrent+1]); - inverterData.phaseBCurrent = convertFloatValues(data[Model11X::Model11XPhaseBCurrent], data[Model11X::Model11XPhaseBCurrent+1]); - inverterData.phaseCCurrent = convertFloatValues(data[Model11X::Model11XPhaseCCurrent], data[Model11X::Model11XPhaseCCurrent+1]); + inverterData.acCurrent = m_connection->convertFloatValues(data[Model11X::Model11XAcCurrent], data[Model11X::Model11XAcCurrent+1]); - inverterData.phaseVoltageAN = convertFloatValues(data[Model11X::Model11XPhaseVoltageAN], data[Model11X::Model11XPhaseVoltageAN+1]); - inverterData.phaseVoltageBN = convertFloatValues(data[Model11X::Model11XPhaseVoltageBN], data[Model11X::Model11XPhaseVoltageBN+1]); - inverterData.phaseVoltageCN = convertFloatValues(data[Model11X::Model11XPhaseVoltageCN], data[Model11X::Model11XPhaseVoltageCN+1]); + inverterData.phaseCCurrent = m_connection->convertFloatValues(data[Model11X::Model11XPhaseCCurrent], data[Model11X::Model11XPhaseCCurrent+1]); + inverterData.phaseVoltageCN = m_connection->convertFloatValues(data[Model11X::Model11XPhaseVoltageCN], data[Model11X::Model11XPhaseVoltageCN+1]); - inverterData.acPower = convertFloatValues(data[Model11X::Model11XACPower], data[Model11X::Model11XACPower+1]); - inverterData.lineFrequency = convertFloatValues(data[Model11X::Model11XLineFrequency], data[Model11X::Model11XLineFrequency+1]); + inverterData.phaseBCurrent = m_connection->convertFloatValues(data[Model11X::Model11XPhaseBCurrent], data[Model11X::Model11XPhaseBCurrent+1]); + inverterData.phaseVoltageBN = m_connection->convertFloatValues(data[Model11X::Model11XPhaseVoltageBN], data[Model11X::Model11XPhaseVoltageBN+1]); - inverterData.acEnergy = convertFloatValues(data[Model11X::Model11XAcEnergy], data[Model11X::Model11XAcEnergy+1]); - inverterData.cabinetTemperature = convertFloatValues(data[Model11X::Model11XCabinetTemperature], data[Model11X::Model11XCabinetTemperature+1]); - inverterData.event = SunSpecEvent1(data[Model11X::Model11XEvent1]); - inverterData.operatingState = SunSpecOperatingState(data[Model11X::Model11XOperatingState]); + inverterData.phaseACurrent = m_connection->convertFloatValues(data[Model11X::Model11XPhaseACurrent], data[Model11X::Model11XPhaseACurrent+1]); + inverterData.phaseVoltageAN = m_connection->convertFloatValues(data[Model11X::Model11XPhaseVoltageAN], data[Model11X::Model11XPhaseVoltageAN+1]); + + inverterData.acPower = m_connection->convertFloatValues(data[Model11X::Model11XACPower], data[Model11X::Model11XACPower+1]); + inverterData.lineFrequency = m_connection->convertFloatValues(data[Model11X::Model11XLineFrequency], data[Model11X::Model11XLineFrequency+1]); + + inverterData.acEnergy = m_connection->convertFloatValues(data[Model11X::Model11XAcEnergy], data[Model11X::Model11XAcEnergy+1]); + inverterData.cabinetTemperature =m_connection->convertFloatValues(data[Model11X::Model11XCabinetTemperature], data[Model11X::Model11XCabinetTemperature+1]); + inverterData.event = SunSpec::SunSpecEvent1(data[Model11X::Model11XEvent1]); + inverterData.operatingState = SunSpec::SunSpecOperatingState(data[Model11X::Model11XOperatingState]); emit inverterDataReceived(inverterData); } break; default: diff --git a/sunspec/sunspecinverter.h b/sunspec/sunspecinverter.h index f68c68b..2c6ca04 100644 --- a/sunspec/sunspecinverter.h +++ b/sunspec/sunspecinverter.h @@ -34,32 +34,31 @@ #include #include "sunspec.h" -class SunSpecInverter : public SunSpec +class SunSpecInverter : public QObject { Q_OBJECT public: - SunSpecInverter(const QHostAddress &hostAddress, uint port = 502, QObject *parent = 0); enum Model10X { // Mandatory register - Model10XAcCurrent = 2, - Model10XPhaseACurrent = 3, - Model10XPhaseBCurrent = 4, - Model10XPhaseCCurrent = 5, - Model10XAmpereScaleFactor = 6, - Model10XPhaseVoltageAN = 10, - Model10XPhaseVoltageBN = 11, - Model10XPhaseVoltageCN = 12, - Model10XVoltageScaleFactor = 13, - Model10XACPower = 14, - Model10XWattScaleFactor = 15, - Model10XLineFrequency = 16, - Model10XHerzScaleFactor = 17, - Model10XAcEnergy = 24, - Model10XWattHoursScaleFactor = 25, - Model10XCabinetTemperature = 33, - Model10XTemperatureScaleFactor = 37, - Model10XOperatingState = 38, - Model10XEvent1 = 40 + Model10XAcCurrent = 0, + Model10XPhaseACurrent = 1, + Model10XPhaseBCurrent = 2, + Model10XPhaseCCurrent = 3, + Model10XAmpereScaleFactor = 4, + Model10XPhaseVoltageAN = 8, + Model10XPhaseVoltageBN = 9, + Model10XPhaseVoltageCN = 10, + Model10XVoltageScaleFactor = 11, + Model10XACPower = 12, + Model10XWattScaleFactor = 13, + Model10XLineFrequency = 14, + Model10XHerzScaleFactor = 15, + Model10XAcEnergy = 22, + Model10XWattHoursScaleFactor = 24, + Model10XCabinetTemperature = 31, + Model10XTemperatureScaleFactor = 35, + Model10XOperatingState = 36, + Model10XEvent1 = 38 }; enum Model11X { // Mandatory register @@ -93,24 +92,29 @@ public: float lineFrequency; float acEnergy; float cabinetTemperature; // in degree Celsius - SunSpecEvent1 event; - SunSpecOperatingState operatingState; + SunSpec::SunSpecEvent1 event; + SunSpec::SunSpecOperatingState operatingState; }; + SunSpecInverter(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress); + SunSpec::BlockId blockId(); + void init(); void getInverterMap(); private: - BlockId m_id = BlockIdInverterThreePhase; //e.g. 103 for three phase inverter, 113 for three phase inverter with floating point representation + 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; + bool m_initFinishedSuccess = false; void readInverterBlockHeader(); private slots: - void onModbusMapReceived(BlockId mapId, uint mapLength, QVector data); + void onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector data); signals: - void initFinished(); + void initFinished(bool success); void inverterDataReceived(InverterData data); }; diff --git a/sunspec/sunspecmeter.cpp b/sunspec/sunspecmeter.cpp index cfd3b88..d7d6481 100644 --- a/sunspec/sunspecmeter.cpp +++ b/sunspec/sunspecmeter.cpp @@ -31,31 +31,54 @@ #include "sunspecmeter.h" #include "extern-plugininfo.h" -SunSpecMeter::SunSpecMeter(const QHostAddress &hostAddress, uint port, QObject *parent) : SunSpec(hostAddress, port, parent) +SunSpecMeter::SunSpecMeter(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress) : + QObject(sunspec), + m_connection(sunspec), + m_id(blockId), + m_mapModbusStartRegister(modbusAddress) { - connect(m_modbusTcpClient, &QModbusClient::stateChanged, this, [this] (QModbusDevice::State state) { - if (state == QModbusDevice::ConnectedState) { - qCDebug(dcSunSpec()) << "Meter connected successfully"; - QList mapIds; - mapIds.append(BlockIdSinglePhaseMeter); - mapIds.append(BlockIdSplitSinglePhaseMeter); - mapIds.append(BlockIdWyeConnectThreePhaseMeter); - mapIds.append(BlockIdDeltaConnectThreePhaseMeter); - mapIds.append(BlockIdSinglePhaseMeterFloat); - mapIds.append(BlockIdSplitSinglePhaseMeterFloat); - mapIds.append(BlockIdWyeConnectThreePhaseMeterFloat); - mapIds.append(BlockIdDeltaConnectThreePhaseMeterFloat); - findModbusMap(mapIds); - } + qCDebug(dcSunSpec()) << "SunSpecMeter: Setting up meter"; + connect(m_connection, &SunSpec::mapReceived, this, &SunSpecMeter::onModbusMapReceived); +} + +SunSpec::BlockId SunSpecMeter::blockId() +{ + 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; + emit initFinished(true); + m_initFinishedSuccess = true; + }); + QTimer::singleShot(10000, this,[this] { + if (!m_initFinishedSuccess) { + emit initFinished(false); + } }); } -void SunSpecMeter::geMeterMap() +void SunSpecMeter::getMeterMap() { - readMap(m_mapModbusStartRegister, m_mapLength); + m_connection->readMap(m_mapModbusStartRegister, m_mapLength); } void SunSpecMeter::readMeterBlockHeader() { } + +void SunSpecMeter::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector data) +{ + Q_UNUSED(mapLength) + Q_UNUSED(data) + switch (mapId) { + default: + break; + } +} diff --git a/sunspec/sunspecmeter.h b/sunspec/sunspecmeter.h index e3154f4..f7f81b8 100644 --- a/sunspec/sunspecmeter.h +++ b/sunspec/sunspecmeter.h @@ -34,7 +34,7 @@ #include #include "sunspec.h" -class SunSpecMeter : public SunSpec +class SunSpecMeter : public QObject { Q_OBJECT public: @@ -64,26 +64,29 @@ public: }; struct MeterData { - SunSpecEvent1 event; - SunSpecOperatingState operatingState; + SunSpec::SunSpecEvent1 event; + SunSpec::SunSpecOperatingState operatingState; }; - SunSpecMeter(const QHostAddress &hostAddress, uint port = 502, QObject *parent = 0); - - void geMeterMap(); + SunSpecMeter(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress); + SunSpec::BlockId blockId(); + void init(); + void getMeterMap(); private: - BlockId m_id = BlockIdDeltaConnectThreePhaseMeter; + SunSpec *m_connection = nullptr; + SunSpec::BlockId m_id = SunSpec::BlockIdDeltaConnectThreePhaseMeter; uint m_mapLength = 0; uint m_mapModbusStartRegister = 40000; + bool m_initFinishedSuccess = false; void readMeterBlockHeader(); private slots: - // void onModbusMapReceived(BlockId mapId, uint mapLength, QVector data); + void onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector data); signals: - void initFinished(); + void initFinished(bool success); void meterDataReceived(const MeterData &data); }; diff --git a/sunspec/sunspecstorage.cpp b/sunspec/sunspecstorage.cpp index 8ff722d..426f63e 100644 --- a/sunspec/sunspecstorage.cpp +++ b/sunspec/sunspecstorage.cpp @@ -31,39 +31,47 @@ #include "sunspecstorage.h" #include "extern-plugininfo.h" -SunSpecStorage::SunSpecStorage(const QHostAddress &hostAddress, uint port, QObject *parent) : SunSpec(hostAddress, port, parent) +SunSpecStorage::SunSpecStorage(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress) : + QObject(sunspec), + m_connection(sunspec), + m_id(blockId), + m_mapModbusStartRegister(modbusAddress) { - connect(m_modbusTcpClient, &QModbusClient::stateChanged, this, [this] (QModbusDevice::State state) { - if (state == QModbusDevice::ConnectedState) { - qCDebug(dcSunSpec()) << "Inverter connected successfully"; - QList mapIds; - mapIds.append(BlockIdStorage); - findModbusMap(mapIds); - } - }); - connect(this, &SunSpec::foundModbusMap, this, [this] (BlockId mapId, uint modbusRegisterAddress) { - qCDebug(dcSunSpec()) << "Read map header for mapId" << mapId << "and modbus register" << modbusRegisterAddress; - readMapHeader(modbusRegisterAddress); - }); + qCDebug(dcSunSpec()) << "SunSpecStorage: Setting up storage"; + connect(m_connection, &SunSpec::mapReceived, this, &SunSpecStorage::onModbusMapReceived); +} - connect(this, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, BlockId mapId, uint mapLength) { - m_id = mapId; +SunSpec::BlockId SunSpecStorage::blockId() +{ + 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; - m_mapModbusStartRegister = modbusAddress; - readMap(modbusAddress, mapLength); + emit initFinished(true); + m_initFinishedSuccess = true; + }); + QTimer::singleShot(10000, this,[this] { + if (!m_initFinishedSuccess) { + emit initFinished(false); + } }); - - connect(this, &SunSpec::mapReceived, this, &SunSpecStorage::onModbusMapReceived); } void SunSpecStorage::getStorageMap() { - readMap(m_mapModbusStartRegister, m_mapLength); + m_connection->readMap(m_mapModbusStartRegister, m_mapLength); } void SunSpecStorage::readStorageBlockHeader() { - readMapHeader(m_mapModbusStartRegister); + m_connection->readMapHeader(m_mapModbusStartRegister); } QUuid SunSpecStorage::setGridCharging(bool enabled) @@ -76,7 +84,7 @@ QUuid SunSpecStorage::setGridCharging(bool enabled) uint registerAddress = m_mapModbusStartRegister + Model124::Model124ChaGriSet; quint16 value = enabled; - return writeHoldingRegister(m_slaveId, registerAddress, value); + return m_connection->writeHoldingRegister(registerAddress, value); } QUuid SunSpecStorage::setStorageControlMode(bool chargingEnabled, bool dischargingEnabled) @@ -86,7 +94,7 @@ QUuid SunSpecStorage::setStorageControlMode(bool chargingEnabled, bool dischargi (static_cast(dischargingEnabled) << StorageControlBitFieldDischarge)) ; uint modbusRegister = m_mapModbusStartRegister + Model124::Model124ActivateStorageControlMode; - return writeHoldingRegister(m_slaveId, modbusRegister, value); + return m_connection->writeHoldingRegister(modbusRegister, value); } QUuid SunSpecStorage::setChargingRate(int rate) @@ -96,7 +104,7 @@ QUuid SunSpecStorage::setChargingRate(int rate) uint modbusRegister = m_mapModbusStartRegister + Model124::Model124SetpointMaximumChargingRate; int16_t value = rate * 100; - return writeHoldingRegister(m_slaveId, modbusRegister, value); + return m_connection->writeHoldingRegister(modbusRegister, value); } QUuid SunSpecStorage::setDischargingRate(int charging) @@ -105,14 +113,14 @@ QUuid SunSpecStorage::setDischargingRate(int charging) /* Defines the maximum discharge rate (discharge limit). Default is 100% */ uint modbusRegister = m_mapModbusStartRegister + Model124::Model124SetpointMaximumDischargeRate; quint16 value = charging * 100; - return writeHoldingRegister(m_slaveId, modbusRegister, value); + return m_connection->writeHoldingRegister(modbusRegister, value); } void SunSpecStorage::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, const QVector &data) { Q_UNUSED(mapLength) switch (mapId) { - case BlockIdStorage: { + case SunSpec::BlockIdStorage: { StorageData storageData; storageData.chargingState = ChargingState(data[Model124::Model124ChargeStatus]); emit storageDataReceived(storageData); diff --git a/sunspec/sunspecstorage.h b/sunspec/sunspecstorage.h index 29ceea2..3d2cd3e 100644 --- a/sunspec/sunspecstorage.h +++ b/sunspec/sunspecstorage.h @@ -34,18 +34,21 @@ #include #include "sunspec.h" -class SunSpecStorage : public SunSpec +class SunSpecStorage : public QObject { Q_OBJECT public: - SunSpecStorage(const QHostAddress &hostAddress, uint port = 502, QObject *parent = 0); + SunSpecStorage(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress); + + SunSpec::BlockId blockId(); + void init(); + void getStorageMap(); QUuid setGridCharging(bool enabled); QUuid setDischargingRate(int rate); QUuid setChargingRate(int rate); QUuid setStorageControlMode(bool chargingEnabled, bool dischargingEnabled); - enum StorageControlBitField { StorageControlBitFieldCharge = 0, StorageControlBitFieldDischarge = 1 @@ -88,20 +91,20 @@ public: bool gridChargingEnabled; }; - void getStorageMap(); - private: - BlockId m_id = BlockIdEnergyStorageBaseModel; + SunSpec *m_connection = nullptr; + SunSpec::BlockId m_id = SunSpec::BlockIdEnergyStorageBaseModel; uint m_mapLength = 0; uint m_mapModbusStartRegister = 40000; + bool m_initFinishedSuccess = false; void readStorageBlockHeader(); private slots: - void onModbusMapReceived(BlockId mapId, uint mapLength, const QVector &data); + void onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, const QVector &data); signals: - void initFinished(); + void initFinished(bool success); void storageDataReceived(const StorageData &data); };