fixed rebase
This commit is contained in:
parent
c1cdaac571
commit
5a2be02340
@ -35,25 +35,71 @@
|
|||||||
|
|
||||||
IntegrationPluginSunSpec::IntegrationPluginSunSpec()
|
IntegrationPluginSunSpec::IntegrationPluginSunSpec()
|
||||||
{
|
{
|
||||||
}
|
|
||||||
|
|
||||||
void IntegrationPluginSunSpec::discoverThings(ThingDiscoveryInfo *info)
|
|
||||||
{
|
|
||||||
// Discovery possible?
|
|
||||||
Q_ASSERT_X(false, "discoverThing", QString("Unhandled thingClassId: %1").arg(info->thingClassId().toString()).toUtf8());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info)
|
void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info)
|
||||||
{
|
{
|
||||||
if (info->thing()->thingClassId() == sunspecInverterThingClassId) {
|
if (info->thing()->thingClassId() == sunspecInverterThingClassId) {
|
||||||
|
QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecInverterThingIpAddressParamTypeId).toString());
|
||||||
|
SunSpecInverter *sunSpecInverter = new SunSpecInverter(address, 502, this);
|
||||||
|
m_sunSpecInverters.insert(info->thing(), sunSpecInverter);
|
||||||
|
if (!sunSpecInverter->connectModbus()) {
|
||||||
|
QTimer::singleShot(2000, info, [this, info] {
|
||||||
|
setupThing(info);
|
||||||
|
});
|
||||||
|
qCWarning(dcSunSpec()) << "Error connecting to SunSpec device";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connect(sunSpecInverter, &SunSpecInverter::initFinished, info, [info] {
|
||||||
|
qCDebug(dcSunSpec()) << "Modbus Inverter init finished";
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
});
|
||||||
|
|
||||||
} else if (info->thing()->thingClassId() == sunspecPvModuleThingClassId) {
|
connect(info, &ThingSetupInfo::aborted, sunSpecInverter, [info, this] {
|
||||||
|
m_sunSpecInverters.take(info->thing())->deleteLater();
|
||||||
|
});
|
||||||
|
connect(sunSpecInverter, &SunSpecInverter::inverterDataReceived, this, &IntegrationPluginSunSpec::onInverterDataReceived);
|
||||||
|
|
||||||
} else if (info->thing()->thingClassId() == sunspecMeterThingClassId) {
|
} else if (info->thing()->thingClassId() == sunspecMeterThingClassId) {
|
||||||
|
QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecMeterThingIpAddressParamTypeId).toString());
|
||||||
|
SunSpecMeter *sunSpecMeter = new SunSpecMeter(address, 502, this);
|
||||||
|
m_sunSpecMeters.insert(info->thing(), sunSpecMeter);
|
||||||
|
if (!sunSpecMeter->connectModbus()) {
|
||||||
|
QTimer::singleShot(2000, info, [this, info] {
|
||||||
|
setupThing(info);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(info, &ThingSetupInfo::aborted, sunSpecMeter, [info, this] {
|
||||||
|
m_sunSpecMeters.take(info->thing())->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
} else if (info->thing()->thingClassId() == sunspecTrackerThingClassId) {
|
} else if (info->thing()->thingClassId() == sunspecTrackerThingClassId) {
|
||||||
|
QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecTrackerThingIpAddressParamTypeId).toString());
|
||||||
|
SunSpecTracker *sunSpecTracker = new SunSpecTracker(address, 502, this);
|
||||||
|
m_sunSpecTrackers.insert(info->thing(), sunSpecTracker);
|
||||||
|
if (!sunSpecTracker->connectModbus()) {
|
||||||
|
QTimer::singleShot(2000, info, [this, info] {
|
||||||
|
setupThing(info);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
connect(info, &ThingSetupInfo::aborted, sunSpecTracker, [info, this] {
|
||||||
|
m_sunSpecTrackers.take(info->thing())->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
} else if (info->thing()->thingClassId() == sunspecStorageThingClassId) {
|
} else if (info->thing()->thingClassId() == sunspecStorageThingClassId) {
|
||||||
|
QHostAddress address = QHostAddress(info->thing()->paramValue(sunspecStorageThingIpAddressParamTypeId).toString());
|
||||||
|
SunSpecStorage *sunSpecStorage = new SunSpecStorage(address, 502, this);
|
||||||
|
m_sunSpecStorages.insert(info->thing(), sunSpecStorage);
|
||||||
|
if (!sunSpecStorage->connectModbus()) {
|
||||||
|
QTimer::singleShot(2000, info, [this, info] {
|
||||||
|
setupThing(info);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
connect(info, &ThingSetupInfo::aborted, sunSpecStorage, [info, this] {
|
||||||
|
m_sunSpecStorages.take(info->thing())->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
} 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());
|
||||||
@ -69,12 +115,77 @@ void IntegrationPluginSunSpec::postSetupThing(Thing *thing)
|
|||||||
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(refreshTime);
|
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(refreshTime);
|
||||||
connect(m_refreshTimer, &PluginTimer::timeout, this, &IntegrationPluginSunSpec::onRefreshTimer);
|
connect(m_refreshTimer, &PluginTimer::timeout, this, &IntegrationPluginSunSpec::onRefreshTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (thing->thingClassId() == sunspecInverterThingClassId) {
|
||||||
|
SunSpecInverter *sunSpecInverter = m_sunSpecInverters.take(thing);
|
||||||
|
if (!sunSpecInverter)
|
||||||
|
return;
|
||||||
|
sunSpecInverter->getInverterMap();
|
||||||
|
thing->setParamValue(sunspecInverterThingManufacturerParamTypeId, sunSpecInverter->manufacturer());
|
||||||
|
thing->setParamValue(sunspecInverterThingSerialNumberParamTypeId, sunSpecInverter->serialNumber());
|
||||||
|
thing->setParamValue(sunspecInverterThingDeviceModelParamTypeId, sunSpecInverter->deviceModel());
|
||||||
|
|
||||||
|
} else if (thing->thingClassId() == sunspecMeterThingClassId) {
|
||||||
|
SunSpecMeter *sunSpecMeter = m_sunSpecMeters.take(thing);
|
||||||
|
if (!sunSpecMeter)
|
||||||
|
return;
|
||||||
|
//TODO upate data
|
||||||
|
thing->setParamValue(sunspecMeterThingManufacturerParamTypeId, sunSpecMeter->manufacturer());
|
||||||
|
thing->setParamValue(sunspecMeterThingSerialNumberParamTypeId, sunSpecMeter->serialNumber());
|
||||||
|
thing->setParamValue(sunspecMeterThingDeviceModelParamTypeId, sunSpecMeter->deviceModel());
|
||||||
|
|
||||||
|
} else if (thing->thingClassId() == sunspecTrackerThingClassId) {
|
||||||
|
SunSpecTracker *sunSpecTracker = m_sunSpecTrackers.take(thing);
|
||||||
|
if (!sunSpecTracker)
|
||||||
|
return;
|
||||||
|
//TODO upate data
|
||||||
|
thing->setParamValue(sunspecTrackerThingManufacturerParamTypeId, sunSpecTracker->manufacturer());
|
||||||
|
thing->setParamValue(sunspecTrackerThingSerialNumberParamTypeId, sunSpecTracker->serialNumber());
|
||||||
|
thing->setParamValue(sunspecTrackerThingDeviceModelParamTypeId, sunSpecTracker->deviceModel());
|
||||||
|
|
||||||
|
} else if (thing->thingClassId() == sunspecStorageThingClassId) {
|
||||||
|
SunSpecStorage *sunSpecStorage = m_sunSpecStorages.take(thing);
|
||||||
|
if (!sunSpecStorage)
|
||||||
|
return;
|
||||||
|
//TODO upate data
|
||||||
|
thing->setParamValue(sunspecStorageThingManufacturerParamTypeId, sunSpecStorage->manufacturer());
|
||||||
|
thing->setParamValue(sunspecStorageThingSerialNumberParamTypeId, sunSpecStorage->serialNumber());
|
||||||
|
thing->setParamValue(sunspecStorageThingDeviceModelParamTypeId, sunSpecStorage->deviceModel());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Q_ASSERT_X(false, "postSetupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginSunSpec::thingRemoved(Thing *thing)
|
void IntegrationPluginSunSpec::thingRemoved(Thing *thing)
|
||||||
{
|
{
|
||||||
Q_UNUSED(thing)
|
Q_UNUSED(thing)
|
||||||
|
|
||||||
|
if (thing->thingClassId() == sunspecInverterThingClassId) {
|
||||||
|
SunSpecInverter *sunSpecInverter = m_sunSpecInverters.take(thing);
|
||||||
|
if (sunSpecInverter)
|
||||||
|
sunSpecInverter->deleteLater();
|
||||||
|
|
||||||
|
} else if (thing->thingClassId() == sunspecMeterThingClassId) {
|
||||||
|
SunSpecMeter *sunSpecMeter = m_sunSpecMeters.take(thing);
|
||||||
|
if (sunSpecMeter)
|
||||||
|
sunSpecMeter->deleteLater();
|
||||||
|
|
||||||
|
} else if (thing->thingClassId() == sunspecTrackerThingClassId) {
|
||||||
|
SunSpecTracker *sunSpecTracker = m_sunSpecTrackers.take(thing);
|
||||||
|
if (sunSpecTracker)
|
||||||
|
sunSpecTracker->deleteLater();
|
||||||
|
|
||||||
|
} else if (thing->thingClassId() == sunspecStorageThingClassId) {
|
||||||
|
SunSpecStorage *sunSpecStorage = m_sunSpecStorages.take(thing);
|
||||||
|
if (sunSpecStorage)
|
||||||
|
sunSpecStorage->deleteLater();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Q_ASSERT_X(false, "thingRemoved", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
||||||
|
}
|
||||||
|
|
||||||
if (myThings().isEmpty()) {
|
if (myThings().isEmpty()) {
|
||||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
|
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
|
||||||
m_refreshTimer = nullptr;
|
m_refreshTimer = nullptr;
|
||||||
@ -84,18 +195,76 @@ void IntegrationPluginSunSpec::thingRemoved(Thing *thing)
|
|||||||
void IntegrationPluginSunSpec::executeAction(ThingActionInfo *info)
|
void IntegrationPluginSunSpec::executeAction(ThingActionInfo *info)
|
||||||
{
|
{
|
||||||
Thing *thing = info->thing();
|
Thing *thing = info->thing();
|
||||||
//Action action = info->action();
|
Action action = info->action();
|
||||||
|
|
||||||
if (thing->thingClassId() == sunspecInverterThingClassId) {
|
if (thing->thingClassId() == sunspecInverterThingClassId) {
|
||||||
|
SunSpecInverter *sunSpecInverter = m_sunSpecInverters.value(thing);
|
||||||
|
if (!sunSpecInverter) {
|
||||||
|
qWarning(dcSunSpec()) << "Could not find SunSpec inverter for thing" << thing->name();
|
||||||
|
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
info->finish(Thing::ThingErrorActionTypeNotFound);
|
info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||||
|
|
||||||
} else if (thing->thingClassId() == sunspecMeterThingClassId) {
|
} else if (thing->thingClassId() == sunspecMeterThingClassId) {
|
||||||
info->finish(Thing::ThingErrorActionTypeNotFound);
|
info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||||
|
|
||||||
} else if (thing->thingClassId() == sunspecTrackerThingClassId) {
|
} else if (thing->thingClassId() == sunspecTrackerThingClassId) {
|
||||||
info->finish(Thing::ThingErrorActionTypeNotFound);
|
info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||||
} else if (thing->thingClassId() == sunspecPvModuleThingClassId) {
|
|
||||||
info->finish(Thing::ThingErrorActionTypeNotFound);
|
|
||||||
} else if (thing->thingClassId() == sunspecStorageThingClassId) {
|
} else if (thing->thingClassId() == sunspecStorageThingClassId) {
|
||||||
info->finish(Thing::ThingErrorActionTypeNotFound);
|
SunSpecStorage *sunSpecStorage = m_sunSpecStorages.value(thing);
|
||||||
|
if (!sunSpecStorage) {
|
||||||
|
qWarning(dcSunSpec()) << "Could not find sunspec instance for thing";
|
||||||
|
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.actionTypeId() == sunspecStorageGridChargingActionTypeId) {
|
||||||
|
QUuid requestId = sunSpecStorage->setGridCharging(action.param(sunspecStorageGridChargingActionGridChargingParamTypeId).value().toBool());
|
||||||
|
if (requestId.isNull()) {
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
} else {
|
||||||
|
m_asyncActions.insert(requestId, info);
|
||||||
|
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
|
||||||
|
}
|
||||||
|
} else if (action.actionTypeId() == sunspecStorageEnableChargingLimitActionTypeId) {
|
||||||
|
int value = (action.param(sunspecStorageEnableChargingLimitActionEnableChargingLimitParamTypeId).value().toBool() << 1) | thing->stateValue(sunspecStorageEnableDischargingLimitStateTypeId).toBool();
|
||||||
|
QUuid requestId = sunSpecStorage->setStorageControlMode(value);
|
||||||
|
if (requestId.isNull()) {
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
} else {
|
||||||
|
m_asyncActions.insert(requestId, info);
|
||||||
|
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
|
||||||
|
}
|
||||||
|
} else if (action.actionTypeId() == sunspecStorageChargingRateActionTypeId) {
|
||||||
|
QUuid requestId = sunSpecStorage->setChargingRate(action.param(sunspecStorageChargingRateActionChargingRateParamTypeId).value().toInt());
|
||||||
|
if (requestId.isNull()) {
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
} else {
|
||||||
|
m_asyncActions.insert(requestId, info);
|
||||||
|
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
|
||||||
|
}
|
||||||
|
} else if (action.actionTypeId() == sunspecStorageEnableDischargingLimitActionTypeId) {
|
||||||
|
int value = (action.param(sunspecStorageEnableDischargingLimitActionEnableDischargingLimitParamTypeId).value().toBool() << 1) | thing->stateValue(sunspecStorageEnableChargingLimitStateTypeId).toBool();
|
||||||
|
QUuid requestId = sunSpecStorage->setStorageControlMode(value);
|
||||||
|
if (requestId.isNull()) {
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
} else {
|
||||||
|
m_asyncActions.insert(requestId, info);
|
||||||
|
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
|
||||||
|
}
|
||||||
|
} else if (action.actionTypeId() == sunspecStorageDischargingRateActionTypeId) {
|
||||||
|
QUuid requestId = sunSpecStorage->setDischargingRate(action.param(sunspecStorageDischargingRateActionDischargingRateParamTypeId).value().toInt());
|
||||||
|
if (requestId.isNull()) {
|
||||||
|
info->finish(Thing::ThingErrorHardwareFailure);
|
||||||
|
} else {
|
||||||
|
m_asyncActions.insert(requestId, info);
|
||||||
|
connect(info, &ThingActionInfo::aborted, this, [requestId, this] {m_asyncActions.remove(requestId);});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Q_ASSERT_X(false, "executeAction", QString("Unhandled thingClassId: %1").arg(info->thing()->thingClassId().toString()).toUtf8());
|
Q_ASSERT_X(false, "executeAction", QString("Unhandled thingClassId: %1").arg(info->thing()->thingClassId().toString()).toUtf8());
|
||||||
}
|
}
|
||||||
@ -104,6 +273,18 @@ void IntegrationPluginSunSpec::executeAction(ThingActionInfo *info)
|
|||||||
void IntegrationPluginSunSpec::onRefreshTimer()
|
void IntegrationPluginSunSpec::onRefreshTimer()
|
||||||
{
|
{
|
||||||
//get data
|
//get data
|
||||||
|
foreach (SunSpecInverter *inverter, m_sunSpecInverters) {
|
||||||
|
inverter->getInverterMap();
|
||||||
|
}
|
||||||
|
foreach (SunSpecMeter *meter, m_sunSpecMeters) {
|
||||||
|
Q_UNUSED(meter) //TODO
|
||||||
|
}
|
||||||
|
foreach (SunSpecTracker *tracker, m_sunSpecTrackers) {
|
||||||
|
Q_UNUSED(tracker) //TODO
|
||||||
|
}
|
||||||
|
foreach (SunSpecStorage *storage, m_sunSpecStorages) {
|
||||||
|
Q_UNUSED(storage) //TODO
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void IntegrationPluginSunSpec::onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value)
|
void IntegrationPluginSunSpec::onPluginConfigurationChanged(const ParamTypeId ¶mTypeId, const QVariant &value)
|
||||||
@ -117,3 +298,137 @@ void IntegrationPluginSunSpec::onPluginConfigurationChanged(const ParamTypeId &p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginSunSpec::onInverterDataReceived(const SunSpecInverter::InverterData &inverterData)
|
||||||
|
{
|
||||||
|
SunSpecInverter *inverter = static_cast<SunSpecInverter *>(sender());
|
||||||
|
Thing *thing = m_sunSpecInverters.key(inverter);
|
||||||
|
|
||||||
|
if(!thing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
thing->setStateValue(sunspecInverterAcPowerStateTypeId, inverterData.acPower);
|
||||||
|
thing->setStateValue(sunspecInverterAcEnergyStateTypeId, inverterData.acEnergy/1000.00);
|
||||||
|
thing->setStateValue(sunspecInverterLineFrequencyStateTypeId, inverterData.lineFrequency);
|
||||||
|
thing->setStateValue(sunspecInverterTotalCurrentStateTypeId, inverterData.acCurrent);
|
||||||
|
thing->setStateValue(sunspecInverterCabinetTemperatureStateTypeId, inverterData.cabinetTemperature);
|
||||||
|
|
||||||
|
thing->setStateValue(sunspecInverterPhaseACurrentStateTypeId, inverterData.phaseACurrent);
|
||||||
|
thing->setStateValue(sunspecInverterPhaseBCurrentStateTypeId, inverterData.phaseBCurrent);
|
||||||
|
thing->setStateValue(sunspecInverterPhaseCCurrentStateTypeId, inverterData.phaseCCurrent);
|
||||||
|
|
||||||
|
thing->setStateValue(sunspecInverterPhaseANVoltageStateTypeId, inverterData.phaseVoltageAN);
|
||||||
|
thing->setStateValue(sunspecInverterPhaseBNVoltageStateTypeId, inverterData.phaseVoltageBN);
|
||||||
|
thing->setStateValue(sunspecInverterPhaseCNVoltageStateTypeId, inverterData.phaseVoltageCN);
|
||||||
|
|
||||||
|
switch(inverterData.operatingState) {
|
||||||
|
case SunSpec::SunSpecOperatingState::Off:
|
||||||
|
thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Off");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecOperatingState::MPPT:
|
||||||
|
thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "MPPT");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecOperatingState::Fault:
|
||||||
|
thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Fault");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecOperatingState::Standby:
|
||||||
|
thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Standby");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecOperatingState::Sleeping:
|
||||||
|
thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Sleeping");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecOperatingState::Starting:
|
||||||
|
thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Starting");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecOperatingState::Throttled:
|
||||||
|
thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Throttled");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecOperatingState::ShuttingDown:
|
||||||
|
thing->setStateValue(sunspecInverterOperatingStateStateTypeId, "Shutting down");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(inverterData.event) {
|
||||||
|
case SunSpec::SunSpecEvent1::OVER_TEMP:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Over temperature");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::UNDER_TEMP:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Under temperature");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::GroundFault:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Ground fault");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::MEMORY_LOSS:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Memory loss");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::AC_OVER_VOLT:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "AC voltage above limit");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::CABINET_OPEN:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Cabinet open");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::AC_DISCONNECT:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "AC disconnect open");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::AC_UNDER_VOLT:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "AC voltage under limit");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::DC_DISCONNECT:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "DC disconnect open");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::DcOverVolatage:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "DC over voltage");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::OVER_FREQUENCY:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Frequency above limit");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::GRID_DISCONNECT:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Grid disconnect");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::HW_TEST_FAILURE:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Hardware test failure");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::MANUAL_SHUTDOWN:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Manual shutdown");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::UNDER_FREQUENCY:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Frequency under limit");
|
||||||
|
break;
|
||||||
|
case SunSpec::SunSpecEvent1::BLOWN_STRING_FUSE:
|
||||||
|
thing->setStateValue(sunspecInverterErrorStateTypeId, "Blown string fuse on input");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginSunSpec::onStorageDataReceived(const SunSpecStorage::StorageData &storageData)
|
||||||
|
{
|
||||||
|
SunSpecStorage *storage = static_cast<SunSpecStorage *>(sender());
|
||||||
|
Thing *thing = m_sunSpecStorages.key(storage);
|
||||||
|
|
||||||
|
if(!thing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
thing->setStateValue(sunspecStorageStorageStateStateTypeId, storageData.chargingState);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginSunSpec::onMeterDataReceived(const SunSpecMeter::MeterData &meterData)
|
||||||
|
{
|
||||||
|
SunSpecMeter *meter = static_cast<SunSpecMeter *>(sender());
|
||||||
|
Thing *thing = m_sunSpecMeters.key(meter);
|
||||||
|
|
||||||
|
if(!thing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//thing->setStateValue(sunspecMeterStorageStateStateTypeId, meterData.event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IntegrationPluginSunSpec::onTrackerDataReceived(const SunSpecTracker::TrackerData &trackerData)
|
||||||
|
{
|
||||||
|
SunSpecTracker *tracker = static_cast<SunSpecTracker *>(sender());
|
||||||
|
Thing *thing = m_sunSpecTrackers.key(tracker);
|
||||||
|
|
||||||
|
if(!thing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//thing->setStateValue(sunspecStorageStorageStateStateTypeId, storageData.chargingState);
|
||||||
|
}
|
||||||
|
|||||||
@ -36,6 +36,11 @@
|
|||||||
|
|
||||||
#include "../modbus/modbustcpmaster.h"
|
#include "../modbus/modbustcpmaster.h"
|
||||||
|
|
||||||
|
#include "sunspecinverter.h"
|
||||||
|
#include "sunspecstorage.h"
|
||||||
|
#include "sunspecmeter.h"
|
||||||
|
#include "sunspectracker.h"
|
||||||
|
|
||||||
#include <QUuid>
|
#include <QUuid>
|
||||||
|
|
||||||
class IntegrationPluginSunSpec: public IntegrationPlugin
|
class IntegrationPluginSunSpec: public IntegrationPlugin
|
||||||
@ -45,22 +50,21 @@ class IntegrationPluginSunSpec: public IntegrationPlugin
|
|||||||
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginsunspec.json")
|
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginsunspec.json")
|
||||||
Q_INTERFACES(IntegrationPlugin)
|
Q_INTERFACES(IntegrationPlugin)
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit IntegrationPluginSunSpec();
|
explicit IntegrationPluginSunSpec();
|
||||||
|
|
||||||
void discoverThings(ThingDiscoveryInfo *info) override;
|
|
||||||
void setupThing(ThingSetupInfo *info) override;
|
void setupThing(ThingSetupInfo *info) override;
|
||||||
void postSetupThing(Thing *thing) override;
|
void postSetupThing(Thing *thing) override;
|
||||||
void thingRemoved(Thing *thing) override;
|
void thingRemoved(Thing *thing) override;
|
||||||
void executeAction(ThingActionInfo *info) override;
|
void executeAction(ThingActionInfo *info) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
PluginTimer *m_refreshTimer = nullptr;
|
PluginTimer *m_refreshTimer = nullptr;
|
||||||
QHash<Thing *, ModbusTCPMaster *> m_modbusTcpMasters;
|
|
||||||
QHash<QUuid, ThingActionInfo *> m_asyncActions;
|
QHash<QUuid, ThingActionInfo *> m_asyncActions;
|
||||||
|
|
||||||
|
QHash<Thing *, SunSpecInverter *> m_sunSpecInverters;
|
||||||
|
QHash<Thing *, SunSpecStorage *> m_sunSpecStorages;
|
||||||
|
QHash<Thing *, SunSpecMeter *> m_sunSpecMeters;
|
||||||
|
QHash<Thing *, SunSpecTracker *> m_sunSpecTrackers;
|
||||||
void update(Thing *thing);
|
void update(Thing *thing);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
@ -72,8 +76,10 @@ private slots:
|
|||||||
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);
|
||||||
|
|
||||||
void onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, const QVector<quint16> &values);
|
void onInverterDataReceived(const SunSpecInverter::InverterData &inverterData);
|
||||||
void onReceivedInputRegister(quint32 slaveAddress, quint32 modbusRegister, const QVector<quint16> &values);
|
void onStorageDataReceived(const SunSpecStorage::StorageData &storageData);
|
||||||
|
void onMeterDataReceived(const SunSpecMeter::MeterData &meterData);
|
||||||
|
void onTrackerDataReceived(const SunSpecTracker::TrackerData &trackerData);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // INTEGRATIONPLUGINSUNSPEC_H
|
#endif // INTEGRATIONPLUGINSUNSPEC_H
|
||||||
|
|||||||
@ -32,10 +32,28 @@
|
|||||||
"type": "QString"
|
"type": "QString"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "08bf6597-976d-4fb9-8fc2-4372c07961d4",
|
"id": "418d90eb-16e9-47e4-83e3-a116a8fa4de7",
|
||||||
|
"name":"manufacturer",
|
||||||
|
"displayName": "Manufacturer",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "edf1bc0f-d143-4b75-a11f-635339fb30bf",
|
||||||
|
"name":"deviceModel",
|
||||||
|
"displayName": "Device model",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d5181377-c746-4352-8683-6d90a4a83e6e",
|
||||||
"name":"serialNumber",
|
"name":"serialNumber",
|
||||||
"displayName": "Serial number",
|
"displayName": "Serial number",
|
||||||
"type": "QString"
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stateTypes":[
|
"stateTypes":[
|
||||||
@ -57,48 +75,150 @@
|
|||||||
"type": "bool",
|
"type": "bool",
|
||||||
"defaultValue": 0,
|
"defaultValue": 0,
|
||||||
"writable": true
|
"writable": true
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "sunspecPvModule",
|
|
||||||
"displayName": "SunSpec PV Module",
|
|
||||||
"id": "fb7ff0df-a745-4313-83e4-1e5007b06fe2",
|
|
||||||
"createMethods": [ "User" ],
|
|
||||||
"interfaces": ["connectable"],
|
|
||||||
"paramTypes": [
|
|
||||||
{
|
|
||||||
"id": "890e468b-fc76-4641-9569-93cd45e6bb8d",
|
|
||||||
"name":"ipAddress",
|
|
||||||
"displayName": "IP address",
|
|
||||||
"type": "QString"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "d5a9510d-720f-4a28-9d91-977eedd022bd",
|
"id": "26560dd8-6de4-445e-ba55-391d7241c370",
|
||||||
"name":"serialNumber",
|
"name": "totalCurrent",
|
||||||
"displayName": "Serial number",
|
"displayName": "Total AC current",
|
||||||
"type": "QString"
|
"displayNameEvent": "Total AC current changed",
|
||||||
}
|
"type": "int",
|
||||||
],
|
"unit": "Ampere",
|
||||||
"stateTypes":[
|
"defaultValue": 0
|
||||||
{
|
|
||||||
"id": "edc032d2-022f-43f7-9131-831ad23ee62a",
|
|
||||||
"name": "connected",
|
|
||||||
"displayName": "Connected",
|
|
||||||
"displayNameEvent": "Connected changed",
|
|
||||||
"type": "bool",
|
|
||||||
"defaultValue": false,
|
|
||||||
"cached": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "0b7485eb-3cf7-45b2-87cf-9ec85366b197",
|
"id": "3140ccd3-40cf-46c8-8bb2-8c3ea4582f84",
|
||||||
"name": "power",
|
"name": "phaseACurrent",
|
||||||
"displayName": "Power",
|
"displayName": "Phase A current",
|
||||||
"displayNameEvent": "Power changed",
|
"displayNameEvent": "Phase A current changed",
|
||||||
"displayNameAction": "Change power",
|
"type": "int",
|
||||||
"type": "bool",
|
"unit": "Ampere",
|
||||||
"defaultValue": 0,
|
"defaultValue": 0
|
||||||
"writable": true
|
},
|
||||||
|
{
|
||||||
|
"id": "7ea1a53a-6fd9-4914-8283-b57aa1aaaebf",
|
||||||
|
"name": "phaseBCurrent",
|
||||||
|
"displayName": "Phase B current",
|
||||||
|
"displayNameEvent": "Phase B current changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Ampere",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "aa4b4cf5-43d0-4be5-9505-403918b5371d",
|
||||||
|
"name": "phaseCCurrent",
|
||||||
|
"displayName": "Phase C current",
|
||||||
|
"displayNameEvent": "Phase C current changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Ampere",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f08521aa-9c38-4c31-95e1-acb616f6e9c6",
|
||||||
|
"name": "phaseANVoltage",
|
||||||
|
"displayName": "Phase AN voltage",
|
||||||
|
"displayNameEvent": "Phase AN volatage changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Volt",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "739b8805-d522-4406-bede-d1e4200a3aa9",
|
||||||
|
"name": "phaseBNVoltage",
|
||||||
|
"displayName": "Phase BN voltage",
|
||||||
|
"displayNameEvent": "Phase BN voltage changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Volt",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "949b797d-5566-4667-8982-e430d23548e2",
|
||||||
|
"name": "phaseCNVoltage",
|
||||||
|
"displayName": "Phase CN voltage",
|
||||||
|
"displayNameEvent": "Phase CN voltage changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Volt",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "14036f44-25fd-4e93-8e8c-c677b06a2c34",
|
||||||
|
"name": "acPower",
|
||||||
|
"displayName": "AC power",
|
||||||
|
"displayNameEvent": "AC power changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "KiloWatt",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "faa45cae-ed28-4150-9036-fceddf9d6776",
|
||||||
|
"name": "lineFrequency",
|
||||||
|
"displayName": "Line frequency",
|
||||||
|
"displayNameEvent": "Line frequency changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Hertz",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d493880d-eb58-4530-8010-8ea4f6d63387",
|
||||||
|
"name": "acEnergy",
|
||||||
|
"displayName": "AC energy",
|
||||||
|
"displayNameEvent": "AC energy changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "KiloWattHour",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "44b0320f-89a7-4248-bad4-288ef898a5cc",
|
||||||
|
"name": "cabinetTemperature",
|
||||||
|
"displayName": "Cabinet temperature",
|
||||||
|
"displayNameEvent": "Cabinet temperature changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "DegreeCelsius",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cebdce98-42d1-4a28-8834-8960efc0e83f",
|
||||||
|
"name": "operatingState",
|
||||||
|
"displayName": "Operating state",
|
||||||
|
"displayNameEvent": "Operating state changed",
|
||||||
|
"type": "QString",
|
||||||
|
"possibleValues": [
|
||||||
|
"Off",
|
||||||
|
"Sleeping",
|
||||||
|
"Starting",
|
||||||
|
"MPPT",
|
||||||
|
"Throttled",
|
||||||
|
"Shutting down",
|
||||||
|
"Fault",
|
||||||
|
"Standby"
|
||||||
|
],
|
||||||
|
"defaultValue": "Off"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4479af96-c499-4f15-abd6-4afdb18a9e09",
|
||||||
|
"name": "error",
|
||||||
|
"displayName": "Error",
|
||||||
|
"displayNameEvent": "Error changed",
|
||||||
|
"type": "QString",
|
||||||
|
"possibleValues": [
|
||||||
|
"None",
|
||||||
|
"Ground fault",
|
||||||
|
"DC over voltage",
|
||||||
|
"AC disconnect open",
|
||||||
|
"DC disconnect open",
|
||||||
|
"Grid disconnect",
|
||||||
|
"Cabinet open",
|
||||||
|
"Manual shutdown",
|
||||||
|
"Over temperature",
|
||||||
|
"Frequency above limit",
|
||||||
|
"Frequency under limit",
|
||||||
|
"AC voltage above limit",
|
||||||
|
"AC voltage under limit",
|
||||||
|
"Blown string fuse on input",
|
||||||
|
"Under temperature",
|
||||||
|
"Communication error",
|
||||||
|
"Hardware test failure"
|
||||||
|
],
|
||||||
|
"defaultValue": "None"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -116,10 +236,28 @@
|
|||||||
"type": "QString"
|
"type": "QString"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "89af6d27-4b81-42ef-9883-1e2ba2067884",
|
"id": "aa1ca51d-5e9c-4a1a-a4ec-e479d4f702d5",
|
||||||
|
"name":"manufacturer",
|
||||||
|
"displayName": "Manufacturer",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "52da5388-28d7-48a2-8ad6-4c1e852a4348",
|
||||||
|
"name":"deviceModel",
|
||||||
|
"displayName": "Device model",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9fa7288c-e7a1-4163-baa1-6f484e1feb55",
|
||||||
"name":"serialNumber",
|
"name":"serialNumber",
|
||||||
"displayName": "Serial number",
|
"displayName": "Serial number",
|
||||||
"type": "QString"
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stateTypes":[
|
"stateTypes":[
|
||||||
@ -133,14 +271,113 @@
|
|||||||
"cached": false
|
"cached": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "36f18720-69ca-4021-9328-262d87397f1b",
|
"id": "7855d176-c6b8-439e-9f12-a71f01c1c7aa",
|
||||||
"name": "power",
|
"name": "totalCurrent",
|
||||||
"displayName": "Power",
|
"displayName": "Total AC current",
|
||||||
"displayNameEvent": "Power changed",
|
"displayNameEvent": "Total AC current changed",
|
||||||
"displayNameAction": "Change power",
|
"type": "int",
|
||||||
"type": "bool",
|
"unit": "Ampere",
|
||||||
"defaultValue": 0,
|
"defaultValue": 0
|
||||||
"writable": true
|
},
|
||||||
|
{
|
||||||
|
"id": "da494d99-5de3-4d03-b7dd-33a33db32164",
|
||||||
|
"name": "phaseACurrent",
|
||||||
|
"displayName": "Phase A current",
|
||||||
|
"displayNameEvent": "Phase A current changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Ampere",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "023b6e5c-be3f-4d8c-adfd-e009c7bebffb",
|
||||||
|
"name": "phaseBCurrent",
|
||||||
|
"displayName": "Phase B current",
|
||||||
|
"displayNameEvent": "Phase B current changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Ampere",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2676ad36-3d97-4691-8334-e13934cc58d1",
|
||||||
|
"name": "phaseCCurrent",
|
||||||
|
"displayName": "Phase C current",
|
||||||
|
"displayNameEvent": "Phase C current changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Ampere",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bc54ca1e-1476-40eb-9974-9e3c2f893dd8",
|
||||||
|
"name": "lnACVoltage",
|
||||||
|
"displayName": "Line to Neutral AC Voltage",
|
||||||
|
"displayNameEvent": "Line to Neutral AC Voltage changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Volt",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4bd32d91-877a-4821-9db9-5981de20d21d",
|
||||||
|
"name": "phaseANVoltage",
|
||||||
|
"displayName": "Phase AN voltage",
|
||||||
|
"displayNameEvent": "Phase AN volatage changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Volt",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f090cb78-d7ed-44fd-a5ad-ea9016021c34",
|
||||||
|
"name": "phaseBNVoltage",
|
||||||
|
"displayName": "Phase BN voltage",
|
||||||
|
"displayNameEvent": "Phase BN voltage changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Volt",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "fa5aa6f5-e67d-485a-835f-24e49298856c",
|
||||||
|
"name": "phaseCNVoltage",
|
||||||
|
"displayName": "Phase CN voltage",
|
||||||
|
"displayNameEvent": "Phase CN voltage changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Volt",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "dfcf52f5-6279-4d25-b7c8-a93b92c39a0c",
|
||||||
|
"name": "frequency",
|
||||||
|
"displayName": "Frequency",
|
||||||
|
"displayNameEvent": "Frequency changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Hertz",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c28c642f-46da-44de-ba0d-c4cbfadbf2cd",
|
||||||
|
"name": "totalRealPower",
|
||||||
|
"displayName": "Total real power",
|
||||||
|
"displayNameEvent": "Total real power changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "KiloWatt",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "73ebf57f-1ad2-4d19-bfd9-9e0a514c1243
|
||||||
|
",
|
||||||
|
"name": "energyExported",
|
||||||
|
"displayName": "Total real energy exported",
|
||||||
|
"displayNameEvent": "Total real energy exported changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "KiloWattHour",
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "63fa4721-1b0a-458c-b66c-17c161107f0d",
|
||||||
|
"name": "energyImported",
|
||||||
|
"displayName": "Total real energy imported",
|
||||||
|
"displayNameEvent": "Total real energy imported changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "KiloWattHour",
|
||||||
|
"defaultValue": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -158,10 +395,28 @@
|
|||||||
"type": "QString"
|
"type": "QString"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "67ce05ff-14bf-4d43-94ba-d5f290278514",
|
"id": "2795052c-5d63-423d-b44e-fdf02dfaac37",
|
||||||
|
"name":"manufacturer",
|
||||||
|
"displayName": "Manufacturer",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7528c840-da8c-40ba-ab82-914cfc686a49",
|
||||||
|
"name":"deviceModel",
|
||||||
|
"displayName": "Device model",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ce9cc598-947c-4efc-b04f-21e784c5d2df",
|
||||||
"name":"serialNumber",
|
"name":"serialNumber",
|
||||||
"displayName": "Serial number",
|
"displayName": "Serial number",
|
||||||
"type": "QString"
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stateTypes":[
|
"stateTypes":[
|
||||||
@ -200,10 +455,28 @@
|
|||||||
"type": "QString"
|
"type": "QString"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "ae76db81-7969-440f-85f0-28ed46d772db",
|
"id": "1d8f1ef7-33ad-40ed-99dd-ec54f87c2f17",
|
||||||
|
"name":"manufacturer",
|
||||||
|
"displayName": "Manufacturer",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2a7f8464-6f03-4113-ac1d-d8ebd8705695",
|
||||||
|
"name":"deviceModel",
|
||||||
|
"displayName": "Device model",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2003acdb-38ad-4184-baf8-9ef8e67fc452",
|
||||||
"name":"serialNumber",
|
"name":"serialNumber",
|
||||||
"displayName": "Serial number",
|
"displayName": "Serial number",
|
||||||
"type": "QString"
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stateTypes":[
|
"stateTypes":[
|
||||||
@ -217,14 +490,146 @@
|
|||||||
"cached": false
|
"cached": false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "9c0e0b08-4461-4e54-aaf3-cab9155028da",
|
"id": "3171a6e0-43a7-4de8-8e20-f748e44af7ac",
|
||||||
"name": "power",
|
"name": "batteryCritical",
|
||||||
"displayName": "Power",
|
"displayName": "Battery critical",
|
||||||
"displayNameEvent": "Power changed",
|
"displayNameEvent": "Battery critical changed",
|
||||||
"displayNameAction": "Change power",
|
|
||||||
"type": "bool",
|
"type": "bool",
|
||||||
"defaultValue": 0,
|
"defaultValue": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "0bf53f80-97f8-488b-b514-58f9fe08c183",
|
||||||
|
"name": "batteryLevel",
|
||||||
|
"displayName": "Battery level",
|
||||||
|
"displayNameEvent": "Battery level changed",
|
||||||
|
"type": "int",
|
||||||
|
"unit": "Percentage",
|
||||||
|
"minValue": 0,
|
||||||
|
"maxValue": 100,
|
||||||
|
"defaultValue": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "da2b19c5-0f48-49d1-93f0-abdc0051407d",
|
||||||
|
"name": "storageState",
|
||||||
|
"displayName": "State",
|
||||||
|
"displayNameEvent": "State changed",
|
||||||
|
"type": "QString",
|
||||||
|
"possibleValues": [
|
||||||
|
"Off",
|
||||||
|
"Empty",
|
||||||
|
"Discharging",
|
||||||
|
"Charging",
|
||||||
|
"Full",
|
||||||
|
"Holding",
|
||||||
|
"Testing"
|
||||||
|
],
|
||||||
|
"defaultValue": "Off"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "221a2ef6-0a92-4ff0-87fe-7bd920dbec0b",
|
||||||
|
"name": "gridCharging",
|
||||||
|
"displayName": "Grid charging",
|
||||||
|
"displayNameEvent": "Grid charging changed",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"writable": true,
|
||||||
|
"displayNameAction": "Set grid charging"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "1f530f79-c0d2-466b-90e1-79149e34d92f",
|
||||||
|
"name": "enableChargingLimit",
|
||||||
|
"displayName": "Charging limit",
|
||||||
|
"displayNameEvent": "Charging limit changed",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"writable": true,
|
||||||
|
"displayNameAction": "Enable Charging Limit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7f469bbc-64a5-4045-8d5f-9a1a85039851",
|
||||||
|
"name": "chargingRate",
|
||||||
|
"displayName": "Charging rate",
|
||||||
|
"displayNameEvent": "Charging rate changed",
|
||||||
|
"type": "int",
|
||||||
|
"minValue": -100,
|
||||||
|
"maxValue": 100,
|
||||||
|
"unit": "Percentage",
|
||||||
|
"defaultValue": false,
|
||||||
|
"writable": true,
|
||||||
|
"displayNameAction": "Set charging rate"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bc99a159-815a-40ab-a6e8-b46f315305f7",
|
||||||
|
"name": "enableDischargingLimit",
|
||||||
|
"displayName": "Discharging limit",
|
||||||
|
"displayNameEvent": "Discharging limit changed",
|
||||||
|
"displayNameAction": "Enable Discharging Limit",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
"writable": true
|
"writable": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6068f030-acce-44a2-b95f-bd00dd5ca760",
|
||||||
|
"name": "dischargingRate",
|
||||||
|
"displayName": "Discharging rate",
|
||||||
|
"displayNameEvent": "Discharging rate changed",
|
||||||
|
"type": "int",
|
||||||
|
"minValue": -100,
|
||||||
|
"maxValue": 100,
|
||||||
|
"unit": "Percentage",
|
||||||
|
"defaultValue": false,
|
||||||
|
"writable": true,
|
||||||
|
"displayNameAction": "Set discharging rate"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sunspecStringCombiner",
|
||||||
|
"displayName": "SunSpec String Combiner",
|
||||||
|
"id": "7787f238-f5a3-4ba5-b3ec-7d513b87bf71",
|
||||||
|
"createMethods": [ "User" ],
|
||||||
|
"interfaces": ["connectable"],
|
||||||
|
"paramTypes": [
|
||||||
|
{
|
||||||
|
"id": "226697c3-37bd-49c7-8c0e-40537573c18e",
|
||||||
|
"name":"ipAddress",
|
||||||
|
"displayName": "IP address",
|
||||||
|
"type": "QString"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3f126e65-05e2-46a3-aeeb-bfacc0bdd9b0",
|
||||||
|
"name":"manufacturer",
|
||||||
|
"displayName": "Manufacturer",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "40666030-3609-467f-b26c-3a42d1c6dca1",
|
||||||
|
"name":"deviceModel",
|
||||||
|
"displayName": "Device model",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "f5046138-9271-4a91-a3eb-c2ecfa755ef5",
|
||||||
|
"name":"serialNumber",
|
||||||
|
"displayName": "Serial number",
|
||||||
|
"type": "QString",
|
||||||
|
"readOnly": true,
|
||||||
|
"defaultValue": "Unkown"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateTypes":[
|
||||||
|
{
|
||||||
|
"id": "3f791612-50fd-4a3c-ac19-27e01dd8c63a",
|
||||||
|
"name": "connected",
|
||||||
|
"displayName": "Connected",
|
||||||
|
"displayNameEvent": "Connected changed",
|
||||||
|
"type": "bool",
|
||||||
|
"defaultValue": false,
|
||||||
|
"cached": false
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,413 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#include "sunspec.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
#include <QtEndian>
|
||||||
|
|
||||||
|
SunSpec::SunSpec(const QHostAddress &hostAddress, uint port, QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_hostAddress(hostAddress),
|
||||||
|
m_port(port)
|
||||||
|
{
|
||||||
|
m_modbusTcpClient = new QModbusTcpClient(this);
|
||||||
|
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_port);
|
||||||
|
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_hostAddress.toString());
|
||||||
|
m_modbusTcpClient->setTimeout(2000);
|
||||||
|
m_modbusTcpClient->setNumberOfRetries(3);
|
||||||
|
|
||||||
|
connect(m_modbusTcpClient, &QModbusTcpClient::stateChanged, this, &SunSpec::onModbusStateChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
SunSpec::~SunSpec()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SunSpec::connectModbus()
|
||||||
|
{
|
||||||
|
return m_modbusTcpClient->connectDevice();;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpec::findBaseRegister()
|
||||||
|
{
|
||||||
|
qCDebug(dcSunSpec()) << "find base register";
|
||||||
|
|
||||||
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, 40000, 2);
|
||||||
|
|
||||||
|
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) {
|
||||||
|
if (!reply->isFinished()) {
|
||||||
|
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||||
|
connect(reply, &QModbusReply::finished, this, [reply, this] {
|
||||||
|
|
||||||
|
if (reply->error() == QModbusDevice::NoError) {
|
||||||
|
const QModbusDataUnit unit = reply->result();
|
||||||
|
//uint modbusAddress = unit.startAddress(); TODO
|
||||||
|
if ((unit.value(0) << 16 | unit.value(1)) == 0x53756e53) {
|
||||||
|
//Well-known value. Uniquely identifies this as a SunSpec Modbus Map
|
||||||
|
qCDebug(dcSunSpec()) << "Found start of modbus map";
|
||||||
|
//emit foundBaseRegister(modbusAddress);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read response error:" << reply->error();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error) {
|
||||||
|
qCWarning(dcSunSpec()) << "Modbus reply error:" << error;
|
||||||
|
reply->finished(); // To make sure it will be deleted
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||||
|
delete reply; // broadcast replies return immediately
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpec::findModbusMap(const QList<BlockId> &ids, uint modbusAddressOffset)
|
||||||
|
{
|
||||||
|
qCDebug(dcSunSpec()) << "Find modbus map. Start register" << m_baseRegister+modbusAddressOffset;
|
||||||
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_baseRegister+modbusAddressOffset, 2);
|
||||||
|
|
||||||
|
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) {
|
||||||
|
if (!reply->isFinished()) {
|
||||||
|
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||||
|
connect(reply, &QModbusReply::finished, this, [ids, reply, this] {
|
||||||
|
|
||||||
|
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 > 800 || blockId == BlockId::End) {
|
||||||
|
qCDebug(dcSunSpec()) << "Block id not found, Id:" << ids;
|
||||||
|
modbusMapSearchFinished(ids, 0, "Ids not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (ids.contains(blockId)) {
|
||||||
|
qCDebug(dcSunSpec()) << "Found block" << BlockId(blockId);
|
||||||
|
foundModbusMap(BlockId(blockId), modbusAddress);
|
||||||
|
} else {
|
||||||
|
//read next block header
|
||||||
|
qCDebug(dcSunSpec()) << "Found Block" << blockId << "with block length" << blockLength;
|
||||||
|
findModbusMap(ids, modbusAddress+2+blockLength-m_baseRegister); //read next block
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read response error:" << reply->error();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error) {
|
||||||
|
qCWarning(dcSunSpec()) << "Modbus replay error:" << error;
|
||||||
|
reply->finished(); // To make sure it will be deleted
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
delete reply; // broadcast replies return immediately
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpec::readMapHeader(uint modbusAddress)
|
||||||
|
{
|
||||||
|
qCDebug(dcSunSpec()) << "Read block header. Modbus Address:" << modbusAddress << "Slave ID" << m_slaveId;
|
||||||
|
if (modbusAddress == 40000)
|
||||||
|
modbusAddress += 2;
|
||||||
|
|
||||||
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, modbusAddress, 2);
|
||||||
|
|
||||||
|
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) {
|
||||||
|
if (!reply->isFinished()) {
|
||||||
|
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||||
|
connect(reply, &QModbusReply::finished, this, [reply, this] {
|
||||||
|
|
||||||
|
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);
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read response error:" << reply->error();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error) {
|
||||||
|
qCWarning(dcSunSpec()) << "Modbus reply error:" << error;
|
||||||
|
reply->finished(); // To make sure it will be deleted
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||||
|
delete reply; // broadcast replies return immediately
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpec::readMap(uint modbusAddress, uint modelLength)
|
||||||
|
{
|
||||||
|
qCDebug(dcSunSpec()) << "Read map. Modbus Address" << modbusAddress << ", Slave ID" << m_slaveId;
|
||||||
|
|
||||||
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, modbusAddress, modelLength+2);
|
||||||
|
|
||||||
|
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) {
|
||||||
|
if (!reply->isFinished()) {
|
||||||
|
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||||
|
connect(reply, &QModbusReply::finished, this, [reply, this] {
|
||||||
|
|
||||||
|
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));
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read response error:" << reply->error();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error) {
|
||||||
|
qCWarning(dcSunSpec()) << "Modbus reply error:" << error;
|
||||||
|
reply->finished(); // To make sure it will be deleted
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||||
|
delete reply; // broadcast replies return immediately
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpec::readCommonMap()
|
||||||
|
{
|
||||||
|
qCDebug(dcSunSpec()) << "Read common block";
|
||||||
|
|
||||||
|
// Base and Alternate Base Register Addresses
|
||||||
|
// DeviceModbus maps begin at one of three well-known Modbus base addresses.
|
||||||
|
// Preferred Base Register: 40000
|
||||||
|
// Alternate Base Register: 50000
|
||||||
|
// Alternate Base Register: 00000
|
||||||
|
|
||||||
|
// Common Model model-length is 66
|
||||||
|
// First two registers are the SunSpec Modbus Map identifier
|
||||||
|
|
||||||
|
qCDebug(dcSunSpec()) << "Read common block header. Modbus Address" << m_baseRegister+2 << ", Slave ID" << m_slaveId;
|
||||||
|
|
||||||
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, m_baseRegister+2, 66);
|
||||||
|
|
||||||
|
if (QModbusReply *reply = m_modbusTcpClient->sendReadRequest(request, m_slaveId)) {
|
||||||
|
if (!reply->isFinished()) {
|
||||||
|
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||||
|
connect(reply, &QModbusReply::finished, this, [reply, this] {
|
||||||
|
|
||||||
|
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);
|
||||||
|
m_deviceModel.remove('\x00');
|
||||||
|
m_serialNumber = convertModbusRegisters(unit.values(), MandatoryRegistersModel1::SerialNumber, 16);
|
||||||
|
m_serialNumber.remove('\x00');
|
||||||
|
qCDebug(dcSunSpec()) << "Received common block response. Manufacturer" << m_manufacturer << "Model" << m_deviceModel << "Serial number" << m_serialNumber;
|
||||||
|
mapHeaderReceived(modbusAddress, mapId, mapLength);
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read response error:" << reply->error();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error) {
|
||||||
|
qCWarning(dcSunSpec()) << "Modbus reply error:" << error;
|
||||||
|
reply->finished(); // To make sure it will be deleted
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||||
|
delete reply; // broadcast replies return immediately
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(dcSunSpec()) << "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];
|
||||||
|
data[0] = modbusData >> 8;
|
||||||
|
data[1] = modbusData & 0xFF;
|
||||||
|
//qCDebug(dcSunSpec()) << (char)data[0] << (char)data[1];
|
||||||
|
return QByteArray().append((char)data[0]).append((char)data[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
QBitArray SunSpec::convertModbusRegisterBits(const uint16_t &modbusData)
|
||||||
|
{
|
||||||
|
QByteArray data = convertModbusRegister(modbusData);
|
||||||
|
QBitArray bits(data.count() * 8);
|
||||||
|
|
||||||
|
// Convert from QByteArray to QBitArray
|
||||||
|
for(int i = 0; i < data.count(); ++i) {
|
||||||
|
for(int b = 0; b < 8; b++) {
|
||||||
|
bits.setBit(i * 8 + b, data.at(i) & (1 << ( 7 - b)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray SunSpec::convertModbusRegisters(const QVector<quint16> &modbusData, int offset, int size)
|
||||||
|
{
|
||||||
|
QByteArray bytes;
|
||||||
|
for (int i = offset; i < offset + size; i++)
|
||||||
|
bytes.append(convertModbusRegister(modbusData[i]));
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
float SunSpec::convertValueWithSSF(quint16 rawValue, quint16 sunssf)
|
||||||
|
{
|
||||||
|
float value;
|
||||||
|
value = rawValue * (10^static_cast<qint16>(sunssf));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
float SunSpec::convertFloatValues(quint16 rawValue0, quint16 rawValue1)
|
||||||
|
{
|
||||||
|
float value;
|
||||||
|
uint32_t i = qFromLittleEndian(((uint32_t)rawValue0 << 16) + rawValue1);
|
||||||
|
memcpy(&value, &i, sizeof(float));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpec::onModbusStateChanged(QModbusDevice::State state)
|
||||||
|
{
|
||||||
|
bool connected = (state != QModbusDevice::UnconnectedState);
|
||||||
|
if (!connected) {
|
||||||
|
//try to reconnect in 10 seconds
|
||||||
|
QTimer::singleShot(10000, m_modbusTcpClient, [this] {
|
||||||
|
if (m_modbusTcpClient->connectDevice()) {
|
||||||
|
qCDebug(dcSunSpec()) << "Could not reconnect";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
readCommonMap();
|
||||||
|
}
|
||||||
|
emit connectionStateChanged(connected);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid SunSpec::writeHoldingRegister(uint slaveAddress, uint registerAddress, quint16 value)
|
||||||
|
{
|
||||||
|
return writeHoldingRegisters(slaveAddress, registerAddress, QVector<quint16>() << value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid SunSpec::writeHoldingRegisters(uint slaveAddress, uint registerAddress, const QVector<quint16> &values)
|
||||||
|
{
|
||||||
|
if (!m_modbusTcpClient) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
QUuid requestId = QUuid::createUuid();
|
||||||
|
QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, values.length());
|
||||||
|
request.setValues(values);
|
||||||
|
|
||||||
|
if (QModbusReply *reply = m_modbusTcpClient->sendWriteRequest(request, slaveAddress)) {
|
||||||
|
if (!reply->isFinished()) {
|
||||||
|
connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);
|
||||||
|
connect(reply, &QModbusReply::finished, this, [reply, requestId, this] {
|
||||||
|
|
||||||
|
if (reply->error() != QModbusDevice::NoError) {
|
||||||
|
qCWarning(dcSunSpec()) << "Read response error:" << reply->error();
|
||||||
|
}
|
||||||
|
reply->deleteLater();
|
||||||
|
});
|
||||||
|
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();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return requestId;
|
||||||
|
}
|
||||||
@ -0,0 +1,223 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#ifndef SUNSPEC_H
|
||||||
|
#define SUNSPEC_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QUuid>
|
||||||
|
#include <QHostAddress>
|
||||||
|
#include <QtSerialBus>
|
||||||
|
|
||||||
|
class SunSpec : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum MandatoryRegistersModel1 {
|
||||||
|
Manufacturer = 2,
|
||||||
|
Model = 18,
|
||||||
|
SerialNumber = 50
|
||||||
|
};
|
||||||
|
|
||||||
|
enum OptionalRegistersModel1 {
|
||||||
|
Options = 34,
|
||||||
|
Version = 40,
|
||||||
|
DeviceAddress = 64,
|
||||||
|
ForceAlignment = 65
|
||||||
|
};
|
||||||
|
|
||||||
|
enum SunSpecOperatingState {
|
||||||
|
Off = 1,
|
||||||
|
Sleeping,
|
||||||
|
Starting,
|
||||||
|
MPPT,
|
||||||
|
Throttled,
|
||||||
|
ShuttingDown,
|
||||||
|
Fault,
|
||||||
|
Standby
|
||||||
|
};
|
||||||
|
Q_ENUM(SunSpecOperatingState)
|
||||||
|
|
||||||
|
enum SunSpecEvent1 {
|
||||||
|
GroundFault = 0,
|
||||||
|
DcOverVolatage,
|
||||||
|
AC_DISCONNECT,
|
||||||
|
DC_DISCONNECT,
|
||||||
|
GRID_DISCONNECT,
|
||||||
|
CABINET_OPEN,
|
||||||
|
MANUAL_SHUTDOWN,
|
||||||
|
OVER_TEMP,
|
||||||
|
OVER_FREQUENCY,
|
||||||
|
UNDER_FREQUENCY,
|
||||||
|
AC_OVER_VOLT,
|
||||||
|
AC_UNDER_VOLT,
|
||||||
|
BLOWN_STRING_FUSE,
|
||||||
|
UNDER_TEMP,
|
||||||
|
MEMORY_LOSS,
|
||||||
|
HW_TEST_FAILURE
|
||||||
|
};
|
||||||
|
Q_ENUM(SunSpecEvent1)
|
||||||
|
|
||||||
|
enum BlockId {
|
||||||
|
Common = 1,
|
||||||
|
BasicAggregator = 2,
|
||||||
|
SecureDatasetReadRequest = 3,
|
||||||
|
SecureDatasetReadResponse = 4,
|
||||||
|
SecureWriteRequest = 5,
|
||||||
|
SecureWriteSequentialRequest = 6,
|
||||||
|
SecureWriteResponseModel = 7,
|
||||||
|
GetDeviceSecurityCertificate = 8,
|
||||||
|
SetOperatorSecurityCertificate = 9,
|
||||||
|
CommunicationInterfaceHeader = 10,
|
||||||
|
EthernetLinkLayer = 11,
|
||||||
|
IPv4 = 12,
|
||||||
|
IPv6 = 13,
|
||||||
|
ProxyServer = 14,
|
||||||
|
InterfaceCountersModel = 15,
|
||||||
|
SimpleIpNetwork = 16,
|
||||||
|
SerialInterface = 17,
|
||||||
|
CellularLink = 18,
|
||||||
|
PPPLink = 19,
|
||||||
|
InverterSinglePhase = 101,
|
||||||
|
InverterSplitPhase = 102,
|
||||||
|
InverterThreePhase = 103,
|
||||||
|
InverterSinglePhaseFloat = 111,
|
||||||
|
InverterSplitPhaseFloat = 112,
|
||||||
|
InverterThreePhaseFloat = 113,
|
||||||
|
Nameplate = 120,
|
||||||
|
BasicSettings = 121,
|
||||||
|
MeasurementsStatus = 122,
|
||||||
|
ImmediateControls = 123,
|
||||||
|
Storage = 124,
|
||||||
|
Pricing = 125,
|
||||||
|
StaticVoltVAR = 126,
|
||||||
|
FreqWattParam = 127,
|
||||||
|
DynamicReactiveCurrent = 128,
|
||||||
|
LVRTD = 129,
|
||||||
|
HVRTD = 130,
|
||||||
|
WattPF = 131,
|
||||||
|
VoltWatt = 132,
|
||||||
|
BasicScheduling = 133,
|
||||||
|
FreqWattCrv = 134,
|
||||||
|
LFRT = 135,
|
||||||
|
HFRT = 136,
|
||||||
|
LVRTC = 137,
|
||||||
|
HVRTC = 138,
|
||||||
|
MultipleMPPTInverterExtensionModel = 160,
|
||||||
|
SinglePhaseMeter = 201,
|
||||||
|
SplitSinglePhaseMeter = 202,
|
||||||
|
WyeConnectThreePhaseMeter = 203,
|
||||||
|
DeltaConnectThreePhaseMeter = 204,
|
||||||
|
SinglePhaseMeterFloat = 211,
|
||||||
|
SplitSinglePhaseMeterFloat = 212,
|
||||||
|
WyeConnectThreePhaseMeterFloat = 213,
|
||||||
|
DeltaConnectThreePhaseMeterFloat = 214,
|
||||||
|
SecureACMeterSelectedReadings = 220,
|
||||||
|
IrradianceModel = 302,
|
||||||
|
BackOfModuleTemperatureModel = 303,
|
||||||
|
InclinometerModel = 304,
|
||||||
|
GPS = 305,
|
||||||
|
ReferencePointModel = 306,
|
||||||
|
BaseMet = 307,
|
||||||
|
MiniMetModel = 308,
|
||||||
|
StringCombiner = 401,
|
||||||
|
StringCombinerAdvanced = 402,
|
||||||
|
StringCombinerCurrent = 403,
|
||||||
|
StringCombinerCurrentAdvanced = 404,
|
||||||
|
SolarModuleFloat = 501,
|
||||||
|
SolarModule = 502,
|
||||||
|
TrackerController = 601,
|
||||||
|
EnergyStorageBaseModel = 801,
|
||||||
|
BatteryBaseModel = 802,
|
||||||
|
LithiumIonBatteryModel = 803,
|
||||||
|
VerisStatusConfiguration = 64001,
|
||||||
|
MersenGreenString = 64020,
|
||||||
|
EltekInverterExtension = 64101,
|
||||||
|
OutBackAXSDevice = 64110,
|
||||||
|
BasicChargeController = 64111,
|
||||||
|
OutBackFMChargeController = 64112,
|
||||||
|
End = 65535
|
||||||
|
};
|
||||||
|
Q_ENUM(BlockId)
|
||||||
|
|
||||||
|
explicit SunSpec(const QHostAddress &hostAddress, uint port = 502, QObject *parent = 0);
|
||||||
|
~SunSpec();
|
||||||
|
bool connectModbus();
|
||||||
|
QString manufacturer();
|
||||||
|
QString deviceModel();
|
||||||
|
QString serialNumber();
|
||||||
|
|
||||||
|
QHostAddress m_hostAddress;
|
||||||
|
uint m_port;
|
||||||
|
QModbusTcpClient *m_modbusTcpClient = nullptr;
|
||||||
|
int m_slaveId = 1;
|
||||||
|
int m_baseRegister = 40000;
|
||||||
|
bool m_floatingPointRepresentation = false;
|
||||||
|
QString m_manufacturer = "unkown";
|
||||||
|
QString m_deviceModel = "unknown";
|
||||||
|
QString m_serialNumber = "unknown";
|
||||||
|
|
||||||
|
void findBaseRegister();
|
||||||
|
void findModbusMap(const QList<BlockId> &mapIds, uint modbusAddressOffset = 69);
|
||||||
|
|
||||||
|
void readMapHeader(uint modbusAddress);
|
||||||
|
void readMap(uint modbusAddress, uint modelLength); //modbusAddress = model start address, model length is without header
|
||||||
|
void readCommonMap();
|
||||||
|
|
||||||
|
float convertValueWithSSF(quint16 rawValue, quint16 sunssf);
|
||||||
|
float convertFloatValues(quint16 rawValue0, quint16 rawValue1);
|
||||||
|
QByteArray convertModbusRegister(const uint16_t &modbusData);
|
||||||
|
QBitArray convertModbusRegisterBits(const uint16_t &modbusData);
|
||||||
|
QByteArray convertModbusRegisters(const QVector<quint16> &modbusData, int offset, int size);
|
||||||
|
|
||||||
|
QUuid writeHoldingRegister(uint slaveAddress, uint registerAddress, quint16 value);
|
||||||
|
QUuid writeHoldingRegisters(uint slaveAddress, uint registerAddress, const QVector<quint16> &values);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void connectionStateChanged(bool status);
|
||||||
|
void requestExecuted(QUuid requetId, bool success);
|
||||||
|
|
||||||
|
void foundModbusMap(BlockId mapId, int modbusStartRegister);
|
||||||
|
void modbusMapSearchFinished(const QList<BlockId> &mapIds, uint modbusStartRegister, const QString &error);
|
||||||
|
|
||||||
|
void mapHeaderReceived(uint modbusAddress, BlockId mapId, uint mapLength);
|
||||||
|
void mapReceived(BlockId mapId, uint mapLength, QVector<quint16> data);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onModbusStateChanged(QModbusDevice::State state);
|
||||||
|
|
||||||
|
void onRequestExecuted(QUuid requestId, bool success);
|
||||||
|
void onRequestError(QUuid requestId, const QString &error);
|
||||||
|
void onReceivedHoldingRegister(quint32 slaveAddress, quint32 modbusRegister, const QVector<quint16> &values);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SUNSPEC_H
|
||||||
@ -7,9 +7,17 @@ QT += \
|
|||||||
SOURCES += \
|
SOURCES += \
|
||||||
integrationpluginsunspec.cpp \
|
integrationpluginsunspec.cpp \
|
||||||
sunspec.cpp \
|
sunspec.cpp \
|
||||||
../modbus/modbustcpmaster.cpp \
|
sunspecinverter.cpp \
|
||||||
|
sunspecmeter.cpp \
|
||||||
|
sunspecstorage.cpp \
|
||||||
|
sunspectracker.cpp \
|
||||||
|
sunspecstringcombiner.cpp \
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
integrationpluginsunspec.h \
|
integrationpluginsunspec.h \
|
||||||
sunspec.h \
|
sunspec.h \
|
||||||
../modbus/modbustcpmaster.h \
|
sunspecinverter.h \
|
||||||
|
sunspecmeter.h \
|
||||||
|
sunspecstorage.h \
|
||||||
|
sunspectracker.h \
|
||||||
|
sunspecstringcombiner.h \
|
||||||
|
|||||||
129
sunspec/sunspecinverter.cpp
Normal file
129
sunspec/sunspecinverter.cpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#include "sunspecinverter.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
SunSpecInverter::SunSpecInverter(const QHostAddress &hostAddress, uint port, QObject *parent) : SunSpec(hostAddress, port, parent)
|
||||||
|
{
|
||||||
|
connect(m_modbusTcpClient, &QModbusClient::stateChanged, this, [this] (QModbusDevice::State state) {
|
||||||
|
if (state == QModbusDevice::ConnectedState) {
|
||||||
|
qCDebug(dcSunSpec()) << "Inverter connected successfully";
|
||||||
|
QList<BlockId> mapIds;
|
||||||
|
mapIds.append(BlockId::InverterSinglePhase);
|
||||||
|
mapIds.append(BlockId::InverterSplitPhase);
|
||||||
|
mapIds.append(BlockId::InverterThreePhase);
|
||||||
|
mapIds.append(BlockId::InverterSinglePhaseFloat);
|
||||||
|
mapIds.append(BlockId::InverterSplitPhaseFloat);
|
||||||
|
mapIds.append(BlockId::InverterThreePhaseFloat);
|
||||||
|
findModbusMap(mapIds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(this, &SunSpec::foundModbusMap, this, [this] (BlockId mapId, uint modbusRegisterAddress) {
|
||||||
|
qCDebug(dcSunSpec()) << "Read map header for mapId" << mapId << "and modbus register" << modbusRegisterAddress;
|
||||||
|
readMapHeader(modbusRegisterAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, BlockId mapId, uint mapLength) {
|
||||||
|
m_id = mapId;
|
||||||
|
m_mapLength = mapLength;
|
||||||
|
m_mapModbusStartRegister = modbusAddress;
|
||||||
|
readMap(modbusAddress, mapLength);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &SunSpec::mapReceived, this, &SunSpecInverter::onModbusMapReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecInverter::getInverterMap()
|
||||||
|
{
|
||||||
|
readMap(m_mapModbusStartRegister, m_mapLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecInverter::readInverterBlockHeader()
|
||||||
|
{
|
||||||
|
readMapHeader(m_mapModbusStartRegister);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecInverter::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector<quint16> data)
|
||||||
|
{
|
||||||
|
Q_UNUSED(mapLength)
|
||||||
|
switch (mapId) {
|
||||||
|
case BlockId::InverterSinglePhase:
|
||||||
|
case BlockId::InverterSplitPhase:
|
||||||
|
case BlockId::InverterThreePhase: {
|
||||||
|
InverterData inverterData;
|
||||||
|
inverterData.acCurrent= convertValueWithSSF(data[Model10X::Model10XAcCurrent], data[Model10X::Model10XAmpereScaleFactor]);
|
||||||
|
inverterData.acPower = convertValueWithSSF(data[Model10X::Model10XACPower], data[Model10X::Model10XWattScaleFactor]);
|
||||||
|
inverterData.lineFrequency = convertValueWithSSF(data[Model10X::Model10XLineFrequency], data[Model10X::Model10XHerzScaleFactor]);
|
||||||
|
|
||||||
|
inverterData.phaseACurrent = convertValueWithSSF(data[Model10X::Model10XPhaseACurrent], data[Model10X::Model10XAmpereScaleFactor]);
|
||||||
|
inverterData.phaseBCurrent = convertValueWithSSF(data[Model10X::Model10XPhaseBCurrent], data[Model10X::Model10XAmpereScaleFactor]);
|
||||||
|
inverterData.phaseCCurrent = convertValueWithSSF(data[Model10X::Model10XPhaseCCurrent], data[Model10X::Model10XAmpereScaleFactor]);
|
||||||
|
|
||||||
|
inverterData.phaseVoltageAN = convertValueWithSSF(data[Model10X::Model10XPhaseVoltageAN], data[Model10X::Model10XVoltageScaleFactor]);
|
||||||
|
inverterData.phaseVoltageBN = convertValueWithSSF(data[Model10X::Model10XPhaseVoltageBN], data[Model10X::Model10XVoltageScaleFactor]);
|
||||||
|
inverterData.phaseVoltageCN = convertValueWithSSF(data[Model10X::Model10XPhaseVoltageCN], data[Model10X::Model10XVoltageScaleFactor]);
|
||||||
|
|
||||||
|
inverterData.acEnergy = convertValueWithSSF(data[Model10X::Model10XAcEnergy], data[Model10X::Model10XWattHoursScaleFactor]);
|
||||||
|
|
||||||
|
inverterData.cabinetTemperature = convertValueWithSSF(data[Model10X::Model10XCabinetTemperature], data[Model10X::Model10XTemperatureScaleFactor]);
|
||||||
|
inverterData.event = SunSpecEvent1(data[Model10X::Model10XEvent1]);
|
||||||
|
inverterData.operatingState = SunSpecOperatingState(data[Model10X::Model10XOperatingState]);
|
||||||
|
emit inverterDataReceived(inverterData);
|
||||||
|
|
||||||
|
} break;
|
||||||
|
case BlockId::InverterSinglePhaseFloat:
|
||||||
|
case BlockId::InverterSplitPhaseFloat:
|
||||||
|
case BlockId::InverterThreePhaseFloat: {
|
||||||
|
InverterData inverterData;
|
||||||
|
inverterData.acCurrent = convertFloatValues(data[Model11X::Model11XAcCurrent], data[Model11X::Model11XAcCurrent+1]);
|
||||||
|
|
||||||
|
inverterData.phaseACurrent = convertFloatValues(data[Model11X::Model11XPhaseACurrent], data[Model11X::Model11XPhaseACurrent+1]);
|
||||||
|
inverterData.phaseBCurrent = convertFloatValues(data[Model11X::Model11XPhaseBCurrent], data[Model11X::Model11XPhaseBCurrent+1]);
|
||||||
|
inverterData.phaseCCurrent = convertFloatValues(data[Model11X::Model11XPhaseCCurrent], data[Model11X::Model11XPhaseCCurrent+1]);
|
||||||
|
|
||||||
|
inverterData.phaseVoltageAN = convertFloatValues(data[Model11X::Model11XPhaseVoltageAN], data[Model11X::Model11XPhaseVoltageAN+1]);
|
||||||
|
inverterData.phaseVoltageBN = convertFloatValues(data[Model11X::Model11XPhaseVoltageBN], data[Model11X::Model11XPhaseVoltageBN+1]);
|
||||||
|
inverterData.phaseVoltageCN = convertFloatValues(data[Model11X::Model11XPhaseVoltageCN], data[Model11X::Model11XPhaseVoltageCN+1]);
|
||||||
|
|
||||||
|
inverterData.acPower = convertFloatValues(data[Model11X::Model11XACPower], data[Model11X::Model11XACPower+1]);
|
||||||
|
inverterData.lineFrequency = convertFloatValues(data[Model11X::Model11XLineFrequency], data[Model11X::Model11XLineFrequency+1]);
|
||||||
|
|
||||||
|
inverterData.acEnergy = convertFloatValues(data[Model11X::Model11XAcEnergy], data[Model11X::Model11XAcEnergy+1]);
|
||||||
|
inverterData.cabinetTemperature = convertFloatValues(data[Model11X::Model11XCabinetTemperature], data[Model11X::Model11XCabinetTemperature+1]);
|
||||||
|
inverterData.event = SunSpecEvent1(data[Model11X::Model11XEvent1]);
|
||||||
|
inverterData.operatingState = SunSpecOperatingState(data[Model11X::Model11XOperatingState]);
|
||||||
|
emit inverterDataReceived(inverterData);
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
//ignore
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
118
sunspec/sunspecinverter.h
Normal file
118
sunspec/sunspecinverter.h
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#ifndef SUNSPECINVERTER_H
|
||||||
|
#define SUNSPECINVERTER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "sunspec.h"
|
||||||
|
|
||||||
|
class SunSpecInverter : public SunSpec
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SunSpecInverter(const QHostAddress &hostAddress, uint port = 502, QObject *parent = 0);
|
||||||
|
|
||||||
|
enum Model10X { // Mandatory register
|
||||||
|
Model10XAcCurrent = 2,
|
||||||
|
Model10XPhaseACurrent = 3,
|
||||||
|
Model10XPhaseBCurrent = 4,
|
||||||
|
Model10XPhaseCCurrent = 5,
|
||||||
|
Model10XAmpereScaleFactor = 6,
|
||||||
|
Model10XPhaseVoltageAN = 10,
|
||||||
|
Model10XPhaseVoltageBN = 11,
|
||||||
|
Model10XPhaseVoltageCN = 12,
|
||||||
|
Model10XVoltageScaleFactor = 13,
|
||||||
|
Model10XACPower = 14,
|
||||||
|
Model10XWattScaleFactor = 15,
|
||||||
|
Model10XLineFrequency = 16,
|
||||||
|
Model10XHerzScaleFactor = 17,
|
||||||
|
Model10XAcEnergy = 24,
|
||||||
|
Model10XWattHoursScaleFactor = 25,
|
||||||
|
Model10XCabinetTemperature = 33,
|
||||||
|
Model10XTemperatureScaleFactor = 37,
|
||||||
|
Model10XOperatingState = 38,
|
||||||
|
Model10XEvent1 = 40
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Model11X { // Mandatory register
|
||||||
|
Model11XAcCurrent = 0,
|
||||||
|
Model11XPhaseACurrent = 2,
|
||||||
|
Model11XPhaseBCurrent = 4,
|
||||||
|
Model11XPhaseCCurrent = 6,
|
||||||
|
Model11XPhaseVoltageAN = 14,
|
||||||
|
Model11XPhaseVoltageBN = 16,
|
||||||
|
Model11XPhaseVoltageCN = 18,
|
||||||
|
Model11XACPower = 20,
|
||||||
|
Model11XLineFrequency = 22,
|
||||||
|
Model11XAcEnergy = 30,
|
||||||
|
Model11XCabinetTemperature = 38,
|
||||||
|
Model11XOperatingState = 46,
|
||||||
|
Model11XEvent1 = 48
|
||||||
|
};
|
||||||
|
|
||||||
|
struct InverterData {
|
||||||
|
float acCurrent; //in ampere
|
||||||
|
float phaseACurrent;
|
||||||
|
float phaseBCurrent;
|
||||||
|
float phaseCCurrent;
|
||||||
|
float phaseVoltageAB;
|
||||||
|
float phaseVoltageBC;
|
||||||
|
float phaseVoltageCA;
|
||||||
|
float phaseVoltageAN;
|
||||||
|
float phaseVoltageBN;
|
||||||
|
float phaseVoltageCN;
|
||||||
|
float acPower;
|
||||||
|
float lineFrequency;
|
||||||
|
float acEnergy;
|
||||||
|
float cabinetTemperature; // in degree Celsius
|
||||||
|
SunSpecEvent1 event;
|
||||||
|
SunSpecOperatingState operatingState;
|
||||||
|
};
|
||||||
|
|
||||||
|
void getInverterMap();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BlockId m_id = BlockId::InverterThreePhase; //e.g. 103 for three phase inverter, 113 for three phase inverter with floating point representation
|
||||||
|
uint m_mapLength = 0;
|
||||||
|
uint m_mapModbusStartRegister = 40000;
|
||||||
|
|
||||||
|
void readInverterBlockHeader();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConnectionStateChanged();
|
||||||
|
void onModbusMapReceived(BlockId mapId, uint mapLength, QVector<quint16> data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void initFinished();
|
||||||
|
void inverterDataReceived(InverterData data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SUNSPECINVERTER_H
|
||||||
61
sunspec/sunspecmeter.cpp
Normal file
61
sunspec/sunspecmeter.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#include "sunspecmeter.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
SunSpecMeter::SunSpecMeter(const QHostAddress &hostAddress, uint port, QObject *parent) : SunSpec(hostAddress, port, parent)
|
||||||
|
{
|
||||||
|
connect(m_modbusTcpClient, &QModbusClient::stateChanged, this, [this] (QModbusDevice::State state) {
|
||||||
|
if (state == QModbusDevice::ConnectedState) {
|
||||||
|
qCDebug(dcSunSpec()) << "Meter connected successfully";
|
||||||
|
QList<BlockId> mapIds;
|
||||||
|
mapIds.append(BlockId::SinglePhaseMeter);
|
||||||
|
mapIds.append(BlockId::SplitSinglePhaseMeter);
|
||||||
|
mapIds.append(BlockId::WyeConnectThreePhaseMeter);
|
||||||
|
mapIds.append(BlockId::DeltaConnectThreePhaseMeter);
|
||||||
|
mapIds.append(BlockId::SinglePhaseMeterFloat);
|
||||||
|
mapIds.append(BlockId::SplitSinglePhaseMeterFloat);
|
||||||
|
mapIds.append(BlockId::WyeConnectThreePhaseMeterFloat);
|
||||||
|
mapIds.append(BlockId::DeltaConnectThreePhaseMeterFloat);
|
||||||
|
findModbusMap(mapIds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecMeter::geMeterMap()
|
||||||
|
{
|
||||||
|
readMap(m_mapModbusStartRegister, m_mapLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecMeter::readMeterBlockHeader()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
91
sunspec/sunspecmeter.h
Normal file
91
sunspec/sunspecmeter.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#ifndef SUNSPECMETER_H
|
||||||
|
#define SUNSPECMETER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "sunspec.h"
|
||||||
|
|
||||||
|
class SunSpecMeter : public SunSpec
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
//Model 203 = Three phase meter
|
||||||
|
enum MandatoryRegistersModel20X {
|
||||||
|
TotalAcCurrent = 2,
|
||||||
|
PhaseACurrent = 3,
|
||||||
|
PhaseBCurrent = 4,
|
||||||
|
PhaseCCurrent = 5,
|
||||||
|
CurrentScaleFactor = 6,
|
||||||
|
VoltageLN = 7,
|
||||||
|
PhaseVoltageAN = 8,
|
||||||
|
PhaseVoltageBN = 9,
|
||||||
|
PhaseVoltageCN = 10,
|
||||||
|
VoltageLL = 11,
|
||||||
|
PhaseVoltageAB = 12,
|
||||||
|
PhaseVoltageBC = 13,
|
||||||
|
PhaseVoltageCA = 14,
|
||||||
|
VoltageScaleFactor = 15,
|
||||||
|
Frequency = 16,
|
||||||
|
TotalRealPower = 18,
|
||||||
|
RealPowerScaleFactor = 22,
|
||||||
|
TotalRealEnergyExported = 38,
|
||||||
|
TotalRealEnergyImported = 46,
|
||||||
|
RealEnergyScaleFactor = 54,
|
||||||
|
MeterEventFlags = 105
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MeterData {
|
||||||
|
SunSpecEvent1 event;
|
||||||
|
SunSpecOperatingState operatingState;
|
||||||
|
};
|
||||||
|
|
||||||
|
SunSpecMeter(const QHostAddress &hostAddress, uint port = 502, QObject *parent = 0);
|
||||||
|
|
||||||
|
void geMeterMap();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BlockId m_id = BlockId::DeltaConnectThreePhaseMeter;
|
||||||
|
uint m_mapLength = 0;
|
||||||
|
uint m_mapModbusStartRegister = 40000;
|
||||||
|
|
||||||
|
void readMeterBlockHeader();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConnectionStateChanged();
|
||||||
|
void onModbusMapReceived(BlockId mapId, uint mapLength, QVector<quint16> data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void initFinished();
|
||||||
|
void meterDataReceived(const MeterData &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SUNSPECMETER_H
|
||||||
123
sunspec/sunspecstorage.cpp
Normal file
123
sunspec/sunspecstorage.cpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#include "sunspecstorage.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
SunSpecStorage::SunSpecStorage(const QHostAddress &hostAddress, uint port, QObject *parent) : SunSpec(hostAddress, port, parent)
|
||||||
|
{
|
||||||
|
connect(m_modbusTcpClient, &QModbusClient::stateChanged, this, [this] (QModbusDevice::State state) {
|
||||||
|
if (state == QModbusDevice::ConnectedState) {
|
||||||
|
qCDebug(dcSunSpec()) << "Inverter connected successfully";
|
||||||
|
QList<BlockId> mapIds;
|
||||||
|
mapIds.append(BlockId::Storage);
|
||||||
|
findModbusMap(mapIds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(this, &SunSpec::foundModbusMap, this, [this] (BlockId mapId, uint modbusRegisterAddress) {
|
||||||
|
qCDebug(dcSunSpec()) << "Read map header for mapId" << mapId << "and modbus register" << modbusRegisterAddress;
|
||||||
|
readMapHeader(modbusRegisterAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, BlockId mapId, uint mapLength) {
|
||||||
|
m_id = mapId;
|
||||||
|
m_mapLength = mapLength;
|
||||||
|
m_mapModbusStartRegister = modbusAddress;
|
||||||
|
readMap(modbusAddress, mapLength);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &SunSpec::mapReceived, this, &SunSpecStorage::onModbusMapReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecStorage::getStorageMap()
|
||||||
|
{
|
||||||
|
readMap(m_mapModbusStartRegister, m_mapLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecStorage::readStorageBlockHeader()
|
||||||
|
{
|
||||||
|
readMapHeader(m_mapModbusStartRegister);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid SunSpecStorage::setGridCharging(bool enabled)
|
||||||
|
{
|
||||||
|
// Name ChaGriSet
|
||||||
|
/* Setpoint to enable/dis-
|
||||||
|
able charging from grid
|
||||||
|
PV (charging from grid 0 disabled)
|
||||||
|
GRID (charging from 1 grid enabled*/
|
||||||
|
|
||||||
|
uint registerAddress = m_mapModbusStartRegister + Model124::Model124ChaGriSet;
|
||||||
|
quint16 value = enabled;
|
||||||
|
return writeHoldingRegister(m_slaveId, registerAddress, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid SunSpecStorage::setStorageControlMode(bool chargingEnabled, bool dischargingEnabled)
|
||||||
|
{
|
||||||
|
// Set charge bit to enable charge limit, set discharge bit to enable discharge limit, set both bits to enable both limits
|
||||||
|
quint16 value = ((static_cast<quint16>(chargingEnabled) << StorageControlBitFieldCharge) |
|
||||||
|
(static_cast<quint16>(dischargingEnabled) << StorageControlBitFieldDischarge)) ;
|
||||||
|
|
||||||
|
uint modbusRegister = m_mapModbusStartRegister + Model124::Model124ActivateStorageControlMode;
|
||||||
|
return writeHoldingRegister(m_slaveId, modbusRegister, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid SunSpecStorage::setChargingRate(int rate)
|
||||||
|
{
|
||||||
|
//Register Name InWRte
|
||||||
|
/* Defines the maximum charge rate (charge limit). Default is 100% */
|
||||||
|
|
||||||
|
uint modbusRegister = m_mapModbusStartRegister + Model124::Model124SetpointMaximumChargingRate;
|
||||||
|
int16_t value = rate * 100;
|
||||||
|
return writeHoldingRegister(m_slaveId, modbusRegister, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QUuid SunSpecStorage::setDischargingRate(int charging)
|
||||||
|
{
|
||||||
|
//Register Name OutWRte
|
||||||
|
/* Defines the maximum discharge rate (discharge limit). Default is 100% */
|
||||||
|
uint modbusRegister = m_mapModbusStartRegister + Model124::Model124SetpointMaximumDischargeRate;
|
||||||
|
quint16 value = charging * 100;
|
||||||
|
return writeHoldingRegister(m_slaveId, modbusRegister, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecStorage::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, const QVector<quint16> &data)
|
||||||
|
{
|
||||||
|
Q_UNUSED(mapLength)
|
||||||
|
switch (mapId) {
|
||||||
|
case BlockId::Storage: {
|
||||||
|
StorageData storageData;
|
||||||
|
storageData.chargingState = ChargingState(data[Model124::Model124ChargeStatus]);
|
||||||
|
emit storageDataReceived(storageData);
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
109
sunspec/sunspecstorage.h
Normal file
109
sunspec/sunspecstorage.h
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#ifndef SUNSPECSTORAGE_H
|
||||||
|
#define SUNSPECSTORAGE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "sunspec.h"
|
||||||
|
|
||||||
|
class SunSpecStorage : public SunSpec
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
SunSpecStorage(const QHostAddress &hostAddress, uint port = 502, QObject *parent = 0);
|
||||||
|
|
||||||
|
QUuid setGridCharging(bool enabled);
|
||||||
|
QUuid setDischargingRate(int rate);
|
||||||
|
QUuid setChargingRate(int rate);
|
||||||
|
QUuid setStorageControlMode(bool chargingEnabled, bool dischargingEnabled);
|
||||||
|
|
||||||
|
|
||||||
|
enum StorageControlBitField {
|
||||||
|
StorageControlBitFieldCharge = 0,
|
||||||
|
StorageControlBitFieldDischarge = 1
|
||||||
|
};
|
||||||
|
Q_ENUM(StorageControlBitField)
|
||||||
|
|
||||||
|
enum GridCharge {
|
||||||
|
PV = 0,
|
||||||
|
Grid = 1
|
||||||
|
};
|
||||||
|
Q_ENUM(GridCharge)
|
||||||
|
|
||||||
|
enum ChargingState {
|
||||||
|
ChargingStateOff,
|
||||||
|
ChargingStateEmpty,
|
||||||
|
ChargingStateDischarging,
|
||||||
|
ChargingStateCharging,
|
||||||
|
ChargingStateFull,
|
||||||
|
ChargingStateHolding,
|
||||||
|
ChargingStateTesting
|
||||||
|
};
|
||||||
|
Q_ENUM(ChargingState)
|
||||||
|
|
||||||
|
enum Model124 { // Mandatory register
|
||||||
|
Model124SetpointMaximumCharge = 0,
|
||||||
|
Model124SetpointMaximumChargingRate = 1,
|
||||||
|
Model124SetpointMaximumDischargeRate = 2,
|
||||||
|
Model124ActivateStorageControlMode = 3,
|
||||||
|
Model124CurrentlyAvailableEnergy = 6,
|
||||||
|
Model124ChargeStatus = 9,
|
||||||
|
Model124ChaGriSet = 15,
|
||||||
|
Model124ScaleFactorMaximumCharge = 16,
|
||||||
|
Model124ScaleFactorMaximumChargeDischargeRate = 17,
|
||||||
|
Model124ScaleFactorAvailableEnergyPercent = 20
|
||||||
|
};
|
||||||
|
Q_ENUM(Model124)
|
||||||
|
|
||||||
|
struct StorageData {
|
||||||
|
ChargingState chargingState;
|
||||||
|
bool gridChargingEnabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
void getStorageMap();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BlockId m_id = BlockId::EnergyStorageBaseModel;
|
||||||
|
uint m_mapLength = 0;
|
||||||
|
uint m_mapModbusStartRegister = 40000;
|
||||||
|
|
||||||
|
void readStorageBlockHeader();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConnectionStateChanged();
|
||||||
|
void onModbusMapReceived(BlockId mapId, uint mapLength, const QVector<quint16> &data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void initFinished();
|
||||||
|
void storageDataReceived(const StorageData &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SUNSPECSTORAGE_H
|
||||||
89
sunspec/sunspecstringcombiner.cpp
Normal file
89
sunspec/sunspecstringcombiner.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#include "sunspecstringcombiner.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
SunSpecStringCombiner::SunSpecStringCombiner(const QHostAddress &hostAddress, uint port, QObject *parent) : SunSpec(hostAddress, port, parent)
|
||||||
|
{
|
||||||
|
connect(m_modbusTcpClient, &QModbusClient::stateChanged, this, [this] (QModbusDevice::State state) {
|
||||||
|
if (state == QModbusDevice::ConnectedState) {
|
||||||
|
qCDebug(dcSunSpec()) << "String combiner connected successfully";
|
||||||
|
QList<BlockId> mapIds;
|
||||||
|
mapIds.append(BlockId::StringCombiner);
|
||||||
|
mapIds.append(BlockId::StringCombinerCurrent);
|
||||||
|
mapIds.append(BlockId::StringCombinerAdvanced);
|
||||||
|
mapIds.append(BlockId::StringCombinerCurrentAdvanced);
|
||||||
|
findModbusMap(mapIds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
connect(this, &SunSpec::foundModbusMap, this, [this] (BlockId mapId, uint modbusRegisterAddress) {
|
||||||
|
qCDebug(dcSunSpec()) << "Read map header for mapId" << mapId << "and modbus register" << modbusRegisterAddress;
|
||||||
|
readMapHeader(modbusRegisterAddress);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &SunSpec::mapHeaderReceived, this, [this] (uint modbusAddress, BlockId mapId, uint mapLength) {
|
||||||
|
m_id = mapId;
|
||||||
|
m_mapLength = mapLength;
|
||||||
|
m_mapModbusStartRegister = modbusAddress;
|
||||||
|
readMap(modbusAddress, mapLength);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(this, &SunSpec::mapReceived, this, &SunSpecStringCombiner::onModbusMapReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecStringCombiner::getStringCombinerMap()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecStringCombiner::readStringCombinerMapHeader()
|
||||||
|
{
|
||||||
|
readMap(m_mapModbusStartRegister, m_mapLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecStringCombiner::onModbusMapReceived(SunSpec::BlockId mapId, uint mapLength, QVector<quint16> data)
|
||||||
|
{
|
||||||
|
|
||||||
|
switch (mapId) {
|
||||||
|
case BlockId::StringCombiner: {
|
||||||
|
int rbCount = (mapLength-14)/8;
|
||||||
|
qCDebug(dcSunSpec()) << "Map" << mapId << "Repeating Block Count" << rbCount;
|
||||||
|
} break;
|
||||||
|
case BlockId::StringCombinerCurrent:
|
||||||
|
case BlockId::StringCombinerAdvanced:
|
||||||
|
case BlockId::StringCombinerCurrentAdvanced: {
|
||||||
|
//StringCombinerData stringCombinerData;
|
||||||
|
//stringCombinerData.acCurrent= convertValueWithSSF(data[Model10X::Model10XAcCurrent], data[Model10X::Model10XAmpereScaleFactor]);
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
129
sunspec/sunspecstringcombiner.h
Normal file
129
sunspec/sunspecstringcombiner.h
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#ifndef SUNSPECSTRINGCOMBINER_H
|
||||||
|
#define SUNSPECSTRINGCOMBINER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "sunspec.h"
|
||||||
|
|
||||||
|
class SunSpecStringCombiner : public SunSpec
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
//Map401 length: 14 + (RB Count * 8)
|
||||||
|
//Map403 length: 16 + (RB Count * 8)
|
||||||
|
enum Map401 {
|
||||||
|
CurrentScaleFactor = 0,
|
||||||
|
AmpHourScaleFactor = 1,
|
||||||
|
VoltageScaleFactor = 2,
|
||||||
|
MaximumDCCurrentRating = 3,
|
||||||
|
NumberOfInputs = 4,
|
||||||
|
Events = 5,
|
||||||
|
VendorDefniedEvents = 7,
|
||||||
|
TotalMeasuredCurrent = 9,
|
||||||
|
TotalMeteredAmpHours = 10,
|
||||||
|
OutputVoltage = 12,
|
||||||
|
InternalOperatingTemperature = 13
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Map402 {
|
||||||
|
CurrentScaleFactor,
|
||||||
|
AmpHourScaleFactor,
|
||||||
|
VoltageScaleFactor
|
||||||
|
PowerScale factor
|
||||||
|
EnergyScale factor
|
||||||
|
Maximum DC Current Rating
|
||||||
|
Number of Inputs
|
||||||
|
Bitmask value. Events
|
||||||
|
Bitmask value. Vendor defnied events
|
||||||
|
Total measured current
|
||||||
|
Total metered Amp-hours
|
||||||
|
OutputVoltage
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Map401RB { //Repeating block
|
||||||
|
ID = 0,
|
||||||
|
Event = 1,
|
||||||
|
VendorEvent = 3,
|
||||||
|
Amps = 5,
|
||||||
|
AmpHours = 6
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StringCombinerEvent {
|
||||||
|
LowVoltage = 0,
|
||||||
|
LowPower,
|
||||||
|
LowEfficiency,
|
||||||
|
Current,
|
||||||
|
Voltage,
|
||||||
|
Power,
|
||||||
|
Pr,
|
||||||
|
Disconnected,
|
||||||
|
FuseFault,
|
||||||
|
CombinerFuseFault,
|
||||||
|
CombinerCabinetOpen,
|
||||||
|
Temp,
|
||||||
|
Groundfault,
|
||||||
|
ReversedPolarity,
|
||||||
|
Incompatible,
|
||||||
|
CommunicationError,
|
||||||
|
InternalError,
|
||||||
|
Theft,
|
||||||
|
ArcDetected
|
||||||
|
};
|
||||||
|
Q_ENUM(StringCombinerEvent)
|
||||||
|
|
||||||
|
struct StringCombinerData {
|
||||||
|
SunSpecEvent1 event;
|
||||||
|
SunSpecOperatingState operatingState;
|
||||||
|
};
|
||||||
|
|
||||||
|
SunSpecStringCombiner(const QHostAddress &hostAddress, uint port = 502, QObject *parent = 0);
|
||||||
|
|
||||||
|
void getStringCombinerMap();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BlockId m_id = BlockId::StringCombiner;
|
||||||
|
uint m_mapLength = 0;
|
||||||
|
uint m_mapModbusStartRegister = 40000;
|
||||||
|
|
||||||
|
void readStringCombinerMapHeader();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConnectionStateChanged();
|
||||||
|
void onModbusMapReceived(BlockId mapId, uint mapLength, QVector<quint16> data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void initFinished();
|
||||||
|
void stringCombinerDataReceived(const StringCombinerData &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SUNSPECSTRINGCOMBINER_H
|
||||||
42
sunspec/sunspectracker.cpp
Normal file
42
sunspec/sunspectracker.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#include "sunspectracker.h"
|
||||||
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
|
SunSpecTracker::SunSpecTracker(const QHostAddress &hostAddress, uint port, QObject *parent) : SunSpec(hostAddress, port, parent)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void SunSpecTracker::readTrackerBlockHeader()
|
||||||
|
{
|
||||||
|
readMap(m_mapModbusStartRegister, m_mapLength);
|
||||||
|
}
|
||||||
78
sunspec/sunspectracker.h
Normal file
78
sunspec/sunspectracker.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2020, nymea GmbH
|
||||||
|
* Contact: contact@nymea.io
|
||||||
|
*
|
||||||
|
* This file is part of nymea.
|
||||||
|
* This project including source code and documentation is protected by
|
||||||
|
* copyright law, and remains the property of nymea GmbH. All rights, including
|
||||||
|
* reproduction, publication, editing and translation, are reserved. The use of
|
||||||
|
* this project is subject to the terms of a license agreement to be concluded
|
||||||
|
* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
|
||||||
|
* under https://nymea.io/license
|
||||||
|
*
|
||||||
|
* GNU Lesser General Public License Usage
|
||||||
|
* Alternatively, this project may be redistributed and/or modified under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free
|
||||||
|
* Software Foundation; version 3. This project is distributed in the hope that
|
||||||
|
* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this project. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* For any further details and any questions please contact us under
|
||||||
|
* contact@nymea.io or see our FAQ/Licensing Information on
|
||||||
|
* https://nymea.io/license/faq
|
||||||
|
*
|
||||||
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
#ifndef SUNSPECTRACKER_H
|
||||||
|
#define SUNSPECTRACKER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include "sunspec.h"
|
||||||
|
|
||||||
|
class SunSpecTracker : public SunSpec
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum TrackerType {
|
||||||
|
Unknown = 0,
|
||||||
|
Fixed = 1,
|
||||||
|
Horizontal = 2,
|
||||||
|
Tilted = 3,
|
||||||
|
Azimuth = 4,
|
||||||
|
Dual = 5,
|
||||||
|
Other = 99
|
||||||
|
};
|
||||||
|
Q_ENUM(TrackerType)
|
||||||
|
|
||||||
|
struct TrackerData {
|
||||||
|
SunSpecEvent1 event;
|
||||||
|
SunSpecOperatingState operatingState;
|
||||||
|
};
|
||||||
|
|
||||||
|
SunSpecTracker(const QHostAddress &hostAddress, uint port = 502, QObject *parent = 0);
|
||||||
|
|
||||||
|
void getTrackerMap();
|
||||||
|
|
||||||
|
private:
|
||||||
|
BlockId m_id = BlockId::TrackerController;
|
||||||
|
uint m_mapLength = 0;
|
||||||
|
uint m_mapModbusStartRegister = 40000;
|
||||||
|
|
||||||
|
void readTrackerBlockHeader();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onConnectionStateChanged();
|
||||||
|
void onModbusMapReceived(BlockId mapId, uint mapLength, QVector<quint16> data);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void initFinished();
|
||||||
|
void trackerDataReceived(const TrackerData &data);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SUNSPECTRACKER_H
|
||||||
Reference in New Issue
Block a user