diff --git a/libnymea-core/hardware/serialport/serialportmonitor.cpp b/libnymea-core/hardware/serialport/serialportmonitor.cpp
new file mode 100644
index 00000000..cd1addfc
--- /dev/null
+++ b/libnymea-core/hardware/serialport/serialportmonitor.cpp
@@ -0,0 +1,189 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2021, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project is distributed in the hope that
+* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#include "serialportmonitor.h"
+#include "loggingcategories.h"
+
+namespace nymeaserver {
+
+NYMEA_LOGGING_CATEGORY(dcSerialPortMonitor, "SerialPortMonitor")
+
+SerialPortMonitor::SerialPortMonitor(QObject *parent) : QObject(parent)
+{
+ // Read initially all tty devices
+ foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) {
+ m_serialPorts.insert(serialPortInfo.systemLocation(), SerialPort(serialPortInfo));
+ }
+
+#ifdef WITH_UDEV
+ // Init udev
+ m_udev = udev_new();
+ if (!m_udev) {
+ qCWarning(dcSerialPortMonitor()) << "Could not initialize udev for the adapter monitor";
+ return;
+ }
+
+ // Create udev monitor
+ m_monitor = udev_monitor_new_from_netlink(m_udev, "udev");
+ if (!m_monitor) {
+ qCWarning(dcSerialPortMonitor()) << "Could not initialize udev monitor.";
+ udev_unref(m_udev);
+ m_udev = nullptr;
+ return;
+ }
+
+ // Set monitor filter to tty subsystem
+ if (udev_monitor_filter_add_match_subsystem_devtype(m_monitor, "tty", nullptr) < 0) {
+ qCWarning(dcSerialPortMonitor()) << "Could not set subsystem device type filter to tty.";
+ udev_monitor_unref(m_monitor);
+ m_monitor = nullptr;
+ udev_unref(m_udev);
+ m_udev = nullptr;
+ return;
+ }
+
+ // Enable the monitor
+ if (udev_monitor_enable_receiving(m_monitor) < 0) {
+ qCWarning(dcSerialPortMonitor()) << "Could not enable udev monitor.";
+ udev_monitor_unref(m_monitor);
+ m_monitor = nullptr;
+ udev_unref(m_udev);
+ m_udev = nullptr;
+ return;
+ }
+
+ // Create socket notifier for read
+ int socketDescriptor = udev_monitor_get_fd(m_monitor);
+ m_notifier = new QSocketNotifier(socketDescriptor, QSocketNotifier::Read, this);
+ connect(m_notifier, &QSocketNotifier::activated, this, [this, socketDescriptor](int socket){
+ // Make sure the socket matches
+ if (socketDescriptor != socket) {
+ qCWarning(dcSerialPortMonitor()) << "Socket handles do not match. socket != socketdescriptor";
+ return;
+ }
+
+ // Create udev device
+ udev_device *device = udev_monitor_receive_device(m_monitor);
+ if (!device) {
+ qCWarning(dcSerialPortMonitor()) << "Got socket sotification but could not read device information.";
+ return;
+ }
+
+ QString actionString = QString::fromLatin1(udev_device_get_action(device));
+ QString systemPath = QString::fromLatin1(udev_device_get_property_value(device,"DEVNAME"));
+ QString manufacturerString = QString::fromLatin1(udev_device_get_property_value(device,"ID_VENDOR_ENC"));
+ QString descriptionString = QString::fromLatin1(udev_device_get_property_value(device,"ID_MODEL_ENC"));
+ QString serialNumberString = QString::fromLatin1(udev_device_get_property_value(device, "ID_SERIAL_SHORT"));
+
+ // Clean udev device
+ udev_device_unref(device);
+
+ // Make sure we know the action
+ if (actionString.isEmpty())
+ return;
+
+ if (actionString == "add") {
+ qCDebug(dcSerialPortMonitor()) << "[+]" << systemPath << serialNumberString << manufacturerString << descriptionString;
+ if (!m_serialPorts.contains(systemPath)) {
+ // Get the serial port info and add it internally
+ foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) {
+ if (serialPortInfo.systemLocation() == systemPath) {
+ addSerialPortInternally(SerialPort(serialPortInfo));
+ }
+ }
+
+ }
+ }
+
+ if (actionString == "remove") {
+ qCDebug(dcSerialPortMonitor()) << "[-]" << systemPath << serialNumberString << manufacturerString << descriptionString;
+ if (m_serialPorts.contains(systemPath)) {
+ SerialPort serialPort = m_serialPorts.take(systemPath);
+ qCDebug(dcSerialPortMonitor()) << "Removed" << serialPort.systemLocation();
+ emit serialPortRemoved(serialPort);
+ }
+ }
+ });
+
+ qCDebug(dcSerialPortMonitor()) << "Serial port monitor enabled successfully";
+ foreach (const SerialPort &serialPort, m_serialPorts) {
+ qCDebug(dcSerialPortMonitor()) << "-" << serialPort.systemLocation() << serialPort.description();
+ }
+ m_notifier->setEnabled(true);
+#else
+ m_timer = new QTimer(this);
+ m_timer->setInterval(5000);
+ m_timer->setSingleShot(false);
+ connect(m_timer, &QTimer::timeout, this, [=](){
+ QStringList availablePorts;
+ // Add a new adapter if not in the list already
+ foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) {
+ availablePorts.append(serialPortInfo.systemLocation());
+ if (!m_serialPorts.keys().contains(serialPortInfo.systemLocation())) {
+ qCDebug(dcSerialPortMonitor()) << "[+]" << serialPortInfo.systemLocation() << serialPortInfo.manufacturer() << serialPortInfo.description();
+ addSerialPortInternally(SerialPort(serialPortInfo));
+ }
+ }
+ // Remove adapters no longer available
+ foreach (const QString &systemLocation, m_serialPorts.keys()) {
+ if (!availablePorts.contains(systemLocation)) {
+ SerialPort serialPortInfo = m_serialPorts.take(systemLocation);
+ qCDebug(dcSerialPortMonitor()) << "[-]" << serialPortInfo.systemLocation() << serialPortInfo.manufacturer() << serialPortInfo.description();
+ emit serialPortRemoved(serialPortInfo);
+ }
+ }
+ });
+ m_timer->start();
+
+#endif
+}
+
+SerialPorts SerialPortMonitor::serialPorts() const
+{
+ return m_serialPorts.values();
+}
+
+bool SerialPortMonitor::serialPortAvailable(const QString &systemLocation) const
+{
+ return m_serialPorts.keys().contains(systemLocation);
+}
+
+void SerialPortMonitor::addSerialPortInternally(const SerialPort &serialPort)
+{
+ if (m_serialPorts.keys().contains(serialPort.systemLocation())) {
+ qCWarning(dcSerialPortMonitor()) << "Tried to add serial port but the port has alrady been added.";
+ return;
+ }
+
+ m_serialPorts.insert(serialPort.systemLocation(), serialPort);
+ emit serialPortAdded(serialPort);
+}
+
+}
diff --git a/libnymea-core/hardware/serialport/serialportmonitor.h b/libnymea-core/hardware/serialport/serialportmonitor.h
new file mode 100644
index 00000000..115b6b04
--- /dev/null
+++ b/libnymea-core/hardware/serialport/serialportmonitor.h
@@ -0,0 +1,145 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2021, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea.
+* This project including source code and documentation is protected by
+* copyright law, and remains the property of nymea GmbH. All rights, including
+* reproduction, publication, editing and translation, are reserved. The use of
+* this project is subject to the terms of a license agreement to be concluded
+* with nymea GmbH in accordance with the terms of use of nymea GmbH, available
+* under https://nymea.io/license
+*
+* GNU Lesser General Public License Usage
+* Alternatively, this project may be redistributed and/or modified under the
+* terms of the GNU Lesser General Public License as published by the Free
+* Software Foundation; version 3. This project is distributed in the hope that
+* it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this project. If not, see .
+*
+* For any further details and any questions please contact us under
+* contact@nymea.io or see our FAQ/Licensing Information on
+* https://nymea.io/license/faq
+*
+* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
+
+#ifndef SERIALPORTMONITOR_H
+#define SERIALPORTMONITOR_H
+
+#include
+#include
+#include
+#include
+
+#ifdef WITH_UDEV
+#include
+#include
+#else
+#include
+#endif
+
+namespace nymeaserver {
+
+class SerialPort : public QSerialPortInfo {
+ Q_GADGET
+ Q_PROPERTY(QString systemLocation READ systemLocation)
+ Q_PROPERTY(QString manufacturer READ manufacturer)
+ Q_PROPERTY(QString description READ description)
+ Q_PROPERTY(QString serialNumber READ serialNumber)
+
+public:
+ enum SerialPortParity {
+ SerialPortParityNoParity = 0,
+ SerialPortParityEvenParity = 2,
+ SerialPortParityOddParity = 3,
+ SerialPortParitySpaceParity = 4,
+ SerialPortParityMarkParity = 5,
+ SerialPortParityUnknownParity = -1
+ };
+ Q_ENUM(SerialPortParity)
+
+
+ enum SerialPortDataBits {
+ SerialPortDataBitsData5 = 5,
+ SerialPortDataBitsData6 = 6,
+ SerialPortDataBitsData7 = 7,
+ SerialPortDataBitsData8 = 8,
+ SerialPortDataBitsUnknownDataBits = -1
+ };
+ Q_ENUM(SerialPortDataBits)
+
+
+ enum SerialPortStopBits {
+ SerialPortStopBitsOneStop = 1,
+ SerialPortStopBitsOneAndHalfStop = 3,
+ SerialPortStopBitsTwoStop = 2,
+ SerialPortStopBitsUnknownStopBits = -1
+ };
+ Q_ENUM(SerialPortStopBits)
+
+ SerialPort() : QSerialPortInfo() { };
+ explicit SerialPort(const QSerialPortInfo &other) : QSerialPortInfo(other) { };
+
+};
+
+class SerialPorts : public QList
+{
+ Q_GADGET
+ Q_PROPERTY(int count READ count)
+
+public:
+ inline SerialPorts() = default;
+ inline SerialPorts(const QList &other) : QList(other) { };
+ inline bool hasSerialPort(const QString &serialPort) {
+ for (int i = 0; i < count(); i++) {
+ if (at(i).systemLocation() == serialPort) {
+ return true;
+ }
+ }
+ return false;
+ };
+
+ inline Q_INVOKABLE QVariant get(int index) const { return QVariant::fromValue(at(index)); };
+ inline Q_INVOKABLE void put(const QVariant &variant) { append(variant.value()); };
+};
+
+
+class SerialPortMonitor : public QObject
+{
+ Q_OBJECT
+public:
+ explicit SerialPortMonitor(QObject *parent = nullptr);
+
+ SerialPorts serialPorts() const;
+ bool serialPortAvailable(const QString &systemLocation) const;
+
+signals:
+ void serialPortAdded(const SerialPort &serialPort);
+ void serialPortRemoved(const SerialPort &serialPort);
+
+private:
+ QHash m_serialPorts;
+
+ void addSerialPortInternally(const SerialPort &serialPort);
+
+#ifdef WITH_UDEV
+ struct udev *m_udev = nullptr;
+ struct udev_monitor *m_monitor = nullptr;
+ QSocketNotifier *m_notifier = nullptr;
+#else
+ QTimer *m_timer = nullptr;
+#endif
+
+};
+
+}
+
+Q_DECLARE_METATYPE(nymeaserver::SerialPort)
+Q_DECLARE_METATYPE(nymeaserver::SerialPorts)
+
+#endif // SERIALPORTMONITOR_H
diff --git a/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp b/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp
index 36a75646..147c460e 100644
--- a/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp
+++ b/libnymea-core/jsonrpc/jsonrpcserverimplementation.cpp
@@ -75,6 +75,7 @@
#include "systemhandler.h"
#include "usershandler.h"
#include "zigbeehandler.h"
+#include "modbusrtuhandler.h"
#include
#include
@@ -599,6 +600,7 @@ void JsonRPCServerImplementation::setup()
registerHandler(new SystemHandler(NymeaCore::instance()->platform(), this));
registerHandler(new UsersHandler(NymeaCore::instance()->userManager(), this));
registerHandler(new ZigbeeHandler(NymeaCore::instance()->zigbeeManager(), this));
+ registerHandler(new ModbusRtuHandler(NymeaCore::instance()->modbusRtuManager(), this));
connect(NymeaCore::instance()->cloudManager(), &CloudManager::pairingReply, this, &JsonRPCServerImplementation::pairingFinished);
connect(NymeaCore::instance()->cloudManager(), &CloudManager::connectionStateChanged, this, &JsonRPCServerImplementation::onCloudConnectionStateChanged);
diff --git a/libnymea-core/jsonrpc/modbusrtuhandler.cpp b/libnymea-core/jsonrpc/modbusrtuhandler.cpp
index 535deb6b..1a210485 100644
--- a/libnymea-core/jsonrpc/modbusrtuhandler.cpp
+++ b/libnymea-core/jsonrpc/modbusrtuhandler.cpp
@@ -29,12 +29,140 @@
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "modbusrtuhandler.h"
+#include "modbus/modbusrtumanager.h"
+#include "hardware/serialport/serialportmonitor.h"
namespace nymeaserver {
-ModbusRtuHandler::ModbusRtuHandler(QObject *parent) : JsonHandler(parent)
+ModbusRtuHandler::ModbusRtuHandler(ModbusRtuManager *modbusRtuManager, QObject *parent) :
+ JsonHandler(parent),
+ m_modbusRtuManager(modbusRtuManager)
{
+ qRegisterMetaType();
+ registerEnum();
+ registerEnum();
+ registerEnum();
+ registerEnum();
+ registerObject();
+ QVariantMap modbusRtuMasterDescription;
+ modbusRtuMasterDescription.insert("modbusUuid", enumValueName(Uuid));
+ modbusRtuMasterDescription.insert("connected", enumValueName(Bool));
+ modbusRtuMasterDescription.insert("serialPort", enumValueName(String));
+ modbusRtuMasterDescription.insert("baudrate", enumValueName(Uint));
+ modbusRtuMasterDescription.insert("parity", enumRef());
+ modbusRtuMasterDescription.insert("stopBits", enumRef());
+ modbusRtuMasterDescription.insert("dataBits", enumRef());
+ registerObject("ModbusRtuMaster", modbusRtuMasterDescription);
+
+ QVariantMap params, returns;
+ QString description;
+
+ // GetSerialPorts
+ params.clear(); returns.clear();
+ description = "Get the list of available serial ports from the host system.";
+ returns.insert("serialPorts", objectRef());
+ registerMethod("GetSerialPorts", description, params, returns);
+
+ // SerialPortAdded notification
+ params.clear();
+ description = "Emitted whenever a serial port has been added to the system.";
+ params.insert("serialPort", objectRef());
+ registerNotification("SerialPortAdded", description, params);
+
+ // SerialPortRemoved notification
+ params.clear();
+ description = "Emitted whenever a serial port has been removed from the system.";
+ params.insert("serialPort", objectRef());
+ registerNotification("SerialPortRemoved", description, params);
+
+ // GetModbusRtuMasters
+ params.clear(); returns.clear();
+ description = "Get the list of configured modbus RTU masters.";
+ returns.insert("modbusRtuMasters", QVariantList() << objectRef("ModbusRtuMaster"));
+ registerMethod("GetModbusRtuMasters", description, params, returns);
+
+ // ModbusRtuMasterAdded notification
+ params.clear();
+ description = "Emitted whenever a new modbus RTU master has been added to the system.";
+ params.insert("modbusRtuMaster", objectRef("ModbusRtuMaster"));
+ registerNotification("ModbusRtuMasterAdded", description, params);
+
+ // ModbusRtuMasterRemoved notification
+ params.clear();
+ description = "Emitted whenever a new modbus RTU master has been removed from the system.";
+ params.insert("modbusUuid", enumValueName(Uuid));
+ registerNotification("ModbusRtuMasterRemoved", description, params);
+
+ // ModbusRtuMasterChanged notification
+ params.clear();
+ description = "Emitted whenever a new modbus RTU master has been changed to the system.";
+ params.insert("modbusRtuMaster", objectRef("ModbusRtuMaster"));
+ registerNotification("ModbusRtuMasterChanged", description, params);
+
+ // AddModbusRtuMaster
+ params.clear(); returns.clear();
+ description = "Add a new modbus RTU master with the given configuration.";
+ params.insert("serialPort", enumValueName(String));
+ params.insert("baudrate", enumValueName(Uint));
+ params.insert("parity", enumRef());
+ params.insert("dataBits", enumRef());
+ params.insert("stopBits", enumRef());
+ returns.insert("o:modbusUuid", enumValueName(Uuid));
+ returns.insert("modbusError", enumRef());
+ registerMethod("AddModbusRtuMaster", description, params, returns);
+
+ // RemoveModbusRtuMaster
+ params.clear(); returns.clear();
+ description = "Remove the modbus RTU master with the given modbus UUID.";
+ params.insert("modbusUuid", enumValueName(Uuid));
+ returns.insert("modbusError", enumRef());
+ registerMethod("RemoveModbusRtuMaster", description, params, returns);
+
+ // ReconfigureModbusRtuMaster
+ params.clear(); returns.clear();
+ description = "Rconfigure the modbus RTU master with the given UUID and configuration.";
+ params.insert("modbusUuid", enumValueName(Uuid));
+ params.insert("serialPort", enumValueName(String));
+ params.insert("baudrate", enumValueName(Uint));
+ params.insert("parity", enumRef());
+ params.insert("dataBits", enumRef());
+ params.insert("stopBits", enumRef());
+ returns.insert("modbusError", enumRef());
+ registerMethod("ReconfigureModbusRtuMaster", description, params, returns);
+
+ // Serial port monitor
+ connect(modbusRtuManager->serialPortMonitor(), &SerialPortMonitor::serialPortAdded, this, [=](const SerialPort &serialPort){
+ QVariantMap params;
+ params.insert("serialPort", pack(serialPort));
+ emit SerialPortAdded(params);
+ });
+
+ connect(modbusRtuManager->serialPortMonitor(), &SerialPortMonitor::serialPortRemoved, this, [=](const SerialPort &serialPort){
+ QVariantMap params;
+ params.insert("serialPort", pack(serialPort));
+ emit SerialPortAdded(params);
+ });
+
+
+ // Modbus manager
+ connect(modbusRtuManager, &ModbusRtuManager::modbusRtuMasterAdded, this, [=](ModbusRtuMaster *modbusRtuMaster){
+ QVariantMap params;
+ params.insert("modbusRtuMaster", packModbusRtuMaster(modbusRtuMaster));
+ emit ModbusRtuMasterAdded(params);
+ });
+
+ connect(modbusRtuManager, &ModbusRtuManager::modbusRtuMasterChanged, this, [=](ModbusRtuMaster *modbusRtuMaster){
+ QVariantMap params;
+ params.insert("modbusRtuMaster", packModbusRtuMaster(modbusRtuMaster));
+ emit ModbusRtuMasterChanged(params);
+ });
+
+ connect(modbusRtuManager, &ModbusRtuManager::modbusRtuMasterRemoved, this, [=](ModbusRtuMaster *modbusRtuMaster){
+ QVariantMap params;
+ params.insert("modbusUuid", modbusRtuMaster->modbusUuid());
+ emit ModbusRtuMasterRemoved(params);
+ });
}
QString ModbusRtuHandler::name() const
@@ -42,4 +170,88 @@ QString ModbusRtuHandler::name() const
return "ModbusRtu";
}
+JsonReply *ModbusRtuHandler::GetSerialPorts(const QVariantMap ¶ms)
+{
+ Q_UNUSED(params)
+
+ QVariantMap returnMap;
+ QVariantList portList;
+ foreach (const SerialPort &serialPort, m_modbusRtuManager->serialPortMonitor()->serialPorts()) {
+ portList << pack(serialPort);
+ }
+ returnMap.insert("serialPorts", portList);
+ return createReply(returnMap);
+}
+
+JsonReply *ModbusRtuHandler::GetModbusRtuMasters(const QVariantMap ¶ms)
+{
+ Q_UNUSED(params)
+
+ QVariantMap returnMap;
+ QVariantList modbusList;
+ foreach (ModbusRtuMaster *modbusMaster, m_modbusRtuManager->modbusRtuMasters()) {
+ modbusList << packModbusRtuMaster(modbusMaster);
+ }
+ returnMap.insert("modbusRtuMasters", modbusList);
+ return createReply(returnMap);
+}
+
+JsonReply *ModbusRtuHandler::AddModbusRtuMaster(const QVariantMap ¶ms)
+{
+ QString serialPort = params.value("serialPort").toString();
+ qint32 baudrate = params.value("baudrate").toUInt();
+ QSerialPort::Parity parity = static_cast(enumNameToValue(params.value("parity").toString()));
+ QSerialPort::StopBits stopBits = static_cast(enumNameToValue(params.value("stopBits").toString()));
+ QSerialPort::DataBits dataBits = static_cast(enumNameToValue(params.value("dataBits").toString()));
+
+ QPair result = m_modbusRtuManager->addNewModbusRtuMaster(serialPort, baudrate, parity, dataBits, stopBits);
+
+ QVariantMap returnMap;
+ returnMap.insert("modbusError", enumValueName(result.first));
+ if (result.first == ModbusRtuManager::ModbusRtuErrorNoError) {
+ returnMap.insert("modbusUuid", result.second);
+ }
+
+ return createReply(returnMap);
+}
+
+JsonReply *ModbusRtuHandler::RemoveModbusRtuMaster(const QVariantMap ¶ms)
+{
+ QUuid modbusUuid = params.value("modbusUuid").toUuid();
+
+ ModbusRtuManager::ModbusRtuError result = m_modbusRtuManager->removeModbusRtuMaster(modbusUuid);
+ QVariantMap returnMap;
+ returnMap.insert("modbusError", enumValueName(result));
+ return createReply(returnMap);
+}
+
+JsonReply *ModbusRtuHandler::ReconfigureModbusRtuMaster(const QVariantMap ¶ms)
+{
+ QUuid modbusUuid = params.value("modbusUuid").toUuid();
+ QString serialPort = params.value("serialPort").toString();
+ qint32 baudrate = params.value("baudrate").toUInt();
+ QSerialPort::Parity parity = static_cast(enumNameToValue(params.value("parity").toString()));
+ QSerialPort::StopBits stopBits = static_cast(enumNameToValue(params.value("stopBits").toString()));
+ QSerialPort::DataBits dataBits = static_cast(enumNameToValue(params.value("dataBits").toString()));
+
+ ModbusRtuManager::ModbusRtuError result = m_modbusRtuManager->reconfigureModbusRtuMaster(modbusUuid, serialPort, baudrate, parity, dataBits, stopBits);
+ QVariantMap returnMap;
+ returnMap.insert("modbusError", enumValueName(result));
+ return createReply(returnMap);
+}
+
+
+QVariantMap ModbusRtuHandler::packModbusRtuMaster(ModbusRtuMaster *modbusRtuMaster)
+{
+ QVariantMap modbusRtuMasterMap;
+ modbusRtuMasterMap.insert("modbusUuid", modbusRtuMaster->modbusUuid());
+ modbusRtuMasterMap.insert("connected", modbusRtuMaster->connected());
+ modbusRtuMasterMap.insert("serialPort", modbusRtuMaster->serialPort());
+ modbusRtuMasterMap.insert("baudrate", modbusRtuMaster->baudrate());
+ modbusRtuMasterMap.insert("parity", enumValueName(static_cast(modbusRtuMaster->parity())));
+ modbusRtuMasterMap.insert("stopBits", enumValueName(static_cast(modbusRtuMaster->stopBits())));
+ modbusRtuMasterMap.insert("dataBits", enumValueName(static_cast(modbusRtuMaster->dataBits())));
+ return modbusRtuMasterMap;
+}
+
}
diff --git a/libnymea-core/jsonrpc/modbusrtuhandler.h b/libnymea-core/jsonrpc/modbusrtuhandler.h
index a0242525..171d3540 100644
--- a/libnymea-core/jsonrpc/modbusrtuhandler.h
+++ b/libnymea-core/jsonrpc/modbusrtuhandler.h
@@ -34,19 +34,39 @@
#include
#include "jsonrpc/jsonhandler.h"
+#include "hardware/modbus/modbusrtumaster.h"
namespace nymeaserver {
+class ModbusRtuManager;
+
class ModbusRtuHandler : public JsonHandler
{
Q_OBJECT
public:
- explicit ModbusRtuHandler(QObject *parent = nullptr);
+ explicit ModbusRtuHandler(ModbusRtuManager *modbusRtuManager, QObject *parent = nullptr);
QString name() const override;
-signals:
+ Q_INVOKABLE JsonReply *GetSerialPorts(const QVariantMap ¶ms);
+ Q_INVOKABLE JsonReply *GetModbusRtuMasters(const QVariantMap ¶ms);
+ Q_INVOKABLE JsonReply *AddModbusRtuMaster(const QVariantMap ¶ms);
+ Q_INVOKABLE JsonReply *RemoveModbusRtuMaster(const QVariantMap ¶ms);
+ Q_INVOKABLE JsonReply *ReconfigureModbusRtuMaster(const QVariantMap ¶ms);
+
+signals:
+ void SerialPortAdded(const QVariantMap ¶ms);
+ void SerialPortRemoved(const QVariantMap ¶ms);
+
+ void ModbusRtuMasterAdded(const QVariantMap ¶ms);
+ void ModbusRtuMasterRemoved(const QVariantMap ¶ms);
+ void ModbusRtuMasterChanged(const QVariantMap ¶ms);
+
+private:
+ ModbusRtuManager *m_modbusRtuManager = nullptr;
+
+ QVariantMap packModbusRtuMaster(ModbusRtuMaster *modbusRtuMaster);
};
}
diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro
index 1668715f..eb17feef 100644
--- a/libnymea-core/libnymea-core.pro
+++ b/libnymea-core/libnymea-core.pro
@@ -47,6 +47,14 @@ packagesExist(Qt5SerialBus) {
message("Qt5SerialBus package not found. Building without QtSerialBus support.")
}
+# Note: udev is not available on all platforms
+packagesExist(libudev) {
+ message("Build with udev support")
+ PKGCONFIG += libudev
+ DEFINES += WITH_UDEV
+} else {
+ message("Build without udev support.")
+}
target.path = $$[QT_INSTALL_LIBS]
INSTALLS += target
@@ -57,6 +65,7 @@ RESOURCES += $$top_srcdir/icons.qrc \
HEADERS += nymeacore.h \
+ hardware/serialport/serialportmonitor.h \
integrations/apikeysprovidersloader.h \
integrations/plugininfocache.h \
integrations/python/pyapikeystorage.h \
@@ -156,6 +165,7 @@ HEADERS += nymeacore.h \
SOURCES += nymeacore.cpp \
+ hardware/serialport/serialportmonitor.cpp \
integrations/apikeysprovidersloader.cpp \
integrations/plugininfocache.cpp \
integrations/thingmanagerimplementation.cpp \
diff --git a/libnymea-core/modbus/modbusrtumanager.cpp b/libnymea-core/modbus/modbusrtumanager.cpp
index 241f2e3c..55b20a37 100644
--- a/libnymea-core/modbus/modbusrtumanager.cpp
+++ b/libnymea-core/modbus/modbusrtumanager.cpp
@@ -33,32 +33,28 @@
#include "loggingcategories.h"
#include "modbusrtumasterimpl.h"
+#include "hardware/serialport/serialportmonitor.h"
NYMEA_LOGGING_CATEGORY(dcModbusRtu, "ModbusRtu")
namespace nymeaserver {
-ModbusRtuManager::ModbusRtuManager(QObject *parent) : QObject(parent)
+ModbusRtuManager::ModbusRtuManager(SerialPortMonitor *serialPortMonitor, QObject *parent) :
+ QObject(parent),
+ m_serialPortMonitor(serialPortMonitor)
{
- // Load available serial ports
- updateSerialPorts();
-
// Load uart configurations
loadRtuMasters();
- // Enable autoconnect for each modbus rtu master
- m_timer = new QTimer(this);
- m_timer->setInterval(5000);
- m_timer->setSingleShot(false);
- connect(m_timer, &QTimer::timeout, this, [=](){
- // Update serial port list
- updateSerialPorts();
+ connect(m_serialPortMonitor, &SerialPortMonitor::serialPortAdded, this, [=](const QSerialPortInfo &serialPortInfo){
+ qCDebug(dcModbusRtu()) << "Serial port added. Verify modbus RTU masters...";
- // Check if we have to reconnect a device
+ // Check if we have to reconnect any modbus RTU masters
foreach (ModbusRtuMaster *modbusMaster, m_modbusRtuMasters.values()) {
ModbusRtuMasterImpl *modbusMasterImpl = qobject_cast(modbusMaster);
- if (!modbusMasterImpl->connected()) {
+ // Try only to reconnect if the added serial port matches a disconnected modbus RTU master
+ if (!modbusMasterImpl->connected() && modbusMasterImpl->serialPort() == serialPortInfo.systemLocation()) {
if (!modbusMasterImpl->connectDevice()) {
qCDebug(dcModbusRtu()) << "Reconnect" << modbusMaster << "failed.";
} else {
@@ -67,8 +63,11 @@ ModbusRtuManager::ModbusRtuManager(QObject *parent) : QObject(parent)
}
}
});
+}
- m_timer->start();
+SerialPortMonitor *ModbusRtuManager::serialPortMonitor() const
+{
+ return m_serialPortMonitor;
}
QList ModbusRtuManager::modbusRtuMasters() const
@@ -90,30 +89,37 @@ ModbusRtuMaster *ModbusRtuManager::getModbusRtuMaster(const QUuid &modbusUuid)
return nullptr;
}
-QPair ModbusRtuManager::addNewModbusRtuMaster(const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits)
+QPair ModbusRtuManager::addNewModbusRtuMaster(const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits)
{
// Check if the serial port exists
-
- // Check if the serial port is not occupied
+ if (!m_serialPortMonitor->serialPortAvailable(serialPort)) {
+ qCWarning(dcModbusRtu()) << "Cannot add new modbus RTU master because the serial port" << serialPort << "is not available any more";
+ return QPair(ModbusRtuErrorHardwareNotFound, QUuid());
+ }
QUuid modbusUuid = QUuid::createUuid();
ModbusRtuMasterImpl *modbusMaster = new ModbusRtuMasterImpl(modbusUuid, serialPort, baudrate, parity, dataBits, stopBits, this);
ModbusRtuMaster *modbus = qobject_cast(modbusMaster);
- qCDebug(dcModbusRtu()) << "Adding new" << qobject_cast(modbusMaster) << parity << dataBits << stopBits;
+ qCDebug(dcModbusRtu()) << "Adding new" << modbus << parity << dataBits << stopBits;
- // Connect the modbus master bus;
- m_modbusRtuMasters.insert(modbusUuid, modbus);
- emit modbusRtuMasterAdded(modbus);
+ // Note: We could add the modbus master event if a connection is currently not possible...not sure yet
+ if (!modbusMaster->connectDevice()) {
+ qCWarning(dcModbusRtu()) << "Failed to add new modbus RTU master. Could not connect to" << modbus << parity << dataBits << stopBits;
+ modbusMaster->deleteLater();
+ return QPair(ModbusRtuErrorConnectionFailed, QUuid());
+ }
+
+ addModbusRtuMasterInternally(modbusMaster);
saveModbusRtuMaster(modbus);
- return QPair(ErrorNoError, modbusUuid);
+ return QPair(ModbusRtuErrorNoError, modbusUuid);
}
-ModbusRtuManager::Error ModbusRtuManager::reconfigureRtuMaster(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits)
+ModbusRtuManager::ModbusRtuError ModbusRtuManager::reconfigureModbusRtuMaster(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits)
{
if (!m_modbusRtuMasters.contains(modbusUuid)) {
qCWarning(dcModbusRtu()) << "Could not reconfigure modbus RTU master because no resource could be found with uuid" << modbusUuid.toString();
- return ErrorNotFound;
+ return ModbusRtuErrorUuidNotFound;
}
ModbusRtuMasterImpl *modbusMaster = qobject_cast(m_modbusRtuMasters.value(modbusUuid));
@@ -131,38 +137,33 @@ ModbusRtuManager::Error ModbusRtuManager::reconfigureRtuMaster(const QUuid &modb
// Connect again
if (!modbusMaster->connectDevice()) {
qCWarning(dcModbusRtu()) << "Failed to connect to" << m_modbusRtuMasters.value(modbusUuid);
- return ErrorConnectionFailed;
+ emit modbusRtuMasterChanged(m_modbusRtuMasters.value(modbusUuid));
+ return ModbusRtuErrorConnectionFailed;
}
emit modbusRtuMasterChanged(m_modbusRtuMasters.value(modbusUuid));
qCDebug(dcModbusRtu()) << "Reconfigured successfully" << m_modbusRtuMasters.value(modbusUuid);
- return ErrorNoError;
+ return ModbusRtuErrorNoError;
}
-
-ModbusRtuManager::Error ModbusRtuManager::removeModbusRtuMaster(const QUuid &modbusUuid)
+ModbusRtuManager::ModbusRtuError ModbusRtuManager::removeModbusRtuMaster(const QUuid &modbusUuid)
{
- ModbusRtuMasterImpl *modbusMaster = qobject_cast(m_modbusRtuMasters.value(modbusUuid));
- if (!modbusMaster) {
+ if (!m_modbusRtuMasters.contains(modbusUuid)) {
qCWarning(dcModbusRtu()) << "Could not remove modbus RTU master because no resource could be found with uuid" << modbusUuid.toString();
- return ErrorNotFound;
+ return ModbusRtuErrorUuidNotFound;
}
+ ModbusRtuMasterImpl *modbusMaster = qobject_cast(m_modbusRtuMasters.take(modbusUuid));
qCDebug(dcModbusRtu()) << "Removing modbus RTU master" << qobject_cast(modbusMaster);
+ modbusMaster->disconnectDevice();
+ modbusMaster->deleteLater();
+
emit modbusRtuMasterRemoved(modbusMaster);
- modbusMaster->deleteLater();
- return ErrorNoError;
+ return ModbusRtuErrorNoError;
}
-void ModbusRtuManager::updateSerialPorts()
-{
- // Check if serial port added or removed
-
-}
-
-
void ModbusRtuManager::loadRtuMasters()
{
NymeaSettings settings(NymeaSettings::SettingsRoleModbusRtu);
@@ -178,11 +179,7 @@ void ModbusRtuManager::loadRtuMasters()
QSerialPort::StopBits stopBits = static_cast(settings.value("stopBits").toInt());
settings.endGroup(); // uuid
- ModbusRtuMasterImpl *modbus = new ModbusRtuMasterImpl(QUuid(uuidString), serialPort, baudrate, parity, dataBits, stopBits, this);
- ModbusRtuMaster *modbusRtuMaster = qobject_cast(modbus);
- qCDebug(dcModbusRtu()) << "Loaded" << modbusRtuMaster;
- m_modbusRtuMasters.insert(modbusRtuMaster->modbusUuid(), modbusRtuMaster);
- emit modbusRtuMasterAdded(modbusRtuMaster);
+ addModbusRtuMasterInternally(new ModbusRtuMasterImpl(QUuid(uuidString), serialPort, baudrate, parity, dataBits, stopBits, this));
}
settings.endGroup(); // ModbusRtuMasters
@@ -191,6 +188,7 @@ void ModbusRtuManager::loadRtuMasters()
void ModbusRtuManager::saveModbusRtuMaster(ModbusRtuMaster *modbusRtuMaster)
{
NymeaSettings settings(NymeaSettings::SettingsRoleModbusRtu);
+ qCDebug(dcModbusRtu()) << "Saving" << modbusRtuMaster << "to" << settings.fileName();
settings.beginGroup("ModbusRtuMasters");
settings.beginGroup(modbusRtuMaster->modbusUuid().toString());
settings.setValue("serialPort", modbusRtuMaster->serialPort());
@@ -202,4 +200,18 @@ void ModbusRtuManager::saveModbusRtuMaster(ModbusRtuMaster *modbusRtuMaster)
settings.endGroup(); // ModbusRtuMasters
}
+void ModbusRtuManager::addModbusRtuMasterInternally(ModbusRtuMasterImpl *modbusRtuMaster)
+{
+ ModbusRtuMaster *modbusMaster = qobject_cast(modbusRtuMaster);
+ qCDebug(dcModbusRtu()) << "Adding" << modbusMaster;
+ m_modbusRtuMasters.insert(modbusMaster->modbusUuid(), modbusMaster);
+
+ connect(modbusMaster, &ModbusRtuMaster::connectedChanged, this, [=](bool connected){
+ qCDebug(dcModbusRtu()) << modbusMaster << (connected ? "connected" : "disconnected");
+ emit modbusRtuMasterChanged(modbusMaster);
+ });
+
+ emit modbusRtuMasterAdded(modbusMaster);
+}
+
}
diff --git a/libnymea-core/modbus/modbusrtumanager.h b/libnymea-core/modbus/modbusrtumanager.h
index fc01aa32..f4529ec4 100644
--- a/libnymea-core/modbus/modbusrtumanager.h
+++ b/libnymea-core/modbus/modbusrtumanager.h
@@ -40,27 +40,33 @@
namespace nymeaserver {
+class SerialPortMonitor;
+class ModbusRtuMasterImpl;
+
class ModbusRtuManager : public QObject
{
Q_OBJECT
public:
- enum Error {
- ErrorNoError,
- ErrorNotFound,
- ErrorConnectionFailed
+ enum ModbusRtuError {
+ ModbusRtuErrorNoError,
+ ModbusRtuErrorUuidNotFound,
+ ModbusRtuErrorHardwareNotFound,
+ ModbusRtuErrorConnectionFailed
};
- Q_ENUM(Error)
+ Q_ENUM(ModbusRtuError)
- explicit ModbusRtuManager(QObject *parent = nullptr);
+ explicit ModbusRtuManager(SerialPortMonitor *serialPortMonitor, QObject *parent = nullptr);
~ModbusRtuManager() = default;
+ SerialPortMonitor *serialPortMonitor() const;
+
QList modbusRtuMasters() const;
bool hasModbusRtuMaster(const QUuid &modbusUuid) const;
ModbusRtuMaster *getModbusRtuMaster(const QUuid &modbusUuid);
- QPair addNewModbusRtuMaster(const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits);
- Error reconfigureRtuMaster(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits);
- Error removeModbusRtuMaster(const QUuid &modbusUuid);
+ QPair addNewModbusRtuMaster(const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits);
+ ModbusRtuError reconfigureModbusRtuMaster(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits);
+ ModbusRtuError removeModbusRtuMaster(const QUuid &modbusUuid);
signals:
void modbusRtuMasterAdded(ModbusRtuMaster *modbusRtuMaster);
@@ -69,10 +75,13 @@ signals:
private:
QHash m_modbusRtuMasters;
+ SerialPortMonitor *m_serialPortMonitor = nullptr;
void loadRtuMasters();
void saveModbusRtuMaster(ModbusRtuMaster *modbusRtuMaster);
+ void addModbusRtuMasterInternally(ModbusRtuMasterImpl *modbusRtuMaster);
+
};
}
diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp
index 4f8d9330..ca7cda79 100644
--- a/libnymea-core/nymeacore.cpp
+++ b/libnymea-core/nymeacore.cpp
@@ -54,6 +54,7 @@
#include "zigbee/zigbeemanager.h"
#include "modbus/modbusrtumanager.h"
+#include "hardware/serialport/serialportmonitor.h"
#include
@@ -110,8 +111,11 @@ void NymeaCore::init(const QStringList &additionalInterfaces) {
qCDebug(dcCore()) << "Create Zigbee Manager";
m_zigbeeManager = new ZigbeeManager(this);
+ qCDebug(dcCore()) << "Create Serial Port Monitor";
+ m_serialPortMonitor = new SerialPortMonitor(this);
+
qCDebug(dcCore()) << "Create Modbus RTU Manager";
- m_modbusRtuManager = new ModbusRtuManager(this);
+ m_modbusRtuManager = new ModbusRtuManager(m_serialPortMonitor, this);
qCDebug(dcCore) << "Creating Hardware Manager";
m_hardwareManager = new HardwareManagerImplementation(m_platform, m_serverManager->mqttBroker(), m_zigbeeManager, m_modbusRtuManager, this);
diff --git a/libnymea-core/nymeacore.h b/libnymea-core/nymeacore.h
index 2514f7d4..b67ac29a 100644
--- a/libnymea-core/nymeacore.h
+++ b/libnymea-core/nymeacore.h
@@ -68,6 +68,7 @@ class ScriptEngine;
class CloudManager;
class ZigbeeManager;
class ModbusRtuManager;
+class SerialPortMonitor;
class NymeaCore : public QObject
{
@@ -153,6 +154,7 @@ private:
System *m_system;
ExperienceManager *m_experienceManager;
ZigbeeManager *m_zigbeeManager;
+ SerialPortMonitor *m_serialPortMonitor;
ModbusRtuManager *m_modbusRtuManager;
QList m_executingRules;