SunSpec: Improve discovery and add slave ID scanning

pull/133/head
Simon Stürz 2023-05-31 11:27:20 +02:00
parent 0bdfeb81dc
commit ae3a1cb407
3 changed files with 94 additions and 56 deletions

View File

@ -133,7 +133,13 @@ void IntegrationPluginSunSpec::discoverThings(ThingDiscoveryInfo *info)
return;
}
SunSpecDiscovery *discovery = new SunSpecDiscovery(hardwareManager()->networkDeviceDiscovery(), 1, info);
QList<quint16> slaveIds = {1, 2};
SunSpecDataPoint::ByteOrder byteOrder = SunSpecDataPoint::ByteOrderLittleEndian;
if (info->thingClassId() == solarEdgeConnectionThingClassId) {
byteOrder = SunSpecDataPoint::ByteOrderBigEndian;
}
SunSpecDiscovery *discovery = new SunSpecDiscovery(hardwareManager()->networkDeviceDiscovery(), slaveIds, byteOrder, info);
// Note: we could add here more
connect(discovery, &SunSpecDiscovery::discoveryFinished, info, [=](){
foreach (const SunSpecDiscovery::Result &result, discovery->results()) {
@ -189,6 +195,7 @@ void IntegrationPluginSunSpec::discoverThings(ThingDiscoveryInfo *info)
ParamList params;
params << Param(m_connectionPortParamTypeIds.value(info->thingClassId()), result.port);
params << Param(m_connectionMacAddressParamTypeIds.value(info->thingClassId()), result.networkDeviceInfo.macAddress());
params << Param(m_connectionSlaveIdParamTypeIds.value(info->thingClassId()), result.slaveId);
descriptor.setParams(params);
info->addThingDescriptor(descriptor);
}

View File

@ -34,10 +34,11 @@
#include <models/sunspecmodelfactory.h>
#include <models/sunspeccommonmodel.h>
SunSpecDiscovery::SunSpecDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 modbusAddress, QObject *parent)
SunSpecDiscovery::SunSpecDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, const QList<quint16> &slaveIds, SunSpecDataPoint::ByteOrder byteOrder, QObject *parent)
: QObject{parent},
m_networkDeviceDiscovery{networkDeviceDiscovery},
m_modbusAddress{modbusAddress}
m_slaveIds{slaveIds},
m_byteOrder{byteOrder}
{
m_scanPorts.append(502);
m_scanPorts.append(1502);
@ -79,74 +80,98 @@ void SunSpecDiscovery::startDiscovery()
});
}
void SunSpecDiscovery::testNextConnection(const QHostAddress &address)
{
if (!m_pendingConnectionAttempts.contains(address))
return;
SunSpecConnection *connection = m_pendingConnectionAttempts[address].dequeue();
if (m_pendingConnectionAttempts.value(address).isEmpty())
m_pendingConnectionAttempts.remove(address);
qCDebug(dcSunSpec()) << "Discovery: Start searching on" << QString("%1:%2").arg(address.toString()).arg(connection->port()) << "slave ID:" << connection->slaveId();
// Try to connect, maybe it works, maybe not...
if (!connection->connectDevice()) {
qCDebug(dcSunSpec()) << "Discovery: Failed to connect to" << QString("%1:%2").arg(address.toString()).arg(connection->port()) << "slave ID:" << connection->slaveId() << "Continue...";;
cleanupConnection(connection);
}
}
void SunSpecDiscovery::checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo)
{
// Create a sunspec connection and try to initialize it (read models).
if (m_verifiedNetworkDeviceInfos.contains(networkDeviceInfo))
return;
// Create a connection queue for this network device
QQueue<SunSpecConnection *> connectionQueue;
// Check all ports for this host
foreach (quint16 port, m_scanPorts) {
SunSpecConnection *connection = new SunSpecConnection(networkDeviceInfo.address(), port, m_modbusAddress, this);
m_connections.append(connection);
m_verifiedNetworkDeviceInfos.append(networkDeviceInfo);
connect(connection, &SunSpecConnection::connectedChanged, this, [=](bool connected){
if (!connected) {
// Disconnected ... done with this connection
cleanupConnection(connection);
return;
}
foreach (quint16 slaveId, m_slaveIds) {
// Modbus TCP connected, try to discovery sunspec models...
connect(connection, &SunSpecConnection::discoveryFinished, this, [=](bool success){
if (!success) {
qCDebug(dcSunSpec()) << "Discovery: SunSpec discovery failed on" << networkDeviceInfo.address().toString() << "Continue...";;
SunSpecConnection *connection = new SunSpecConnection(networkDeviceInfo.address(), port, slaveId, m_byteOrder, this);
connection->setNumberOfRetries(1);
connection->setTimeout(500);
m_connections.append(connection);
connectionQueue.enqueue(connection);
connect(connection, &SunSpecConnection::connectedChanged, this, [=](bool connected){
if (!connected) {
// Disconnected ... done with this connection
cleanupConnection(connection);
return;
}
// 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(networkDeviceInfo.address().toString()).arg(port) << "slave ID:" << slaveId << "Continue...";;
cleanupConnection(connection);
return;
}
Result result;
result.networkDeviceInfo = networkDeviceInfo;
result.port = connection->port();
// Success, we found some sunspec models here, let's read some infomation from the models
qCDebug(dcSunSpec()) << "Discovery: --> Found SunSpec devices on" << result.networkDeviceInfo << "port" << result.port;
foreach (SunSpecModel *model, connection->models()) {
if (model->modelId() == SunSpecModelFactory::ModelIdCommon) {
SunSpecCommonModel *commonModel = qobject_cast<SunSpecCommonModel *>(model);
QString manufacturer = commonModel->manufacturer();
if (!manufacturer.isEmpty() && !result.modelManufacturers.contains(manufacturer)) {
result.modelManufacturers.append(manufacturer);
Result result;
result.networkDeviceInfo = networkDeviceInfo;
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<SunSpecCommonModel *>(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(networkDeviceInfo.address().toString()).arg(port) << "slave ID:" << slaveId << "Continue...";;
cleanupConnection(connection);
}
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" << networkDeviceInfo.address().toString() << "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" << networkDeviceInfo.address().toString() << "Continue...";;
cleanupConnection(connection);
}
});
// Try to connect, maybe it works, maybe not...
connection->connectDevice();
// 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(networkDeviceInfo.address().toString()).arg(port) << "slave ID:" << slaveId << "Continue...";;
cleanupConnection(connection);
}
});
}
}
m_pendingConnectionAttempts[networkDeviceInfo.address()] = connectionQueue;
testNextConnection(networkDeviceInfo.address());
}
void SunSpecDiscovery::cleanupConnection(SunSpecConnection *connection)
@ -154,6 +179,8 @@ void SunSpecDiscovery::cleanupConnection(SunSpecConnection *connection)
m_connections.removeAll(connection);
connection->disconnectDevice();
connection->deleteLater();
testNextConnection(connection->hostAddress());
}
void SunSpecDiscovery::finishDiscovery()

View File

@ -31,6 +31,7 @@
#ifndef SUNSPECDISCOVERY_H
#define SUNSPECDISCOVERY_H
#include <QQueue>
#include <QObject>
#include <QDateTime>
@ -41,10 +42,11 @@ class SunSpecDiscovery : public QObject
{
Q_OBJECT
public:
explicit SunSpecDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, quint16 modbusAddress = 1, QObject *parent = nullptr);
explicit SunSpecDiscovery(NetworkDeviceDiscovery *networkDeviceDiscovery, const QList<quint16> &slaveIds, SunSpecDataPoint::ByteOrder byteOrder = SunSpecDataPoint::ByteOrderLittleEndian, QObject *parent = nullptr);
typedef struct Result {
NetworkDeviceInfo networkDeviceInfo;
quint16 port;
quint16 slaveId;
QStringList modelManufacturers;
} Result;
@ -58,16 +60,18 @@ signals:
private:
NetworkDeviceDiscovery *m_networkDeviceDiscovery = nullptr;
quint16 m_modbusAddress;
QList<quint16> m_scanPorts;
QList<quint16> m_slaveIds;
SunSpecDataPoint::ByteOrder m_byteOrder;
QDateTime m_startDateTime;
NetworkDeviceInfos m_verifiedNetworkDeviceInfos;
QHash<QHostAddress, QQueue<SunSpecConnection *>> m_pendingConnectionAttempts;
QList<SunSpecConnection *> m_connections;
QList<Result> m_results;
void testNextConnection(const QHostAddress &address);
void checkNetworkDevice(const NetworkDeviceInfo &networkDeviceInfo);
void cleanupConnection(SunSpecConnection *connection);