renamed block or map to model

This commit is contained in:
Boernsman 2021-01-22 09:10:29 +01:00
parent 4a9064c647
commit b536b1b16e
13 changed files with 384 additions and 383 deletions

View File

@ -5,12 +5,19 @@ Connect to SunSpec devices over Modbus TCP
## Supported Things ## Supported Things
* SunSpec Inverter * SunSpec Inverter
* 101 & 111 Single phase inverter
* 102 & 112 Split phase inverter
* 103 & 113 Three phase inverter
* SunSpec Meter * SunSpec Meter
* SunSpec Storage * Single phase meter
* Split phase meter
* 203 Three phase meter
* SunSpec Storage [124]
## Requirements ## Requirements
* The package 'nymea-plugin-sunspec' must be installed. * The package 'nymea-plugin-sunspec' must be installed.
* The SunSpec device must support SunSpec over modbus TCP
## More ## More
https://sunspec.org https://sunspec.org

View File

@ -49,13 +49,13 @@ void IntegrationPluginSunSpec::init()
m_connectedStateTypeIds.insert(sunspecSplitPhaseMeterThingClassId, sunspecSplitPhaseMeterConnectedStateTypeId); m_connectedStateTypeIds.insert(sunspecSplitPhaseMeterThingClassId, sunspecSplitPhaseMeterConnectedStateTypeId);
m_connectedStateTypeIds.insert(sunspecThreePhaseMeterThingClassId, sunspecThreePhaseMeterConnectedStateTypeId); m_connectedStateTypeIds.insert(sunspecThreePhaseMeterThingClassId, sunspecThreePhaseMeterConnectedStateTypeId);
m_mapIdParamTypeIds.insert(sunspecSinglePhaseInverterThingClassId, sunspecSinglePhaseInverterThingMapIdParamTypeId); m_modelIdParamTypeIds.insert(sunspecSinglePhaseInverterThingClassId, sunspecSinglePhaseInverterThingModelIdParamTypeId);
m_mapIdParamTypeIds.insert(sunspecSplitPhaseInverterThingClassId, sunspecSplitPhaseInverterThingMapIdParamTypeId); m_modelIdParamTypeIds.insert(sunspecSplitPhaseInverterThingClassId, sunspecSplitPhaseInverterThingModelIdParamTypeId);
m_mapIdParamTypeIds.insert(sunspecThreePhaseInverterThingClassId, sunspecThreePhaseInverterThingMapIdParamTypeId); m_modelIdParamTypeIds.insert(sunspecThreePhaseInverterThingClassId, sunspecThreePhaseInverterThingModelIdParamTypeId);
m_mapIdParamTypeIds.insert(sunspecStorageThingClassId, sunspecStorageThingMapIdParamTypeId); m_modelIdParamTypeIds.insert(sunspecStorageThingClassId, sunspecStorageThingModelIdParamTypeId);
m_mapIdParamTypeIds.insert(sunspecSinglePhaseMeterThingClassId, sunspecSinglePhaseMeterThingMapIdParamTypeId); m_modelIdParamTypeIds.insert(sunspecSinglePhaseMeterThingClassId, sunspecSinglePhaseMeterThingModelIdParamTypeId);
m_mapIdParamTypeIds.insert(sunspecSplitPhaseMeterThingClassId, sunspecSplitPhaseMeterThingMapIdParamTypeId); m_modelIdParamTypeIds.insert(sunspecSplitPhaseMeterThingClassId, sunspecSplitPhaseMeterThingModelIdParamTypeId);
m_mapIdParamTypeIds.insert(sunspecThreePhaseMeterThingClassId, sunspecThreePhaseMeterThingMapIdParamTypeId); m_modelIdParamTypeIds.insert(sunspecThreePhaseMeterThingClassId, sunspecThreePhaseMeterThingModelIdParamTypeId);
m_modbusAddressParamTypeIds.insert(sunspecSinglePhaseInverterThingClassId, sunspecSinglePhaseInverterThingModbusAddressParamTypeId); m_modbusAddressParamTypeIds.insert(sunspecSinglePhaseInverterThingClassId, sunspecSinglePhaseInverterThingModbusAddressParamTypeId);
m_modbusAddressParamTypeIds.insert(sunspecSplitPhaseInverterThingClassId, sunspecSplitPhaseInverterThingModbusAddressParamTypeId); m_modbusAddressParamTypeIds.insert(sunspecSplitPhaseInverterThingClassId, sunspecSplitPhaseInverterThingModbusAddressParamTypeId);
@ -98,17 +98,17 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info)
QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecConnectionThingIpAddressParamTypeId).toString()); QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecConnectionThingIpAddressParamTypeId).toString());
int port = info->thing()->paramValue(sunspecConnectionThingPortParamTypeId).toInt(); 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())) { if (m_sunSpecConnections.contains(thing->id())) {
qCDebug(dcSunSpec()) << "Reconfigure SunSpec connection with new address" << address; qCDebug(dcSunSpec()) << "Reconfigure SunSpec connection with new address" << address;
sunSpec = m_sunSpecConnections.value(thing->id()); m_sunSpecConnections.take(thing->id())->deleteLater();
sunSpec->setHostAddress(address);
} else {
sunSpec = new SunSpec(address, port, this);
m_sunSpecConnections.insert(thing->id(), sunSpec);
} }
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()) { if (!sunSpec->connectModbus()) {
qCWarning(dcSunSpec()) << "Error connecting to SunSpec device"; qCWarning(dcSunSpec()) << "Error connecting to SunSpec device";
@ -127,9 +127,9 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info)
connect(sunSpec, &SunSpec::destroyed, [this, thing] { connect(sunSpec, &SunSpec::destroyed, [this, thing] {
m_sunSpecConnections.remove(thing->id()); m_sunSpecConnections.remove(thing->id());
}); });
connect(sunSpec, &SunSpec::foundModbusMap, this, &IntegrationPluginSunSpec::onFoundModbusMap); connect(sunSpec, &SunSpec::foundSunSpecModel, this, &IntegrationPluginSunSpec::onFoundSunSpecModel);
connect(sunSpec, &SunSpec::modbusMapSearchFinished, this, &IntegrationPluginSunSpec::onModbusMapSearchFinished); connect(sunSpec, &SunSpec::sunspecModelSearchFinished, this, &IntegrationPluginSunSpec::onSunSpecModelSearchFinished);
connect(sunSpec, &SunSpec::commonMapReceived, thing, [thing] (const QString &manufacturer, const QString &deviceModel, const QString &serielNumber) { connect(sunSpec, &SunSpec::commonModelReceived, thing, [thing] (const QString &manufacturer, const QString &deviceModel, const QString &serielNumber) {
thing->setStateValue(sunspecConnectionConnectedStateTypeId, true); thing->setStateValue(sunspecConnectionConnectedStateTypeId, true);
thing->setStateValue(sunspecConnectionManufacturerStateTypeId, manufacturer); thing->setStateValue(sunspecConnectionManufacturerStateTypeId, manufacturer);
thing->setStateValue(sunspecConnectionDeviceModelStateTypeId, deviceModel); thing->setStateValue(sunspecConnectionDeviceModelStateTypeId, deviceModel);
@ -140,14 +140,14 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info)
thing->thingClassId() == sunspecSplitPhaseInverterThingClassId || thing->thingClassId() == sunspecSplitPhaseInverterThingClassId ||
thing->thingClassId() == sunspecSinglePhaseInverterThingClassId ) { 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(); int modbusAddress = thing->paramValue(m_modbusAddressParamTypeIds.value(thing->thingClassId())).toInt();
SunSpec *connection = m_sunSpecConnections.value(thing->parentId()); SunSpec *connection = m_sunSpecConnections.value(thing->parentId());
if (!connection) { if (!connection) {
qCWarning(dcSunSpec()) << "Could not find SunSpec connection"; qCWarning(dcSunSpec()) << "Could not find SunSpec connection";
return info->finish(Thing::ThingErrorHardwareNotAvailable); 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(); sunSpecInverter->init();
connect(sunSpecInverter, &SunSpecInverter::initFinished, info, [this, sunSpecInverter, info] (bool success){ connect(sunSpecInverter, &SunSpecInverter::initFinished, info, [this, sunSpecInverter, info] (bool success){
qCDebug(dcSunSpec()) << "Modbus Inverter init finished, success:" << success; qCDebug(dcSunSpec()) << "Modbus Inverter init finished, success:" << success;
@ -167,14 +167,14 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info)
thing->thingClassId() == sunspecSplitPhaseMeterThingClassId || thing->thingClassId() == sunspecSplitPhaseMeterThingClassId ||
thing->thingClassId() == sunspecThreePhaseMeterThingClassId) { 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(); int modbusAddress = thing->paramValue(m_modbusAddressParamTypeIds.value(thing->thingClassId())).toInt();
SunSpec *connection = m_sunSpecConnections.value(thing->parentId()); SunSpec *connection = m_sunSpecConnections.value(thing->parentId());
if (!connection) { if (!connection) {
qCWarning(dcSunSpec()) << "Could not find SunSpec connection"; qCWarning(dcSunSpec()) << "Could not find SunSpec connection";
return info->finish(Thing::ThingErrorHardwareNotAvailable); 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(); sunSpecMeter->init();
connect(sunSpecMeter, &SunSpecMeter::initFinished, info, [this, sunSpecMeter, info] (bool success){ connect(sunSpecMeter, &SunSpecMeter::initFinished, info, [this, sunSpecMeter, info] (bool success){
qCDebug(dcSunSpec()) << "Modbus meter init finished, success:" << success; qCDebug(dcSunSpec()) << "Modbus meter init finished, success:" << success;
@ -192,6 +192,28 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info)
} else if (info->thing()->thingClassId() == sunspecStorageThingClassId) { } 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 { } else {
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(info->thing()->thingClassId().toString()).toUtf8()); 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"; qCDebug(dcSunSpec()) << "SunSpecConnection not found";
return; return;
} }
connection->readCommonMap(); connection->readCommonModel();
connection->findModbusMap(QList<SunSpec::BlockId>()); // Discover all maps, without filter connection->findSunSpecModels(QList<SunSpec::ModelId>()); // Discover all models, without filter
} else if (thing->thingClassId() == sunspecSinglePhaseInverterThingClassId || } else if (thing->thingClassId() == sunspecSinglePhaseInverterThingClassId ||
thing->thingClassId() == sunspecSplitPhaseInverterThingClassId || thing->thingClassId() == sunspecSplitPhaseInverterThingClassId ||
@ -227,7 +249,7 @@ void IntegrationPluginSunSpec::postSetupThing(Thing *thing)
qCDebug(dcSunSpec()) << "SunSpecInverter not found"; qCDebug(dcSunSpec()) << "SunSpecInverter not found";
return; return;
} }
sunSpecInverter->getInverterMap(); sunSpecInverter->getInverterModelDataBlock();
} else if (thing->thingClassId() == sunspecSinglePhaseMeterThingClassId || } else if (thing->thingClassId() == sunspecSinglePhaseMeterThingClassId ||
thing->thingClassId() == sunspecSplitPhaseMeterThingClassId || thing->thingClassId() == sunspecSplitPhaseMeterThingClassId ||
@ -238,7 +260,7 @@ void IntegrationPluginSunSpec::postSetupThing(Thing *thing)
qCDebug(dcSunSpec()) << "SunSpecMeter not found"; qCDebug(dcSunSpec()) << "SunSpecMeter not found";
return; return;
} }
sunSpecMeter->getMeterMap(); sunSpecMeter->getMeterModelDataBlock();
} else if (thing->thingClassId() == sunspecStorageThingClassId) { } else if (thing->thingClassId() == sunspecStorageThingClassId) {
SunSpecStorage *sunSpecStorage = m_sunSpecStorages.value(thing); SunSpecStorage *sunSpecStorage = m_sunSpecStorages.value(thing);
@ -246,7 +268,7 @@ void IntegrationPluginSunSpec::postSetupThing(Thing *thing)
qCDebug(dcSunSpec()) << "SunSpecStorage not found"; qCDebug(dcSunSpec()) << "SunSpecStorage not found";
return; return;
} }
sunSpecStorage->getStorageMap(); sunSpecStorage->getStorageModelDataBlock();
} else { } else {
Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8()); Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
@ -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()) { Q_FOREACH(Thing *thing, myThings()) {
if (thing->paramValue(m_mapIdParamTypeIds.value(thing->thingClassId())).toUInt() == mapId && if (thing->paramValue(m_modelIdParamTypeIds.value(thing->thingClassId())).toUInt() == modelId &&
thing->paramValue(m_modbusAddressParamTypeIds.value(thing->thingClassId())).toUInt() == modbusAddress) { thing->paramValue(m_modbusAddressParamTypeIds.value(thing->thingClassId())).toUInt() == modbusAddress) {
return true; return true;
} }
} }
@ -387,16 +409,16 @@ bool IntegrationPluginSunSpec::checkIfThingExists(uint mapId, uint modbusAddress
void IntegrationPluginSunSpec::onRefreshTimer() void IntegrationPluginSunSpec::onRefreshTimer()
{ {
foreach (SunSpec *connection, m_sunSpecConnections) { foreach (SunSpec *connection, m_sunSpecConnections) {
connection->readCommonMap(); //check connection connection->readCommonModel(); //check connection
} }
foreach (SunSpecInverter *inverter, m_sunSpecInverters) { foreach (SunSpecInverter *inverter, m_sunSpecInverters) {
inverter->getInverterMap(); inverter->getInverterModelDataBlock();
} }
foreach (SunSpecMeter *meter, m_sunSpecMeters) { foreach (SunSpecMeter *meter, m_sunSpecMeters) {
meter->getMeterMap(); meter->getMeterModelDataBlock();
} }
foreach (SunSpecStorage *storage, m_sunSpecStorages) { foreach (SunSpecStorage *storage, m_sunSpecStorages) {
storage->getStorageMap(); storage->getStorageModelDataBlock();
} }
} }
@ -410,6 +432,18 @@ void IntegrationPluginSunSpec::onPluginConfigurationChanged(const ParamTypeId &p
m_refreshTimer->stop(); m_refreshTimer->stop();
m_refreshTimer->startTimer(refreshTime); 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 { } else {
qCWarning(dcSunSpec()) << "Unknown plugin configuration" << paramTypeId << "Value" << value; 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) void IntegrationPluginSunSpec::onFoundSunSpecModel(SunSpec::ModelId modelId, int modbusStartRegister)
{
qCDebug(dcSunSpec()) << "On map header received" << modbusAddress << mapId << mapLength;
}
void IntegrationPluginSunSpec::onMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector<quint16> data)
{
Q_UNUSED(data)
SunSpec *connection = static_cast<SunSpec *>(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)
{ {
SunSpec *connection = static_cast<SunSpec *>(sender()); SunSpec *connection = static_cast<SunSpec *>(sender());
Thing *thing = myThings().findById(m_sunSpecConnections.key(connection)); Thing *thing = myThings().findById(m_sunSpecConnections.key(connection));
@ -456,71 +474,71 @@ void IntegrationPluginSunSpec::onFoundModbusMap(SunSpec::BlockId mapId, int modb
return; return;
} }
qCDebug(dcSunSpec()) << "On map received" << mapId << "Map length" << modbusStartRegister << "Thing:" << thing->name(); qCDebug(dcSunSpec()) << "On model received" << modelId << "length" << modbusStartRegister << "Thing:" << thing->name();
if (checkIfThingExists(mapId, modbusStartRegister)) { if (checkIfThingExists(modelId, modbusStartRegister)) {
return; return;
} }
QString model = thing->stateValue(sunspecConnectionDeviceModelStateTypeId).toString(); QString model = thing->stateValue(sunspecConnectionDeviceModelStateTypeId).toString();
switch (mapId) { switch (modelId) {
case SunSpec::BlockId::BlockIdInverterSinglePhase: case SunSpec::ModelId::ModelIdInverterSinglePhase:
case SunSpec::BlockId::BlockIdInverterSinglePhaseFloat: { case SunSpec::ModelId::ModelIdInverterSinglePhaseFloat: {
ThingDescriptor descriptor(sunspecSinglePhaseInverterThingClassId, model+tr(" single phase inverter"), "", thing->id()); ThingDescriptor descriptor(sunspecSinglePhaseInverterThingClassId, model+tr(" single phase inverter"), "", thing->id());
ParamList params; ParamList params;
params.append(Param(sunspecSinglePhaseInverterThingMapIdParamTypeId, mapId)); params.append(Param(sunspecSinglePhaseInverterThingModelIdParamTypeId, modelId));
params.append(Param(sunspecSinglePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); params.append(Param(sunspecSinglePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister));
descriptor.setParams(params); descriptor.setParams(params);
emit autoThingsAppeared({descriptor}); emit autoThingsAppeared({descriptor});
} break; } break;
case SunSpec::BlockId::BlockIdInverterSplitPhase: case SunSpec::ModelId::ModelIdInverterSplitPhase:
case SunSpec::BlockId::BlockIdInverterSplitPhaseFloat: { case SunSpec::ModelId::ModelIdInverterSplitPhaseFloat: {
ThingDescriptor descriptor(sunspecSplitPhaseInverterThingClassId, model+tr(" split phase inverter"), "", thing->id()); ThingDescriptor descriptor(sunspecSplitPhaseInverterThingClassId, model+tr(" split phase inverter"), "", thing->id());
ParamList params; ParamList params;
params.append(Param(sunspecSplitPhaseInverterThingMapIdParamTypeId, mapId)); params.append(Param(sunspecSplitPhaseInverterThingModelIdParamTypeId, modelId));
params.append(Param(sunspecSplitPhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); params.append(Param(sunspecSplitPhaseInverterThingModbusAddressParamTypeId, modbusStartRegister));
descriptor.setParams(params); descriptor.setParams(params);
emit autoThingsAppeared({descriptor}); emit autoThingsAppeared({descriptor});
} break; } break;
case SunSpec::BlockId::BlockIdInverterThreePhase: case SunSpec::ModelId::ModelIdInverterThreePhase:
case SunSpec::BlockId::BlockIdInverterThreePhaseFloat: { case SunSpec::ModelId::ModelIdInverterThreePhaseFloat: {
ThingDescriptor descriptor(sunspecThreePhaseInverterThingClassId, model+tr(" three phase inverter"), "", thing->id()); ThingDescriptor descriptor(sunspecThreePhaseInverterThingClassId, model+tr(" three phase inverter"), "", thing->id());
ParamList params; ParamList params;
params.append(Param(sunspecThreePhaseInverterThingMapIdParamTypeId, mapId)); params.append(Param(sunspecThreePhaseInverterThingModelIdParamTypeId, modelId));
params.append(Param(sunspecThreePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); params.append(Param(sunspecThreePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister));
descriptor.setParams(params); descriptor.setParams(params);
emit autoThingsAppeared({descriptor}); emit autoThingsAppeared({descriptor});
} break; } break;
case SunSpec::BlockIdSinglePhaseMeter: case SunSpec::ModelIdSinglePhaseMeter:
case SunSpec::BlockIdSinglePhaseMeterFloat: { case SunSpec::ModelIdSinglePhaseMeterFloat: {
ThingDescriptor descriptor(sunspecSinglePhaseMeterThingClassId, model+tr(" Meter"), "", thing->id()); ThingDescriptor descriptor(sunspecSinglePhaseMeterThingClassId, model+tr(" Meter"), "", thing->id());
ParamList params; ParamList params;
params.append(Param(sunspecSinglePhaseMeterThingMapIdParamTypeId, mapId)); params.append(Param(sunspecSinglePhaseMeterThingModelIdParamTypeId, modelId));
params.append(Param(sunspecSinglePhaseMeterThingModbusAddressParamTypeId, modbusStartRegister)); params.append(Param(sunspecSinglePhaseMeterThingModbusAddressParamTypeId, modbusStartRegister));
descriptor.setParams(params); descriptor.setParams(params);
emit autoThingsAppeared({descriptor}); emit autoThingsAppeared({descriptor});
} break; } break;
case SunSpec::BlockIdSplitSinglePhaseMeter: case SunSpec::ModelIdSplitSinglePhaseMeter:
case SunSpec::BlockIdSplitSinglePhaseMeterFloat: { case SunSpec::ModelIdSplitSinglePhaseMeterFloat: {
ThingDescriptor descriptor(sunspecSplitPhaseMeterThingClassId, model+tr(" Meter"), "", thing->id()); ThingDescriptor descriptor(sunspecSplitPhaseMeterThingClassId, model+tr(" Meter"), "", thing->id());
ParamList params; ParamList params;
params.append(Param(sunspecSplitPhaseMeterThingMapIdParamTypeId, mapId)); params.append(Param(sunspecSplitPhaseMeterThingModelIdParamTypeId, modelId));
params.append(Param(sunspecSplitPhaseMeterThingModbusAddressParamTypeId, modbusStartRegister)); params.append(Param(sunspecSplitPhaseMeterThingModbusAddressParamTypeId, modbusStartRegister));
descriptor.setParams(params); descriptor.setParams(params);
emit autoThingsAppeared({descriptor}); emit autoThingsAppeared({descriptor});
} break; } break;
case SunSpec::BlockIdWyeConnectThreePhaseMeterFloat: case SunSpec::ModelIdWyeConnectThreePhaseMeterFloat:
case SunSpec::BlockIdDeltaConnectThreePhaseMeterFloat: { case SunSpec::ModelIdDeltaConnectThreePhaseMeterFloat: {
ThingDescriptor descriptor(sunspecThreePhaseMeterThingClassId, model+" Meter", "", thing->id()); ThingDescriptor descriptor(sunspecThreePhaseMeterThingClassId, model+" Meter", "", thing->id());
ParamList params; ParamList params;
params.append(Param(sunspecThreePhaseMeterThingMapIdParamTypeId, mapId)); params.append(Param(sunspecThreePhaseMeterThingModelIdParamTypeId, modelId));
params.append(Param(sunspecThreePhaseMeterThingModbusAddressParamTypeId, modbusStartRegister)); params.append(Param(sunspecThreePhaseMeterThingModbusAddressParamTypeId, modbusStartRegister));
descriptor.setParams(params); descriptor.setParams(params);
emit autoThingsAppeared({descriptor}); emit autoThingsAppeared({descriptor});
} break; } break;
case SunSpec::BlockIdStorage: { case SunSpec::ModelIdStorage: {
ThingDescriptor descriptor(sunspecStorageThingClassId, model+" Storage", "", thing->id()); ThingDescriptor descriptor(sunspecStorageThingClassId, model+" Storage", "", thing->id());
ParamList params; ParamList params;
params.append(Param(sunspecStorageThingMapIdParamTypeId, mapId)); params.append(Param(sunspecStorageThingModelIdParamTypeId, modelId));
params.append(Param(sunspecThreePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister)); params.append(Param(sunspecThreePhaseInverterThingModbusAddressParamTypeId, modbusStartRegister));
descriptor.setParams(params); descriptor.setParams(params);
emit autoThingsAppeared({descriptor}); emit autoThingsAppeared({descriptor});
@ -530,7 +548,7 @@ void IntegrationPluginSunSpec::onFoundModbusMap(SunSpec::BlockId mapId, int modb
} }
} }
void IntegrationPluginSunSpec::onModbusMapSearchFinished(const QHash<SunSpec::BlockId, int> &mapIds) void IntegrationPluginSunSpec::onSunSpecModelSearchFinished(const QHash<SunSpec::ModelId, int> &modelIds)
{ {
SunSpec *connection = static_cast<SunSpec *>(sender()); SunSpec *connection = static_cast<SunSpec *>(sender());
Thing *thing = myThings().findById(m_sunSpecConnections.key(connection)); Thing *thing = myThings().findById(m_sunSpecConnections.key(connection));
@ -539,7 +557,7 @@ void IntegrationPluginSunSpec::onModbusMapSearchFinished(const QHash<SunSpec::Bl
return; return;
} }
qCDebug(dcSunSpec()) << "On modbus map search finished, maps:" << mapIds.count(); qCDebug(dcSunSpec()) << "On sunspec model search finished, models:" << modelIds.count();
} }
@ -679,6 +697,7 @@ void IntegrationPluginSunSpec::onStorageDataReceived(const SunSpecStorage::Stora
if(!thing) { if(!thing) {
return; return;
} }
thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), true);
thing->setStateValue(sunspecStorageStorageStateStateTypeId, storageData.chargingState); thing->setStateValue(sunspecStorageStorageStateStateTypeId, storageData.chargingState);
} }
@ -688,8 +707,8 @@ void IntegrationPluginSunSpec::onMeterDataReceived(const SunSpecMeter::MeterData
SunSpecMeter *meter = static_cast<SunSpecMeter *>(sender()); SunSpecMeter *meter = static_cast<SunSpecMeter *>(sender());
Thing *thing = m_sunSpecMeters.key(meter); Thing *thing = m_sunSpecMeters.key(meter);
if(!thing) { if (!thing) {
return; return;
} }
//thing->setStateValue(sunspecMeterStorageStateStateTypeId, meterData.event); thing->setStateValue(m_connectedStateTypeIds.value(thing->thingClassId()), true);
} }

View File

@ -59,7 +59,7 @@ public:
void executeAction(ThingActionInfo *info) override; void executeAction(ThingActionInfo *info) override;
private: private:
QHash<ThingClassId, ParamTypeId> m_mapIdParamTypeIds; QHash<ThingClassId, ParamTypeId> m_modelIdParamTypeIds;
QHash<ThingClassId, ParamTypeId> m_modbusAddressParamTypeIds; QHash<ThingClassId, ParamTypeId> m_modbusAddressParamTypeIds;
QHash<ThingClassId, StateTypeId> m_connectedStateTypeIds; QHash<ThingClassId, StateTypeId> m_connectedStateTypeIds;
@ -77,7 +77,7 @@ private:
QHash<Thing *, SunSpecStorage *> m_sunSpecStorages; QHash<Thing *, SunSpecStorage *> m_sunSpecStorages;
QHash<Thing *, SunSpecMeter *> m_sunSpecMeters; QHash<Thing *, SunSpecMeter *> m_sunSpecMeters;
bool checkIfThingExists(uint mapId, uint modbusAddress); bool checkIfThingExists(uint modelId, uint modbusAddress);
private slots: private slots:
void onRefreshTimer(); void onRefreshTimer();
@ -86,11 +86,8 @@ private slots:
void onConnectionStateChanged(bool status); void onConnectionStateChanged(bool status);
void onMapHeaderReceived(uint modbusAddress, SunSpec::BlockId mapId, uint mapLength); void onFoundSunSpecModel(SunSpec::ModelId modelId, int modbusStartRegister);
void onMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector<quint16> data); void onSunSpecModelSearchFinished(const QHash<SunSpec::ModelId, int> &modelIds);
void onFoundModbusMap(SunSpec::BlockId mapId, int modbusStartRegister);
void onModbusMapSearchFinished(const QHash<SunSpec::BlockId, int> &mapIds);
void onWriteRequestExecuted(QUuid requestId, bool success); void onWriteRequestExecuted(QUuid requestId, bool success);
void onWriteRequestError(QUuid requestId, const QString &error); void onWriteRequestError(QUuid requestId, const QString &error);
@ -99,6 +96,5 @@ private slots:
void onStorageDataReceived(const SunSpecStorage::StorageData &storageData); void onStorageDataReceived(const SunSpecStorage::StorageData &storageData);
void onMeterDataReceived(const SunSpecMeter::MeterData &meterData); void onMeterDataReceived(const SunSpecMeter::MeterData &meterData);
}; };
#endif // INTEGRATIONPLUGINSUNSPEC_H #endif // INTEGRATIONPLUGINSUNSPEC_H

View File

@ -37,7 +37,7 @@
"thingClasses": [ "thingClasses": [
{ {
"name": "sunspecConnection", "name": "sunspecConnection",
"displayName": "SunSpec Device", "displayName": "SunSpec connection",
"id": "f51853f3-8815-4cf3-b337-45cda1f3e6d5", "id": "f51853f3-8815-4cf3-b337-45cda1f3e6d5",
"createMethods": [ "User" ], "createMethods": [ "User" ],
"interfaces": ["connectable"], "interfaces": ["connectable"],
@ -109,8 +109,8 @@
"paramTypes": [ "paramTypes": [
{ {
"id": "41715d00-a947-4f43-a475-cea05790e01d", "id": "41715d00-a947-4f43-a475-cea05790e01d",
"name":"mapId", "name":"modelId",
"displayName": "Map id", "displayName": "Model",
"type": "int", "type": "int",
"readOnly": true "readOnly": true
}, },
@ -243,8 +243,8 @@
"paramTypes": [ "paramTypes": [
{ {
"id": "c42fb50e-210f-4b53-88eb-fa216e15f88f", "id": "c42fb50e-210f-4b53-88eb-fa216e15f88f",
"name":"mapId", "name":"modelId",
"displayName": "Map id", "displayName": "Model",
"type": "int", "type": "int",
"readOnly": true "readOnly": true
}, },
@ -404,8 +404,8 @@
"paramTypes": [ "paramTypes": [
{ {
"id": "8d5b2b58-ce46-406d-844e-f53136afcf09", "id": "8d5b2b58-ce46-406d-844e-f53136afcf09",
"name":"mapId", "name":"modelId",
"displayName": "Map id", "displayName": "Model",
"type": "int", "type": "int",
"readOnly": true "readOnly": true
}, },
@ -583,8 +583,8 @@
"paramTypes": [ "paramTypes": [
{ {
"id": "7d6fcafb-c62e-4a21-aae2-f4041c487149", "id": "7d6fcafb-c62e-4a21-aae2-f4041c487149",
"name":"mapId", "name":"modelId",
"displayName": "Map id", "displayName": "Model",
"type": "int", "type": "int",
"readOnly": true, "readOnly": true,
"defaultValue": 0 "defaultValue": 0
@ -682,8 +682,8 @@
"paramTypes": [ "paramTypes": [
{ {
"id": "89aeec6d-abeb-48b5-9594-214ad5db2d03", "id": "89aeec6d-abeb-48b5-9594-214ad5db2d03",
"name":"mapId", "name":"modelId",
"displayName": "Map id", "displayName": "Model",
"type": "int", "type": "int",
"readOnly": true, "readOnly": true,
"defaultValue": 0 "defaultValue": 0
@ -808,8 +808,8 @@
"paramTypes": [ "paramTypes": [
{ {
"id": "a1960821-155c-4176-86fa-974429039182", "id": "a1960821-155c-4176-86fa-974429039182",
"name":"mapId", "name":"modelId",
"displayName": "Map id", "displayName": "Model",
"type": "int", "type": "int",
"readOnly": true, "readOnly": true,
"defaultValue": 0 "defaultValue": 0
@ -952,8 +952,8 @@
"paramTypes": [ "paramTypes": [
{ {
"id": "219beb96-b9fe-4dd2-a386-ecfbbab8786d", "id": "219beb96-b9fe-4dd2-a386-ecfbbab8786d",
"name":"mapId", "name":"modelId",
"displayName": "Map id", "displayName": "Model",
"type": "int", "type": "int",
"readOnly": true, "readOnly": true,
"defaultValue": 0 "defaultValue": 0

View File

@ -32,10 +32,11 @@
#include "extern-plugininfo.h" #include "extern-plugininfo.h"
#include <QtEndian> #include <QtEndian>
SunSpec::SunSpec(const QHostAddress &hostAddress, uint port, QObject *parent) : SunSpec::SunSpec(const QHostAddress &hostAddress, uint port, uint slaveId, QObject *parent) :
QObject(parent), QObject(parent),
m_hostAddress(hostAddress), m_hostAddress(hostAddress),
m_port(port) m_port(port),
m_slaveId(slaveId)
{ {
qCDebug(dcSunSpec()) << "SunSpec: Creating SunSpec connection"; qCDebug(dcSunSpec()) << "SunSpec: Creating SunSpec connection";
m_modbusTcpClient = new QModbusTcpClient(this); m_modbusTcpClient = new QModbusTcpClient(this);
@ -143,8 +144,8 @@ void SunSpec::findBaseRegister()
if (reply->error() == QModbusDevice::NoError) { if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result(); const QModbusDataUnit unit = reply->result();
if ((unit.value(0) << 16 | unit.value(1)) == 0x53756e53) { if ((unit.value(0) << 16 | unit.value(1)) == 0x53756e53) {
//Well-known value. Uniquely identifies this as a SunSpec Modbus Map //Well-known value. Uniquely identifies this as a SunSpec Modbus model
qCDebug(dcSunSpec()) << "SunSpec: Found start of modbus map" << baseRegister; qCDebug(dcSunSpec()) << "SunSpec: Found start of modbus model" << baseRegister;
m_baseRegister = baseRegister; m_baseRegister = baseRegister;
emit foundBaseRegister(baseRegister); emit foundBaseRegister(baseRegister);
} else { } else {
@ -162,9 +163,9 @@ void SunSpec::findBaseRegister()
} }
} }
void SunSpec::findModbusMap(const QList<BlockId> &ids, uint modbusAddressOffset) void SunSpec::findSunSpecModels(const QList<ModelId> &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); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_baseRegister+modbusAddressOffset, 2);
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) { if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) {
@ -175,23 +176,23 @@ void SunSpec::findModbusMap(const QList<BlockId> &ids, uint modbusAddressOffset)
if (reply->error() == QModbusDevice::NoError) { if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result(); const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress(); uint modbusAddress = unit.startAddress();
BlockId blockId = BlockId(unit.value(0)); ModelId modelId = ModelId(unit.value(0));
int blockLength = unit.value(1); int modelLength = unit.value(1);
if (blockId == BlockIdEnd) { if (modelId == ModelIdEnd) {
qCDebug(dcSunSpec()) << "SunSpec: Block Id End"; qCDebug(dcSunSpec()) << "SunSpec: Model Id End";
modbusMapSearchFinished(m_mapList); sunspecModelSearchFinished(m_modelList);
return; return;
} }
if (ids.isEmpty() || ids.contains(blockId)) { if (ids.isEmpty() || ids.contains(modelId)) {
// If ids is empty then emit all blocks // If ids is empty then emit all models
qCDebug(dcSunSpec()) << "SunSpec: Found block" << BlockId(blockId) << "with block length" << blockLength; qCDebug(dcSunSpec()) << "SunSpec: Found model" << ModelId(modelId) << "with length" << modelLength;
m_mapList.insert(BlockId(blockId), modbusAddress); m_modelList.insert(ModelId(modelId), modbusAddress);
foundModbusMap(BlockId(blockId), 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 { } else {
qCWarning(dcSunSpec()) << "SunSpec: Find modbus map, read response error:" << reply->error(); qCWarning(dcSunSpec()) << "SunSpec: Find modbus model, read response error:" << reply->error();
} }
}); });
} else { } else {
@ -204,9 +205,9 @@ void SunSpec::findModbusMap(const QList<BlockId> &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) if (modbusAddress == 0 || modbusAddress == 40000 || modbusAddress == 50000)
modbusAddress += 2; modbusAddress += 2;
@ -220,34 +221,34 @@ void SunSpec::readMapHeader(uint modbusAddress)
if (reply->error() == QModbusDevice::NoError) { if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result(); const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress(); uint modbusAddress = unit.startAddress();
BlockId mapId = BlockId(unit.value(0)); ModelId modelId = ModelId(unit.value(0));
int mapLength = unit.value(1); int length = unit.value(1);
qCDebug(dcSunSpec()) << "Received block header response. Map ID:" << mapId << "Map length" << mapLength; qCDebug(dcSunSpec()) << "SunSpec: Received model header response. Model ID:" << modelId << "length" << length;
mapHeaderReceived(modbusAddress, mapId, mapLength); modelHeaderReceived(modbusAddress, modelId, length);
} else { } 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) { 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 reply->finished(); // To make sure it will be deleted
}); });
} else { } else {
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString();
delete reply; // broadcast replies return immediately delete reply; // broadcast replies return immediately
return; return;
} }
} else { } else {
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString();
return; 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 (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) {
if (!reply->isFinished()) { if (!reply->isFinished()) {
@ -257,32 +258,32 @@ void SunSpec::readMap(uint modbusAddress, uint modelLength)
if (reply->error() == QModbusDevice::NoError) { if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result(); const QModbusDataUnit unit = reply->result();
uint modbusAddress = unit.startAddress(); uint modbusAddress = unit.startAddress();
BlockId mapId = BlockId(unit.value(0)); ModelId modelId = ModelId(unit.value(0));
uint mapLength = unit.value(1); uint length = unit.value(1);
qCDebug(dcSunSpec()) << "Received map. Modbus address" << modbusAddress << "Map ID" << mapId << "Map Length" << mapLength; qCDebug(dcSunSpec()) << "SunSpec: Received model. Modbus address" << modbusAddress << "model ID" << modelId << "length" << length;
emit mapReceived(mapId, mapLength, unit.values().mid(2)); emit modelDataBlockReceived(modelId, length, unit.values().mid(2));
} else { } 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) { 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 reply->finished(); // To make sure it will be deleted
}); });
} else { } else {
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString();
delete reply; // broadcast replies return immediately delete reply; // broadcast replies return immediately
return; return;
} }
} else { } else {
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString();
return; 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); QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_baseRegister+2, 66);
@ -293,9 +294,6 @@ void SunSpec::readCommonMap()
if (reply->error() == QModbusDevice::NoError) { if (reply->error() == QModbusDevice::NoError) {
const QModbusDataUnit unit = reply->result(); 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 = convertModbusRegisters(unit.values(), MandatoryRegistersModel1::Manufacturer, 16);
m_manufacturer.remove('\x00'); m_manufacturer.remove('\x00');
m_deviceModel = convertModbusRegisters(unit.values(), MandatoryRegistersModel1::Model, 16); 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 = convertModbusRegisters(unit.values(), MandatoryRegistersModel1::SerialNumber, 16);
m_serialNumber.remove('\x00'); m_serialNumber.remove('\x00');
qCDebug(dcSunSpec()) << "SunSpec: Received common block response. Manufacturer" << m_manufacturer << "Model" << m_deviceModel << "Serial number" << m_serialNumber; 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 { } else {
qCWarning(dcSunSpec()) << "SunSpec: Read common map, read response error:" << reply->error(); qCWarning(dcSunSpec()) << "SunSpec: Read common model, read response error:" << reply->error();
} }
}); });
} else { } 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 delete reply; // broadcast replies return immediately
return; return;
} }
} else { } else {
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString();
return; return;
} }
} }
void SunSpec::onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, const QVector<quint16> &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) QByteArray SunSpec::convertModbusRegister(const uint16_t &modbusData)
{ {
uint8_t data[2]; uint8_t data[2];
@ -438,22 +398,18 @@ QUuid SunSpec::writeHoldingRegisters(uint registerAddress, const QVector<quint16
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] { connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
if (reply->error() != QModbusDevice::NoError) { if (reply->error() != 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 { } else {
delete reply; // broadcast replies return immediately delete reply; // broadcast replies return immediately
return ""; return "";
} }
} else { } else {
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString(); qCWarning(dcSunSpec()) << "SunSpec: Read error: " << m_modbusTcpClient->errorString();
return ""; return "";
} }
return requestId; return requestId;

View File

@ -87,89 +87,89 @@ public:
}; };
Q_ENUM(SunSpecEvent1) Q_ENUM(SunSpecEvent1)
enum BlockId { enum ModelId {
BlockIdCommon = 1, ModelIdCommon = 1,
BlockIdBasicAggregator = 2, ModelIdBasicAggregator = 2,
BlockIdSecureDatasetReadRequest = 3, ModelIdSecureDatasetReadRequest = 3,
BlockIdSecureDatasetReadResponse = 4, ModelIdSecureDatasetReadResponse = 4,
BlockIdSecureWriteRequest = 5, ModelIdSecureWriteRequest = 5,
BlockIdSecureWriteSequentialRequest = 6, ModelIdSecureWriteSequentialRequest = 6,
BlockIdSecureWriteResponseModel = 7, ModelIdSecureWriteResponseModel = 7,
BlockIdGetDeviceSecurityCertificate = 8, ModelIdGetDeviceSecurityCertificate = 8,
BlockIdSetOperatorSecurityCertificate = 9, ModelIdSetOperatorSecurityCertificate = 9,
BlockIdCommunicationInterfaceHeader = 10, ModelIdCommunicationInterfaceHeader = 10,
BlockIdEthernetLinkLayer = 11, ModelIdEthernetLinkLayer = 11,
BlockIdIPv4 = 12, ModelIdIPv4 = 12,
BlockIdIPv6 = 13, ModelIdIPv6 = 13,
BlockIdProxyServer = 14, ModelIdProxyServer = 14,
BlockIdInterfaceCountersModel = 15, ModelIdInterfaceCountersModel = 15,
BlockIdSimpleIpNetwork = 16, ModelIdSimpleIpNetwork = 16,
BlockIdSerialInterface = 17, ModelIdSerialInterface = 17,
BlockIdCellularLink = 18, ModelIdCellularLink = 18,
BlockIdPPPLink = 19, ModelIdPPPLink = 19,
BlockIdInverterSinglePhase = 101, ModelIdInverterSinglePhase = 101,
BlockIdInverterSplitPhase = 102, ModelIdInverterSplitPhase = 102,
BlockIdInverterThreePhase = 103, ModelIdInverterThreePhase = 103,
BlockIdInverterSinglePhaseFloat = 111, ModelIdInverterSinglePhaseFloat = 111,
BlockIdInverterSplitPhaseFloat = 112, ModelIdInverterSplitPhaseFloat = 112,
BlockIdInverterThreePhaseFloat = 113, ModelIdInverterThreePhaseFloat = 113,
BlockIdNameplate = 120, ModelIdNameplate = 120,
BlockIdBasicSettings = 121, ModelIdBasicSettings = 121,
BlockIdMeasurementsStatus = 122, ModelIdMeasurementsStatus = 122,
BlockIdImmediateControls = 123, ModelIdImmediateControls = 123,
BlockIdStorage = 124, ModelIdStorage = 124,
BlockIdPricing = 125, ModelIdPricing = 125,
BlockIdStaticVoltVAR = 126, ModelIdStaticVoltVAR = 126,
BlockIdFreqWattParam = 127, ModelIdFreqWattParam = 127,
BlockIdDynamicReactiveCurrent = 128, ModelIdDynamicReactiveCurrent = 128,
BlockIdLVRTD = 129, ModelIdLVRTD = 129,
BlockIdHVRTD = 130, ModelIdHVRTD = 130,
BlockIdWattPF = 131, ModelIdWattPF = 131,
BlockIdVoltWatt = 132, ModelIdVoltWatt = 132,
BlockIdBasicScheduling = 133, ModelIdBasicScheduling = 133,
BlockIdFreqWattCrv = 134, ModelIdFreqWattCrv = 134,
BlockIdLFRT = 135, ModelIdLFRT = 135,
BlockIdHFRT = 136, ModelIdHFRT = 136,
BlockIdLVRTC = 137, ModelIdLVRTC = 137,
BlockIdHVRTC = 138, ModelIdHVRTC = 138,
BlockIdMultipleMPPTInverterExtensionModel = 160, ModelIdMultipleMPPTInverterExtensionModel = 160,
BlockIdSinglePhaseMeter = 201, ModelIdSinglePhaseMeter = 201,
BlockIdSplitSinglePhaseMeter = 202, ModelIdSplitSinglePhaseMeter = 202,
BlockIdWyeConnectThreePhaseMeter = 203, ModelIdWyeConnectThreePhaseMeter = 203,
BlockIdDeltaConnectThreePhaseMeter = 204, ModelIdDeltaConnectThreePhaseMeter = 204,
BlockIdSinglePhaseMeterFloat = 211, ModelIdSinglePhaseMeterFloat = 211,
BlockIdSplitSinglePhaseMeterFloat = 212, ModelIdSplitSinglePhaseMeterFloat = 212,
BlockIdWyeConnectThreePhaseMeterFloat = 213, ModelIdWyeConnectThreePhaseMeterFloat = 213,
BlockIdDeltaConnectThreePhaseMeterFloat = 214, ModelIdDeltaConnectThreePhaseMeterFloat = 214,
BlockIdSecureACMeterSelectedReadings = 220, ModelIdSecureACMeterSelectedReadings = 220,
BlockIdIrradianceModel = 302, ModelIdIrradianceModel = 302,
BlockIdBackOfModuleTemperatureModel = 303, ModelIdBackOfModuleTemperatureModel = 303,
BlockIdInclinometerModel = 304, ModelIdInclinometerModel = 304,
BlockIdGPS = 305, ModelIdGPS = 305,
BlockIdReferencePointModel = 306, ModelIdReferencePointModel = 306,
BlockIdBaseMet = 307, ModelIdBaseMet = 307,
BlockIdMiniMetModel = 308, ModelIdMiniMetModel = 308,
BlockIdStringCombiner = 401, ModelIdStringCombiner = 401,
BlockIdStringCombinerAdvanced = 402, ModelIdStringCombinerAdvanced = 402,
BlockIdStringCombinerCurrent = 403, ModelIdStringCombinerCurrent = 403,
BlockIdStringCombinerCurrentAdvanced = 404, ModelIdStringCombinerCurrentAdvanced = 404,
BlockIdSolarModuleFloat = 501, ModelIdSolarModuleFloat = 501,
BlockIdSolarModule = 502, ModelIdSolarModule = 502,
BlockIdTrackerController = 601, ModelIdTrackerController = 601,
BlockIdEnergyStorageBaseModel = 801, ModelIdEnergyStorageBaseModel = 801,
BlockIdBatteryBaseModel = 802, ModelIdBatteryBaseModel = 802,
BlockIdLithiumIonBatteryModel = 803, ModelIdLithiumIonBatteryModel = 803,
BlockIdVerisStatusConfiguration = 64001, ModelIdVerisStatusConfiguration = 64001,
BlockIdMersenGreenString = 64020, ModelIdMersenGreenString = 64020,
BlockIdEltekInverterExtension = 64101, ModelIdEltekInverterExtension = 64101,
BlockIdOutBackAXSDevice = 64110, ModelIdOutBackAXSDevice = 64110,
BlockIdBasicChargeController = 64111, ModelIdBasicChargeController = 64111,
BlockIdOutBackFMChargeController = 64112, ModelIdOutBackFMChargeController = 64112,
BlockIdEnd = 65535 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(); ~SunSpec();
bool connectModbus(); bool connectModbus();
void setHostAddress(const QHostAddress &hostAddress); void setHostAddress(const QHostAddress &hostAddress);
@ -194,14 +194,14 @@ public:
QString m_manufacturer = "Unknown"; QString m_manufacturer = "Unknown";
QString m_deviceModel = "Unknown"; QString m_deviceModel = "Unknown";
QString m_serialNumber = "Unknown"; QString m_serialNumber = "Unknown";
QHash<BlockId, int> m_mapList; QHash<ModelId, int> m_modelList;
void findBaseRegister(); void findBaseRegister();
void findModbusMap(const QList<BlockId> &mapIds, uint modbusAddressOffset = 2); void findSunSpecModels(const QList<ModelId> &modelIds, uint modbusAddressOffset = 2);
void readCommonMap(); void readCommonModel();
void readMapHeader(uint modbusAddress); void readModelHeader(uint modbusAddress);
void readMap(uint modbusAddress, uint modelLength); //modbusAddress = model start address, model length is without header void readModelDataBlock(uint modbusAddress, uint modelLength); //modbusAddress = model start address, model length is without header
float convertValueWithSSF(quint32 rawValue, quint16 sunssf); float convertValueWithSSF(quint32 rawValue, quint16 sunssf);
float convertFloatValues(quint16 rawValue0, quint16 rawValue1); float convertFloatValues(quint16 rawValue0, quint16 rawValue1);
@ -214,20 +214,19 @@ public:
signals: signals:
void connectionStateChanged(bool status); void connectionStateChanged(bool status);
void requestExecuted(QUuid requetId, bool success); void requestExecuted(const QUuid &requestId, bool success);
void foundBaseRegister(int modbusAddress); 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 foundSunSpecModel(ModelId modelId, int modbusStartRegister);
void modbusMapSearchFinished(const QHash<BlockId, int> &mapIds); void sunspecModelSearchFinished(const QHash<ModelId, int> &mapIds);
void mapHeaderReceived(uint modbusAddress, BlockId mapId, uint mapLength); void modelHeaderReceived(uint modbusAddress, ModelId mapId, uint mapLength);
void mapReceived(BlockId mapId, uint mapLength, QVector<quint16> data); void modelDataBlockReceived(ModelId id, uint ength, QVector<quint16> data);
private slots: private slots:
void onModbusStateChanged(QModbusDevice::State state); void onModbusStateChanged(QModbusDevice::State state);
void onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, const QVector<quint16> &values);
}; };
#endif // SUNSPEC_H #endif // SUNSPEC_H

View File

@ -33,17 +33,17 @@
#include <QTimer> #include <QTimer>
SunSpecInverter::SunSpecInverter(SunSpec *sunspec, SunSpec::BlockId mapId, int modbusAddress) : SunSpecInverter::SunSpecInverter(SunSpec *sunspec, SunSpec::ModelId modelId, int modbusAddress) :
QObject(sunspec), QObject(sunspec),
m_connection(sunspec), m_connection(sunspec),
m_id(mapId), m_id(modelId),
m_mapModbusStartRegister(modbusAddress) m_modelModbusStartRegister(modbusAddress)
{ {
qCDebug(dcSunSpec()) << "SunSpecInverter: Setting up inverter"; 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; return m_id;
} }
@ -51,10 +51,10 @@ SunSpec::BlockId SunSpecInverter::blockId()
void SunSpecInverter::init() void SunSpecInverter::init()
{ {
qCDebug(dcSunSpec()) << "SunSpecInverter: Init"; qCDebug(dcSunSpec()) << "SunSpecInverter: Init";
m_connection->readMapHeader(m_mapModbusStartRegister); m_connection->readModelHeader(m_modelModbusStartRegister);
connect(m_connection, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, SunSpec::BlockId mapId, uint mapLength) { connect(m_connection, &SunSpec::modelHeaderReceived, this, [this] (uint modbusAddress, SunSpec::ModelId modelId, uint length) {
qCDebug(dcSunSpec()) << "SunSpecInverter: Map Header received, modbus address:" << modbusAddress << "map Id:" << mapId << "map length:" << mapLength; qCDebug(dcSunSpec()) << "SunSpecInverter: Model Header received, modbus address:" << modbusAddress << "model Id:" << modelId << "length:" << length;
m_mapLength = mapLength; m_modelLength = length;
emit initFinished(true); emit initFinished(true);
m_initFinishedSuccess = 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 // 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<quint16> data) void SunSpecInverter::onModelDataBlockReceived(SunSpec::ModelId mapId, uint mapLength, QVector<quint16> data)
{ {
Q_UNUSED(mapLength) Q_UNUSED(mapLength)
if (mapId != m_id) { if (mapId != m_id) {
return; return;
} }
if (mapLength < m_mapLength) { if (mapLength < m_modelLength) {
qCDebug(dcSunSpec()) << "SunSpecInverter: on modbus map received, map length ist too short" << mapLength; qCDebug(dcSunSpec()) << "SunSpecInverter: on modbus map received, map length ist too short" << mapLength;
//return; //return;
} }
InverterData inverterData; InverterData inverterData;
switch (mapId) { switch (mapId) {
case SunSpec::BlockIdInverterSinglePhase: case SunSpec::ModelIdInverterSinglePhase:
case SunSpec::BlockIdInverterSplitPhase: case SunSpec::ModelIdInverterSplitPhase:
case SunSpec::BlockIdInverterThreePhase: { case SunSpec::ModelIdInverterThreePhase: {
inverterData.acCurrent= m_connection->convertValueWithSSF(data[Model10X::Model10XAcCurrent], data[Model10X::Model10XAmpereScaleFactor]); inverterData.acCurrent= m_connection->convertValueWithSSF(data[Model10X::Model10XAcCurrent], data[Model10X::Model10XAmpereScaleFactor]);
inverterData.acPower = m_connection->convertValueWithSSF(data[Model10X::Model10XACPower], data[Model10X::Model10XWattScaleFactor]); 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.phaseVoltageBN = m_connection->convertValueWithSSF(data[Model10X::Model10XPhaseVoltageBN], data[Model10X::Model10XVoltageScaleFactor]);
inverterData.phaseVoltageCN = m_connection->convertValueWithSSF(data[Model10X::Model10XPhaseVoltageCN], 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<quint32>(data.value(Model10X::Model10XAcEnergy))<<16)|static_cast<quint32>(data.value(Model10X::Model10XAcEnergy+1))); quint32 acEnergy = ((static_cast<quint32>(data.value(Model10X::Model10XAcEnergy))<<16)|static_cast<quint32>(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.acEnergy = m_connection->convertValueWithSSF(acEnergy, data[Model10X::Model10XWattHoursScaleFactor]);
inverterData.cabinetTemperature = m_connection->convertValueWithSSF(data[Model10X::Model10XCabinetTemperature], data[Model10X::Model10XTemperatureScaleFactor]); 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); emit inverterDataReceived(inverterData);
} break; } break;
case SunSpec::BlockIdInverterThreePhaseFloat: case SunSpec::ModelIdInverterThreePhaseFloat:
case SunSpec::BlockIdInverterSplitPhaseFloat: case SunSpec::ModelIdInverterSplitPhaseFloat:
case SunSpec::BlockIdInverterSinglePhaseFloat: { case SunSpec::ModelIdInverterSinglePhaseFloat: {
inverterData.acCurrent = m_connection->convertFloatValues(data[Model11X::Model11XAcCurrent], data[Model11X::Model11XAcCurrent+1]); inverterData.acCurrent = m_connection->convertFloatValues(data[Model11X::Model11XAcCurrent], data[Model11X::Model11XAcCurrent+1]);

View File

@ -96,22 +96,22 @@ public:
SunSpec::SunSpecOperatingState operatingState; SunSpec::SunSpecOperatingState operatingState;
}; };
SunSpecInverter(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress); SunSpecInverter(SunSpec *sunspec, SunSpec::ModelId modelId, int modbusAddress);
SunSpec::BlockId blockId(); SunSpec::ModelId modelId();
void init(); void init();
void getInverterMap(); void getInverterModelDataBlock();
private: private:
SunSpec *m_connection = nullptr; SunSpec *m_connection = nullptr;
SunSpec::BlockId m_id; //e.g. 103 for three phase inverter, 113 for three phase inverter with floating point representation SunSpec::ModelId m_id; //e.g. 103 for three phase inverter, 113 for three phase inverter with floating point representation
uint m_mapLength = 0; uint m_modelLength = 0;
uint m_mapModbusStartRegister = 40000; uint m_modelModbusStartRegister = 40000;
bool m_initFinishedSuccess = false; bool m_initFinishedSuccess = false;
void readInverterBlockHeader(); void getInverterModelHeader();
private slots: private slots:
void onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector<quint16> data); void onModelDataBlockReceived(SunSpec::ModelId mapId, uint mapLength, QVector<quint16> data);
signals: signals:
void initFinished(bool success); void initFinished(bool success);

View File

@ -31,28 +31,29 @@
#include "sunspecmeter.h" #include "sunspecmeter.h"
#include "extern-plugininfo.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), QObject(sunspec),
m_connection(sunspec), m_connection(sunspec),
m_id(blockId), m_id(modelId),
m_mapModbusStartRegister(modbusAddress) m_modelModbusStartRegister(modbusAddress)
{ {
qCDebug(dcSunSpec()) << "SunSpecMeter: Setting up meter"; 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; return m_id;
} }
void SunSpecMeter::init() void SunSpecMeter::init()
{ {
qCDebug(dcSunSpec()) << "SunSpecInverter: Init"; qCDebug(dcSunSpec()) << "SunSpecMeter: Init";
m_connection->readMapHeader(m_mapModbusStartRegister); m_connection->readModelHeader(m_modelModbusStartRegister);
connect(m_connection, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, SunSpec::BlockId mapId, uint mapLength) { connect(m_connection, &SunSpec::modelHeaderReceived, this, [this] (uint modbusAddress, SunSpec::ModelId modelId, uint length) {
qCDebug(dcSunSpec()) << "SunSpecInverter: Map Header received, modbus address:" << modbusAddress << "map Id:" << mapId << "map length:" << mapLength; qCDebug(dcSunSpec()) << "SunSpecMeter: Model Header received, modbus address:" << modbusAddress << "model Id:" << modelId << "length:" << length;
m_mapLength = mapLength; m_modelLength = length;
emit initFinished(true); emit initFinished(true);
m_initFinishedSuccess = 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<quint16> data) void SunSpecMeter::onModelDataBlockReceived(SunSpec::ModelId modelId, uint length, QVector<quint16> data)
{ {
Q_UNUSED(mapLength) if (modelId != m_id) {
return;
}
Q_UNUSED(length)
Q_UNUSED(data) 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: default:
break; break;
} }

View File

@ -68,22 +68,21 @@ public:
SunSpec::SunSpecOperatingState operatingState; SunSpec::SunSpecOperatingState operatingState;
}; };
SunSpecMeter(SunSpec *sunspec, SunSpec::BlockId blockId, int modbusAddress); SunSpecMeter(SunSpec *sunspec, SunSpec::ModelId modelId, int modbusAddress);
SunSpec::BlockId blockId(); SunSpec::ModelId modelId();
void init(); void init();
void getMeterMap(); void getMeterModelHeader();
void getMeterModelDataBlock();
private: private:
SunSpec *m_connection = nullptr; SunSpec *m_connection = nullptr;
SunSpec::BlockId m_id = SunSpec::BlockIdDeltaConnectThreePhaseMeter; SunSpec::ModelId m_id = SunSpec::ModelIdDeltaConnectThreePhaseMeter;
uint m_mapLength = 0; uint m_modelLength = 0;
uint m_mapModbusStartRegister = 40000; uint m_modelModbusStartRegister = 40000;
bool m_initFinishedSuccess = false; bool m_initFinishedSuccess = false;
void readMeterBlockHeader();
private slots: private slots:
void onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector<quint16> data); void onModelDataBlockReceived(SunSpec::ModelId modelId, uint length, QVector<quint16> data);
signals: signals:
void initFinished(bool success); void initFinished(bool success);

View File

@ -31,29 +31,28 @@
#include "sunspecstorage.h" #include "sunspecstorage.h"
#include "extern-plugininfo.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), QObject(sunspec),
m_connection(sunspec), m_connection(sunspec),
m_id(blockId), m_id(modelId),
m_mapModbusStartRegister(modbusAddress) m_modelModbusStartRegister(modbusAddress)
{ {
qCDebug(dcSunSpec()) << "SunSpecStorage: Setting up storage"; 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; return m_id;
} }
void SunSpecStorage::init() void SunSpecStorage::init()
{ {
qCDebug(dcSunSpec()) << "SunSpecInverter: Init"; qCDebug(dcSunSpec()) << "SunSpecStorage: Init";
m_connection->readMapHeader(m_mapModbusStartRegister); m_connection->readModelHeader(m_modelModbusStartRegister);
connect(m_connection, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, SunSpec::BlockId mapId, uint mapLength) { connect(m_connection, &SunSpec::modelHeaderReceived, this, [this] (uint modbusAddress, SunSpec::ModelId modelId, uint length) {
qCDebug(dcSunSpec()) << "SunSpecInverter: Map Header received, modbus address:" << modbusAddress << "map Id:" << mapId << "map length:" << mapLength; qCDebug(dcSunSpec()) << "SunSpecStorager: Model header received, modbus address:" << modbusAddress << "model Id:" << modelId << "length:" << length;
m_mapLength = mapLength; m_modelLength = length;
emit initFinished(true); emit initFinished(true);
m_initFinishedSuccess = 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) QUuid SunSpecStorage::setGridCharging(bool enabled)
@ -82,7 +81,7 @@ QUuid SunSpecStorage::setGridCharging(bool enabled)
PV (charging from grid 0 disabled) PV (charging from grid 0 disabled)
GRID (charging from 1 grid enabled*/ GRID (charging from 1 grid enabled*/
uint registerAddress = m_mapModbusStartRegister + Model124::Model124ChaGriSet; uint registerAddress = m_modelModbusStartRegister + Model124::Model124ChaGriSet;
quint16 value = enabled; quint16 value = enabled;
return m_connection->writeHoldingRegister(registerAddress, value); return m_connection->writeHoldingRegister(registerAddress, value);
} }
@ -93,7 +92,7 @@ QUuid SunSpecStorage::setStorageControlMode(bool chargingEnabled, bool dischargi
quint16 value = ((static_cast<quint16>(chargingEnabled) << StorageControlBitFieldCharge) | quint16 value = ((static_cast<quint16>(chargingEnabled) << StorageControlBitFieldCharge) |
(static_cast<quint16>(dischargingEnabled) << StorageControlBitFieldDischarge)) ; (static_cast<quint16>(dischargingEnabled) << StorageControlBitFieldDischarge)) ;
uint modbusRegister = m_mapModbusStartRegister + Model124::Model124ActivateStorageControlMode; uint modbusRegister = m_modelModbusStartRegister + Model124::Model124ActivateStorageControlMode;
return m_connection->writeHoldingRegister(modbusRegister, value); return m_connection->writeHoldingRegister(modbusRegister, value);
} }
@ -102,7 +101,7 @@ QUuid SunSpecStorage::setChargingRate(int rate)
//Register Name InWRte //Register Name InWRte
/* Defines the maximum charge rate (charge limit). Default is 100% */ /* 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; int16_t value = rate * 100;
return m_connection->writeHoldingRegister(modbusRegister, value); return m_connection->writeHoldingRegister(modbusRegister, value);
} }
@ -111,20 +110,37 @@ QUuid SunSpecStorage::setDischargingRate(int charging)
{ {
//Register Name OutWRte //Register Name OutWRte
/* Defines the maximum discharge rate (discharge limit). Default is 100% */ /* 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; quint16 value = charging * 100;
return m_connection->writeHoldingRegister(modbusRegister, value); return m_connection->writeHoldingRegister(modbusRegister, value);
} }
void SunSpecStorage::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, const QVector<quint16> &data) void SunSpecStorage::onModelDataBlockReceived(SunSpec::ModelId modelId, uint length, const QVector<quint16> &data)
{ {
Q_UNUSED(mapLength) Q_UNUSED(length)
switch (mapId) { if (modelId != m_id) {
case SunSpec::BlockIdStorage: { return;
}
switch (modelId) {
case SunSpec::ModelIdStorage: {
StorageData storageData; 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]); 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); emit storageDataReceived(storageData);
} break; } break;
case SunSpec::ModelIdBatteryBaseModel:
case SunSpec::ModelIdLithiumIonBatteryModel: {
}
default: default:
break; break;
} }

View File

@ -38,11 +38,12 @@ class SunSpecStorage : public QObject
{ {
Q_OBJECT Q_OBJECT
public: 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 init();
void getStorageMap(); void getStorageModelHeader();
void getStorageModelDataBlock();
QUuid setGridCharging(bool enabled); QUuid setGridCharging(bool enabled);
QUuid setDischargingRate(int rate); QUuid setDischargingRate(int rate);
@ -93,15 +94,13 @@ public:
private: private:
SunSpec *m_connection = nullptr; SunSpec *m_connection = nullptr;
SunSpec::BlockId m_id = SunSpec::BlockIdEnergyStorageBaseModel; SunSpec::ModelId m_id = SunSpec::ModelIdEnergyStorageBaseModel;
uint m_mapLength = 0; uint m_modelLength = 0;
uint m_mapModbusStartRegister = 40000; uint m_modelModbusStartRegister = 40000;
bool m_initFinishedSuccess = false; bool m_initFinishedSuccess = false;
void readStorageBlockHeader();
private slots: private slots:
void onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, const QVector<quint16> &data); void onModelDataBlockReceived(SunSpec::ModelId modelId, uint length, const QVector<quint16> &data);
signals: signals:
void initFinished(bool success); void initFinished(bool success);

View File

@ -60,7 +60,7 @@ public:
void getTrackerMap(); void getTrackerMap();
private: private:
BlockId m_id = BlockIdTrackerController; ModelId m_id = ModelIdTrackerController;
uint m_mapLength = 0; uint m_mapLength = 0;
uint m_mapModbusStartRegister = 40000; uint m_mapModbusStartRegister = 40000;