Merge PR #142: SMA: Improve speedwire interface handling and sharing

This commit is contained in:
jenkins 2023-07-26 15:35:08 +02:00
commit e901edc7cc
11 changed files with 253 additions and 409 deletions

View File

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

View File

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

View File

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

View File

@ -30,12 +30,14 @@
#include "speedwirediscovery.h" #include "speedwirediscovery.h"
#include "extern-plugininfo.h" #include "extern-plugininfo.h"
#include "speedwire.h"
#include <QDataStream> #include <QDataStream>
SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint32 localSerialNumber, QObject *parent) : SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, SpeedwireInterface *speedwireInterface, quint32 localSerialNumber, QObject *parent) :
QObject(parent), QObject(parent),
m_networkDeviceDiscovery(networkDeviceDiscovery), m_networkDeviceDiscovery(networkDeviceDiscovery),
m_speedwireInterface(speedwireInterface),
m_localSerialNumber(localSerialNumber) m_localSerialNumber(localSerialNumber)
{ {
// More details: https://github.com/RalfOGit/libspeedwire/ // More details: https://github.com/RalfOGit/libspeedwire/
@ -45,44 +47,14 @@ SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDisc
m_multicastSearchRequestTimer.setInterval(1000); m_multicastSearchRequestTimer.setInterval(1000);
m_multicastSearchRequestTimer.setSingleShot(false); m_multicastSearchRequestTimer.setSingleShot(false);
connect(m_speedwireInterface, &SpeedwireInterface::dataReceived, this, &SpeedwireDiscovery::processDatagram);
connect(&m_multicastSearchRequestTimer, &QTimer::timeout, this, &SpeedwireDiscovery::sendDiscoveryRequest); connect(&m_multicastSearchRequestTimer, &QTimer::timeout, this, &SpeedwireDiscovery::sendDiscoveryRequest);
} }
SpeedwireDiscovery::~SpeedwireDiscovery() 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() bool SpeedwireDiscovery::startDiscovery()
@ -90,20 +62,17 @@ bool SpeedwireDiscovery::startDiscovery()
if (discoveryRunning()) if (discoveryRunning())
return true; return true;
if (!m_initialized) { if (!m_speedwireInterface->available()) {
qCDebug(dcSma()) << "SpeedwireDiscovery: Failed to start discovery because the socket has not been initialized successfully."; qCDebug(dcSma()) << "SpeedwireDiscovery: Failed to start discovery because the speedwire interface is not available.";
return false; return false;
} }
// Start clean // Start clean
m_results.clear(); m_results.clear();
m_networkDeviceInfos.clear(); m_networkDeviceInfos.clear();
m_multicastRunning = false;
m_unicastRunning = false;
startUnicastDiscovery(); startUnicastDiscovery();
startMulticastDiscovery(); startMulticastDiscovery();
return true; return true;
} }
@ -117,49 +86,6 @@ QList<SpeedwireDiscovery::SpeedwireDiscoveryResult> SpeedwireDiscovery::discover
return m_results; 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() void SpeedwireDiscovery::startMulticastDiscovery()
{ {
qCDebug(dcSma()) << "SpeedwireDiscovery: Start multicast discovery..."; qCDebug(dcSma()) << "SpeedwireDiscovery: Start multicast discovery...";
@ -170,26 +96,6 @@ void SpeedwireDiscovery::startMulticastDiscovery()
sendDiscoveryRequest(); 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() void SpeedwireDiscovery::startUnicastDiscovery()
{ {
@ -218,70 +124,14 @@ void SpeedwireDiscovery::startUnicastDiscovery()
void SpeedwireDiscovery::sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress) void SpeedwireDiscovery::sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress)
{ {
if (m_unicastSocket->writeDatagram(Speedwire::pingRequest(Speedwire::sourceModelId(), m_localSerialNumber), targetHostAddress, Speedwire::port()) < 0) { qCDebug(dcSma()) << "SpeedwireDiscovery: Sent discovery request to unicast address" << targetHostAddress.toString();
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to send unicast discovery datagram to address" << targetHostAddress.toString(); m_speedwireInterface->sendDataUnicast(targetHostAddress, Speedwire::pingRequest(Speedwire::sourceModelId(), m_localSerialNumber));
return;
}
qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to unicast address" << targetHostAddress.toString();
} }
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 // Check min size of SMA datagrams
if (datagram.size() < 18) { if (datagram.size() < 18) {
qCDebug(dcSma()) << "SpeedwireDiscovery: Received datagram is to short to be a SMA speedwire message. Ignoring data..."; 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)) { if (!m_resultMeters.contains(senderAddress)) {
SpeedwireDiscoveryResult result; SpeedwireDiscoveryResult result;
result.address = senderAddress; result.address = senderAddress;
result.deviceType = SpeedwireInterface::DeviceTypeMeter; result.deviceType = Speedwire::DeviceTypeMeter;
m_resultMeters.insert(senderAddress, result); m_resultMeters.insert(senderAddress, result);
} }
@ -342,7 +192,7 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
if (!m_resultInverters.contains(senderAddress)) { if (!m_resultInverters.contains(senderAddress)) {
SpeedwireDiscoveryResult result; SpeedwireDiscoveryResult result;
result.address = senderAddress; result.address = senderAddress;
result.deviceType = SpeedwireInterface::DeviceTypeInverter; result.deviceType = Speedwire::DeviceTypeInverter;
m_resultInverters.insert(senderAddress, result); m_resultInverters.insert(senderAddress, result);
} }
@ -359,20 +209,20 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
if (m_inverters.contains(senderAddress)) { if (m_inverters.contains(senderAddress)) {
inverter = m_inverters.value(senderAddress); inverter = m_inverters.value(senderAddress);
} else { } 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); m_inverters.insert(senderAddress, inverter);
} }
SpeedwireInverterReply *reply = inverter->sendIdentifyRequest(); SpeedwireInverterReply *reply = inverter->sendIdentifyRequest();
qCDebug(dcSma()) << "SpeedwireDiscovery: send identify request to" << senderAddress.toString(); qCDebug(dcSma()) << "SpeedwireDiscovery: Send identify request to" << senderAddress.toString();
connect(reply, &SpeedwireInverterReply::finished, this, [this, inverter, senderAddress, reply](){ connect(reply, &SpeedwireInverterReply::finished, this, [/*this, inverter,*/ senderAddress, reply](){
qCDebug(dcSma()) << "SpeedwireDiscovery: identify request finished from" << senderAddress.toString() << reply->error(); qCDebug(dcSma()) << "SpeedwireDiscovery: Identify request finished from" << senderAddress.toString() << reply->error();
SpeedwireInverterReply *loginReply = inverter->sendLoginRequest(); // SpeedwireInverterReply *loginReply = inverter->sendLoginRequest();
qCDebug(dcSma()) << "SpeedwireDiscovery: make login attempt using the default password."; // qCDebug(dcSma()) << "SpeedwireDiscovery: make login attempt using the default password.";
connect(loginReply, &SpeedwireInverterReply::finished, this, [loginReply, senderAddress](){ // connect(loginReply, &SpeedwireInverterReply::finished, this, [loginReply, senderAddress](){
qCDebug(dcSma()) << "SpeedwireDiscovery: login attempt finished" << senderAddress.toString() << loginReply->error(); // qCDebug(dcSma()) << "SpeedwireDiscovery: login attempt finished" << senderAddress.toString() << loginReply->error();
}); // });
}); });
} else { } else {
@ -383,12 +233,8 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
void SpeedwireDiscovery::sendDiscoveryRequest() void SpeedwireDiscovery::sendDiscoveryRequest()
{ {
if (m_multicastSocket->writeDatagram(Speedwire::discoveryDatagramMulticast(), Speedwire::multicastAddress(), Speedwire::port()) < 0) { qCDebug(dcSma()) << "SpeedwireDiscovery: Sent discovery request to multicast address" << Speedwire::multicastAddress().toString();
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to send discovery datagram to multicast address" << Speedwire::multicastAddress().toString(); m_speedwireInterface->sendDataMulticast(Speedwire::discoveryDatagramMulticast());
return;
}
qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to multicast address" << Speedwire::multicastAddress().toString();
} }
void SpeedwireDiscovery::evaluateDiscoveryFinished() void SpeedwireDiscovery::evaluateDiscoveryFinished()
@ -405,23 +251,6 @@ 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_multicastSearchRequestTimer.stop(); 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) { 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;

View File

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

View File

@ -31,74 +31,117 @@
#include "speedwireinterface.h" #include "speedwireinterface.h"
#include "extern-plugininfo.h" #include "extern-plugininfo.h"
SpeedwireInterface::SpeedwireInterface(bool multicast, quint32 sourceSerialNumber, QObject *parent) : #include <QNetworkInterface>
SpeedwireInterface::SpeedwireInterface(quint32 sourceSerialNumber, QObject *parent) :
QObject(parent), QObject(parent),
m_multicast(multicast),
m_sourceSerialNumber(sourceSerialNumber) 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); while (m_unicast->hasPendingDatagrams()) {
connect(m_socket, &QUdpSocket::readyRead, this, &SpeedwireInterface::readPendingDatagrams); datagram.resize(m_unicast->pendingDatagramSize());
connect(m_socket, &QUdpSocket::stateChanged, this, &SpeedwireInterface::onSocketStateChanged); m_unicast->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError)));
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() SpeedwireInterface::~SpeedwireInterface()
{ {
deinitialize(); if (m_unicast)
} m_unicast->close();
bool SpeedwireInterface::initialize(const QHostAddress &address) if (m_multicast) {
{ if (!m_multicast->leaveMulticastGroup(Speedwire::multicastAddress())) {
if (m_initialized && !m_multicast) { qCWarning(dcSma()) << "SpeedwireInterface: Failed to leave multicast group" << Speedwire::multicastAddress().toString();
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();
}
} }
m_address = QHostAddress(); m_multicast->close();
m_socket->close();
m_initialized = false;
} }
} }
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 quint32 SpeedwireInterface::sourceSerialNumber() const
@ -106,40 +149,53 @@ quint32 SpeedwireInterface::sourceSerialNumber() const
return m_sourceSerialNumber; return m_sourceSerialNumber;
} }
void SpeedwireInterface::sendData(const QByteArray &data) bool SpeedwireInterface::initialize()
{ {
qCDebug(dcSma()) << "SpeedwireInterface: -->" << m_address.toString() << Speedwire::port() << data.toHex(); bool success = true;
if (m_socket->writeDatagram(data, m_address, Speedwire::port()) < 0) { if (m_unicast->state() != QUdpSocket::BoundState) {
qCWarning(dcSma()) << "SpeedwireInterface: failed to send data" << m_socket->errorString(); 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; qCDebug(dcSma()) << "SpeedwireInterface: Multicast -->" << Speedwire::multicastAddress().toString() << Speedwire::port() << data.toHex();
QHostAddress senderAddress; if (!m_multicast) {
quint16 senderPort; qCWarning(dcSma()) << "SpeedwireInterface: Failed to send multicast data, the socket is not available";
return;
}
while (m_socket->hasPendingDatagrams()) { if (m_multicast->writeDatagram(data, Speedwire::multicastAddress(), Speedwire::port()) < 0) {
datagram.resize(m_socket->pendingDatagramSize()); qCWarning(dcSma()) << "SpeedwireInterface: Failed to send multicast data to" << Speedwire::multicastAddress().toString() << m_multicast->errorString();
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);
} }
} }
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 <QObject>
#include <QUdpSocket> #include <QUdpSocket>
#include <QDataStream> #include <QDataStream>
#include <QTimer>
#include "speedwire.h" #include "speedwire.h"
@ -41,46 +42,32 @@ class SpeedwireInterface : public QObject
{ {
Q_OBJECT Q_OBJECT
public: 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(); ~SpeedwireInterface();
bool initialize(const QHostAddress &address = QHostAddress()); bool available() const;
void deinitialize();
bool multicast() const;
bool initialized() const;
quint16 sourceModelId() const;
quint32 sourceSerialNumber() const; quint32 sourceSerialNumber() const;
bool initialize();
public slots: public slots:
void sendData(const QByteArray &data); void sendDataUnicast(const QHostAddress &address, const QByteArray &data);
void sendDataMulticast(const QByteArray &data);
signals: signals:
void dataReceived(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data); void dataReceived(const QHostAddress &senderAddress, quint16 senderPort, const QByteArray &data, bool multicast = false);
private:
QUdpSocket *m_socket = nullptr;
QHostAddress m_address;
bool m_multicast = false;
bool m_initialized = false;
// Requester
quint32 m_sourceSerialNumber = 0;
private slots: private slots:
void readPendingDatagrams(); void reconfigureMulticastGroup();
void onSocketError(QAbstractSocket::SocketError error);
void onSocketStateChanged(QAbstractSocket::SocketState socketState);
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> #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), QObject(parent),
m_speedwireInterface(speedwireInterface),
m_address(address), m_address(address),
m_modelId(modelId), m_modelId(modelId),
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(false, serialNumber, this); connect(m_speedwireInterface, &SpeedwireInterface::dataReceived, this, &SpeedwireInverter::processData);
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();
} }
SpeedwireInverter::State SpeedwireInverter::state() const SpeedwireInverter::State SpeedwireInverter::state() const
@ -270,7 +260,7 @@ SpeedwireInverterReply *SpeedwireInverter::sendLogoutRequest()
// Source // Source
stream << Speedwire::sourceModelId(); stream << Speedwire::sourceModelId();
stream << m_interface->sourceSerialNumber(); stream << m_speedwireInterface->sourceSerialNumber();
stream << static_cast<quint16>(0x0300); stream << static_cast<quint16>(0x0300);
stream << static_cast<quint16>(0); stream << static_cast<quint16>(0);
@ -413,7 +403,7 @@ void SpeedwireInverter::sendNextReply()
// Pick the next reply and send request // Pick the next reply and send request
m_currentReply = m_replyQueue.dequeue(); m_currentReply = m_replyQueue.dequeue();
qCDebug(dcSma()) << "Inverter: --> Sending" << m_currentReply->request().command() << "packet ID:" << m_currentReply->request().packetId(); 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(); m_currentReply->startWaiting();
} }
@ -489,7 +479,7 @@ void SpeedwireInverter::buildPacket(QDataStream &stream, quint32 command, quint1
// Destination Ctrl // Destination Ctrl
stream << static_cast<quint16>(0x0100); stream << static_cast<quint16>(0x0100);
// Source // Source
stream << Speedwire::sourceModelId() << m_interface->sourceSerialNumber(); stream << Speedwire::sourceModelId() << m_speedwireInterface->sourceSerialNumber();
// Destination Ctrl // Destination Ctrl
stream << static_cast<quint16>(0x0100); 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) 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) 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) { 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;
@ -1193,7 +1185,7 @@ void SpeedwireInverter::processData(const QHostAddress &senderAddress, quint16 s
} }
if (header.protocolId != Speedwire::ProtocolIdInverter) { 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; 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(); qCDebug(dcSma()) << "Inverter: Failed to query battery info from inverter:" << reply->request().command() << reply->error();
setBatteryAvailable(false); setBatteryAvailable(false);
setState(StateIdle); setState(StateIdle);
return;
} else { } else {
qCDebug(dcSma()) << "Inverter: Process battery info response" << reply->responsePayload().toHex(); qCDebug(dcSma()) << "Inverter: Process battery info response" << reply->responsePayload().toHex();
processBatteryInfoResponse(reply->responsePayload()); 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(); qCWarning(dcSma()) << "Inverter: Failed to query battery charge status from inverter:" << reply->request().command() << reply->error();
setBatteryAvailable(false); setBatteryAvailable(false);
setState(StateIdle); setState(StateIdle);
return;
} else { } else {
qCDebug(dcSma()) << "Inverter: Process battery charge status response" << reply->responsePayload().toHex(); qCDebug(dcSma()) << "Inverter: Process battery charge status response" << reply->responsePayload().toHex();
processBatteryChargeResponse(reply->responsePayload()); processBatteryChargeResponse(reply->responsePayload());

View File

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

View File

@ -33,13 +33,13 @@
#include "sma.h" #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), QObject(parent),
m_interface(multicastInterface), m_speedwireInterface(speedwireInterface),
m_modelId(modelId), m_modelId(modelId),
m_serialNumber(serialNumber) m_serialNumber(serialNumber)
{ {
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireMeter::processData); connect(m_speedwireInterface, &SpeedwireInterface::dataReceived, this, &SpeedwireMeter::processData);
// Reachable timestamp // Reachable timestamp
m_timer.setInterval(5000); m_timer.setInterval(5000);
@ -47,20 +47,6 @@ SpeedwireMeter::SpeedwireMeter(SpeedwireInterface *multicastInterface, quint16 m
connect(&m_timer, &QTimer::timeout, this, &SpeedwireMeter::evaluateReachable); 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 bool SpeedwireMeter::reachable() const
{ {
return m_reachable; 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); QDataStream stream(data);
stream.setByteOrder(QDataStream::BigEndian); stream.setByteOrder(QDataStream::BigEndian);
@ -220,7 +208,7 @@ void SpeedwireMeter::processData(const QHostAddress &senderAddress, quint16 send
qCDebug(dcSma()) << "Meter: ======================= Meter measurements"; qCDebug(dcSma()) << "Meter: ======================= Meter measurements";
quint32 timestamp; quint32 timestamp;
stream >> 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 // 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

View File

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