Merge PR #99: SMA: Update speedwire discovery

This commit is contained in:
jenkins 2022-12-14 23:59:32 +01:00
commit da4e43081d
14 changed files with 307 additions and 184 deletions

View File

@ -63,7 +63,7 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
webBoxDiscovery->deleteLater(); webBoxDiscovery->deleteLater();
ThingDescriptors descriptors; ThingDescriptors descriptors;
foreach (const NetworkDeviceInfo &networkDeviceInfo, webBoxDiscovery->discoveryResults()) { foreach (const NetworkDeviceInfo &networkDeviceInfo, webBoxDiscovery->discoveryResults()) {
QString title = networkDeviceInfo.hostName() + " (" + networkDeviceInfo.address().toString() + ")"; QString title = "SMA Sunny WebBox (" + networkDeviceInfo.address().toString() + ")";
QString description; QString description;
if (networkDeviceInfo.macAddressManufacturer().isEmpty()) { if (networkDeviceInfo.macAddressManufacturer().isEmpty()) {
description = networkDeviceInfo.macAddress(); description = networkDeviceInfo.macAddress();
@ -94,8 +94,10 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
webBoxDiscovery->startDiscovery(); webBoxDiscovery->startDiscovery();
} else if (info->thingClassId() == speedwireMeterThingClassId) { } else if (info->thingClassId() == speedwireMeterThingClassId) {
// Note: does not require the network device discovery...
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), info); SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), info);
if (!speedwireDiscovery->initialize()) { if (!speedwireDiscovery->initialize(SpeedwireInterface::DeviceTypeMeter)) {
qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed."; qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed.";
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network.")); info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network."));
return; return;
@ -107,27 +109,27 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
ThingDescriptors descriptors; ThingDescriptors descriptors;
foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) { 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()); if (result.deviceType != SpeedwireInterface::DeviceTypeMeter)
// We found an energy meter, let's check if we already added this one continue;
foreach (Thing *existingThing, myThings()) {
if (existingThing->paramValue(speedwireMeterThingSerialNumberParamTypeId).toUInt() == result.serialNumber) { if (result.serialNumber == 0)
descriptor.setThingId(existingThing->id()); continue;
break;
} ThingDescriptor descriptor(speedwireMeterThingClassId, "SMA Energy Meter (" + 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);
} }
ParamList params;
params << Param(speedwireMeterThingSerialNumberParamTypeId, result.serialNumber);
params << Param(speedwireMeterThingModelIdParamTypeId, result.modelId);
descriptor.setParams(params);
descriptors.append(descriptor);
} }
info->addThingDescriptors(descriptors); info->addThingDescriptors(descriptors);
@ -135,9 +137,16 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
}); });
speedwireDiscovery->startDiscovery(); speedwireDiscovery->startDiscovery();
} else if (info->thingClassId() == speedwireInverterThingClassId) { } else if (info->thingClassId() == speedwireInverterThingClassId) {
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
qCWarning(dcSma()) << "Failed to discover network devices. The network device discovery is not available.";
info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("Unable to discover devices in your network."));
return;
}
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), info); SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), info);
if (!speedwireDiscovery->initialize()) { if (!speedwireDiscovery->initialize(SpeedwireInterface::DeviceTypeInverter)) {
qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed."; qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed.";
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network.")); info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network."));
return; return;
@ -149,27 +158,29 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
ThingDescriptors descriptors; ThingDescriptors descriptors;
foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) { foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) {
if (result.deviceType == SpeedwireInterface::DeviceTypeInverter) {
if (result.serialNumber == 0)
continue;
ThingDescriptor descriptor(speedwireInverterThingClassId, Sma::getModelName(result.modelId), "Serial: " + QString::number(result.serialNumber) + " - " + result.address.toString()); if (result.deviceType != SpeedwireInterface::DeviceTypeInverter)
// We found an energy meter, let's check if we already added this one continue;
foreach (Thing *existingThing, myThings()) {
if (existingThing->paramValue(speedwireInverterThingSerialNumberParamTypeId).toUInt() == result.serialNumber) { if (result.serialNumber == 0)
descriptor.setThingId(existingThing->id()); continue;
break;
} ThingDescriptor descriptor(speedwireInverterThingClassId, "SMA inverter (" + QString::number(result.serialNumber) + ")", result.address.toString());
// We found an inverter, let's check if we already added this one
foreach (Thing *existingThing, myThings()) {
if (existingThing->paramValue(speedwireInverterThingSerialNumberParamTypeId).toUInt() == result.serialNumber) {
descriptor.setThingId(existingThing->id());
break;
} }
ParamList params;
params << Param(speedwireInverterThingHostParamTypeId, result.address.toString());
params << Param(speedwireInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
params << Param(speedwireInverterThingSerialNumberParamTypeId, result.serialNumber);
params << Param(speedwireInverterThingModelIdParamTypeId, result.modelId);
descriptor.setParams(params);
descriptors.append(descriptor);
} }
ParamList params;
params << Param(speedwireInverterThingHostParamTypeId, result.address.toString());
params << Param(speedwireInverterThingMacAddressParamTypeId, result.networkDeviceInfo.macAddress());
params << Param(speedwireInverterThingSerialNumberParamTypeId, result.serialNumber);
params << Param(speedwireInverterThingModelIdParamTypeId, result.modelId);
descriptor.setParams(params);
descriptors.append(descriptor);
} }
info->addThingDescriptors(descriptors); info->addThingDescriptors(descriptors);
@ -177,6 +188,7 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
}); });
speedwireDiscovery->startDiscovery(); speedwireDiscovery->startDiscovery();
} else if (info->thingClassId() == modbusInverterThingClassId) { } else if (info->thingClassId() == modbusInverterThingClassId) {
if (!hardwareManager()->networkDeviceDiscovery()->available()) { if (!hardwareManager()->networkDeviceDiscovery()->available()) {
qCWarning(dcSma()) << "The network discovery is not available on this platform."; qCWarning(dcSma()) << "The network discovery is not available on this platform.";
@ -278,27 +290,30 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
QString requestId = sunnyWebBox->getPlantOverview(); QString requestId = sunnyWebBox->getPlantOverview();
connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, info, [=] (const QString &messageId, SunnyWebBox::Overview overview) { connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, info, [=] (const QString &messageId, SunnyWebBox::Overview overview) {
qCDebug(dcSma()) << "Received plant overview" << messageId << "Finish setup"; qCDebug(dcSma()) << "Received plant overview" << messageId << "finish setup";
Q_UNUSED(overview)
info->finish(Thing::ThingErrorNoError); info->finish(Thing::ThingErrorNoError);
connect(sunnyWebBox, &SunnyWebBox::connectedChanged, this, &IntegrationPluginSma::onConnectedChanged); connect(sunnyWebBox, &SunnyWebBox::connectedChanged, this, &IntegrationPluginSma::onConnectedChanged);
connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, this, &IntegrationPluginSma::onPlantOverviewReceived); connect(sunnyWebBox, &SunnyWebBox::plantOverviewReceived, this, &IntegrationPluginSma::onPlantOverviewReceived);
m_sunnyWebBoxes.insert(info->thing(), sunnyWebBox); m_sunnyWebBoxes.insert(info->thing(), sunnyWebBox);
onPlantOverviewReceived(messageId, overview);
}); });
} else if (thing->thingClassId() == speedwireMeterThingClassId) { } else if (thing->thingClassId() == speedwireMeterThingClassId) {
QHostAddress address = QHostAddress(thing->paramValue(speedwireMeterThingHostParamTypeId).toString()); // Create the multicast interface if not created already.
if (!m_multicastInterface)
m_multicastInterface = new SpeedwireInterface(true, this);
quint32 serialNumber = static_cast<quint32>(thing->paramValue(speedwireMeterThingSerialNumberParamTypeId).toUInt()); quint32 serialNumber = static_cast<quint32>(thing->paramValue(speedwireMeterThingSerialNumberParamTypeId).toUInt());
quint16 modelId = static_cast<quint16>(thing->paramValue(speedwireMeterThingModelIdParamTypeId).toUInt()); quint16 modelId = static_cast<quint16>(thing->paramValue(speedwireMeterThingModelIdParamTypeId).toUInt());
if (m_speedwireMeters.contains(thing)) { // Handle reconfigure
if (m_speedwireMeters.contains(thing))
m_speedwireMeters.take(thing)->deleteLater(); m_speedwireMeters.take(thing)->deleteLater();
}
SpeedwireMeter *meter = new SpeedwireMeter(address, modelId, serialNumber, this); SpeedwireMeter *meter = new SpeedwireMeter(m_multicastInterface, modelId, serialNumber, this);
if (!meter->initialize()) { if (!meter->initialize()) {
meter->deleteLater();
qCWarning(dcSma()) << "Setup failed. Could not initialize meter interface."; qCWarning(dcSma()) << "Setup failed. Could not initialize meter interface.";
info->finish(Thing::ThingErrorHardwareFailure); info->finish(Thing::ThingErrorHardwareFailure);
return; return;
@ -522,6 +537,12 @@ void IntegrationPluginSma::thingRemoved(Thing *thing)
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing)); hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
} }
if (myThings().filterByThingClassId(speedwireMeterThingClassId).isEmpty() && m_multicastInterface) {
// Delete shared multicast socket...
m_multicastInterface->deleteLater();
m_multicastInterface = nullptr;
}
if (myThings().isEmpty()) { if (myThings().isEmpty()) {
qCDebug(dcSma()) << "Stopping timer"; qCDebug(dcSma()) << "Stopping timer";
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer); hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
@ -547,7 +568,7 @@ void IntegrationPluginSma::onPlantOverviewReceived(const QString &messageId, Sun
if (!thing) if (!thing)
return; return;
thing->setStateValue(sunnyWebBoxCurrentPowerStateTypeId, overview.power); thing->setStateValue(sunnyWebBoxCurrentPowerStateTypeId, -overview.power);
thing->setStateValue(sunnyWebBoxDayEnergyProducedStateTypeId, overview.dailyYield); thing->setStateValue(sunnyWebBoxDayEnergyProducedStateTypeId, overview.dailyYield);
thing->setStateValue(sunnyWebBoxTotalEnergyProducedStateTypeId, overview.totalYield); thing->setStateValue(sunnyWebBoxTotalEnergyProducedStateTypeId, overview.totalYield);
thing->setStateValue(sunnyWebBoxModeStateTypeId, overview.status); thing->setStateValue(sunnyWebBoxModeStateTypeId, overview.status);
@ -565,9 +586,9 @@ void IntegrationPluginSma::setupRefreshTimer()
m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(5); m_refreshTimer = hardwareManager()->pluginTimerManager()->registerTimer(5);
connect(m_refreshTimer, &PluginTimer::timeout, this, [=](){ connect(m_refreshTimer, &PluginTimer::timeout, this, [=](){
foreach (Thing *thing, myThings().filterByThingClassId(sunnyWebBoxThingClassId)) { foreach (SunnyWebBox *webbox, m_sunnyWebBoxes) {
SunnyWebBox *sunnyWebBox = m_sunnyWebBoxes.value(thing); // Max refresh rate according to docs should be 30 seconds, will be handled in the webbox class
sunnyWebBox->getPlantOverview(); webbox->getPlantOverview();
} }
foreach (SpeedwireInverter *inverter, m_speedwireInverters) { foreach (SpeedwireInverter *inverter, m_speedwireInverters) {

View File

@ -40,6 +40,7 @@
#include "sunnywebbox/sunnywebbox.h" #include "sunnywebbox/sunnywebbox.h"
#include "speedwire/speedwiremeter.h" #include "speedwire/speedwiremeter.h"
#include "speedwire/speedwireinverter.h" #include "speedwire/speedwireinverter.h"
#include "speedwire/speedwireinterface.h"
#include "smainvertermodbustcpconnection.h" #include "smainvertermodbustcpconnection.h"
@ -79,6 +80,9 @@ private:
QHash<Thing *, SpeedwireInverter *> m_speedwireInverters; QHash<Thing *, SpeedwireInverter *> m_speedwireInverters;
QHash<Thing *, SmaInverterModbusTcpConnection *> m_modbusInverters; QHash<Thing *, SmaInverterModbusTcpConnection *> m_modbusInverters;
// Shared interface accross meters
SpeedwireInterface *m_multicastInterface = nullptr;
// Sma modbus data validation // Sma modbus data validation
bool isModbusValueValid(quint32 value); bool isModbusValueValid(quint32 value);
bool isModbusValueValid(qint32 value); bool isModbusValueValid(qint32 value);

View File

@ -90,23 +90,6 @@
"createMethods": ["discovery", "user"], "createMethods": ["discovery", "user"],
"interfaces": [ "energymeter" ], "interfaces": [ "energymeter" ],
"paramTypes": [ "paramTypes": [
{
"id": "d90193e6-a996-4e49-bf6d-564d596d7e74",
"name": "host",
"displayName": "Host address",
"type": "QString",
"inputType": "IPv4Address",
"defaultValue": "192.168.0.168"
},
{
"id": "2780eab7-1f1c-4cc7-a789-a8790329ca9e",
"name": "macAddress",
"displayName": "MAC address",
"type": "QString",
"inputType": "TextLine",
"readOnly": true,
"defaultValue": ""
},
{ {
"id": "7c81a0c5-9bc6-43bb-a01a-4de5fe656bba", "id": "7c81a0c5-9bc6-43bb-a01a-4de5fe656bba",
"name": "serialNumber", "name": "serialNumber",

View File

@ -93,6 +93,8 @@ public:
case 5: case 5:
revisionCharacter = 'S'; revisionCharacter = 'S';
break; break;
default:
revisionCharacter = QChar(revision);
} }
return QString("%1.%2.%3-%4").arg(major).arg(minor).arg(build).arg(revisionCharacter); return QString("%1.%2.%3-%4").arg(major).arg(minor).arg(build).arg(revisionCharacter);

View File

@ -39,33 +39,17 @@ SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDisc
{ {
// More details: https://github.com/RalfOGit/libspeedwire/ // More details: https://github.com/RalfOGit/libspeedwire/
// 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
m_multicastSearchRequestTimer.setInterval(1000);
// // Request: 534d4100000402a00000000100260010 606509a0 ffffffffffff0000 7d0052be283a0000 000000000180 00020000 00000000 00000000 00000000 => command = 0x00000200, first = 0x00000000; last = 0x00000000; trailer = 0x00000000 m_multicastSearchRequestTimer.setSingleShot(false);
// // Response 534d4100000402a000000001004e0010 606513a0 7d0052be283a00c0 7a01842a71b30000 000000000180 01020000 00000000 00000000 00030000 00ff0000 00000000 01007a01 842a71b3 00000a00 0c000000 00000000 00000000 01010000 00000000 connect(&m_multicastSearchRequestTimer, &QTimer::timeout, this, &SpeedwireDiscovery::sendDiscoveryRequest);
// 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() SpeedwireDiscovery::~SpeedwireDiscovery()
{ {
if (m_initialized) { if (m_initialized && m_multicastSocket) {
if (!m_multicastSocket->leaveMulticastGroup(m_multicastAddress)) { if (!m_multicastSocket->leaveMulticastGroup(m_multicastAddress)) {
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << m_multicastAddress.toString(); qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << m_multicastAddress.toString();
} }
@ -74,30 +58,24 @@ SpeedwireDiscovery::~SpeedwireDiscovery()
} }
} }
bool SpeedwireDiscovery::initialize() bool SpeedwireDiscovery::initialize(SpeedwireInterface::DeviceType deviceType)
{ {
m_multicastSocket->close(); m_deviceType = deviceType;
m_initialized = false; switch(deviceType) {
case SpeedwireInterface::DeviceTypeMeter:
// Setup multicast socket m_initialized = setupMulticastSocket();
if (!m_multicastSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) { break;
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind multicast socket to port" << m_port << m_multicastSocket->errorString(); case SpeedwireInterface::DeviceTypeInverter:
return false; m_initialized = setupUnicastSocket();
break;
default:
m_initialized = setupMulticastSocket() && setupUnicastSocket();
break;
} }
if (!m_multicastSocket->joinMulticastGroup(m_multicastAddress)) { if (m_initialized)
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_multicastSocket->errorString(); qCDebug(dcSma()) << "SpeedwireDiscovery: Interfaces initialized successfully.";
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; return m_initialized;
} }
@ -108,10 +86,7 @@ bool SpeedwireDiscovery::initialized() const
bool SpeedwireDiscovery::startDiscovery() bool SpeedwireDiscovery::startDiscovery()
{ {
// 1. Discover all network devices if (discoveryRunning())
// 2. Send upd multicast and unicast messages to verify if it is a SMA speedwire device
if (m_discoveryRunning)
return true; return true;
if (!m_initialized) { if (!m_initialized) {
@ -119,31 +94,31 @@ bool SpeedwireDiscovery::startDiscovery()
return false; return false;
} }
// CLean up // Start clean
m_results.clear(); m_results.clear();
m_networkDeviceInfos.clear(); m_networkDeviceInfos.clear();
m_multicastRunning = false;
m_unicastRunning = false;
qCDebug(dcSma()) << "SpeedwireDiscovery: Start discovering network..."; switch(m_deviceType) {
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover(); case SpeedwireInterface::DeviceTypeMeter:
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
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(); startMulticastDiscovery();
}); break;
case SpeedwireInterface::DeviceTypeInverter:
startUnicastDiscovery();
break;
default:
startUnicastDiscovery();
startMulticastDiscovery();
break;
}
return true; return true;
} }
bool SpeedwireDiscovery::discoveryRunning() const bool SpeedwireDiscovery::discoveryRunning() const
{ {
return m_discoveryRunning; return m_unicastRunning || m_multicastRunning;
} }
QList<SpeedwireDiscovery::SpeedwireDiscoveryResult> SpeedwireDiscovery::discoveryResult() const QList<SpeedwireDiscovery::SpeedwireDiscoveryResult> SpeedwireDiscovery::discoveryResult() const
@ -151,15 +126,92 @@ QList<SpeedwireDiscovery::SpeedwireDiscoveryResult> SpeedwireDiscovery::discover
return m_results.values(); return m_results.values();
} }
bool SpeedwireDiscovery::setupMulticastSocket()
{
if (m_multicastSocket)
return true;
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)));
// 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();
m_multicastSocket->deleteLater();
m_multicastSocket = nullptr;
return false;
}
if (!m_multicastSocket->joinMulticastGroup(m_multicastAddress)) {
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_multicastSocket->errorString();
m_multicastSocket->deleteLater();
m_multicastSocket = nullptr;
return false;
}
return true;
}
void SpeedwireDiscovery::startMulticastDiscovery() void SpeedwireDiscovery::startMulticastDiscovery()
{ {
qCDebug(dcSma()) << "SpeedwireDiscovery: Start multicast discovery...";
m_multicastRunning = true;
// Start sending multicast messages // Start sending multicast messages
sendDiscoveryRequest(); sendDiscoveryRequest();
m_discoveryRunning = true; // Give 5 seconds time, on one of the requests sent in this period the meter would have responded...
QTimer::singleShot(5000, this, &SpeedwireDiscovery::onDiscoveryProcessFinished); QTimer::singleShot(5000, this, [this](){
m_multicastRunning = false;
evaluateDiscoveryFinished();
});
m_discoveryTimer.start(); m_multicastSearchRequestTimer.start();
}
bool SpeedwireDiscovery::setupUnicastSocket()
{
if (m_unicastSocket)
return true;
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)));
// 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();
m_unicastSocket->deleteLater();
m_unicastSocket = nullptr;
return false;
}
return true;
}
void SpeedwireDiscovery::startUnicastDiscovery()
{
qCDebug(dcSma()) << "SpeedwireDiscovery: Start discovering network...";
m_unicastRunning = true;
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, discoveryReply, &NetworkDeviceDiscoveryReply::deleteLater);
connect(discoveryReply, &NetworkDeviceDiscoveryReply::networkDeviceInfoAdded, this, [this](const NetworkDeviceInfo &networkDeviceInfo){
m_networkDeviceInfos.append(networkDeviceInfo);
sendUnicastDiscoveryRequest(networkDeviceInfo.address());
});
connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){
qCDebug(dcSma()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices for unicast requests.";
// Wait some extra second in otder to give the last hosts joined some time to respond.
QTimer::singleShot(3000, this, [this](){
m_unicastRunning = false;
evaluateDiscoveryFinished();
});
});
} }
void SpeedwireDiscovery::sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress) void SpeedwireDiscovery::sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress)
@ -206,7 +258,6 @@ void SpeedwireDiscovery::readPendingDatagramsUnicast()
} }
} }
void SpeedwireDiscovery::onSocketError(QAbstractSocket::SocketError error) void SpeedwireDiscovery::onSocketError(QAbstractSocket::SocketError error)
{ {
qCDebug(dcSma()) << "SpeedwireDiscovery: Socket error" << error; qCDebug(dcSma()) << "SpeedwireDiscovery: Socket error" << error;
@ -252,8 +303,7 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
if (!m_results.contains(senderAddress)) { if (!m_results.contains(senderAddress)) {
qCDebug(dcSma()) << "SpeedwireDiscovery: --> Found SMA device on" << senderAddress.toString(); qCDebug(dcSma()) << "SpeedwireDiscovery: --> Found SMA device on" << senderAddress.toString();
if (!m_networkDeviceInfos.hasHostAddress(senderAddress)) { 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(); qCDebug(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; SpeedwireDiscoveryResult result;
@ -327,22 +377,43 @@ void SpeedwireDiscovery::sendDiscoveryRequest()
qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to multicast address" << m_multicastAddress.toString(); qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to multicast address" << m_multicastAddress.toString();
} }
void SpeedwireDiscovery::onDiscoveryProcessFinished() void SpeedwireDiscovery::evaluateDiscoveryFinished()
{
if (!m_multicastRunning && !m_unicastRunning) {
finishDiscovery();
}
}
void SpeedwireDiscovery::finishDiscovery()
{ {
qCDebug(dcSma()) << "SpeedwireDiscovery: Discovey finished. Found" << m_results.count() << "SMA devices in the network"; qCDebug(dcSma()) << "SpeedwireDiscovery: Discovey finished. Found" << m_results.count() << "SMA devices in the network";
m_discoveryTimer.stop(); m_multicastSearchRequestTimer.stop();
m_discoveryRunning = false;
if (m_multicastSocket) {
m_multicastSocket->close();
m_multicastSocket->deleteLater();
m_multicastSocket = nullptr;
}
if (m_unicastSocket) {
m_unicastSocket->close();
m_unicastSocket->deleteLater();
m_unicastSocket = nullptr;
}
foreach (const SpeedwireDiscoveryResult &result, m_results) { foreach (const SpeedwireDiscoveryResult &result, m_results) {
qCDebug(dcSma()) << "SpeedwireDiscovery: ============================================"; qCDebug(dcSma()) << "SpeedwireDiscovery: ============================================";
qCDebug(dcSma()) << "SpeedwireDiscovery: Device type:" << result.deviceType; qCDebug(dcSma()) << "SpeedwireDiscovery: Device type:" << result.deviceType;
qCDebug(dcSma()) << "SpeedwireDiscovery: Address:" << result.address.toString(); qCDebug(dcSma()) << "SpeedwireDiscovery: Address:" << result.address.toString();
qCDebug(dcSma()) << "SpeedwireDiscovery: Hostname:" << result.networkDeviceInfo.hostName(); if (result.networkDeviceInfo.isValid()) {
qCDebug(dcSma()) << "SpeedwireDiscovery: MAC:" << result.networkDeviceInfo.macAddress(); qCDebug(dcSma()) << "SpeedwireDiscovery: Hostname:" << result.networkDeviceInfo.hostName();
qCDebug(dcSma()) << "SpeedwireDiscovery: MAC manufacturer:" << result.networkDeviceInfo.macAddressManufacturer(); 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: Model ID:" << result.modelId;
qCDebug(dcSma()) << "SpeedwireDiscovery: Serial number:" << result.serialNumber; qCDebug(dcSma()) << "SpeedwireDiscovery: Serial number:" << result.serialNumber;
} }
emit discoveryFinished(); emit discoveryFinished();
} }

View File

@ -55,7 +55,7 @@ public:
explicit SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr); explicit SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
~SpeedwireDiscovery(); ~SpeedwireDiscovery();
bool initialize(); bool initialize(SpeedwireInterface::DeviceType deviceType = SpeedwireInterface::DeviceTypeUnknown);
bool initialized() const; bool initialized() const;
bool startDiscovery(); bool startDiscovery();
@ -68,6 +68,7 @@ signals:
private: private:
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr; NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
SpeedwireInterface::DeviceType m_deviceType = SpeedwireInterface::DeviceTypeUnknown;
QUdpSocket *m_multicastSocket = nullptr; QUdpSocket *m_multicastSocket = nullptr;
QUdpSocket *m_unicastSocket = nullptr; QUdpSocket *m_unicastSocket = nullptr;
QHostAddress m_multicastAddress = Speedwire::multicastAddress(); QHostAddress m_multicastAddress = Speedwire::multicastAddress();
@ -75,12 +76,19 @@ private:
bool m_initialized = false; bool m_initialized = false;
// Discovery // Discovery
QTimer m_discoveryTimer; QTimer m_multicastSearchRequestTimer;
bool m_discoveryRunning = false;
NetworkDeviceInfos m_networkDeviceInfos; NetworkDeviceInfos m_networkDeviceInfos;
QHash<QHostAddress, SpeedwireDiscoveryResult> m_results; QHash<QHostAddress, SpeedwireDiscoveryResult> m_results;
bool m_multicastRunning = false;
bool m_unicastRunning = false;
bool setupMulticastSocket();
void startMulticastDiscovery(); void startMulticastDiscovery();
bool setupUnicastSocket();
void startUnicastDiscovery();
;
void sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress); void sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress);
private slots: private slots:
@ -93,7 +101,9 @@ private slots:
void sendDiscoveryRequest(); void sendDiscoveryRequest();
void onDiscoveryProcessFinished(); void evaluateDiscoveryFinished();
void finishDiscovery();
}; };

View File

@ -31,13 +31,11 @@
#include "speedwireinterface.h" #include "speedwireinterface.h"
#include "extern-plugininfo.h" #include "extern-plugininfo.h"
SpeedwireInterface::SpeedwireInterface(const QHostAddress &address, bool multicast, QObject *parent) : SpeedwireInterface::SpeedwireInterface(bool multicast, QObject *parent) :
QObject(parent), QObject(parent),
m_address(address),
m_multicast(multicast) m_multicast(multicast)
{ {
qCDebug(dcSma()) << "SpeedwireInterface: Create interface for" << address.toString() << (multicast ? "multicast" : "unicast");
m_socket = new QUdpSocket(this); m_socket = new QUdpSocket(this);
connect(m_socket, &QUdpSocket::readyRead, this, &SpeedwireInterface::readPendingDatagrams); connect(m_socket, &QUdpSocket::readyRead, this, &SpeedwireInterface::readPendingDatagrams);
connect(m_socket, &QUdpSocket::stateChanged, this, &SpeedwireInterface::onSocketStateChanged); connect(m_socket, &QUdpSocket::stateChanged, this, &SpeedwireInterface::onSocketStateChanged);
@ -49,8 +47,18 @@ SpeedwireInterface::~SpeedwireInterface()
deinitialize(); deinitialize();
} }
bool SpeedwireInterface::initialize() bool SpeedwireInterface::initialize(const QHostAddress &address)
{ {
if (m_initialized && !m_multicast) {
qCWarning(dcSma()) << "Try to initialize an already initialized speed wire interface. Please deinitialize() before calling this method again.";
return false;
}
// If already initialized and multicast, nothing could habe changed...done here
if (m_initialized && m_multicast)
return true;
if (!m_socket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) { if (!m_socket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not bind to port" << m_port; qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not bind to port" << m_port;
return false; return false;
@ -61,7 +69,8 @@ bool SpeedwireInterface::initialize()
return false; return false;
} }
qCDebug(dcSma()) << "SpeedwireInterface: Interface initialized successfully."; qCDebug(dcSma()) << "SpeedwireInterface: Interface initialized successfully for" << address.toString() << (m_multicast ? "multicast" : "unicast");
m_address = address;
m_initialized = true; m_initialized = true;
return m_initialized; return m_initialized;
} }
@ -75,11 +84,17 @@ void SpeedwireInterface::deinitialize()
} }
} }
m_address = QHostAddress();
m_socket->close(); m_socket->close();
m_initialized = false; m_initialized = false;
} }
} }
bool SpeedwireInterface::multicast() const
{
return m_multicast;
}
bool SpeedwireInterface::initialized() const bool SpeedwireInterface::initialized() const
{ {
return m_initialized; return m_initialized;
@ -113,13 +128,13 @@ void SpeedwireInterface::readPendingDatagrams()
datagram.resize(m_socket->pendingDatagramSize()); datagram.resize(m_socket->pendingDatagramSize());
m_socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort); m_socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
// Process only data coming from our target address // Process only data coming from our target address if there is any
if (senderAddress != m_address) if (!m_address.isNull() && senderAddress != m_address)
continue; continue;
qCDebug(dcSma()) << "SpeedwireInterface: Received data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort); qCDebug(dcSma()) << "SpeedwireInterface: Received data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort);
//qCDebug(dcSma()) << "SpeedwireInterface: " << datagram.toHex(); //qCDebug(dcSma()) << "SpeedwireInterface: " << datagram.toHex();
emit dataReceived(datagram); emit dataReceived(senderAddress, senderPort, datagram);
} }
} }

View File

@ -48,12 +48,14 @@ public:
}; };
Q_ENUM(DeviceType) Q_ENUM(DeviceType)
explicit SpeedwireInterface(const QHostAddress &address, bool multicast, QObject *parent = nullptr); explicit SpeedwireInterface(bool multicast, QObject *parent = nullptr);
~SpeedwireInterface(); ~SpeedwireInterface();
bool initialize(); bool initialize(const QHostAddress &address = QHostAddress());
void deinitialize(); void deinitialize();
bool multicast() const;
bool initialized() const; bool initialized() const;
quint16 sourceModelId() const; quint16 sourceModelId() const;
@ -63,7 +65,7 @@ public slots:
void sendData(const QByteArray &data); void sendData(const QByteArray &data);
signals: signals:
void dataReceived(const QByteArray &data); void dataReceived(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data);
private: private:
QUdpSocket *m_socket = nullptr; QUdpSocket *m_socket = nullptr;

View File

@ -40,13 +40,13 @@ SpeedwireInverter::SpeedwireInverter(const QHostAddress &address, quint16 modelI
m_serialNumber(serialNumber) m_serialNumber(serialNumber)
{ {
qCDebug(dcSma()) << "Inverter: setup interface on" << m_address.toString(); qCDebug(dcSma()) << "Inverter: setup interface on" << m_address.toString();
m_interface = new SpeedwireInterface(m_address, false, this); m_interface = new SpeedwireInterface(false, this);
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireInverter::processData); connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireInverter::processData);
} }
bool SpeedwireInverter::initialize() bool SpeedwireInverter::initialize()
{ {
return m_interface->initialize(); return m_interface->initialize(m_address);
} }
bool SpeedwireInverter::initialized() const bool SpeedwireInverter::initialized() const
@ -976,8 +976,12 @@ void SpeedwireInverter::setReachable(bool reachable)
emit reachableChanged(m_reachable); emit reachableChanged(m_reachable);
} }
void SpeedwireInverter::processData(const QByteArray &data) void SpeedwireInverter::processData(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data)
{ {
// Note: the interface is already filtering out data from other hosts m_address
Q_UNUSED(senderAddress)
Q_UNUSED(senderPort)
if (data.size() < 18) { if (data.size() < 18) {
qCDebug(dcSma()) << "Inverter: The received datagram is to short to be a SMA speedwire message. Ignoring data..."; qCDebug(dcSma()) << "Inverter: The received datagram is to short to be a SMA speedwire message. Ignoring data...";
return; return;

View File

@ -193,7 +193,7 @@ private:
void setReachable(bool reachable); void setReachable(bool reachable);
private slots: private slots:
void processData(const QByteArray &data); void processData(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data);
void onReplyTimeout(); void onReplyTimeout();
void onReplyFinished(); void onReplyFinished();

View File

@ -33,13 +33,12 @@
#include "sma.h" #include "sma.h"
SpeedwireMeter::SpeedwireMeter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent) : SpeedwireMeter::SpeedwireMeter(SpeedwireInterface *multicastInterface, quint16 modelId, quint32 serialNumber, QObject *parent) :
QObject(parent), QObject(parent),
m_address(address), m_interface(multicastInterface),
m_modelId(modelId), m_modelId(modelId),
m_serialNumber(serialNumber) m_serialNumber(serialNumber)
{ {
m_interface = new SpeedwireInterface(m_address, true, this);
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireMeter::processData); connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireMeter::processData);
// Reachable timestamp // Reachable timestamp
@ -165,10 +164,9 @@ QString SpeedwireMeter::softwareVersion() const
void SpeedwireMeter::evaluateReachable() void SpeedwireMeter::evaluateReachable()
{ {
// Note: the meter sends every second the data on the multicast // Note: the meter sends every second the data on the multicast
qint64 currentTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000;
// If the meter has not sent data within the last 5 seconds it seems not to be reachable // If the meter has not sent data within the last 5 seconds it seems not to be reachable
bool reachable = false; bool reachable = false;
if (currentTimestamp - m_lastSeenTimestamp < 10) { if (QDateTime::currentDateTime().toMSecsSinceEpoch() - m_lastSeenTimestamp < 5000) {
reachable = true; reachable = true;
} }
@ -188,9 +186,8 @@ void SpeedwireMeter::evaluateReachable()
} }
} }
void SpeedwireMeter::processData(const QByteArray &data) void SpeedwireMeter::processData(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data)
{ {
qCDebug(dcSma()) << "Meter: data received" << data.toHex();
QDataStream stream(data); QDataStream stream(data);
stream.setByteOrder(QDataStream::BigEndian); stream.setByteOrder(QDataStream::BigEndian);
@ -200,27 +197,30 @@ void SpeedwireMeter::processData(const QByteArray &data)
return; return;
} }
if (header.protocolId != Speedwire::ProtocolIdMeter) { if (header.protocolId != Speedwire::ProtocolIdMeter)
qCDebug(dcSma()) << "Meter: received header protocol which is not from the meter protocol. Ignoring data...";
return; return;
}
quint16 modelId; quint16 modelId;
quint32 serialNumber; quint32 serialNumber;
stream >> modelId >> 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; // Make sure this payload belongs to us
qCDebug(dcSma()) << "Meter: Serial number:" << serialNumber; if (m_modelId != modelId || serialNumber != m_serialNumber)
return;
//qCDebug(dcSma()) << "Meter: data received" << data.toHex();
qCDebug(dcSma()).noquote() << "Meter: Measurements received from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort) << "Serial number:" << serialNumber << "Model ID:" << modelId;
// Make sure the rate is at max 1Hz, some meters send much more data, which creates an uneccessary load
if (QDateTime::currentDateTime().toMSecsSinceEpoch() - m_lastSeenTimestamp < 1000)
return;
// Parse the packet data // Parse the packet data
// Timestamp e618a416 // Timestamp e618a416
qCDebug(dcSma()) << "Meter: ======================= Meter measurements"; qCDebug(dcSma()) << "Meter: ======================= Meter measurements";
quint32 timestamp; quint32 timestamp;
stream >> timestamp; stream >> timestamp;
qCDebug(dcSma()) << "Meter: Timestamp:" << timestamp; qCDebug(dcSma()) << "Meter: Timestamp:" << timestamp << QDateTime::fromMSecsSinceEpoch(timestamp * 1000);
// Obis data // 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 //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
@ -323,7 +323,6 @@ void SpeedwireMeter::processData(const QByteArray &data)
quint32 versionData; quint32 versionData;
stream >> versionData; stream >> versionData;
m_softwareVersion = Sma::buildSoftwareVersionString(versionData); m_softwareVersion = Sma::buildSoftwareVersionString(versionData);
qCDebug(dcSma()) << "Meter: Software version" << m_softwareVersion; qCDebug(dcSma()) << "Meter: Software version" << m_softwareVersion;
} else if (measurementChannel == 0 && measurementIndex == 0 && measurmentType == 0 && measurmentTariff == 0) { } else if (measurementChannel == 0 && measurementIndex == 0 && measurmentType == 0 && measurmentTariff == 0) {
// 00 00 00 00 // 00 00 00 00
@ -332,7 +331,7 @@ void SpeedwireMeter::processData(const QByteArray &data)
} }
// Save the current timestamp for reachable evaluation // Save the current timestamp for reachable evaluation
m_lastSeenTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000; m_lastSeenTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
evaluateReachable(); evaluateReachable();
emit valuesUpdated(); emit valuesUpdated();

View File

@ -41,7 +41,7 @@ class SpeedwireMeter : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit SpeedwireMeter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent = nullptr); explicit SpeedwireMeter(SpeedwireInterface *multicastInterface, quint16 modelId, quint32 serialNumber, QObject *parent = nullptr);
bool initialize(); bool initialize();
bool initialized() const; bool initialized() const;
@ -118,7 +118,7 @@ private:
private slots: private slots:
void evaluateReachable(); void evaluateReachable();
void processData(const QByteArray &data); void processData(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data);
}; };

View File

@ -37,8 +37,8 @@
SunnyWebBox::SunnyWebBox(NetworkAccessManager *networkAccessManager, const QHostAddress &hostAddress, QObject *parrent) : SunnyWebBox::SunnyWebBox(NetworkAccessManager *networkAccessManager, const QHostAddress &hostAddress, QObject *parrent) :
QObject(parrent), QObject(parrent),
m_hostAddresss(hostAddress), m_networkManager(networkAccessManager),
m_networkManager(networkAccessManager) m_hostAddresss(hostAddress)
{ {
qCDebug(dcSma()) << "SunnyWebBox: Creating Sunny Web Box connection"; qCDebug(dcSma()) << "SunnyWebBox: Creating Sunny Web Box connection";
} }
@ -50,6 +50,16 @@ SunnyWebBox::~SunnyWebBox()
QString SunnyWebBox::getPlantOverview() QString SunnyWebBox::getPlantOverview()
{ {
QDateTime currentDateTime = QDateTime::currentDateTime();
if (!m_lastRequest.isNull()) {
// According to the documentation, the interval between 2 requests should not be less than 30 seconds.
if (QDateTime::currentDateTime().toMSecsSinceEpoch() - m_lastRequest.toMSecsSinceEpoch() < 30000) {
return QString();
}
}
m_lastRequest = currentDateTime;
return sendMessage(m_hostAddresss, "GetPlantOverview"); return sendMessage(m_hostAddresss, "GetPlantOverview");
} }
@ -163,10 +173,10 @@ QNetworkReply *SunnyWebBox::sendRequest(const QHostAddress &address, const QStri
doc.setObject(obj); doc.setObject(obj);
QUrl url; QUrl url;
url.setScheme("http");
url.setHost(address.toString()); url.setHost(address.toString());
url.setPath("/rpc"); url.setPath("/rpc");
url.setPort(80); url.setPort(80);
url.setScheme("http");
QNetworkRequest request(url); QNetworkRequest request(url);
request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json"); request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json");
QByteArray data = doc.toJson(QJsonDocument::JsonFormat::Compact); QByteArray data = doc.toJson(QJsonDocument::JsonFormat::Compact);
@ -309,6 +319,7 @@ QString SunnyWebBox::sendMessage(const QHostAddress &address, const QString &pro
setConnectionStatus(false); setConnectionStatus(false);
return; return;
} }
setConnectionStatus(true); setConnectionStatus(true);
QByteArray data = reply->readAll(); QByteArray data = reply->readAll();

View File

@ -38,11 +38,11 @@
#include <QJsonObject> #include <QJsonObject>
#include <QHostAddress> #include <QHostAddress>
#include <QUdpSocket> #include <QUdpSocket>
#include <QDateTime>
class SunnyWebBox : public QObject class SunnyWebBox : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
struct Overview { struct Overview {
int power; int power;
@ -96,10 +96,11 @@ public:
static QString generateRequestId(); static QString generateRequestId();
private: private:
NetworkAccessManager *m_networkManager = nullptr;
bool m_connected = false; bool m_connected = false;
QHostAddress m_hostAddresss; QHostAddress m_hostAddresss;
QString m_macAddress; QString m_macAddress;
NetworkAccessManager *m_networkManager = nullptr; QDateTime m_lastRequest;
QString sendMessage(const QHostAddress &address, const QString &procedure); QString sendMessage(const QHostAddress &address, const QString &procedure);
QString sendMessage(const QHostAddress &address, const QString &procedure, const QJsonObject &params); QString sendMessage(const QHostAddress &address, const QString &procedure, const QJsonObject &params);