Implement first meter version
This commit is contained in:
parent
cd9c048ebd
commit
a30d338de9
@ -118,6 +118,7 @@ isEmpty(WITH_PLUGINS) {
|
||||
}
|
||||
PLUGINS-=$${WITHOUT_PLUGINS}
|
||||
|
||||
#FIXME: PLUGINS=sma
|
||||
message("Building plugins:")
|
||||
for(plugin, PLUGINS) {
|
||||
exists($${plugin}) {
|
||||
|
||||
@ -31,13 +31,19 @@
|
||||
#include "integrationpluginsma.h"
|
||||
#include "plugininfo.h"
|
||||
|
||||
#include "network/networkdevicediscovery.h"
|
||||
#include <network/networkdevicediscovery.h>
|
||||
#include "speedwirediscovery.h"
|
||||
|
||||
IntegrationPluginSma::IntegrationPluginSma()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IntegrationPluginSma::init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
||||
{
|
||||
if (info->thingClassId() == sunnyWebBoxThingClassId) {
|
||||
@ -84,18 +90,44 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
||||
info->addThingDescriptors(descriptors);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
} else if (info->thingClassId() == speedwireInverterThingClassId) {
|
||||
SpeedwireInterface *speedwireDiscovery = new SpeedwireInterface(info);
|
||||
} else if (info->thingClassId() == speedwireMeterThingClassId) {
|
||||
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), info);
|
||||
if (!speedwireDiscovery->initialize()) {
|
||||
qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed.";
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network."));
|
||||
return;
|
||||
}
|
||||
|
||||
connect(speedwireDiscovery, &SpeedwireInterface::discoveryFinished, this, [=](){
|
||||
connect(speedwireDiscovery, &SpeedwireDiscovery::discoveryFinished, this, [=](){
|
||||
qCDebug(dcSma()) << "Speed wire discovery finished.";
|
||||
|
||||
speedwireDiscovery->deleteLater();
|
||||
|
||||
ThingDescriptors descriptors;
|
||||
foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) {
|
||||
if (result.deviceType == SpeedwireInterface::DeviceTypeMeter) {
|
||||
if (result.serialNumber == 0)
|
||||
continue;
|
||||
|
||||
ThingDescriptor descriptor(speedwireMeterThingClassId, "SMA Energy Meter", "Serial: " + QString::number(result.serialNumber) + " - " + result.address.toString());
|
||||
// We found an energy meter, let's check if we already added this one
|
||||
foreach (Thing *existingThing, myThings()) {
|
||||
if (existingThing->paramValue(speedwireMeterThingSerialNumberParamTypeId).toUInt() == result.serialNumber) {
|
||||
descriptor.setThingId(existingThing->id());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ParamList params;
|
||||
params << Param(speedwireMeterThingHostParamTypeId, result.address.toString());
|
||||
params << Param(speedwireMeterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
|
||||
params << Param(speedwireMeterThingSerialNumberParamTypeId, result.serialNumber);
|
||||
params << Param(speedwireMeterThingModelIdParamTypeId, result.modelId);
|
||||
descriptor.setParams(params);
|
||||
descriptors.append(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
info->addThingDescriptors(descriptors);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
@ -145,7 +177,49 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
|
||||
connect(m_refreshTimer, &PluginTimer::timeout, this, &IntegrationPluginSma::onRefreshTimer);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
} else if (thing->thingClassId() == speedwireMeterThingClassId) {
|
||||
QHostAddress address = QHostAddress(thing->paramValue(speedwireMeterThingHostParamTypeId).toString());
|
||||
quint32 serialNumber = static_cast<quint32>(thing->paramValue(speedwireMeterThingSerialNumberParamTypeId).toUInt());
|
||||
quint16 modelId = static_cast<quint16>(thing->paramValue(speedwireMeterThingModelIdParamTypeId).toUInt());
|
||||
|
||||
if (m_speedwireMeters.contains(thing)) {
|
||||
m_speedwireMeters.take(thing)->deleteLater();
|
||||
}
|
||||
|
||||
SpeedwireMeter *meter = new SpeedwireMeter(address, modelId, serialNumber, this);
|
||||
if (!meter->initialize()) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: reachable state
|
||||
|
||||
connect(meter, &SpeedwireMeter::valuesUpdated, this, [=](){
|
||||
thing->setStateValue(speedwireMeterConnectedStateTypeId, true);
|
||||
thing->setStateValue(speedwireMeterCurrentPowerStateTypeId, meter->currentPower());
|
||||
thing->setStateValue(speedwireMeterCurrentPowerPhaseAStateTypeId, meter->currentPowerPhaseA());
|
||||
thing->setStateValue(speedwireMeterCurrentPowerPhaseBStateTypeId, meter->currentPowerPhaseB());
|
||||
thing->setStateValue(speedwireMeterCurrentPowerPhaseCStateTypeId, meter->currentPowerPhaseC());
|
||||
thing->setStateValue(speedwireMeterVoltagePhaseAStateTypeId, meter->voltagePhaseA());
|
||||
thing->setStateValue(speedwireMeterVoltagePhaseBStateTypeId, meter->voltagePhaseB());
|
||||
thing->setStateValue(speedwireMeterVoltagePhaseCStateTypeId, meter->voltagePhaseC());
|
||||
thing->setStateValue(speedwireMeterTotalEnergyConsumedStateTypeId, meter->totalEnergyConsumed());
|
||||
thing->setStateValue(speedwireMeterTotalEnergyProducedStateTypeId, meter->totalEnergyProduced());
|
||||
thing->setStateValue(speedwireMeterEnergyConsumedPhaseAStateTypeId, meter->energyConsumedPhaseA());
|
||||
thing->setStateValue(speedwireMeterEnergyConsumedPhaseBStateTypeId, meter->energyConsumedPhaseB());
|
||||
thing->setStateValue(speedwireMeterEnergyConsumedPhaseCStateTypeId, meter->energyConsumedPhaseC());
|
||||
thing->setStateValue(speedwireMeterEnergyProducedPhaseAStateTypeId, meter->energyProducedPhaseA());
|
||||
thing->setStateValue(speedwireMeterEnergyProducedPhaseBStateTypeId, meter->energyProducedPhaseB());
|
||||
thing->setStateValue(speedwireMeterEnergyProducedPhaseCStateTypeId, meter->energyProducedPhaseC());
|
||||
thing->setStateValue(speedwireMeterCurrentPhaseAStateTypeId, meter->amperePhaseA());
|
||||
thing->setStateValue(speedwireMeterCurrentPhaseBStateTypeId, meter->amperePhaseB());
|
||||
thing->setStateValue(speedwireMeterCurrentPhaseCStateTypeId, meter->amperePhaseC());
|
||||
thing->setStateValue(speedwireMeterFirmwareVersionStateTypeId, meter->softwareVersion());
|
||||
});
|
||||
|
||||
m_speedwireMeters.insert(thing, meter);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}else {
|
||||
Q_ASSERT_X(false, "setupThing", QString("Unhandled thingClassId: %1").arg(thing->thingClassId().toString()).toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
#include "plugintimer.h"
|
||||
#include "sunnywebbox.h"
|
||||
|
||||
#include "speedwireinterface.h"
|
||||
#include "speedwiremeter.h"
|
||||
|
||||
class IntegrationPluginSma: public IntegrationPlugin {
|
||||
Q_OBJECT
|
||||
@ -45,6 +45,7 @@ class IntegrationPluginSma: public IntegrationPlugin {
|
||||
public:
|
||||
explicit IntegrationPluginSma();
|
||||
|
||||
void init() override;
|
||||
void discoverThings(ThingDiscoveryInfo *info) override;
|
||||
void setupThing(ThingSetupInfo *info) override;
|
||||
void postSetupThing(Thing *thing) override;
|
||||
@ -59,6 +60,7 @@ private slots:
|
||||
private:
|
||||
PluginTimer *m_refreshTimer = nullptr;
|
||||
QHash<Thing *, SunnyWebBox *> m_sunnyWebBoxes;
|
||||
QHash<Thing *, SpeedwireMeter *> m_speedwireMeters;
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINSMA_H
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
{
|
||||
"id": "03f32361-4e13-4597-a346-af8d16a986b3",
|
||||
"name": "macAddress",
|
||||
"displayName": "hardware address",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"readOnly": true
|
||||
@ -88,10 +88,10 @@
|
||||
},
|
||||
{
|
||||
"id": "0c5097af-e136-4430-9fb4-0ccbb30c3e1c",
|
||||
"name": "speedwireInverter",
|
||||
"displayName": "SMA Inverter Speedwire",
|
||||
"name": "speedwireMeter",
|
||||
"displayName": "SMA Energy Meter",
|
||||
"createMethods": ["discovery", "user"],
|
||||
"interfaces": ["solarinverter"],
|
||||
"interfaces": [ "energymeter" ],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "d90193e6-a996-4e49-bf6d-564d596d7e74",
|
||||
@ -104,10 +104,26 @@
|
||||
{
|
||||
"id": "2780eab7-1f1c-4cc7-a789-a8790329ca9e",
|
||||
"name": "macAddress",
|
||||
"displayName": "hardware address",
|
||||
"displayName": "MAC address",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "7c81a0c5-9bc6-43bb-a01a-4de5fe656bba",
|
||||
"name": "serialNumber",
|
||||
"displayName": "Serial number",
|
||||
"type": "QString",
|
||||
"inputType": "TextLine",
|
||||
"readOnly": true
|
||||
},
|
||||
{
|
||||
"id": "abdc114d-1fac-4454-8b82-871ed5cdf28c",
|
||||
"name": "modelId",
|
||||
"displayName": "Model ID",
|
||||
"type": "uint",
|
||||
"inputType": "TextLine",
|
||||
"readOnly": true
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
@ -120,7 +136,61 @@
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"id": "015868bd-cc35-44cf-b631-78ea7c73b967",
|
||||
"id": "44ee2491-8376-41cd-a21d-185c736152ec",
|
||||
"name": "voltagePhaseA",
|
||||
"displayName": "Voltage phase A",
|
||||
"displayNameEvent": "Voltage phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "56ae3555-f874-4c2d-8833-17573dce477a",
|
||||
"name": "voltagePhaseB",
|
||||
"displayName": "Voltage phase B",
|
||||
"displayNameEvent": "Voltage phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "51cbb29b-29f0-480a-9d7d-b8f4e6a205ae",
|
||||
"name": "voltagePhaseC",
|
||||
"displayName": "Voltage phase C",
|
||||
"displayNameEvent": "Voltage phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Volt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "45bbdbef-1832-4870-bff5-299e580fb4da",
|
||||
"name": "currentPhaseA",
|
||||
"displayName": "Current phase A",
|
||||
"displayNameEvent": "Current phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "b3a4fdd2-b6b8-4c58-9da3-2084ad414022",
|
||||
"name": "currentPhaseB",
|
||||
"displayName": "Current phase B",
|
||||
"displayNameEvent": "Current phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "b3655188-3854-4336-ae3c-61d3bda6fc4d",
|
||||
"name": "currentPhaseC",
|
||||
"displayName": "Current phase C",
|
||||
"displayNameEvent": "Current phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Ampere",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "d4ac7f37-e30a-44e4-93cb-ad16df18b8f1",
|
||||
"name": "currentPower",
|
||||
"displayName": "Current power",
|
||||
"displayNameEvent": "Current power changed",
|
||||
@ -129,13 +199,111 @@
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "f29b6283-873b-45f5-8a14-622d34f11d4f",
|
||||
"id": "c5d09c63-7461-4fb8-a6fe-bc7aa919be30",
|
||||
"name": "currentPowerPhaseA",
|
||||
"displayName": "Current power phase A",
|
||||
"displayNameEvent": "Current power phase A changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "c52d4422-b521-4804-a7a7-c4398e91e760",
|
||||
"name": "currentPowerPhaseB",
|
||||
"displayName": "Current power phase B",
|
||||
"displayNameEvent": "Current power phase B changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "555e892c-3ca7-4100-9832-6ac13b87eb04",
|
||||
"name": "currentPowerPhaseC",
|
||||
"displayName": "Current power phase C",
|
||||
"displayNameEvent": "Current power phase C changed",
|
||||
"type": "double",
|
||||
"unit": "Watt",
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "4fb0a4c1-18ed-4d02-b6d0-c07e9b96a56d",
|
||||
"name": "totalEnergyConsumed",
|
||||
"displayName": "Total energy consumed",
|
||||
"displayNameEvent": "Total energy consumed changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "76ca68d8-6781-4d2a-8663-440aec40b4de",
|
||||
"name": "totalEnergyProduced",
|
||||
"displayName": "Total energy produced",
|
||||
"displayNameEvent": "Total energy produced changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "b4ff2c71-f81d-4904-bbac-0c0c6e8a5a33",
|
||||
"name": "energyConsumedPhaseA",
|
||||
"displayName": "Energy consumed phase A",
|
||||
"displayNameEvent": "Energy consumed phase A changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "c4e5f569-ac5d-4761-a898-888880bfd59f",
|
||||
"name": "energyConsumedPhaseB",
|
||||
"displayName": "Energy consumed phase B",
|
||||
"displayNameEvent": "Energy consumed phase B changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "aabc02d7-8dc3-4637-8bf2-dc2e0e737ad3",
|
||||
"name": "energyConsumedPhaseC",
|
||||
"displayName": "Energy consumed phase C",
|
||||
"displayNameEvent": "Energy consumed phase C changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "754c3b67-768a-47f7-99d8-f66c198f0835",
|
||||
"name": "energyProducedPhaseA",
|
||||
"displayName": "Energy produced phase A",
|
||||
"displayNameEvent": "Energy produced phase A changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "7eb08c45-24cf-40ce-be28-f3564f087672",
|
||||
"name": "energyProducedPhaseB",
|
||||
"displayName": "Energy produced phase B",
|
||||
"displayNameEvent": "Energy produced phase B changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "1eb2bf01-5ec6-42e5-b348-ac1e95199d14",
|
||||
"name": "energyProducedPhaseC",
|
||||
"displayName": "Energy produced phase C",
|
||||
"displayNameEvent": "Energy produced phase C changed",
|
||||
"type": "double",
|
||||
"unit": "KiloWattHour",
|
||||
"defaultValue": 0.00
|
||||
},
|
||||
{
|
||||
"id": "a685393c-8b7e-42c5-bb41-f9907c074626",
|
||||
"name": "firmwareVersion",
|
||||
"displayName": "Firmware version",
|
||||
"displayNameEvent": "Firmware version changed",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
6
sma/obisdata.cpp
Normal file
6
sma/obisdata.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
#include "obisdata.h"
|
||||
|
||||
ObisData::ObisData()
|
||||
{
|
||||
|
||||
}
|
||||
11
sma/obisdata.h
Normal file
11
sma/obisdata.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef OBISDATA_H
|
||||
#define OBISDATA_H
|
||||
|
||||
|
||||
class ObisData
|
||||
{
|
||||
public:
|
||||
ObisData();
|
||||
};
|
||||
|
||||
#endif // OBISDATA_H
|
||||
@ -4,10 +4,16 @@ QT += network
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginsma.cpp \
|
||||
obisdata.cpp \
|
||||
speedwirediscovery.cpp \
|
||||
speedwireinterface.cpp \
|
||||
speedwiremeter.cpp \
|
||||
sunnywebbox.cpp
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginsma.h \
|
||||
obisdata.h \
|
||||
speedwirediscovery.h \
|
||||
speedwireinterface.h \
|
||||
speedwiremeter.h \
|
||||
sunnywebbox.h
|
||||
|
||||
365
sma/speedwirediscovery.cpp
Normal file
365
sma/speedwirediscovery.cpp
Normal file
@ -0,0 +1,365 @@
|
||||
#include "speedwirediscovery.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <speedwirediscovery.h>
|
||||
|
||||
SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_networkDeviceDiscovery(networkDeviceDiscovery)
|
||||
{
|
||||
// More details: https://github.com/RalfOGit/libspeedwire/
|
||||
|
||||
|
||||
// //! Multicast device discovery request packet, according to SMA documentation.
|
||||
// //! However, this does not seem to be supported anymore with version 3.x devices
|
||||
// const unsigned char multicast_request[] = {
|
||||
// 0x53, 0x4d, 0x41, 0x00, 0x00, 0x04, 0x02, 0xa0, // sma signature, tag0
|
||||
// 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x20, // 0xffffffff group, 0x0000 length, 0x0020 "SMA Net ?", Version ?
|
||||
// 0x00, 0x00, 0x00, 0x00 // 0x0000 protocol, 0x00 #long words, 0x00 ctrl
|
||||
// };
|
||||
|
||||
// //! Unicast device discovery request packet, according to SMA documentation
|
||||
// const unsigned char unicast_request[] = {
|
||||
// 0x53, 0x4d, 0x41, 0x00, 0x00, 0x04, 0x02, 0xa0, // sma signature, tag0
|
||||
// 0x00, 0x00, 0x00, 0x01, 0x00, 0x26, 0x00, 0x10, // 0x26 length, 0x0010 "SMA Net 2", Version 0
|
||||
// 0x60, 0x65, 0x09, 0xa0, 0xff, 0xff, 0xff, 0xff, // 0x6065 protocol, 0x09 #long words, 0xa0 ctrl, 0xffff dst susyID any, 0xffffffff dst serial any
|
||||
// 0xff, 0xff, 0x00, 0x00, 0x7d, 0x00, 0x52, 0xbe, // 0x0000 dst cntrl, 0x007d src susy id, 0x3a28be52 src serial
|
||||
// 0x28, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000 src cntrl, 0x0000 error code, 0x0000 fragment ID
|
||||
// 0x01, 0x80, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, // 0x8001 packet ID
|
||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
// 0x00, 0x00
|
||||
// };
|
||||
|
||||
// // Request: 534d4100000402a00000000100260010 606509a0 ffffffffffff0000 7d0052be283a0000 000000000180 00020000 00000000 00000000 00000000 => command = 0x00000200, first = 0x00000000; last = 0x00000000; trailer = 0x00000000
|
||||
// // Response 534d4100000402a000000001004e0010 606513a0 7d0052be283a00c0 7a01842a71b30000 000000000180 01020000 00000000 00000000 00030000 00ff0000 00000000 01007a01 842a71b3 00000a00 0c000000 00000000 00000000 01010000 00000000
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// qCDebug(dcSma()) << "SpeedwireDiscovery: Create speed wire interface for multicast" << m_multicastAddress.toString() << "on port" << m_port;
|
||||
// QByteArray exampleData = QByteArray::fromHex("534d4100000402a000000001024400106069010e714369aee618a41600010400000000000001080000000021391229100002040000004415000208000000001575a137d800030400000000000003080000000003debed0e800040400000017c6000408000000001008c2070000090400000000000009080000000027c77bed20000a04000000481d000a08000000001722823410000d0400000003b00015040000000000001508000000000d1e1e0e3000160400000015120016080000000006c5a2d8b800170400000000000017080000000001bd6f680000180400000007990018080000000004def712b8001d040000000000001d08000000000eeefaafd0001e040000001666001e0800000000074b38bf88001f040000000a300020040000037bcb00210400000003ad0029040000000000002908000000000a9b1afec8002a040000001a81002a08000000000803e62b88002b040000000000002b080000000001511459b8002c0400000006d5002c0800000000052c8455b80031040000000000003108000000000cf83b37100032040000001b5f0032080000000008a6e257f80033040000000c3f003404000003747900350400000003c8003d040000000000003d08000000000a53d0ba08003e040000001482003e080000000007800fd188003f040000000000003f080000000001185820c8004004000000095800400800000000064563b1900045040000000000004508000000000d26d3eae0004604000000168900460800000000082b4fc5a80047040000000a440048040000037ed1004904000000038e900000000102085200000000");
|
||||
// processDatagram(QHostAddress("127.0.0.1"), m_port, exampleData);
|
||||
|
||||
m_multicastSocket = new QUdpSocket(this);
|
||||
connect(m_multicastSocket, &QUdpSocket::readyRead, this, &SpeedwireDiscovery::readPendingDatagramsMulticast);
|
||||
connect(m_multicastSocket, &QUdpSocket::stateChanged, this, &SpeedwireDiscovery::onSocketStateChanged);
|
||||
connect(m_multicastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError)));
|
||||
|
||||
m_unicastSocket = new QUdpSocket(this);
|
||||
connect(m_unicastSocket, &QUdpSocket::readyRead, this, &SpeedwireDiscovery::readPendingDatagramsUnicast);
|
||||
connect(m_unicastSocket, &QUdpSocket::stateChanged, this, &SpeedwireDiscovery::onSocketStateChanged);
|
||||
connect(m_unicastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError)));
|
||||
|
||||
m_discoveryTimer.setInterval(1000);
|
||||
m_discoveryTimer.setSingleShot(false);
|
||||
connect(&m_discoveryTimer, &QTimer::timeout, this, &SpeedwireDiscovery::sendDiscoveryRequest);
|
||||
}
|
||||
|
||||
SpeedwireDiscovery::~SpeedwireDiscovery()
|
||||
{
|
||||
if (m_initialized) {
|
||||
if (!m_multicastSocket->leaveMulticastGroup(m_multicastAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << m_multicastAddress.toString();
|
||||
}
|
||||
|
||||
m_multicastSocket->close();
|
||||
}
|
||||
}
|
||||
|
||||
bool SpeedwireDiscovery::initialize()
|
||||
{
|
||||
m_multicastSocket->close();
|
||||
m_initialized = false;
|
||||
|
||||
// Setup multicast socket
|
||||
if (!m_multicastSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind multicast socket to port" << m_port << m_multicastSocket->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_multicastSocket->joinMulticastGroup(m_multicastAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_multicastSocket->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup unicast socket
|
||||
if (!m_unicastSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind to port" << m_port << m_multicastSocket->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Interface initialized successfully.";
|
||||
m_initialized = true;
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
bool SpeedwireDiscovery::initialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
bool SpeedwireDiscovery::startDiscovery()
|
||||
{
|
||||
// 1. Discover all network devices
|
||||
// 2. Send upd multicast and unicast messages to verify if it is a SMA speedwire device
|
||||
|
||||
if (m_discoveryRunning)
|
||||
return true;
|
||||
|
||||
if (!m_initialized) {
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Failed to start discovery because the socket has not been initialized successfully.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// CLean up
|
||||
m_results.clear();
|
||||
m_networkDeviceInfos.clear();
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Start discovering network...";
|
||||
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
||||
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
|
||||
qCDebug(dcSma()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices";
|
||||
m_networkDeviceInfos = discoveryReply->networkDeviceInfos();
|
||||
|
||||
foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) {
|
||||
// 2. Send unicast to all results and start requesting on multicast address
|
||||
sendUnicastDiscoveryRequest(networkDeviceInfo.address());
|
||||
}
|
||||
|
||||
startMulticastDiscovery();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpeedwireDiscovery::discoveryRunning() const
|
||||
{
|
||||
return m_discoveryRunning;
|
||||
}
|
||||
|
||||
QList<SpeedwireDiscovery::SpeedwireDiscoveryResult> SpeedwireDiscovery::discoveryResult() const
|
||||
{
|
||||
return m_results.values();
|
||||
}
|
||||
|
||||
void SpeedwireDiscovery::startMulticastDiscovery()
|
||||
{
|
||||
// Start sending multicast messages
|
||||
sendDiscoveryRequest();
|
||||
|
||||
m_discoveryRunning = true;
|
||||
QTimer::singleShot(5000, this, &SpeedwireDiscovery::onDiscoveryProcessFinished);
|
||||
|
||||
m_discoveryTimer.start();
|
||||
}
|
||||
|
||||
void SpeedwireDiscovery::sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress)
|
||||
{
|
||||
if (m_unicastSocket->writeDatagram(m_discoveryDatagramUnicast, targetHostAddress, m_port) < 0) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to send unicast discovery datagram to address" << targetHostAddress.toString();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to unicast address" << targetHostAddress.toString();
|
||||
}
|
||||
|
||||
void SpeedwireDiscovery::readPendingDatagramsMulticast()
|
||||
{
|
||||
QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender());
|
||||
|
||||
QByteArray datagram;
|
||||
QHostAddress senderAddress;
|
||||
quint16 senderPort;
|
||||
|
||||
while (socket->hasPendingDatagrams()) {
|
||||
datagram.resize(socket->pendingDatagramSize());
|
||||
socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Received multicast data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort);
|
||||
//qCDebug(dcSma()) << "SpeedwireDiscovery: " << datagram.toHex();
|
||||
processDatagram(senderAddress, senderPort, datagram);
|
||||
}
|
||||
}
|
||||
|
||||
void SpeedwireDiscovery::readPendingDatagramsUnicast()
|
||||
{
|
||||
QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender());
|
||||
|
||||
QByteArray datagram;
|
||||
QHostAddress senderAddress;
|
||||
quint16 senderPort;
|
||||
|
||||
while (socket->hasPendingDatagrams()) {
|
||||
datagram.resize(socket->pendingDatagramSize());
|
||||
socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Received unicast data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort);
|
||||
//qCDebug(dcSma()) << "SpeedwireDiscovery: " << datagram.toHex();
|
||||
processDatagram(senderAddress, senderPort, datagram);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SpeedwireDiscovery::onSocketError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Socket error" << error;
|
||||
}
|
||||
|
||||
void SpeedwireDiscovery::onSocketStateChanged(QAbstractSocket::SocketState socketState)
|
||||
{
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Socket state changed" << socketState;
|
||||
}
|
||||
|
||||
void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &datagram)
|
||||
{
|
||||
// Check min size of SMA datagrams
|
||||
if (datagram.size() < 18) {
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Received datagram is to short to be a SMA speedwire message. Ignoring data...";
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore discovery requests
|
||||
if (datagram == m_discoveryDatagramMulticast || datagram == m_discoveryDatagramUnicast)
|
||||
return;
|
||||
|
||||
QDataStream stream(datagram);
|
||||
stream.setByteOrder(QDataStream::BigEndian);
|
||||
|
||||
SpeedwireInterface::SpeedwireHeader header = SpeedwireInterface::parseHeader(stream);
|
||||
if (!header.isValid()) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Datagram header is not valid. Ignoring data...";
|
||||
return;
|
||||
}
|
||||
// Example data:
|
||||
// 534d4100 0004 02a0 0000 0001 0244 0010 6069 010e 7143 69ae e618a416 00010400000000000001080000000021391229100002040000004415000208000000001575a137d800030400000000000003080000000003debed0e800040400000017c6000408000000001008c2070000090400000000000009080000000027c77bed20000a04000000481d000a08000000001722823410000d0400000003b00015040000000000001508000000000d1e1e0e3000160400000015120016080000000006c5a2d8b800170400000000000017080000000001bd6f680000180400000007990018080000000004def712b8001d040000000000001d08000000000eeefaafd0001e040000001666001e0800000000074b38bf88001f040000000a300020040000037bcb00210400000003ad0029040000000000002908000000000a9b1afec8002a040000001a81002a08000000000803e62b88002b040000000000002b080000000001511459b8002c0400000006d5002c0800000000052c8455b80031040000000000003108000000000cf83b37100032040000001b5f0032080000000008a6e257f80033040000000c3f003404000003747900350400000003c8003d040000000000003d08000000000a53d0ba08003e040000001482003e080000000007800fd188003f040000000000003f080000000001185820c8004004000000095800400800000000064563b1900045040000000000004508000000000d26d3eae0004604000000168900460800000000082b4fc5a80047040000000a440048040000037ed1004904000000038e900000000102085200000000
|
||||
|
||||
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: ======================= Header";
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Length:" << header.headerLength;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Tag0:" << header.tagType;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Tag0 version:" << header.tagVersion;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Group:" << header.group << (header.group == 1 ? "(default group)" : "");
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Data length:" << header.payloadLength << datagram.length();
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: SMA Net 2 Version" << header.smaNet2Version;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Protocol ID" << header.protocolId;
|
||||
|
||||
if (header.protocolId == SpeedwireInterface::ProtocolIdDiscoveryResponse) {
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Received discovery response from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort);
|
||||
// Response: 534d4100 0004 02a0 0000 0001 0002 0000 0001
|
||||
// "192.168.178.25:9522" "534d4100 0004 02a0 0000 0001 0002 0000 0001 0004 0010 0001 0003 0004 0020 0000 0001 0004 0030 c0a8 b219 0004 0040 0000 0000 0002 0070 ef0c 00000000"
|
||||
// "192.168.178.22:9522" "534d4100 0004 02a0 0000 0001 0002 0000 0001 0004 0010 0001 0001 0004 0020 0000 0001 0004 0030 c0a8 b216 0004 0040 0000 0001 00000000"
|
||||
|
||||
if (!datagram.startsWith(m_discoveryResponseDatagram)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Received discovery reply but the message start does not match the required schema. Ignoring data...";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_results.contains(senderAddress)) {
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: --> Found SMA device on" << senderAddress.toString();
|
||||
if (!m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Found SMA using UDP discovery but the host is not in the network discovery result list. Not adding to results" << senderAddress.toString();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
SpeedwireDiscoveryResult result;
|
||||
result.address = senderAddress;
|
||||
if (m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
||||
result.networkDeviceInfo = m_networkDeviceInfos.get(senderAddress);
|
||||
}
|
||||
result.deviceType = SpeedwireInterface::DeviceTypeUnknown;
|
||||
m_results.insert(senderAddress, result);
|
||||
} else {
|
||||
if (m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
||||
m_results[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// We received SMA data, let's parse depending on the protocol id
|
||||
|
||||
if (header.protocolId == SpeedwireInterface::ProtocolIdMeter) {
|
||||
// Example: 010e 714369ae
|
||||
quint16 modelId;
|
||||
quint32 serialNumber;
|
||||
stream >> modelId >> serialNumber;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: ======================= Meter identifier";
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Model ID:" << modelId;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Serial number:" << serialNumber;
|
||||
|
||||
if (!m_results.contains(senderAddress)) {
|
||||
SpeedwireDiscoveryResult result;
|
||||
result.address = senderAddress;
|
||||
result.deviceType = SpeedwireInterface::DeviceTypeMeter;
|
||||
m_results.insert(senderAddress, result);
|
||||
}
|
||||
|
||||
if (m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
||||
m_results[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress);
|
||||
}
|
||||
|
||||
m_results[senderAddress].modelId = modelId;
|
||||
m_results[senderAddress].serialNumber = serialNumber;
|
||||
} else if (header.protocolId == SpeedwireInterface::ProtocolIdInverter) {
|
||||
quint16 modelId;
|
||||
quint32 serialNumber;
|
||||
stream >> modelId >> serialNumber;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: ======================= Inverter identifier";
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Model ID:" << modelId;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Serial number:" << serialNumber;
|
||||
|
||||
if (!m_results.contains(senderAddress)) {
|
||||
SpeedwireDiscoveryResult result;
|
||||
result.address = senderAddress;
|
||||
result.deviceType = SpeedwireInterface::DeviceTypeInverter;
|
||||
m_results.insert(senderAddress, result);
|
||||
}
|
||||
|
||||
if (m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
||||
m_results[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress);
|
||||
}
|
||||
|
||||
m_results[senderAddress].modelId = modelId;
|
||||
m_results[senderAddress].serialNumber = serialNumber;
|
||||
} else {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Unhandled data received" << datagram.toHex();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void SpeedwireDiscovery::sendDiscoveryRequest()
|
||||
{
|
||||
if (m_multicastSocket->writeDatagram(m_discoveryDatagramMulticast, m_multicastAddress, m_port) < 0) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to send discovery datagram to multicast address" << m_multicastAddress.toString();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to multicast address" << m_multicastAddress.toString();
|
||||
}
|
||||
|
||||
void SpeedwireDiscovery::onDiscoveryProcessFinished()
|
||||
{
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Discovey finished. Found" << m_results.count() << "SMA devices in the network";
|
||||
m_discoveryTimer.stop();
|
||||
m_discoveryRunning = false;
|
||||
|
||||
foreach (const SpeedwireDiscoveryResult &result, m_results) {
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: ============================================";
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Device type:" << result.deviceType;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Address:" << result.address.toString();
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Hostname:" << result.networkDeviceInfo.hostName();
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: MAC:" << result.networkDeviceInfo.macAddress();
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: MAC manufacturer:" << result.networkDeviceInfo.macAddressManufacturer();
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Model ID:" << result.modelId;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Serial number:" << result.serialNumber;
|
||||
}
|
||||
|
||||
|
||||
emit discoveryFinished();
|
||||
}
|
||||
75
sma/speedwirediscovery.h
Normal file
75
sma/speedwirediscovery.h
Normal file
@ -0,0 +1,75 @@
|
||||
#ifndef SPEEDWIREDISCOVERY_H
|
||||
#define SPEEDWIREDISCOVERY_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include <network/networkdevicediscovery.h>
|
||||
|
||||
#include "speedwireinterface.h"
|
||||
|
||||
class SpeedwireDiscovery : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef struct SpeedwireDiscoveryResult {
|
||||
QHostAddress address;
|
||||
NetworkDeviceInfo networkDeviceInfo;
|
||||
SpeedwireInterface::DeviceType deviceType = SpeedwireInterface::DeviceTypeUnknown;
|
||||
quint16 modelId = 0;
|
||||
quint32 serialNumber = 0;
|
||||
} SpeedwireDiscoveryResult;
|
||||
|
||||
explicit SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
|
||||
~SpeedwireDiscovery();
|
||||
|
||||
bool initialize();
|
||||
bool initialized() const;
|
||||
|
||||
bool startDiscovery();
|
||||
bool discoveryRunning() const;
|
||||
|
||||
QList<SpeedwireDiscoveryResult> discoveryResult() const;
|
||||
|
||||
signals:
|
||||
void discoveryFinished();
|
||||
|
||||
private:
|
||||
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||
QUdpSocket *m_multicastSocket = nullptr;
|
||||
QUdpSocket *m_unicastSocket = nullptr;
|
||||
QHostAddress m_multicastAddress = QHostAddress("239.12.255.254");
|
||||
quint16 m_port = 9522;
|
||||
bool m_initialized = false;
|
||||
|
||||
// Discovery
|
||||
QTimer m_discoveryTimer;
|
||||
bool m_discoveryRunning = false;
|
||||
NetworkDeviceInfos m_networkDeviceInfos;
|
||||
QHash<QHostAddress, SpeedwireDiscoveryResult> m_results;
|
||||
|
||||
// Static discovery datagrams for speedwire
|
||||
QByteArray m_discoveryDatagramMulticast = QByteArray::fromHex("534d4100000402a0ffffffff0000002000000000");
|
||||
QByteArray m_discoveryResponseDatagram = QByteArray::fromHex("534d4100000402A000000001000200000001");
|
||||
|
||||
QByteArray m_discoveryDatagramUnicast = QByteArray::fromHex("534d4100000402a00000000100260010606509a0ffffffffffff00007d0052be283a000000000000018000020000000000000000000000000000");
|
||||
|
||||
void startMulticastDiscovery();
|
||||
void sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress);
|
||||
|
||||
private slots:
|
||||
void readPendingDatagramsMulticast();
|
||||
void readPendingDatagramsUnicast();
|
||||
void onSocketError(QAbstractSocket::SocketError error);
|
||||
void onSocketStateChanged(QAbstractSocket::SocketState socketState);
|
||||
|
||||
void processDatagram(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &datagram);
|
||||
|
||||
void sendDiscoveryRequest();
|
||||
|
||||
void onDiscoveryProcessFinished();
|
||||
|
||||
};
|
||||
|
||||
#endif // SPEEDWIREDISCOVERY_H
|
||||
@ -1,49 +1,85 @@
|
||||
#include "speedwireinterface.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
SpeedwireInterface::SpeedwireInterface(QObject *parent) :
|
||||
QObject(parent)
|
||||
SpeedwireInterface::SpeedwireInterface(const QHostAddress &address, bool multicast, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_address(address),
|
||||
m_multicast(multicast)
|
||||
{
|
||||
m_socket = new QUdpSocket(this);
|
||||
connect(m_socket, &QUdpSocket::readyRead, this, &SpeedwireInterface::readPendingDatagrams);
|
||||
connect(m_socket, &QUdpSocket::stateChanged, this, &SpeedwireInterface::onSocketStateChanged);
|
||||
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError)));
|
||||
|
||||
|
||||
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000480 00020058 001e8200 ff208200 00000000 => query device type
|
||||
// Response 534d4100000402a000000001009e0010 606527a0 7d0042be283a00a1 7a01842a71b30001 000000000480 01020058 01000000 03000000 011e8210 6f89e95f 534e3a20 33303130 35333831 31360000 00000000 00000000 00000000 00000000
|
||||
// 011f8208 6f89e95f 411f0001 feffff00 00000000 00000000 00000000 00000000 00000000 00000000 => 1f41 solar inverter
|
||||
// 01208208 6f89e95f 96240000 80240000 81240001 82240000 feffff00 00000000 00000000 00000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000480 00028053 001e2500 ff1e2500 00000000 => query spot dc power
|
||||
// Response 534d4100000402a000000001005e0010 606517a0 7d0042be283a00a1 7a01842a71b30001 000000000480 01028053 00000000 01000000 011e2540 61a7e95f 57000000 57000000 57000000 57000000 01000000
|
||||
// 021e2540 61a7e95f 5e000000 5e000000 5e000000 5e000000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000580 00028053 001f4500 ff214500 00000000 => query spot dc voltage/current
|
||||
// Response 534d4100000402a00000000100960010 606525a0 7d0042be283a00a1 7a01842a71b30001 000000000580 01028053 02000000 05000000 011f4540 61a7e95f 05610000 05610000 05610000 05610000 01000000
|
||||
// 021f4540 61a7e95f 505b0000 505b0000 505b0000 505b0000 01000000
|
||||
// 01214540 61a7e95f 60010000 60010000 60010000 60010000 01000000
|
||||
// 02214540 61a7e95f 95010000 95010000 95010000 95010000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000680 00020051 00404600 ff424600 00000000 => query spot ac power
|
||||
// Response 534d4100000402a000000001007a0010 60651ea0 7d0042be283a00a1 7a01842a71b30001 000000000680 01020051 09000000 0b000000 01404640 61a7e95f 38000000 38000000 38000000 38000000 01000000
|
||||
// 01414640 61a7e95f 37000000 37000000 37000000 37000000 01000000
|
||||
// 01424640 61a7e95f 39000000 39000000 39000000 39000000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000780 00020051 00484600 ff554600 00000000 => query spot ac voltage/current
|
||||
// Response 534d4100000402a000000001013e0010 60654fa0 7d0042be283a00a1 7a01842a71b30001 000000000780 01020051 0c000000 15000000 01484600 61a7e95f 5a590000 5a590000 5a590000 5a590000 01000000
|
||||
// 01494600 61a7e95f cf590000 cf590000 cf590000 cf590000 01000000
|
||||
// 014a4600 61a7e95f 7a590000 7a590000 7a590000 7a590000 01000000
|
||||
// 014b4600 61a7e95f f19a0000 f19a0000 f19a0000 f19a0000 01000000
|
||||
// 014c4600 61a7e95f 3c9b0000 3c9b0000 3c9b0000 3c9b0000 01000000
|
||||
// 014d4600 61a7e95f 189b0000 189b0000 189b0000 189b0000 01000000
|
||||
// 014e4600 51a7e95f 1d000000 1d000000 1d000000 1d000000 01000000
|
||||
// 01534640 61a7e95f 24010000 24010000 24010000 24010000 01000000
|
||||
// 01544640 61a7e95f 1e010000 1e010000 1e010000 1e010000 01000000
|
||||
// 01554640 61a7e95f 23010000 23010000 23010000 23010000 01000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000980 00028051 00482100 ff482100 00000000 => query device status
|
||||
// Response 534d4100000402a000000001004e0010 606513a0 7d0042be283a00a1 7a01842a71b30001 000000000980 01028051 00000000 00000000 01482108 59c5e95f 33010001 feffff00 00000000 00000000 00000000 00000000 00000000 00000000 00000000
|
||||
// Request 534d4100000402a00000000100260010 606509a0 7a01842a71b30001 7d0042be283a0001 000000000a80 00028051 00644100 ff644100 00000000 => query grid relay status
|
||||
// Response 534d4100000402a000000001004e0010 606513a0 7d0042be283a00a1 7a01842a71b30001 000000000a80 01028051 07000000 07000000 01644108 59c5e95f 33000001 37010000 fdffff00 feffff00 00000000 00000000 00000000 00000000 00000000
|
||||
}
|
||||
|
||||
SpeedwireInterface::~SpeedwireInterface()
|
||||
{
|
||||
if (m_initialized) {
|
||||
if (!m_socket->leaveMulticastGroup(m_multicastAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireInterface: Failed to leave multicast group" << m_multicastAddress.toString();
|
||||
}
|
||||
|
||||
m_socket->close();
|
||||
}
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
bool SpeedwireInterface::initialize()
|
||||
{
|
||||
// If we already initialized the socket, we are done
|
||||
if (m_initialized)
|
||||
return true;
|
||||
|
||||
m_socket->close();
|
||||
m_initialized = false;
|
||||
|
||||
if (!m_socket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireInterface: Cannot bind to port" << m_port << m_socket->errorString();
|
||||
if (!m_socket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
|
||||
qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not bind to port" << m_port;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!m_socket->joinMulticastGroup(m_multicastAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireInterface: Failed to join multicast group" << m_multicastAddress.toString() << m_socket->errorString();
|
||||
if (m_multicast && !m_socket->joinMulticastGroup(m_multicastAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_socket->errorString();
|
||||
return false;
|
||||
}
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: Interface initialized successfully.";
|
||||
m_initialized = true;
|
||||
return true;
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
void SpeedwireInterface::deinitialize()
|
||||
{
|
||||
if (m_initialized) {
|
||||
if (m_multicast) {
|
||||
if (!m_socket->leaveMulticastGroup(m_multicastAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << m_multicastAddress.toString();
|
||||
}
|
||||
|
||||
m_socket->close();
|
||||
m_initialized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool SpeedwireInterface::initialized() const
|
||||
@ -51,52 +87,50 @@ bool SpeedwireInterface::initialized() const
|
||||
return m_initialized;
|
||||
}
|
||||
|
||||
bool SpeedwireInterface::startDiscovery()
|
||||
quint16 SpeedwireInterface::sourceModelId() const
|
||||
{
|
||||
if (m_discoveryRunning)
|
||||
return true;
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: Start discovering network...";
|
||||
if (!m_initialized) {
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: Failed to start discovery because the socket has not been initialized successfully.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Discovery message
|
||||
QByteArray discoveryDatagram = QByteArray::fromHex("534d4100000402a0ffffffff0000002000000000");
|
||||
if (m_socket->write(discoveryDatagram) < 0) {
|
||||
qCWarning(dcSma()) << "SpeedwireInterface: Failed to send discovery datagram to multicast address" << m_multicastAddress.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_discoveryRunning = true;
|
||||
QTimer::singleShot(10000, this, [=](){
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: Discovey finished.";
|
||||
m_discoveryRunning = false;
|
||||
emit discoveryFinished();
|
||||
});
|
||||
|
||||
return true;
|
||||
return m_sourceModelId;
|
||||
}
|
||||
|
||||
bool SpeedwireInterface::discoveryRunning() const
|
||||
quint32 SpeedwireInterface::sourceSerialNumber() const
|
||||
{
|
||||
return m_discoveryRunning;
|
||||
return m_sourceSerialNumber;
|
||||
}
|
||||
|
||||
SpeedwireInterface::SpeedwireHeader SpeedwireInterface::parseHeader(QDataStream &stream)
|
||||
{
|
||||
SpeedwireHeader header;
|
||||
quint16 protocolId;
|
||||
stream >> header.smaSignature >> header.headerLength;
|
||||
stream >> header.tagType >> header.tagVersion >> header.group;
|
||||
stream >> header.payloadLength >> header.smaNet2Version;
|
||||
stream >> protocolId;
|
||||
header.protocolId = static_cast<ProtocolId>(protocolId);
|
||||
return header;
|
||||
}
|
||||
|
||||
void SpeedwireInterface::sendData(const QByteArray &data)
|
||||
{
|
||||
m_socket->writeDatagram(data, m_address, m_port);
|
||||
}
|
||||
|
||||
void SpeedwireInterface::readPendingDatagrams()
|
||||
{
|
||||
QUdpSocket *socket= qobject_cast<QUdpSocket *>(sender());
|
||||
|
||||
QByteArray datagram;
|
||||
QHostAddress senderAddress;
|
||||
quint16 senderPort;
|
||||
|
||||
while (socket->hasPendingDatagrams()) {
|
||||
datagram.resize(socket->pendingDatagramSize());
|
||||
socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: Data received from" << senderAddress.toString() << datagram.toHex();
|
||||
//emit datagramReceived(senderAddress, datagram);
|
||||
while (m_socket->hasPendingDatagrams()) {
|
||||
datagram.resize(m_socket->pendingDatagramSize());
|
||||
m_socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
|
||||
|
||||
// Process only data coming from our target address
|
||||
if (senderAddress != m_address)
|
||||
continue;
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: Received data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort);
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: " << datagram.toHex();
|
||||
emit dataReceived(datagram);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,29 +3,81 @@
|
||||
|
||||
#include <QObject>
|
||||
#include <QUdpSocket>
|
||||
#include <QDataStream>
|
||||
|
||||
|
||||
|
||||
|
||||
class SpeedwireInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SpeedwireInterface(QObject *parent = nullptr);
|
||||
enum ProtocolId {
|
||||
ProtocolIdUnknown = 0x0000,
|
||||
ProtocolIdMeter = 0x6069,
|
||||
ProtocolIdInverter = 0x6065,
|
||||
ProtocolIdDiscoveryResponse = 0x0001,
|
||||
ProtocolIdDiscovery = 0xffff
|
||||
};
|
||||
Q_ENUM(ProtocolId)
|
||||
|
||||
enum DeviceType {
|
||||
DeviceTypeUnknown,
|
||||
DeviceTypeMeter,
|
||||
DeviceTypeInverter
|
||||
};
|
||||
Q_ENUM(DeviceType)
|
||||
|
||||
class SpeedwireHeader
|
||||
{
|
||||
public:
|
||||
SpeedwireHeader() = default;
|
||||
quint32 smaSignature = 0;
|
||||
quint16 headerLength = 0;
|
||||
quint16 tagType = 0;
|
||||
quint16 tagVersion = 0;
|
||||
quint16 group = 0;
|
||||
quint16 payloadLength = 0;
|
||||
quint16 smaNet2Version = 0;
|
||||
ProtocolId protocolId = ProtocolIdUnknown;
|
||||
|
||||
inline bool isValid() const {
|
||||
return smaSignature == 0x534d4100 && protocolId != ProtocolIdUnknown;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
explicit SpeedwireInterface(const QHostAddress &address, bool multicast, QObject *parent = nullptr);
|
||||
~SpeedwireInterface();
|
||||
|
||||
bool initialize();
|
||||
void deinitialize();
|
||||
|
||||
bool initialized() const;
|
||||
|
||||
bool startDiscovery();
|
||||
bool discoveryRunning() const;
|
||||
quint16 sourceModelId() const;
|
||||
quint32 sourceSerialNumber() const;
|
||||
|
||||
static SpeedwireInterface::SpeedwireHeader parseHeader(QDataStream &stream);
|
||||
|
||||
public slots:
|
||||
void sendData(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void discoveryFinished();
|
||||
void dataReceived(const QByteArray &data);
|
||||
|
||||
private:
|
||||
QUdpSocket *m_socket = nullptr;
|
||||
QHostAddress m_multicastAddress = QHostAddress("239.12.255.254");
|
||||
QHostAddress m_address;
|
||||
quint16 m_port = 9522;
|
||||
QHostAddress m_multicastAddress = QHostAddress("239.12.255.254");
|
||||
bool m_multicast = false;
|
||||
bool m_initialized = false;
|
||||
bool m_discoveryRunning = false;
|
||||
|
||||
// Requester
|
||||
quint16 m_sourceModelId = 0x007d;
|
||||
quint32 m_sourceSerialNumber = 0x3a28be42;
|
||||
|
||||
private slots:
|
||||
void readPendingDatagrams();
|
||||
|
||||
270
sma/speedwiremeter.cpp
Normal file
270
sma/speedwiremeter.cpp
Normal file
@ -0,0 +1,270 @@
|
||||
#include "speedwiremeter.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
SpeedwireMeter::SpeedwireMeter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_address(address),
|
||||
m_modelId(modelId),
|
||||
m_serialNumber(serialNumber)
|
||||
{
|
||||
m_interface = new SpeedwireInterface(m_address, true, this);
|
||||
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireMeter::processData);
|
||||
}
|
||||
|
||||
bool SpeedwireMeter::initialize()
|
||||
{
|
||||
return m_interface->initialize();
|
||||
}
|
||||
|
||||
bool SpeedwireMeter::initialized() const
|
||||
{
|
||||
return m_interface->initialized();
|
||||
}
|
||||
|
||||
double SpeedwireMeter::currentPower() const
|
||||
{
|
||||
return m_currentPower;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::totalEnergyProduced() const
|
||||
{
|
||||
return m_totalEnergyProduced;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::totalEnergyConsumed() const
|
||||
{
|
||||
return m_totalEnergyConsumed;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::energyConsumedPhaseA() const
|
||||
{
|
||||
return m_energyConsumedPhaseA;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::energyConsumedPhaseB() const
|
||||
{
|
||||
return m_energyConsumedPhaseB;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::energyConsumedPhaseC() const
|
||||
{
|
||||
return m_energyConsumedPhaseC;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::energyProducedPhaseA() const
|
||||
{
|
||||
return m_energyProducedPhaseA;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::energyProducedPhaseB() const
|
||||
{
|
||||
return m_energyProducedPhaseB;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::energyProducedPhaseC() const
|
||||
{
|
||||
return m_energyProducedPhaseC;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::currentPowerPhaseA() const
|
||||
{
|
||||
return m_currentPowerPhaseA;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::currentPowerPhaseB() const
|
||||
{
|
||||
return m_currentPowerPhaseB;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::currentPowerPhaseC() const
|
||||
{
|
||||
return m_currentPowerPhaseC;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::voltagePhaseA() const
|
||||
{
|
||||
return m_voltagePhaseA;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::voltagePhaseB() const
|
||||
{
|
||||
return m_voltagePhaseB;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::voltagePhaseC() const
|
||||
{
|
||||
return m_voltagePhaseC;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::amperePhaseA() const
|
||||
{
|
||||
return m_amperePhaseA;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::amperePhaseB() const
|
||||
{
|
||||
return m_amperePhaseB;
|
||||
}
|
||||
|
||||
double SpeedwireMeter::amperePhaseC() const
|
||||
{
|
||||
return m_amperePhaseC;
|
||||
}
|
||||
|
||||
QString SpeedwireMeter::softwareVersion() const
|
||||
{
|
||||
return m_softwareVersion;
|
||||
}
|
||||
|
||||
void SpeedwireMeter::processData(const QByteArray &data)
|
||||
{
|
||||
qCDebug(dcSma()) << "Meter: data received" << data.toHex();
|
||||
QDataStream stream(data);
|
||||
stream.setByteOrder(QDataStream::BigEndian);
|
||||
|
||||
SpeedwireInterface::SpeedwireHeader header = SpeedwireInterface::parseHeader(stream);
|
||||
if (!header.isValid()) {
|
||||
qCDebug(dcSma()) << "Meter: Datagram header is not valid. Ignoring data...";
|
||||
return;
|
||||
}
|
||||
|
||||
if (header.protocolId != SpeedwireInterface::ProtocolIdMeter) {
|
||||
qCDebug(dcSma()) << "Meter: received header protocol which is not from the meter protocol. Ignoring data...";
|
||||
return;
|
||||
}
|
||||
|
||||
quint16 modelId;
|
||||
quint32 serialNumber;
|
||||
stream >> modelId >> serialNumber;
|
||||
if (m_modelId != modelId && serialNumber != m_serialNumber) {
|
||||
qCDebug(dcSma()) << "Meter: received meter data from an other meter. Ignoring data...";
|
||||
}
|
||||
|
||||
qCDebug(dcSma()) << "Meter: Model ID:" << modelId;
|
||||
qCDebug(dcSma()) << "Meter: Serial number:" << serialNumber;
|
||||
|
||||
// Parse the packet data
|
||||
// Timestamp e618a416
|
||||
qCDebug(dcSma()) << "Meter: ======================= Meter measurements";
|
||||
quint32 timestamp;
|
||||
stream >> timestamp;
|
||||
qCDebug(dcSma()) << "Meter: Timestamp:" << timestamp;
|
||||
|
||||
// Obis data
|
||||
//00 01 04 00 00000000 00 01 08 00 0000002139122910 00 02 04 00 00004415 00 02 08 00 0000001575a137d8 00 03 04 00 00000000 00 03 08 00 00000003debed0e8 00040400000017c6000408000000001008c2070000090400000000000009080000000027c77bed20000a04000000481d000a08000000001722823410000d0400000003b00015040000000000001508000000000d1e1e0e3000160400000015120016080000000006c5a2d8b800170400000000000017080000000001bd6f680000180400000007990018080000000004def712b8001d040000000000001d08000000000eeefaafd0001e040000001666001e0800000000074b38bf88001f040000000a300020040000037bcb00210400000003ad0029040000000000002908000000000a9b1afec8002a040000001a81002a08000000000803e62b88002b040000000000002b080000000001511459b8002c0400000006d5002c0800000000052c8455b80031040000000000003108000000000cf83b37100032040000001b5f0032080000000008a6e257f80033040000000c3f003404000003747900350400000003c8003d040000000000003d08000000000a53d0ba08003e040000001482003e080000000007800fd188003f040000000000003f080000000001185820c8004004000000095800400800000000064563b1900045040000000000004508000000000d26d3eae0004604000000168900460800000000082b4fc5a80047040000000a440048040000037ed1004904000000038e90000000 01020852 00000000
|
||||
while (!stream.atEnd()) {
|
||||
quint8 measurementChannel;
|
||||
quint8 measurementIndex;
|
||||
quint8 measurmentType;
|
||||
quint8 measurmentTariff;
|
||||
|
||||
stream >> measurementChannel >> measurementIndex >> measurmentType >> measurmentTariff;
|
||||
|
||||
if (measurmentType == 4) {
|
||||
qint32 measurement;
|
||||
stream >> measurement;
|
||||
|
||||
if (measurementIndex == 1 && measurement != 0) {
|
||||
m_currentPower = measurement / 10.0;
|
||||
qCDebug(dcSma()) << "Meter: Current power" << m_currentPower << "W";
|
||||
} else if (measurementIndex == 2 && measurement != 0) {
|
||||
m_currentPower = -measurement / 10.0;
|
||||
qCDebug(dcSma()) << "Meter: Current power" << m_currentPower << "W";
|
||||
} else if (measurementIndex == 21 && measurement != 0) {
|
||||
m_currentPowerPhaseA = measurement / 10.0;
|
||||
qCDebug(dcSma()) << "Meter: Current power phase A" << m_currentPowerPhaseA << "W";
|
||||
} else if (measurementIndex == 22 && measurement != 0) {
|
||||
m_currentPowerPhaseA = -measurement / 10.0;
|
||||
qCDebug(dcSma()) << "Meter: Current power phase A" << m_currentPowerPhaseA << "W";
|
||||
} else if (measurementIndex == 41 && measurement != 0) {
|
||||
m_currentPowerPhaseB = measurement / 10.0;
|
||||
qCDebug(dcSma()) << "Meter: Current power phase B" << m_currentPowerPhaseB << "W";
|
||||
} else if (measurementIndex == 42 && measurement != 0) {
|
||||
m_currentPowerPhaseB = -measurement / 10.0;
|
||||
qCDebug(dcSma()) << "Meter: Current power phase B" << m_currentPowerPhaseB << "W";
|
||||
} else if (measurementIndex == 61 && measurement != 0) {
|
||||
m_currentPowerPhaseC = measurement / 10.0;
|
||||
qCDebug(dcSma()) << "Meter: Current power phase C" << m_currentPowerPhaseC << "W";
|
||||
} else if (measurementIndex == 62 && measurement != 0) {
|
||||
m_currentPowerPhaseC = -measurement / 10.0;
|
||||
qCDebug(dcSma()) << "Meter: Current power phase C" << m_currentPowerPhaseC << "W";
|
||||
} else if (measurementIndex == 31) {
|
||||
m_amperePhaseA = measurement / 1000.0;
|
||||
qCDebug(dcSma()) << "Meter: Ampere phase A" << m_amperePhaseA << "A";
|
||||
} else if (measurementIndex == 51) {
|
||||
m_amperePhaseB = measurement / 1000.0;
|
||||
qCDebug(dcSma()) << "Meter: Ampere phase B" << m_amperePhaseB << "A";
|
||||
} else if (measurementIndex == 71) {
|
||||
m_amperePhaseC = measurement / 1000.0;
|
||||
qCDebug(dcSma()) << "Meter: Ampere phase C" << m_amperePhaseC << "A";
|
||||
} else if (measurementIndex == 32) {
|
||||
m_voltagePhaseA = measurement / 1000.0;
|
||||
qCDebug(dcSma()) << "Meter: Voltage phase A" << m_voltagePhaseA << "V";
|
||||
} else if (measurementIndex == 52) {
|
||||
m_voltagePhaseB = measurement / 1000.0;
|
||||
qCDebug(dcSma()) << "Meter: Voltage phase B" << m_voltagePhaseB << "V";
|
||||
} else if (measurementIndex == 72) {
|
||||
m_voltagePhaseC = measurement / 1000.0;
|
||||
qCDebug(dcSma()) << "Meter: Voltage phase C" << m_voltagePhaseC << "V";
|
||||
} else {
|
||||
// qCDebug(dcSma()) << "Meter: --> Channel:" << measurementChannel << "Index:" << measurementIndex << "Type:" << measurmentType << "Rate:" << measurmentTariff;
|
||||
// qCDebug(dcSma()) << "Meter: Value:" << measurement;
|
||||
}
|
||||
|
||||
|
||||
} else if (measurmentType == 8) {
|
||||
qint64 measurement;
|
||||
stream >> measurement;
|
||||
|
||||
if (measurementIndex == 1 && measurement != 0) {
|
||||
m_totalEnergyConsumed = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Total energy consumed" << m_totalEnergyConsumed << "kWh";
|
||||
} else if (measurementIndex == 2 && measurement != 0) {
|
||||
m_totalEnergyProduced = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Total energy produced" << m_totalEnergyProduced << "kWh";
|
||||
} else if (measurementIndex == 21 && measurement != 0) {
|
||||
m_energyConsumedPhaseA = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy consumed phase A" << m_energyConsumedPhaseA << "kWh";
|
||||
} else if (measurementIndex == 41 && measurement != 0) {
|
||||
m_energyConsumedPhaseB = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy consumed phase B" << m_energyConsumedPhaseB << "kWh";
|
||||
} else if (measurementIndex == 61 && measurement != 0) {
|
||||
m_energyConsumedPhaseC = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy consumed phase C" << m_energyConsumedPhaseC << "kWh";
|
||||
} else if (measurementIndex == 22 && measurement != 0) {
|
||||
m_energyProducedPhaseA = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy produced phase A" << m_energyProducedPhaseA << "kWh";
|
||||
} else if (measurementIndex == 42 && measurement != 0) {
|
||||
m_energyProducedPhaseB = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy produced phase B" << m_energyProducedPhaseB << "kWh";
|
||||
} else if (measurementIndex == 62 && measurement != 0) {
|
||||
m_energyProducedPhaseC = measurement / 3600000.0;
|
||||
qCDebug(dcSma()) << "Energy produced phase C" << m_energyProducedPhaseC << "kWh";
|
||||
} else {
|
||||
// qCDebug(dcSma()) << "Meter: --> Channel:" << measurementChannel << "Index:" << measurementIndex << "Type:" << measurmentType << "Rate:" << measurmentTariff;
|
||||
// qCDebug(dcSma()) << "Meter: Value:" << measurement;
|
||||
}
|
||||
|
||||
} if (measurementChannel == 144 && measurementIndex == 0 && measurmentType == 0 && measurmentTariff == 0) {
|
||||
// Software version
|
||||
// 90000000 01 02 08 52
|
||||
quint8 major, minor, build, revision;
|
||||
stream >> major >> minor >> build >> revision;
|
||||
// Revision types:
|
||||
// S: Special version
|
||||
// A: Alpha version
|
||||
// B: Beta version
|
||||
// R: Release version
|
||||
// E: Experimental version
|
||||
// N: No revision
|
||||
m_softwareVersion = QString("%1.%2.%3-%4").arg(major).arg(minor).arg(build).arg(QChar(revision));
|
||||
|
||||
qCDebug(dcSma()) << "Meter: Software version" << m_softwareVersion;
|
||||
} else if (measurementChannel == 0 && measurementIndex == 0 && measurmentType == 0 && measurmentTariff == 0) {
|
||||
// 00 00 00 00
|
||||
//qCDebug(dcSma()) << "Meter: End of data reached.";
|
||||
}
|
||||
}
|
||||
|
||||
emit valuesUpdated();
|
||||
}
|
||||
86
sma/speedwiremeter.h
Normal file
86
sma/speedwiremeter.h
Normal file
@ -0,0 +1,86 @@
|
||||
#ifndef SPEEDWIREMETER_H
|
||||
#define SPEEDWIREMETER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "speedwireinterface.h"
|
||||
|
||||
class SpeedwireMeter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit SpeedwireMeter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent = nullptr);
|
||||
|
||||
bool initialize();
|
||||
bool initialized() const;
|
||||
|
||||
|
||||
double currentPower() const;
|
||||
double totalEnergyProduced() const;
|
||||
double totalEnergyConsumed() const;
|
||||
|
||||
double energyConsumedPhaseA() const;
|
||||
double energyConsumedPhaseB() const;
|
||||
double energyConsumedPhaseC() const;
|
||||
|
||||
double energyProducedPhaseA() const;
|
||||
double energyProducedPhaseB() const;
|
||||
double energyProducedPhaseC() const;
|
||||
|
||||
double currentPowerPhaseA() const;
|
||||
double currentPowerPhaseB() const;
|
||||
double currentPowerPhaseC() const;
|
||||
|
||||
double voltagePhaseA() const;
|
||||
double voltagePhaseB() const;
|
||||
double voltagePhaseC() const;
|
||||
|
||||
double amperePhaseA() const;
|
||||
double amperePhaseB() const;
|
||||
double amperePhaseC() const;
|
||||
|
||||
QString softwareVersion() const;
|
||||
|
||||
|
||||
signals:
|
||||
void valuesUpdated();
|
||||
|
||||
private:
|
||||
SpeedwireInterface *m_interface = nullptr;
|
||||
QHostAddress m_address;
|
||||
bool m_initialized = false;
|
||||
quint16 m_modelId = 0;
|
||||
quint32 m_serialNumber = 0;
|
||||
|
||||
double m_currentPower = 0;
|
||||
double m_totalEnergyProduced = 0;
|
||||
double m_totalEnergyConsumed = 0;
|
||||
|
||||
double m_energyConsumedPhaseA = 0;
|
||||
double m_energyConsumedPhaseB = 0;
|
||||
double m_energyConsumedPhaseC = 0;
|
||||
|
||||
double m_energyProducedPhaseA = 0;
|
||||
double m_energyProducedPhaseB = 0;
|
||||
double m_energyProducedPhaseC = 0;
|
||||
|
||||
double m_currentPowerPhaseA = 0;
|
||||
double m_currentPowerPhaseB = 0;
|
||||
double m_currentPowerPhaseC = 0;
|
||||
|
||||
double m_voltagePhaseA = 0;
|
||||
double m_voltagePhaseB = 0;
|
||||
double m_voltagePhaseC = 0;
|
||||
|
||||
double m_amperePhaseA = 0;
|
||||
double m_amperePhaseB = 0;
|
||||
double m_amperePhaseC = 0;
|
||||
|
||||
QString m_softwareVersion;
|
||||
|
||||
private slots:
|
||||
void processData(const QByteArray &data);
|
||||
|
||||
};
|
||||
|
||||
#endif // SPEEDWIREMETER_H
|
||||
Loading…
x
Reference in New Issue
Block a user