Update SMA speedwire discovery and improve meter behavior
parent
a60c361df7
commit
0e7390555d
|
|
@ -94,8 +94,10 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
|||
webBoxDiscovery->startDiscovery();
|
||||
|
||||
} else if (info->thingClassId() == speedwireMeterThingClassId) {
|
||||
|
||||
// Note: does not require the network device discovery...
|
||||
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.";
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network."));
|
||||
return;
|
||||
|
|
@ -107,11 +109,14 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
|||
|
||||
ThingDescriptors descriptors;
|
||||
foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) {
|
||||
if (result.deviceType == SpeedwireInterface::DeviceTypeMeter) {
|
||||
|
||||
if (result.deviceType != SpeedwireInterface::DeviceTypeMeter)
|
||||
continue;
|
||||
|
||||
if (result.serialNumber == 0)
|
||||
continue;
|
||||
|
||||
ThingDescriptor descriptor(speedwireMeterThingClassId, "SMA Energy Meter", "Serial: " + QString::number(result.serialNumber) + " - " + result.address.toString());
|
||||
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) {
|
||||
|
|
@ -121,23 +126,27 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
|||
}
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
speedwireDiscovery->startDiscovery();
|
||||
|
||||
} 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);
|
||||
if (!speedwireDiscovery->initialize()) {
|
||||
if (!speedwireDiscovery->initialize(SpeedwireInterface::DeviceTypeInverter)) {
|
||||
qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed.";
|
||||
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network."));
|
||||
return;
|
||||
|
|
@ -149,12 +158,15 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
|||
|
||||
ThingDescriptors descriptors;
|
||||
foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) {
|
||||
if (result.deviceType == SpeedwireInterface::DeviceTypeInverter) {
|
||||
|
||||
if (result.deviceType != SpeedwireInterface::DeviceTypeInverter)
|
||||
continue;
|
||||
|
||||
if (result.serialNumber == 0)
|
||||
continue;
|
||||
|
||||
ThingDescriptor descriptor(speedwireInverterThingClassId, Sma::getModelName(result.modelId), "Serial: " + QString::number(result.serialNumber) + " - " + result.address.toString());
|
||||
// We found an energy meter, let's check if we already added this one
|
||||
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());
|
||||
|
|
@ -170,13 +182,13 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
|||
descriptor.setParams(params);
|
||||
descriptors.append(descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
info->addThingDescriptors(descriptors);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
|
||||
speedwireDiscovery->startDiscovery();
|
||||
|
||||
} else if (info->thingClassId() == modbusInverterThingClassId) {
|
||||
if (!hardwareManager()->networkDeviceDiscovery()->available()) {
|
||||
qCWarning(dcSma()) << "The network discovery is not available on this platform.";
|
||||
|
|
@ -289,16 +301,20 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
|
|||
|
||||
} 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());
|
||||
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();
|
||||
}
|
||||
|
||||
SpeedwireMeter *meter = new SpeedwireMeter(address, modelId, serialNumber, this);
|
||||
SpeedwireMeter *meter = new SpeedwireMeter(m_multicastInterface, modelId, serialNumber, this);
|
||||
if (!meter->initialize()) {
|
||||
meter->deleteLater();
|
||||
qCWarning(dcSma()) << "Setup failed. Could not initialize meter interface.";
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
|
|
@ -522,6 +538,12 @@ void IntegrationPluginSma::thingRemoved(Thing *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()) {
|
||||
qCDebug(dcSma()) << "Stopping timer";
|
||||
hardwareManager()->pluginTimerManager()->unregisterTimer(m_refreshTimer);
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include "sunnywebbox/sunnywebbox.h"
|
||||
#include "speedwire/speedwiremeter.h"
|
||||
#include "speedwire/speedwireinverter.h"
|
||||
#include "speedwire/speedwireinterface.h"
|
||||
|
||||
#include "smainvertermodbustcpconnection.h"
|
||||
|
||||
|
|
@ -79,6 +80,9 @@ private:
|
|||
QHash<Thing *, SpeedwireInverter *> m_speedwireInverters;
|
||||
QHash<Thing *, SmaInverterModbusTcpConnection *> m_modbusInverters;
|
||||
|
||||
// Shared interface accross meters
|
||||
SpeedwireInterface *m_multicastInterface = nullptr;
|
||||
|
||||
// Sma modbus data validation
|
||||
bool isModbusValueValid(quint32 value);
|
||||
bool isModbusValueValid(qint32 value);
|
||||
|
|
|
|||
|
|
@ -90,23 +90,6 @@
|
|||
"createMethods": ["discovery", "user"],
|
||||
"interfaces": [ "energymeter" ],
|
||||
"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",
|
||||
"name": "serialNumber",
|
||||
|
|
|
|||
|
|
@ -93,6 +93,8 @@ public:
|
|||
case 5:
|
||||
revisionCharacter = 'S';
|
||||
break;
|
||||
default:
|
||||
revisionCharacter = QChar(revision);
|
||||
}
|
||||
|
||||
return QString("%1.%2.%3-%4").arg(major).arg(minor).arg(build).arg(revisionCharacter);
|
||||
|
|
|
|||
|
|
@ -39,33 +39,17 @@ SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDisc
|
|||
{
|
||||
// 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
|
||||
|
||||
|
||||
// // 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);
|
||||
m_multicastSearchRequestTimer.setInterval(1000);
|
||||
m_multicastSearchRequestTimer.setSingleShot(false);
|
||||
connect(&m_multicastSearchRequestTimer, &QTimer::timeout, this, &SpeedwireDiscovery::sendDiscoveryRequest);
|
||||
}
|
||||
|
||||
SpeedwireDiscovery::~SpeedwireDiscovery()
|
||||
{
|
||||
if (m_initialized) {
|
||||
if (m_initialized && m_multicastSocket) {
|
||||
if (!m_multicastSocket->leaveMulticastGroup(m_multicastAddress)) {
|
||||
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_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;
|
||||
m_deviceType = deviceType;
|
||||
switch(deviceType) {
|
||||
case SpeedwireInterface::DeviceTypeMeter:
|
||||
m_initialized = setupMulticastSocket();
|
||||
break;
|
||||
case SpeedwireInterface::DeviceTypeInverter:
|
||||
m_initialized = setupUnicastSocket();
|
||||
break;
|
||||
default:
|
||||
m_initialized = setupMulticastSocket() && setupUnicastSocket();
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_multicastSocket->joinMulticastGroup(m_multicastAddress)) {
|
||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_multicastSocket->errorString();
|
||||
return false;
|
||||
}
|
||||
if (m_initialized)
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Interfaces initialized successfully.";
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
|
@ -108,10 +86,7 @@ bool SpeedwireDiscovery::initialized() const
|
|||
|
||||
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)
|
||||
if (discoveryRunning())
|
||||
return true;
|
||||
|
||||
if (!m_initialized) {
|
||||
|
|
@ -119,31 +94,31 @@ bool SpeedwireDiscovery::startDiscovery()
|
|||
return false;
|
||||
}
|
||||
|
||||
// CLean up
|
||||
// Start clean
|
||||
m_results.clear();
|
||||
m_networkDeviceInfos.clear();
|
||||
m_multicastRunning = false;
|
||||
m_unicastRunning = false;
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Start discovering network...";
|
||||
NetworkDeviceDiscoveryReply *discoveryReply = m_networkDeviceDiscovery->discover();
|
||||
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());
|
||||
}
|
||||
|
||||
switch(m_deviceType) {
|
||||
case SpeedwireInterface::DeviceTypeMeter:
|
||||
startMulticastDiscovery();
|
||||
});
|
||||
break;
|
||||
case SpeedwireInterface::DeviceTypeInverter:
|
||||
startUnicastDiscovery();
|
||||
break;
|
||||
default:
|
||||
startUnicastDiscovery();
|
||||
startMulticastDiscovery();
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpeedwireDiscovery::discoveryRunning() const
|
||||
{
|
||||
return m_discoveryRunning;
|
||||
return m_unicastRunning || m_multicastRunning;
|
||||
}
|
||||
|
||||
QList<SpeedwireDiscovery::SpeedwireDiscoveryResult> SpeedwireDiscovery::discoveryResult() const
|
||||
|
|
@ -151,15 +126,92 @@ QList<SpeedwireDiscovery::SpeedwireDiscoveryResult> SpeedwireDiscovery::discover
|
|||
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()
|
||||
{
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Start multicast discovery...";
|
||||
m_multicastRunning = true;
|
||||
|
||||
// Start sending multicast messages
|
||||
sendDiscoveryRequest();
|
||||
|
||||
m_discoveryRunning = true;
|
||||
QTimer::singleShot(5000, this, &SpeedwireDiscovery::onDiscoveryProcessFinished);
|
||||
// Give 5 seconds time, on one of the requests sent in this period the meter would have responded...
|
||||
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)
|
||||
|
|
@ -206,7 +258,6 @@ void SpeedwireDiscovery::readPendingDatagramsUnicast()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void SpeedwireDiscovery::onSocketError(QAbstractSocket::SocketError error)
|
||||
{
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Socket error" << error;
|
||||
|
|
@ -252,8 +303,7 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
|
|||
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;
|
||||
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();
|
||||
}
|
||||
|
||||
SpeedwireDiscoveryResult result;
|
||||
|
|
@ -327,22 +377,43 @@ void SpeedwireDiscovery::sendDiscoveryRequest()
|
|||
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";
|
||||
m_discoveryTimer.stop();
|
||||
m_discoveryRunning = false;
|
||||
m_multicastSearchRequestTimer.stop();
|
||||
|
||||
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) {
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: ============================================";
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Device type:" << result.deviceType;
|
||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Address:" << result.address.toString();
|
||||
if (result.networkDeviceInfo.isValid()) {
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ public:
|
|||
explicit SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
|
||||
~SpeedwireDiscovery();
|
||||
|
||||
bool initialize();
|
||||
bool initialize(SpeedwireInterface::DeviceType deviceType = SpeedwireInterface::DeviceTypeUnknown);
|
||||
bool initialized() const;
|
||||
|
||||
bool startDiscovery();
|
||||
|
|
@ -68,6 +68,7 @@ signals:
|
|||
|
||||
private:
|
||||
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
|
||||
SpeedwireInterface::DeviceType m_deviceType = SpeedwireInterface::DeviceTypeUnknown;
|
||||
QUdpSocket *m_multicastSocket = nullptr;
|
||||
QUdpSocket *m_unicastSocket = nullptr;
|
||||
QHostAddress m_multicastAddress = Speedwire::multicastAddress();
|
||||
|
|
@ -75,12 +76,19 @@ private:
|
|||
bool m_initialized = false;
|
||||
|
||||
// Discovery
|
||||
QTimer m_discoveryTimer;
|
||||
bool m_discoveryRunning = false;
|
||||
QTimer m_multicastSearchRequestTimer;
|
||||
NetworkDeviceInfos m_networkDeviceInfos;
|
||||
QHash<QHostAddress, SpeedwireDiscoveryResult> m_results;
|
||||
|
||||
bool m_multicastRunning = false;
|
||||
bool m_unicastRunning = false;
|
||||
|
||||
bool setupMulticastSocket();
|
||||
void startMulticastDiscovery();
|
||||
|
||||
bool setupUnicastSocket();
|
||||
void startUnicastDiscovery();
|
||||
;
|
||||
void sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress);
|
||||
|
||||
private slots:
|
||||
|
|
@ -93,7 +101,9 @@ private slots:
|
|||
|
||||
void sendDiscoveryRequest();
|
||||
|
||||
void onDiscoveryProcessFinished();
|
||||
void evaluateDiscoveryFinished();
|
||||
|
||||
void finishDiscovery();
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,13 +31,11 @@
|
|||
#include "speedwireinterface.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
SpeedwireInterface::SpeedwireInterface(const QHostAddress &address, bool multicast, QObject *parent) :
|
||||
SpeedwireInterface::SpeedwireInterface(bool multicast, QObject *parent) :
|
||||
QObject(parent),
|
||||
m_address(address),
|
||||
m_multicast(multicast)
|
||||
{
|
||||
|
||||
qCDebug(dcSma()) << "SpeedwireInterface: Create interface for" << address.toString() << (multicast ? "multicast" : "unicast");
|
||||
m_socket = new QUdpSocket(this);
|
||||
connect(m_socket, &QUdpSocket::readyRead, this, &SpeedwireInterface::readPendingDatagrams);
|
||||
connect(m_socket, &QUdpSocket::stateChanged, this, &SpeedwireInterface::onSocketStateChanged);
|
||||
|
|
@ -49,8 +47,18 @@ SpeedwireInterface::~SpeedwireInterface()
|
|||
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)) {
|
||||
qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not bind to port" << m_port;
|
||||
return false;
|
||||
|
|
@ -61,7 +69,8 @@ bool SpeedwireInterface::initialize()
|
|||
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;
|
||||
return m_initialized;
|
||||
}
|
||||
|
|
@ -75,11 +84,17 @@ void SpeedwireInterface::deinitialize()
|
|||
}
|
||||
}
|
||||
|
||||
m_address = QHostAddress();
|
||||
m_socket->close();
|
||||
m_initialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool SpeedwireInterface::multicast() const
|
||||
{
|
||||
return m_multicast;
|
||||
}
|
||||
|
||||
bool SpeedwireInterface::initialized() const
|
||||
{
|
||||
return m_initialized;
|
||||
|
|
@ -113,13 +128,13 @@ void SpeedwireInterface::readPendingDatagrams()
|
|||
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)
|
||||
// Process only data coming from our target address if there is any
|
||||
if (!m_address.isNull() && 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);
|
||||
emit dataReceived(senderAddress, senderPort, datagram);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,12 +48,14 @@ public:
|
|||
};
|
||||
Q_ENUM(DeviceType)
|
||||
|
||||
explicit SpeedwireInterface(const QHostAddress &address, bool multicast, QObject *parent = nullptr);
|
||||
explicit SpeedwireInterface(bool multicast, QObject *parent = nullptr);
|
||||
~SpeedwireInterface();
|
||||
|
||||
bool initialize();
|
||||
bool initialize(const QHostAddress &address = QHostAddress());
|
||||
void deinitialize();
|
||||
|
||||
bool multicast() const;
|
||||
|
||||
bool initialized() const;
|
||||
|
||||
quint16 sourceModelId() const;
|
||||
|
|
@ -63,7 +65,7 @@ public slots:
|
|||
void sendData(const QByteArray &data);
|
||||
|
||||
signals:
|
||||
void dataReceived(const QByteArray &data);
|
||||
void dataReceived(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data);
|
||||
|
||||
private:
|
||||
QUdpSocket *m_socket = nullptr;
|
||||
|
|
|
|||
|
|
@ -40,13 +40,13 @@ SpeedwireInverter::SpeedwireInverter(const QHostAddress &address, quint16 modelI
|
|||
m_serialNumber(serialNumber)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
bool SpeedwireInverter::initialize()
|
||||
{
|
||||
return m_interface->initialize();
|
||||
return m_interface->initialize(m_address);
|
||||
}
|
||||
|
||||
bool SpeedwireInverter::initialized() const
|
||||
|
|
@ -976,8 +976,12 @@ void SpeedwireInverter::setReachable(bool 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) {
|
||||
qCDebug(dcSma()) << "Inverter: The received datagram is to short to be a SMA speedwire message. Ignoring data...";
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ private:
|
|||
void setReachable(bool reachable);
|
||||
|
||||
private slots:
|
||||
void processData(const QByteArray &data);
|
||||
void processData(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data);
|
||||
|
||||
void onReplyTimeout();
|
||||
void onReplyFinished();
|
||||
|
|
|
|||
|
|
@ -33,13 +33,12 @@
|
|||
|
||||
#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),
|
||||
m_address(address),
|
||||
m_interface(multicastInterface),
|
||||
m_modelId(modelId),
|
||||
m_serialNumber(serialNumber)
|
||||
{
|
||||
m_interface = new SpeedwireInterface(m_address, true, this);
|
||||
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireMeter::processData);
|
||||
|
||||
// Reachable timestamp
|
||||
|
|
@ -165,10 +164,9 @@ QString SpeedwireMeter::softwareVersion() const
|
|||
void SpeedwireMeter::evaluateReachable()
|
||||
{
|
||||
// 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
|
||||
bool reachable = false;
|
||||
if (currentTimestamp - m_lastSeenTimestamp < 10) {
|
||||
if (QDateTime::currentDateTime().toMSecsSinceEpoch() - m_lastSeenTimestamp < 5000) {
|
||||
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);
|
||||
stream.setByteOrder(QDataStream::BigEndian);
|
||||
|
||||
|
|
@ -200,27 +197,30 @@ void SpeedwireMeter::processData(const QByteArray &data)
|
|||
return;
|
||||
}
|
||||
|
||||
if (header.protocolId != Speedwire::ProtocolIdMeter) {
|
||||
qCDebug(dcSma()) << "Meter: received header protocol which is not from the meter protocol. Ignoring data...";
|
||||
if (header.protocolId != Speedwire::ProtocolIdMeter)
|
||||
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;
|
||||
// Make sure this payload belongs to us
|
||||
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
|
||||
// Timestamp e618a416
|
||||
qCDebug(dcSma()) << "Meter: ======================= Meter measurements";
|
||||
quint32 timestamp;
|
||||
stream >> timestamp;
|
||||
qCDebug(dcSma()) << "Meter: Timestamp:" << timestamp;
|
||||
qCDebug(dcSma()) << "Meter: Timestamp:" << timestamp << QDateTime::fromMSecsSinceEpoch(timestamp * 1000);
|
||||
|
||||
// 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
|
||||
|
|
@ -323,7 +323,6 @@ void SpeedwireMeter::processData(const QByteArray &data)
|
|||
quint32 versionData;
|
||||
stream >> versionData;
|
||||
m_softwareVersion = Sma::buildSoftwareVersionString(versionData);
|
||||
|
||||
qCDebug(dcSma()) << "Meter: Software version" << m_softwareVersion;
|
||||
} else if (measurementChannel == 0 && measurementIndex == 0 && measurmentType == 0 && measurmentTariff == 0) {
|
||||
// 00 00 00 00
|
||||
|
|
@ -332,7 +331,7 @@ void SpeedwireMeter::processData(const QByteArray &data)
|
|||
}
|
||||
|
||||
// Save the current timestamp for reachable evaluation
|
||||
m_lastSeenTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch() / 1000;
|
||||
m_lastSeenTimestamp = QDateTime::currentDateTime().toMSecsSinceEpoch();
|
||||
evaluateReachable();
|
||||
|
||||
emit valuesUpdated();
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class SpeedwireMeter : public QObject
|
|||
{
|
||||
Q_OBJECT
|
||||
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 initialized() const;
|
||||
|
|
@ -118,7 +118,7 @@ private:
|
|||
|
||||
private slots:
|
||||
void evaluateReachable();
|
||||
void processData(const QByteArray &data);
|
||||
void processData(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue