From acb5dbe9a267ee57c39ceba20c1652a14caf698d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 7 Jun 2021 15:57:42 +0200 Subject: [PATCH 1/7] Update iDM plugin and add network discovery --- idm/idm.cpp | 20 +++++------- idm/idm.h | 2 +- idm/integrationpluginidm.cpp | 58 ++++++++++++++++++++++++++++------- idm/integrationpluginidm.h | 2 +- idm/integrationpluginidm.json | 15 +++++++-- 5 files changed, 69 insertions(+), 28 deletions(-) diff --git a/idm/idm.cpp b/idm/idm.cpp index 6890cae..dc96863 100644 --- a/idm/idm.cpp +++ b/idm/idm.cpp @@ -40,14 +40,10 @@ Idm::Idm(const QHostAddress &address, QObject *parent) : { qCDebug(dcIdm()) << "iDM: Creating iDM connection" << m_hostAddress.toString(); m_modbusMaster = new ModbusTCPMaster(address, 502, this); - - if (m_modbusMaster) { - qCDebug(dcIdm()) << "iDM: Created ModbusTCPMaster"; - connect(m_modbusMaster, &ModbusTCPMaster::receivedHoldingRegister, this, &Idm::onReceivedHoldingRegister); - connect(m_modbusMaster, &ModbusTCPMaster::readRequestError, this, &Idm::onModbusError); - connect(m_modbusMaster, &ModbusTCPMaster::writeRequestError, this, &Idm::onModbusError); - connect(m_modbusMaster, &ModbusTCPMaster::writeRequestExecuted, this, &Idm::writeRequestExecuted); - } + connect(m_modbusMaster, &ModbusTCPMaster::receivedHoldingRegister, this, &Idm::onReceivedHoldingRegister); + connect(m_modbusMaster, &ModbusTCPMaster::readRequestError, this, &Idm::onModbusError); + connect(m_modbusMaster, &ModbusTCPMaster::writeRequestError, this, &Idm::onModbusError); + connect(m_modbusMaster, &ModbusTCPMaster::writeRequestExecuted, this, &Idm::writeRequestExecuted); } Idm::~Idm() @@ -61,21 +57,21 @@ bool Idm::connectDevice() return m_modbusMaster->connectDevice(); } -QHostAddress Idm::getIdmAddress() const +QHostAddress Idm::address() const { return m_hostAddress; } void Idm::getStatus() { - //this request starts an update cycle + // This request starts an update cycle m_modbusMaster->readHoldingRegister(Idm::modbusUnitID, Idm::OutsideTemperature, 2); } QUuid Idm::setTargetTemperature(double targetTemperature) { - QVector value = ModbusHelpers::convertFloatToRegister(targetTemperature); - return m_modbusMaster->writeHoldingRegisters(Idm::modbusUnitID, Idm::RegisterList::RoomTemperatureTargetHeatingEcoHKA, value); + QVector value = ModbusHelpers::convertFloatToRegister(targetTemperature); + return m_modbusMaster->writeHoldingRegisters(Idm::modbusUnitID, Idm::RegisterList::RoomTemperatureTargetHeatingEcoHKA, value); } void Idm::onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector &value) diff --git a/idm/idm.h b/idm/idm.h index 7a70efc..53f1d02 100644 --- a/idm/idm.h +++ b/idm/idm.h @@ -72,7 +72,7 @@ public: ~Idm(); bool connectDevice(); - QHostAddress getIdmAddress() const; + QHostAddress address() const; QUuid setTargetTemperature(double targetTemperature); void getStatus(); diff --git a/idm/integrationpluginidm.cpp b/idm/integrationpluginidm.cpp index 3fac78d..e8be7c9 100644 --- a/idm/integrationpluginidm.cpp +++ b/idm/integrationpluginidm.cpp @@ -28,6 +28,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "network/networkdevicediscovery.h" #include "integrationpluginidm.h" #include "plugininfo.h" @@ -36,6 +37,48 @@ IntegrationPluginIdm::IntegrationPluginIdm() } +void IntegrationPluginIdm::discoverThings(ThingDiscoveryInfo *info) +{ + qCDebug(dcIdm()) << "Discovering network..."; + NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); + connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ + ThingDescriptors descriptors; + qCDebug(dcIdm()) << "Discovery finished. Found" << discoveryReply->networkDevices().count() << "devices"; + foreach (const NetworkDevice &networkDevice, discoveryReply->networkDevices()) { + qCDebug(dcIdm()) << networkDevice; + QString title; + if (networkDevice.hostName().isEmpty()) { + title += networkDevice.address().toString(); + } else { + title += networkDevice.address().toString() + " (" + networkDevice.hostName() + ")"; + } + + QString description; + if (networkDevice.macAddressManufacturer().isEmpty()) { + description = networkDevice.macAddress(); + } else { + description = networkDevice.macAddress() + " (" + networkDevice.macAddressManufacturer() + ")"; + } + + ThingDescriptor descriptor(navigator2ThingClassId, title, description); + + // Check if we already have set up this device + Things existingThings = myThings().filterByParam(navigator2ThingMacAddressParamTypeId, networkDevice.macAddress()); + if (existingThings.count() == 1) { + qCDebug(dcIdm()) << "This thing already exists in the system." << existingThings.first() << networkDevice; + descriptor.setThingId(existingThings.first()->id()); + } + + ParamList params; + params << Param(navigator2ThingMacAddressParamTypeId, networkDevice.macAddress()); + params << Param(navigator2ThingIpAddressParamTypeId, networkDevice.address().toString()); + descriptor.setParams(params); + info->addThingDescriptor(descriptor); + } + info->finish(Thing::ThingErrorNoError); + }); +} + void IntegrationPluginIdm::setupThing(ThingSetupInfo *info) { Thing *thing = info->thing(); @@ -57,8 +100,8 @@ void IntegrationPluginIdm::setupThing(ThingSetupInfo *info) qCDebug(dcIdm()) << "User entered address: " << hostAddress.toString(); /* Check, if address is already in use for another device */ - Q_FOREACH (Idm *idm, m_idmConnections) { - if (hostAddress.isEqual(idm->getIdmAddress())) { + foreach (Idm *idm, m_idmConnections) { + if (hostAddress.isEqual(idm->address())) { qCWarning(dcIdm()) << "Address already in use"; info->finish(Thing::ThingErrorSetupFailed, QT_TR_NOOP("IP address already in use")); return; @@ -144,8 +187,7 @@ void IntegrationPluginIdm::executeAction(ThingActionInfo *info) double targetTemperature = thing->stateValue(navigator2TargetTemperatureStateTypeId).toDouble(); QUuid requestId = idm->setTargetTemperature(targetTemperature); m_asyncActions.insert(requestId, info); - connect(info, &ThingActionInfo::aborted, [requestId, this] {m_asyncActions.remove(requestId);}); - + connect(info, &ThingActionInfo::aborted, [requestId, this] (){ m_asyncActions.remove(requestId); }); } else { Q_ASSERT_X(false, "executeAction", QString("Unhandled action: %1").arg(action.actionTypeId().toString()).toUtf8()); } @@ -158,11 +200,8 @@ void IntegrationPluginIdm::update(Thing *thing) { if (thing->thingClassId() == navigator2ThingClassId) { qCDebug(dcIdm()) << "Updating thing" << thing->name(); - Idm *idm = m_idmConnections.value(thing); - if (!idm) { - return; - } + if (!idm) { return; }; idm->getStatus(); } } @@ -170,7 +209,6 @@ void IntegrationPluginIdm::update(Thing *thing) void IntegrationPluginIdm::onStatusUpdated(const IdmInfo &info) { qCDebug(dcIdm()) << "Received status from heat pump"; - Idm *idm = qobject_cast(sender()); Thing *thing = m_idmConnections.key(idm); @@ -205,8 +243,6 @@ void IntegrationPluginIdm::onWriteRequestExecuted(const QUuid &requestId, bool s void IntegrationPluginIdm::onRefreshTimer() { - qCDebug(dcIdm()) << "onRefreshTimer called"; - foreach (Thing *thing, myThings().filterByThingClassId(navigator2ThingClassId)) { update(thing); } diff --git a/idm/integrationpluginidm.h b/idm/integrationpluginidm.h index 808edb8..058988e 100644 --- a/idm/integrationpluginidm.h +++ b/idm/integrationpluginidm.h @@ -33,7 +33,6 @@ #include "integrations/integrationplugin.h" #include "plugintimer.h" - #include "idm.h" #include @@ -48,6 +47,7 @@ class IntegrationPluginIdm: public IntegrationPlugin public: explicit IntegrationPluginIdm(); + void discoverThings(ThingDiscoveryInfo *info) override; void setupThing(ThingSetupInfo *info) override; void postSetupThing(Thing *thing) override; void thingRemoved(Thing *thing) override; diff --git a/idm/integrationpluginidm.json b/idm/integrationpluginidm.json index e66b23e..a17f4d4 100644 --- a/idm/integrationpluginidm.json +++ b/idm/integrationpluginidm.json @@ -1,5 +1,5 @@ { - "name": "Idm", + "name": "idm", "displayName": "iDM", "id": "3968d86d-d51a-4ad1-a185-91faa017e38f", "vendors": [ @@ -12,14 +12,23 @@ "name": "navigator2", "displayName": "Navigator 2.0", "id": "1c95ac91-4eca-4cbf-b0f4-d60d35d069ed", - "createMethods": ["user"], + "createMethods": ["user", "discovery"], "interfaces": ["thermostat", "connectable"], "paramTypes": [ { "id": "05714e5c-d66a-4095-bbff-a0eb96fb035b", "name":"ipAddress", "displayName": "IP address", - "type": "QString" + "inputType": "IPv4Address", + "type": "QString", + "defaultValue": "0.0.0.0" + }, + { + "id": "d178ca29-41a1-4f56-82ec-76a833c1de50", + "name": "macAddress", + "displayName": "MAC address", + "type": "QString", + "defaultValue": "" } ], "stateTypes":[ From 6b8469177f3e228b7afba3537b50190349c54fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 7 Jun 2021 16:06:43 +0200 Subject: [PATCH 2/7] Add network discovery to the modbus TCP commander --- .../integrationpluginmodbuscommander.cpp | 39 +++++++++++++++++++ .../integrationpluginmodbuscommander.json | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/modbuscommander/integrationpluginmodbuscommander.cpp b/modbuscommander/integrationpluginmodbuscommander.cpp index cae1683..00e8e8b 100644 --- a/modbuscommander/integrationpluginmodbuscommander.cpp +++ b/modbuscommander/integrationpluginmodbuscommander.cpp @@ -32,6 +32,7 @@ #include "plugininfo.h" #include "hardwaremanager.h" +#include "network/networkdevicediscovery.h" #include "hardware/modbus/modbusrtumaster.h" #include "hardware/modbus/modbusrtuhardwareresource.h" @@ -109,6 +110,44 @@ void IntegrationPluginModbusCommander::discoverThings(ThingDiscoveryInfo *info) info->finish(Thing::ThingErrorNoError); return; + } else if (thingClassId == modbusTCPClientThingClassId) { + NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); + connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ + ThingDescriptors descriptors; + qCDebug(dcModbusCommander()) << "Discovery finished. Found" << discoveryReply->networkDevices().count() << "devices"; + foreach (const NetworkDevice &networkDevice, discoveryReply->networkDevices()) { + qCDebug(dcModbusCommander()) << networkDevice; + QString title; + if (networkDevice.hostName().isEmpty()) { + title += networkDevice.address().toString(); + } else { + title += networkDevice.address().toString() + " (" + networkDevice.hostName() + ")"; + } + + QString description; + if (networkDevice.macAddressManufacturer().isEmpty()) { + description = networkDevice.macAddress(); + } else { + description = networkDevice.macAddress() + " (" + networkDevice.macAddressManufacturer() + ")"; + } + + ThingDescriptor descriptor(modbusTCPClientThingClassId, title, description); + + // Check if we already have set up this device + Things existingThings = myThings().filterByParam(modbusTCPClientThingIpAddressParamTypeId, networkDevice.address().toString()); + if (existingThings.count() == 1) { + qCDebug(dcModbusCommander()) << "This thing already exists in the system." << existingThings.first() << networkDevice; + descriptor.setThingId(existingThings.first()->id()); + } + + ParamList params; + params << Param(modbusTCPClientThingIpAddressParamTypeId, networkDevice.address().toString()); + descriptor.setParams(params); + info->addThingDescriptor(descriptor); + } + info->finish(Thing::ThingErrorNoError); + }); + return; } else if (thingClassId == discreteInputThingClassId) { Q_FOREACH(Thing *clientThing, myThings()){ if (clientThing->thingClassId() == modbusTCPClientThingClassId) { diff --git a/modbuscommander/integrationpluginmodbuscommander.json b/modbuscommander/integrationpluginmodbuscommander.json index 14f3162..3c3d36e 100644 --- a/modbuscommander/integrationpluginmodbuscommander.json +++ b/modbuscommander/integrationpluginmodbuscommander.json @@ -22,7 +22,7 @@ "id": "35d3e7dc-1f33-4b8c-baa3-eb10b4f157a7", "name": "modbusTCPClient", "displayName": "Modbus TCP client", - "createMethods": ["user"], + "createMethods": ["user", "discovery"], "interfaces": ["connectable"], "settingsTypes": [ { From d9138ff01d82f1b3c5eac440e0ca5d4f0ceec8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 7 Jun 2021 16:20:31 +0200 Subject: [PATCH 3/7] Add network discovery to the sunspec plugin --- sunspec/integrationpluginsunspec.cpp | 43 ++++++++++++++++++++++++++- sunspec/integrationpluginsunspec.h | 1 + sunspec/integrationpluginsunspec.json | 2 +- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/sunspec/integrationpluginsunspec.cpp b/sunspec/integrationpluginsunspec.cpp index c6a95ed..e353d12 100644 --- a/sunspec/integrationpluginsunspec.cpp +++ b/sunspec/integrationpluginsunspec.cpp @@ -30,6 +30,7 @@ #include "plugininfo.h" #include "integrationpluginsunspec.h" +#include "network/networkdevicediscovery.h" #include @@ -98,6 +99,46 @@ void IntegrationPluginSunSpec::init() m_frequencyStateTypeIds.insert(sunspecThreePhaseMeterThingClassId, sunspecThreePhaseMeterFrequencyStateTypeId); } +void IntegrationPluginSunSpec::discoverThings(ThingDiscoveryInfo *info) +{ + NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); + connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ + ThingDescriptors descriptors; + qCDebug(dcSunSpec()) << "Discovery finished. Found" << discoveryReply->networkDevices().count() << "devices"; + foreach (const NetworkDevice &networkDevice, discoveryReply->networkDevices()) { + qCDebug(dcSunSpec()) << networkDevice; + QString title; + if (networkDevice.hostName().isEmpty()) { + title += networkDevice.address().toString(); + } else { + title += networkDevice.address().toString() + " (" + networkDevice.hostName() + ")"; + } + + QString description; + if (networkDevice.macAddressManufacturer().isEmpty()) { + description = networkDevice.macAddress(); + } else { + description = networkDevice.macAddress() + " (" + networkDevice.macAddressManufacturer() + ")"; + } + + ThingDescriptor descriptor(sunspecConnectionThingClassId, title, description); + + // Check if we already have set up this device + Things existingThings = myThings().filterByParam(sunspecConnectionThingIpAddressParamTypeId, networkDevice.address().toString()); + if (existingThings.count() == 1) { + qCDebug(dcSunSpec()) << "This thing already exists in the system." << existingThings.first() << networkDevice; + descriptor.setThingId(existingThings.first()->id()); + } + + ParamList params; + params << Param(sunspecConnectionThingIpAddressParamTypeId, networkDevice.address().toString()); + descriptor.setParams(params); + info->addThingDescriptor(descriptor); + } + info->finish(Thing::ThingErrorNoError); + }); +} + void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) { Thing *thing = info->thing(); @@ -132,7 +173,7 @@ void IntegrationPluginSunSpec::setupThing(ThingSetupInfo *info) }); connect(info, &ThingSetupInfo::aborted, sunSpec, &SunSpec::deleteLater); - connect(sunSpec, &SunSpec::destroyed, [this, thing] { + connect(sunSpec, &SunSpec::destroyed, thing, [this, thing] { m_sunSpecConnections.remove(thing->id()); }); connect(sunSpec, &SunSpec::foundSunSpecModel, this, &IntegrationPluginSunSpec::onFoundSunSpecModel); diff --git a/sunspec/integrationpluginsunspec.h b/sunspec/integrationpluginsunspec.h index bdb8252..dd9b2f5 100644 --- a/sunspec/integrationpluginsunspec.h +++ b/sunspec/integrationpluginsunspec.h @@ -52,6 +52,7 @@ class IntegrationPluginSunSpec: public IntegrationPlugin public: explicit IntegrationPluginSunSpec(); void init() override; + void discoverThings(ThingDiscoveryInfo *info) override; void setupThing(ThingSetupInfo *info) override; void postSetupThing(Thing *thing) override; void thingRemoved(Thing *thing) override; diff --git a/sunspec/integrationpluginsunspec.json b/sunspec/integrationpluginsunspec.json index df83b8b..8de1498 100644 --- a/sunspec/integrationpluginsunspec.json +++ b/sunspec/integrationpluginsunspec.json @@ -39,7 +39,7 @@ "name": "sunspecConnection", "displayName": "SunSpec connection", "id": "f51853f3-8815-4cf3-b337-45cda1f3e6d5", - "createMethods": [ "User" ], + "createMethods": [ "User", "Discovery" ], "interfaces": ["gateway"], "paramTypes": [ { From d482fe341893d9888174297de817653e271f13c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 7 Jun 2021 16:38:24 +0200 Subject: [PATCH 4/7] Update wallbe to make use of nymea internal network discovery --- wallbe/discovery.cpp | 271 ----------------------------- wallbe/discovery.h | 75 -------- wallbe/host.cpp | 104 ----------- wallbe/host.h | 74 -------- wallbe/integrationpluginwallbe.cpp | 111 +++++++----- wallbe/integrationpluginwallbe.h | 7 +- wallbe/wallbe.pro | 8 +- 7 files changed, 68 insertions(+), 582 deletions(-) delete mode 100644 wallbe/discovery.cpp delete mode 100644 wallbe/discovery.h delete mode 100644 wallbe/host.cpp delete mode 100644 wallbe/host.h diff --git a/wallbe/discovery.cpp b/wallbe/discovery.cpp deleted file mode 100644 index a08cc68..0000000 --- a/wallbe/discovery.cpp +++ /dev/null @@ -1,271 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "discovery.h" -#include "extern-plugininfo.h" - -#include -#include -#include -#include -#include - -Discovery::Discovery(QObject *parent) : QObject(parent) -{ - connect(&m_timeoutTimer, &QTimer::timeout, this, &Discovery::onTimeout); -} - -void Discovery::discoverHosts(int timeout) -{ - if (isRunning()) { - qCWarning(dcWallbe()) << "Discovery already running. Cannot start twice."; - return; - } - m_timeoutTimer.start(timeout * 1000); - - foreach (const QString &target, getDefaultTargets()) { - QProcess *discoveryProcess = new QProcess(this); - m_discoveryProcesses.append(discoveryProcess); - connect(discoveryProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(discoveryFinished(int,QProcess::ExitStatus))); - - QStringList arguments; - arguments << "-oX" << "-" << "-n" << "-sn"; - arguments << target; - - qCDebug(dcWallbe()) << "Scanning network:" << "nmap" << arguments.join(" "); - discoveryProcess->start(QStringLiteral("nmap"), arguments); - } -} - -void Discovery::abort() -{ - foreach (QProcess *discoveryProcess, m_discoveryProcesses) { - if (discoveryProcess->state() == QProcess::Running) { - qCDebug(dcWallbe()) << "Kill running discovery process"; - discoveryProcess->terminate(); - discoveryProcess->waitForFinished(5000); - } - } - foreach (QProcess *p, m_pendingArpLookups.keys()) { - p->terminate(); - delete p; - } - m_pendingArpLookups.clear(); - m_pendingNameLookups.clear(); - qDeleteAll(m_scanResults); - m_scanResults.clear(); -} - -bool Discovery::isRunning() const -{ - return !m_discoveryProcesses.isEmpty() || !m_pendingArpLookups.isEmpty() || !m_pendingNameLookups.isEmpty(); -} - -void Discovery::discoveryFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - QProcess *discoveryProcess = static_cast(sender()); - - if (exitCode != 0 || exitStatus != QProcess::NormalExit) { - qCWarning(dcWallbe()) << "Nmap error failed. Is nmap installed correctly?"; - m_discoveryProcesses.removeAll(discoveryProcess); - discoveryProcess->deleteLater(); - discoveryProcess = nullptr; - finishDiscovery(); - return; - } - - QByteArray data = discoveryProcess->readAll(); - m_discoveryProcesses.removeAll(discoveryProcess); - discoveryProcess->deleteLater(); - discoveryProcess = nullptr; - - QXmlStreamReader reader(data); - - int foundHosts = 0; - qCDebug(dcWallbe()) << "nmap finished network discovery:"; - while (!reader.atEnd() && !reader.hasError()) { - QXmlStreamReader::TokenType token = reader.readNext(); - if(token == QXmlStreamReader::StartDocument) - continue; - - if(token == QXmlStreamReader::StartElement && reader.name() == "host") { - bool isUp = false; - QString address; - QString macAddress; - QString vendor; - while (!reader.atEnd() && !reader.hasError() && !(token == QXmlStreamReader::EndElement && reader.name() == "host")) { - token = reader.readNext(); - - if (reader.name() == "address") { - QString addr = reader.attributes().value("addr").toString(); - QString type = reader.attributes().value("addrtype").toString(); - if (type == "ipv4" && !addr.isEmpty()) { - address = addr; - } else if (type == "mac") { - macAddress = addr; - vendor = reader.attributes().value("vendor").toString(); - } - } - - if (reader.name() == "status") { - QString state = reader.attributes().value("state").toString(); - if (!state.isEmpty()) - isUp = state == "up"; - } - } - - if (isUp) { - foundHosts++; - qCDebug(dcWallbe()) << " - host:" << vendor << address << macAddress; - - Host *host = new Host(); - host->setAddress(address); - - if (!macAddress.isEmpty()) { - host->setMacAddress(macAddress); - } else { - QProcess *arpLookup = new QProcess(this); - m_pendingArpLookups.insert(arpLookup, host); - connect(arpLookup, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(arpLookupDone(int,QProcess::ExitStatus))); - arpLookup->start("arp", {"-vn"}); - } - - host->setVendor(vendor); - QHostInfo::lookupHost(address, this, SLOT(hostLookupDone(QHostInfo))); - m_pendingNameLookups.insert(address, host); - - m_scanResults.append(host); - } - } - } - - if (foundHosts == 0 && m_discoveryProcesses.isEmpty()) { - qCDebug(dcWallbe()) << "Network scan successful but no hosts found in this network"; - finishDiscovery(); - } -} - -void Discovery::hostLookupDone(const QHostInfo &info) -{ - Host *host = m_pendingNameLookups.take(info.addresses().first().toString()); - if (!host) { - // Probably aborted... - return; - } - if (info.error() != QHostInfo::NoError) { - qWarning(dcWallbe()) << "Host lookup failed:" << info.errorString(); - } - if (host->hostName().isEmpty() || info.hostName() != host->address()) { - host->setHostName(info.hostName()); - } - - finishDiscovery(); -} - -void Discovery::arpLookupDone(int exitCode, QProcess::ExitStatus exitStatus) -{ - QProcess *p = static_cast(sender()); - p->deleteLater(); - - Host *host = m_pendingArpLookups.take(p); - - if (exitCode != 0 || exitStatus != QProcess::NormalExit) { - qCWarning(dcWallbe()) << "ARP lookup process failed for host" << host->address(); - finishDiscovery(); - return; - } - - QString data = QString::fromLatin1(p->readAll()); - foreach (QString line, data.split('\n')) { - line.replace(QRegExp("[ ]{1,}"), " "); - QStringList parts = line.split(" "); - if (parts.count() >= 3 && parts.first() == host->address() && parts.at(1) == "ether") { - host->setMacAddress(parts.at(2)); - break; - } - } - finishDiscovery(); -} - -void Discovery::onTimeout() -{ - qWarning(dcWallbe()) << "Timeout hit. Stopping discovery"; - while (!m_discoveryProcesses.isEmpty()) { - QProcess *discoveryProcess = m_discoveryProcesses.takeFirst(); - disconnect(this, SLOT(discoveryFinished(int,QProcess::ExitStatus))); - discoveryProcess->terminate(); - delete discoveryProcess; - } - foreach (QProcess *p, m_pendingArpLookups.keys()) { - p->terminate(); - m_scanResults.removeAll(m_pendingArpLookups.value(p)); - delete p; - } - m_pendingArpLookups.clear(); - m_pendingNameLookups.clear(); - finishDiscovery(); -} - -QStringList Discovery::getDefaultTargets() -{ - QStringList targets; - foreach (const QHostAddress &interface, QNetworkInterface::allAddresses()) { - if (!interface.isLoopback() && interface.scopeId().isEmpty() && interface.protocol() == QAbstractSocket::IPv4Protocol) { - QPair pair = QHostAddress::parseSubnet(interface.toString() + "/24"); - QString newTarget = QString("%1/%2").arg(pair.first.toString()).arg(pair.second); - if (!targets.contains(newTarget)) { - targets.append(newTarget); - } - } - } - return targets; -} - -void Discovery::finishDiscovery() -{ - if (m_discoveryProcesses.count() > 0 || m_pendingNameLookups.count() > 0 || m_pendingArpLookups.count() > 0) { - // Still busy... - return; - } - - QList hosts; - foreach (Host *host, m_scanResults) { - if (!host->macAddress().isEmpty()) { - hosts.append(*host); - } - } - qDeleteAll(m_scanResults); - m_scanResults.clear(); - - qCDebug(dcWallbe()) << "Found" << hosts.count() << "network devices"; - m_timeoutTimer.stop(); - emit finished(hosts); -} - diff --git a/wallbe/discovery.h b/wallbe/discovery.h deleted file mode 100644 index 02284c3..0000000 --- a/wallbe/discovery.h +++ /dev/null @@ -1,75 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef DISCOVERY_H -#define DISCOVERY_H - -#include -#include -#include -#include - -#include "host.h" - -class Discovery : public QObject -{ - Q_OBJECT -public: - explicit Discovery(QObject *parent = nullptr); - - void discoverHosts(int timeout); - void abort(); - bool isRunning() const; - -signals: - void finished(const QList &hosts); - -private: - QStringList getDefaultTargets(); - - void finishDiscovery(); - -private slots: - void discoveryFinished(int exitCode, QProcess::ExitStatus exitStatus); - void hostLookupDone(const QHostInfo &info); - void arpLookupDone(int exitCode, QProcess::ExitStatus exitStatus); - void onTimeout(); - -private: - QList m_discoveryProcesses; - QTimer m_timeoutTimer; - - QHash m_pendingArpLookups; - QHash m_pendingNameLookups; - QList m_scanResults; -}; - -#endif // DISCOVERY_H - diff --git a/wallbe/host.cpp b/wallbe/host.cpp deleted file mode 100644 index e746395..0000000 --- a/wallbe/host.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "host.h" - -Host::Host() -{ - qRegisterMetaType(); - qRegisterMetaType >(); -} - -QString Host::macAddress() const -{ - return m_macAddress; -} - -void Host::setMacAddress(const QString &macAddress) -{ - m_macAddress = macAddress; -} - -QString Host::hostName() const -{ - return m_hostName; -} - -void Host::setHostName(const QString &hostName) -{ - m_hostName = hostName; -} - -QString Host::vendor() const -{ - return m_vendor; -} - -void Host::setVendor(const QString &vendor) -{ - m_vendor = vendor; -} - -QString Host::address() const -{ - return m_address; -} - -void Host::setAddress(const QString &address) -{ - m_address = address; -} - -void Host::seen() -{ - m_lastSeenTime = QDateTime::currentDateTime(); -} - -QDateTime Host::lastSeenTime() const -{ - return m_lastSeenTime; -} - -bool Host::reachable() const -{ - return m_reachable; -} - -void Host::setReachable(bool reachable) -{ - m_reachable = reachable; -} - -QDebug operator<<(QDebug dbg, const Host &host) -{ - dbg.nospace() << "Host(" << host.macAddress() << "," << host.hostName() << ", " << host.address() << ", " << (host.reachable() ? "up" : "down") << ")"; - return dbg.space(); -} - diff --git a/wallbe/host.h b/wallbe/host.h deleted file mode 100644 index 0c982b4..0000000 --- a/wallbe/host.h +++ /dev/null @@ -1,74 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef HOST_H -#define HOST_H - -#include -#include -#include - -class Host -{ -public: - Host(); - - QString macAddress() const; - void setMacAddress(const QString &macAddress); - - QString hostName() const; - void setHostName(const QString &hostName); - - QString vendor() const; - void setVendor(const QString &vendor); - - QString address() const; - void setAddress(const QString &address); - - void seen(); - QDateTime lastSeenTime() const; - - bool reachable() const; - void setReachable(bool reachable); - -private: - QString m_macAddress; - QString m_hostName; - QString m_address; - QString m_vendor; - QDateTime m_lastSeenTime; - bool m_reachable; -}; -Q_DECLARE_METATYPE(Host) - -QDebug operator<<(QDebug dbg, const Host &host); - -#endif // HOST_H - diff --git a/wallbe/integrationpluginwallbe.cpp b/wallbe/integrationpluginwallbe.cpp index 6865beb..9bd15dc 100644 --- a/wallbe/integrationpluginwallbe.cpp +++ b/wallbe/integrationpluginwallbe.cpp @@ -30,7 +30,7 @@ #include "integrationpluginwallbe.h" #include "plugininfo.h" - +#include "network/networkdevicediscovery.h" #include "types/param.h" #include @@ -41,66 +41,83 @@ IntegrationPluginWallbe::IntegrationPluginWallbe() { + } -void IntegrationPluginWallbe::init() { +void IntegrationPluginWallbe::init() +{ + // FIXME: make use of the internal network discovery if the device gets unavailable. For now, commented out since it has not been used + // at the moment of changing this. - m_discovery = new Discovery(); - connect(m_discovery, &Discovery::finished, this, [this](const QList &hosts) { - foreach (const Host &host, hosts) { - if (!host.vendor().contains("Phoenix", Qt::CaseSensitivity::CaseInsensitive)) - continue; + // m_discovery = new Discovery(); + // connect(m_discovery, &Discovery::finished, this, [this](const QList &hosts) { + // foreach (const Host &host, hosts) { + // if (!host.vendor().contains("Phoenix", Qt::CaseSensitivity::CaseInsensitive)) + // continue; - Q_FOREACH(Thing *existingThing, myThings()) { - if (existingThing->paramValue(wallbeEcoThingMacParamTypeId).toString().isEmpty()) { - //This device got probably manually setup, to enable auto rediscovery the MAC address needs to setup - if (existingThing->paramValue(wallbeEcoThingIpParamTypeId).toString() == host.address()) { - qCDebug(dcWallbe()) << "Wallbe Wallbox MAC Address has been discovered" << existingThing->name() << host.macAddress(); - existingThing->setParamValue(wallbeEcoThingMacParamTypeId, host.macAddress()); + // Q_FOREACH(Thing *existingThing, myThings()) { + // if (existingThing->paramValue(wallbeEcoThingMacParamTypeId).toString().isEmpty()) { + // //This device got probably manually setup, to enable auto rediscovery the MAC address needs to setup + // if (existingThing->paramValue(wallbeEcoThingIpParamTypeId).toString() == host.address()) { + // qCDebug(dcWallbe()) << "Wallbe Wallbox MAC Address has been discovered" << existingThing->name() << host.macAddress(); + // existingThing->setParamValue(wallbeEcoThingMacParamTypeId, host.macAddress()); - } - } else if (existingThing->paramValue(wallbeEcoThingMacParamTypeId).toString() == host.macAddress()) { - if (existingThing->paramValue(wallbeEcoThingIpParamTypeId).toString() != host.address()) { - qCDebug(dcWallbe()) << "Wallbe Wallbox IP Address has changed, from" << existingThing->paramValue(wallbeEcoThingIpParamTypeId).toString() << "to" << host.address(); - existingThing->setParamValue(wallbeEcoThingIpParamTypeId, host.address()); + // } + // } else if (existingThing->paramValue(wallbeEcoThingMacParamTypeId).toString() == host.macAddress()) { + // if (existingThing->paramValue(wallbeEcoThingIpParamTypeId).toString() != host.address()) { + // qCDebug(dcWallbe()) << "Wallbe Wallbox IP Address has changed, from" << existingThing->paramValue(wallbeEcoThingIpParamTypeId).toString() << "to" << host.address(); + // existingThing->setParamValue(wallbeEcoThingIpParamTypeId, host.address()); - } else { - qCDebug(dcWallbe()) << "Wallbe Wallbox" << existingThing->name() << "IP address has not changed" << host.address(); - } - break; - } - } - } - }); + // } else { + // qCDebug(dcWallbe()) << "Wallbe Wallbox" << existingThing->name() << "IP address has not changed" << host.address(); + // } + // break; + // } + // } + // } + // }); } void IntegrationPluginWallbe::discoverThings(ThingDiscoveryInfo *info) { if (info->thingClassId() == wallbeEcoThingClassId){ - qCDebug(dcWallbe) << "Start Wallbe eco discovery"; - - m_discovery->discoverHosts(50); - connect(m_discovery, &Discovery::finished, info, [this, info] (const QList &hosts) { - - foreach (const Host &host, hosts) { - if (!host.vendor().contains("Phoenix", Qt::CaseSensitivity::CaseInsensitive)) + qCDebug(dcWallbe()) << "Start Wallbe eco discovery"; + NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); + connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ + ThingDescriptors descriptors; + qCDebug(dcWallbe()) << "Discovery finished. Found" << discoveryReply->networkDevices().count() << "devices"; + foreach (const NetworkDevice &networkDevice, discoveryReply->networkDevices()) { + qCDebug(dcWallbe()) << networkDevice; + if (!networkDevice.macAddressManufacturer().contains("Phoenix", Qt::CaseSensitivity::CaseInsensitive)) continue; - ThingDescriptor descriptor(wallbeEcoThingClassId); - // Rediscovery - foreach (Thing *existingThing, myThings()) { - if (existingThing->paramValue(wallbeEcoThingMacParamTypeId).toString() == host.macAddress()) { - qCDebug(dcWallbe()) << " - Device is already added"; - descriptor.setThingId(existingThing->id()); - break; - } + QString title; + if (networkDevice.hostName().isEmpty()) { + title += networkDevice.address().toString(); + } else { + title += networkDevice.address().toString() + " (" + networkDevice.hostName() + ")"; } - descriptor.setTitle(host.hostName().remove(".localdomain")); - descriptor.setDescription(host.address()); + + QString description; + if (networkDevice.macAddressManufacturer().isEmpty()) { + description = networkDevice.macAddress(); + } else { + description = networkDevice.macAddress() + " (" + networkDevice.macAddressManufacturer() + ")"; + } + + ThingDescriptor descriptor(wallbeEcoThingClassId, title, description); + + // Check if we already have set up this device + Things existingThings = myThings().filterByParam(wallbeEcoThingIpParamTypeId, networkDevice.address().toString()); + if (existingThings.count() == 1) { + qCDebug(dcWallbe()) << "This thing already exists in the system." << existingThings.first() << networkDevice; + descriptor.setThingId(existingThings.first()->id()); + } + ParamList params; - params.append(Param(wallbeEcoThingIpParamTypeId, host.address())); - params.append(Param(wallbeEcoThingMacParamTypeId, host.macAddress())); + params << Param(wallbeEcoThingIpParamTypeId, networkDevice.address().toString()); + params << Param(wallbeEcoThingMacParamTypeId, networkDevice.macAddress()); descriptor.setParams(params); info->addThingDescriptor(descriptor); } @@ -297,8 +314,8 @@ void IntegrationPluginWallbe::onReceivedInputRegister(int slaveAddress, int modb } else if (WallbeRegisterAddress(modbusRegister) == WallbeRegisterAddress::FirmwareVersion) { int firmware = (uint32_t)(value[1]<<16)|(uint32_t)(value[0]); uint major = firmware/10000; - uint minor = (firmware%10000)/100; - uint patch = firmware%100; + uint minor = (firmware%10000)/100; + uint patch = firmware%100; QString firmwarestring = QString::number(major)+'.'+QString::number(minor)+'.'+QString::number(patch); thing->setStateValue(wallbeEcoFirmwareVersionStateTypeId, firmwarestring); } diff --git a/wallbe/integrationpluginwallbe.h b/wallbe/integrationpluginwallbe.h index b55bd30..4d3f545 100644 --- a/wallbe/integrationpluginwallbe.h +++ b/wallbe/integrationpluginwallbe.h @@ -34,8 +34,6 @@ #include "integrations/integrationplugin.h" #include "plugintimer.h" -#include "host.h" -#include "discovery.h" #include "../modbus/modbustcpmaster.h" #include @@ -72,7 +70,6 @@ public: void thingRemoved(Thing *thing) override; private: - Discovery *m_discovery = nullptr; QHash m_connections; PluginTimer *m_pluginTimer = nullptr; QHash m_asyncActions; @@ -82,8 +79,8 @@ private: private slots: void onConnectionStateChanged(bool status); - void onReceivedInputRegister(int slaveAddress, int modbusRegister, const QVector &value); - void onReceivedCoil(int slaveAddress, int modbusRegister, const QVector &value); + void onReceivedInputRegister(int slaveAddress, int modbusRegister, const QVector &value); + void onReceivedCoil(int slaveAddress, int modbusRegister, const QVector &value); void onReceivedHoldingRegister(int slaveAddress, int modbusRegister, const QVector &value); void onWriteRequestExecuted(const QUuid &requestId, bool success); diff --git a/wallbe/wallbe.pro b/wallbe/wallbe.pro index e0bca99..c0cd408 100644 --- a/wallbe/wallbe.pro +++ b/wallbe/wallbe.pro @@ -6,12 +6,8 @@ QT += \ SOURCES += \ integrationpluginwallbe.cpp \ - ../modbus/modbustcpmaster.cpp \ - discovery.cpp \ - host.cpp + ../modbus/modbustcpmaster.cpp HEADERS += \ integrationpluginwallbe.h \ - ../modbus/modbustcpmaster.h \ - discovery.h \ - host.h + ../modbus/modbustcpmaster.h From cb43bc07975b30703017882068c7a86b85c703e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 7 Jun 2021 16:48:09 +0200 Subject: [PATCH 5/7] Update webasto to make use of nymea internal network discovery --- webasto/integrationpluginwebasto.cpp | 102 ++++++++++++++++----------- webasto/integrationpluginwebasto.h | 3 - webasto/webasto.cpp | 2 +- webasto/webasto.pro | 12 +--- 4 files changed, 64 insertions(+), 55 deletions(-) diff --git a/webasto/integrationpluginwebasto.cpp b/webasto/integrationpluginwebasto.cpp index f7e05af..6f720e5 100644 --- a/webasto/integrationpluginwebasto.cpp +++ b/webasto/integrationpluginwebasto.cpp @@ -28,6 +28,7 @@ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ +#include "network/networkdevicediscovery.h" #include "integrationpluginwebasto.h" #include "plugininfo.h" @@ -45,61 +46,78 @@ IntegrationPluginWebasto::IntegrationPluginWebasto() void IntegrationPluginWebasto::init() { - m_discovery = new Discovery(this); - connect(m_discovery, &Discovery::finished, this, [this](const QList &hosts) { + // FIXME: make use of the internal network discovery if the device gets unavailable. For now, commented out since it has not been used + // at the moment of changing this. - foreach (const Host &host, hosts) { - if (!host.hostName().contains("webasto", Qt::CaseSensitivity::CaseInsensitive)) - continue; +// m_discovery = new Discovery(this); +// connect(m_discovery, &Discovery::finished, this, [this](const QList &hosts) { - foreach (Thing *existingThing, myThings()) { - if (existingThing->paramValue(liveWallboxThingMacAddressParamTypeId).toString().isEmpty()) { - //This device got probably manually setup, to enable auto rediscovery the MAC address needs to setup - if (existingThing->paramValue(liveWallboxThingIpAddressParamTypeId).toString() == host.address()) { - qCDebug(dcWebasto()) << "Wallbox MAC Address has been discovered" << existingThing->name() << host.macAddress(); - existingThing->setParamValue(liveWallboxThingMacAddressParamTypeId, host.macAddress()); +// foreach (const Host &host, hosts) { +// if (!host.hostName().contains("webasto", Qt::CaseSensitivity::CaseInsensitive)) +// continue; - } - } else if (existingThing->paramValue(liveWallboxThingMacAddressParamTypeId).toString() == host.macAddress()) { - if (existingThing->paramValue(liveWallboxThingIpAddressParamTypeId).toString() != host.address()) { - qCDebug(dcWebasto()) << "Wallbox IP Address has changed, from" << existingThing->paramValue(liveWallboxThingIpAddressParamTypeId).toString() << "to" << host.address(); - existingThing->setParamValue(liveWallboxThingIpAddressParamTypeId, host.address()); +// foreach (Thing *existingThing, myThings()) { +// if (existingThing->paramValue(liveWallboxThingMacAddressParamTypeId).toString().isEmpty()) { +// //This device got probably manually setup, to enable auto rediscovery the MAC address needs to setup +// if (existingThing->paramValue(liveWallboxThingIpAddressParamTypeId).toString() == host.address()) { +// qCDebug(dcWebasto()) << "Wallbox MAC Address has been discovered" << existingThing->name() << host.macAddress(); +// existingThing->setParamValue(liveWallboxThingMacAddressParamTypeId, host.macAddress()); - } else { - qCDebug(dcWebasto()) << "Wallbox" << existingThing->name() << "IP address has not changed" << host.address(); - } - break; - } - } - } - }); +// } +// } else if (existingThing->paramValue(liveWallboxThingMacAddressParamTypeId).toString() == host.macAddress()) { +// if (existingThing->paramValue(liveWallboxThingIpAddressParamTypeId).toString() != host.address()) { +// qCDebug(dcWebasto()) << "Wallbox IP Address has changed, from" << existingThing->paramValue(liveWallboxThingIpAddressParamTypeId).toString() << "to" << host.address(); +// existingThing->setParamValue(liveWallboxThingIpAddressParamTypeId, host.address()); + +// } else { +// qCDebug(dcWebasto()) << "Wallbox" << existingThing->name() << "IP address has not changed" << host.address(); +// } +// break; +// } +// } +// } +// }); } void IntegrationPluginWebasto::discoverThings(ThingDiscoveryInfo *info) { - qCDebug(dcWebasto()) << "Discover things"; if (info->thingClassId() == liveWallboxThingClassId) { - m_discovery->discoverHosts(25); - connect(m_discovery, &Discovery::finished, info, [this, info] (const QList &hosts) { - - foreach (const Host &host, hosts) { - if (!host.hostName().contains("webasto", Qt::CaseSensitivity::CaseInsensitive)) + qCDebug(dcWebasto()) << "Discover things"; + NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); + connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ + ThingDescriptors descriptors; + qCDebug(dcWebasto()) << "Discovery finished. Found" << discoveryReply->networkDevices().count() << "devices"; + foreach (const NetworkDevice &networkDevice, discoveryReply->networkDevices()) { + qCDebug(dcWebasto()) << networkDevice; + if (!networkDevice.hostName().contains("webasto", Qt::CaseSensitivity::CaseInsensitive)) continue; - qCDebug(dcWebasto()) << " - " << host.hostName() << host.address() << host.macAddress(); - ThingDescriptor descriptor(liveWallboxThingClassId, "Wallbox", host.address() + " (" + host.macAddress() + ")"); - - // Rediscovery - foreach (Thing *existingThing, myThings()) { - if (existingThing->paramValue(liveWallboxThingMacAddressParamTypeId).toString() == host.macAddress()) { - qCDebug(dcWebasto()) << " - Device is already added"; - descriptor.setThingId(existingThing->id()); - break; - } + QString title = "Wallbox "; + if (networkDevice.hostName().isEmpty()) { + title += networkDevice.address().toString(); + } else { + title += networkDevice.address().toString() + " (" + networkDevice.hostName() + ")"; } + + QString description; + if (networkDevice.macAddressManufacturer().isEmpty()) { + description = networkDevice.macAddress(); + } else { + description = networkDevice.macAddress() + " (" + networkDevice.macAddressManufacturer() + ")"; + } + + ThingDescriptor descriptor(liveWallboxThingClassId, title, description); + + // Check if we already have set up this device + Things existingThings = myThings().filterByParam(liveWallboxThingIpAddressParamTypeId, networkDevice.address().toString()); + if (existingThings.count() == 1) { + qCDebug(dcWebasto()) << "This thing already exists in the system." << existingThings.first() << networkDevice; + descriptor.setThingId(existingThings.first()->id()); + } + ParamList params; - params << Param(liveWallboxThingMacAddressParamTypeId, host.macAddress()); - params << Param(liveWallboxThingIpAddressParamTypeId, host.address()); + params << Param(liveWallboxThingIpAddressParamTypeId, networkDevice.address().toString()); + params << Param(liveWallboxThingMacAddressParamTypeId, networkDevice.macAddress()); descriptor.setParams(params); info->addThingDescriptor(descriptor); } diff --git a/webasto/integrationpluginwebasto.h b/webasto/integrationpluginwebasto.h index b1d765b..199cf1a 100644 --- a/webasto/integrationpluginwebasto.h +++ b/webasto/integrationpluginwebasto.h @@ -34,8 +34,6 @@ #include "integrations/integrationplugin.h" #include "plugintimer.h" #include "webasto.h" -#include "../discovery/discovery.h" -#include "../discovery/host.h" #include "../modbus/modbustcpmaster.h" #include @@ -59,7 +57,6 @@ public: void thingRemoved(Thing *thing) override; private: - Discovery *m_discovery = nullptr; PluginTimer *m_pluginTimer = nullptr; QHash m_webastoConnections; QHash m_asyncActions; diff --git a/webasto/webasto.cpp b/webasto/webasto.cpp index 9c980ad..94c6d77 100644 --- a/webasto/webasto.cpp +++ b/webasto/webasto.cpp @@ -80,7 +80,7 @@ void Webasto::setLivebitInterval(uint seconds) void Webasto::getRegister(Webasto::TqModbusRegister modbusRegister, uint length) { qCDebug(dcWebasto()) << "Webasto: Get register" << modbusRegister << length; - if (length < 1 && length > 10) { + if (length < 1 || length > 10) { qCWarning(dcWebasto()) << "Invalide register length, allowed values [1,10]"; return; } diff --git a/webasto/webasto.pro b/webasto/webasto.pro index 3d94e5f..c39fc78 100644 --- a/webasto/webasto.pro +++ b/webasto/webasto.pro @@ -1,19 +1,13 @@ include(../plugins.pri) -QT += \ - serialbus \ - network +QT += serialbus network SOURCES += \ integrationpluginwebasto.cpp \ webasto.cpp \ - ../modbus/modbustcpmaster.cpp \ - ../discovery/discovery.cpp \ - ../discovery/host.cpp + ../modbus/modbustcpmaster.cpp HEADERS += \ integrationpluginwebasto.h \ webasto.h \ - ../modbus/modbustcpmaster.h \ - ../discovery/discovery.h \ - ../discovery/host.h + ../modbus/modbustcpmaster.h From 515d8f6e429ee83cbf30a03745e9bd0520ab3a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Mon, 7 Jun 2021 16:49:33 +0200 Subject: [PATCH 6/7] Remove discovery classes in favour of nymea internal networ device discovery --- discovery/discovery.cpp | 272 ---------------------------------------- discovery/discovery.h | 75 ----------- discovery/host.cpp | 94 -------------- discovery/host.h | 70 ----------- 4 files changed, 511 deletions(-) delete mode 100644 discovery/discovery.cpp delete mode 100644 discovery/discovery.h delete mode 100644 discovery/host.cpp delete mode 100644 discovery/host.h diff --git a/discovery/discovery.cpp b/discovery/discovery.cpp deleted file mode 100644 index 96c459c..0000000 --- a/discovery/discovery.cpp +++ /dev/null @@ -1,272 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "discovery.h" - -#include -#include -#include -#include -#include -#include -NYMEA_LOGGING_CATEGORY(dcDiscovery, "Discovery") - -Discovery::Discovery(QObject *parent) : QObject(parent) -{ - connect(&m_timeoutTimer, &QTimer::timeout, this, &Discovery::onTimeout); -} - -void Discovery::discoverHosts(int timeout) -{ - if (isRunning()) { - qCWarning(dcDiscovery()) << "Discovery already running. Cannot start twice."; - return; - } - m_timeoutTimer.start(timeout * 1000); - - foreach (const QString &target, getDefaultTargets()) { - QProcess *discoveryProcess = new QProcess(this); - m_discoveryProcesses.append(discoveryProcess); - connect(discoveryProcess, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(discoveryFinished(int,QProcess::ExitStatus))); - - QStringList arguments; - arguments << "-oX" << "-" << "-n" << "-sn"; - arguments << target; - - qCDebug(dcDiscovery()) << "Scanning network:" << "nmap" << arguments.join(" "); - discoveryProcess->start(QStringLiteral("nmap"), arguments); - } -} - -void Discovery::abort() -{ - foreach (QProcess *discoveryProcess, m_discoveryProcesses) { - if (discoveryProcess->state() == QProcess::Running) { - qCDebug(dcDiscovery()) << "Kill running discovery process"; - discoveryProcess->terminate(); - discoveryProcess->waitForFinished(5000); - } - } - foreach (QProcess *p, m_pendingArpLookups.keys()) { - p->terminate(); - delete p; - } - m_pendingArpLookups.clear(); - m_pendingNameLookups.clear(); - qDeleteAll(m_scanResults); - m_scanResults.clear(); -} - -bool Discovery::isRunning() const -{ - return !m_discoveryProcesses.isEmpty() || !m_pendingArpLookups.isEmpty() || !m_pendingNameLookups.isEmpty(); -} - -void Discovery::discoveryFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - QProcess *discoveryProcess = static_cast(sender()); - - if (exitCode != 0 || exitStatus != QProcess::NormalExit) { - qCWarning(dcDiscovery()) << "Nmap error failed. Is nmap installed correctly?"; - m_discoveryProcesses.removeAll(discoveryProcess); - discoveryProcess->deleteLater(); - discoveryProcess = nullptr; - finishDiscovery(); - return; - } - - QByteArray data = discoveryProcess->readAll(); - m_discoveryProcesses.removeAll(discoveryProcess); - discoveryProcess->deleteLater(); - discoveryProcess = nullptr; - - QXmlStreamReader reader(data); - - int foundHosts = 0; - qCDebug(dcDiscovery()) << "nmap finished network discovery:"; - while (!reader.atEnd() && !reader.hasError()) { - QXmlStreamReader::TokenType token = reader.readNext(); - if(token == QXmlStreamReader::StartDocument) - continue; - - if(token == QXmlStreamReader::StartElement && reader.name() == "host") { - bool isUp = false; - QString address; - QString macAddress; - QString vendor; - while (!reader.atEnd() && !reader.hasError() && !(token == QXmlStreamReader::EndElement && reader.name() == "host")) { - token = reader.readNext(); - - if (reader.name() == "address") { - QString addr = reader.attributes().value("addr").toString(); - QString type = reader.attributes().value("addrtype").toString(); - if (type == "ipv4" && !addr.isEmpty()) { - address = addr; - } else if (type == "mac") { - macAddress = addr; - vendor = reader.attributes().value("vendor").toString(); - } - } - - if (reader.name() == "status") { - QString state = reader.attributes().value("state").toString(); - if (!state.isEmpty()) - isUp = state == "up"; - } - } - - if (isUp) { - foundHosts++; - qCDebug(dcDiscovery()) << " - host:" << address; - - Host *host = new Host(); - host->setAddress(address); - - if (!macAddress.isEmpty()) { - host->setMacAddress(macAddress); - } else { - QProcess *arpLookup = new QProcess(this); - m_pendingArpLookups.insert(arpLookup, host); - connect(arpLookup, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(arpLookupDone(int,QProcess::ExitStatus))); - arpLookup->start("arp", {"-vn"}); - } - - host->setHostName(vendor); - QHostInfo::lookupHost(address, this, SLOT(hostLookupDone(QHostInfo))); - m_pendingNameLookups.insert(address, host); - - m_scanResults.append(host); - } - } - } - - if (foundHosts == 0 && m_discoveryProcesses.isEmpty()) { - qCDebug(dcDiscovery()) << "Network scan successful but no hosts found in this network"; - finishDiscovery(); - } -} - -void Discovery::hostLookupDone(const QHostInfo &info) -{ - Host *host = m_pendingNameLookups.take(info.addresses().first().toString()); - if (!host) { - // Probably aborted... - return; - } - if (info.error() != QHostInfo::NoError) { - qWarning(dcDiscovery()) << "Host lookup failed:" << info.errorString(); - } - if (host->hostName().isEmpty() || info.hostName() != host->address()) { - host->setHostName(info.hostName()); - } - - finishDiscovery(); -} - -void Discovery::arpLookupDone(int exitCode, QProcess::ExitStatus exitStatus) -{ - QProcess *p = static_cast(sender()); - p->deleteLater(); - - Host *host = m_pendingArpLookups.take(p); - - if (exitCode != 0 || exitStatus != QProcess::NormalExit) { - qCWarning(dcDiscovery()) << "ARP lookup process failed for host" << host->address(); - finishDiscovery(); - return; - } - - QString data = QString::fromLatin1(p->readAll()); - foreach (QString line, data.split('\n')) { - line.replace(QRegExp("[ ]{1,}"), " "); - QStringList parts = line.split(" "); - if (parts.count() >= 3 && parts.first() == host->address() && parts.at(1) == "ether") { - host->setMacAddress(parts.at(2)); - break; - } - } - finishDiscovery(); -} - -void Discovery::onTimeout() -{ - qWarning(dcDiscovery()) << "Timeout hit. Stopping discovery"; - while (!m_discoveryProcesses.isEmpty()) { - QProcess *discoveryProcess = m_discoveryProcesses.takeFirst(); - disconnect(this, SLOT(discoveryFinished(int,QProcess::ExitStatus))); - discoveryProcess->terminate(); - delete discoveryProcess; - } - foreach (QProcess *p, m_pendingArpLookups.keys()) { - p->terminate(); - m_scanResults.removeAll(m_pendingArpLookups.value(p)); - delete p; - } - m_pendingArpLookups.clear(); - m_pendingNameLookups.clear(); - finishDiscovery(); -} - -QStringList Discovery::getDefaultTargets() -{ - QStringList targets; - foreach (const QHostAddress &interface, QNetworkInterface::allAddresses()) { - if (!interface.isLoopback() && interface.scopeId().isEmpty() && interface.protocol() == QAbstractSocket::IPv4Protocol) { - QPair pair = QHostAddress::parseSubnet(interface.toString() + "/24"); - QString newTarget = QString("%1/%2").arg(pair.first.toString()).arg(pair.second); - if (!targets.contains(newTarget)) { - targets.append(newTarget); - } - } - } - return targets; -} - -void Discovery::finishDiscovery() -{ - if (m_discoveryProcesses.count() > 0 || m_pendingNameLookups.count() > 0 || m_pendingArpLookups.count() > 0) { - // Still busy... - return; - } - - QList hosts; - foreach (Host *host, m_scanResults) { - if (!host->macAddress().isEmpty()) { - hosts.append(*host); - } - } - qDeleteAll(m_scanResults); - m_scanResults.clear(); - - qCDebug(dcDiscovery()) << "Found" << hosts.count() << "network devices"; - m_timeoutTimer.stop(); - emit finished(hosts); -} - diff --git a/discovery/discovery.h b/discovery/discovery.h deleted file mode 100644 index 02284c3..0000000 --- a/discovery/discovery.h +++ /dev/null @@ -1,75 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef DISCOVERY_H -#define DISCOVERY_H - -#include -#include -#include -#include - -#include "host.h" - -class Discovery : public QObject -{ - Q_OBJECT -public: - explicit Discovery(QObject *parent = nullptr); - - void discoverHosts(int timeout); - void abort(); - bool isRunning() const; - -signals: - void finished(const QList &hosts); - -private: - QStringList getDefaultTargets(); - - void finishDiscovery(); - -private slots: - void discoveryFinished(int exitCode, QProcess::ExitStatus exitStatus); - void hostLookupDone(const QHostInfo &info); - void arpLookupDone(int exitCode, QProcess::ExitStatus exitStatus); - void onTimeout(); - -private: - QList m_discoveryProcesses; - QTimer m_timeoutTimer; - - QHash m_pendingArpLookups; - QHash m_pendingNameLookups; - QList m_scanResults; -}; - -#endif // DISCOVERY_H - diff --git a/discovery/host.cpp b/discovery/host.cpp deleted file mode 100644 index f1e80a5..0000000 --- a/discovery/host.cpp +++ /dev/null @@ -1,94 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#include "host.h" - -Host::Host() -{ - qRegisterMetaType(); - qRegisterMetaType >(); -} - -QString Host::macAddress() const -{ - return m_macAddress; -} - -void Host::setMacAddress(const QString &macAddress) -{ - m_macAddress = macAddress; -} - -QString Host::hostName() const -{ - return m_hostName; -} - -void Host::setHostName(const QString &hostName) -{ - m_hostName = hostName; -} - -QString Host::address() const -{ - return m_address; -} - -void Host::setAddress(const QString &address) -{ - m_address = address; -} - -void Host::seen() -{ - m_lastSeenTime = QDateTime::currentDateTime(); -} - -QDateTime Host::lastSeenTime() const -{ - return m_lastSeenTime; -} - -bool Host::reachable() const -{ - return m_reachable; -} - -void Host::setReachable(bool reachable) -{ - m_reachable = reachable; -} - -QDebug operator<<(QDebug dbg, const Host &host) -{ - dbg.nospace() << "Host(" << host.macAddress() << "," << host.hostName() << ", " << host.address() << ", " << (host.reachable() ? "up" : "down") << ")"; - return dbg.space(); -} - diff --git a/discovery/host.h b/discovery/host.h deleted file mode 100644 index 8fe28a2..0000000 --- a/discovery/host.h +++ /dev/null @@ -1,70 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -* -* Copyright 2013 - 2020, nymea GmbH -* Contact: contact@nymea.io -* -* This file is part of nymea. -* This project including source code and documentation is protected by -* copyright law, and remains the property of nymea GmbH. All rights, including -* reproduction, publication, editing and translation, are reserved. The use of -* this project is subject to the terms of a license agreement to be concluded -* with nymea GmbH in accordance with the terms of use of nymea GmbH, available -* under https://nymea.io/license -* -* GNU Lesser General Public License Usage -* Alternatively, this project may be redistributed and/or modified under the -* terms of the GNU Lesser General Public License as published by the Free -* Software Foundation; version 3. This project is distributed in the hope that -* it will be useful, but WITHOUT ANY WARRANTY; without even the implied -* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -* Lesser General Public License for more details. -* -* You should have received a copy of the GNU Lesser General Public License -* along with this project. If not, see . -* -* For any further details and any questions please contact us under -* contact@nymea.io or see our FAQ/Licensing Information on -* https://nymea.io/license/faq -* -* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - -#ifndef HOST_H -#define HOST_H - -#include -#include -#include - -class Host -{ -public: - Host(); - - QString macAddress() const; - void setMacAddress(const QString &macAddress); - - QString hostName() const; - void setHostName(const QString &hostName); - - QString address() const; - void setAddress(const QString &address); - - void seen(); - QDateTime lastSeenTime() const; - - bool reachable() const; - void setReachable(bool reachable); - -private: - QString m_macAddress; - QString m_hostName; - QString m_address; - QDateTime m_lastSeenTime; - bool m_reachable; -}; -Q_DECLARE_METATYPE(Host) - -QDebug operator<<(QDebug dbg, const Host &host); - -#endif // HOST_H - From fdee0d6453f5e02f53f5e0e7e9c0cd80757a2653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 11 Jun 2021 11:15:16 +0200 Subject: [PATCH 7/7] Update discovery to renamed network device --- idm/integrationpluginidm.cpp | 32 ++++---- modbus/modbustcpmaster.cpp | 75 +++++++------------ modbus/modbustcpmaster.h | 3 +- .../integrationpluginmodbuscommander.cpp | 30 +++++--- sunspec/integrationpluginsunspec.cpp | 30 +++++--- wallbe/integrationpluginwallbe.cpp | 36 +++++---- webasto/integrationpluginwebasto.cpp | 34 +++++---- 7 files changed, 124 insertions(+), 116 deletions(-) diff --git a/idm/integrationpluginidm.cpp b/idm/integrationpluginidm.cpp index e8be7c9..7f92ed1 100644 --- a/idm/integrationpluginidm.cpp +++ b/idm/integrationpluginidm.cpp @@ -39,39 +39,45 @@ IntegrationPluginIdm::IntegrationPluginIdm() void IntegrationPluginIdm::discoverThings(ThingDiscoveryInfo *info) { + if (!hardwareManager()->networkDeviceDiscovery()->available()) { + qCWarning(dcIdm()) << "Failed to discover network devices. The network device discovery is not available."; + info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The discovery is not available.")); + return; + } + qCDebug(dcIdm()) << "Discovering network..."; NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ ThingDescriptors descriptors; - qCDebug(dcIdm()) << "Discovery finished. Found" << discoveryReply->networkDevices().count() << "devices"; - foreach (const NetworkDevice &networkDevice, discoveryReply->networkDevices()) { - qCDebug(dcIdm()) << networkDevice; + qCDebug(dcIdm()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices"; + foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) { + qCDebug(dcIdm()) << networkDeviceInfo; QString title; - if (networkDevice.hostName().isEmpty()) { - title += networkDevice.address().toString(); + if (networkDeviceInfo.hostName().isEmpty()) { + title += networkDeviceInfo.address().toString(); } else { - title += networkDevice.address().toString() + " (" + networkDevice.hostName() + ")"; + title += networkDeviceInfo.address().toString() + " (" + networkDeviceInfo.hostName() + ")"; } QString description; - if (networkDevice.macAddressManufacturer().isEmpty()) { - description = networkDevice.macAddress(); + if (networkDeviceInfo.macAddressManufacturer().isEmpty()) { + description = networkDeviceInfo.macAddress(); } else { - description = networkDevice.macAddress() + " (" + networkDevice.macAddressManufacturer() + ")"; + description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")"; } ThingDescriptor descriptor(navigator2ThingClassId, title, description); // Check if we already have set up this device - Things existingThings = myThings().filterByParam(navigator2ThingMacAddressParamTypeId, networkDevice.macAddress()); + Things existingThings = myThings().filterByParam(navigator2ThingMacAddressParamTypeId, networkDeviceInfo.macAddress()); if (existingThings.count() == 1) { - qCDebug(dcIdm()) << "This thing already exists in the system." << existingThings.first() << networkDevice; + qCDebug(dcIdm()) << "This thing already exists in the system." << existingThings.first() << networkDeviceInfo; descriptor.setThingId(existingThings.first()->id()); } ParamList params; - params << Param(navigator2ThingMacAddressParamTypeId, networkDevice.macAddress()); - params << Param(navigator2ThingIpAddressParamTypeId, networkDevice.address().toString()); + params << Param(navigator2ThingMacAddressParamTypeId, networkDeviceInfo.macAddress()); + params << Param(navigator2ThingIpAddressParamTypeId, networkDeviceInfo.address().toString()); descriptor.setParams(params); info->addThingDescriptor(descriptor); } diff --git a/modbus/modbustcpmaster.cpp b/modbus/modbustcpmaster.cpp index bface2c..880f4c6 100644 --- a/modbus/modbustcpmaster.cpp +++ b/modbus/modbustcpmaster.cpp @@ -57,14 +57,6 @@ ModbusTCPMaster::~ModbusTCPMaster() { if (m_reconnectTimer) { m_reconnectTimer->stop(); - delete m_reconnectTimer; - m_reconnectTimer = nullptr; - } - - if (m_modbusTcpClient) { - m_modbusTcpClient->disconnectDevice(); - delete m_modbusTcpClient; - m_modbusTcpClient = nullptr; } if (m_modbusTcpClient) { @@ -92,40 +84,27 @@ void ModbusTCPMaster::setHostAddress(const QHostAddress &hostAddress) m_hostAddress = hostAddress; } -QHostAddress ModbusTCPMaster::hostAddress() const -{ - return m_hostAddress; -} - -uint ModbusTCPMaster::port() const -{ - return m_port; -} - -bool ModbusTCPMaster::setPort(uint port) -{ - m_port = port; - return connectDevice(); -} - -bool ModbusTCPMaster::setHostAddress(const QHostAddress &hostAddress) -{ - m_hostAddress = hostAddress; - return connectDevice(); -} - bool ModbusTCPMaster::connectDevice() { // TCP connection to target device - qCDebug(dcModbusTCP()) << "Setting up TCP connecion" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port); if (!m_modbusTcpClient) return false; - m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_port); - m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_hostAddress.toString()); - m_modbusTcpClient->setTimeout(m_timeout); - m_modbusTcpClient->setNumberOfRetries(m_numberOfRetries); + // Only connect if we are in the unconnected state + if (m_modbusTcpClient->state() == QModbusDevice::UnconnectedState) { + qCDebug(dcModbusTCP()) << "Connecting modbus TCP client to" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port); + m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_port); + m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_hostAddress.toString()); + m_modbusTcpClient->setTimeout(m_timeout); + m_modbusTcpClient->setNumberOfRetries(m_numberOfRetries); + return m_modbusTcpClient->connectDevice(); + } else if (m_modbusTcpClient->state() != QModbusDevice::ConnectedState) { + // Restart the timer in case of connecting not finished yet or closing + m_reconnectTimer->start(); + } else { + qCWarning(dcModbusTCP()) << "Connect modbus TCP device" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port) << "called, but the socket is currently in the" << m_modbusTcpClient->state(); + } - return m_modbusTcpClient->connectDevice(); + return false; } void ModbusTCPMaster::disconnectDevice() @@ -133,9 +112,21 @@ void ModbusTCPMaster::disconnectDevice() if (!m_modbusTcpClient) return; + // Stop the reconnect timer since disconnect was explicitly called + m_reconnectTimer->stop(); m_modbusTcpClient->disconnectDevice(); } +bool ModbusTCPMaster::reconnectDevice() +{ + qCWarning(dcModbusTCP()) << "Reconnecting modbus TCP device" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port); + if (!m_modbusTcpClient) + return false; + + disconnectDevice(); + return connectDevice(); +} + bool ModbusTCPMaster::connected() const { return m_connected; @@ -173,16 +164,6 @@ QModbusDevice::Error ModbusTCPMaster::error() const return m_modbusTcpClient->error(); } -QString ModbusTCPMaster::errorString() const -{ - return m_modbusTcpClient->errorString(); -} - -QModbusDevice::Error ModbusTCPMaster::error() const -{ - return m_modbusTcpClient->error(); -} - QUuid ModbusTCPMaster::readCoil(uint slaveAddress, uint registerAddress, uint size) { if (!m_modbusTcpClient) { @@ -499,6 +480,4 @@ void ModbusTCPMaster::onModbusStateChanged(QModbusDevice::State state) } else if (state == QModbusDevice::UnconnectedState) { m_reconnectTimer->start(); } - - emit connectionStateChanged(connected); } diff --git a/modbus/modbustcpmaster.h b/modbus/modbustcpmaster.h index 1ecdb51..7857922 100644 --- a/modbus/modbustcpmaster.h +++ b/modbus/modbustcpmaster.h @@ -59,7 +59,6 @@ public: int timeout() const; void setTimeout(int timeout); - int timeout(); QString errorString() const; QModbusDevice::Error error() const; @@ -105,9 +104,9 @@ signals: void writeRequestExecuted(const QUuid &requestId, bool success); void writeRequestError(const QUuid &requestId, const QString &error); - void readRequestError(const QUuid &requestId, const QString &error); void readRequestExecuted(const QUuid &requestId, bool success); + void readRequestError(const QUuid &requestId, const QString &error); void receivedCoil(uint slaveAddress, uint modbusRegister, const QVector &values); void receivedDiscreteInput(uint slaveAddress, uint modbusRegister, const QVector &values); diff --git a/modbuscommander/integrationpluginmodbuscommander.cpp b/modbuscommander/integrationpluginmodbuscommander.cpp index 00e8e8b..5889534 100644 --- a/modbuscommander/integrationpluginmodbuscommander.cpp +++ b/modbuscommander/integrationpluginmodbuscommander.cpp @@ -111,37 +111,43 @@ void IntegrationPluginModbusCommander::discoverThings(ThingDiscoveryInfo *info) info->finish(Thing::ThingErrorNoError); return; } else if (thingClassId == modbusTCPClientThingClassId) { + if (!hardwareManager()->networkDeviceDiscovery()->available()) { + qCWarning(dcModbusCommander()) << "Failed to discover network devices. The network device discovery is not available."; + info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The discovery is not available.")); + return; + } + NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ ThingDescriptors descriptors; - qCDebug(dcModbusCommander()) << "Discovery finished. Found" << discoveryReply->networkDevices().count() << "devices"; - foreach (const NetworkDevice &networkDevice, discoveryReply->networkDevices()) { - qCDebug(dcModbusCommander()) << networkDevice; + qCDebug(dcModbusCommander()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices"; + foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) { + qCDebug(dcModbusCommander()) << networkDeviceInfo; QString title; - if (networkDevice.hostName().isEmpty()) { - title += networkDevice.address().toString(); + if (networkDeviceInfo.hostName().isEmpty()) { + title += networkDeviceInfo.address().toString(); } else { - title += networkDevice.address().toString() + " (" + networkDevice.hostName() + ")"; + title += networkDeviceInfo.address().toString() + " (" + networkDeviceInfo.hostName() + ")"; } QString description; - if (networkDevice.macAddressManufacturer().isEmpty()) { - description = networkDevice.macAddress(); + if (networkDeviceInfo.macAddressManufacturer().isEmpty()) { + description = networkDeviceInfo.macAddress(); } else { - description = networkDevice.macAddress() + " (" + networkDevice.macAddressManufacturer() + ")"; + description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")"; } ThingDescriptor descriptor(modbusTCPClientThingClassId, title, description); // Check if we already have set up this device - Things existingThings = myThings().filterByParam(modbusTCPClientThingIpAddressParamTypeId, networkDevice.address().toString()); + Things existingThings = myThings().filterByParam(modbusTCPClientThingIpAddressParamTypeId, networkDeviceInfo.address().toString()); if (existingThings.count() == 1) { - qCDebug(dcModbusCommander()) << "This thing already exists in the system." << existingThings.first() << networkDevice; + qCDebug(dcModbusCommander()) << "This thing already exists in the system." << existingThings.first() << networkDeviceInfo; descriptor.setThingId(existingThings.first()->id()); } ParamList params; - params << Param(modbusTCPClientThingIpAddressParamTypeId, networkDevice.address().toString()); + params << Param(modbusTCPClientThingIpAddressParamTypeId, networkDeviceInfo.address().toString()); descriptor.setParams(params); info->addThingDescriptor(descriptor); } diff --git a/sunspec/integrationpluginsunspec.cpp b/sunspec/integrationpluginsunspec.cpp index e353d12..bb6e3de 100644 --- a/sunspec/integrationpluginsunspec.cpp +++ b/sunspec/integrationpluginsunspec.cpp @@ -101,37 +101,43 @@ void IntegrationPluginSunSpec::init() void IntegrationPluginSunSpec::discoverThings(ThingDiscoveryInfo *info) { + if (!hardwareManager()->networkDeviceDiscovery()->available()) { + qCWarning(dcSunSpec()) << "Failed to discover network devices. The network device discovery is not available."; + info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The discovery is not available.")); + return; + } + NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ ThingDescriptors descriptors; - qCDebug(dcSunSpec()) << "Discovery finished. Found" << discoveryReply->networkDevices().count() << "devices"; - foreach (const NetworkDevice &networkDevice, discoveryReply->networkDevices()) { - qCDebug(dcSunSpec()) << networkDevice; + qCDebug(dcSunSpec()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices"; + foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) { + qCDebug(dcSunSpec()) << networkDeviceInfo; QString title; - if (networkDevice.hostName().isEmpty()) { - title += networkDevice.address().toString(); + if (networkDeviceInfo.hostName().isEmpty()) { + title += networkDeviceInfo.address().toString(); } else { - title += networkDevice.address().toString() + " (" + networkDevice.hostName() + ")"; + title += networkDeviceInfo.address().toString() + " (" + networkDeviceInfo.hostName() + ")"; } QString description; - if (networkDevice.macAddressManufacturer().isEmpty()) { - description = networkDevice.macAddress(); + if (networkDeviceInfo.macAddressManufacturer().isEmpty()) { + description = networkDeviceInfo.macAddress(); } else { - description = networkDevice.macAddress() + " (" + networkDevice.macAddressManufacturer() + ")"; + description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")"; } ThingDescriptor descriptor(sunspecConnectionThingClassId, title, description); // Check if we already have set up this device - Things existingThings = myThings().filterByParam(sunspecConnectionThingIpAddressParamTypeId, networkDevice.address().toString()); + Things existingThings = myThings().filterByParam(sunspecConnectionThingIpAddressParamTypeId, networkDeviceInfo.address().toString()); if (existingThings.count() == 1) { - qCDebug(dcSunSpec()) << "This thing already exists in the system." << existingThings.first() << networkDevice; + qCDebug(dcSunSpec()) << "This thing already exists in the system." << existingThings.first() << networkDeviceInfo; descriptor.setThingId(existingThings.first()->id()); } ParamList params; - params << Param(sunspecConnectionThingIpAddressParamTypeId, networkDevice.address().toString()); + params << Param(sunspecConnectionThingIpAddressParamTypeId, networkDeviceInfo.address().toString()); descriptor.setParams(params); info->addThingDescriptor(descriptor); } diff --git a/wallbe/integrationpluginwallbe.cpp b/wallbe/integrationpluginwallbe.cpp index 9bd15dc..379dd6b 100644 --- a/wallbe/integrationpluginwallbe.cpp +++ b/wallbe/integrationpluginwallbe.cpp @@ -81,43 +81,49 @@ void IntegrationPluginWallbe::init() void IntegrationPluginWallbe::discoverThings(ThingDiscoveryInfo *info) { - if (info->thingClassId() == wallbeEcoThingClassId){ + if (info->thingClassId() == wallbeEcoThingClassId) { + if (!hardwareManager()->networkDeviceDiscovery()->available()) { + qCWarning(dcWallbe()) << "Failed to discover network devices. The network device discovery is not available."; + info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The discovery is not available.")); + return; + } + qCDebug(dcWallbe()) << "Start Wallbe eco discovery"; NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ ThingDescriptors descriptors; - qCDebug(dcWallbe()) << "Discovery finished. Found" << discoveryReply->networkDevices().count() << "devices"; - foreach (const NetworkDevice &networkDevice, discoveryReply->networkDevices()) { - qCDebug(dcWallbe()) << networkDevice; - if (!networkDevice.macAddressManufacturer().contains("Phoenix", Qt::CaseSensitivity::CaseInsensitive)) + qCDebug(dcWallbe()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices"; + foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) { + qCDebug(dcWallbe()) << networkDeviceInfo; + if (!networkDeviceInfo.macAddressManufacturer().contains("Phoenix", Qt::CaseSensitivity::CaseInsensitive)) continue; QString title; - if (networkDevice.hostName().isEmpty()) { - title += networkDevice.address().toString(); + if (networkDeviceInfo.hostName().isEmpty()) { + title += networkDeviceInfo.address().toString(); } else { - title += networkDevice.address().toString() + " (" + networkDevice.hostName() + ")"; + title += networkDeviceInfo.address().toString() + " (" + networkDeviceInfo.hostName() + ")"; } QString description; - if (networkDevice.macAddressManufacturer().isEmpty()) { - description = networkDevice.macAddress(); + if (networkDeviceInfo.macAddressManufacturer().isEmpty()) { + description = networkDeviceInfo.macAddress(); } else { - description = networkDevice.macAddress() + " (" + networkDevice.macAddressManufacturer() + ")"; + description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")"; } ThingDescriptor descriptor(wallbeEcoThingClassId, title, description); // Check if we already have set up this device - Things existingThings = myThings().filterByParam(wallbeEcoThingIpParamTypeId, networkDevice.address().toString()); + Things existingThings = myThings().filterByParam(wallbeEcoThingIpParamTypeId, networkDeviceInfo.address().toString()); if (existingThings.count() == 1) { - qCDebug(dcWallbe()) << "This thing already exists in the system." << existingThings.first() << networkDevice; + qCDebug(dcWallbe()) << "This thing already exists in the system." << existingThings.first() << networkDeviceInfo; descriptor.setThingId(existingThings.first()->id()); } ParamList params; - params << Param(wallbeEcoThingIpParamTypeId, networkDevice.address().toString()); - params << Param(wallbeEcoThingMacParamTypeId, networkDevice.macAddress()); + params << Param(wallbeEcoThingIpParamTypeId, networkDeviceInfo.address().toString()); + params << Param(wallbeEcoThingMacParamTypeId, networkDeviceInfo.macAddress()); descriptor.setParams(params); info->addThingDescriptor(descriptor); } diff --git a/webasto/integrationpluginwebasto.cpp b/webasto/integrationpluginwebasto.cpp index 6f720e5..5e25778 100644 --- a/webasto/integrationpluginwebasto.cpp +++ b/webasto/integrationpluginwebasto.cpp @@ -82,42 +82,48 @@ void IntegrationPluginWebasto::init() void IntegrationPluginWebasto::discoverThings(ThingDiscoveryInfo *info) { if (info->thingClassId() == liveWallboxThingClassId) { + if (!hardwareManager()->networkDeviceDiscovery()->available()) { + qCWarning(dcWebasto()) << "Failed to discover network devices. The network device discovery is not available."; + info->finish(Thing::ThingErrorHardwareNotAvailable, QT_TR_NOOP("The discovery is not available.")); + return; + } + qCDebug(dcWebasto()) << "Discover things"; NetworkDeviceDiscoveryReply *discoveryReply = hardwareManager()->networkDeviceDiscovery()->discover(); connect(discoveryReply, &NetworkDeviceDiscoveryReply::finished, this, [=](){ ThingDescriptors descriptors; - qCDebug(dcWebasto()) << "Discovery finished. Found" << discoveryReply->networkDevices().count() << "devices"; - foreach (const NetworkDevice &networkDevice, discoveryReply->networkDevices()) { - qCDebug(dcWebasto()) << networkDevice; - if (!networkDevice.hostName().contains("webasto", Qt::CaseSensitivity::CaseInsensitive)) + qCDebug(dcWebasto()) << "Discovery finished. Found" << discoveryReply->networkDeviceInfos().count() << "devices"; + foreach (const NetworkDeviceInfo &networkDeviceInfo, discoveryReply->networkDeviceInfos()) { + qCDebug(dcWebasto()) << networkDeviceInfo; + if (!networkDeviceInfo.hostName().contains("webasto", Qt::CaseSensitivity::CaseInsensitive)) continue; QString title = "Wallbox "; - if (networkDevice.hostName().isEmpty()) { - title += networkDevice.address().toString(); + if (networkDeviceInfo.hostName().isEmpty()) { + title += networkDeviceInfo.address().toString(); } else { - title += networkDevice.address().toString() + " (" + networkDevice.hostName() + ")"; + title += networkDeviceInfo.address().toString() + " (" + networkDeviceInfo.hostName() + ")"; } QString description; - if (networkDevice.macAddressManufacturer().isEmpty()) { - description = networkDevice.macAddress(); + if (networkDeviceInfo.macAddressManufacturer().isEmpty()) { + description = networkDeviceInfo.macAddress(); } else { - description = networkDevice.macAddress() + " (" + networkDevice.macAddressManufacturer() + ")"; + description = networkDeviceInfo.macAddress() + " (" + networkDeviceInfo.macAddressManufacturer() + ")"; } ThingDescriptor descriptor(liveWallboxThingClassId, title, description); // Check if we already have set up this device - Things existingThings = myThings().filterByParam(liveWallboxThingIpAddressParamTypeId, networkDevice.address().toString()); + Things existingThings = myThings().filterByParam(liveWallboxThingIpAddressParamTypeId, networkDeviceInfo.address().toString()); if (existingThings.count() == 1) { - qCDebug(dcWebasto()) << "This thing already exists in the system." << existingThings.first() << networkDevice; + qCDebug(dcWebasto()) << "This thing already exists in the system." << existingThings.first() << networkDeviceInfo; descriptor.setThingId(existingThings.first()->id()); } ParamList params; - params << Param(liveWallboxThingIpAddressParamTypeId, networkDevice.address().toString()); - params << Param(liveWallboxThingMacAddressParamTypeId, networkDevice.macAddress()); + params << Param(liveWallboxThingIpAddressParamTypeId, networkDeviceInfo.address().toString()); + params << Param(liveWallboxThingMacAddressParamTypeId, networkDeviceInfo.macAddress()); descriptor.setParams(params); info->addThingDescriptor(descriptor); }