Merge PR #689: Rework network discovery
This commit is contained in:
commit
95ebffee0c
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2022, nymea GmbH
|
* Copyright 2013 - 2024, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
@ -32,12 +32,15 @@
|
|||||||
#include "nymeasettings.h"
|
#include "nymeasettings.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include <network/ping.h>
|
#include <network/ping.h>
|
||||||
#include <network/arpsocket.h>
|
#include <network/arpsocket.h>
|
||||||
#include <network/networkutils.h>
|
#include <network/networkutils.h>
|
||||||
|
|
||||||
|
#define CACHE_VERSION 1
|
||||||
|
|
||||||
NYMEA_LOGGING_CATEGORY(dcNetworkDeviceDiscovery, "NetworkDeviceDiscovery")
|
NYMEA_LOGGING_CATEGORY(dcNetworkDeviceDiscovery, "NetworkDeviceDiscovery")
|
||||||
|
|
||||||
namespace nymeaserver {
|
namespace nymeaserver {
|
||||||
@ -86,7 +89,7 @@ NetworkDeviceDiscoveryImpl::NetworkDeviceDiscoveryImpl(QObject *parent) :
|
|||||||
|
|
||||||
// Timer for updating the monitors
|
// Timer for updating the monitors
|
||||||
m_monitorTimer = new QTimer(this);
|
m_monitorTimer = new QTimer(this);
|
||||||
m_monitorTimer->setInterval(5000);
|
m_monitorTimer->setInterval(10000);
|
||||||
m_monitorTimer->setSingleShot(false);
|
m_monitorTimer->setSingleShot(false);
|
||||||
connect(m_monitorTimer, &QTimer::timeout, this, &NetworkDeviceDiscoveryImpl::evaluateMonitors);
|
connect(m_monitorTimer, &QTimer::timeout, this, &NetworkDeviceDiscoveryImpl::evaluateMonitors);
|
||||||
|
|
||||||
@ -128,19 +131,19 @@ NetworkDeviceDiscoveryReply *NetworkDeviceDiscoveryImpl::discover()
|
|||||||
} else {
|
} else {
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Starting internally a new discovery.";
|
qCDebug(dcNetworkDeviceDiscovery()) << "Starting internally a new discovery.";
|
||||||
m_currentDiscoveryReply = new NetworkDeviceDiscoveryReplyImpl(this);
|
m_currentDiscoveryReply = new NetworkDeviceDiscoveryReplyImpl(this);
|
||||||
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::networkDeviceInfoAdded, this, &NetworkDeviceDiscoveryImpl::updateCache);
|
// connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::networkDeviceInfoAdded, this, &NetworkDeviceDiscoveryImpl::updateCache);
|
||||||
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::finished, this, [this](){
|
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::finished, this, [this](){
|
||||||
// Finish all pending replies
|
// Finish all pending replies
|
||||||
foreach (NetworkDeviceDiscoveryReplyImpl *reply, m_pendingDiscoveryReplies) {
|
foreach (NetworkDeviceDiscoveryReplyImpl *reply, m_pendingDiscoveryReplies) {
|
||||||
|
|
||||||
// Sync all network device infos with all pending replies
|
// Sync all network device infos with all pending replies
|
||||||
foreach (const NetworkDeviceInfo &info, m_currentDiscoveryReply->networkDeviceInfos()) {
|
foreach (const NetworkDeviceInfo &info, m_currentDiscoveryReply->networkDeviceInfos()) {
|
||||||
reply->addCompleteNetworkDeviceInfo(info);
|
reply->addCompleteNetworkDeviceInfo(info);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
foreach (const NetworkDeviceInfo &info, m_currentDiscoveryReply->virtualNetworkDeviceInfos()) {
|
// Update local cache right after a discovery finished
|
||||||
reply->addVirtualNetworkDeviceInfo(info);
|
foreach (const NetworkDeviceInfo &info, m_currentDiscoveryReply->networkDeviceInfos()) {
|
||||||
}
|
updateCache(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the current reply before finishing the pending replies.
|
// Delete the current reply before finishing the pending replies.
|
||||||
@ -159,8 +162,6 @@ NetworkDeviceDiscoveryReply *NetworkDeviceDiscoveryImpl::discover()
|
|||||||
|
|
||||||
// Create the reply for the user
|
// Create the reply for the user
|
||||||
NetworkDeviceDiscoveryReplyImpl *reply = new NetworkDeviceDiscoveryReplyImpl(this);
|
NetworkDeviceDiscoveryReplyImpl *reply = new NetworkDeviceDiscoveryReplyImpl(this);
|
||||||
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::networkDeviceInfoAdded, reply, &NetworkDeviceDiscoveryReplyImpl::addCompleteNetworkDeviceInfo);
|
|
||||||
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered, reply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered);
|
|
||||||
m_pendingDiscoveryReplies.append(reply);
|
m_pendingDiscoveryReplies.append(reply);
|
||||||
|
|
||||||
if (!available()) {
|
if (!available()) {
|
||||||
@ -171,19 +172,23 @@ NetworkDeviceDiscoveryReply *NetworkDeviceDiscoveryImpl::discover()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (alreadyRunning) {
|
if (alreadyRunning) {
|
||||||
// Add already discovered network device infos in the next event loop
|
// Emit allready discovered hosts so any integration can implement safly a connect on the host hostAddressDiscovered signal
|
||||||
// so any connections after this method call will work as expected
|
// even if the internal discvoery was already running
|
||||||
QTimer::singleShot(0, reply, [this, reply](){
|
QTimer::singleShot(0, reply, [this, reply](){
|
||||||
if (!m_currentDiscoveryReply)
|
if (!m_currentDiscoveryReply)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (const NetworkDeviceInfo &networkDeviceInfo, m_currentDiscoveryReply->networkDeviceInfos()) {
|
foreach (const QHostAddress &address, m_currentDiscoveryReply->currentCache().keys()) {
|
||||||
reply->addCompleteNetworkDeviceInfo(networkDeviceInfo);
|
reply->hostAddressDiscovered(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered, reply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
qCInfo(dcNetworkDeviceDiscovery()) << "Starting network device discovery ...";
|
qCInfo(dcNetworkDeviceDiscovery()) << "Starting network device discovery ...";
|
||||||
|
|
||||||
|
connect(m_currentDiscoveryReply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered, reply, &NetworkDeviceDiscoveryReplyImpl::hostAddressDiscovered);
|
||||||
|
|
||||||
if (m_ping->available())
|
if (m_ping->available())
|
||||||
pingAllNetworkDevices();
|
pingAllNetworkDevices();
|
||||||
|
|
||||||
@ -213,74 +218,101 @@ bool NetworkDeviceDiscoveryImpl::running() const
|
|||||||
return m_running;
|
return m_running;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkDeviceMonitor *NetworkDeviceDiscoveryImpl::registerMonitor(const MacAddress &macAddress)
|
NetworkDeviceMonitor *NetworkDeviceDiscoveryImpl::registerMonitor(Thing *thing)
|
||||||
{
|
{
|
||||||
if (macAddress.isNull()) {
|
if (!thing->thingClass().interfaces().contains("networkdevice")) {
|
||||||
qCWarning(dcNetworkDeviceDiscovery()) << "Could not register monitor for invalid" << macAddress;
|
qCWarning(dcNetworkDeviceDiscovery()) << "Cannot register network device monitor because the thing"
|
||||||
|
<< thing << "does not implement the \"networkdevice\" interface.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we create only one monitor per MAC and keep track how many user
|
MacAddress macAddress(thing->paramValue("macAddress").toString());
|
||||||
// have access to this monitor otherwise an unregister could cause a crash in
|
QString hostName = thing->paramValue("hostName").toString();
|
||||||
// an other plugin plugin which might still need it
|
QHostAddress address(thing->paramValue("address").toString());
|
||||||
if (m_monitors.contains(macAddress)) {
|
|
||||||
m_monitorsReferenceCount[macAddress] += 1;
|
|
||||||
qCInfo(dcNetworkDeviceDiscovery()) << "Register network device monitor for" << macAddress << "which already exists. Returning existing monitor having now" << m_monitorsReferenceCount[macAddress] << "references.";
|
|
||||||
return m_monitors.value(macAddress);
|
|
||||||
}
|
|
||||||
|
|
||||||
qCInfo(dcNetworkDeviceDiscovery()) << "Register new network device monitor for" << macAddress;
|
NetworkDeviceInfo::MonitorMode mode = NetworkDeviceInfo::MonitorModeMac;
|
||||||
|
if (macAddress.isNull()) {
|
||||||
|
if (!hostName.isEmpty()) {
|
||||||
|
mode = NetworkDeviceInfo::MonitorModeHostName;
|
||||||
|
} else {
|
||||||
|
if (address.isNull()) {
|
||||||
|
qCWarning(dcNetworkDeviceDiscovery()) << "Cannot register monitor for thing" << thing <<
|
||||||
|
"because there are not enough information available." <<
|
||||||
|
"At least one parameter from the networkdevice interface needs to be provided." << thing->params();
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
mode = NetworkDeviceInfo::MonitorModeIp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // else we use the MAC address mode
|
||||||
|
|
||||||
// Fill in cached information
|
// Check if we already have a monitor for these settings...
|
||||||
NetworkDeviceInfo info;
|
NetworkDeviceMonitorImpl *internalMonitor = nullptr;
|
||||||
if (m_networkInfoCache.contains(macAddress)) {
|
for (QHash<NetworkDeviceMonitorImpl *, QVector<NetworkDeviceMonitorImpl *>>::const_iterator iterator = m_monitors.cbegin(),
|
||||||
info = m_networkInfoCache.value(macAddress);
|
end = m_monitors.cend(); iterator != end; ++iterator) {
|
||||||
} else {
|
if (iterator.key()->address() == address && iterator.key()->macAddress() == macAddress && iterator.key()->hostName() == hostName) {
|
||||||
info.setMacAddress(macAddress.toString());
|
internalMonitor = iterator.key();
|
||||||
}
|
break;
|
||||||
|
|
||||||
NetworkDeviceMonitorImpl *monitor = new NetworkDeviceMonitorImpl(macAddress, this);
|
|
||||||
monitor->setNetworkDeviceInfo(info);
|
|
||||||
monitor->setLastSeen(m_lastSeen.value(macAddress, QDateTime()));
|
|
||||||
m_monitors.insert(macAddress, monitor);
|
|
||||||
m_monitorsReferenceCount[macAddress] = 1;
|
|
||||||
|
|
||||||
if (!available()) {
|
|
||||||
qCWarning(dcNetworkDeviceDiscovery()) << "Registered monitor but the hardware resource is not available. The monitor will not work as expected" << monitor;
|
|
||||||
return monitor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restart the monitor timer since we evaluate this one now
|
|
||||||
m_monitorTimer->start();
|
|
||||||
|
|
||||||
if (!monitor->networkDeviceInfo().isValid()) {
|
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Adding network device monitor for unresolved mac address. Starting a discovery...";
|
|
||||||
NetworkDeviceDiscoveryReply *reply = discover();
|
|
||||||
connect(reply, &NetworkDeviceDiscoveryReply::finished, reply, &NetworkDeviceDiscoveryReply::deleteLater);
|
|
||||||
} else {
|
|
||||||
evaluateMonitor(monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Registered successfully" << monitor;
|
|
||||||
return monitor;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryImpl::unregisterMonitor(const MacAddress &macAddress)
|
|
||||||
{
|
|
||||||
if (m_monitorsReferenceCount.contains(macAddress)) {
|
|
||||||
m_monitorsReferenceCount[macAddress] -= 1;
|
|
||||||
if (m_monitorsReferenceCount[macAddress] > 0) {
|
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Unregistered monitor for" << macAddress.toString() << "but keeping the monitor. There are still" << m_monitorsReferenceCount[macAddress] << "references to it.";
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_monitors.contains(macAddress)) {
|
bool newMonitor = true;
|
||||||
NetworkDeviceMonitorImpl *monitor = m_monitors.take(macAddress);
|
if (internalMonitor) {
|
||||||
qCInfo(dcNetworkDeviceDiscovery()) << "Unregister" << monitor;
|
qCDebug(dcNetworkDeviceDiscovery()) << "Already have an internal monitor for this network device" << internalMonitor;
|
||||||
monitor->deleteLater();
|
newMonitor = false;
|
||||||
m_monitorsReferenceCount.remove(macAddress);
|
} else {
|
||||||
|
// Create a new monitor for the internal use
|
||||||
|
internalMonitor = new NetworkDeviceMonitorImpl(macAddress, hostName, address, this);
|
||||||
|
m_monitors.insert(internalMonitor, QVector<NetworkDeviceMonitorImpl*>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internalMonitor->setMonitorMode(mode);
|
||||||
|
|
||||||
|
if (m_networkInfoCache.isEmpty()) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "Cache is empty. Starting an internal discovery...";
|
||||||
|
NetworkDeviceDiscoveryReply *reply = discover();
|
||||||
|
connect(reply, &NetworkDeviceDiscoveryReply::finished, reply, &NetworkDeviceDiscoveryReply::deleteLater);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find and set the network device info
|
||||||
|
for (int i = 0; i < m_networkInfoCache.count(); i++) {
|
||||||
|
const NetworkDeviceInfo networkDeviceInfo = m_networkInfoCache.at(i);
|
||||||
|
|
||||||
|
switch (internalMonitor->monitorMode()) {
|
||||||
|
case NetworkDeviceInfo::MonitorModeMac:
|
||||||
|
// Search the unique mac address
|
||||||
|
if (networkDeviceInfo.macAddressInfos().hasMacAddress(internalMonitor->macAddress())) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "MAC monitor:" << networkDeviceInfo;
|
||||||
|
internalMonitor->setNetworkDeviceInfo(networkDeviceInfo);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NetworkDeviceInfo::MonitorModeHostName:
|
||||||
|
// Search the unique mac address
|
||||||
|
if (networkDeviceInfo.hostName() == internalMonitor->hostName()) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "Host name monitor:" << networkDeviceInfo;
|
||||||
|
internalMonitor->setNetworkDeviceInfo(networkDeviceInfo);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NetworkDeviceInfo::MonitorModeIp:
|
||||||
|
// Search the unique mac address
|
||||||
|
if (networkDeviceInfo.address() == internalMonitor->address()) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "IP monitor:" << networkDeviceInfo;
|
||||||
|
internalMonitor->setNetworkDeviceInfo(networkDeviceInfo);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new plugin monitor object we are going to return...
|
||||||
|
NetworkDeviceMonitorImpl *pluginMonitor = createPluginMonitor(internalMonitor);
|
||||||
|
m_monitors[internalMonitor].append(pluginMonitor);
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "Registered successfully" << pluginMonitor;
|
||||||
|
|
||||||
|
// In case this is a new monitor, let's evaluate it right the way so know asap if the device is reachable or not
|
||||||
|
if (newMonitor)
|
||||||
|
evaluateMonitor(internalMonitor);
|
||||||
|
|
||||||
|
return pluginMonitor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryImpl::unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor)
|
void NetworkDeviceDiscoveryImpl::unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor)
|
||||||
@ -288,10 +320,7 @@ void NetworkDeviceDiscoveryImpl::unregisterMonitor(NetworkDeviceMonitor *network
|
|||||||
if (!networkDeviceMonitor)
|
if (!networkDeviceMonitor)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!m_monitors.values().contains(qobject_cast<NetworkDeviceMonitorImpl *>(networkDeviceMonitor)))
|
cleanupPluginMonitor(qobject_cast<NetworkDeviceMonitorImpl *>(networkDeviceMonitor));
|
||||||
return;
|
|
||||||
|
|
||||||
unregisterMonitor(MacAddress(networkDeviceMonitor->networkDeviceInfo().macAddress()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, uint retries)
|
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, uint retries)
|
||||||
@ -302,6 +331,14 @@ PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, uint re
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PingReply *NetworkDeviceDiscoveryImpl::ping(const QString &hostName, uint retries)
|
||||||
|
{
|
||||||
|
PingReply *reply = m_ping->ping(hostName, retries);
|
||||||
|
// Note: we use any ping used trough this method also for the monitor evaluation
|
||||||
|
watchPingReply(reply);
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, bool lookupHost, uint retries)
|
PingReply *NetworkDeviceDiscoveryImpl::ping(const QHostAddress &address, bool lookupHost, uint retries)
|
||||||
{
|
{
|
||||||
PingReply *reply = m_ping->ping(address, lookupHost, retries);
|
PingReply *reply = m_ping->ping(address, lookupHost, retries);
|
||||||
@ -328,7 +365,7 @@ bool NetworkDeviceDiscoveryImpl::sendArpRequest(const QHostAddress &address)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QHash<MacAddress, NetworkDeviceInfo> NetworkDeviceDiscoveryImpl::cache() const
|
NetworkDeviceInfos NetworkDeviceDiscoveryImpl::cache() const
|
||||||
{
|
{
|
||||||
return m_networkInfoCache;
|
return m_networkInfoCache;
|
||||||
}
|
}
|
||||||
@ -449,7 +486,35 @@ void NetworkDeviceDiscoveryImpl::processMonitorPingResult(PingReply *reply, Netw
|
|||||||
// Save the last time we tried to communicate
|
// Save the last time we tried to communicate
|
||||||
if (reply->error() == PingReply::ErrorNoError) {
|
if (reply->error() == PingReply::ErrorNoError) {
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Ping response from" << monitor << reply->duration() << "ms";
|
qCDebug(dcNetworkDeviceDiscovery()) << "Ping response from" << monitor << reply->duration() << "ms";
|
||||||
monitor->setLastSeen(QDateTime::currentDateTime());
|
|
||||||
|
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||||
|
m_lastSeen[reply->targetHostAddress()] = currentDateTime;
|
||||||
|
|
||||||
|
for (int i = 0; i < m_networkInfoCache.count(); i++) {
|
||||||
|
|
||||||
|
if (monitor->monitorMode() == NetworkDeviceInfo::MonitorModeHostName) {
|
||||||
|
if (m_networkInfoCache.at(i).hostName() == monitor->hostName() && m_networkInfoCache.at(i).address() != reply->targetHostAddress()) {
|
||||||
|
QHostAddress oldAddress = m_networkInfoCache.at(i).address();
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "Hostname" << monitor->hostName() << "changed the IP address from"
|
||||||
|
<< oldAddress.toString()
|
||||||
|
<< "-->"
|
||||||
|
<< reply->targetHostAddress().toString();
|
||||||
|
|
||||||
|
removeFromNetworkDeviceCache(oldAddress);
|
||||||
|
|
||||||
|
NetworkDeviceInfo info = m_networkInfoCache.at(i);
|
||||||
|
info.setAddress(reply->targetHostAddress());
|
||||||
|
|
||||||
|
monitor->setNetworkDeviceInfo(info);
|
||||||
|
m_networkInfoCache[i] = info;
|
||||||
|
m_networkInfoCache.sortNetworkDevices();
|
||||||
|
saveNetworkDeviceCache(info);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor->setLastSeen(currentDateTime);
|
||||||
monitor->setReachable(true);
|
monitor->setReachable(true);
|
||||||
} else {
|
} else {
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Failed to ping device from" << monitor << "retrying" << reply->retries() << "times:" << reply->error();
|
qCDebug(dcNetworkDeviceDiscovery()) << "Failed to ping device from" << monitor << "retrying" << reply->retries() << "times:" << reply->error();
|
||||||
@ -459,24 +524,22 @@ void NetworkDeviceDiscoveryImpl::processMonitorPingResult(PingReply *reply, Netw
|
|||||||
|
|
||||||
void NetworkDeviceDiscoveryImpl::watchPingReply(PingReply *reply)
|
void NetworkDeviceDiscoveryImpl::watchPingReply(PingReply *reply)
|
||||||
{
|
{
|
||||||
connect(reply, &PingReply::finished, this, [=](){
|
connect(reply, &PingReply::finished, this, [this, reply](){
|
||||||
// Search cache for mac address and update last seen
|
|
||||||
if (reply->error() == PingReply::ErrorNoError) {
|
if (reply->error() == PingReply::ErrorNoError) {
|
||||||
foreach (const NetworkDeviceInfo &info, m_networkInfoCache) {
|
|
||||||
if (info.address() == reply->targetHostAddress()) {
|
int index = m_networkInfoCache.indexFromHostAddress(reply->targetHostAddress());
|
||||||
// Found info for this ip, update the cache
|
if (index < 0)
|
||||||
MacAddress macAddress(info.macAddress());
|
return;
|
||||||
if (!macAddress.isNull() && m_networkInfoCache.contains(macAddress)) {
|
|
||||||
m_lastSeen[macAddress] = QDateTime::currentDateTime();
|
m_lastSeen[reply->targetHostAddress()] = QDateTime::currentDateTime();
|
||||||
saveNetworkDeviceCache(m_networkInfoCache.value(macAddress));
|
saveNetworkDeviceCache(m_networkInfoCache.at(index));
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update any monitor
|
// Update any relevant monitor
|
||||||
foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.values()) {
|
foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.keys()) {
|
||||||
if (monitor->networkDeviceInfo().address() == reply->targetHostAddress()) {
|
if ((monitor->monitorMode() == NetworkDeviceInfo::MonitorModeIp && reply->targetHostAddress() == monitor->address()) ||
|
||||||
|
(monitor->monitorMode() == NetworkDeviceInfo::MonitorModeHostName && reply->hostName() == monitor->hostName()) ||
|
||||||
|
(monitor->monitorMode() == NetworkDeviceInfo::MonitorModeMac && reply->targetHostAddress() == monitor->networkDeviceInfo().address())) {
|
||||||
processMonitorPingResult(reply, monitor);
|
processMonitorPingResult(reply, monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -488,41 +551,61 @@ void NetworkDeviceDiscoveryImpl::loadNetworkDeviceCache()
|
|||||||
qCInfo(dcNetworkDeviceDiscovery()) << "Loading cached network device information from" << m_cacheSettings->fileName();
|
qCInfo(dcNetworkDeviceDiscovery()) << "Loading cached network device information from" << m_cacheSettings->fileName();
|
||||||
|
|
||||||
m_networkInfoCache.clear();
|
m_networkInfoCache.clear();
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||||
|
|
||||||
m_cacheSettings->beginGroup("NetworkDeviceInfos");
|
uint cacheVersion = m_cacheSettings->value("version", 0).toUInt();
|
||||||
foreach (const QString &macAddress, m_cacheSettings->childGroups()) {
|
|
||||||
m_cacheSettings->beginGroup(macAddress);
|
|
||||||
|
|
||||||
MacAddress mac(macAddress);
|
// Load only the cache if we have the same format, otherwise we discard the cache and start over with the current version
|
||||||
QDateTime lastSeen = QDateTime::fromMSecsSinceEpoch(m_cacheSettings->value("lastSeen").toLongLong());
|
// No need to migrate the caches, a new discovery will to that for us ...
|
||||||
|
|
||||||
// Remove the info from the cache if not seen fo the last 30 days...
|
if (cacheVersion == CACHE_VERSION) {
|
||||||
if (lastSeen.date().addDays(m_cacheCleanupPeriod) < now.date()) {
|
m_cacheSettings->beginGroup("NetworkDeviceInfos");
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Removing network device cache entry since it did not show up within the last" << m_cacheCleanupPeriod << "days" << mac.toString();
|
foreach (const QString &addressString, m_cacheSettings->childGroups()) {
|
||||||
m_cacheSettings->remove("");
|
|
||||||
m_cacheSettings->endGroup(); // mac address
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkDeviceInfo info(mac.toString());
|
m_cacheSettings->beginGroup(addressString);
|
||||||
info.setAddress(QHostAddress(m_cacheSettings->value("address").toString()));
|
|
||||||
info.setHostName(m_cacheSettings->value("hostName").toString());
|
|
||||||
info.setMacAddressManufacturer(m_cacheSettings->value("manufacturer").toString());
|
|
||||||
info.setNetworkInterface(QNetworkInterface::interfaceFromName(m_cacheSettings->value("interface").toString()));
|
|
||||||
|
|
||||||
if (info.isValid() && info.isComplete()) {
|
QHostAddress address(addressString);
|
||||||
|
QDateTime lastSeen = QDateTime::fromMSecsSinceEpoch(m_cacheSettings->value("lastSeen").toLongLong());
|
||||||
|
|
||||||
|
// Remove the info from the cache if not seen fo the last 30 days...
|
||||||
|
if (lastSeen.date().addDays(m_cacheCleanupPeriod) < currentDateTime.date()) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "Removing network device cache entry since it did not show up within the last" << m_cacheCleanupPeriod << "days" << address.toString();
|
||||||
|
m_cacheSettings->remove("");
|
||||||
|
m_cacheSettings->endGroup(); // mac address
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkDeviceInfo info(address);
|
||||||
|
info.setHostName(m_cacheSettings->value("hostName").toString());
|
||||||
|
info.setNetworkInterface(QNetworkInterface::interfaceFromName(m_cacheSettings->value("interface").toString()));
|
||||||
|
|
||||||
|
int size = m_cacheSettings->beginReadArray("mac");
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
m_cacheSettings->setArrayIndex(i);
|
||||||
|
MacAddress macAddress(m_cacheSettings->value("mac").toString());
|
||||||
|
QString vendor = m_cacheSettings->value("vendor").toString();
|
||||||
|
info.addMacAddress(macAddress, vendor);
|
||||||
|
// Cache the mac information for less DB access
|
||||||
|
if (!macAddress.isNull() && !vendor.isEmpty()) {
|
||||||
|
m_macVendorCache[macAddress] = vendor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_cacheSettings->endArray();
|
||||||
|
m_cacheSettings->endGroup(); // address
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Loaded cached" << info << "last seen" << lastSeen.toString();
|
qCDebug(dcNetworkDeviceDiscovery()) << "Loaded cached" << info << "last seen" << lastSeen.toString();
|
||||||
m_networkInfoCache[mac] = info;
|
m_networkInfoCache.append(info);
|
||||||
m_lastSeen[mac] = lastSeen;
|
m_lastSeen[info.address()] = lastSeen;
|
||||||
} else {
|
|
||||||
qCWarning(dcNetworkDeviceDiscovery()) << "Clean up invalid cached network device info from cache" << info;
|
|
||||||
m_cacheSettings->remove("");
|
|
||||||
}
|
}
|
||||||
|
m_cacheSettings->endGroup(); // NetworkDeviceInfos
|
||||||
|
|
||||||
m_cacheSettings->endGroup(); // mac address
|
} else {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "The cache format version has changed. Discard the network device cache ...";
|
||||||
|
m_cacheSettings->setValue("version", CACHE_VERSION);
|
||||||
|
|
||||||
|
m_cacheSettings->beginGroup("NetworkDeviceInfos");
|
||||||
|
m_cacheSettings->remove("");
|
||||||
|
m_cacheSettings->endGroup(); // NetworkDeviceInfos
|
||||||
}
|
}
|
||||||
m_cacheSettings->endGroup(); // NetworkDeviceInfos
|
|
||||||
|
|
||||||
qCInfo(dcNetworkDeviceDiscovery()) << "Loaded" << m_networkInfoCache.count() << "network device infos from cache.";
|
qCInfo(dcNetworkDeviceDiscovery()) << "Loaded" << m_networkInfoCache.count() << "network device infos from cache.";
|
||||||
|
|
||||||
@ -530,17 +613,17 @@ void NetworkDeviceDiscoveryImpl::loadNetworkDeviceCache()
|
|||||||
m_lastCacheHousekeeping = QDateTime::currentDateTime();
|
m_lastCacheHousekeeping = QDateTime::currentDateTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryImpl::removeFromNetworkDeviceCache(const MacAddress &macAddress)
|
void NetworkDeviceDiscoveryImpl::removeFromNetworkDeviceCache(const QHostAddress &address)
|
||||||
{
|
{
|
||||||
if (macAddress.isNull())
|
if (address.isNull())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_networkInfoCache.remove(macAddress);
|
m_networkInfoCache.removeHostAddress(address);
|
||||||
m_lastSeen.remove(macAddress);
|
m_lastSeen.remove(address);
|
||||||
m_cacheSettings->beginGroup("NetworkDeviceInfos");
|
m_cacheSettings->beginGroup("NetworkDeviceInfos");
|
||||||
m_cacheSettings->beginGroup(macAddress.toString());
|
m_cacheSettings->beginGroup(address.toString());
|
||||||
m_cacheSettings->remove("");
|
m_cacheSettings->remove("");
|
||||||
m_cacheSettings->endGroup(); // mac address
|
m_cacheSettings->endGroup(); // address
|
||||||
m_cacheSettings->endGroup(); // NetworkDeviceInfos
|
m_cacheSettings->endGroup(); // NetworkDeviceInfos
|
||||||
m_cacheSettings->sync();
|
m_cacheSettings->sync();
|
||||||
}
|
}
|
||||||
@ -551,47 +634,70 @@ void NetworkDeviceDiscoveryImpl::saveNetworkDeviceCache(const NetworkDeviceInfo
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_cacheSettings->beginGroup("NetworkDeviceInfos");
|
m_cacheSettings->beginGroup("NetworkDeviceInfos");
|
||||||
m_cacheSettings->beginGroup(deviceInfo.macAddress());
|
m_cacheSettings->beginGroup(deviceInfo.address().toString());
|
||||||
m_cacheSettings->setValue("address", deviceInfo.address().toString());
|
|
||||||
m_cacheSettings->setValue("hostName", deviceInfo.hostName());
|
m_cacheSettings->setValue("hostName", deviceInfo.hostName());
|
||||||
m_cacheSettings->setValue("manufacturer", deviceInfo.macAddressManufacturer());
|
|
||||||
m_cacheSettings->setValue("interface", deviceInfo.networkInterface().name());
|
m_cacheSettings->setValue("interface", deviceInfo.networkInterface().name());
|
||||||
m_cacheSettings->setValue("lastSeen", convertMinuteBased(m_lastSeen.value(MacAddress(deviceInfo.macAddress()))).toMSecsSinceEpoch());
|
m_cacheSettings->setValue("lastSeen", convertMinuteBased(m_lastSeen.value(deviceInfo.address())).toMSecsSinceEpoch());
|
||||||
m_cacheSettings->endGroup(); // mac address
|
|
||||||
|
if (!deviceInfo.macAddressInfos().isEmpty()){
|
||||||
|
m_cacheSettings->beginWriteArray("mac");
|
||||||
|
for (int i = 0; i < deviceInfo.macAddressInfos().size(); i++) {
|
||||||
|
m_cacheSettings->setArrayIndex(i);
|
||||||
|
m_cacheSettings->setValue("mac", deviceInfo.macAddressInfos().at(i).macAddress().toString());
|
||||||
|
m_cacheSettings->setValue("vendor", deviceInfo.macAddressInfos().at(i).vendorName());
|
||||||
|
}
|
||||||
|
m_cacheSettings->endArray(); // mac
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cacheSettings->endGroup(); // address
|
||||||
m_cacheSettings->endGroup(); // NetworkDeviceInfos
|
m_cacheSettings->endGroup(); // NetworkDeviceInfos
|
||||||
m_cacheSettings->sync();
|
m_cacheSettings->sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryImpl::updateCache(const NetworkDeviceInfo &deviceInfo)
|
void NetworkDeviceDiscoveryImpl::updateCache(const NetworkDeviceInfo &deviceInfo)
|
||||||
{
|
{
|
||||||
MacAddress macAddress(deviceInfo.macAddress());
|
// Update monitors
|
||||||
if (macAddress.isNull())
|
foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.keys()) {
|
||||||
return;
|
if (monitor->isMyNetworkDeviceInfo(deviceInfo)) {
|
||||||
|
monitor->setNetworkDeviceInfo(deviceInfo);
|
||||||
if (m_monitors.contains(macAddress)) {
|
break;
|
||||||
NetworkDeviceMonitorImpl *monitor = m_monitors.value(macAddress);
|
}
|
||||||
monitor->setNetworkDeviceInfo(deviceInfo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_networkInfoCache.value(macAddress) == deviceInfo)
|
// Save only if changed
|
||||||
|
int index = m_networkInfoCache.indexFromHostAddress(deviceInfo.address());
|
||||||
|
|
||||||
|
if (index >= 0 && m_networkInfoCache.at(index) == deviceInfo)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_networkInfoCache[macAddress] = deviceInfo;
|
if (index < 0) {
|
||||||
|
m_networkInfoCache.append(deviceInfo);
|
||||||
|
} else {
|
||||||
|
m_networkInfoCache[index] = deviceInfo;
|
||||||
|
}
|
||||||
|
|
||||||
saveNetworkDeviceCache(deviceInfo);
|
saveNetworkDeviceCache(deviceInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monitor)
|
void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monitor)
|
||||||
{
|
{
|
||||||
if (!monitor->networkDeviceInfo().isValid())
|
if (monitor->currentPingReply()) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "Monitor has still a ping reply pending:" << monitor;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (monitor->currentPingReply())
|
qCDebug(dcNetworkDeviceDiscovery()) << "Evaluate monitor" << monitor;
|
||||||
return;
|
|
||||||
|
|
||||||
// Start action if we have not seen the device for gracePeriod seconds
|
// Start action if we have not seen the device for gracePeriod seconds
|
||||||
QDateTime currentDateTime = QDateTime::currentDateTime();
|
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||||
|
|
||||||
bool requiresRefresh = false;
|
bool requiresRefresh = false;
|
||||||
|
|
||||||
|
if (!monitor->networkDeviceInfo().isValid()) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "Network device info not valid for" << monitor;
|
||||||
|
requiresRefresh = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (monitor->lastSeen().isNull()) {
|
if (monitor->lastSeen().isNull()) {
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "requires refresh. Not seen since application start.";
|
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "requires refresh. Not seen since application start.";
|
||||||
requiresRefresh = true;
|
requiresRefresh = true;
|
||||||
@ -618,53 +724,83 @@ void NetworkDeviceDiscoveryImpl::evaluateMonitor(NetworkDeviceMonitorImpl *monit
|
|||||||
if (!requiresRefresh)
|
if (!requiresRefresh)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Try to ping first
|
// Try to ping first.
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "try to ping" << monitor->networkDeviceInfo().address().toString();
|
|
||||||
PingReply *reply = m_ping->ping(monitor->networkDeviceInfo().address(), monitor->pingRetries());
|
// IMPORTANT: use the ping methods from this object, so the result will automatically
|
||||||
|
// be evaluated for the monitors and cache
|
||||||
|
PingReply *reply = nullptr;
|
||||||
|
if (monitor->monitorMode() == NetworkDeviceInfo::MonitorModeHostName) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "try to ping" << monitor->hostName();
|
||||||
|
reply = ping(monitor->hostName(), monitor->pingRetries());
|
||||||
|
} else {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << monitor << "try to ping" << monitor->networkDeviceInfo().address().toString();
|
||||||
|
reply = ping(monitor->networkDeviceInfo().address(), monitor->pingRetries());
|
||||||
|
}
|
||||||
|
|
||||||
monitor->setCurrentPingReply(reply);
|
monitor->setCurrentPingReply(reply);
|
||||||
monitor->setLastConnectionAttempt(currentDateTime);
|
monitor->setLastConnectionAttempt(currentDateTime);
|
||||||
|
|
||||||
connect(reply, &PingReply::retry, monitor, [=](PingReply::Error error, uint retryCount){
|
connect(reply, &PingReply::retry, monitor, [monitor](PingReply::Error error, uint retryCount){
|
||||||
Q_UNUSED(error)
|
Q_UNUSED(error)
|
||||||
Q_UNUSED(retryCount)
|
Q_UNUSED(retryCount)
|
||||||
monitor->setLastConnectionAttempt(QDateTime::currentDateTime());
|
monitor->setLastConnectionAttempt(QDateTime::currentDateTime());
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(reply, &PingReply::destroyed, monitor, [=](){
|
connect(reply, &PingReply::destroyed, monitor, [monitor, reply](){
|
||||||
monitor->setCurrentPingReply(nullptr);
|
if (monitor->currentPingReply() == reply) {
|
||||||
|
monitor->setCurrentPingReply(nullptr);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(reply, &PingReply::finished, monitor, [=](){
|
connect(reply, &PingReply::finished, monitor, [monitor, reply](){
|
||||||
processMonitorPingResult(reply, monitor);
|
if (monitor->currentPingReply() == reply) {
|
||||||
|
monitor->setCurrentPingReply(nullptr);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryImpl::processArpTraffic(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
|
void NetworkDeviceDiscoveryImpl::processArpTraffic(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
|
||||||
{
|
{
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||||
m_lastSeen[macAddress] = now;
|
m_lastSeen[address] = currentDateTime;
|
||||||
|
|
||||||
if (m_networkInfoCache.contains(macAddress)) {
|
// Update monitors and cache
|
||||||
if (m_networkInfoCache.value(macAddress).address() != address) {
|
for (int i = 0; i < m_networkInfoCache.count(); i++) {
|
||||||
m_networkInfoCache[macAddress].setAddress(address);
|
switch (m_networkInfoCache.at(i).monitorMode()) {
|
||||||
saveNetworkDeviceCache(m_networkInfoCache.value(macAddress));
|
case NetworkDeviceInfo::MonitorModeMac:
|
||||||
}
|
if (m_networkInfoCache.at(i).macAddressInfos().hasMacAddress(macAddress) &&
|
||||||
}
|
m_networkInfoCache.at(i).address() != address) {
|
||||||
|
|
||||||
// Update the monitors
|
QHostAddress oldAddress = m_networkInfoCache.at(i).address();
|
||||||
NetworkDeviceMonitorImpl *monitor = m_monitors.value(macAddress);
|
qCDebug(dcNetworkDeviceDiscovery()) << "Host" << macAddress.toString() << "changed the IP address from"
|
||||||
if (monitor) {
|
<< oldAddress.toString()
|
||||||
monitor->setLastSeen(now);
|
<< "-->"
|
||||||
if (monitor->networkDeviceInfo().address() != address) {
|
<< address.toString();
|
||||||
NetworkDeviceInfo info = monitor->networkDeviceInfo();
|
|
||||||
info.setAddress(address);
|
|
||||||
monitor->setNetworkDeviceInfo(info);
|
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "NetworkDeviceMonitor" << monitor << "ip address changed";
|
|
||||||
emit monitor->networkDeviceInfoChanged(monitor->networkDeviceInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (monitor->networkDeviceInfo().isComplete()) {
|
removeFromNetworkDeviceCache(oldAddress);
|
||||||
monitor->setReachable(true);
|
|
||||||
|
NetworkDeviceInfo info = m_networkInfoCache.at(i);
|
||||||
|
info.setAddress(address);
|
||||||
|
|
||||||
|
m_networkInfoCache[i] = info;
|
||||||
|
m_networkInfoCache.sortNetworkDevices();
|
||||||
|
saveNetworkDeviceCache(info);
|
||||||
|
|
||||||
|
foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.keys()) {
|
||||||
|
if (monitor->macAddress() == macAddress) {
|
||||||
|
monitor->setNetworkDeviceInfo(info);
|
||||||
|
monitor->setLastSeen(currentDateTime);
|
||||||
|
monitor->setReachable(true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case NetworkDeviceInfo::MonitorModeHostName:
|
||||||
|
case NetworkDeviceInfo::MonitorModeIp:
|
||||||
|
saveNetworkDeviceCache(m_networkInfoCache.at(i));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -677,8 +813,8 @@ void NetworkDeviceDiscoveryImpl::processArpTraffic(const QNetworkInterface &inte
|
|||||||
|
|
||||||
// Check if we know the mac address manufacturer from the cache
|
// Check if we know the mac address manufacturer from the cache
|
||||||
bool requiresMacAddressLookup = true;
|
bool requiresMacAddressLookup = true;
|
||||||
if (m_networkInfoCache.contains(macAddress)) {
|
if (m_macVendorCache.contains(macAddress)) {
|
||||||
QString cachedManufacturer = m_networkInfoCache[macAddress].macAddressManufacturer();
|
QString cachedManufacturer = m_macVendorCache.value(macAddress);
|
||||||
if (!cachedManufacturer.isEmpty()) {
|
if (!cachedManufacturer.isEmpty()) {
|
||||||
// Found the mac address manufacturer in the cache, let's use that one...
|
// Found the mac address manufacturer in the cache, let's use that one...
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Using cached manufacturer " << cachedManufacturer << "for" << macAddress.toString();
|
qCDebug(dcNetworkDeviceDiscovery()) << "Using cached manufacturer " << cachedManufacturer << "for" << macAddress.toString();
|
||||||
@ -700,6 +836,9 @@ void NetworkDeviceDiscoveryImpl::processArpTraffic(const QNetworkInterface &inte
|
|||||||
|
|
||||||
// Note: set the mac manufacturer explicitly to make the info complete (even an empty sring)
|
// Note: set the mac manufacturer explicitly to make the info complete (even an empty sring)
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "MAC manufacturer lookup finished for" << macAddress << ":" << reply->manufacturer();
|
qCDebug(dcNetworkDeviceDiscovery()) << "MAC manufacturer lookup finished for" << macAddress << ":" << reply->manufacturer();
|
||||||
|
if (!reply->manufacturer().isEmpty())
|
||||||
|
m_macVendorCache.insert(macAddress, reply->manufacturer());
|
||||||
|
|
||||||
if (m_currentDiscoveryReply) {
|
if (m_currentDiscoveryReply) {
|
||||||
m_currentDiscoveryReply->processMacManufacturer(macAddress, reply->manufacturer());
|
m_currentDiscoveryReply->processMacManufacturer(macAddress, reply->manufacturer());
|
||||||
|
|
||||||
@ -739,6 +878,58 @@ QDateTime NetworkDeviceDiscoveryImpl::convertMinuteBased(const QDateTime &dateTi
|
|||||||
return dateTimeToConvert;
|
return dateTimeToConvert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkDeviceMonitorImpl *NetworkDeviceDiscoveryImpl::createPluginMonitor(NetworkDeviceMonitorImpl *internalMonitor)
|
||||||
|
{
|
||||||
|
NetworkDeviceMonitorImpl *pluginMonitor = new NetworkDeviceMonitorImpl(internalMonitor->macAddress(), internalMonitor->hostName(), internalMonitor->address(), this);
|
||||||
|
pluginMonitor->setNetworkDeviceInfo(internalMonitor->networkDeviceInfo());
|
||||||
|
pluginMonitor->setMonitorMode(internalMonitor->monitorMode());
|
||||||
|
pluginMonitor->setLastSeen(internalMonitor->lastSeen());
|
||||||
|
pluginMonitor->setLastConnectionAttempt(internalMonitor->lastConnectionAttempt());
|
||||||
|
pluginMonitor->setPingRetries(internalMonitor->pingRetries());
|
||||||
|
pluginMonitor->setReachable(internalMonitor->reachable());
|
||||||
|
|
||||||
|
// internal monitor --> plugin monitor
|
||||||
|
connect(internalMonitor, &NetworkDeviceMonitorImpl::reachableChanged, pluginMonitor, [pluginMonitor](bool reachable){
|
||||||
|
pluginMonitor->setReachable(reachable);
|
||||||
|
});
|
||||||
|
connect(internalMonitor, &NetworkDeviceMonitorImpl::lastSeenChanged, pluginMonitor, [pluginMonitor](const QDateTime &lastSeen){
|
||||||
|
pluginMonitor->setLastSeen(lastSeen);
|
||||||
|
});
|
||||||
|
connect(internalMonitor, &NetworkDeviceMonitorImpl::networkDeviceInfoChanged, pluginMonitor, [pluginMonitor](const NetworkDeviceInfo &networkDeviceInfo){
|
||||||
|
pluginMonitor->setNetworkDeviceInfo(networkDeviceInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
// plugin monitor --> internal monitor
|
||||||
|
connect(pluginMonitor, &NetworkDeviceMonitorImpl::pingRetriesChanged, internalMonitor, [internalMonitor](uint pingRetries){
|
||||||
|
internalMonitor->setPingRetries(pingRetries);
|
||||||
|
});
|
||||||
|
|
||||||
|
// In case the plugin user is deleting the monitor object, we need to clean up here and check if we can remove the internal monitor
|
||||||
|
connect(pluginMonitor, &NetworkDeviceDiscoveryImpl::destroyed, this, [this, pluginMonitor](QObject *) {
|
||||||
|
cleanupPluginMonitor(pluginMonitor);
|
||||||
|
});
|
||||||
|
|
||||||
|
return pluginMonitor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkDeviceDiscoveryImpl::cleanupPluginMonitor(NetworkDeviceMonitorImpl *pluginMonitor)
|
||||||
|
{
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "Unregister plugin monitor" << pluginMonitor;
|
||||||
|
foreach (NetworkDeviceMonitorImpl *internalMonitor, m_monitors.keys()) {
|
||||||
|
if (m_monitors.value(internalMonitor).contains(pluginMonitor)) {
|
||||||
|
m_monitors[internalMonitor].removeAll(pluginMonitor);
|
||||||
|
pluginMonitor->deleteLater();
|
||||||
|
|
||||||
|
if (m_monitors.value(internalMonitor).isEmpty()) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "No monitor registered for this network device any more. Unregister internal monitor" << internalMonitor;
|
||||||
|
// Last refference for this monitor, nobody need this any more. Clean up...
|
||||||
|
m_monitors.remove(internalMonitor);
|
||||||
|
internalMonitor->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryImpl::onArpResponseReceived(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
|
void NetworkDeviceDiscoveryImpl::onArpResponseReceived(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
|
||||||
{
|
{
|
||||||
// Ignore ARP from zero mac
|
// Ignore ARP from zero mac
|
||||||
@ -766,7 +957,7 @@ void NetworkDeviceDiscoveryImpl::onArpRequstReceived(const QNetworkInterface &in
|
|||||||
void NetworkDeviceDiscoveryImpl::evaluateMonitors()
|
void NetworkDeviceDiscoveryImpl::evaluateMonitors()
|
||||||
{
|
{
|
||||||
bool monitorRequiresRediscovery = false;
|
bool monitorRequiresRediscovery = false;
|
||||||
foreach (NetworkDeviceMonitorImpl *monitor, m_monitors) {
|
foreach (NetworkDeviceMonitorImpl *monitor, m_monitors.keys()) {
|
||||||
evaluateMonitor(monitor);
|
evaluateMonitor(monitor);
|
||||||
|
|
||||||
// Check if there is any monitor which has not be seen since
|
// Check if there is any monitor which has not be seen since
|
||||||
@ -781,20 +972,20 @@ void NetworkDeviceDiscoveryImpl::evaluateMonitors()
|
|||||||
connect(reply, &NetworkDeviceDiscoveryReply::finished, reply, &NetworkDeviceDiscoveryReply::deleteLater);
|
connect(reply, &NetworkDeviceDiscoveryReply::finished, reply, &NetworkDeviceDiscoveryReply::deleteLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do some cache housekeeping if required
|
// FIXME
|
||||||
if (m_lastCacheHousekeeping.addDays(1) < QDateTime::currentDateTime()) {
|
// // Do some cache housekeeping if required
|
||||||
qCInfo(dcNetworkDeviceDiscovery()) << "Starting cache housekeeping since it is more than one day since the last clanup...";
|
// if (m_lastCacheHousekeeping.addDays(1) < QDateTime::currentDateTime()) {
|
||||||
QDateTime now = QDateTime::currentDateTime();
|
// qCInfo(dcNetworkDeviceDiscovery()) << "Starting cache housekeeping since it is more than one day since the last clanup...";
|
||||||
foreach (const MacAddress &mac, m_lastSeen.keys()) {
|
// QDateTime currentDateTime = QDateTime::currentDateTime();
|
||||||
// Remove the info from the cache if not seen fo the last 30 days...
|
// foreach (const MacAddress &mac, m_lastSeen.keys()) {
|
||||||
if (m_lastSeen.value(mac).date().addDays(m_cacheCleanupPeriod) < QDateTime::currentDateTime().date()) {
|
// // Remove the info from the cache if not seen fo the last 30 days...
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Removing network device cache entry since it did not show up within the last" << m_cacheCleanupPeriod << "days" << mac.toString();
|
// if (m_lastSeen.value(mac).date().addDays(m_cacheCleanupPeriod) < QDateTime::currentDateTime().date()) {
|
||||||
removeFromNetworkDeviceCache(mac);
|
// qCDebug(dcNetworkDeviceDiscovery()) << "Removing network device cache entry since it did not show up within the last" << m_cacheCleanupPeriod << "days" << mac.toString();
|
||||||
}
|
// removeFromNetworkDeviceCache(mac);
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
m_lastCacheHousekeeping = now;
|
// m_lastCacheHousekeeping = currentDateTime;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryImpl::finishDiscovery()
|
void NetworkDeviceDiscoveryImpl::finishDiscovery()
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2022, nymea GmbH
|
* Copyright 2013 - 2024, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
@ -31,10 +31,11 @@
|
|||||||
#ifndef NETWORKDEVICEDISCOVERYIMPL_H
|
#ifndef NETWORKDEVICEDISCOVERYIMPL_H
|
||||||
#define NETWORKDEVICEDISCOVERYIMPL_H
|
#define NETWORKDEVICEDISCOVERYIMPL_H
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSettings>
|
#include <QSettings>
|
||||||
#include <QLoggingCategory>
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
#include <network/networkdeviceinfo.h>
|
#include <network/networkdeviceinfo.h>
|
||||||
#include <network/networkdevicediscovery.h>
|
#include <network/networkdevicediscovery.h>
|
||||||
@ -66,12 +67,11 @@ public:
|
|||||||
|
|
||||||
NetworkDeviceDiscoveryReply *discover() override;
|
NetworkDeviceDiscoveryReply *discover() override;
|
||||||
|
|
||||||
NetworkDeviceMonitor *registerMonitor(const MacAddress &macAddress) override;
|
NetworkDeviceMonitor *registerMonitor(Thing *thing) override;
|
||||||
|
|
||||||
void unregisterMonitor(const MacAddress &macAddress) override;
|
|
||||||
void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) override;
|
void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) override;
|
||||||
|
|
||||||
PingReply *ping(const QHostAddress &address, uint retries = 3) override;
|
PingReply *ping(const QHostAddress &address, uint retries = 3) override;
|
||||||
|
PingReply *ping(const QString &hostName, uint retries = 3) override;
|
||||||
PingReply *ping(const QHostAddress &address, bool lookupHost, uint retries = 3);
|
PingReply *ping(const QHostAddress &address, bool lookupHost, uint retries = 3);
|
||||||
|
|
||||||
MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) override;
|
MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) override;
|
||||||
@ -79,7 +79,7 @@ public:
|
|||||||
|
|
||||||
bool sendArpRequest(const QHostAddress &address) override;
|
bool sendArpRequest(const QHostAddress &address) override;
|
||||||
|
|
||||||
QHash<MacAddress, NetworkDeviceInfo> cache() const override;
|
NetworkDeviceInfos cache() const override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void setEnabled(bool enabled) override;
|
void setEnabled(bool enabled) override;
|
||||||
@ -94,6 +94,7 @@ private:
|
|||||||
MacAddressDatabase *m_macAddressDatabase = nullptr;
|
MacAddressDatabase *m_macAddressDatabase = nullptr;
|
||||||
ArpSocket *m_arpSocket = nullptr;
|
ArpSocket *m_arpSocket = nullptr;
|
||||||
Ping *m_ping = nullptr;
|
Ping *m_ping = nullptr;
|
||||||
|
|
||||||
bool m_enabled = true;
|
bool m_enabled = true;
|
||||||
bool m_running = false;
|
bool m_running = false;
|
||||||
|
|
||||||
@ -112,12 +113,15 @@ private:
|
|||||||
QList<MacAddressDatabaseReply *> m_runningMacDatabaseReplies;
|
QList<MacAddressDatabaseReply *> m_runningMacDatabaseReplies;
|
||||||
QList<PingReply *> m_runningPingReplies;
|
QList<PingReply *> m_runningPingReplies;
|
||||||
|
|
||||||
QHash<MacAddress, NetworkDeviceMonitorImpl *> m_monitors;
|
QHash<NetworkDeviceMonitorImpl *, QVector<NetworkDeviceMonitorImpl *>> m_monitors;
|
||||||
QHash<MacAddress, int> m_monitorsReferenceCount;
|
QHash<QHostAddress, QDateTime> m_lastSeen;
|
||||||
QHash<MacAddress, QDateTime> m_lastSeen;
|
|
||||||
|
QHash<MacAddress, QString> m_macVendorCache;
|
||||||
|
|
||||||
|
QHash<QHostAddress, NetworkDeviceInfo> m_infos;
|
||||||
|
|
||||||
QSettings *m_cacheSettings;
|
QSettings *m_cacheSettings;
|
||||||
QHash<MacAddress, NetworkDeviceInfo> m_networkInfoCache;
|
NetworkDeviceInfos m_networkInfoCache;
|
||||||
|
|
||||||
void pingAllNetworkDevices();
|
void pingAllNetworkDevices();
|
||||||
|
|
||||||
@ -127,6 +131,7 @@ private:
|
|||||||
|
|
||||||
void loadNetworkDeviceCache();
|
void loadNetworkDeviceCache();
|
||||||
void removeFromNetworkDeviceCache(const MacAddress &macAddress);
|
void removeFromNetworkDeviceCache(const MacAddress &macAddress);
|
||||||
|
void removeFromNetworkDeviceCache(const QHostAddress &address);
|
||||||
void saveNetworkDeviceCache(const NetworkDeviceInfo &deviceInfo);
|
void saveNetworkDeviceCache(const NetworkDeviceInfo &deviceInfo);
|
||||||
|
|
||||||
void updateCache(const NetworkDeviceInfo &deviceInfo);
|
void updateCache(const NetworkDeviceInfo &deviceInfo);
|
||||||
@ -134,10 +139,15 @@ private:
|
|||||||
|
|
||||||
void processArpTraffic(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
|
void processArpTraffic(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
|
||||||
|
|
||||||
|
void testPingMonitor(NetworkDeviceMonitorImpl *monitor);
|
||||||
|
|
||||||
// Time helpers
|
// Time helpers
|
||||||
bool longerAgoThan(const QDateTime &dateTime, uint minutes);
|
bool longerAgoThan(const QDateTime &dateTime, uint minutes);
|
||||||
QDateTime convertMinuteBased(const QDateTime &dateTime = QDateTime());
|
QDateTime convertMinuteBased(const QDateTime &dateTime = QDateTime());
|
||||||
|
|
||||||
|
NetworkDeviceMonitorImpl *createPluginMonitor(NetworkDeviceMonitorImpl *internalMonitor);
|
||||||
|
void cleanupPluginMonitor(NetworkDeviceMonitorImpl *pluginMonitor);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onArpResponseReceived(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
|
void onArpResponseReceived(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
|
||||||
void onArpRequstReceived(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
|
void onArpRequstReceived(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress);
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
#include "networkdevicediscoveryreplyimpl.h"
|
#include "networkdevicediscoveryreplyimpl.h"
|
||||||
|
#include "network/networkutils.h"
|
||||||
#include "loggingcategories.h"
|
#include "loggingcategories.h"
|
||||||
|
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
@ -48,11 +49,6 @@ NetworkDeviceInfos NetworkDeviceDiscoveryReplyImpl::networkDeviceInfos() const
|
|||||||
return m_networkDeviceInfos;
|
return m_networkDeviceInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkDeviceInfos NetworkDeviceDiscoveryReplyImpl::virtualNetworkDeviceInfos() const
|
|
||||||
{
|
|
||||||
return m_virtualNetworkDeviceInfos;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NetworkDeviceDiscoveryReplyImpl::isFinished() const
|
bool NetworkDeviceDiscoveryReplyImpl::isFinished() const
|
||||||
{
|
{
|
||||||
return m_isFinished;
|
return m_isFinished;
|
||||||
@ -65,49 +61,34 @@ void NetworkDeviceDiscoveryReplyImpl::setFinished(bool finished)
|
|||||||
|
|
||||||
void NetworkDeviceDiscoveryReplyImpl::processPingResponse(const QHostAddress &address, const QString &hostName)
|
void NetworkDeviceDiscoveryReplyImpl::processPingResponse(const QHostAddress &address, const QString &hostName)
|
||||||
{
|
{
|
||||||
foreach (const NetworkDeviceInfo &info, m_networkDeviceCache) {
|
if (m_networkDeviceCache.contains(address)) {
|
||||||
if (info.address() == address) {
|
m_networkDeviceCache[address].setHostName(hostName);
|
||||||
// Already found info, set host name and check if complete
|
} else {
|
||||||
MacAddress macAddress(info.macAddress());
|
NetworkDeviceInfo info;
|
||||||
m_networkDeviceCache[macAddress].setHostName(hostName);
|
info.setAddress(address);
|
||||||
verifyComplete(macAddress);
|
info.setHostName(hostName);
|
||||||
return;
|
m_networkDeviceCache.insert(address, info);
|
||||||
}
|
|
||||||
|
// First time seeing this host address
|
||||||
|
emit hostAddressDiscovered(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unknown and we have no mac address yet, add it to the ping cache
|
|
||||||
NetworkDeviceInfo info;
|
|
||||||
info.setAddress(address);
|
|
||||||
info.setHostName(hostName);
|
|
||||||
m_pingCache.insert(address, info);
|
|
||||||
// First time seeing this host address
|
|
||||||
emit hostAddressDiscovered(address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryReplyImpl::processArpResponse(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
|
void NetworkDeviceDiscoveryReplyImpl::processArpResponse(const QNetworkInterface &interface, const QHostAddress &address, const MacAddress &macAddress)
|
||||||
{
|
{
|
||||||
if (m_pingCache.contains(address)) {
|
if (m_networkDeviceCache.contains(address)) {
|
||||||
// We know this device from a ping response
|
m_networkDeviceCache[address].addMacAddress(macAddress);
|
||||||
NetworkDeviceInfo info = m_pingCache.take(address);
|
m_networkDeviceCache[address].setNetworkInterface(interface);
|
||||||
|
} else {
|
||||||
|
NetworkDeviceInfo info(macAddress.toString());
|
||||||
info.setAddress(address);
|
info.setAddress(address);
|
||||||
info.setNetworkInterface(interface);
|
info.setNetworkInterface(interface);
|
||||||
info.setMacAddress(macAddress.toString());
|
m_networkDeviceCache[address] = info;
|
||||||
m_networkDeviceCache[macAddress] = info;
|
|
||||||
} else {
|
|
||||||
if (m_networkDeviceCache.contains(macAddress)) {
|
|
||||||
m_networkDeviceCache[macAddress].setAddress(address);
|
|
||||||
m_networkDeviceCache[macAddress].setNetworkInterface(interface);
|
|
||||||
} else {
|
|
||||||
NetworkDeviceInfo info(macAddress.toString());
|
|
||||||
info.setAddress(address);
|
|
||||||
info.setNetworkInterface(interface);
|
|
||||||
m_networkDeviceCache[macAddress] = info;
|
|
||||||
// First time seeing this host address
|
|
||||||
emit hostAddressDiscovered(address);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
verifyComplete(macAddress);
|
// First time seeing this host address
|
||||||
|
emit hostAddressDiscovered(address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryReplyImpl::processMacManufacturer(const MacAddress &macAddress, const QString &manufacturer)
|
void NetworkDeviceDiscoveryReplyImpl::processMacManufacturer(const MacAddress &macAddress, const QString &manufacturer)
|
||||||
@ -115,45 +96,37 @@ void NetworkDeviceDiscoveryReplyImpl::processMacManufacturer(const MacAddress &m
|
|||||||
if (macAddress.isNull())
|
if (macAddress.isNull())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (m_networkDeviceCache.contains(macAddress)) {
|
foreach (const NetworkDeviceInfo &info, m_networkDeviceCache) {
|
||||||
m_networkDeviceCache[macAddress].setMacAddressManufacturer(manufacturer);
|
if (info.macAddressInfos().hasMacAddress(macAddress)) {
|
||||||
} else {
|
m_networkDeviceCache[info.address()].addMacAddress(macAddress, manufacturer);
|
||||||
NetworkDeviceInfo info(macAddress.toString());
|
}
|
||||||
info.setMacAddressManufacturer(manufacturer);
|
|
||||||
m_networkDeviceCache[macAddress] = info;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyComplete(macAddress);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryReplyImpl::processDiscoveryFinished()
|
void NetworkDeviceDiscoveryReplyImpl::processDiscoveryFinished()
|
||||||
{
|
{
|
||||||
// Lets see if we have any incomplete infos but enougth data to be shown
|
// Add the discovery cache to the final result
|
||||||
foreach (const MacAddress &macAddress, m_networkDeviceCache.keys()) {
|
foreach (const QHostAddress &address, m_networkDeviceCache.keys()) {
|
||||||
// If already in the result, ignore it
|
|
||||||
if (m_networkDeviceInfos.hasMacAddress(macAddress))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
NetworkDeviceInfo info = m_networkDeviceCache.value(macAddress);
|
if (m_networkDeviceCache.value(address).macAddressInfos().isEmpty() && !m_networkDeviceCache.value(address).networkInterface().isValid()) {
|
||||||
MacAddress infoMacAddress(info.macAddress());
|
// Set the network interface for the virtual hosts like VPN where we are not receiving any ARP information
|
||||||
|
m_networkDeviceCache[address].setNetworkInterface(NetworkUtils::getInterfaceForHostaddress(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkDeviceInfo info = m_networkDeviceCache.value(address);
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "--> " << info
|
qCDebug(dcNetworkDeviceDiscovery()) << "--> " << info
|
||||||
<< "Valid:" << info.isValid()
|
<< "Valid:" << info.isValid()
|
||||||
<< "Complete:" << info.isComplete()
|
<< "Complete:" << info.isComplete()
|
||||||
<< info.incompleteProperties();
|
<< info.incompleteProperties();
|
||||||
|
|
||||||
// We need at least a valid mac address and a valid ip address, the rest ist pure informative
|
qCDebug(dcNetworkDeviceDiscovery()) << "Adding incomplete" << info << "to the final result:" << info.incompleteProperties();
|
||||||
if (infoMacAddress == macAddress && !infoMacAddress.isNull() && !info.address().isNull()) {
|
m_networkDeviceCache[address].forceComplete();
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Adding incomplete" << info << "to the final result:" << info.incompleteProperties();
|
m_networkDeviceInfos.append(m_networkDeviceCache.take(address));
|
||||||
// Note: makeing it complete
|
|
||||||
m_networkDeviceCache[macAddress].setAddress(info.address());
|
|
||||||
m_networkDeviceCache[macAddress].setHostName(info.hostName());
|
|
||||||
m_networkDeviceCache[macAddress].setMacAddress(info.macAddress());
|
|
||||||
m_networkDeviceCache[macAddress].setMacAddressManufacturer(info.macAddressManufacturer());
|
|
||||||
m_networkDeviceCache[macAddress].setNetworkInterface(info.networkInterface());
|
|
||||||
verifyComplete(macAddress);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Evaluate overall monitor mode...
|
||||||
|
evaluateMonitorMode();
|
||||||
|
|
||||||
// Done, lets sort the result and inform
|
// Done, lets sort the result and inform
|
||||||
m_networkDeviceInfos.sortNetworkDevices();
|
m_networkDeviceInfos.sortNetworkDevices();
|
||||||
|
|
||||||
@ -165,88 +138,87 @@ void NetworkDeviceDiscoveryReplyImpl::processDiscoveryFinished()
|
|||||||
qCDebug(dcNetworkDeviceDiscovery()) << "--> " << info;
|
qCDebug(dcNetworkDeviceDiscovery()) << "--> " << info;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create valid infos from the ping cache and offer them in the virtual infos
|
|
||||||
foreach (const NetworkDeviceInfo &info, m_pingCache) {
|
|
||||||
NetworkDeviceInfo finalInfo = info;
|
|
||||||
finalInfo.setAddress(finalInfo.address());
|
|
||||||
finalInfo.setHostName(finalInfo.hostName());
|
|
||||||
finalInfo.setMacAddress(finalInfo.macAddress());
|
|
||||||
finalInfo.setNetworkInterface(finalInfo.networkInterface());
|
|
||||||
finalInfo.setMacAddressManufacturer(finalInfo.macAddressManufacturer());
|
|
||||||
m_virtualNetworkDeviceInfos.append(info);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_virtualNetworkDeviceInfos.sortNetworkDevices();
|
|
||||||
|
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Virtual hosts (" << m_virtualNetworkDeviceInfos.count() << ")";
|
|
||||||
foreach (const NetworkDeviceInfo &info, m_virtualNetworkDeviceInfos) {
|
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "--> " << info;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (const MacAddress &macAddress, m_networkDeviceCache.keys()) {
|
|
||||||
if (m_networkDeviceInfos.hasMacAddress(macAddress))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
NetworkDeviceInfo info = m_networkDeviceCache.value(macAddress);
|
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "Unhandled information:" << info << "Valid:" << info.isValid() << "Complete:" << info.isComplete() << info.incompleteProperties();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_isFinished = true;
|
m_isFinished = true;
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QHash<QHostAddress, NetworkDeviceInfo> NetworkDeviceDiscoveryReplyImpl::currentCache() const
|
||||||
|
{
|
||||||
|
return m_networkDeviceCache;
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryReplyImpl::addCompleteNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo)
|
void NetworkDeviceDiscoveryReplyImpl::addCompleteNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo)
|
||||||
{
|
{
|
||||||
// Note: this method will be called only from the discovery
|
if (!m_networkDeviceInfos.hasHostAddress(networkDeviceInfo.address()))
|
||||||
if (!m_networkDeviceInfos.hasMacAddress(networkDeviceInfo.macAddress())) {
|
|
||||||
m_networkDeviceInfos.append(networkDeviceInfo);
|
m_networkDeviceInfos.append(networkDeviceInfo);
|
||||||
|
|
||||||
emit hostAddressDiscovered(networkDeviceInfo.address());
|
|
||||||
|
|
||||||
emit networkDeviceInfoAdded(networkDeviceInfo);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryReplyImpl::addVirtualNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo)
|
void NetworkDeviceDiscoveryReplyImpl::evaluateMonitorMode()
|
||||||
{
|
{
|
||||||
// Note: this method will be called only from the discovery
|
for (int i = 0; i < m_networkDeviceInfos.size(); i++) {
|
||||||
if (!m_networkDeviceInfos.hasHostAddress(networkDeviceInfo.address())) {
|
|
||||||
m_virtualNetworkDeviceInfos.append(networkDeviceInfo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString NetworkDeviceDiscoveryReplyImpl::macAddressFromHostAddress(const QHostAddress &address)
|
const NetworkDeviceInfo info = m_networkDeviceInfos.at(i);
|
||||||
{
|
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: Evaluating host" << info.address().toString();
|
||||||
foreach (const NetworkDeviceInfo &info, m_networkDeviceCache) {
|
|
||||||
if (info.address() == address) {
|
|
||||||
return info.macAddress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return QString();
|
NetworkDeviceInfo::MonitorMode mode = NetworkDeviceInfo::MonitorModeMac;
|
||||||
}
|
|
||||||
|
|
||||||
bool NetworkDeviceDiscoveryReplyImpl::hasHostAddress(const QHostAddress &address)
|
if (info.macAddressInfos().isEmpty()) {
|
||||||
{
|
|
||||||
return ! macAddressFromHostAddress(address).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkDeviceDiscoveryReplyImpl::verifyComplete(const MacAddress &macAddress)
|
// No MAC address found, no ARP for this host, probably a VPN client
|
||||||
{
|
if (info.hostName().isEmpty()) {
|
||||||
if (!m_networkDeviceCache.contains(macAddress))
|
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: --> No MAC address and no host name, using MonitorModeIp";
|
||||||
return;
|
mode = NetworkDeviceInfo::MonitorModeIp;
|
||||||
|
} else {
|
||||||
if (m_networkDeviceCache[macAddress].isComplete() && m_networkDeviceCache[macAddress].isValid()) {
|
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: --> No MAC address, but we have a host name, suing MonitorModeHostName";
|
||||||
if (m_networkDeviceInfos.hasMacAddress(macAddress)) {
|
mode = NetworkDeviceInfo::MonitorModeHostName;
|
||||||
if (m_networkDeviceInfos.get(macAddress) != m_networkDeviceCache.value(macAddress)) {
|
}
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "One MAC address seem to be reachable using 2 IP addresses, which is OK. Not updating the network device info and keeping the current information.";
|
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "--> Keeping " << m_networkDeviceInfos.get(macAddress);
|
} else if (info.macAddressInfos().size() == 1) {
|
||||||
qCDebug(dcNetworkDeviceDiscovery()) << "--> Ignoring" << m_networkDeviceCache.value(macAddress);
|
|
||||||
|
// Single mac address for this host..
|
||||||
|
MacAddress macAddress = info.macAddressInfos().constFirst().macAddress();
|
||||||
|
bool macAddressIsUnique = true;
|
||||||
|
|
||||||
|
// Check if this mac is unique
|
||||||
|
foreach (const NetworkDeviceInfo &networkDeviceInfo, m_networkDeviceInfos) {
|
||||||
|
|
||||||
|
// Skip our self...
|
||||||
|
if (networkDeviceInfo.address() == info.address())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (networkDeviceInfo.macAddressInfos().hasMacAddress(macAddress)) {
|
||||||
|
macAddressIsUnique = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!macAddressIsUnique) {
|
||||||
|
if (info.hostName().isEmpty()) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: --> the MAC address of" << info.address().toString() << "is not unique in this network and no host name available, usgin MonitorModeIp";
|
||||||
|
mode = NetworkDeviceInfo::MonitorModeIp;
|
||||||
|
} else {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: --> the MAC address of" << info.address().toString() << "is not unique in this network but we have a host name, usgin MonitorModeHostName";
|
||||||
|
mode = NetworkDeviceInfo::MonitorModeHostName;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: --> the MAC address of" << info.address().toString() << "is unique in this network, usgin MonitorModeMac";
|
||||||
|
mode = NetworkDeviceInfo::MonitorModeMac;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (info.macAddressInfos().size() > 1) {
|
||||||
|
|
||||||
|
// Multiple MAC addresses
|
||||||
|
if (info.hostName().isEmpty()) {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: --> multiple MAC addresses and no host name, usgin MonitorModeIp";
|
||||||
|
mode = NetworkDeviceInfo::MonitorModeIp;
|
||||||
|
} else {
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: --> multiple MAC addresses, but we have a host name, usgin MonitorModeHostName";
|
||||||
|
mode = NetworkDeviceInfo::MonitorModeHostName;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
m_networkDeviceInfos.append(m_networkDeviceCache.value(macAddress));
|
|
||||||
emit networkDeviceInfoAdded(m_networkDeviceCache[macAddress]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_networkDeviceInfos[i].setMonitorMode(mode);
|
||||||
|
qCDebug(dcNetworkDeviceDiscovery()) << "MonitorMode: --> Final" << m_networkDeviceInfos.at(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,7 +48,6 @@ public:
|
|||||||
~NetworkDeviceDiscoveryReplyImpl() override = default;
|
~NetworkDeviceDiscoveryReplyImpl() override = default;
|
||||||
|
|
||||||
NetworkDeviceInfos networkDeviceInfos() const override;
|
NetworkDeviceInfos networkDeviceInfos() const override;
|
||||||
NetworkDeviceInfos virtualNetworkDeviceInfos() const override;
|
|
||||||
|
|
||||||
bool isFinished() const override;
|
bool isFinished() const override;
|
||||||
void setFinished(bool finished);
|
void setFinished(bool finished);
|
||||||
@ -60,28 +59,19 @@ public:
|
|||||||
|
|
||||||
void processDiscoveryFinished();
|
void processDiscoveryFinished();
|
||||||
|
|
||||||
|
QHash<QHostAddress, NetworkDeviceInfo> currentCache() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void addCompleteNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo);
|
void addCompleteNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo);
|
||||||
void addVirtualNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NetworkDeviceInfos m_networkDeviceInfos; // Contains only complete and valid infos
|
QHash<QHostAddress, NetworkDeviceInfo> m_networkDeviceCache;
|
||||||
NetworkDeviceInfos m_virtualNetworkDeviceInfos; // Contains ping responses without ARP, like VPN devices
|
|
||||||
|
|
||||||
QHash<MacAddress, NetworkDeviceInfo> m_networkDeviceCache;
|
|
||||||
qint64 m_startTimestamp;
|
qint64 m_startTimestamp;
|
||||||
|
|
||||||
bool m_isFinished = false;
|
bool m_isFinished = false;
|
||||||
|
|
||||||
// Temporary cache for ping responses where the mac is not known yet (like VPN devices)
|
NetworkDeviceInfos m_networkDeviceInfos;
|
||||||
QHash<QHostAddress, NetworkDeviceInfo> m_pingCache;
|
|
||||||
|
|
||||||
QString macAddressFromHostAddress(const QHostAddress &address);
|
|
||||||
bool hasHostAddress(const QHostAddress &address);
|
|
||||||
|
|
||||||
void verifyComplete(const MacAddress &macAddress);
|
|
||||||
|
|
||||||
|
|
||||||
|
void evaluateMonitorMode();
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2022, nymea GmbH
|
* Copyright 2013 - 2024, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
@ -35,9 +35,11 @@ Q_DECLARE_LOGGING_CATEGORY(dcNetworkDeviceDiscovery)
|
|||||||
|
|
||||||
namespace nymeaserver {
|
namespace nymeaserver {
|
||||||
|
|
||||||
NetworkDeviceMonitorImpl::NetworkDeviceMonitorImpl(const MacAddress &macAddress, QObject *parent) :
|
NetworkDeviceMonitorImpl::NetworkDeviceMonitorImpl(const MacAddress &macAddress, const QString &hostName, const QHostAddress &address, QObject *parent) :
|
||||||
NetworkDeviceMonitor(parent),
|
NetworkDeviceMonitor{parent},
|
||||||
m_macAddress(macAddress)
|
m_macAddress{macAddress},
|
||||||
|
m_hostName{hostName},
|
||||||
|
m_address{address}
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -54,6 +56,26 @@ MacAddress NetworkDeviceMonitorImpl::macAddress() const
|
|||||||
return m_macAddress;
|
return m_macAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString NetworkDeviceMonitorImpl::hostName() const
|
||||||
|
{
|
||||||
|
return m_hostName;
|
||||||
|
}
|
||||||
|
|
||||||
|
QHostAddress NetworkDeviceMonitorImpl::address() const
|
||||||
|
{
|
||||||
|
return m_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkDeviceInfo::MonitorMode NetworkDeviceMonitorImpl::monitorMode() const
|
||||||
|
{
|
||||||
|
return m_monitorMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkDeviceMonitorImpl::setMonitorMode(NetworkDeviceInfo::MonitorMode monitorMode)
|
||||||
|
{
|
||||||
|
m_monitorMode = monitorMode;
|
||||||
|
}
|
||||||
|
|
||||||
NetworkDeviceInfo NetworkDeviceMonitorImpl::networkDeviceInfo() const
|
NetworkDeviceInfo NetworkDeviceMonitorImpl::networkDeviceInfo() const
|
||||||
{
|
{
|
||||||
return m_networkDeviceInfo;
|
return m_networkDeviceInfo;
|
||||||
@ -127,4 +149,37 @@ void NetworkDeviceMonitorImpl::setLastConnectionAttempt(const QDateTime &lastCon
|
|||||||
m_lastConnectionAttempt = lastConnectionAttempt;
|
m_lastConnectionAttempt = lastConnectionAttempt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NetworkDeviceMonitorImpl::isMyNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo) const
|
||||||
|
{
|
||||||
|
bool myNetworkDevice = false;
|
||||||
|
switch (m_monitorMode) {
|
||||||
|
case NetworkDeviceInfo::MonitorModeMac:
|
||||||
|
if (!m_macAddress.isNull() && networkDeviceInfo.macAddressInfos().count() == 1 && networkDeviceInfo.macAddressInfos().hasMacAddress(m_macAddress))
|
||||||
|
myNetworkDevice = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case NetworkDeviceInfo::MonitorModeHostName:
|
||||||
|
if (!m_hostName.isEmpty() && networkDeviceInfo.hostName() == m_hostName)
|
||||||
|
myNetworkDevice = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
case NetworkDeviceInfo::MonitorModeIp:
|
||||||
|
if (!m_address.isNull() && networkDeviceInfo.address() == m_address)
|
||||||
|
myNetworkDevice = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return myNetworkDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkDeviceMonitorImpl::operator==(NetworkDeviceMonitorImpl *other) const
|
||||||
|
{
|
||||||
|
return m_macAddress == other->macAddress() && m_hostName == other->hostName() && m_address == other->address();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkDeviceMonitorImpl::operator!=(NetworkDeviceMonitorImpl *other) const
|
||||||
|
{
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2022, nymea GmbH
|
* Copyright 2013 - 2024, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
@ -34,8 +34,8 @@
|
|||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
#include "network/networkdevicemonitor.h"
|
|
||||||
#include "network/pingreply.h"
|
#include "network/pingreply.h"
|
||||||
|
#include "network/networkdevicemonitor.h"
|
||||||
|
|
||||||
namespace nymeaserver {
|
namespace nymeaserver {
|
||||||
|
|
||||||
@ -44,10 +44,16 @@ class NetworkDeviceMonitorImpl : public NetworkDeviceMonitor
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit NetworkDeviceMonitorImpl(const MacAddress &macAddress, QObject *parent = nullptr);
|
explicit NetworkDeviceMonitorImpl(const MacAddress &macAddress, const QString &hostName, const QHostAddress &address, QObject *parent = nullptr);
|
||||||
~NetworkDeviceMonitorImpl() override;
|
~NetworkDeviceMonitorImpl() override;
|
||||||
|
|
||||||
|
// Properties from the thing
|
||||||
MacAddress macAddress() const override;
|
MacAddress macAddress() const override;
|
||||||
|
QString hostName() const override;
|
||||||
|
QHostAddress address() const override;
|
||||||
|
|
||||||
|
NetworkDeviceInfo::MonitorMode monitorMode() const override;
|
||||||
|
void setMonitorMode(NetworkDeviceInfo::MonitorMode monitorMode);
|
||||||
|
|
||||||
NetworkDeviceInfo networkDeviceInfo() const override;
|
NetworkDeviceInfo networkDeviceInfo() const override;
|
||||||
void setNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo);
|
void setNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo);
|
||||||
@ -67,14 +73,25 @@ public:
|
|||||||
QDateTime lastConnectionAttempt() const;
|
QDateTime lastConnectionAttempt() const;
|
||||||
void setLastConnectionAttempt(const QDateTime &lastConnectionAttempt);
|
void setLastConnectionAttempt(const QDateTime &lastConnectionAttempt);
|
||||||
|
|
||||||
|
bool isMyNetworkDeviceInfo(const NetworkDeviceInfo &networkDeviceInfo) const;
|
||||||
|
|
||||||
|
bool operator==(NetworkDeviceMonitorImpl *other) const;
|
||||||
|
bool operator!=(NetworkDeviceMonitorImpl *other) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NetworkDeviceInfo m_networkDeviceInfo;
|
|
||||||
MacAddress m_macAddress;
|
MacAddress m_macAddress;
|
||||||
|
QString m_hostName;
|
||||||
|
QHostAddress m_address;
|
||||||
|
|
||||||
|
NetworkDeviceInfo::MonitorMode m_monitorMode = NetworkDeviceInfo::MonitorModeMac;
|
||||||
|
|
||||||
|
NetworkDeviceInfo m_networkDeviceInfo;
|
||||||
|
|
||||||
bool m_reachable = false;
|
bool m_reachable = false;
|
||||||
QDateTime m_lastSeen;
|
QDateTime m_lastSeen;
|
||||||
QDateTime m_lastConnectionAttempt;
|
QDateTime m_lastConnectionAttempt;
|
||||||
uint m_pingRetries = 5;
|
uint m_pingRetries = 5;
|
||||||
|
|
||||||
PingReply *m_currentPingReply = nullptr;
|
PingReply *m_currentPingReply = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
* Copyright 2013 - 2024, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
@ -202,9 +202,9 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
QJsonObject thingClassObject = thingClassJson.toObject();
|
QJsonObject thingClassObject = thingClassJson.toObject();
|
||||||
/*! Returns a list of all valid JSON properties a ThingClass JSON definition can have. */
|
/*! Returns a list of all valid JSON properties a ThingClass JSON definition can have. */
|
||||||
QStringList thingClassProperties = QStringList() << "id" << "name" << "displayName" << "createMethods" << "setupMethod"
|
QStringList thingClassProperties = QStringList() << "id" << "name" << "displayName" << "createMethods" << "setupMethod"
|
||||||
<< "interfaces" << "providedInterfaces" << "browsable" << "discoveryParamTypes"
|
<< "interfaces" << "providedInterfaces" << "browsable" << "discoveryParamTypes"
|
||||||
<< "paramTypes" << "settingsTypes" << "stateTypes" << "actionTypes" << "eventTypes" << "browserItemActionTypes"
|
<< "paramTypes" << "settingsTypes" << "stateTypes" << "actionTypes" << "eventTypes" << "browserItemActionTypes"
|
||||||
<< "discoveryType";
|
<< "discoveryType";
|
||||||
QStringList mandatoryThingClassProperties = QStringList() << "id" << "name" << "displayName";
|
QStringList mandatoryThingClassProperties = QStringList() << "id" << "name" << "displayName";
|
||||||
|
|
||||||
QPair<QStringList, QStringList> verificationResult = verifyFields(thingClassProperties, mandatoryThingClassProperties, thingClassObject);
|
QPair<QStringList, QStringList> verificationResult = verifyFields(thingClassProperties, mandatoryThingClassProperties, thingClassObject);
|
||||||
@ -703,6 +703,64 @@ void PluginMetadata::parse(const QJsonObject &jsonObject)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (const InterfaceParamType &ifaceParamType, iface.paramTypes()) {
|
||||||
|
if (!thingClass.paramTypes().contains(ifaceParamType.name())) {
|
||||||
|
if (!ifaceParamType.optional()) {
|
||||||
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() +
|
||||||
|
"\" but doesn't implement param \"" + ifaceParamType.name() + "\"");
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ParamType ¶mType = thingClass.paramTypes()[ifaceParamType.name()];
|
||||||
|
if (ifaceParamType.type() != paramType.type()) {
|
||||||
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() +
|
||||||
|
"\" but param \"" + paramType.name() + "\" has not matching type: \"" +
|
||||||
|
QVariant::typeToName(paramType.type()) + "\" != \"" + QVariant::typeToName(ifaceParamType.type()) + "\"");
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
if (ifaceParamType.minValue().isValid() && !ifaceParamType.minValue().isNull()) {
|
||||||
|
if (ifaceParamType.minValue().toString() == "any") {
|
||||||
|
if (paramType.minValue().isNull()) {
|
||||||
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() +
|
||||||
|
"\" but param \"" + paramType.name() + "\" has no minimum value defined.");
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
} else if (ifaceParamType.minValue() != paramType.minValue()) {
|
||||||
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() +
|
||||||
|
"\" but param \"" + paramType.name() + "\" has not matching minimum value: \"" +
|
||||||
|
ifaceParamType.minValue().toString() + "\" != \"" + paramType.minValue().toString() + "\"");
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ifaceParamType.maxValue().isValid() && !ifaceParamType.maxValue().isNull()) {
|
||||||
|
if (ifaceParamType.maxValue().toString() == "any") {
|
||||||
|
if (paramType.maxValue().isNull()) {
|
||||||
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() +
|
||||||
|
"\" but param \"" + paramType.name() + "\" has no maximum value defined.");
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
} else if (ifaceParamType.maxValue() != paramType.maxValue()) {
|
||||||
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() +
|
||||||
|
"\" but param \"" + paramType.name() + "\" has not matching maximum value: \"" +
|
||||||
|
ifaceParamType.maxValue().toString() + "\" != \"" + paramType.minValue().toString() + "\"");
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!ifaceParamType.allowedValues().isEmpty() && ifaceParamType.allowedValues() != paramType.allowedValues()) {
|
||||||
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but param \"" +
|
||||||
|
paramType.name() + "\" has not matching allowed values.");
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
if (ifaceParamType.unit() != Types::UnitNone && ifaceParamType.unit() != paramType.unit()) {
|
||||||
|
QMetaEnum unitEnum = QMetaEnum::fromType<Types::Unit>();
|
||||||
|
m_validationErrors.append("Thing class \"" + thingClass.name() + "\" claims to implement interface \"" + value.toString() + "\" but param \"" +
|
||||||
|
paramType.name() + "\" has not matching unit: \"" + unitEnum.valueToKey(ifaceParamType.unit()) + "\" != \"" + unitEnum.valueToKey(paramType.unit()));
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
foreach (const InterfaceStateType &ifaceStateType, iface.stateTypes()) {
|
foreach (const InterfaceStateType &ifaceStateType, iface.stateTypes()) {
|
||||||
if (!stateTypes.contains(ifaceStateType.name())) {
|
if (!stateTypes.contains(ifaceStateType.name())) {
|
||||||
if (!ifaceStateType.optional()) {
|
if (!ifaceStateType.optional()) {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
* Copyright 2013 - 2024, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
|
|||||||
@ -62,7 +62,7 @@ public slots:
|
|||||||
void addThingDescriptor(const ThingDescriptor &thingDescriptor);
|
void addThingDescriptor(const ThingDescriptor &thingDescriptor);
|
||||||
void addThingDescriptors(const ThingDescriptors &thingDescriptors);
|
void addThingDescriptors(const ThingDescriptors &thingDescriptors);
|
||||||
|
|
||||||
void finish(Thing::ThingError status, const QString &displayMessage = QString());
|
void finish(Thing::ThingError status, const QString &displayMessage = QString());
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void finished();
|
void finished();
|
||||||
|
|||||||
@ -189,9 +189,39 @@ Interface ThingUtils::loadInterface(const QString &name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InterfaceParamTypes paramTypes;
|
||||||
InterfaceStateTypes stateTypes;
|
InterfaceStateTypes stateTypes;
|
||||||
InterfaceActionTypes actionTypes;
|
InterfaceActionTypes actionTypes;
|
||||||
InterfaceEventTypes eventTypes;
|
InterfaceEventTypes eventTypes;
|
||||||
|
|
||||||
|
foreach (const QVariant ¶mVariant, content.value("params").toList()) {
|
||||||
|
QVariantMap paramMap = paramVariant.toMap();
|
||||||
|
|
||||||
|
InterfaceParamType paramType;
|
||||||
|
paramType.setName(paramMap.value("name").toString());
|
||||||
|
paramType.setType(QVariant::nameToType(paramMap.value("type").toByteArray()));
|
||||||
|
paramType.setMinValue(paramMap.value("minValue"));
|
||||||
|
paramType.setMaxValue(paramMap.value("maxValue"));
|
||||||
|
paramType.setDefaultValue(paramMap.value("defaultValue"));
|
||||||
|
paramType.setAllowedValues(paramMap.value("allowedValues").toList());
|
||||||
|
paramType.setReadOnly(paramMap.value("readOnly").toBool());
|
||||||
|
//paramType.setOptional(paramMap.value("optional", false).toBool());
|
||||||
|
|
||||||
|
if (paramMap.contains("unit")) {
|
||||||
|
QMetaEnum unitEnum = QMetaEnum::fromType<Types::Unit>();
|
||||||
|
int enumValue = unitEnum.keyToValue("Unit" + paramMap.value("unit").toByteArray());
|
||||||
|
if (enumValue == -1) {
|
||||||
|
qCWarning(dcThingManager) << "Invalid unit" << paramMap.value("unit").toString() << "in interface" << name;
|
||||||
|
} else {
|
||||||
|
paramType.setUnit(static_cast<Types::Unit>(enumValue));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
paramType.setUnit(Types::UnitNone);
|
||||||
|
}
|
||||||
|
|
||||||
|
paramTypes.append(paramType);
|
||||||
|
}
|
||||||
|
|
||||||
foreach (const QVariant &stateVariant, content.value("states").toList()) {
|
foreach (const QVariant &stateVariant, content.value("states").toList()) {
|
||||||
QVariantMap stateMap = stateVariant.toMap();
|
QVariantMap stateMap = stateVariant.toMap();
|
||||||
InterfaceStateType stateType;
|
InterfaceStateType stateType;
|
||||||
@ -279,11 +309,17 @@ Interface ThingUtils::loadInterface(const QString &name)
|
|||||||
eventTypes.append(eventType);
|
eventTypes.append(eventType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Interface(name, iface.actionTypes() << actionTypes, iface.eventTypes() << eventTypes, iface.stateTypes() << stateTypes);
|
return Interface(name, iface.paramTypes() << paramTypes, iface.actionTypes() << actionTypes, iface.eventTypes() << eventTypes, iface.stateTypes() << stateTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
Interface ThingUtils::mergeInterfaces(const Interface &iface1, const Interface &iface2)
|
Interface ThingUtils::mergeInterfaces(const Interface &iface1, const Interface &iface2)
|
||||||
{
|
{
|
||||||
|
InterfaceParamTypes paramTypes = iface1.paramTypes();
|
||||||
|
foreach (const InterfaceParamType &pt, iface2.paramTypes()) {
|
||||||
|
if (paramTypes.findByName(pt.name()).name().isEmpty()) {
|
||||||
|
paramTypes.append(pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
InterfaceEventTypes eventTypes = iface1.eventTypes();
|
InterfaceEventTypes eventTypes = iface1.eventTypes();
|
||||||
foreach (const InterfaceEventType &et, iface2.eventTypes()) {
|
foreach (const InterfaceEventType &et, iface2.eventTypes()) {
|
||||||
if (eventTypes.findByName(et.name()).name().isEmpty()) {
|
if (eventTypes.findByName(et.name()).name().isEmpty()) {
|
||||||
@ -302,7 +338,7 @@ Interface ThingUtils::mergeInterfaces(const Interface &iface1, const Interface &
|
|||||||
actionTypes.append(at);
|
actionTypes.append(at);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Interface(QString(), actionTypes, eventTypes, stateTypes);
|
return Interface(QString(), paramTypes, actionTypes, eventTypes, stateTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList ThingUtils::generateInterfaceParentList(const QString &interface)
|
QStringList ThingUtils::generateInterfaceParentList(const QString &interface)
|
||||||
|
|||||||
@ -100,5 +100,6 @@
|
|||||||
<file>alarm.json</file>
|
<file>alarm.json</file>
|
||||||
<file>firesensor.json</file>
|
<file>firesensor.json</file>
|
||||||
<file>childlock.json</file>
|
<file>childlock.json</file>
|
||||||
|
<file>networkdevice.json</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|||||||
17
libnymea/interfaces/networkdevice.json
Normal file
17
libnymea/interfaces/networkdevice.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"description": "Things implementing this interface make use of the network device discovery resource and the network device monitor. This interface makes sure all relevant information will be stored. Depending on the MonitorMode of the discovered NetworkDeviceInfo the params of this thing shall be filled in. This makes sure the monitor uses the correct method to monitor the network device presence and IP. If the Mode is MAC, just sotre the mac address param and leave the others empty. If the Mode is HostName, just fill in the host name parameter. If the Mode is IP, just the address.",
|
||||||
|
"params": [
|
||||||
|
{
|
||||||
|
"name": "address",
|
||||||
|
"type": "QString"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hostName",
|
||||||
|
"type": "QString"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "macAddress",
|
||||||
|
"type": "QString"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -57,6 +57,8 @@ HEADERS += \
|
|||||||
network/arpsocket.h \
|
network/arpsocket.h \
|
||||||
network/macaddress.h \
|
network/macaddress.h \
|
||||||
network/macaddressdatabasereply.h \
|
network/macaddressdatabasereply.h \
|
||||||
|
network/macaddressinfo.h \
|
||||||
|
network/macaddressinfos.h \
|
||||||
network/networkdevicediscovery.h \
|
network/networkdevicediscovery.h \
|
||||||
network/networkdevicediscoveryreply.h \
|
network/networkdevicediscoveryreply.h \
|
||||||
network/networkdeviceinfo.h \
|
network/networkdeviceinfo.h \
|
||||||
@ -72,6 +74,7 @@ HEADERS += \
|
|||||||
types/browseraction.h \
|
types/browseraction.h \
|
||||||
types/interfaceactiontype.h \
|
types/interfaceactiontype.h \
|
||||||
types/interfaceeventtype.h \
|
types/interfaceeventtype.h \
|
||||||
|
types/interfaceparamtype.h \
|
||||||
types/interfacestatetype.h \
|
types/interfacestatetype.h \
|
||||||
types/mediabrowseritem.h \
|
types/mediabrowseritem.h \
|
||||||
types/thingclass.h \
|
types/thingclass.h \
|
||||||
@ -174,6 +177,8 @@ SOURCES += \
|
|||||||
network/arpsocket.cpp \
|
network/arpsocket.cpp \
|
||||||
network/macaddress.cpp \
|
network/macaddress.cpp \
|
||||||
network/macaddressdatabasereply.cpp \
|
network/macaddressdatabasereply.cpp \
|
||||||
|
network/macaddressinfo.cpp \
|
||||||
|
network/macaddressinfos.cpp \
|
||||||
network/networkdevicediscovery.cpp \
|
network/networkdevicediscovery.cpp \
|
||||||
network/networkdeviceinfo.cpp \
|
network/networkdeviceinfo.cpp \
|
||||||
network/networkdeviceinfos.cpp \
|
network/networkdeviceinfos.cpp \
|
||||||
@ -214,6 +219,7 @@ SOURCES += \
|
|||||||
types/browseraction.cpp \
|
types/browseraction.cpp \
|
||||||
types/interfaceactiontype.cpp \
|
types/interfaceactiontype.cpp \
|
||||||
types/interfaceeventtype.cpp \
|
types/interfaceeventtype.cpp \
|
||||||
|
types/interfaceparamtype.cpp \
|
||||||
types/interfacestatetype.cpp \
|
types/interfacestatetype.cpp \
|
||||||
types/mediabrowseritem.cpp \
|
types/mediabrowseritem.cpp \
|
||||||
types/action.cpp \
|
types/action.cpp \
|
||||||
|
|||||||
103
libnymea/network/macaddressinfo.cpp
Normal file
103
libnymea/network/macaddressinfo.cpp
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 "macaddressinfo.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
MacAddressInfo::MacAddressInfo()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MacAddressInfo::MacAddressInfo(const MacAddress &macAddress)
|
||||||
|
: m_macAddress{macAddress}
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MacAddressInfo::MacAddressInfo(const MacAddress &macAddress, const QString &vendorName)
|
||||||
|
: m_macAddress{macAddress},
|
||||||
|
m_vendorName{vendorName},
|
||||||
|
m_vendorNameSet{true}
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MacAddress MacAddressInfo::macAddress() const
|
||||||
|
{
|
||||||
|
return m_macAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString MacAddressInfo::vendorName() const
|
||||||
|
{
|
||||||
|
return m_vendorName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacAddressInfo::setVendorName(const QString &vendorName)
|
||||||
|
{
|
||||||
|
m_vendorName = vendorName;
|
||||||
|
m_vendorNameSet = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacAddressInfo::isValid() const
|
||||||
|
{
|
||||||
|
return !m_macAddress.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacAddressInfo::isComplete() const
|
||||||
|
{
|
||||||
|
return isValid() && m_vendorNameSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacAddressInfo::operator==(const MacAddressInfo &other) const
|
||||||
|
{
|
||||||
|
return m_macAddress == other.macAddress() &&
|
||||||
|
m_vendorName == other.vendorName() &&
|
||||||
|
isComplete() == other.isComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacAddressInfo::operator!=(const MacAddressInfo &other) const
|
||||||
|
{
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, const MacAddressInfo &addressInfo)
|
||||||
|
{
|
||||||
|
QDebugStateSaver saver(debug);
|
||||||
|
debug.nospace() << addressInfo.macAddress().toString() << " (";
|
||||||
|
if (addressInfo.vendorName().isEmpty()) {
|
||||||
|
debug.nospace() << "unknown";
|
||||||
|
} else {
|
||||||
|
debug.nospace() << addressInfo.vendorName();
|
||||||
|
}
|
||||||
|
debug.nospace() << ")";
|
||||||
|
return debug;
|
||||||
|
}
|
||||||
64
libnymea/network/macaddressinfo.h
Normal file
64
libnymea/network/macaddressinfo.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 MACADDRESSINFO_H
|
||||||
|
#define MACADDRESSINFO_H
|
||||||
|
|
||||||
|
#include "macaddress.h"
|
||||||
|
|
||||||
|
class MacAddressInfo
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MacAddressInfo();
|
||||||
|
explicit MacAddressInfo(const MacAddress &macAddress);
|
||||||
|
explicit MacAddressInfo(const MacAddress &macAddress, const QString &vendorName);
|
||||||
|
|
||||||
|
MacAddress macAddress() const;
|
||||||
|
|
||||||
|
QString vendorName() const;
|
||||||
|
void setVendorName(const QString &vendorName);
|
||||||
|
|
||||||
|
bool isValid() const;
|
||||||
|
bool isComplete() const;
|
||||||
|
|
||||||
|
bool operator==(const MacAddressInfo &other) const;
|
||||||
|
bool operator!=(const MacAddressInfo &other) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
MacAddress m_macAddress;
|
||||||
|
QString m_vendorName;
|
||||||
|
|
||||||
|
bool m_vendorNameSet = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug debug, const MacAddressInfo &addressInfo);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // MACADDRESSINFO_H
|
||||||
118
libnymea/network/macaddressinfos.cpp
Normal file
118
libnymea/network/macaddressinfos.cpp
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 "macaddressinfos.h"
|
||||||
|
|
||||||
|
MacAddressInfos::MacAddressInfos()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
MacAddressInfos::MacAddressInfos(const QVector<MacAddressInfo> &other)
|
||||||
|
: QVector<MacAddressInfo>(other)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int MacAddressInfos::indexFromMacAddress(const QString &macAddress)
|
||||||
|
{
|
||||||
|
return indexFromMacAddress(MacAddress(macAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
int MacAddressInfos::indexFromMacAddress(const MacAddress &macAddress)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size(); i++) {
|
||||||
|
if (at(i).macAddress() == macAddress) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacAddressInfos::hasMacAddress(const QString &macAddress)
|
||||||
|
{
|
||||||
|
return indexFromMacAddress(macAddress) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacAddressInfos::hasMacAddress(const MacAddress &macAddress)
|
||||||
|
{
|
||||||
|
return indexFromMacAddress(macAddress) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
MacAddressInfo MacAddressInfos::get(const QString &macAddress) const
|
||||||
|
{
|
||||||
|
return get(MacAddress(macAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
MacAddressInfo MacAddressInfos::get(const MacAddress &macAddress) const
|
||||||
|
{
|
||||||
|
foreach (const MacAddressInfo &info, *this) {
|
||||||
|
if (info.macAddress() == macAddress) {
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MacAddressInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacAddressInfos::removeMacAddress(const QString &macAddress)
|
||||||
|
{
|
||||||
|
removeMacAddress(MacAddress(macAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacAddressInfos::removeMacAddress(const MacAddress &macAddress)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < size(); i++) {
|
||||||
|
if (MacAddress(at(i).macAddress()) == macAddress) {
|
||||||
|
remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MacAddressInfos::sortInfos()
|
||||||
|
{
|
||||||
|
std::sort(this->begin(), this->end(), [](const MacAddressInfo& a, const MacAddressInfo& b) {
|
||||||
|
return a.macAddress().toByteArray() < b.macAddress().toByteArray();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MacAddressInfos::isComplete() const
|
||||||
|
{
|
||||||
|
bool complete = true;
|
||||||
|
foreach (const MacAddressInfo &info, *this) {
|
||||||
|
if (!info.isComplete()) {
|
||||||
|
complete = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return complete;
|
||||||
|
}
|
||||||
60
libnymea/network/macaddressinfos.h
Normal file
60
libnymea/network/macaddressinfos.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 MACADDRESSINFOS_H
|
||||||
|
#define MACADDRESSINFOS_H
|
||||||
|
|
||||||
|
#include <QVector>
|
||||||
|
#include "macaddressinfo.h"
|
||||||
|
|
||||||
|
class MacAddressInfos : public QVector<MacAddressInfo>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MacAddressInfos();
|
||||||
|
MacAddressInfos(const QVector<MacAddressInfo> &other);
|
||||||
|
|
||||||
|
int indexFromMacAddress(const QString &macAddress);
|
||||||
|
int indexFromMacAddress(const MacAddress &macAddress);
|
||||||
|
|
||||||
|
bool hasMacAddress(const QString &macAddress);
|
||||||
|
bool hasMacAddress(const MacAddress &macAddress);
|
||||||
|
|
||||||
|
MacAddressInfo get(const QString &macAddress) const;
|
||||||
|
MacAddressInfo get(const MacAddress &macAddress) const;
|
||||||
|
|
||||||
|
void removeMacAddress(const QString &macAddress);
|
||||||
|
void removeMacAddress(const MacAddress &macAddress);
|
||||||
|
|
||||||
|
void sortInfos();
|
||||||
|
|
||||||
|
bool isComplete() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MACADDRESSINFOS_H
|
||||||
@ -44,6 +44,8 @@
|
|||||||
#include "macaddressdatabasereply.h"
|
#include "macaddressdatabasereply.h"
|
||||||
#include "networkdevicediscoveryreply.h"
|
#include "networkdevicediscoveryreply.h"
|
||||||
|
|
||||||
|
#include "integrations/thing.h"
|
||||||
|
|
||||||
class LIBNYMEA_EXPORT NetworkDeviceDiscovery : public HardwareResource
|
class LIBNYMEA_EXPORT NetworkDeviceDiscovery : public HardwareResource
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@ -55,19 +57,18 @@ public:
|
|||||||
|
|
||||||
virtual bool running() const = 0;
|
virtual bool running() const = 0;
|
||||||
|
|
||||||
virtual NetworkDeviceMonitor *registerMonitor(const MacAddress &macAddress) = 0;
|
virtual NetworkDeviceMonitor *registerMonitor(Thing *thing) = 0;
|
||||||
|
|
||||||
virtual void unregisterMonitor(const MacAddress &macAddress) = 0;
|
|
||||||
virtual void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) = 0;
|
virtual void unregisterMonitor(NetworkDeviceMonitor *networkDeviceMonitor) = 0;
|
||||||
|
|
||||||
virtual PingReply *ping(const QHostAddress &address, uint retries = 3) = 0;
|
virtual PingReply *ping(const QHostAddress &address, uint retries = 3) = 0;
|
||||||
|
virtual PingReply *ping(const QString &hostName, uint retries = 3) = 0;
|
||||||
|
|
||||||
virtual MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) = 0;
|
virtual MacAddressDatabaseReply *lookupMacAddress(const QString &macAddress) = 0;
|
||||||
virtual MacAddressDatabaseReply *lookupMacAddress(const MacAddress &macAddress) = 0;
|
virtual MacAddressDatabaseReply *lookupMacAddress(const MacAddress &macAddress) = 0;
|
||||||
|
|
||||||
virtual bool sendArpRequest(const QHostAddress &address) = 0;
|
virtual bool sendArpRequest(const QHostAddress &address) = 0;
|
||||||
|
|
||||||
virtual QHash<MacAddress, NetworkDeviceInfo> cache() const = 0;
|
virtual NetworkDeviceInfos cache() const = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void runningChanged(bool running);
|
void runningChanged(bool running);
|
||||||
|
|||||||
@ -44,17 +44,11 @@ public:
|
|||||||
virtual ~NetworkDeviceDiscoveryReply() = default;
|
virtual ~NetworkDeviceDiscoveryReply() = default;
|
||||||
|
|
||||||
virtual NetworkDeviceInfos networkDeviceInfos() const = 0;
|
virtual NetworkDeviceInfos networkDeviceInfos() const = 0;
|
||||||
virtual NetworkDeviceInfos virtualNetworkDeviceInfos() const = 0;
|
|
||||||
|
|
||||||
virtual bool isFinished() const = 0;
|
virtual bool isFinished() const = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
// Emitted whenever a certain host address has been pinged successfully
|
|
||||||
void hostAddressDiscovered(const QHostAddress &address);
|
void hostAddressDiscovered(const QHostAddress &address);
|
||||||
|
|
||||||
// Emited whenerver a valid NetworkDeviceInfo has been added
|
|
||||||
void networkDeviceInfoAdded(const NetworkDeviceInfo &networkDeviceInfo);
|
|
||||||
|
|
||||||
void finished();
|
void finished();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2022, nymea GmbH
|
* Copyright 2013 - 2024, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
@ -36,38 +36,16 @@ NetworkDeviceInfo::NetworkDeviceInfo()
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkDeviceInfo::NetworkDeviceInfo(const QString &macAddress):
|
NetworkDeviceInfo::NetworkDeviceInfo(const QString &macAddress)
|
||||||
m_macAddress(macAddress)
|
|
||||||
{
|
{
|
||||||
m_macAddressSet = true;
|
addMacAddress(MacAddress(macAddress));
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkDeviceInfo::NetworkDeviceInfo(const QHostAddress &address):
|
NetworkDeviceInfo::NetworkDeviceInfo(const QHostAddress &address):
|
||||||
m_address(address)
|
m_address{address},
|
||||||
|
m_addressSet{true}
|
||||||
{
|
{
|
||||||
m_addressSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString NetworkDeviceInfo::macAddress() const
|
|
||||||
{
|
|
||||||
return m_macAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkDeviceInfo::setMacAddress(const QString &macAddress)
|
|
||||||
{
|
|
||||||
m_macAddress = macAddress;
|
|
||||||
m_macAddressSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString NetworkDeviceInfo::macAddressManufacturer() const
|
|
||||||
{
|
|
||||||
return m_macAddressManufacturer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkDeviceInfo::setMacAddressManufacturer(const QString &macAddressManufacturer)
|
|
||||||
{
|
|
||||||
m_macAddressManufacturer = macAddressManufacturer;
|
|
||||||
m_macAddressManufacturerSet = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QHostAddress NetworkDeviceInfo::address() const
|
QHostAddress NetworkDeviceInfo::address() const
|
||||||
@ -92,6 +70,33 @@ void NetworkDeviceInfo::setHostName(const QString &hostName)
|
|||||||
m_hostNameSet = true;
|
m_hostNameSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MacAddressInfos NetworkDeviceInfo::macAddressInfos() const
|
||||||
|
{
|
||||||
|
return m_macAddressInfos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkDeviceInfo::addMacAddress(const MacAddress &macAddress)
|
||||||
|
{
|
||||||
|
if (m_macAddressInfos.hasMacAddress(macAddress))
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_macAddressInfos.append(MacAddressInfo(macAddress));
|
||||||
|
// Note: we have to sort them in order to compare MacAddressInfos
|
||||||
|
m_macAddressInfos.sortInfos();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkDeviceInfo::addMacAddress(const MacAddress &macAddress, const QString &vendorName)
|
||||||
|
{
|
||||||
|
int index = m_macAddressInfos.indexFromMacAddress(macAddress);
|
||||||
|
if (index >= 0) {
|
||||||
|
m_macAddressInfos[index].setVendorName(vendorName);
|
||||||
|
} else {
|
||||||
|
m_macAddressInfos.append(MacAddressInfo(macAddress, vendorName));
|
||||||
|
// Note: we have to sort them in order to compare MacAddressInfos
|
||||||
|
m_macAddressInfos.sortInfos();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QNetworkInterface NetworkDeviceInfo::networkInterface() const
|
QNetworkInterface NetworkDeviceInfo::networkInterface() const
|
||||||
{
|
{
|
||||||
return m_networkInterface;
|
return m_networkInterface;
|
||||||
@ -103,34 +108,102 @@ void NetworkDeviceInfo::setNetworkInterface(const QNetworkInterface &networkInte
|
|||||||
m_networkInterfaceSet = true;
|
m_networkInterfaceSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetworkDeviceInfo::MonitorMode NetworkDeviceInfo::monitorMode() const
|
||||||
|
{
|
||||||
|
return m_monitorMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkDeviceInfo::setMonitorMode(MonitorMode monitorMode)
|
||||||
|
{
|
||||||
|
m_monitorMode = monitorMode;
|
||||||
|
}
|
||||||
|
|
||||||
bool NetworkDeviceInfo::isValid() const
|
bool NetworkDeviceInfo::isValid() const
|
||||||
{
|
{
|
||||||
return (!m_address.isNull() || !MacAddress(m_macAddress).isNull()) && m_networkInterface.isValid();
|
return (!m_address.isNull() || m_macAddressInfos.isEmpty()) && m_networkInterface.isValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkDeviceInfo::isComplete() const
|
bool NetworkDeviceInfo::isComplete() const
|
||||||
{
|
{
|
||||||
return m_macAddressSet && m_macAddressManufacturerSet && m_addressSet && m_hostNameSet && m_networkInterfaceSet;
|
if (m_forceComplete)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return !m_macAddressInfos.isEmpty() && m_macAddressInfos.isComplete() && m_addressSet && m_hostNameSet && m_networkInterfaceSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkDeviceInfo::forceComplete()
|
||||||
|
{
|
||||||
|
m_forceComplete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString NetworkDeviceInfo::incompleteProperties() const
|
QString NetworkDeviceInfo::incompleteProperties() const
|
||||||
{
|
{
|
||||||
QStringList list;
|
QStringList list;
|
||||||
if (!m_macAddressSet) list.append("MAC not set");
|
if (m_macAddressInfos.isEmpty())
|
||||||
if (!m_macAddressManufacturerSet) list.append("MAC vendor not set");
|
list.append("MAC address not set");
|
||||||
if (!m_hostNameSet) list.append("hostname not set");
|
|
||||||
if (!m_networkInterfaceSet) list.append("nework interface not set");
|
if (!m_macAddressInfos.isEmpty() && !m_macAddressInfos.isComplete())
|
||||||
|
list.append("MAC infos incomplete");
|
||||||
|
|
||||||
|
if (!m_hostNameSet)
|
||||||
|
list.append("host name not set");
|
||||||
|
|
||||||
|
if (!m_networkInterfaceSet)
|
||||||
|
list.append("nework interface not set");
|
||||||
|
|
||||||
return list.join(", ");
|
return list.join(", ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString NetworkDeviceInfo::thingParamValueMacAddress() const
|
||||||
|
{
|
||||||
|
QString macString;
|
||||||
|
switch (m_monitorMode) {
|
||||||
|
case MonitorModeMac:
|
||||||
|
macString = m_macAddressInfos.constFirst().macAddress().toString();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// In any other case we don't want to store the mac address since we can not relai on it
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return macString;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString NetworkDeviceInfo::thingParamValueHostName() const
|
||||||
|
{
|
||||||
|
QString hostNameString;
|
||||||
|
switch (m_monitorMode) {
|
||||||
|
case MonitorModeMac:
|
||||||
|
case MonitorModeHostName:
|
||||||
|
hostNameString = m_hostName;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return hostNameString;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString NetworkDeviceInfo::thingParamValueAddress() const
|
||||||
|
{
|
||||||
|
QString addressString;
|
||||||
|
switch (m_monitorMode) {
|
||||||
|
case MonitorModeIp:
|
||||||
|
addressString = m_address.toString();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// In any other case we don't want to store the IP address because we want to discover it
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return addressString;
|
||||||
|
}
|
||||||
|
|
||||||
bool NetworkDeviceInfo::operator==(const NetworkDeviceInfo &other) const
|
bool NetworkDeviceInfo::operator==(const NetworkDeviceInfo &other) const
|
||||||
{
|
{
|
||||||
return MacAddress(m_macAddress) == MacAddress(other.macAddress()) &&
|
return m_address == other.address() &&
|
||||||
m_address == other.address() &&
|
m_macAddressInfos == other.macAddressInfos() &&
|
||||||
m_hostName == other.hostName() &&
|
m_hostName == other.hostName() &&
|
||||||
m_macAddressManufacturer == other.macAddressManufacturer() &&
|
m_networkInterface.name() == other.networkInterface().name() &&
|
||||||
m_networkInterface.name() == other.networkInterface().name() &&
|
m_monitorMode == other.monitorMode() &&
|
||||||
isComplete() == other.isComplete();
|
isComplete() == other.isComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkDeviceInfo::operator!=(const NetworkDeviceInfo &other) const
|
bool NetworkDeviceInfo::operator!=(const NetworkDeviceInfo &other) const
|
||||||
@ -141,20 +214,30 @@ bool NetworkDeviceInfo::operator!=(const NetworkDeviceInfo &other) const
|
|||||||
QDebug operator<<(QDebug dbg, const NetworkDeviceInfo &networkDeviceInfo)
|
QDebug operator<<(QDebug dbg, const NetworkDeviceInfo &networkDeviceInfo)
|
||||||
{
|
{
|
||||||
QDebugStateSaver saver(dbg);
|
QDebugStateSaver saver(dbg);
|
||||||
dbg.nospace() << "NetworkDeviceInfo(" << networkDeviceInfo.address().toString();
|
dbg.nospace().noquote() << "NetworkDeviceInfo(" << networkDeviceInfo.address().toString();
|
||||||
|
|
||||||
if (!networkDeviceInfo.macAddress().isEmpty())
|
dbg.nospace().noquote() << ", Monitor mode: ";
|
||||||
dbg.nospace() << ", " << MacAddress(networkDeviceInfo.macAddress()).toString();
|
switch (networkDeviceInfo.monitorMode()) {
|
||||||
|
case NetworkDeviceInfo::MonitorModeMac:
|
||||||
|
dbg.nospace().noquote() << "MAC";
|
||||||
|
break;
|
||||||
|
case NetworkDeviceInfo::MonitorModeHostName:
|
||||||
|
dbg.nospace().noquote() << "hostname";
|
||||||
|
break;
|
||||||
|
case NetworkDeviceInfo::MonitorModeIp:
|
||||||
|
dbg.nospace().noquote() << "IP";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (!networkDeviceInfo.macAddressManufacturer().isEmpty())
|
foreach (const MacAddressInfo &macInfo, networkDeviceInfo.macAddressInfos())
|
||||||
dbg.nospace() << " (" << networkDeviceInfo.macAddressManufacturer() << ") ";
|
dbg.nospace().noquote() << ", " << macInfo;
|
||||||
|
|
||||||
if (!networkDeviceInfo.hostName().isEmpty())
|
if (!networkDeviceInfo.hostName().isEmpty())
|
||||||
dbg.nospace() << ", hostname: " << networkDeviceInfo.hostName();
|
dbg.nospace().noquote() << ", hostname: " << networkDeviceInfo.hostName();
|
||||||
|
|
||||||
if (networkDeviceInfo.networkInterface().isValid())
|
if (networkDeviceInfo.networkInterface().isValid())
|
||||||
dbg.nospace() << ", " << networkDeviceInfo.networkInterface().name();
|
dbg.nospace().noquote() << ", " << networkDeviceInfo.networkInterface().name();
|
||||||
|
|
||||||
dbg.nospace() << ")";
|
dbg.nospace().noquote() << ")";
|
||||||
return dbg;
|
return dbg;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2022, nymea GmbH
|
* Copyright 2013 - 2024, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
@ -38,53 +38,70 @@
|
|||||||
#include <QNetworkInterface>
|
#include <QNetworkInterface>
|
||||||
|
|
||||||
#include "libnymea.h"
|
#include "libnymea.h"
|
||||||
|
#include "macaddressinfos.h"
|
||||||
|
|
||||||
class LIBNYMEA_EXPORT NetworkDeviceInfo
|
class LIBNYMEA_EXPORT NetworkDeviceInfo
|
||||||
{
|
{
|
||||||
|
Q_GADGET
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
enum MonitorMode {
|
||||||
|
MonitorModeMac = 0x01, // Unique MAC address within the network
|
||||||
|
MonitorModeHostName = 0x02, // DNS hostname available, but no MAC address or not unique MAC available
|
||||||
|
MonitorModeIp = 0x03 // Only the IP can be used to monitor, simple ping on reachable
|
||||||
|
};
|
||||||
|
Q_ENUM(MonitorMode)
|
||||||
|
|
||||||
explicit NetworkDeviceInfo();
|
explicit NetworkDeviceInfo();
|
||||||
explicit NetworkDeviceInfo(const QString &macAddress);
|
explicit NetworkDeviceInfo(const QString &macAddress);
|
||||||
explicit NetworkDeviceInfo(const QHostAddress &address);
|
explicit NetworkDeviceInfo(const QHostAddress &address);
|
||||||
|
|
||||||
QString macAddress() const;
|
|
||||||
void setMacAddress(const QString &macAddress);
|
|
||||||
|
|
||||||
QString macAddressManufacturer() const;
|
|
||||||
void setMacAddressManufacturer(const QString &macAddressManufacturer);
|
|
||||||
|
|
||||||
QHostAddress address() const;
|
QHostAddress address() const;
|
||||||
void setAddress(const QHostAddress &address);
|
void setAddress(const QHostAddress &address);
|
||||||
|
|
||||||
QString hostName() const;
|
QString hostName() const;
|
||||||
void setHostName(const QString &hostName);
|
void setHostName(const QString &hostName);
|
||||||
|
|
||||||
|
MacAddressInfos macAddressInfos() const;
|
||||||
|
void addMacAddress(const MacAddress &macAddress);
|
||||||
|
void addMacAddress(const MacAddress &macAddress, const QString &vendorName);
|
||||||
|
|
||||||
QNetworkInterface networkInterface() const;
|
QNetworkInterface networkInterface() const;
|
||||||
void setNetworkInterface(const QNetworkInterface &networkInterface);
|
void setNetworkInterface(const QNetworkInterface &networkInterface);
|
||||||
|
|
||||||
|
MonitorMode monitorMode() const;
|
||||||
|
void setMonitorMode(MonitorMode monitorMode);
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
bool isComplete() const;
|
bool isComplete() const;
|
||||||
|
|
||||||
|
void forceComplete();
|
||||||
|
|
||||||
QString incompleteProperties() const;
|
QString incompleteProperties() const;
|
||||||
|
|
||||||
|
// Helper methods for the networkdevice interface
|
||||||
|
// The fill in automaticlally the correct paramters for the
|
||||||
|
// right monitor
|
||||||
|
QString thingParamValueMacAddress() const;
|
||||||
|
QString thingParamValueHostName() const;
|
||||||
|
QString thingParamValueAddress() const;
|
||||||
|
|
||||||
bool operator==(const NetworkDeviceInfo &other) const;
|
bool operator==(const NetworkDeviceInfo &other) const;
|
||||||
bool operator!=(const NetworkDeviceInfo &other) const;
|
bool operator!=(const NetworkDeviceInfo &other) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHostAddress m_address;
|
QHostAddress m_address;
|
||||||
QString m_macAddress;
|
MacAddressInfos m_macAddressInfos;
|
||||||
QString m_macAddressManufacturer;
|
|
||||||
QString m_hostName;
|
QString m_hostName;
|
||||||
QNetworkInterface m_networkInterface;
|
QNetworkInterface m_networkInterface;
|
||||||
|
MonitorMode m_monitorMode = MonitorModeMac;
|
||||||
|
|
||||||
bool m_macAddressSet = false;
|
|
||||||
bool m_macAddressManufacturerSet = false;
|
|
||||||
bool m_addressSet = false;
|
bool m_addressSet = false;
|
||||||
bool m_hostNameSet = false;
|
bool m_hostNameSet = false;
|
||||||
bool m_networkInterfaceSet = false;
|
bool m_networkInterfaceSet = false;
|
||||||
|
bool m_forceComplete = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, const NetworkDeviceInfo &networkDeviceInfo);
|
QDebug operator<<(QDebug debug, const NetworkDeviceInfo &networkDeviceInfo);
|
||||||
|
|
||||||
|
|
||||||
#endif // NETWORKDEVICEINFO_H
|
#endif // NETWORKDEVICEINFO_H
|
||||||
|
|||||||
@ -56,15 +56,10 @@ int NetworkDeviceInfos::indexFromHostAddress(const QHostAddress &address)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NetworkDeviceInfos::indexFromMacAddress(const QString &macAddress)
|
int NetworkDeviceInfos::indexFromHostName(const QString &hostName)
|
||||||
{
|
{
|
||||||
return indexFromMacAddress(MacAddress(macAddress));
|
for (int i = 0; i < this->size(); i++) {
|
||||||
}
|
if (at(i).hostName() == hostName) {
|
||||||
|
|
||||||
int NetworkDeviceInfos::indexFromMacAddress(const MacAddress &macAddress)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < size(); i++) {
|
|
||||||
if (MacAddress(at(i).macAddress()) == macAddress) {
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -72,6 +67,23 @@ int NetworkDeviceInfos::indexFromMacAddress(const MacAddress &macAddress)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<int> NetworkDeviceInfos::indexFromMacAddress(const QString &macAddress)
|
||||||
|
{
|
||||||
|
return indexFromMacAddress(MacAddress(macAddress));
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<int> NetworkDeviceInfos::indexFromMacAddress(const MacAddress &macAddress)
|
||||||
|
{
|
||||||
|
QList<int> indices;
|
||||||
|
for (int i = 0; i < size(); i++) {
|
||||||
|
if (at(i).macAddressInfos().hasMacAddress(macAddress)) {
|
||||||
|
indices << i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return indices;
|
||||||
|
}
|
||||||
|
|
||||||
bool NetworkDeviceInfos::hasHostAddress(const QHostAddress &address)
|
bool NetworkDeviceInfos::hasHostAddress(const QHostAddress &address)
|
||||||
{
|
{
|
||||||
return indexFromHostAddress(address) >= 0;
|
return indexFromHostAddress(address) >= 0;
|
||||||
@ -79,12 +91,12 @@ bool NetworkDeviceInfos::hasHostAddress(const QHostAddress &address)
|
|||||||
|
|
||||||
bool NetworkDeviceInfos::hasMacAddress(const QString &macAddress)
|
bool NetworkDeviceInfos::hasMacAddress(const QString &macAddress)
|
||||||
{
|
{
|
||||||
return indexFromMacAddress(macAddress) >= 0;
|
return !indexFromMacAddress(macAddress).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkDeviceInfos::hasMacAddress(const MacAddress &macAddress)
|
bool NetworkDeviceInfos::hasMacAddress(const MacAddress &macAddress)
|
||||||
{
|
{
|
||||||
return indexFromMacAddress(macAddress) >= 0;
|
return !indexFromMacAddress(macAddress).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkDeviceInfo NetworkDeviceInfos::get(const QHostAddress &address) const
|
NetworkDeviceInfo NetworkDeviceInfos::get(const QHostAddress &address) const
|
||||||
@ -98,31 +110,10 @@ NetworkDeviceInfo NetworkDeviceInfos::get(const QHostAddress &address) const
|
|||||||
return NetworkDeviceInfo();
|
return NetworkDeviceInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkDeviceInfo NetworkDeviceInfos::get(const QString &macAddress) const
|
void NetworkDeviceInfos::removeHostAddress(const QHostAddress &address)
|
||||||
{
|
|
||||||
foreach (const NetworkDeviceInfo &networkDeviceInfo, *this) {
|
|
||||||
if (networkDeviceInfo.macAddress() == macAddress) {
|
|
||||||
return networkDeviceInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NetworkDeviceInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkDeviceInfo NetworkDeviceInfos::get(const MacAddress &macAddress) const
|
|
||||||
{
|
|
||||||
return get(macAddress.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkDeviceInfos::removeMacAddress(const QString &macAddress)
|
|
||||||
{
|
|
||||||
removeMacAddress(MacAddress(macAddress));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkDeviceInfos::removeMacAddress(const MacAddress &macAddress)
|
|
||||||
{
|
{
|
||||||
for (int i = 0; i < size(); i++) {
|
for (int i = 0; i < size(); i++) {
|
||||||
if (MacAddress(at(i).macAddress()) == macAddress) {
|
if (at(i).address() == address) {
|
||||||
remove(i);
|
remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,25 +39,21 @@
|
|||||||
|
|
||||||
class LIBNYMEA_EXPORT NetworkDeviceInfos : public QVector<NetworkDeviceInfo>
|
class LIBNYMEA_EXPORT NetworkDeviceInfos : public QVector<NetworkDeviceInfo>
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit NetworkDeviceInfos();
|
explicit NetworkDeviceInfos();
|
||||||
NetworkDeviceInfos(const QVector<NetworkDeviceInfo> &other);
|
NetworkDeviceInfos(const QVector<NetworkDeviceInfo> &other);
|
||||||
|
|
||||||
int indexFromHostAddress(const QHostAddress &address);
|
int indexFromHostAddress(const QHostAddress &address);
|
||||||
int indexFromMacAddress(const QString &macAddress);
|
int indexFromHostName(const QString &hostName);
|
||||||
int indexFromMacAddress(const MacAddress &macAddress);
|
QList<int> indexFromMacAddress(const QString &macAddress);
|
||||||
|
QList<int> indexFromMacAddress(const MacAddress &macAddress);
|
||||||
|
|
||||||
bool hasHostAddress(const QHostAddress &address);
|
bool hasHostAddress(const QHostAddress &address);
|
||||||
bool hasMacAddress(const QString &macAddress);
|
bool hasMacAddress(const QString &macAddress);
|
||||||
bool hasMacAddress(const MacAddress &macAddress);
|
bool hasMacAddress(const MacAddress &macAddress);
|
||||||
|
|
||||||
NetworkDeviceInfo get(const QHostAddress &address) const;
|
NetworkDeviceInfo get(const QHostAddress &address) const;
|
||||||
NetworkDeviceInfo get(const QString &macAddress) const;
|
void removeHostAddress(const QHostAddress &address);
|
||||||
NetworkDeviceInfo get(const MacAddress &macAddress) const;
|
|
||||||
|
|
||||||
void removeMacAddress(const QString &macAddress);
|
|
||||||
void removeMacAddress(const MacAddress &macAddress);
|
|
||||||
|
|
||||||
void sortNetworkDevices();
|
void sortNetworkDevices();
|
||||||
|
|
||||||
|
|||||||
@ -40,12 +40,20 @@ NetworkDeviceMonitor::NetworkDeviceMonitor(QObject *parent) :
|
|||||||
QDebug operator<<(QDebug dbg, NetworkDeviceMonitor *networkDeviceMonitor)
|
QDebug operator<<(QDebug dbg, NetworkDeviceMonitor *networkDeviceMonitor)
|
||||||
{
|
{
|
||||||
QDebugStateSaver saver(dbg);
|
QDebugStateSaver saver(dbg);
|
||||||
dbg.nospace() << "NetworkDeviceMonitor(" << networkDeviceMonitor->macAddress().toString();
|
dbg.nospace() << "NetworkDeviceMonitor(";
|
||||||
|
|
||||||
if (!networkDeviceMonitor->networkDeviceInfo().macAddressManufacturer().isEmpty())
|
switch (networkDeviceMonitor->monitorMode()) {
|
||||||
dbg.nospace() << " - " << networkDeviceMonitor->networkDeviceInfo().macAddressManufacturer();
|
case NetworkDeviceInfo::MonitorModeMac:
|
||||||
|
dbg.nospace() << "Mode: MAC, " << networkDeviceMonitor->macAddress().toString();
|
||||||
|
break;
|
||||||
|
case NetworkDeviceInfo::MonitorModeHostName:
|
||||||
|
dbg.nospace() << "Mode: host name, " << networkDeviceMonitor->hostName();
|
||||||
|
break;
|
||||||
|
case NetworkDeviceInfo::MonitorModeIp:
|
||||||
|
dbg.nospace() << "Mode: IP address, " << networkDeviceMonitor->address().toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
dbg.nospace() << ", " << networkDeviceMonitor->networkDeviceInfo().address().toString();
|
|
||||||
dbg.nospace() << ", " << (networkDeviceMonitor->reachable() ? "reachable" : "not reachable");
|
dbg.nospace() << ", " << (networkDeviceMonitor->reachable() ? "reachable" : "not reachable");
|
||||||
dbg.nospace() << ")";
|
dbg.nospace() << ")";
|
||||||
return dbg;
|
return dbg;
|
||||||
|
|||||||
@ -35,7 +35,6 @@
|
|||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
||||||
#include "libnymea.h"
|
#include "libnymea.h"
|
||||||
#include "macaddress.h"
|
|
||||||
#include "networkdeviceinfo.h"
|
#include "networkdeviceinfo.h"
|
||||||
|
|
||||||
class LIBNYMEA_EXPORT NetworkDeviceMonitor : public QObject
|
class LIBNYMEA_EXPORT NetworkDeviceMonitor : public QObject
|
||||||
@ -46,8 +45,14 @@ public:
|
|||||||
explicit NetworkDeviceMonitor(QObject *parent = nullptr);
|
explicit NetworkDeviceMonitor(QObject *parent = nullptr);
|
||||||
virtual ~NetworkDeviceMonitor() = default;
|
virtual ~NetworkDeviceMonitor() = default;
|
||||||
|
|
||||||
|
// Monitor parameters defining the monitor mode
|
||||||
virtual MacAddress macAddress() const = 0;
|
virtual MacAddress macAddress() const = 0;
|
||||||
|
virtual QString hostName() const = 0;
|
||||||
|
virtual QHostAddress address() const = 0;
|
||||||
|
|
||||||
|
virtual NetworkDeviceInfo::MonitorMode monitorMode() const = 0;
|
||||||
|
|
||||||
|
// Actual network device information
|
||||||
virtual NetworkDeviceInfo networkDeviceInfo() const = 0;
|
virtual NetworkDeviceInfo networkDeviceInfo() const = 0;
|
||||||
|
|
||||||
virtual bool reachable() const = 0;
|
virtual bool reachable() const = 0;
|
||||||
@ -60,7 +65,7 @@ signals:
|
|||||||
void reachableChanged(bool reachable);
|
void reachableChanged(bool reachable);
|
||||||
void lastSeenChanged(const QDateTime &lastSeen);
|
void lastSeenChanged(const QDateTime &lastSeen);
|
||||||
void networkDeviceInfoChanged(const NetworkDeviceInfo &networkDeviceInfo);
|
void networkDeviceInfoChanged(const NetworkDeviceInfo &networkDeviceInfo);
|
||||||
|
void pingRetriesChanged(uint pingRetries);
|
||||||
};
|
};
|
||||||
|
|
||||||
QDebug operator<<(QDebug debug, NetworkDeviceMonitor *networkDeviceMonitor);
|
QDebug operator<<(QDebug debug, NetworkDeviceMonitor *networkDeviceMonitor);
|
||||||
|
|||||||
@ -141,6 +141,18 @@ PingReply *Ping::ping(const QHostAddress &hostAddress, bool lookupHost, uint ret
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PingReply *Ping::ping(const QString &hostName, uint retries)
|
||||||
|
{
|
||||||
|
// Make first the host lookup, then the ping the first address and save it in the reply
|
||||||
|
PingReply *reply = createReply(hostName);
|
||||||
|
reply->m_retries = retries;
|
||||||
|
|
||||||
|
// Note: due to a Qt bug < 5.9 we need to use old SLOT style and cannot make use of lambda here
|
||||||
|
int lookupId = QHostInfo::lookupHost(hostName, this, SLOT(onHostLookupFinished(QHostInfo)));
|
||||||
|
m_pendingHostAddressLookups.insert(lookupId, reply);
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
void Ping::sendNextReply()
|
void Ping::sendNextReply()
|
||||||
{
|
{
|
||||||
if (m_queueTimer->isActive())
|
if (m_queueTimer->isActive())
|
||||||
@ -150,7 +162,7 @@ void Ping::sendNextReply()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_currentReply = m_replyQueue.dequeue();
|
m_currentReply = m_replyQueue.dequeue();
|
||||||
qCDebug(dcPing()) << "Send next reply " << m_currentReply->targetHostAddress().toString() << QString("0x%1").arg(m_currentReply->requestId(), 4, 16, QChar('0')) << ", " << m_replyQueue.count() << "left in queue";
|
qCDebug(dcPing()).nospace().noquote() << "Send next reply " << m_currentReply->targetHostAddress().toString() << " ID: " << QString("0x%1").arg(m_currentReply->requestId(), 4, 16, QChar('0')) << ", " << m_replyQueue.count() << "left in queue";
|
||||||
m_queueTimer->start();
|
m_queueTimer->start();
|
||||||
QTimer::singleShot(0, this, [=]() {
|
QTimer::singleShot(0, this, [=]() {
|
||||||
if (!m_currentReply)
|
if (!m_currentReply)
|
||||||
@ -209,8 +221,8 @@ void Ping::performPing(PingReply *reply)
|
|||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(dcPingTraffic()) << "Send ICMP echo request" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]"
|
qCDebug(dcPingTraffic()) << "Send ICMP echo request" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]"
|
||||||
<< "ID:" << QString("0x%1").arg(reply->requestId(), 4, 16, QChar('0'))
|
<< "ID:" << QString("0x%1").arg(reply->requestId(), 4, 16, QChar('0'))
|
||||||
<< "Sequence:" << reply->sequenceNumber();
|
<< "Sequence:" << reply->sequenceNumber();
|
||||||
|
|
||||||
// Send packet to the target ip
|
// Send packet to the target ip
|
||||||
int bytesSent = sendto(m_socketDescriptor, &requestPacket, sizeof(requestPacket), 0, (struct sockaddr *)&pingAddress, sizeof(pingAddress));
|
int bytesSent = sendto(m_socketDescriptor, &requestPacket, sizeof(requestPacket), 0, (struct sockaddr *)&pingAddress, sizeof(pingAddress));
|
||||||
@ -329,14 +341,43 @@ PingReply *Ping::createReply(const QHostAddress &hostAddress)
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PingReply *Ping::createReply(const QString &hostName)
|
||||||
|
{
|
||||||
|
PingReply *reply = new PingReply(this);
|
||||||
|
reply->m_hostName = hostName;
|
||||||
|
|
||||||
|
connect(reply, &PingReply::timeout, this, [=](){
|
||||||
|
// Note: this is not the ICMP timeout, here we actually got nothing from anybody...
|
||||||
|
finishReply(reply, PingReply::ErrorTimeout);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(reply, &PingReply::aborted, this, [=](){
|
||||||
|
finishReply(reply, PingReply::ErrorAborted);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(reply, &PingReply::finished, this, [=](){
|
||||||
|
cleanUpReply(reply);
|
||||||
|
reply->deleteLater();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Just in case the reply get's deleted before beeing able to finish ...
|
||||||
|
connect(reply, &PingReply::destroyed, this, [this, reply](){
|
||||||
|
cleanUpReply(reply);
|
||||||
|
});
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
|
||||||
void Ping::finishReply(PingReply *reply, PingReply::Error error)
|
void Ping::finishReply(PingReply *reply, PingReply::Error error)
|
||||||
{
|
{
|
||||||
// Check if we should retry
|
// Check if we should retry
|
||||||
if (reply->m_retryCount >= reply->m_retries ||
|
if (reply->m_retryCount >= reply->m_retries ||
|
||||||
error == PingReply::ErrorNoError ||
|
error == PingReply::ErrorNoError ||
|
||||||
error == PingReply::ErrorAborted ||
|
error == PingReply::ErrorAborted ||
|
||||||
error == PingReply::ErrorInvalidHostAddress ||
|
error == PingReply::ErrorInvalidHostAddress ||
|
||||||
error == PingReply::ErrorPermissionDenied) {
|
error == PingReply::ErrorPermissionDenied||
|
||||||
|
error == PingReply::ErrorHostNameLookupFailed ||
|
||||||
|
error == PingReply::ErrorHostNameNotFound) {
|
||||||
// No retry, we are done
|
// No retry, we are done
|
||||||
reply->m_error = error;
|
reply->m_error = error;
|
||||||
reply->m_timer->stop();
|
reply->m_timer->stop();
|
||||||
@ -374,11 +415,11 @@ void Ping::cleanUpReply(PingReply *reply)
|
|||||||
m_pendingReplies.remove(reply->requestId());
|
m_pendingReplies.remove(reply->requestId());
|
||||||
m_replyQueue.removeAll(reply);
|
m_replyQueue.removeAll(reply);
|
||||||
|
|
||||||
if (m_pendingHostLookups.values().contains(reply)) {
|
if (m_pendingHostNameLookups.values().contains(reply)) {
|
||||||
// Abort any pending host lookups, the reply has been finished
|
// Abort any pending host lookups, the reply has been finished
|
||||||
int lookupId = m_pendingHostLookups.key(reply);
|
int lookupId = m_pendingHostNameLookups.key(reply);
|
||||||
QHostInfo::abortHostLookup(lookupId);
|
QHostInfo::abortHostLookup(lookupId);
|
||||||
m_pendingHostLookups.remove(lookupId);
|
m_pendingHostNameLookups.remove(lookupId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -404,19 +445,19 @@ void Ping::onSocketReadyRead(int socketDescriptor)
|
|||||||
QHostAddress destinationAddress(qFromBigEndian(ipHeader->daddr));
|
QHostAddress destinationAddress(qFromBigEndian(ipHeader->daddr));
|
||||||
|
|
||||||
qCDebug(dcPingTraffic()) << "IP header: Lenght" << ipHeaderLength
|
qCDebug(dcPingTraffic()) << "IP header: Lenght" << ipHeaderLength
|
||||||
<< "Sender:" << senderAddress.toString()
|
<< "Sender:" << senderAddress.toString()
|
||||||
<< "Destination:" << destinationAddress.toString()
|
<< "Destination:" << destinationAddress.toString()
|
||||||
<< "Size:" << htons(ipHeader->tot_len) << "B"
|
<< "Size:" << htons(ipHeader->tot_len) << "B"
|
||||||
<< "TTL" << ipHeader->ttl;
|
<< "TTL" << ipHeader->ttl;
|
||||||
|
|
||||||
struct icmp *responsePacket = reinterpret_cast<struct icmp *>(receiveBuffer + ipHeaderLength);
|
struct icmp *responsePacket = reinterpret_cast<struct icmp *>(receiveBuffer + ipHeaderLength);
|
||||||
quint16 icmpId = htons(responsePacket->icmp_id);
|
quint16 icmpId = htons(responsePacket->icmp_id);
|
||||||
quint16 icmpSequnceNumber = htons(responsePacket->icmp_seq);
|
quint16 icmpSequnceNumber = htons(responsePacket->icmp_seq);
|
||||||
qCDebug(dcPingTraffic()) << "ICMP packt (Size:" << icmpPacketSize << "Bytes):"
|
qCDebug(dcPingTraffic()) << "ICMP packt (Size:" << icmpPacketSize << "Bytes):"
|
||||||
<< "Type" << responsePacket->icmp_type
|
<< "Type" << responsePacket->icmp_type
|
||||||
<< "Code:" << responsePacket->icmp_code
|
<< "Code:" << responsePacket->icmp_code
|
||||||
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
|
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
|
||||||
<< "Sequence:" << icmpSequnceNumber;
|
<< "Sequence:" << icmpSequnceNumber;
|
||||||
|
|
||||||
if (responsePacket->icmp_type == ICMP_ECHOREPLY) {
|
if (responsePacket->icmp_type == ICMP_ECHOREPLY) {
|
||||||
|
|
||||||
@ -447,14 +488,14 @@ void Ping::onSocketReadyRead(int socketDescriptor)
|
|||||||
reply->m_duration = qRound((receiveTimeValue.tv_sec * 1000 + (double)receiveTimeValue.tv_usec / 1000) * 100) / 100.0;
|
reply->m_duration = qRound((receiveTimeValue.tv_sec * 1000 + (double)receiveTimeValue.tv_usec / 1000) * 100) / 100.0;
|
||||||
|
|
||||||
qCDebug(dcPing()) << "Received ICMP response" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]"
|
qCDebug(dcPing()) << "Received ICMP response" << reply->targetHostAddress().toString() << ICMP_PACKET_SIZE << "[Bytes]"
|
||||||
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
|
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
|
||||||
<< "Sequence:" << icmpSequnceNumber
|
<< "Sequence:" << icmpSequnceNumber
|
||||||
<< "Time:" << reply->duration() << "[ms]";
|
<< "Time:" << reply->duration() << "[ms]";
|
||||||
|
|
||||||
if (reply->doHostLookup()) {
|
if (reply->doHostLookup()) {
|
||||||
// Note: due to a Qt bug < 5.9 we need to use old SLOT style and cannot make use of lambda here
|
// Note: due to a Qt bug < 5.9 we need to use old SLOT style and cannot make use of lambda here
|
||||||
int lookupId = QHostInfo::lookupHost(senderAddress.toString(), this, SLOT(onHostLookupFinished(QHostInfo)));
|
int lookupId = QHostInfo::lookupHost(senderAddress.toString(), this, SLOT(onHostLookupFinished(QHostInfo)));
|
||||||
m_pendingHostLookups.insert(lookupId, reply);
|
m_pendingHostNameLookups.insert(lookupId, reply);
|
||||||
// Finish the reply after the host lookup has finished
|
// Finish the reply after the host lookup has finished
|
||||||
} else {
|
} else {
|
||||||
finishReply(reply, PingReply::ErrorNoError);
|
finishReply(reply, PingReply::ErrorNoError);
|
||||||
@ -472,20 +513,20 @@ void Ping::onSocketReadyRead(int socketDescriptor)
|
|||||||
QHostAddress nestedDestinationAddress(qFromBigEndian(nestedIpHeader->daddr));
|
QHostAddress nestedDestinationAddress(qFromBigEndian(nestedIpHeader->daddr));
|
||||||
|
|
||||||
qCDebug(dcPingTraffic()) << "++ IP header: Lenght" << nestedIpHeaderLength
|
qCDebug(dcPingTraffic()) << "++ IP header: Lenght" << nestedIpHeaderLength
|
||||||
<< "Sender:" << nestedSenderAddress.toString()
|
<< "Sender:" << nestedSenderAddress.toString()
|
||||||
<< "Destination:" << nestedDestinationAddress.toString()
|
<< "Destination:" << nestedDestinationAddress.toString()
|
||||||
<< "Size:" << htons(nestedIpHeader->tot_len) << "B"
|
<< "Size:" << htons(nestedIpHeader->tot_len) << "B"
|
||||||
<< "TTL" << ipHeader->ttl;
|
<< "TTL" << ipHeader->ttl;
|
||||||
|
|
||||||
struct icmp *nestedResponsePacket = reinterpret_cast<struct icmp *>(receiveBuffer + messageOffset + nestedIpHeaderLength);
|
struct icmp *nestedResponsePacket = reinterpret_cast<struct icmp *>(receiveBuffer + messageOffset + nestedIpHeaderLength);
|
||||||
icmpId = htons(nestedResponsePacket->icmp_id);
|
icmpId = htons(nestedResponsePacket->icmp_id);
|
||||||
icmpSequnceNumber = htons(nestedResponsePacket->icmp_seq);
|
icmpSequnceNumber = htons(nestedResponsePacket->icmp_seq);
|
||||||
|
|
||||||
qCDebug(dcPingTraffic()) << "++ ICMP packt (Size:" << nestedIcmpPacketSize << "Bytes):"
|
qCDebug(dcPingTraffic()) << "++ ICMP packt (Size:" << nestedIcmpPacketSize << "Bytes):"
|
||||||
<< "Type" << nestedResponsePacket->icmp_type
|
<< "Type" << nestedResponsePacket->icmp_type
|
||||||
<< "Code:" << nestedResponsePacket->icmp_code
|
<< "Code:" << nestedResponsePacket->icmp_code
|
||||||
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
|
<< "ID:" << QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
|
||||||
<< "Sequence:" << icmpSequnceNumber;
|
<< "Sequence:" << icmpSequnceNumber;
|
||||||
|
|
||||||
qCDebug(dcPing()) << "ICMP destination unreachable" << nestedDestinationAddress.toString()
|
qCDebug(dcPing()) << "ICMP destination unreachable" << nestedDestinationAddress.toString()
|
||||||
<< "Code:" << nestedResponsePacket->icmp_code
|
<< "Code:" << nestedResponsePacket->icmp_code
|
||||||
@ -495,9 +536,9 @@ void Ping::onSocketReadyRead(int socketDescriptor)
|
|||||||
PingReply *reply = m_pendingReplies.value(icmpId);
|
PingReply *reply = m_pendingReplies.value(icmpId);
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
qCDebug(dcPingTraffic()) << "No pending reply for ping echo response unreachable with ID"
|
qCDebug(dcPingTraffic()) << "No pending reply for ping echo response unreachable with ID"
|
||||||
<< QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
|
<< QString("0x%1").arg(icmpId, 4, 16, QChar('0'))
|
||||||
<< "Sequence:" << icmpSequnceNumber
|
<< "Sequence:" << icmpSequnceNumber
|
||||||
<< "from" << nestedSenderAddress.toString() << "to" << nestedDestinationAddress.toString();
|
<< "from" << nestedSenderAddress.toString() << "to" << nestedDestinationAddress.toString();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,20 +549,74 @@ void Ping::onSocketReadyRead(int socketDescriptor)
|
|||||||
|
|
||||||
void Ping::onHostLookupFinished(const QHostInfo &info)
|
void Ping::onHostLookupFinished(const QHostInfo &info)
|
||||||
{
|
{
|
||||||
PingReply *reply = m_pendingHostLookups.value(info.lookupId());
|
if (m_pendingHostNameLookups.contains(info.lookupId())) {
|
||||||
if (!reply) {
|
|
||||||
qCWarning(dcPing()) << "Could not find reply after host lookup.";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (info.error() != QHostInfo::NoError) {
|
PingReply *reply = m_pendingHostNameLookups.take(info.lookupId());
|
||||||
qCWarning(dcPing()) << "Failed to look up hostname after successfull ping" << reply->targetHostAddress().toString() << info.error();
|
if (!reply) {
|
||||||
} else {
|
qCWarning(dcPing()) << "Could not find ping reply after finished host lookup" << info.hostName() << info.addresses() << info.errorString();
|
||||||
qCDebug(dcPing()) << "Looked up hostname after successfull ping" << reply->targetHostAddress().toString() << info.hostName();
|
return;
|
||||||
if (info.hostName() != reply->targetHostAddress().toString()) {
|
|
||||||
reply->m_hostName = info.hostName();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
finishReply(reply, PingReply::ErrorNoError);
|
PingReply::Error pingError = PingReply::ErrorNoError;
|
||||||
|
switch(info.error()) {
|
||||||
|
case QHostInfo::NoError:
|
||||||
|
qCDebug(dcPing()) << "Looked up hostname after successfull ping" << reply->targetHostAddress().toString() << info.hostName();
|
||||||
|
if (info.hostName() != reply->targetHostAddress().toString())
|
||||||
|
reply->m_hostName = info.hostName();
|
||||||
|
|
||||||
|
break;
|
||||||
|
case QHostInfo::HostNotFound:
|
||||||
|
qCWarning(dcPing()) << "Unable to find hostname:" << reply->hostName() << info.errorString();
|
||||||
|
pingError = PingReply::ErrorHostNameNotFound;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCWarning(dcPing()) << "Failed to lookup hostname" << (reply->targetHostAddress().isNull() ? reply->hostName() : reply->targetHostAddress().toString()) << info.errorString();
|
||||||
|
pingError = PingReply::ErrorHostNameLookupFailed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
finishReply(reply, pingError);
|
||||||
|
|
||||||
|
} else if (m_pendingHostAddressLookups.contains(info.lookupId())) {
|
||||||
|
|
||||||
|
PingReply *reply = m_pendingHostAddressLookups.take(info.lookupId());
|
||||||
|
if (!reply) {
|
||||||
|
qCWarning(dcPing()) << "Could not find ping reply after finished host lookup" << info.hostName() << info.addresses() << info.errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PingReply::Error pingError = PingReply::ErrorNoError;
|
||||||
|
switch(info.error()) {
|
||||||
|
case QHostInfo::NoError:
|
||||||
|
qCDebug(dcPing()) << "Looked up address for hostname finished successfully" << info.hostName() << info.addresses();
|
||||||
|
if (info.addresses().isEmpty()) {
|
||||||
|
qCWarning(dcPing()) << "Looked up address finished succesfully but there are no addresses available for" << info.hostName();
|
||||||
|
pingError = PingReply::ErrorHostNameNotFound;
|
||||||
|
} else {
|
||||||
|
reply->m_targetHostAddress = info.addresses().first();
|
||||||
|
reply->m_networkInterface = NetworkUtils::getInterfaceForHostaddress(reply->targetHostAddress());
|
||||||
|
pingError = PingReply::ErrorNoError;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QHostInfo::HostNotFound:
|
||||||
|
qCDebug(dcPing()) << "Unable to find hostname:" << reply->hostName() << info.errorString();
|
||||||
|
pingError = PingReply::ErrorHostNameNotFound;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
qCWarning(dcPing()) << "Failed to lookup hostname" << (reply->targetHostAddress().isNull() ? reply->hostName() : reply->targetHostAddress().toString()) << info.errorString();
|
||||||
|
pingError = PingReply::ErrorHostNameLookupFailed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pingError != PingReply::ErrorNoError) {
|
||||||
|
// Host lookup failed, we cannot continue, nothing to ping
|
||||||
|
finishReply(reply, pingError);
|
||||||
|
} else {
|
||||||
|
// Ping the resolved host address
|
||||||
|
m_replyQueue.enqueue(reply);
|
||||||
|
sendNextReply();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qCWarning(dcPing()) << "Host name lookup finished but we have no ping reply for it. Ignoring looked up information" << info.hostName() << info.addresses() << info.error();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -67,6 +67,8 @@ public:
|
|||||||
PingReply *ping(const QHostAddress &hostAddress, uint retries = 3);
|
PingReply *ping(const QHostAddress &hostAddress, uint retries = 3);
|
||||||
PingReply *ping(const QHostAddress &hostAddress, bool lookupHost, uint retries = 3);
|
PingReply *ping(const QHostAddress &hostAddress, bool lookupHost, uint retries = 3);
|
||||||
|
|
||||||
|
PingReply *ping(const QString &hostName, uint retries = 3);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void availableChanged(bool available);
|
void availableChanged(bool available);
|
||||||
|
|
||||||
@ -91,7 +93,8 @@ private:
|
|||||||
QTimer *m_queueTimer = nullptr;
|
QTimer *m_queueTimer = nullptr;
|
||||||
PingReply *m_currentReply = nullptr;
|
PingReply *m_currentReply = nullptr;
|
||||||
void sendNextReply();
|
void sendNextReply();
|
||||||
QHash<int, PingReply *> m_pendingHostLookups;
|
QHash<int, PingReply *> m_pendingHostNameLookups;
|
||||||
|
QHash<int, PingReply *> m_pendingHostAddressLookups;
|
||||||
|
|
||||||
//Error performPing(const QString &address);
|
//Error performPing(const QString &address);
|
||||||
void performPing(PingReply *reply);
|
void performPing(PingReply *reply);
|
||||||
@ -104,13 +107,13 @@ private:
|
|||||||
quint16 calculateRequestId();
|
quint16 calculateRequestId();
|
||||||
|
|
||||||
PingReply *createReply(const QHostAddress &hostAddress);
|
PingReply *createReply(const QHostAddress &hostAddress);
|
||||||
|
PingReply *createReply(const QString &hostName);
|
||||||
void finishReply(PingReply *reply, PingReply::Error error);
|
void finishReply(PingReply *reply, PingReply::Error error);
|
||||||
void cleanUpReply(PingReply *reply);
|
void cleanUpReply(PingReply *reply);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onSocketReadyRead(int socketDescriptor);
|
void onSocketReadyRead(int socketDescriptor);
|
||||||
void onHostLookupFinished(const QHostInfo &info);
|
void onHostLookupFinished(const QHostInfo &info);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PING_H
|
#endif // PING_H
|
||||||
|
|||||||
@ -59,7 +59,9 @@ public:
|
|||||||
ErrorSocketError,
|
ErrorSocketError,
|
||||||
ErrorTimeout,
|
ErrorTimeout,
|
||||||
ErrorHostUnreachable,
|
ErrorHostUnreachable,
|
||||||
ErrorInvalidHostAddress
|
ErrorInvalidHostAddress,
|
||||||
|
ErrorHostNameLookupFailed,
|
||||||
|
ErrorHostNameNotFound
|
||||||
};
|
};
|
||||||
Q_ENUM(Error)
|
Q_ENUM(Error)
|
||||||
|
|
||||||
|
|||||||
@ -30,11 +30,12 @@
|
|||||||
|
|
||||||
#include "interface.h"
|
#include "interface.h"
|
||||||
|
|
||||||
Interface::Interface(const QString &name, const InterfaceActionTypes &actionTypes, const InterfaceEventTypes &eventTypes, const InterfaceStateTypes &stateTypes):
|
Interface::Interface(const QString &name, const InterfaceParamTypes ¶mTypes, const InterfaceActionTypes &actionTypes, const InterfaceEventTypes &eventTypes, const InterfaceStateTypes &stateTypes):
|
||||||
m_name(name),
|
m_name{name},
|
||||||
m_actionTypes(actionTypes),
|
m_paramTypes{paramTypes},
|
||||||
m_eventTypes(eventTypes),
|
m_actionTypes{actionTypes},
|
||||||
m_stateTypes(stateTypes)
|
m_eventTypes{eventTypes},
|
||||||
|
m_stateTypes{stateTypes}
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -44,6 +45,11 @@ QString Interface::name() const
|
|||||||
return m_name;
|
return m_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InterfaceParamTypes Interface::paramTypes() const
|
||||||
|
{
|
||||||
|
return m_paramTypes;
|
||||||
|
}
|
||||||
|
|
||||||
InterfaceActionTypes Interface::actionTypes() const
|
InterfaceActionTypes Interface::actionTypes() const
|
||||||
{
|
{
|
||||||
return m_actionTypes;
|
return m_actionTypes;
|
||||||
|
|||||||
@ -31,6 +31,7 @@
|
|||||||
#ifndef INTERFACE_H
|
#ifndef INTERFACE_H
|
||||||
#define INTERFACE_H
|
#define INTERFACE_H
|
||||||
|
|
||||||
|
#include "interfaceparamtype.h"
|
||||||
#include "interfaceeventtype.h"
|
#include "interfaceeventtype.h"
|
||||||
#include "interfaceactiontype.h"
|
#include "interfaceactiontype.h"
|
||||||
#include "interfacestatetype.h"
|
#include "interfacestatetype.h"
|
||||||
@ -38,18 +39,20 @@
|
|||||||
class Interface{
|
class Interface{
|
||||||
public:
|
public:
|
||||||
Interface() = default;
|
Interface() = default;
|
||||||
Interface(const QString &name, const InterfaceActionTypes &actionTypes, const InterfaceEventTypes &eventTypes, const InterfaceStateTypes &stateTypes);
|
Interface(const QString &name, const InterfaceParamTypes ¶mTypes, const InterfaceActionTypes &actionTypes, const InterfaceEventTypes &eventTypes, const InterfaceStateTypes &stateTypes);
|
||||||
|
|
||||||
QString name() const;
|
QString name() const;
|
||||||
|
|
||||||
|
InterfaceParamTypes paramTypes() const;
|
||||||
InterfaceActionTypes actionTypes() const;
|
InterfaceActionTypes actionTypes() const;
|
||||||
InterfaceEventTypes eventTypes() const;
|
InterfaceEventTypes eventTypes() const;
|
||||||
InterfaceStateTypes stateTypes() const;
|
InterfaceStateTypes stateTypes() const;
|
||||||
|
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QUuid m_id;
|
|
||||||
QString m_name;
|
QString m_name;
|
||||||
|
InterfaceParamTypes m_paramTypes;
|
||||||
InterfaceActionTypes m_actionTypes;
|
InterfaceActionTypes m_actionTypes;
|
||||||
InterfaceEventTypes m_eventTypes;
|
InterfaceEventTypes m_eventTypes;
|
||||||
InterfaceStateTypes m_stateTypes;
|
InterfaceStateTypes m_stateTypes;
|
||||||
|
|||||||
@ -1,3 +1,33 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 "interfaceactiontype.h"
|
#include "interfaceactiontype.h"
|
||||||
|
|
||||||
InterfaceActionType::InterfaceActionType()
|
InterfaceActionType::InterfaceActionType()
|
||||||
|
|||||||
@ -1,3 +1,33 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 INTERFACEACTIONTYPE_H
|
#ifndef INTERFACEACTIONTYPE_H
|
||||||
#define INTERFACEACTIONTYPE_H
|
#define INTERFACEACTIONTYPE_H
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,33 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 "interfaceeventtype.h"
|
#include "interfaceeventtype.h"
|
||||||
|
|
||||||
InterfaceEventType::InterfaceEventType()
|
InterfaceEventType::InterfaceEventType()
|
||||||
|
|||||||
@ -1,3 +1,33 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 INTERFACEEVENTTYPE_H
|
#ifndef INTERFACEEVENTTYPE_H
|
||||||
#define INTERFACEEVENTTYPE_H
|
#define INTERFACEEVENTTYPE_H
|
||||||
|
|
||||||
|
|||||||
64
libnymea/types/interfaceparamtype.cpp
Normal file
64
libnymea/types/interfaceparamtype.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 "interfaceparamtype.h"
|
||||||
|
|
||||||
|
|
||||||
|
InterfaceParamType::InterfaceParamType()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InterfaceParamType::optional() const
|
||||||
|
{
|
||||||
|
return m_optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InterfaceParamType::setOptional(bool optional)
|
||||||
|
{
|
||||||
|
m_optional = optional;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
InterfaceParamTypes::InterfaceParamTypes(const QList<InterfaceParamType> &other)
|
||||||
|
: QList<InterfaceParamType>(other)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
InterfaceParamType InterfaceParamTypes::findByName(const QString &name)
|
||||||
|
{
|
||||||
|
foreach (const InterfaceParamType &ipt, *this) {
|
||||||
|
if (ipt.name() == name) {
|
||||||
|
return ipt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return InterfaceParamType();
|
||||||
|
}
|
||||||
58
libnymea/types/interfaceparamtype.h
Normal file
58
libnymea/types/interfaceparamtype.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 INTERFACEPARAMTYPE_H
|
||||||
|
#define INTERFACEPARAMTYPE_H
|
||||||
|
|
||||||
|
#include "paramtype.h"
|
||||||
|
|
||||||
|
class InterfaceParamType : public ParamType
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InterfaceParamType();
|
||||||
|
|
||||||
|
bool optional() const;
|
||||||
|
void setOptional(bool optional);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_optional = false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class InterfaceParamTypes: public QList<InterfaceParamType>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InterfaceParamTypes() = default;
|
||||||
|
InterfaceParamTypes(const QList<InterfaceParamType> &other);
|
||||||
|
InterfaceParamType findByName(const QString &name);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // INTERFACEPARAMTYPE_H
|
||||||
@ -1,3 +1,33 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 "interfacestatetype.h"
|
#include "interfacestatetype.h"
|
||||||
|
|
||||||
InterfaceStateType::InterfaceStateType()
|
InterfaceStateType::InterfaceStateType()
|
||||||
|
|||||||
@ -1,3 +1,33 @@
|
|||||||
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
|
*
|
||||||
|
* Copyright 2013 - 2024, 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* 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 INTERFACESTATETYPE_H
|
#ifndef INTERFACESTATETYPE_H
|
||||||
#define INTERFACESTATETYPE_H
|
#define INTERFACESTATETYPE_H
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
* Copyright 2013 - 2024, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
@ -264,6 +264,26 @@ ParamTypes::ParamTypes(const QList<ParamType> &other): QList<ParamType>(other)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ParamTypes::contains(const ParamTypeId ¶mTypeId)
|
||||||
|
{
|
||||||
|
foreach (const ParamType ¶mType, *this) {
|
||||||
|
if (paramType.id() == paramTypeId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ParamTypes::contains(const QString &name)
|
||||||
|
{
|
||||||
|
foreach (const ParamType ¶mType, *this) {
|
||||||
|
if (paramType.name() == name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QVariant ParamTypes::get(int index) const
|
QVariant ParamTypes::get(int index) const
|
||||||
{
|
{
|
||||||
return QVariant::fromValue(at(index));
|
return QVariant::fromValue(at(index));
|
||||||
@ -293,3 +313,15 @@ ParamType ParamTypes::findById(const ParamTypeId &id) const
|
|||||||
}
|
}
|
||||||
return ParamType();
|
return ParamType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ParamType &ParamTypes::operator[](const QString &name)
|
||||||
|
{
|
||||||
|
int index = -1;
|
||||||
|
for (int i = 0; i < count(); i++) {
|
||||||
|
if (at(i).name() == name) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QList::operator[](index);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
*
|
*
|
||||||
* Copyright 2013 - 2020, nymea GmbH
|
* Copyright 2013 - 2024, nymea GmbH
|
||||||
* Contact: contact@nymea.io
|
* Contact: contact@nymea.io
|
||||||
*
|
*
|
||||||
* This file is part of nymea.
|
* This file is part of nymea.
|
||||||
@ -122,10 +122,14 @@ class ParamTypes: public QList<ParamType>
|
|||||||
public:
|
public:
|
||||||
ParamTypes() = default;
|
ParamTypes() = default;
|
||||||
ParamTypes(const QList<ParamType> &other);
|
ParamTypes(const QList<ParamType> &other);
|
||||||
|
bool contains(const ParamTypeId ¶mTypeId);
|
||||||
|
bool contains(const QString &name);
|
||||||
Q_INVOKABLE QVariant get(int index) const;
|
Q_INVOKABLE QVariant get(int index) const;
|
||||||
Q_INVOKABLE void put(const QVariant &variant);
|
Q_INVOKABLE void put(const QVariant &variant);
|
||||||
ParamType findByName(const QString &name) const;
|
ParamType findByName(const QString &name) const;
|
||||||
ParamType findById(const ParamTypeId &id) const;
|
ParamType findById(const ParamTypeId &id) const;
|
||||||
|
ParamType &operator[](const QString &name);
|
||||||
|
|
||||||
};
|
};
|
||||||
Q_DECLARE_METATYPE(QList<ParamType>)
|
Q_DECLARE_METATYPE(QList<ParamType>)
|
||||||
Q_DECLARE_METATYPE(ParamTypes)
|
Q_DECLARE_METATYPE(ParamTypes)
|
||||||
|
|||||||
@ -13,7 +13,7 @@ isEmpty(NYMEA_VERSION) {
|
|||||||
JSON_PROTOCOL_VERSION_MAJOR=8
|
JSON_PROTOCOL_VERSION_MAJOR=8
|
||||||
JSON_PROTOCOL_VERSION_MINOR=2
|
JSON_PROTOCOL_VERSION_MINOR=2
|
||||||
JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}"
|
JSON_PROTOCOL_VERSION="$${JSON_PROTOCOL_VERSION_MAJOR}.$${JSON_PROTOCOL_VERSION_MINOR}"
|
||||||
LIBNYMEA_API_VERSION_MAJOR=8
|
LIBNYMEA_API_VERSION_MAJOR=9
|
||||||
LIBNYMEA_API_VERSION_MINOR=0
|
LIBNYMEA_API_VERSION_MINOR=0
|
||||||
LIBNYMEA_API_VERSION_PATCH=0
|
LIBNYMEA_API_VERSION_PATCH=0
|
||||||
LIBNYMEA_API_VERSION="$${LIBNYMEA_API_VERSION_MAJOR}.$${LIBNYMEA_API_VERSION_MINOR}.$${LIBNYMEA_API_VERSION_PATCH}"
|
LIBNYMEA_API_VERSION="$${LIBNYMEA_API_VERSION_MAJOR}.$${LIBNYMEA_API_VERSION_MINOR}.$${LIBNYMEA_API_VERSION_PATCH}"
|
||||||
|
|||||||
@ -232,5 +232,10 @@ extern StateTypeId virtualIoTemperatureSensorMockInputStateTypeId;
|
|||||||
extern StateTypeId virtualIoTemperatureSensorMockTemperatureStateTypeId;
|
extern StateTypeId virtualIoTemperatureSensorMockTemperatureStateTypeId;
|
||||||
extern ActionTypeId virtualIoTemperatureSensorMockInputActionTypeId;
|
extern ActionTypeId virtualIoTemperatureSensorMockInputActionTypeId;
|
||||||
extern ParamTypeId virtualIoTemperatureSensorMockInputActionInputParamTypeId;
|
extern ParamTypeId virtualIoTemperatureSensorMockInputActionInputParamTypeId;
|
||||||
|
extern ThingClassId networkDeviceMockThingClassId;
|
||||||
|
extern ParamTypeId networkDeviceMockThingMacAddressParamTypeId;
|
||||||
|
extern ParamTypeId networkDeviceMockThingHostNameParamTypeId;
|
||||||
|
extern ParamTypeId networkDeviceMockThingAddressParamTypeId;
|
||||||
|
extern ParamTypeId networkDeviceMockDiscoveryResultTypeParamTypeId;
|
||||||
|
|
||||||
#endif // EXTERNPLUGININFO_H
|
#endif // EXTERNPLUGININFO_H
|
||||||
|
|||||||
@ -126,6 +126,27 @@ void IntegrationPluginMock::discoverThings(ThingDiscoveryInfo *info)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info->thingClassId() == networkDeviceMockThingClassId) {
|
||||||
|
qCDebug(dcMock()) << "starting network device mock discovery:" << info->params();
|
||||||
|
QTimer::singleShot(1000, info, [info](){
|
||||||
|
QString resultType = info->params().paramValue(networkDeviceMockDiscoveryResultTypeParamTypeId).toString();
|
||||||
|
ParamList params;
|
||||||
|
if (resultType == "MAC address") {
|
||||||
|
params.append(Param(networkDeviceMockThingMacAddressParamTypeId, "00:11:22:33:44:55"));
|
||||||
|
} else if (resultType == "Host name") {
|
||||||
|
params.append(Param(networkDeviceMockThingHostNameParamTypeId, "hostname.localhost"));
|
||||||
|
} else if (resultType == "IP address") {
|
||||||
|
params.append(Param(networkDeviceMockThingAddressParamTypeId, "127.0.0.1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
ThingDescriptor descriptor(networkDeviceMockThingClassId, "Mocked Thing (networkdevice)", QString());
|
||||||
|
descriptor.setParams(params);
|
||||||
|
info->addThingDescriptor(descriptor);
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qCWarning(dcMock()) << "Cannot discover for ThingClassId" << info->thingClassId();
|
qCWarning(dcMock()) << "Cannot discover for ThingClassId" << info->thingClassId();
|
||||||
info->finish(Thing::ThingErrorThingNotFound);
|
info->finish(Thing::ThingErrorThingNotFound);
|
||||||
}
|
}
|
||||||
@ -273,6 +294,12 @@ void IntegrationPluginMock::setupThing(ThingSetupInfo *info)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (info->thing()->thingClassId() == networkDeviceMockThingClassId) {
|
||||||
|
qCDebug(dcMock()) << "Network device mock setup complete";
|
||||||
|
info->finish(Thing::ThingErrorNoError);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
qCWarning(dcMock()) << "Unhandled thing class" << info->thing()->thingClass();
|
qCWarning(dcMock()) << "Unhandled thing class" << info->thing()->thingClass();
|
||||||
info->finish(Thing::ThingErrorThingClassNotFound);
|
info->finish(Thing::ThingErrorThingClassNotFound);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1198,6 +1198,59 @@
|
|||||||
"defaultValue": -20
|
"defaultValue": -20
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "cd8fad43-174b-4ca4-a225-c07e4dbba10a",
|
||||||
|
"name": "networkDeviceMock",
|
||||||
|
"displayName": "Mocked Thing (network device)",
|
||||||
|
"interfaces": ["networkdevice"],
|
||||||
|
"createMethods": ["discovery", "user"],
|
||||||
|
"setupMethod": "justAdd",
|
||||||
|
"discoveryParamTypes": [
|
||||||
|
{
|
||||||
|
"id": "762d9bc3-c07e-42e7-8dab-62c980998677",
|
||||||
|
"name": "resultType",
|
||||||
|
"displayName": "Result type",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue": "MAC address",
|
||||||
|
"allowedValues": [
|
||||||
|
"MAC address",
|
||||||
|
"Host name",
|
||||||
|
"IP address"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"paramTypes": [
|
||||||
|
{
|
||||||
|
"id": "5c2462ca-883e-4fe1-91f2-7190f9363247",
|
||||||
|
"name": "macAddress",
|
||||||
|
"displayName": "MAC address",
|
||||||
|
"type": "QString",
|
||||||
|
"defaultValue":"",
|
||||||
|
"inputType": "MacAddress",
|
||||||
|
"defaultValue": "",
|
||||||
|
"readOnly": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ae867d45-c743-4185-87d3-1a027c985f11",
|
||||||
|
"name": "hostName",
|
||||||
|
"displayName": "Host name",
|
||||||
|
"type": "QString",
|
||||||
|
"inputType": "TextLine",
|
||||||
|
"defaultValue": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "619fb102-0ebd-497e-9960-615c5d347db9",
|
||||||
|
"name": "address",
|
||||||
|
"displayName": "IP address",
|
||||||
|
"type": "QString",
|
||||||
|
"inputType": "IPv4Address",
|
||||||
|
"defaultValue": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stateTypes": [ ],
|
||||||
|
"actionTypes": [ ],
|
||||||
|
"eventTypes": [ ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
extern "C" const QString libnymea_api_version() { return QString("8.0.0");}
|
extern "C" const QString libnymea_api_version() { return QString("9.0.0");}
|
||||||
|
|
||||||
Q_DECLARE_LOGGING_CATEGORY(dcMock)
|
Q_DECLARE_LOGGING_CATEGORY(dcMock)
|
||||||
Q_LOGGING_CATEGORY(dcMock, "Mock")
|
Q_LOGGING_CATEGORY(dcMock, "Mock")
|
||||||
@ -236,6 +236,11 @@ StateTypeId virtualIoTemperatureSensorMockInputStateTypeId = StateTypeId("{fd341
|
|||||||
StateTypeId virtualIoTemperatureSensorMockTemperatureStateTypeId = StateTypeId("{db9cc518-1012-47e2-8212-6e616fed07a6}");
|
StateTypeId virtualIoTemperatureSensorMockTemperatureStateTypeId = StateTypeId("{db9cc518-1012-47e2-8212-6e616fed07a6}");
|
||||||
ActionTypeId virtualIoTemperatureSensorMockInputActionTypeId = ActionTypeId("{fd341f72-6d9a-4812-9f66-47197c48a935}");
|
ActionTypeId virtualIoTemperatureSensorMockInputActionTypeId = ActionTypeId("{fd341f72-6d9a-4812-9f66-47197c48a935}");
|
||||||
ParamTypeId virtualIoTemperatureSensorMockInputActionInputParamTypeId = ParamTypeId("{fd341f72-6d9a-4812-9f66-47197c48a935}");
|
ParamTypeId virtualIoTemperatureSensorMockInputActionInputParamTypeId = ParamTypeId("{fd341f72-6d9a-4812-9f66-47197c48a935}");
|
||||||
|
ThingClassId networkDeviceMockThingClassId = ThingClassId("{cd8fad43-174b-4ca4-a225-c07e4dbba10a}");
|
||||||
|
ParamTypeId networkDeviceMockThingMacAddressParamTypeId = ParamTypeId("{5c2462ca-883e-4fe1-91f2-7190f9363247}");
|
||||||
|
ParamTypeId networkDeviceMockThingHostNameParamTypeId = ParamTypeId("{ae867d45-c743-4185-87d3-1a027c985f11}");
|
||||||
|
ParamTypeId networkDeviceMockThingAddressParamTypeId = ParamTypeId("{619fb102-0ebd-497e-9960-615c5d347db9}");
|
||||||
|
ParamTypeId networkDeviceMockDiscoveryResultTypeParamTypeId = ParamTypeId("{762d9bc3-c07e-42e7-8dab-62c980998677}");
|
||||||
|
|
||||||
const QString translations[] {
|
const QString translations[] {
|
||||||
//: The name of the Browser Item ActionType ({00b8f0a8-99ca-4aa4-833d-59eb8d4d6de3}) of ThingClass mock
|
//: The name of the Browser Item ActionType ({00b8f0a8-99ca-4aa4-833d-59eb8d4d6de3}) of ThingClass mock
|
||||||
@ -361,6 +366,12 @@ const QString translations[] {
|
|||||||
//: The name of the ThingClass ({f8917e12-c9cb-4ea1-a06e-1ce6db2194f3})
|
//: The name of the ThingClass ({f8917e12-c9cb-4ea1-a06e-1ce6db2194f3})
|
||||||
QT_TRANSLATE_NOOP("mock", "Generic Temperature Sensor (Mock)"),
|
QT_TRANSLATE_NOOP("mock", "Generic Temperature Sensor (Mock)"),
|
||||||
|
|
||||||
|
//: The name of the ParamType (ThingClass: networkDeviceMock, Type: thing, ID: {ae867d45-c743-4185-87d3-1a027c985f11})
|
||||||
|
QT_TRANSLATE_NOOP("mock", "Host name"),
|
||||||
|
|
||||||
|
//: The name of the ParamType (ThingClass: networkDeviceMock, Type: thing, ID: {619fb102-0ebd-497e-9960-615c5d347db9})
|
||||||
|
QT_TRANSLATE_NOOP("mock", "IP address"),
|
||||||
|
|
||||||
//: The name of the ParamType (ThingClass: inputTypeMock, Type: thing, ID: {9e5f86a0-4bb3-4892-bff8-3fc4032af6e2})
|
//: The name of the ParamType (ThingClass: inputTypeMock, Type: thing, ID: {9e5f86a0-4bb3-4892-bff8-3fc4032af6e2})
|
||||||
QT_TRANSLATE_NOOP("mock", "IPv4 address"),
|
QT_TRANSLATE_NOOP("mock", "IPv4 address"),
|
||||||
|
|
||||||
@ -388,6 +399,9 @@ const QString translations[] {
|
|||||||
//: The name of the StateType ({23df3dce-bd10-4eb3-b5e3-221168440cd4}) of ThingClass inputTypeMock
|
//: The name of the StateType ({23df3dce-bd10-4eb3-b5e3-221168440cd4}) of ThingClass inputTypeMock
|
||||||
QT_TRANSLATE_NOOP("mock", "Localized list"),
|
QT_TRANSLATE_NOOP("mock", "Localized list"),
|
||||||
|
|
||||||
|
//: The name of the ParamType (ThingClass: networkDeviceMock, Type: thing, ID: {5c2462ca-883e-4fe1-91f2-7190f9363247})
|
||||||
|
QT_TRANSLATE_NOOP("mock", "MAC address"),
|
||||||
|
|
||||||
//: The name of the ParamType (ThingClass: inputTypeMock, Type: thing, ID: {e93db587-7919-48f3-8c88-1651de63c765})
|
//: The name of the ParamType (ThingClass: inputTypeMock, Type: thing, ID: {e93db587-7919-48f3-8c88-1651de63c765})
|
||||||
QT_TRANSLATE_NOOP("mock", "Mac address"),
|
QT_TRANSLATE_NOOP("mock", "Mac address"),
|
||||||
|
|
||||||
@ -481,6 +495,9 @@ const QString translations[] {
|
|||||||
//: The name of the ThingClass ({6fe07a77-9c07-4736-81e2-d504314bbcb9})
|
//: The name of the ThingClass ({6fe07a77-9c07-4736-81e2-d504314bbcb9})
|
||||||
QT_TRANSLATE_NOOP("mock", "Mocked Thing (User & Password)"),
|
QT_TRANSLATE_NOOP("mock", "Mocked Thing (User & Password)"),
|
||||||
|
|
||||||
|
//: The name of the ThingClass ({cd8fad43-174b-4ca4-a225-c07e4dbba10a})
|
||||||
|
QT_TRANSLATE_NOOP("mock", "Mocked Thing (network device)"),
|
||||||
|
|
||||||
//: The name of the plugin mock ({727a4a9a-c187-446f-aadf-f1b2220607d1})
|
//: The name of the plugin mock ({727a4a9a-c187-446f-aadf-f1b2220607d1})
|
||||||
QT_TRANSLATE_NOOP("mock", "Mocked things"),
|
QT_TRANSLATE_NOOP("mock", "Mocked things"),
|
||||||
|
|
||||||
@ -508,6 +525,9 @@ const QString translations[] {
|
|||||||
//: The name of the ParamType (ThingClass: mock, Type: discovery, ID: {d222adb4-2f9c-4c3f-8655-76400d0fb6ce})
|
//: The name of the ParamType (ThingClass: mock, Type: discovery, ID: {d222adb4-2f9c-4c3f-8655-76400d0fb6ce})
|
||||||
QT_TRANSLATE_NOOP("mock", "Result count"),
|
QT_TRANSLATE_NOOP("mock", "Result count"),
|
||||||
|
|
||||||
|
//: The name of the ParamType (ThingClass: networkDeviceMock, Type: discovery, ID: {762d9bc3-c07e-42e7-8dab-62c980998677})
|
||||||
|
QT_TRANSLATE_NOOP("mock", "Result type"),
|
||||||
|
|
||||||
//: The name of the ParamType (ThingClass: inputTypeMock, Type: thing, ID: {22add8c9-ee4f-43ad-8931-58e999313ac3})
|
//: The name of the ParamType (ThingClass: inputTypeMock, Type: thing, ID: {22add8c9-ee4f-43ad-8931-58e999313ac3})
|
||||||
QT_TRANSLATE_NOOP("mock", "Search text"),
|
QT_TRANSLATE_NOOP("mock", "Search text"),
|
||||||
|
|
||||||
|
|||||||
@ -298,8 +298,8 @@ void TestIntegrations::getThingClasses_data()
|
|||||||
QTest::addColumn<QList<ThingClassId>>("thingClassIds");
|
QTest::addColumn<QList<ThingClassId>>("thingClassIds");
|
||||||
QTest::addColumn<int>("resultCount");
|
QTest::addColumn<int>("resultCount");
|
||||||
|
|
||||||
QTest::newRow("vendor nymea") << nymeaVendorId << QList<ThingClassId>() << 16;
|
QTest::newRow("vendor nymea") << nymeaVendorId << QList<ThingClassId>() << 17;
|
||||||
QTest::newRow("no filter") << VendorId() << QList<ThingClassId>() << 16;
|
QTest::newRow("no filter") << VendorId() << QList<ThingClassId>() << 17;
|
||||||
QTest::newRow("invalid vendor") << VendorId("93e7d361-8025-4354-b17e-b68406c800bc") << QList<ThingClassId>() << 0;
|
QTest::newRow("invalid vendor") << VendorId("93e7d361-8025-4354-b17e-b68406c800bc") << QList<ThingClassId>() << 0;
|
||||||
QTest::newRow("mockThingClassId") << VendorId() << (QList<ThingClassId>() << mockThingClassId) << 1;
|
QTest::newRow("mockThingClassId") << VendorId() << (QList<ThingClassId>() << mockThingClassId) << 1;
|
||||||
QTest::newRow("invalid thingClassId") << VendorId() << (QList<ThingClassId>() << ThingClassId("6c78ec28-09b6-476d-ac27-1d6966a45c57")) << 0;
|
QTest::newRow("invalid thingClassId") << VendorId() << (QList<ThingClassId>() << ThingClassId("6c78ec28-09b6-476d-ac27-1d6966a45c57")) << 0;
|
||||||
|
|||||||
@ -24,6 +24,7 @@ SOURCES += \
|
|||||||
$$top_srcdir/libnymea/types/eventtype.cpp \
|
$$top_srcdir/libnymea/types/eventtype.cpp \
|
||||||
$$top_srcdir/libnymea/types/actiontype.cpp \
|
$$top_srcdir/libnymea/types/actiontype.cpp \
|
||||||
$$top_srcdir/libnymea/types/interface.cpp \
|
$$top_srcdir/libnymea/types/interface.cpp \
|
||||||
|
$$top_srcdir/libnymea/types/interfaceparamtype.cpp \
|
||||||
$$top_srcdir/libnymea/types/interfacestatetype.cpp \
|
$$top_srcdir/libnymea/types/interfacestatetype.cpp \
|
||||||
$$top_srcdir/libnymea/types/interfaceeventtype.cpp \
|
$$top_srcdir/libnymea/types/interfaceeventtype.cpp \
|
||||||
$$top_srcdir/libnymea/types/interfaceactiontype.cpp \
|
$$top_srcdir/libnymea/types/interfaceactiontype.cpp \
|
||||||
@ -42,6 +43,7 @@ HEADERS += \
|
|||||||
$$top_srcdir/libnymea/types/eventtype.h \
|
$$top_srcdir/libnymea/types/eventtype.h \
|
||||||
$$top_srcdir/libnymea/types/actiontype.h \
|
$$top_srcdir/libnymea/types/actiontype.h \
|
||||||
$$top_srcdir/libnymea/types/interface.h \
|
$$top_srcdir/libnymea/types/interface.h \
|
||||||
|
$$top_srcdir/libnymea/types/interfaceparamtype.h \
|
||||||
$$top_srcdir/libnymea/types/interfacestatetype.h \
|
$$top_srcdir/libnymea/types/interfacestatetype.h \
|
||||||
$$top_srcdir/libnymea/types/interfaceeventtype.h \
|
$$top_srcdir/libnymea/types/interfaceeventtype.h \
|
||||||
$$top_srcdir/libnymea/types/interfaceactiontype.h \
|
$$top_srcdir/libnymea/types/interfaceactiontype.h \
|
||||||
|
|||||||
Reference in New Issue
Block a user