SMA: Improve speedwire interface handling and sharing

pull/142/head
Simon Stürz 2023-07-18 14:19:44 +02:00
parent 8e32831047
commit c5f3352c3e
11 changed files with 253 additions and 409 deletions

View File

@ -94,15 +94,15 @@ 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(), getLocalSerialNumber(), info);
if (!speedwireDiscovery->initialize()) {
SpeedwireInterface *speedwireInterface = getSpeedwireInterface();
if (!speedwireInterface || !speedwireInterface->available()) {
qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed.";
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network."));
return;
}
// Note: does not require the network device discovery...
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), speedwireInterface, getLocalSerialNumber(), info);
connect(speedwireDiscovery, &SpeedwireDiscovery::discoveryFinished, this, [=](){
qCDebug(dcSma()) << "Speed wire discovery finished.";
speedwireDiscovery->deleteLater();
@ -110,7 +110,7 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
ThingDescriptors descriptors;
foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) {
if (result.deviceType != SpeedwireInterface::DeviceTypeMeter)
if (result.deviceType != Speedwire::DeviceTypeMeter)
continue;
if (result.serialNumber == 0)
@ -151,13 +151,14 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
return;
}
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), getLocalSerialNumber(), info);
if (!speedwireDiscovery->initialize()) {
SpeedwireInterface *speedwireInterface = getSpeedwireInterface();
if (!speedwireInterface || !speedwireInterface->available()) {
qCWarning(dcSma()) << "Could not discovery inverter. The speedwire interface initialization failed.";
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to discover the network."));
return;
}
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), speedwireInterface, getLocalSerialNumber(), info);
connect(speedwireDiscovery, &SpeedwireDiscovery::discoveryFinished, this, [=](){
qCDebug(dcSma()) << "Speed wire discovery finished.";
speedwireDiscovery->deleteLater();
@ -165,7 +166,7 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
ThingDescriptors descriptors;
foreach (const SpeedwireDiscovery::SpeedwireDiscoveryResult &result, speedwireDiscovery->discoveryResult()) {
if (result.deviceType != SpeedwireInterface::DeviceTypeInverter)
if (result.deviceType != Speedwire::DeviceTypeInverter)
continue;
if (result.serialNumber == 0)
@ -302,9 +303,12 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
} else if (thing->thingClassId() == speedwireMeterThingClassId) {
// Create the multicast interface if not created already.
if (!m_multicastInterface)
m_multicastInterface = new SpeedwireInterface(true, getLocalSerialNumber(), this);
SpeedwireInterface *speedwireInterface = getSpeedwireInterface();
if (!speedwireInterface || !speedwireInterface->available()) {
qCWarning(dcSma()) << "Could not set up speedwire meter. The speedwire interface is not available.";
info->finish(Thing::ThingErrorHardwareFailure, QT_TR_NOOP("Unable to communicate with the meter."));
return;
}
quint32 serialNumber = static_cast<quint32>(thing->paramValue(speedwireMeterThingSerialNumberParamTypeId).toUInt());
quint16 modelId = static_cast<quint16>(thing->paramValue(speedwireMeterThingModelIdParamTypeId).toUInt());
@ -313,14 +317,7 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
if (m_speedwireMeters.contains(thing))
m_speedwireMeters.take(thing)->deleteLater();
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;
}
SpeedwireMeter *meter = new SpeedwireMeter(speedwireInterface, modelId, serialNumber, this);
connect(meter, &SpeedwireMeter::reachableChanged, thing, [=](bool reachable){
thing->setStateValue(speedwireMeterConnectedStateTypeId, reachable);
if (!reachable) {
@ -358,6 +355,9 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
} else if (thing->thingClassId() == speedwireInverterThingClassId) {
QHostAddress address = QHostAddress(thing->paramValue(speedwireInverterThingHostParamTypeId).toString());
// FIXME: use the monitor here since the IP might change
quint32 serialNumber = static_cast<quint32>(thing->paramValue(speedwireInverterThingSerialNumberParamTypeId).toUInt());
quint16 modelId = static_cast<quint16>(thing->paramValue(speedwireInverterThingModelIdParamTypeId).toUInt());
@ -365,13 +365,7 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
m_speedwireInverters.take(thing)->deleteLater();
}
SpeedwireInverter *inverter = new SpeedwireInverter(address, modelId, serialNumber, this);
if (!inverter->initialize()) {
qCWarning(dcSma()) << "Setup failed. Could not initialize inverter interface.";
info->finish(Thing::ThingErrorHardwareFailure);
return;
}
SpeedwireInverter *inverter = new SpeedwireInverter(getSpeedwireInterface(), address, modelId, serialNumber, this);
qCDebug(dcSma()) << "Inverter: Interface initialized successfully.";
QString password;
@ -620,10 +614,12 @@ void IntegrationPluginSma::thingRemoved(Thing *thing)
hardwareManager()->networkDeviceDiscovery()->unregisterMonitor(m_monitors.take(thing));
}
if (myThings().filterByThingClassId(speedwireMeterThingClassId).isEmpty() && m_multicastInterface) {
if (myThings().filterByThingClassId(speedwireMeterThingClassId).isEmpty()
&& myThings().filterByThingClassId(speedwireInverterThingClassId).isEmpty()
&& myThings().filterByThingClassId(speedwireBatteryThingClassId).isEmpty()) {
// Delete shared multicast socket...
m_multicastInterface->deleteLater();
m_multicastInterface = nullptr;
m_speedwireInterface->deleteLater();
m_speedwireInterface = nullptr;
}
if (myThings().isEmpty()) {
@ -823,6 +819,17 @@ void IntegrationPluginSma::setupModbusInverterConnection(ThingSetupInfo *info)
connection->connectDevice();
}
SpeedwireInterface *IntegrationPluginSma::getSpeedwireInterface()
{
if (!m_speedwireInterface)
m_speedwireInterface = new SpeedwireInterface(getLocalSerialNumber(), this);
if (!m_speedwireInterface->available())
m_speedwireInterface->initialize();
return m_speedwireInterface;
}
void IntegrationPluginSma::markSpeedwireMeterAsDisconnected(Thing *thing)
{
thing->setStateValue(speedwireMeterCurrentPowerPhaseAStateTypeId, 0);

View File

@ -82,8 +82,9 @@ private:
quint32 m_localSerialNumber = 0;
// Shared interface accross meters
SpeedwireInterface *m_multicastInterface = nullptr;
// Shared interface accross all speedwire devices
SpeedwireInterface *m_speedwireInterface = nullptr;
SpeedwireInterface *getSpeedwireInterface();
void markSpeedwireMeterAsDisconnected(Thing *thing);
void markSpeedwireInverterAsDisconnected(Thing *thing);

View File

@ -41,6 +41,13 @@ class Speedwire
{
Q_GADGET
public:
enum DeviceType {
DeviceTypeUnknown,
DeviceTypeMeter,
DeviceTypeInverter
};
Q_ENUM(DeviceType)
enum Command {
CommandIdentify = 0x00000201,
CommandQueryStatus = 0x51800200,

View File

@ -30,12 +30,14 @@
#include "speedwirediscovery.h"
#include "extern-plugininfo.h"
#include "speedwire.h"
#include <QDataStream>
SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint32 localSerialNumber, QObject *parent) :
SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, SpeedwireInterface *speedwireInterface, quint32 localSerialNumber, QObject *parent) :
QObject(parent),
m_networkDeviceDiscovery(networkDeviceDiscovery),
m_speedwireInterface(speedwireInterface),
m_localSerialNumber(localSerialNumber)
{
// More details: https://github.com/RalfOGit/libspeedwire/
@ -45,44 +47,14 @@ SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDisc
m_multicastSearchRequestTimer.setInterval(1000);
m_multicastSearchRequestTimer.setSingleShot(false);
connect(m_speedwireInterface, &SpeedwireInterface::dataReceived, this, &SpeedwireDiscovery::processDatagram);
connect(&m_multicastSearchRequestTimer, &QTimer::timeout, this, &SpeedwireDiscovery::sendDiscoveryRequest);
}
SpeedwireDiscovery::~SpeedwireDiscovery()
{
if (m_initialized && m_multicastSocket) {
if (!m_multicastSocket->leaveMulticastGroup(Speedwire::multicastAddress())) {
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << Speedwire::multicastAddress().toString();
}
m_multicastSocket->close();
}
}
bool SpeedwireDiscovery::initialize(SpeedwireInterface::DeviceType deviceType)
{
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_initialized)
qCDebug(dcSma()) << "SpeedwireDiscovery: Interfaces initialized successfully.";
return m_initialized;
}
bool SpeedwireDiscovery::initialized() const
{
return m_initialized;
}
bool SpeedwireDiscovery::startDiscovery()
@ -90,20 +62,17 @@ bool SpeedwireDiscovery::startDiscovery()
if (discoveryRunning())
return true;
if (!m_initialized) {
qCDebug(dcSma()) << "SpeedwireDiscovery: Failed to start discovery because the socket has not been initialized successfully.";
if (!m_speedwireInterface->available()) {
qCDebug(dcSma()) << "SpeedwireDiscovery: Failed to start discovery because the speedwire interface is not available.";
return false;
}
// Start clean
m_results.clear();
m_networkDeviceInfos.clear();
m_multicastRunning = false;
m_unicastRunning = false;
startUnicastDiscovery();
startMulticastDiscovery();
return true;
}
@ -117,49 +86,6 @@ QList<SpeedwireDiscovery::SpeedwireDiscoveryResult> SpeedwireDiscovery::discover
return m_results;
}
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, Speedwire::port(), QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind multicast socket to port" << Speedwire::port() << m_multicastSocket->errorString();
m_multicastSocket->deleteLater();
m_multicastSocket = nullptr;
return false;
}
// Join all available interfaces the multicast group
foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
if(interface.isValid() && !interface.flags().testFlag(QNetworkInterface::IsLoopBack)
&& interface.flags().testFlag(QNetworkInterface::CanMulticast)
&& interface.flags().testFlag(QNetworkInterface::IsRunning)) {
QList<QNetworkAddressEntry> addressEntries = interface.addressEntries();
for (int i = 0; i < addressEntries.length(); i++) {
if (addressEntries.at(i).ip().protocol() == QAbstractSocket::IPv4Protocol) {
if (!m_multicastSocket->joinMulticastGroup(Speedwire::multicastAddress(), interface)) {
qCWarning(dcSma()) << "SpeedwireDiscovery: Could not join multicast group" << Speedwire::multicastAddress().toString() << "on interface" << interface << m_multicastSocket->errorString();
} else {
qCDebug(dcSma()) << "SpeedwireDiscovery: Joined successfully multicast group on" << interface;
}
}
}
}
}
return true;
}
void SpeedwireDiscovery::startMulticastDiscovery()
{
qCDebug(dcSma()) << "SpeedwireDiscovery: Start multicast discovery...";
@ -170,26 +96,6 @@ void SpeedwireDiscovery::startMulticastDiscovery()
sendDiscoveryRequest();
}
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, Speedwire::port(), QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind to port" << Speedwire::port() << m_multicastSocket->errorString();
m_unicastSocket->deleteLater();
m_unicastSocket = nullptr;
return false;
}
return true;
}
void SpeedwireDiscovery::startUnicastDiscovery()
{
@ -218,70 +124,14 @@ void SpeedwireDiscovery::startUnicastDiscovery()
void SpeedwireDiscovery::sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress)
{
if (m_unicastSocket->writeDatagram(Speedwire::pingRequest(Speedwire::sourceModelId(), m_localSerialNumber), targetHostAddress, Speedwire::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();
qCDebug(dcSma()) << "SpeedwireDiscovery: Sent discovery request to unicast address" << targetHostAddress.toString();
m_speedwireInterface->sendDataUnicast(targetHostAddress, Speedwire::pingRequest(Speedwire::sourceModelId(), m_localSerialNumber));
}
void SpeedwireDiscovery::readPendingDatagramsMulticast()
void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &datagram, bool multicast)
{
QUdpSocket *socket = qobject_cast<QUdpSocket *>(sender());
Q_UNUSED(multicast)
QByteArray datagram;
QHostAddress senderAddress;
quint16 senderPort;
while (socket->hasPendingDatagrams()) {
datagram.resize(socket->pendingDatagramSize());
socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
// Ignore our own requests
if (QNetworkInterface::allAddresses().contains(senderAddress))
continue;
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);
// Ignore our own requests
if (QNetworkInterface::allAddresses().contains(senderAddress))
continue;
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...";
@ -324,7 +174,7 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
if (!m_resultMeters.contains(senderAddress)) {
SpeedwireDiscoveryResult result;
result.address = senderAddress;
result.deviceType = SpeedwireInterface::DeviceTypeMeter;
result.deviceType = Speedwire::DeviceTypeMeter;
m_resultMeters.insert(senderAddress, result);
}
@ -342,7 +192,7 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
if (!m_resultInverters.contains(senderAddress)) {
SpeedwireDiscoveryResult result;
result.address = senderAddress;
result.deviceType = SpeedwireInterface::DeviceTypeInverter;
result.deviceType = Speedwire::DeviceTypeInverter;
m_resultInverters.insert(senderAddress, result);
}
@ -359,20 +209,20 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
if (m_inverters.contains(senderAddress)) {
inverter = m_inverters.value(senderAddress);
} else {
inverter = new SpeedwireInverter(senderAddress, Speedwire::sourceModelId(), m_localSerialNumber, this);
inverter = new SpeedwireInverter(m_speedwireInterface, senderAddress, Speedwire::sourceModelId(), m_localSerialNumber, this);
m_inverters.insert(senderAddress, inverter);
}
SpeedwireInverterReply *reply = inverter->sendIdentifyRequest();
qCDebug(dcSma()) << "SpeedwireDiscovery: send identify request to" << senderAddress.toString();
connect(reply, &SpeedwireInverterReply::finished, this, [this, inverter, senderAddress, reply](){
qCDebug(dcSma()) << "SpeedwireDiscovery: identify request finished from" << senderAddress.toString() << reply->error();
qCDebug(dcSma()) << "SpeedwireDiscovery: Send identify request to" << senderAddress.toString();
connect(reply, &SpeedwireInverterReply::finished, this, [/*this, inverter,*/ senderAddress, reply](){
qCDebug(dcSma()) << "SpeedwireDiscovery: Identify request finished from" << senderAddress.toString() << reply->error();
SpeedwireInverterReply *loginReply = inverter->sendLoginRequest();
qCDebug(dcSma()) << "SpeedwireDiscovery: make login attempt using the default password.";
connect(loginReply, &SpeedwireInverterReply::finished, this, [loginReply, senderAddress](){
qCDebug(dcSma()) << "SpeedwireDiscovery: login attempt finished" << senderAddress.toString() << loginReply->error();
});
// SpeedwireInverterReply *loginReply = inverter->sendLoginRequest();
// qCDebug(dcSma()) << "SpeedwireDiscovery: make login attempt using the default password.";
// connect(loginReply, &SpeedwireInverterReply::finished, this, [loginReply, senderAddress](){
// qCDebug(dcSma()) << "SpeedwireDiscovery: login attempt finished" << senderAddress.toString() << loginReply->error();
// });
});
} else {
@ -383,12 +233,8 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
void SpeedwireDiscovery::sendDiscoveryRequest()
{
if (m_multicastSocket->writeDatagram(Speedwire::discoveryDatagramMulticast(), Speedwire::multicastAddress(), Speedwire::port()) < 0) {
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to send discovery datagram to multicast address" << Speedwire::multicastAddress().toString();
return;
}
qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to multicast address" << Speedwire::multicastAddress().toString();
qCDebug(dcSma()) << "SpeedwireDiscovery: Sent discovery request to multicast address" << Speedwire::multicastAddress().toString();
m_speedwireInterface->sendDataMulticast(Speedwire::discoveryDatagramMulticast());
}
void SpeedwireDiscovery::evaluateDiscoveryFinished()
@ -405,23 +251,6 @@ void SpeedwireDiscovery::finishDiscovery()
qCDebug(dcSma()) << "SpeedwireDiscovery: Discovey finished. Found" << m_results.count() << "SMA devices in the network";
m_multicastSearchRequestTimer.stop();
if (m_multicastSocket) {
if (!m_multicastSocket->leaveMulticastGroup(Speedwire::multicastAddress())) {
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << Speedwire::multicastAddress().toString();
}
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;

View File

@ -48,17 +48,14 @@ public:
typedef struct SpeedwireDiscoveryResult {
QHostAddress address;
NetworkDeviceInfo networkDeviceInfo;
SpeedwireInterface::DeviceType deviceType = SpeedwireInterface::DeviceTypeUnknown;
Speedwire::DeviceType deviceType = Speedwire::DeviceTypeUnknown;
quint16 modelId = 0;
quint32 serialNumber = 0;
} SpeedwireDiscoveryResult;
explicit SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint32 localSerialNumber, QObject *parent = nullptr);
explicit SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, SpeedwireInterface *speedwireInterface, quint32 localSerialNumber, QObject *parent = nullptr);
~SpeedwireDiscovery();
bool initialize(SpeedwireInterface::DeviceType deviceType = SpeedwireInterface::DeviceTypeUnknown);
bool initialized() const;
bool startDiscovery();
bool discoveryRunning() const;
@ -69,11 +66,8 @@ signals:
private:
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
SpeedwireInterface::DeviceType m_deviceType = SpeedwireInterface::DeviceTypeUnknown;
QUdpSocket *m_multicastSocket = nullptr;
QUdpSocket *m_unicastSocket = nullptr;
SpeedwireInterface *m_speedwireInterface = nullptr;
quint32 m_localSerialNumber = 0;
bool m_initialized = false;
// Discovery
QTimer m_multicastSearchRequestTimer;
@ -81,34 +75,21 @@ private:
QList<SpeedwireDiscoveryResult> m_results;
QHash<QHostAddress, SpeedwireDiscoveryResult> m_resultMeters;
QHash<QHostAddress, SpeedwireDiscoveryResult> m_resultInverters;
bool m_unicastRunning = false;
bool m_multicastRunning = false;
QHash<QHostAddress, SpeedwireInverter *> m_inverters;
bool m_multicastRunning = false;
bool m_unicastRunning = false;
bool setupMulticastSocket();
void startMulticastDiscovery();
bool setupUnicastSocket();
void startUnicastDiscovery();
void sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress);
private slots:
void readPendingDatagramsMulticast();
void readPendingDatagramsUnicast();
void onSocketError(QAbstractSocket::SocketError error);
void onSocketStateChanged(QAbstractSocket::SocketState socketState);
void startUnicastDiscovery();
void startMulticastDiscovery();
void processDatagram(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &datagram);
void processDatagram(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &datagram, bool multicast);
void sendDiscoveryRequest();
void evaluateDiscoveryFinished();
void finishDiscovery();
};
#endif // SPEEDWIREDISCOVERY_H

View File

@ -31,74 +31,117 @@
#include "speedwireinterface.h"
#include "extern-plugininfo.h"
SpeedwireInterface::SpeedwireInterface(bool multicast, quint32 sourceSerialNumber, QObject *parent) :
#include <QNetworkInterface>
SpeedwireInterface::SpeedwireInterface(quint32 sourceSerialNumber, QObject *parent) :
QObject(parent),
m_multicast(multicast),
m_sourceSerialNumber(sourceSerialNumber)
{
m_unicast = new QUdpSocket(this);
connect(m_unicast, &QUdpSocket::readyRead, this, [=](){
QByteArray datagram;
QHostAddress senderAddress;
quint16 senderPort;
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)));
while (m_unicast->hasPendingDatagrams()) {
datagram.resize(m_unicast->pendingDatagramSize());
m_unicast->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
qCDebug(dcSma()).noquote() << "SpeedwireInterface: Unicast socket received data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort);
qCDebug(dcSma()) << "SpeedwireInterface: " << datagram.toHex();
emit dataReceived(senderAddress, senderPort, datagram, false);
}
});
connect(m_unicast, &QUdpSocket::stateChanged, this, [=](QAbstractSocket::SocketState socketState){
qCDebug(dcSma()) << "SpeedwireInterface: Unicast socket state changed" << socketState;
});
connect(m_unicast, static_cast<void (QUdpSocket::*)( QAbstractSocket::SocketError )>(&QAbstractSocket::error), this, [=](QAbstractSocket::SocketError error){
qCWarning(dcSma()) << "SpeedwireInterface: Unicast socket error occurred" << error << m_unicast->errorString();
});
m_multicast = new QUdpSocket(this);
connect(m_multicast, &QUdpSocket::readyRead, this, [=](){
QByteArray datagram;
QHostAddress senderAddress;
quint16 senderPort;
while (m_multicast->hasPendingDatagrams()) {
datagram.resize(m_multicast->pendingDatagramSize());
m_multicast->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
qCDebug(dcSma()).noquote() << "SpeedwireInterface: Multicast socket received data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort);
//qCDebug(dcSma()) << "SpeedwireInterface: " << datagram.toHex();
emit dataReceived(senderAddress, senderPort, datagram, true);
}
});
connect(m_multicast, &QUdpSocket::stateChanged, this, [=](QAbstractSocket::SocketState socketState){
qCDebug(dcSma()) << "SpeedwireInterface: Multicast socket state changed" << socketState;
});
connect(m_multicast, static_cast<void (QUdpSocket::*)( QAbstractSocket::SocketError )>(&QAbstractSocket::error), this, [=](QAbstractSocket::SocketError error){
qCWarning(dcSma()) << "SpeedwireInterface: Multicast socket error occurred" << error << m_multicast->errorString();
});
if (initialize()) {
qCDebug(dcSma()) << "SpeedwireInterface: Initialized sucessfully unicast and multicast interface.";
} else {
qCWarning(dcSma()) << "SpeedwireInterface: Failed to initialize.";
}
}
SpeedwireInterface::~SpeedwireInterface()
{
deinitialize();
}
if (m_unicast)
m_unicast->close();
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 have changed...done here
if (m_initialized && m_multicast)
return true;
if (!m_socket->bind(QHostAddress::AnyIPv4, Speedwire::port(), QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not bind to port" << Speedwire::port();
return false;
}
if (m_multicast && !m_socket->joinMulticastGroup(Speedwire::multicastAddress())) {
qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not join multicast group" << Speedwire::multicastAddress().toString() << m_socket->errorString();
return false;
}
qCDebug(dcSma()) << "SpeedwireInterface: Interface initialized successfully for" << address.toString() << (m_multicast ? "multicast" : "unicast");
m_address = address;
m_initialized = true;
return m_initialized;
}
void SpeedwireInterface::deinitialize()
{
if (m_initialized) {
if (m_multicast) {
if (!m_socket->leaveMulticastGroup(Speedwire::multicastAddress())) {
qCWarning(dcSma()) << "SpeedwireInterface: Failed to leave multicast group" << Speedwire::multicastAddress().toString();
}
if (m_multicast) {
if (!m_multicast->leaveMulticastGroup(Speedwire::multicastAddress())) {
qCWarning(dcSma()) << "SpeedwireInterface: Failed to leave multicast group" << Speedwire::multicastAddress().toString();
}
m_address = QHostAddress();
m_socket->close();
m_initialized = false;
m_multicast->close();
}
}
bool SpeedwireInterface::multicast() const
bool SpeedwireInterface::available() const
{
return m_multicast;
return m_available;
}
bool SpeedwireInterface::initialized() const
void SpeedwireInterface::reconfigureMulticastGroup()
{
return m_initialized;
qCDebug(dcSma()) << "Reconfigure multicast interfaces";
if (m_multicast->joinMulticastGroup(Speedwire::multicastAddress())) {
qCDebug(dcSma()) << "SpeedwireInterface: Joined successfully multicast group" << Speedwire::multicastAddress().toString();
} else {
qCWarning(dcSma()) << "SpeedwireInterface: Failed to join multicast group" << Speedwire::multicastAddress().toString() << m_multicast->errorString() << "Retrying in 5 seconds...";
// FIXME: It would probably be better to monitor the network interfaces and re-join if necessary
QTimer::singleShot(5000, this, &SpeedwireInterface::reconfigureMulticastGroup);
}
// foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
// if(interface.isValid() && !interface.flags().testFlag(QNetworkInterface::IsLoopBack)
// && interface.flags().testFlag(QNetworkInterface::CanMulticast)
// && interface.flags().testFlag(QNetworkInterface::IsRunning)) {
// QList<QNetworkAddressEntry> addressEntries = interface.addressEntries();
// for (int i = 0; i < addressEntries.length(); i++) {
// if (addressEntries.at(i).ip().protocol() == QAbstractSocket::IPv4Protocol) {
// if (!m_multicast->joinMulticastGroup(Speedwire::multicastAddress(), interface)) {
// qCWarning(dcSma()) << "SpeedwireInterface: Could not join multicast group" << Speedwire::multicastAddress().toString() << "on interface" << interface << m_multicast->errorString();
// } else {
// qCDebug(dcSma()) << "SpeedwireInterface: Joined successfully multicast group" << Speedwire::multicastAddress().toString() << "on interface" << interface ;
// }
// }
// }
// }
// }
// qCDebug(dcSma()) << "Multicast outgoing interface" << m_multicast->multicastInterface();
}
quint32 SpeedwireInterface::sourceSerialNumber() const
@ -106,40 +149,53 @@ quint32 SpeedwireInterface::sourceSerialNumber() const
return m_sourceSerialNumber;
}
void SpeedwireInterface::sendData(const QByteArray &data)
bool SpeedwireInterface::initialize()
{
qCDebug(dcSma()) << "SpeedwireInterface: -->" << m_address.toString() << Speedwire::port() << data.toHex();
if (m_socket->writeDatagram(data, m_address, Speedwire::port()) < 0) {
qCWarning(dcSma()) << "SpeedwireInterface: failed to send data" << m_socket->errorString();
bool success = true;
if (m_unicast->state() != QUdpSocket::BoundState) {
m_unicast->close();
if (!m_unicast->bind(QHostAddress::AnyIPv4, Speedwire::port(), QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
qCWarning(dcSma()) << "SpeedwireInterface: Unicast socket could not be bound to port" << Speedwire::port();
success = false;
}
}
if (m_multicast->state() != QUdpSocket::BoundState) {
if (!m_multicast->bind(QHostAddress::AnyIPv4, Speedwire::port(), QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
qCWarning(dcSma()) << "SpeedwireInterface: Unicast socket could not be bound to port" << Speedwire::port();
success = false;
}
}
reconfigureMulticastGroup();
m_available = success;
return success;
}
void SpeedwireInterface::sendDataUnicast(const QHostAddress &address, const QByteArray &data)
{
qCDebug(dcSma()) << "SpeedwireInterface: Unicast -->" << address.toString() << Speedwire::port() << data.toHex();
if (!m_unicast) {
qCWarning(dcSma()) << "SpeedwireInterface: Failed to send unicast data, the socket is not available";
return;
}
if (m_unicast->writeDatagram(data, address, Speedwire::port()) < 0) {
qCWarning(dcSma()) << "SpeedwireInterface: Failed to send unicast data to" << address.toString() << m_unicast->errorString();
}
}
void SpeedwireInterface::readPendingDatagrams()
void SpeedwireInterface::sendDataMulticast(const QByteArray &data)
{
QByteArray datagram;
QHostAddress senderAddress;
quint16 senderPort;
qCDebug(dcSma()) << "SpeedwireInterface: Multicast -->" << Speedwire::multicastAddress().toString() << Speedwire::port() << data.toHex();
if (!m_multicast) {
qCWarning(dcSma()) << "SpeedwireInterface: Failed to send multicast data, the socket is not available";
return;
}
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 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(senderAddress, senderPort, datagram);
if (m_multicast->writeDatagram(data, Speedwire::multicastAddress(), Speedwire::port()) < 0) {
qCWarning(dcSma()) << "SpeedwireInterface: Failed to send multicast data to" << Speedwire::multicastAddress().toString() << m_multicast->errorString();
}
}
void SpeedwireInterface::onSocketError(QAbstractSocket::SocketError error)
{
qCDebug(dcSma()) << "SpeedwireInterface: Socket error" << error;
}
void SpeedwireInterface::onSocketStateChanged(QAbstractSocket::SocketState socketState)
{
qCDebug(dcSma()) << "SpeedwireInterface: Socket state changed" << socketState;
}

View File

@ -34,6 +34,7 @@
#include <QObject>
#include <QUdpSocket>
#include <QDataStream>
#include <QTimer>
#include "speedwire.h"
@ -41,46 +42,32 @@ class SpeedwireInterface : public QObject
{
Q_OBJECT
public:
enum DeviceType {
DeviceTypeUnknown,
DeviceTypeMeter,
DeviceTypeInverter
};
Q_ENUM(DeviceType)
explicit SpeedwireInterface(bool multicast, quint32 sourceSerialNumber, QObject *parent = nullptr);
explicit SpeedwireInterface(quint32 sourceSerialNumber, QObject *parent = nullptr);
~SpeedwireInterface();
bool initialize(const QHostAddress &address = QHostAddress());
void deinitialize();
bool available() const;
bool multicast() const;
bool initialized() const;
quint16 sourceModelId() const;
quint32 sourceSerialNumber() const;
bool initialize();
public slots:
void sendData(const QByteArray &data);
void sendDataUnicast(const QHostAddress &address, const QByteArray &data);
void sendDataMulticast(const QByteArray &data);
signals:
void dataReceived(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data);
private:
QUdpSocket *m_socket = nullptr;
QHostAddress m_address;
bool m_multicast = false;
bool m_initialized = false;
// Requester
quint32 m_sourceSerialNumber = 0;
void dataReceived(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data, bool multicast = false);
private slots:
void readPendingDatagrams();
void onSocketError(QAbstractSocket::SocketError error);
void onSocketStateChanged(QAbstractSocket::SocketState socketState);
void reconfigureMulticastGroup();
private:
QUdpSocket *m_unicast = nullptr;
QUdpSocket *m_multicast = nullptr;
quint32 m_sourceSerialNumber = 0;
bool m_available = false;
QTimer m_multicastReconfigureationTimer;
};

View File

@ -33,25 +33,15 @@
#include <QDateTime>
SpeedwireInverter::SpeedwireInverter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent) :
SpeedwireInverter::SpeedwireInverter(SpeedwireInterface *speedwireInterface, const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent) :
QObject(parent),
m_speedwireInterface(speedwireInterface),
m_address(address),
m_modelId(modelId),
m_serialNumber(serialNumber)
{
qCDebug(dcSma()) << "Inverter: setup interface on" << m_address.toString();
m_interface = new SpeedwireInterface(false, serialNumber, this);
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireInverter::processData);
}
bool SpeedwireInverter::initialize()
{
return m_interface->initialize(m_address);
}
bool SpeedwireInverter::initialized() const
{
return m_interface->initialized();
connect(m_speedwireInterface, &SpeedwireInterface::dataReceived, this, &SpeedwireInverter::processData);
}
SpeedwireInverter::State SpeedwireInverter::state() const
@ -270,7 +260,7 @@ SpeedwireInverterReply *SpeedwireInverter::sendLogoutRequest()
// Source
stream << Speedwire::sourceModelId();
stream << m_interface->sourceSerialNumber();
stream << m_speedwireInterface->sourceSerialNumber();
stream << static_cast<quint16>(0x0300);
stream << static_cast<quint16>(0);
@ -413,7 +403,7 @@ void SpeedwireInverter::sendNextReply()
// Pick the next reply and send request
m_currentReply = m_replyQueue.dequeue();
qCDebug(dcSma()) << "Inverter: --> Sending" << m_currentReply->request().command() << "packet ID:" << m_currentReply->request().packetId();
m_interface->sendData(m_currentReply->request().requestData());
m_speedwireInterface->sendDataUnicast(m_address, m_currentReply->request().requestData());
m_currentReply->startWaiting();
}
@ -489,7 +479,7 @@ void SpeedwireInverter::buildPacket(QDataStream &stream, quint32 command, quint1
// Destination Ctrl
stream << static_cast<quint16>(0x0100);
// Source
stream << Speedwire::sourceModelId() << m_interface->sourceSerialNumber();
stream << Speedwire::sourceModelId() << m_speedwireInterface->sourceSerialNumber();
// Destination Ctrl
stream << static_cast<quint16>(0x0100);
@ -1176,10 +1166,12 @@ void SpeedwireInverter::setBatteryAvailable(bool available)
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)
// Process only data coming from our target address if there is any
if (!m_address.isNull() && senderAddress != m_address)
return;
if (data.size() < 18) {
qCDebug(dcSma()) << "Inverter: The received datagram is to short to be a SMA speedwire message. Ignoring data...";
return;
@ -1193,7 +1185,7 @@ void SpeedwireInverter::processData(const QHostAddress &senderAddress, quint16 s
}
if (header.protocolId != Speedwire::ProtocolIdInverter) {
qCWarning(dcSma()) << "Inverter: Received datagram from different protocol" << header.protocolId << "Ignoring data...";
//qCWarning(dcSma()) << "Inverter: Received datagram from different protocol" << header.protocolId << "Ignoring data...";
return;
}
@ -1478,6 +1470,7 @@ void SpeedwireInverter::setState(State state)
qCDebug(dcSma()) << "Inverter: Failed to query battery info from inverter:" << reply->request().command() << reply->error();
setBatteryAvailable(false);
setState(StateIdle);
return;
} else {
qCDebug(dcSma()) << "Inverter: Process battery info response" << reply->responsePayload().toHex();
processBatteryInfoResponse(reply->responsePayload());
@ -1490,6 +1483,7 @@ void SpeedwireInverter::setState(State state)
qCWarning(dcSma()) << "Inverter: Failed to query battery charge status from inverter:" << reply->request().command() << reply->error();
setBatteryAvailable(false);
setState(StateIdle);
return;
} else {
qCDebug(dcSma()) << "Inverter: Process battery charge status response" << reply->responsePayload().toHex();
processBatteryChargeResponse(reply->responsePayload());

View File

@ -54,10 +54,7 @@ public:
};
Q_ENUM(State)
explicit SpeedwireInverter(const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent = nullptr);
bool initialize();
bool initialized() const;
explicit SpeedwireInverter(SpeedwireInterface *speedwireInterface, const QHostAddress &address, quint16 modelId, quint32 serialNumber, QObject *parent = nullptr);
State state() const;
@ -124,7 +121,7 @@ signals:
void batteryValuesUpdated();
private:
SpeedwireInterface *m_interface = nullptr;
SpeedwireInterface *m_speedwireInterface = nullptr;
QHostAddress m_address;
QString m_password;

View File

@ -33,13 +33,13 @@
#include "sma.h"
SpeedwireMeter::SpeedwireMeter(SpeedwireInterface *multicastInterface, quint16 modelId, quint32 serialNumber, QObject *parent) :
SpeedwireMeter::SpeedwireMeter(SpeedwireInterface *speedwireInterface, quint16 modelId, quint32 serialNumber, QObject *parent) :
QObject(parent),
m_interface(multicastInterface),
m_speedwireInterface(speedwireInterface),
m_modelId(modelId),
m_serialNumber(serialNumber)
{
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireMeter::processData);
connect(m_speedwireInterface, &SpeedwireInterface::dataReceived, this, &SpeedwireMeter::processData);
// Reachable timestamp
m_timer.setInterval(5000);
@ -47,20 +47,6 @@ SpeedwireMeter::SpeedwireMeter(SpeedwireInterface *multicastInterface, quint16 m
connect(&m_timer, &QTimer::timeout, this, &SpeedwireMeter::evaluateReachable);
}
bool SpeedwireMeter::initialize()
{
bool initSuccess = m_interface->initialize();
if (initSuccess)
m_timer.start();
return initSuccess;
}
bool SpeedwireMeter::initialized() const
{
return m_interface->initialized();
}
bool SpeedwireMeter::reachable() const
{
return m_reachable;
@ -186,8 +172,10 @@ void SpeedwireMeter::evaluateReachable()
}
}
void SpeedwireMeter::processData(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data)
void SpeedwireMeter::processData(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data, bool multicast)
{
Q_UNUSED(multicast)
QDataStream stream(data);
stream.setByteOrder(QDataStream::BigEndian);
@ -220,7 +208,7 @@ void SpeedwireMeter::processData(const QHostAddress &senderAddress, quint16 send
qCDebug(dcSma()) << "Meter: ======================= Meter measurements";
quint32 timestamp;
stream >> timestamp;
qCDebug(dcSma()) << "Meter: Timestamp:" << timestamp << QDateTime::fromMSecsSinceEpoch(timestamp * 1000);
qCDebug(dcSma()) << "Meter: Timestamp:" << timestamp << QDateTime::fromMSecsSinceEpoch(static_cast<qulonglong>(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

View File

@ -41,10 +41,7 @@ class SpeedwireMeter : public QObject
{
Q_OBJECT
public:
explicit SpeedwireMeter(SpeedwireInterface *multicastInterface, quint16 modelId, quint32 serialNumber, QObject *parent = nullptr);
bool initialize();
bool initialized() const;
explicit SpeedwireMeter(SpeedwireInterface *speedwireInterface, quint16 modelId, quint32 serialNumber, QObject *parent = nullptr);
bool reachable() const;
@ -79,7 +76,7 @@ signals:
void valuesUpdated();
private:
SpeedwireInterface *m_interface = nullptr;
SpeedwireInterface *m_speedwireInterface = nullptr;
QHostAddress m_address;
bool m_initialized = false;
quint16 m_modelId = 0;
@ -118,7 +115,7 @@ private:
private slots:
void evaluateReachable();
void processData(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data);
void processData(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data, bool multicast);
};