From 243d73551f597220ef0835e2f67df973f2f1779f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 17 Oct 2025 11:17:31 +0200 Subject: [PATCH] SolarEdge: Update gateway thing classes and introduce slaveId discovery param --- sunspec/integrationpluginsunspec.cpp | 17 ++-- sunspec/integrationpluginsunspec.h | 1 + sunspec/integrationpluginsunspec.json | 22 ++++- sunspec/sunspecdiscovery.cpp | 126 +++++++++++++------------- sunspec/sunspecdiscovery.h | 4 +- 5 files changed, 95 insertions(+), 75 deletions(-) diff --git a/sunspec/integrationpluginsunspec.cpp b/sunspec/integrationpluginsunspec.cpp index fb62eb6..2531d65 100644 --- a/sunspec/integrationpluginsunspec.cpp +++ b/sunspec/integrationpluginsunspec.cpp @@ -70,6 +70,9 @@ IntegrationPluginSunSpec::IntegrationPluginSunSpec() void IntegrationPluginSunSpec::init() { // SunSpec connection params + m_connectionDiscoverySlaveIdParamTypeIds.insert(sunspecConnectionThingClassId, sunspecConnectionDiscoverySlaveIdParamTypeId); + m_connectionDiscoverySlaveIdParamTypeIds.insert(solarEdgeConnectionThingClassId, solarEdgeConnectionDiscoverySlaveIdParamTypeId); + m_connectionMacAddressParamTypeIds.insert(sunspecConnectionThingClassId, sunspecConnectionThingMacAddressParamTypeId); m_connectionMacAddressParamTypeIds.insert(solarEdgeConnectionThingClassId, solarEdgeConnectionThingMacAddressParamTypeId); @@ -139,13 +142,14 @@ void IntegrationPluginSunSpec::discoverThings(ThingDiscoveryInfo *info) return; } - QList slaveIds = {1, 2}; - SunSpecDataPoint::ByteOrder byteOrder = SunSpecDataPoint::ByteOrderLittleEndian; - if (info->thingClassId() == solarEdgeConnectionThingClassId) { - byteOrder = SunSpecDataPoint::ByteOrderBigEndian; - } - SunSpecDiscovery *discovery = new SunSpecDiscovery(hardwareManager()->networkDeviceDiscovery(), slaveIds, byteOrder, info); + SunSpecDataPoint::ByteOrder byteOrder = SunSpecDataPoint::ByteOrderLittleEndian; + if (info->thingClassId() == solarEdgeConnectionThingClassId) + byteOrder = SunSpecDataPoint::ByteOrderBigEndian; + + quint16 discoverySlaveId = info->params().paramValue(m_connectionDiscoverySlaveIdParamTypeIds.value(info->thingClassId())).toUInt(); + + SunSpecDiscovery *discovery = new SunSpecDiscovery(hardwareManager()->networkDeviceDiscovery(), discoverySlaveId, byteOrder, info); // Note: we could add here more connect(discovery, &SunSpecDiscovery::discoveryFinished, info, [this, info, discovery](){ foreach (const SunSpecDiscovery::Result &result, discovery->results()) { @@ -247,7 +251,6 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) } } - // Create the monitor NetworkDeviceMonitor *monitor = hardwareManager()->networkDeviceDiscovery()->registerMonitor(thing); if (!monitor) { diff --git a/sunspec/integrationpluginsunspec.h b/sunspec/integrationpluginsunspec.h index 36ef6ca..9dd84d9 100644 --- a/sunspec/integrationpluginsunspec.h +++ b/sunspec/integrationpluginsunspec.h @@ -62,6 +62,7 @@ public: private: // SunSpec Connection params map + QHash m_connectionDiscoverySlaveIdParamTypeIds; QHash m_connectionMacAddressParamTypeIds; QHash m_connectionAddressParamTypeIds; QHash m_connectionHostNameParamTypeIds; diff --git a/sunspec/integrationpluginsunspec.json b/sunspec/integrationpluginsunspec.json index fa3ad06..6696477 100644 --- a/sunspec/integrationpluginsunspec.json +++ b/sunspec/integrationpluginsunspec.json @@ -39,9 +39,18 @@ "name": "sunspecConnection", "displayName": "SunSpec Generic", "id": "f51853f3-8815-4cf3-b337-45cda1f3e6d5", - "createMethods": [ "Discovery" ], + "createMethods": [ "discovery", "user" ], "interfaces": [ "gateway", "networkdevice" ], "providedInterfaces": [ "solarinverter", "energymeter", "energystorage"], + "discoveryParamTypes": [ + { + "id": "50e027aa-ab15-44df-80d3-71964aee44af", + "name":"slaveId", + "displayName": "Slave ID", + "type": "int", + "defaultValue": 1 + } + ], "paramTypes": [ { "id": "3567b389-9d42-48f9-a29b-d18388fb36a1", @@ -1474,9 +1483,18 @@ "name": "solarEdgeConnection", "displayName": "SolarEdge", "id": "7a92bf65-b443-4491-a012-2bec35eb5bf0", - "createMethods": [ "Discovery" ], + "createMethods": [ "discovery", "user" ], "interfaces": [ "gateway", "networkdevice" ], "providedInterfaces": [ "solarinverter", "energymeter", "energystorage" ], + "discoveryParamTypes": [ + { + "id": "9025a4e9-d994-45c7-9a00-62f7abf47107", + "name":"slaveId", + "displayName": "Slave ID", + "type": "int", + "defaultValue": 1 + } + ], "paramTypes": [ { "id": "9c2bafd0-6d56-42e0-8ef3-c4940b4f18b5", diff --git a/sunspec/sunspecdiscovery.cpp b/sunspec/sunspecdiscovery.cpp index 4f27b1f..cd15f49 100644 --- a/sunspec/sunspecdiscovery.cpp +++ b/sunspec/sunspecdiscovery.cpp @@ -34,10 +34,10 @@ #include #include -SunSpecDiscovery::SunSpecDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, const QList &slaveIds, SunSpecDataPoint::ByteOrder byteOrder, QObject *parent) +SunSpecDiscovery::SunSpecDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 slaveId, SunSpecDataPoint::ByteOrder byteOrder, QObject *parent) : QObject{parent}, m_networkDeviceDiscovery{networkDeviceDiscovery}, - m_slaveIds{slaveIds}, + m_slaveId{slaveId}, m_byteOrder{byteOrder} { m_scanPorts.append(502); @@ -130,81 +130,79 @@ void SunSpecDiscovery::checkNetworkDevice(const QHostAddress &address) // Check all ports for this host foreach (quint16 port, m_scanPorts) { - foreach (quint16 slaveId, m_slaveIds) { - SunSpecConnection *connection = new SunSpecConnection(address, port, slaveId, m_byteOrder, this); - connection->setNumberOfRetries(1); - connection->setTimeout(500); - m_connections.append(connection); - connectionQueue.enqueue(connection); + SunSpecConnection *connection = new SunSpecConnection(address, port, m_slaveId, m_byteOrder, this); + connection->setNumberOfRetries(1); + connection->setTimeout(500); + m_connections.append(connection); + connectionQueue.enqueue(connection); - connect(connection, &SunSpecConnection::connectedChanged, this, [this, connection, connectionQueue, address](bool connected){ - if (!connected) { - // Disconnected ... done with this connection + connect(connection, &SunSpecConnection::connectedChanged, this, [this, connection, connectionQueue, address](bool connected){ + if (!connected) { + // Disconnected ... done with this connection + cleanupConnection(connection); + return; + } + + // Successfully connected, we can stop the connection timer which takes care about blocking connection attempts + if (m_connectionTimers.contains(connection)) { + QTimer *connectionTimer = m_connectionTimers.take(connection); + connectionTimer->stop(); + connectionTimer->deleteLater(); + } + + // Modbus TCP connected, try to discovery sunspec models... + connect(connection, &SunSpecConnection::discoveryFinished, this, [=](bool success){ + if (!success) { + qCDebug(dcSunSpec()) << "Discovery: SunSpec discovery failed on" + << QString("%1:%2").arg(address.toString()).arg(connection->port()) + << "slave ID:" << connection->slaveId() << "Continue..."; cleanupConnection(connection); return; } - // Successfully connected, we can stop the connection timer which takes care about blocking connection attempts - if (m_connectionTimers.contains(connection)) { - QTimer *connectionTimer = m_connectionTimers.take(connection); - connectionTimer->stop(); - connectionTimer->deleteLater(); - } + // Success, we found some sunspec models here, let's read some infomation from the models - // Modbus TCP connected, try to discovery sunspec models... - connect(connection, &SunSpecConnection::discoveryFinished, this, [=](bool success){ - if (!success) { - qCDebug(dcSunSpec()) << "Discovery: SunSpec discovery failed on" - << QString("%1:%2").arg(address.toString()).arg(connection->port()) - << "slave ID:" << connection->slaveId() << "Continue..."; - cleanupConnection(connection); - return; - } + Result result; + result.address = address; + result.port = connection->port(); + result.slaveId = connection->slaveId(); - // Success, we found some sunspec models here, let's read some infomation from the models - - Result result; - result.address = address; - result.port = connection->port(); - result.slaveId = connection->slaveId(); - - qCDebug(dcSunSpec()) << "Discovery: --> Found SunSpec devices on" << result.networkDeviceInfo << "port" << result.port << "slave ID:" << result.slaveId; - foreach (SunSpecModel *model, connection->models()) { - if (model->modelId() == SunSpecModelFactory::ModelIdCommon) { - SunSpecCommonModel *commonModel = qobject_cast(model); - QString manufacturer = commonModel->manufacturer(); - if (!manufacturer.isEmpty() && !result.modelManufacturers.contains(manufacturer)) { - result.modelManufacturers.append(manufacturer); - } + qCDebug(dcSunSpec()) << "Discovery: --> Found SunSpec devices on" << result.networkDeviceInfo << "port" << result.port << "slave ID:" << result.slaveId; + foreach (SunSpecModel *model, connection->models()) { + if (model->modelId() == SunSpecModelFactory::ModelIdCommon) { + SunSpecCommonModel *commonModel = qobject_cast(model); + QString manufacturer = commonModel->manufacturer(); + if (!manufacturer.isEmpty() && !result.modelManufacturers.contains(manufacturer)) { + result.modelManufacturers.append(manufacturer); } } - - m_results.append(result); - - // Done with this connection - cleanupConnection(connection); - }); - - // Run SunSpec discovery on connection... - if (!connection->startDiscovery()) { - qCDebug(dcSunSpec()) << "Discovery: Unable to discover SunSpec data on connection" - << QString("%1:%2").arg(address.toString()).arg(connection->port()) - << "slave ID:" << connection->slaveId() << "Continue..."; - cleanupConnection(connection); } + + m_results.append(result); + + // Done with this connection + cleanupConnection(connection); }); - // If we get any error...skip this host... - connect(connection->modbusTcpClient(), &QModbusTcpClient::errorOccurred, this, [=](QModbusDevice::Error error){ - if (error != QModbusDevice::NoError) { - qCDebug(dcSunSpec()) << "Discovery: Connection error on" - << QString("%1:%2").arg(address.toString()).arg(connection->port()) - << "slave ID:" << connection->slaveId() << "Continue..."; - cleanupConnection(connection); - } - }); - } + // Run SunSpec discovery on connection... + if (!connection->startDiscovery()) { + qCDebug(dcSunSpec()) << "Discovery: Unable to discover SunSpec data on connection" + << QString("%1:%2").arg(address.toString()).arg(connection->port()) + << "slave ID:" << connection->slaveId() << "Continue..."; + cleanupConnection(connection); + } + }); + + // If we get any error...skip this host... + connect(connection->modbusTcpClient(), &QModbusTcpClient::errorOccurred, this, [=](QModbusDevice::Error error){ + if (error != QModbusDevice::NoError) { + qCDebug(dcSunSpec()) << "Discovery: Connection error on" + << QString("%1:%2").arg(address.toString()).arg(connection->port()) + << "slave ID:" << connection->slaveId() << "Continue..."; + cleanupConnection(connection); + } + }); } m_pendingConnectionAttempts[address] = connectionQueue; diff --git a/sunspec/sunspecdiscovery.h b/sunspec/sunspecdiscovery.h index eedeb54..0be6aab 100644 --- a/sunspec/sunspecdiscovery.h +++ b/sunspec/sunspecdiscovery.h @@ -42,7 +42,7 @@ class SunSpecDiscovery : public QObject { Q_OBJECT public: - explicit SunSpecDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, const QList &slaveIds, SunSpecDataPoint::ByteOrder byteOrder = SunSpecDataPoint::ByteOrderLittleEndian, QObject *parent = nullptr); + explicit SunSpecDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 slaveId, SunSpecDataPoint::ByteOrder byteOrder = SunSpecDataPoint::ByteOrderLittleEndian, QObject *parent = nullptr); typedef struct Result { QHostAddress address; NetworkDeviceInfo networkDeviceInfo; @@ -62,7 +62,7 @@ signals: private: NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr; QList m_scanPorts; - QList m_slaveIds; + quint16 m_slaveId = 1; SunSpecDataPoint::ByteOrder m_byteOrder; QDateTime m_startDateTime;