SMA: Improve speedwire communication and discovery.
This commit is contained in:
parent
fd80c7e99a
commit
e4721de1c5
@ -96,8 +96,8 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
} else if (info->thingClassId() == speedwireMeterThingClassId) {
|
} else if (info->thingClassId() == speedwireMeterThingClassId) {
|
||||||
|
|
||||||
// Note: does not require the network device discovery...
|
// Note: does not require the network device discovery...
|
||||||
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), info);
|
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), getLocalSerialNumber(), info);
|
||||||
if (!speedwireDiscovery->initialize(SpeedwireInterface::DeviceTypeMeter)) {
|
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;
|
||||||
@ -145,8 +145,8 @@ void IntegrationPluginSma::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), info);
|
SpeedwireDiscovery *speedwireDiscovery = new SpeedwireDiscovery(hardwareManager()->networkDeviceDiscovery(), getLocalSerialNumber(), info);
|
||||||
if (!speedwireDiscovery->initialize(SpeedwireInterface::DeviceTypeInverter)) {
|
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;
|
||||||
@ -302,7 +302,7 @@ void IntegrationPluginSma::setupThing(ThingSetupInfo *info)
|
|||||||
|
|
||||||
// Create the multicast interface if not created already.
|
// Create the multicast interface if not created already.
|
||||||
if (!m_multicastInterface)
|
if (!m_multicastInterface)
|
||||||
m_multicastInterface = new SpeedwireInterface(true, this);
|
m_multicastInterface = new SpeedwireInterface(true, getLocalSerialNumber(), this);
|
||||||
|
|
||||||
quint32 serialNumber = static_cast<quint32>(thing->paramValue(speedwireMeterThingSerialNumberParamTypeId).toUInt());
|
quint32 serialNumber = static_cast<quint32>(thing->paramValue(speedwireMeterThingSerialNumberParamTypeId).toUInt());
|
||||||
quint16 modelId = static_cast<quint16>(thing->paramValue(speedwireMeterThingModelIdParamTypeId).toUInt());
|
quint16 modelId = static_cast<quint16>(thing->paramValue(speedwireMeterThingModelIdParamTypeId).toUInt());
|
||||||
@ -541,6 +541,11 @@ void IntegrationPluginSma::thingRemoved(Thing *thing)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (thing->thingClassId() == speedwireInverterThingClassId && m_speedwireInverters.contains(thing)) {
|
if (thing->thingClassId() == speedwireInverterThingClassId && m_speedwireInverters.contains(thing)) {
|
||||||
|
// Remove invalid password from settings
|
||||||
|
pluginStorage()->beginGroup(thing->id().toString());
|
||||||
|
pluginStorage()->remove("");
|
||||||
|
pluginStorage()->endGroup();
|
||||||
|
|
||||||
m_speedwireInverters.take(thing)->deleteLater();
|
m_speedwireInverters.take(thing)->deleteLater();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -796,6 +801,28 @@ void IntegrationPluginSma::markModbusInverterAsDisconnected(Thing *thing)
|
|||||||
thing->setStateValue(modbusInverterCurrentPowerStateTypeId, 0);
|
thing->setStateValue(modbusInverterCurrentPowerStateTypeId, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quint64 IntegrationPluginSma::getLocalSerialNumber()
|
||||||
|
{
|
||||||
|
m_localSerialNumber = pluginStorage()->value("localSerialNumber", 0).toUInt();
|
||||||
|
|
||||||
|
if (m_localSerialNumber == 0) {
|
||||||
|
srand(QDateTime::currentMSecsSinceEpoch() / 1000);
|
||||||
|
// Generate one and save it for the next time, each instance should have it's own serial number
|
||||||
|
QByteArray data;
|
||||||
|
QDataStream inStream(&data, QIODevice::ReadWrite);
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
inStream << static_cast<quint8>(rand() % 256);
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream outStream(data);
|
||||||
|
outStream >> m_localSerialNumber;
|
||||||
|
pluginStorage()->setValue("localSerialNumber", m_localSerialNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
qCInfo(dcSma()) << "Using local serial number" << m_localSerialNumber;
|
||||||
|
return m_localSerialNumber;
|
||||||
|
}
|
||||||
|
|
||||||
bool IntegrationPluginSma::isModbusValueValid(quint32 value)
|
bool IntegrationPluginSma::isModbusValueValid(quint32 value)
|
||||||
{
|
{
|
||||||
return value != 0xffffffff;
|
return value != 0xffffffff;
|
||||||
|
|||||||
@ -80,6 +80,8 @@ private:
|
|||||||
QHash<Thing *, SpeedwireInverter *> m_speedwireInverters;
|
QHash<Thing *, SpeedwireInverter *> m_speedwireInverters;
|
||||||
QHash<Thing *, SmaInverterModbusTcpConnection *> m_modbusInverters;
|
QHash<Thing *, SmaInverterModbusTcpConnection *> m_modbusInverters;
|
||||||
|
|
||||||
|
quint32 m_localSerialNumber = 0;
|
||||||
|
|
||||||
// Shared interface accross meters
|
// Shared interface accross meters
|
||||||
SpeedwireInterface *m_multicastInterface = nullptr;
|
SpeedwireInterface *m_multicastInterface = nullptr;
|
||||||
|
|
||||||
@ -87,6 +89,8 @@ private:
|
|||||||
void markSpeedwireInverterAsDisconnected(Thing *thing);
|
void markSpeedwireInverterAsDisconnected(Thing *thing);
|
||||||
void markModbusInverterAsDisconnected(Thing *thing);
|
void markModbusInverterAsDisconnected(Thing *thing);
|
||||||
|
|
||||||
|
quint64 getLocalSerialNumber();
|
||||||
|
|
||||||
// Sma modbus data validation
|
// Sma modbus data validation
|
||||||
bool isModbusValueValid(quint32 value);
|
bool isModbusValueValid(quint32 value);
|
||||||
bool isModbusValueValid(qint32 value);
|
bool isModbusValueValid(qint32 value);
|
||||||
|
|||||||
@ -101,6 +101,7 @@ public:
|
|||||||
|
|
||||||
//static QHash<quint16, QString> deviceTypes = { {0x0000, "Unknwon"} };
|
//static QHash<quint16, QString> deviceTypes = { {0x0000, "Unknwon"} };
|
||||||
|
|
||||||
|
static quint16 sourceModelId() { return 120; }
|
||||||
static quint16 port() { return 9522; }
|
static quint16 port() { return 9522; }
|
||||||
static QHostAddress multicastAddress() { return QHostAddress("239.12.255.254"); }
|
static QHostAddress multicastAddress() { return QHostAddress("239.12.255.254"); }
|
||||||
static quint32 smaSignature() { return 0x534d4100; }
|
static quint32 smaSignature() { return 0x534d4100; }
|
||||||
@ -115,19 +116,44 @@ public:
|
|||||||
// 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x20, // 0xffffffff group, 0x0000 length, 0x0020 "SMA Net ?", Version ?
|
// 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x20, // 0xffffffff group, 0x0000 length, 0x0020 "SMA Net ?", Version ?
|
||||||
// 0x00, 0x00, 0x00, 0x00 // 0x0000 protocol, 0x00 #long words, 0x00 ctrl
|
// 0x00, 0x00, 0x00, 0x00 // 0x0000 protocol, 0x00 #long words, 0x00 ctrl
|
||||||
|
|
||||||
// Unicast device discovery request packet, according to SMA documentation
|
|
||||||
// 0x53, 0x4d, 0x41, 0x00, 0x00, 0x04, 0x02, 0xa0, // sma signature, tag0
|
|
||||||
// 0x00, 0x00, 0x00, 0x01, 0x00, 0x26, 0x00, 0x10, // 0x26 length, 0x0010 "SMA Net 2", Version 0
|
|
||||||
// 0x60, 0x65, 0x09, 0xa0, 0xff, 0xff, 0xff, 0xff, // 0x6065 protocol, 0x09 #long words, 0xa0 ctrl, 0xffff dst susyID any, 0xffffffff dst serial any
|
|
||||||
// 0xff, 0xff, 0x00, 0x00, 0x7d, 0x00, 0x52, 0xbe, // 0x0000 dst cntrl, 0x007d src susy id, 0x3a28be52 src serial
|
|
||||||
// 0x28, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x0000 src cntrl, 0x0000 error code, 0x0000 fragment ID
|
|
||||||
// 0x01, 0x80, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, // 0x8001 packet ID
|
|
||||||
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
// 0x00, 0x00
|
|
||||||
|
|
||||||
static QByteArray discoveryDatagramMulticast() { return QByteArray::fromHex("534d4100000402a0ffffffff0000002000000000"); }
|
static QByteArray discoveryDatagramMulticast() { return QByteArray::fromHex("534d4100000402a0ffffffff0000002000000000"); }
|
||||||
static QByteArray discoveryResponseDatagram() { return QByteArray::fromHex("534d4100000402A000000001000200000001"); }
|
static QByteArray discoveryResponseDatagram() { return QByteArray::fromHex("534d4100000402A000000001000200000001"); }
|
||||||
static QByteArray discoveryDatagramUnicast() { return QByteArray::fromHex("534d4100000402a00000000100260010606509a0ffffffffffff00007d0052be283a000000000000018000020000000000000000000000000000"); }
|
|
||||||
|
static QByteArray pingRequest( quint16 sourceSusyId, quint32 sourceSerialNumber) {
|
||||||
|
QByteArray requestData;
|
||||||
|
QDataStream stream(&requestData, QIODevice::WriteOnly);
|
||||||
|
stream << smaSignature();
|
||||||
|
stream << static_cast<quint16>(0x0004); // header length
|
||||||
|
stream << tag0();
|
||||||
|
stream << static_cast<quint32>(0x00000001); // group
|
||||||
|
stream << static_cast<quint16>(0x0026); // entry length
|
||||||
|
stream << smaNet2Version();
|
||||||
|
stream << static_cast<quint16>(ProtocolIdInverter);
|
||||||
|
|
||||||
|
// From now on little endian
|
||||||
|
stream.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
stream << static_cast<quint8>(0x09); // length
|
||||||
|
stream << static_cast<quint8>(0xa0); // control
|
||||||
|
stream << static_cast<quint16>(0xffff); // destination susyID
|
||||||
|
stream << static_cast<quint32>(0xffffffff); // destination serial
|
||||||
|
stream << static_cast<quint16>(0x00); // job id
|
||||||
|
stream << static_cast<quint16>(sourceSusyId); // source susyID
|
||||||
|
stream << static_cast<quint32>(sourceSerialNumber); // source susyID
|
||||||
|
stream << static_cast<quint16>(0x00); // job id
|
||||||
|
stream << static_cast<quint16>(0x00); // status
|
||||||
|
stream << static_cast<quint16>(0x00); // packet count
|
||||||
|
stream << static_cast<quint16>(0x8001); // packet id
|
||||||
|
stream << static_cast<quint8>(0x00); // command
|
||||||
|
stream << static_cast<quint8>(0x02); // param count
|
||||||
|
stream << static_cast<quint16>(0x00); // Object
|
||||||
|
|
||||||
|
stream << static_cast<quint32>(0x0); // Param 1
|
||||||
|
stream << static_cast<quint32>(0x0); // Param 2
|
||||||
|
|
||||||
|
stream << static_cast<quint32>(0x0); // Packet end
|
||||||
|
|
||||||
|
return requestData;
|
||||||
|
}
|
||||||
|
|
||||||
static Speedwire::Header parseHeader(QDataStream &stream) {
|
static Speedwire::Header parseHeader(QDataStream &stream) {
|
||||||
stream.setByteOrder(QDataStream::BigEndian);
|
stream.setByteOrder(QDataStream::BigEndian);
|
||||||
@ -139,7 +165,7 @@ public:
|
|||||||
stream >> protocolId;
|
stream >> protocolId;
|
||||||
header.protocolId = static_cast<ProtocolId>(protocolId);
|
header.protocolId = static_cast<ProtocolId>(protocolId);
|
||||||
return header;
|
return header;
|
||||||
};
|
}
|
||||||
|
|
||||||
static Speedwire::InverterPacket parseInverterPacket(QDataStream &stream) {
|
static Speedwire::InverterPacket parseInverterPacket(QDataStream &stream) {
|
||||||
// Make sure the data stream is little endian
|
// Make sure the data stream is little endian
|
||||||
@ -158,7 +184,7 @@ public:
|
|||||||
stream >> packet.packetId;
|
stream >> packet.packetId;
|
||||||
stream >> packet.command;
|
stream >> packet.command;
|
||||||
return packet;
|
return packet;
|
||||||
};
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
inline QDebug operator<<(QDebug debug, const Speedwire::Header &header)
|
inline QDebug operator<<(QDebug debug, const Speedwire::Header &header)
|
||||||
@ -174,7 +200,7 @@ inline QDebug operator<<(QDebug debug, const Speedwire::InverterPacket &packet)
|
|||||||
debug.nospace() << ", command: " << packet.command;
|
debug.nospace() << ", command: " << packet.command;
|
||||||
debug.nospace() << ", error: " << packet.errorCode;
|
debug.nospace() << ", error: " << packet.errorCode;
|
||||||
debug.nospace() << ", fragment: " << packet.fragmentId;
|
debug.nospace() << ", fragment: " << packet.fragmentId;
|
||||||
debug.nospace() << ", packet ID: " << packet.fragmentId;
|
debug.nospace() << ", packet ID: " << packet.packetId;
|
||||||
debug.nospace() << ")";
|
debug.nospace() << ")";
|
||||||
return debug.maybeSpace();
|
return debug.maybeSpace();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,9 +33,10 @@
|
|||||||
|
|
||||||
#include <QDataStream>
|
#include <QDataStream>
|
||||||
|
|
||||||
SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent) :
|
SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint32 localSerialNumber, QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
m_networkDeviceDiscovery(networkDeviceDiscovery)
|
m_networkDeviceDiscovery(networkDeviceDiscovery),
|
||||||
|
m_localSerialNumber(localSerialNumber)
|
||||||
{
|
{
|
||||||
// More details: https://github.com/RalfOGit/libspeedwire/
|
// More details: https://github.com/RalfOGit/libspeedwire/
|
||||||
|
|
||||||
@ -50,8 +51,8 @@ SpeedwireDiscovery::SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDisc
|
|||||||
SpeedwireDiscovery::~SpeedwireDiscovery()
|
SpeedwireDiscovery::~SpeedwireDiscovery()
|
||||||
{
|
{
|
||||||
if (m_initialized && m_multicastSocket) {
|
if (m_initialized && m_multicastSocket) {
|
||||||
if (!m_multicastSocket->leaveMulticastGroup(m_multicastAddress)) {
|
if (!m_multicastSocket->leaveMulticastGroup(Speedwire::multicastAddress())) {
|
||||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << m_multicastAddress.toString();
|
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << Speedwire::multicastAddress().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
m_multicastSocket->close();
|
m_multicastSocket->close();
|
||||||
@ -123,7 +124,7 @@ bool SpeedwireDiscovery::discoveryRunning() const
|
|||||||
|
|
||||||
QList<SpeedwireDiscovery::SpeedwireDiscoveryResult> SpeedwireDiscovery::discoveryResult() const
|
QList<SpeedwireDiscovery::SpeedwireDiscoveryResult> SpeedwireDiscovery::discoveryResult() const
|
||||||
{
|
{
|
||||||
return m_results.values();
|
return m_results;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpeedwireDiscovery::setupMulticastSocket()
|
bool SpeedwireDiscovery::setupMulticastSocket()
|
||||||
@ -137,18 +138,33 @@ bool SpeedwireDiscovery::setupMulticastSocket()
|
|||||||
connect(m_multicastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError)));
|
connect(m_multicastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError)));
|
||||||
|
|
||||||
// Setup multicast socket
|
// Setup multicast socket
|
||||||
if (!m_multicastSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
|
if (!m_multicastSocket->bind(QHostAddress::AnyIPv4, Speedwire::port(), QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
|
||||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind multicast socket to port" << m_port << m_multicastSocket->errorString();
|
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind multicast socket to port" << Speedwire::port() << m_multicastSocket->errorString();
|
||||||
m_multicastSocket->deleteLater();
|
m_multicastSocket->deleteLater();
|
||||||
m_multicastSocket = nullptr;
|
m_multicastSocket = nullptr;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_multicastSocket->joinMulticastGroup(m_multicastAddress)) {
|
|
||||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_multicastSocket->errorString();
|
// Join all available interfaces the multicast group
|
||||||
m_multicastSocket->deleteLater();
|
foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) {
|
||||||
m_multicastSocket = nullptr;
|
if(interface.isValid() && !interface.flags().testFlag(QNetworkInterface::IsLoopBack)
|
||||||
return false;
|
&& 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;
|
return true;
|
||||||
@ -158,17 +174,10 @@ void SpeedwireDiscovery::startMulticastDiscovery()
|
|||||||
{
|
{
|
||||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Start multicast discovery...";
|
qCDebug(dcSma()) << "SpeedwireDiscovery: Start multicast discovery...";
|
||||||
m_multicastRunning = true;
|
m_multicastRunning = true;
|
||||||
|
|
||||||
// Start sending multicast messages
|
|
||||||
sendDiscoveryRequest();
|
|
||||||
|
|
||||||
// Give 5 seconds time, on one of the requests sent in this period the meter would have responded...
|
|
||||||
QTimer::singleShot(5000, this, [this](){
|
|
||||||
m_multicastRunning = false;
|
|
||||||
evaluateDiscoveryFinished();
|
|
||||||
});
|
|
||||||
|
|
||||||
m_multicastSearchRequestTimer.start();
|
m_multicastSearchRequestTimer.start();
|
||||||
|
|
||||||
|
// Start sending multicast messages periodically
|
||||||
|
sendDiscoveryRequest();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpeedwireDiscovery::setupUnicastSocket()
|
bool SpeedwireDiscovery::setupUnicastSocket()
|
||||||
@ -182,8 +191,8 @@ bool SpeedwireDiscovery::setupUnicastSocket()
|
|||||||
connect(m_unicastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError)));
|
connect(m_unicastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(onSocketError(QAbstractSocket::SocketError)));
|
||||||
|
|
||||||
// Setup unicast socket
|
// Setup unicast socket
|
||||||
if (!m_unicastSocket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
|
if (!m_unicastSocket->bind(QHostAddress::AnyIPv4, Speedwire::port(), QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
|
||||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind to port" << m_port << m_multicastSocket->errorString();
|
qCWarning(dcSma()) << "SpeedwireDiscovery: Initialization failed. Could not bind to port" << Speedwire::port() << m_multicastSocket->errorString();
|
||||||
m_unicastSocket->deleteLater();
|
m_unicastSocket->deleteLater();
|
||||||
m_unicastSocket = nullptr;
|
m_unicastSocket = nullptr;
|
||||||
return false;
|
return false;
|
||||||
@ -208,6 +217,9 @@ void SpeedwireDiscovery::startUnicastDiscovery()
|
|||||||
qCDebug(dcSma()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices for unicast requests.";
|
qCDebug(dcSma()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "network devices for unicast requests.";
|
||||||
// Wait some extra second in otder to give the last hosts joined some time to respond.
|
// Wait some extra second in otder to give the last hosts joined some time to respond.
|
||||||
QTimer::singleShot(3000, this, [this](){
|
QTimer::singleShot(3000, this, [this](){
|
||||||
|
m_multicastSearchRequestTimer.stop();
|
||||||
|
m_multicastRunning = false;
|
||||||
|
|
||||||
m_unicastRunning = false;
|
m_unicastRunning = false;
|
||||||
evaluateDiscoveryFinished();
|
evaluateDiscoveryFinished();
|
||||||
});
|
});
|
||||||
@ -216,7 +228,7 @@ void SpeedwireDiscovery::startUnicastDiscovery()
|
|||||||
|
|
||||||
void SpeedwireDiscovery::sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress)
|
void SpeedwireDiscovery::sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress)
|
||||||
{
|
{
|
||||||
if (m_unicastSocket->writeDatagram(Speedwire::discoveryDatagramUnicast(), targetHostAddress, m_port) < 0) {
|
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();
|
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to send unicast discovery datagram to address" << targetHostAddress.toString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -235,8 +247,13 @@ void SpeedwireDiscovery::readPendingDatagramsMulticast()
|
|||||||
while (socket->hasPendingDatagrams()) {
|
while (socket->hasPendingDatagrams()) {
|
||||||
datagram.resize(socket->pendingDatagramSize());
|
datagram.resize(socket->pendingDatagramSize());
|
||||||
socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
|
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: Received multicast data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort);
|
||||||
//qCDebug(dcSma()) << "SpeedwireDiscovery: " << datagram.toHex();
|
qCDebug(dcSma()) << "SpeedwireDiscovery: " << datagram.toHex();
|
||||||
processDatagram(senderAddress, senderPort, datagram);
|
processDatagram(senderAddress, senderPort, datagram);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -252,8 +269,13 @@ void SpeedwireDiscovery::readPendingDatagramsUnicast()
|
|||||||
while (socket->hasPendingDatagrams()) {
|
while (socket->hasPendingDatagrams()) {
|
||||||
datagram.resize(socket->pendingDatagramSize());
|
datagram.resize(socket->pendingDatagramSize());
|
||||||
socket->readDatagram(datagram.data(), datagram.size(), &senderAddress, &senderPort);
|
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: Received unicast data from" << QString("%1:%2").arg(senderAddress.toString()).arg(senderPort);
|
||||||
//qCDebug(dcSma()) << "SpeedwireDiscovery: " << datagram.toHex();
|
qCDebug(dcSma()) << "SpeedwireDiscovery: " << datagram.toHex();
|
||||||
processDatagram(senderAddress, senderPort, datagram);
|
processDatagram(senderAddress, senderPort, datagram);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -276,10 +298,6 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore discovery requests
|
|
||||||
if (datagram == Speedwire::discoveryDatagramMulticast() || datagram == Speedwire::discoveryDatagramUnicast())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QDataStream stream(datagram);
|
QDataStream stream(datagram);
|
||||||
Speedwire::Header header = Speedwire::parseHeader(stream);
|
Speedwire::Header header = Speedwire::parseHeader(stream);
|
||||||
if (!header.isValid()) {
|
if (!header.isValid()) {
|
||||||
@ -300,24 +318,7 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_results.contains(senderAddress)) {
|
qCDebug(dcSma()) << "SpeedwireDiscovery: --> Found SMA device on" << senderAddress.toString();
|
||||||
qCDebug(dcSma()) << "SpeedwireDiscovery: --> Found SMA device on" << senderAddress.toString();
|
|
||||||
if (!m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
|
||||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Found SMA using UDP discovery but the host is not in the network discovery result list. Not adding to results" << senderAddress.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
SpeedwireDiscoveryResult result;
|
|
||||||
result.address = senderAddress;
|
|
||||||
if (m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
|
||||||
result.networkDeviceInfo = m_networkDeviceInfos.get(senderAddress);
|
|
||||||
}
|
|
||||||
result.deviceType = SpeedwireInterface::DeviceTypeUnknown;
|
|
||||||
m_results.insert(senderAddress, result);
|
|
||||||
} else {
|
|
||||||
if (m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
|
||||||
m_results[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -330,37 +331,62 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
|
|||||||
stream >> modelId >> serialNumber;
|
stream >> modelId >> serialNumber;
|
||||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Meter identifier: Model ID:" << modelId << "Serial number:" << serialNumber;
|
qCDebug(dcSma()) << "SpeedwireDiscovery: Meter identifier: Model ID:" << modelId << "Serial number:" << serialNumber;
|
||||||
|
|
||||||
if (!m_results.contains(senderAddress)) {
|
if (!m_resultMeters.contains(senderAddress)) {
|
||||||
SpeedwireDiscoveryResult result;
|
SpeedwireDiscoveryResult result;
|
||||||
result.address = senderAddress;
|
result.address = senderAddress;
|
||||||
result.deviceType = SpeedwireInterface::DeviceTypeMeter;
|
result.deviceType = SpeedwireInterface::DeviceTypeMeter;
|
||||||
m_results.insert(senderAddress, result);
|
m_resultMeters.insert(senderAddress, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
if (m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
||||||
m_results[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress);
|
m_resultMeters[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_results[senderAddress].modelId = modelId;
|
m_resultMeters[senderAddress].modelId = modelId;
|
||||||
m_results[senderAddress].serialNumber = serialNumber;
|
m_resultMeters[senderAddress].serialNumber = serialNumber;
|
||||||
} else if (header.protocolId == Speedwire::ProtocolIdInverter) {
|
} else if (header.protocolId == Speedwire::ProtocolIdInverter) {
|
||||||
Speedwire::InverterPacket inverterPacket = Speedwire::parseInverterPacket(stream);
|
Speedwire::InverterPacket inverterPacket = Speedwire::parseInverterPacket(stream);
|
||||||
// Response from inverter 534d4100 0004 02a0 0000 0001 004e 0010 6065 1390 7d00 52be283a 0000 b500 c2c12e12 0000 0000 00000 1800102000000000000000000000003000000ff0000ecd5ff1f0100b500c2c12e1200000a000c00000000000000030000000101000000000000
|
// Response from inverter 534d4100 0004 02a0 0000 0001 004e 0010 6065 1390 7d00 52be283a 0000 b500 c2c12e12 0000 0000 00000 1800102000000000000000000000003000000ff0000ecd5ff1f0100b500c2c12e1200000a000c00000000000000030000000101000000000000
|
||||||
qCDebug(dcSma()) << "SpeedwireDiscovery:" << inverterPacket;
|
qCDebug(dcSma()) << "SpeedwireDiscovery:" << inverterPacket;
|
||||||
|
|
||||||
if (!m_results.contains(senderAddress)) {
|
if (!m_resultInverters.contains(senderAddress)) {
|
||||||
SpeedwireDiscoveryResult result;
|
SpeedwireDiscoveryResult result;
|
||||||
result.address = senderAddress;
|
result.address = senderAddress;
|
||||||
result.deviceType = SpeedwireInterface::DeviceTypeInverter;
|
result.deviceType = SpeedwireInterface::DeviceTypeInverter;
|
||||||
m_results.insert(senderAddress, result);
|
m_resultInverters.insert(senderAddress, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
if (m_networkDeviceInfos.hasHostAddress(senderAddress)) {
|
||||||
m_results[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress);
|
m_resultInverters[senderAddress].networkDeviceInfo = m_networkDeviceInfos.get(senderAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_results[senderAddress].modelId = inverterPacket.sourceModelId;
|
m_resultInverters[senderAddress].modelId = inverterPacket.sourceModelId;
|
||||||
m_results[senderAddress].serialNumber = inverterPacket.sourceSerialNumber;
|
m_resultInverters[senderAddress].serialNumber = inverterPacket.sourceSerialNumber;
|
||||||
|
|
||||||
|
// Send the default login request, maybe it activates the Energy meter measurment data streaming
|
||||||
|
|
||||||
|
SpeedwireInverter *inverter = nullptr;
|
||||||
|
if (m_inverters.contains(senderAddress)) {
|
||||||
|
inverter = m_inverters.value(senderAddress);
|
||||||
|
} else {
|
||||||
|
inverter = new SpeedwireInverter(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();
|
||||||
|
|
||||||
|
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 {
|
} else {
|
||||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Unhandled data received" << datagram.toHex();
|
qCWarning(dcSma()) << "SpeedwireDiscovery: Unhandled data received" << datagram.toHex();
|
||||||
return;
|
return;
|
||||||
@ -369,12 +395,12 @@ void SpeedwireDiscovery::processDatagram(const QHostAddress &senderAddress, quin
|
|||||||
|
|
||||||
void SpeedwireDiscovery::sendDiscoveryRequest()
|
void SpeedwireDiscovery::sendDiscoveryRequest()
|
||||||
{
|
{
|
||||||
if (m_multicastSocket->writeDatagram(Speedwire::discoveryDatagramMulticast(), m_multicastAddress, m_port) < 0) {
|
if (m_multicastSocket->writeDatagram(Speedwire::discoveryDatagramMulticast(), Speedwire::multicastAddress(), Speedwire::port()) < 0) {
|
||||||
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to send discovery datagram to multicast address" << m_multicastAddress.toString();
|
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to send discovery datagram to multicast address" << Speedwire::multicastAddress().toString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to multicast address" << m_multicastAddress.toString();
|
qCDebug(dcSma()) << "SpeedwireDiscovery: Sent successfully the discovery request to multicast address" << Speedwire::multicastAddress().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpeedwireDiscovery::evaluateDiscoveryFinished()
|
void SpeedwireDiscovery::evaluateDiscoveryFinished()
|
||||||
@ -386,10 +412,16 @@ void SpeedwireDiscovery::evaluateDiscoveryFinished()
|
|||||||
|
|
||||||
void SpeedwireDiscovery::finishDiscovery()
|
void SpeedwireDiscovery::finishDiscovery()
|
||||||
{
|
{
|
||||||
|
m_results = m_resultMeters.values() + m_resultInverters.values();
|
||||||
|
|
||||||
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) {
|
||||||
|
if (!m_multicastSocket->leaveMultic astGroup(Speedwire::multicastAddress())) {
|
||||||
|
qCWarning(dcSma()) << "SpeedwireDiscovery: Failed to leave multicast group" << Speedwire::multicastAddress().toString();
|
||||||
|
}
|
||||||
|
|
||||||
m_multicastSocket->close();
|
m_multicastSocket->close();
|
||||||
m_multicastSocket->deleteLater();
|
m_multicastSocket->deleteLater();
|
||||||
m_multicastSocket = nullptr;
|
m_multicastSocket = nullptr;
|
||||||
@ -401,6 +433,7 @@ void SpeedwireDiscovery::finishDiscovery()
|
|||||||
m_unicastSocket = nullptr;
|
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;
|
||||||
|
|||||||
@ -38,6 +38,7 @@
|
|||||||
#include <network/networkdevicediscovery.h>
|
#include <network/networkdevicediscovery.h>
|
||||||
|
|
||||||
#include "speedwire.h"
|
#include "speedwire.h"
|
||||||
|
#include "speedwireinverter.h"
|
||||||
#include "speedwireinterface.h"
|
#include "speedwireinterface.h"
|
||||||
|
|
||||||
class SpeedwireDiscovery : public QObject
|
class SpeedwireDiscovery : public QObject
|
||||||
@ -52,7 +53,7 @@ public:
|
|||||||
quint32 serialNumber = 0;
|
quint32 serialNumber = 0;
|
||||||
} SpeedwireDiscoveryResult;
|
} SpeedwireDiscoveryResult;
|
||||||
|
|
||||||
explicit SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, QObject *parent = nullptr);
|
explicit SpeedwireDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint32 localSerialNumber, QObject *parent = nullptr);
|
||||||
~SpeedwireDiscovery();
|
~SpeedwireDiscovery();
|
||||||
|
|
||||||
bool initialize(SpeedwireInterface::DeviceType deviceType = SpeedwireInterface::DeviceTypeUnknown);
|
bool initialize(SpeedwireInterface::DeviceType deviceType = SpeedwireInterface::DeviceTypeUnknown);
|
||||||
@ -71,14 +72,17 @@ private:
|
|||||||
SpeedwireInterface::DeviceType m_deviceType = SpeedwireInterface::DeviceTypeUnknown;
|
SpeedwireInterface::DeviceType m_deviceType = SpeedwireInterface::DeviceTypeUnknown;
|
||||||
QUdpSocket *m_multicastSocket = nullptr;
|
QUdpSocket *m_multicastSocket = nullptr;
|
||||||
QUdpSocket *m_unicastSocket = nullptr;
|
QUdpSocket *m_unicastSocket = nullptr;
|
||||||
QHostAddress m_multicastAddress = Speedwire::multicastAddress();
|
quint32 m_localSerialNumber = 0;
|
||||||
quint16 m_port = Speedwire::port();
|
|
||||||
bool m_initialized = false;
|
bool m_initialized = false;
|
||||||
|
|
||||||
// Discovery
|
// Discovery
|
||||||
QTimer m_multicastSearchRequestTimer;
|
QTimer m_multicastSearchRequestTimer;
|
||||||
NetworkDeviceInfos m_networkDeviceInfos;
|
NetworkDeviceInfos m_networkDeviceInfos;
|
||||||
QHash<QHostAddress, SpeedwireDiscoveryResult> m_results;
|
QList<SpeedwireDiscoveryResult> m_results;
|
||||||
|
QHash<QHostAddress, SpeedwireDiscoveryResult> m_resultMeters;
|
||||||
|
QHash<QHostAddress, SpeedwireDiscoveryResult> m_resultInverters;
|
||||||
|
|
||||||
|
QHash<QHostAddress, SpeedwireInverter *> m_inverters;
|
||||||
|
|
||||||
bool m_multicastRunning = false;
|
bool m_multicastRunning = false;
|
||||||
bool m_unicastRunning = false;
|
bool m_unicastRunning = false;
|
||||||
@ -88,7 +92,7 @@ private:
|
|||||||
|
|
||||||
bool setupUnicastSocket();
|
bool setupUnicastSocket();
|
||||||
void startUnicastDiscovery();
|
void startUnicastDiscovery();
|
||||||
;
|
|
||||||
void sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress);
|
void sendUnicastDiscoveryRequest(const QHostAddress &targetHostAddress);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|||||||
@ -31,9 +31,10 @@
|
|||||||
#include "speedwireinterface.h"
|
#include "speedwireinterface.h"
|
||||||
#include "extern-plugininfo.h"
|
#include "extern-plugininfo.h"
|
||||||
|
|
||||||
SpeedwireInterface::SpeedwireInterface(bool multicast, QObject *parent) :
|
SpeedwireInterface::SpeedwireInterface(bool multicast, quint32 sourceSerialNumber, QObject *parent) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
m_multicast(multicast)
|
m_multicast(multicast),
|
||||||
|
m_sourceSerialNumber(sourceSerialNumber)
|
||||||
{
|
{
|
||||||
|
|
||||||
m_socket = new QUdpSocket(this);
|
m_socket = new QUdpSocket(this);
|
||||||
@ -54,18 +55,18 @@ bool SpeedwireInterface::initialize(const QHostAddress &address)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If already initialized and multicast, nothing could habe changed...done here
|
// If already initialized and multicast, nothing could have changed...done here
|
||||||
if (m_initialized && m_multicast)
|
if (m_initialized && m_multicast)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
||||||
if (!m_socket->bind(QHostAddress::AnyIPv4, m_port, QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
|
if (!m_socket->bind(QHostAddress::AnyIPv4, Speedwire::port(), QAbstractSocket::ShareAddress | QAbstractSocket::ReuseAddressHint)) {
|
||||||
qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not bind to port" << m_port;
|
qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not bind to port" << Speedwire::port();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_multicast && !m_socket->joinMulticastGroup(m_multicastAddress)) {
|
if (m_multicast && !m_socket->joinMulticastGroup(Speedwire::multicastAddress())) {
|
||||||
qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not join multicast group" << m_multicastAddress.toString() << m_socket->errorString();
|
qCWarning(dcSma()) << "SpeedwireInterface: Initialization failed. Could not join multicast group" << Speedwire::multicastAddress().toString() << m_socket->errorString();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,8 +80,8 @@ void SpeedwireInterface::deinitialize()
|
|||||||
{
|
{
|
||||||
if (m_initialized) {
|
if (m_initialized) {
|
||||||
if (m_multicast) {
|
if (m_multicast) {
|
||||||
if (!m_socket->leaveMulticastGroup(m_multicastAddress)) {
|
if (!m_socket->leaveMulticastGroup(Speedwire::multicastAddress())) {
|
||||||
qCWarning(dcSma()) << "SpeedwireInterface: Failed to leave multicast group" << m_multicastAddress.toString();
|
qCWarning(dcSma()) << "SpeedwireInterface: Failed to leave multicast group" << Speedwire::multicastAddress().toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,11 +101,6 @@ bool SpeedwireInterface::initialized() const
|
|||||||
return m_initialized;
|
return m_initialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
quint16 SpeedwireInterface::sourceModelId() const
|
|
||||||
{
|
|
||||||
return m_sourceModelId;
|
|
||||||
}
|
|
||||||
|
|
||||||
quint32 SpeedwireInterface::sourceSerialNumber() const
|
quint32 SpeedwireInterface::sourceSerialNumber() const
|
||||||
{
|
{
|
||||||
return m_sourceSerialNumber;
|
return m_sourceSerialNumber;
|
||||||
@ -112,8 +108,8 @@ quint32 SpeedwireInterface::sourceSerialNumber() const
|
|||||||
|
|
||||||
void SpeedwireInterface::sendData(const QByteArray &data)
|
void SpeedwireInterface::sendData(const QByteArray &data)
|
||||||
{
|
{
|
||||||
qCDebug(dcSma()) << "SpeedwireInterface: -->" << m_address.toString() << m_port << data.toHex();
|
qCDebug(dcSma()) << "SpeedwireInterface: -->" << m_address.toString() << Speedwire::port() << data.toHex();
|
||||||
if (m_socket->writeDatagram(data, m_address, m_port) < 0) {
|
if (m_socket->writeDatagram(data, m_address, Speedwire::port()) < 0) {
|
||||||
qCWarning(dcSma()) << "SpeedwireInterface: failed to send data" << m_socket->errorString();
|
qCWarning(dcSma()) << "SpeedwireInterface: failed to send data" << m_socket->errorString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,7 +48,7 @@ public:
|
|||||||
};
|
};
|
||||||
Q_ENUM(DeviceType)
|
Q_ENUM(DeviceType)
|
||||||
|
|
||||||
explicit SpeedwireInterface(bool multicast, QObject *parent = nullptr);
|
explicit SpeedwireInterface(bool multicast, quint32 sourceSerialNumber, QObject *parent = nullptr);
|
||||||
~SpeedwireInterface();
|
~SpeedwireInterface();
|
||||||
|
|
||||||
bool initialize(const QHostAddress &address = QHostAddress());
|
bool initialize(const QHostAddress &address = QHostAddress());
|
||||||
@ -70,14 +70,11 @@ signals:
|
|||||||
private:
|
private:
|
||||||
QUdpSocket *m_socket = nullptr;
|
QUdpSocket *m_socket = nullptr;
|
||||||
QHostAddress m_address;
|
QHostAddress m_address;
|
||||||
quint16 m_port = Speedwire::port();
|
|
||||||
QHostAddress m_multicastAddress = Speedwire::multicastAddress();
|
|
||||||
bool m_multicast = false;
|
bool m_multicast = false;
|
||||||
bool m_initialized = false;
|
bool m_initialized = false;
|
||||||
|
|
||||||
// Requester
|
// Requester
|
||||||
quint16 m_sourceModelId = 0x007d;
|
quint32 m_sourceSerialNumber = 0;
|
||||||
quint32 m_sourceSerialNumber = 0x3a28be52;
|
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void readPendingDatagrams();
|
void readPendingDatagrams();
|
||||||
|
|||||||
@ -40,7 +40,7 @@ SpeedwireInverter::SpeedwireInverter(const QHostAddress &address, quint16 modelI
|
|||||||
m_serialNumber(serialNumber)
|
m_serialNumber(serialNumber)
|
||||||
{
|
{
|
||||||
qCDebug(dcSma()) << "Inverter: setup interface on" << m_address.toString();
|
qCDebug(dcSma()) << "Inverter: setup interface on" << m_address.toString();
|
||||||
m_interface = new SpeedwireInterface(false, this);
|
m_interface = new SpeedwireInterface(false, serialNumber, this);
|
||||||
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireInverter::processData);
|
connect(m_interface, &SpeedwireInterface::dataReceived, this, &SpeedwireInverter::processData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ SpeedwireInverterReply *SpeedwireInverter::sendIdentifyRequest()
|
|||||||
SpeedwireInverterRequest request;
|
SpeedwireInverterRequest request;
|
||||||
request.setPacketId(0x8001);
|
request.setPacketId(0x8001);
|
||||||
request.setCommand(Speedwire::CommandIdentify);
|
request.setCommand(Speedwire::CommandIdentify);
|
||||||
request.setRequestData(Speedwire::discoveryDatagramUnicast());
|
request.setRequestData(Speedwire::pingRequest(Speedwire::sourceModelId(), m_serialNumber));
|
||||||
return createReply(request);
|
return createReply(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,9 +171,7 @@ SpeedwireInverterReply *SpeedwireInverter::sendLoginRequest(const QString &passw
|
|||||||
QDataStream stream(&datagram, QIODevice::WriteOnly);
|
QDataStream stream(&datagram, QIODevice::WriteOnly);
|
||||||
buildDefaultHeader(stream, 58, 0xa0);
|
buildDefaultHeader(stream, 58, 0xa0);
|
||||||
|
|
||||||
// Reset the packet id counter, otherwise there will be no response
|
quint16 packetId = static_cast<quint16>(m_packetId++) | 0x8000;
|
||||||
//m_packetId = 0;
|
|
||||||
quint16 packetId = m_packetId++ | 0x8000;
|
|
||||||
Speedwire::Command command = Speedwire::CommandLogin;
|
Speedwire::Command command = Speedwire::CommandLogin;
|
||||||
|
|
||||||
// The payload is little endian encoded
|
// The payload is little endian encoded
|
||||||
@ -227,7 +225,7 @@ SpeedwireInverterReply *SpeedwireInverter::sendLogoutRequest()
|
|||||||
buildDefaultHeader(stream, 34);
|
buildDefaultHeader(stream, 34);
|
||||||
|
|
||||||
// Reset the packet id counter, otherwise there will be no response
|
// Reset the packet id counter, otherwise there will be no response
|
||||||
quint16 packetId = m_packetId++ | 0x8000;
|
quint16 packetId = static_cast<quint16>(m_packetId++) | 0x8000;
|
||||||
Speedwire::Command command = Speedwire::CommandLogout;
|
Speedwire::Command command = Speedwire::CommandLogout;
|
||||||
|
|
||||||
// The payload is little endian encoded
|
// The payload is little endian encoded
|
||||||
@ -241,7 +239,7 @@ SpeedwireInverterReply *SpeedwireInverter::sendLogoutRequest()
|
|||||||
stream << static_cast<quint16>(0x0300);
|
stream << static_cast<quint16>(0x0300);
|
||||||
|
|
||||||
// Source
|
// Source
|
||||||
stream << m_interface->sourceModelId();
|
stream << Speedwire::sourceModelId();
|
||||||
stream << m_interface->sourceSerialNumber();
|
stream << m_interface->sourceSerialNumber();
|
||||||
stream << static_cast<quint16>(0x0300);
|
stream << static_cast<quint16>(0x0300);
|
||||||
|
|
||||||
@ -275,7 +273,7 @@ SpeedwireInverterReply *SpeedwireInverter::sendSoftwareVersionRequest()
|
|||||||
buildDefaultHeader(stream, 38, 0xa0);
|
buildDefaultHeader(stream, 38, 0xa0);
|
||||||
|
|
||||||
// Reset the packet id counter, otherwise there will be no response
|
// Reset the packet id counter, otherwise there will be no response
|
||||||
quint16 packetId = m_packetId++ | 0x8000;
|
quint16 packetId = static_cast<quint16>(m_packetId++) | 0x8000;
|
||||||
Speedwire::Command command = Speedwire::CommandQueryDevice;
|
Speedwire::Command command = Speedwire::CommandQueryDevice;
|
||||||
|
|
||||||
// The payload is little endian encoded
|
// The payload is little endian encoded
|
||||||
@ -305,7 +303,7 @@ SpeedwireInverterReply *SpeedwireInverter::sendDeviceTypeRequest()
|
|||||||
buildDefaultHeader(stream, 38, 0xa0);
|
buildDefaultHeader(stream, 38, 0xa0);
|
||||||
|
|
||||||
// Reset the packet id counter, otherwise there will be no response
|
// Reset the packet id counter, otherwise there will be no response
|
||||||
quint16 packetId = m_packetId++ | 0x8000;
|
quint16 packetId = static_cast<quint16>(m_packetId++) | 0x8000;
|
||||||
Speedwire::Command command = Speedwire::CommandQueryDevice;
|
Speedwire::Command command = Speedwire::CommandQueryDevice;
|
||||||
|
|
||||||
// The payload is little endian encoded
|
// The payload is little endian encoded
|
||||||
@ -326,6 +324,36 @@ SpeedwireInverterReply *SpeedwireInverter::sendDeviceTypeRequest()
|
|||||||
return createReply(request);
|
return createReply(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SpeedwireInverterReply *SpeedwireInverter::sendBatteryInfoRequest()
|
||||||
|
{
|
||||||
|
qCDebug(dcSma()) << "Inverter: Sending battery info request to" << m_address.toString();
|
||||||
|
// Build the header
|
||||||
|
QByteArray datagram;
|
||||||
|
QDataStream stream(&datagram, QIODevice::WriteOnly);
|
||||||
|
buildDefaultHeader(stream, 38, 0xa0);
|
||||||
|
|
||||||
|
// Reset the packet id counter, otherwise there will be no response
|
||||||
|
quint16 packetId = static_cast<quint16>(m_packetId++) | 0x8000;
|
||||||
|
Speedwire::Command command = Speedwire::CommandQueryAc;
|
||||||
|
|
||||||
|
// The payload is little endian encoded
|
||||||
|
buildPacket(stream, command, packetId);
|
||||||
|
|
||||||
|
// 2 words
|
||||||
|
stream << static_cast<quint32>(0x00491E00);
|
||||||
|
stream << static_cast<quint32>(0x00495DFF);
|
||||||
|
|
||||||
|
// End of data
|
||||||
|
stream << static_cast<quint32>(0);
|
||||||
|
|
||||||
|
// Final datagram
|
||||||
|
SpeedwireInverterRequest request;
|
||||||
|
request.setPacketId(packetId);
|
||||||
|
request.setCommand(command);
|
||||||
|
request.setRequestData(datagram);
|
||||||
|
return createReply(request);
|
||||||
|
}
|
||||||
|
|
||||||
void SpeedwireInverter::startConnecting(const QString &password)
|
void SpeedwireInverter::startConnecting(const QString &password)
|
||||||
{
|
{
|
||||||
m_password = password;
|
m_password = password;
|
||||||
@ -431,7 +459,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 << m_interface->sourceModelId() << m_interface->sourceSerialNumber();
|
stream << Speedwire::sourceModelId() << m_interface->sourceSerialNumber();
|
||||||
// Destination Ctrl
|
// Destination Ctrl
|
||||||
stream << static_cast<quint16>(0x0100);
|
stream << static_cast<quint16>(0x0100);
|
||||||
|
|
||||||
@ -489,7 +517,7 @@ SpeedwireInverterReply *SpeedwireInverter::sendQueryRequest(Speedwire::Command c
|
|||||||
buildDefaultHeader(stream);
|
buildDefaultHeader(stream);
|
||||||
|
|
||||||
// Reset the packet id counter, otherwise there will be no response
|
// Reset the packet id counter, otherwise there will be no response
|
||||||
quint16 packetId = m_packetId++ | 0x8000;
|
quint16 packetId = static_cast<quint16>(m_packetId++) | 0x8000;
|
||||||
|
|
||||||
// The payload is little endian encoded
|
// The payload is little endian encoded
|
||||||
buildPacket(stream, command, packetId);
|
buildPacket(stream, command, packetId);
|
||||||
@ -514,20 +542,20 @@ void SpeedwireInverter::processSoftwareVersionResponse(const QByteArray &respons
|
|||||||
// 07000000 07000000 01348200 2ff5b261 00000000 00000000 feffffff feffffff 00055302 00055302 00000000 00000000 00000000
|
// 07000000 07000000 01348200 2ff5b261 00000000 00000000 feffffff feffffff 00055302 00055302 00000000 00000000 00000000
|
||||||
qCDebug(dcSma()) << "Inverter: Process software version request response" << response.toHex();
|
qCDebug(dcSma()) << "Inverter: Process software version request response" << response.toHex();
|
||||||
// TODO:
|
// TODO:
|
||||||
// QDataStream stream(response);
|
// QDataStream stream(response);
|
||||||
// stream.setByteOrder(QDataStream::LittleEndian);
|
// stream.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
|
||||||
// // First
|
// // First
|
||||||
// quint32 firstWord;
|
// quint32 firstWord;
|
||||||
// quint32 secondWord;
|
// quint32 secondWord;
|
||||||
// stream >> firstWord >> secondWord;
|
// stream >> firstWord >> secondWord;
|
||||||
// quint8 byte1, byte2, byte3, byte4;
|
// quint8 byte1, byte2, byte3, byte4;
|
||||||
// stream >> byte1 >> byte2 >> byte3 >> byte4;
|
// stream >> byte1 >> byte2 >> byte3 >> byte4;
|
||||||
|
|
||||||
// BCD
|
// BCD
|
||||||
// 00 82 34 01 ??
|
// 00 82 34 01 ??
|
||||||
// QString softwareVersion = QString("%1.%2.%3.%4").arg(byte1).arg(byte2).arg(byte3).arg(byte4);
|
// QString softwareVersion = QString("%1.%2.%3.%4").arg(byte1).arg(byte2).arg(byte3).arg(byte4);
|
||||||
// qCDebug(dcSma()) << "Inverter: Software version" << softwareVersion;
|
// qCDebug(dcSma()) << "Inverter: Software version" << softwareVersion;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -938,6 +966,46 @@ void SpeedwireInverter::processGridFrequencyResponse(const QByteArray &response)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpeedwireInverter::processBatteryInfoResponse(const QByteArray &response)
|
||||||
|
{
|
||||||
|
// 10000000 10000000
|
||||||
|
// 01574600 c20cbb61 89130000 89130000 89130000 89130000 010000000
|
||||||
|
// 0000000
|
||||||
|
qCDebug(dcSma()) << "Inverter: ################ Process battery info response" << response.toHex();
|
||||||
|
// QDataStream stream(response);
|
||||||
|
// stream.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
// quint32 firstWord, secondWord;
|
||||||
|
// stream >> firstWord >> secondWord;
|
||||||
|
|
||||||
|
//// // Each line has 7 words
|
||||||
|
//// quint32 measurementId;
|
||||||
|
//// quint32 measurementType; // ?
|
||||||
|
|
||||||
|
////// while (!stream.atEnd()) {
|
||||||
|
////// // First row
|
||||||
|
////// stream >> measurementId;
|
||||||
|
|
||||||
|
////// // End of data, we are done
|
||||||
|
////// if (measurementId == 0)
|
||||||
|
////// return;
|
||||||
|
|
||||||
|
////// // Unknown
|
||||||
|
////// stream >> measurementType;
|
||||||
|
|
||||||
|
////// quint8 measurmentNumber = static_cast<quint8>(measurementId & 0xff);
|
||||||
|
////// measurementId = measurementId & 0x00ffff00;
|
||||||
|
|
||||||
|
////// // Read measurent lines
|
||||||
|
////// if (measurementId == 0x465700 && measurmentNumber == 0x01) {
|
||||||
|
////// quint32 frequency;
|
||||||
|
////// stream >> frequency;
|
||||||
|
////// m_gridFrequency = readValue(frequency, 100.0);
|
||||||
|
////// qCDebug(dcSma()) << "Inverter: Grid frequency" << m_gridFrequency << "Hz";
|
||||||
|
////// readUntilEndOfMeasurement(stream);
|
||||||
|
////// }
|
||||||
|
////// }
|
||||||
|
}
|
||||||
|
|
||||||
void SpeedwireInverter::processInverterStatusResponse(const QByteArray &response)
|
void SpeedwireInverter::processInverterStatusResponse(const QByteArray &response)
|
||||||
{
|
{
|
||||||
// 00000000 00000000
|
// 00000000 00000000
|
||||||
@ -1083,43 +1151,88 @@ void SpeedwireInverter::setState(State state)
|
|||||||
setReachable(false);
|
setReachable(false);
|
||||||
break;
|
break;
|
||||||
case StateInitializing: {
|
case StateInitializing: {
|
||||||
// Try to fetch ac power
|
|
||||||
qCDebug(dcSma()) << "Inverter: Request AC power...";
|
if (m_modelId == 372) {
|
||||||
SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00464000, 0x004642ff);
|
// Home manager 2.0, no login, just fetch...testing
|
||||||
connect(reply, &SpeedwireInverterReply::finished, this, [=](){
|
|
||||||
if (reply->error() != SpeedwireInverterReply::ErrorNoError) {
|
// ############# TESTING ##########
|
||||||
if (reply->error() == SpeedwireInverterReply::ErrorTimeout) {
|
|
||||||
|
// Query battery info
|
||||||
|
qCDebug(dcSma()) << "Inverter: Request battery info...";
|
||||||
|
SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00491e00, 0x00495dff); // Battery infos
|
||||||
|
connect(reply, &SpeedwireInverterReply::finished, this, [=](){
|
||||||
|
if (reply->error() != SpeedwireInverterReply::ErrorNoError) {
|
||||||
qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error();
|
qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error();
|
||||||
|
// setState(StateDisconnected);
|
||||||
// TODO: try to send identify request and retry 3 times before giving up,
|
// return;
|
||||||
// still need to figure out why the inverter stops responding sometimes and how we can
|
|
||||||
// make it communicative again, a reconfugre always fixes this issue...somehow...
|
|
||||||
|
|
||||||
setState(StateDisconnected);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reachable, but received an inverter error, probably not logged
|
qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command();
|
||||||
if (reply->error() == SpeedwireInverterReply::ErrorInverterError) {
|
processBatteryInfoResponse(reply->responsePayload());
|
||||||
qCDebug(dcSma()) << "Inverter: Query data request finished with inverter error. Try to login...";
|
|
||||||
setState(StateLogin);
|
|
||||||
return;
|
qCDebug(dcSma()) << "Inverter: Request battery SoC...";
|
||||||
|
SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00295A00, 0x00295AFF); // SoC
|
||||||
|
connect(reply, &SpeedwireInverterReply::finished, this, [=](){
|
||||||
|
if (reply->error() != SpeedwireInverterReply::ErrorNoError) {
|
||||||
|
qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error();
|
||||||
|
// setState(StateDisconnected);
|
||||||
|
// return;
|
||||||
|
} else {
|
||||||
|
processBatteryInfoResponse(reply->responsePayload());
|
||||||
|
}
|
||||||
|
qCDebug(dcSma()) << "Inverter: Request battery termperature...";
|
||||||
|
SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00491E00, 0x00495DFF); // SoC
|
||||||
|
connect(reply, &SpeedwireInverterReply::finished, this, [=](){
|
||||||
|
if (reply->error() != SpeedwireInverterReply::ErrorNoError) {
|
||||||
|
qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error();
|
||||||
|
// setState(StateDisconnected);
|
||||||
|
// return;
|
||||||
|
} else {
|
||||||
|
processBatteryInfoResponse(reply->responsePayload());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Try to fetch ac power
|
||||||
|
qCDebug(dcSma()) << "Inverter: Request AC power...";
|
||||||
|
SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00464000, 0x004642ff);
|
||||||
|
connect(reply, &SpeedwireInverterReply::finished, this, [=](){
|
||||||
|
if (reply->error() != SpeedwireInverterReply::ErrorNoError) {
|
||||||
|
if (reply->error() == SpeedwireInverterReply::ErrorTimeout) {
|
||||||
|
qCWarning(dcSma()) << "Inverter: Failed to query data from inverter:" << reply->request().command() << reply->error();
|
||||||
|
|
||||||
|
// TODO: try to send identify request and retry 3 times before giving up,
|
||||||
|
// still need to figure out why the inverter stops responding sometimes and how we can
|
||||||
|
// make it communicative again, a reconfugre always fixes this issue...somehow...
|
||||||
|
|
||||||
|
setState(StateDisconnected);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reachable, but received an inverter error, probably not logged
|
||||||
|
if (reply->error() == SpeedwireInverterReply::ErrorInverterError) {
|
||||||
|
qCDebug(dcSma()) << "Inverter: Query data request finished with inverter error. Try to login...";
|
||||||
|
setState(StateLogin);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// We where able to read data...emit the signal for the setup just incase
|
// We where able to read data...emit the signal for the setup just incase
|
||||||
emit loginFinished(true);
|
emit loginFinished(true);
|
||||||
|
|
||||||
qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command();
|
qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command();
|
||||||
processAcPowerResponse(reply->responseData());
|
processAcPowerResponse(reply->responseData());
|
||||||
|
|
||||||
|
if (m_deviceInformationFetched) {
|
||||||
if (m_deviceInformationFetched) {
|
setState(StateQueryData);
|
||||||
setState(StateQueryData);
|
} else {
|
||||||
} else {
|
setState(StateGetInformation);
|
||||||
setState(StateGetInformation);
|
}
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case StateLogin: {
|
case StateLogin: {
|
||||||
@ -1248,6 +1361,7 @@ void SpeedwireInverter::setState(State state)
|
|||||||
qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command();
|
qCDebug(dcSma()) << "Inverter: Query request finished successfully" << reply->request().command();
|
||||||
processAcTotalPowerResponse(reply->responsePayload());
|
processAcTotalPowerResponse(reply->responsePayload());
|
||||||
|
|
||||||
|
|
||||||
// Query grid frequency
|
// Query grid frequency
|
||||||
qCDebug(dcSma()) << "Inverter: Request grid frequency...";
|
qCDebug(dcSma()) << "Inverter: Request grid frequency...";
|
||||||
SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00465700, 0x004657ff);
|
SpeedwireInverterReply *reply = sendQueryRequest(Speedwire::CommandQueryAc, 0x00465700, 0x004657ff);
|
||||||
|
|||||||
@ -100,6 +100,7 @@ public:
|
|||||||
SpeedwireInverterReply *sendLogoutRequest();
|
SpeedwireInverterReply *sendLogoutRequest();
|
||||||
SpeedwireInverterReply *sendSoftwareVersionRequest();
|
SpeedwireInverterReply *sendSoftwareVersionRequest();
|
||||||
SpeedwireInverterReply *sendDeviceTypeRequest();
|
SpeedwireInverterReply *sendDeviceTypeRequest();
|
||||||
|
SpeedwireInverterReply *sendBatteryInfoRequest();
|
||||||
|
|
||||||
// Start connecting
|
// Start connecting
|
||||||
void startConnecting(const QString &password = "0000");
|
void startConnecting(const QString &password = "0000");
|
||||||
@ -124,7 +125,7 @@ private:
|
|||||||
|
|
||||||
bool m_reachable = false;
|
bool m_reachable = false;
|
||||||
State m_state = StateDisconnected;
|
State m_state = StateDisconnected;
|
||||||
quint16 m_packetId = 1;
|
quint8 m_packetId = 1;
|
||||||
|
|
||||||
bool m_deviceInformationFetched = false;
|
bool m_deviceInformationFetched = false;
|
||||||
|
|
||||||
@ -185,6 +186,7 @@ private:
|
|||||||
void processDcVoltageCurrentResponse(const QByteArray &response);
|
void processDcVoltageCurrentResponse(const QByteArray &response);
|
||||||
void processEnergyProductionResponse(const QByteArray &response);
|
void processEnergyProductionResponse(const QByteArray &response);
|
||||||
void processGridFrequencyResponse(const QByteArray &response);
|
void processGridFrequencyResponse(const QByteArray &response);
|
||||||
|
void processBatteryInfoResponse(const QByteArray &response);
|
||||||
void processInverterStatusResponse(const QByteArray &response);
|
void processInverterStatusResponse(const QByteArray &response);
|
||||||
|
|
||||||
void readUntilEndOfMeasurement(QDataStream &stream);
|
void readUntilEndOfMeasurement(QDataStream &stream);
|
||||||
|
|||||||
Reference in New Issue
Block a user