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