diff --git a/debian/control b/debian/control
index 0581cb77..62258bd4 100644
--- a/debian/control
+++ b/debian/control
@@ -26,6 +26,8 @@ Build-Depends: debhelper (>= 9.0.0),
qttools5-dev-tools,
qtconnectivity5-dev,
qtdeclarative5-dev,
+ libqt5serialport5-dev,
+ libqt5serialbus5-dev
Package: nymea
Architecture: any
diff --git a/libnymea-core/hardware/modbus/modbusrtuhardwareresourceimplementation.cpp b/libnymea-core/hardware/modbus/modbusrtuhardwareresourceimplementation.cpp
new file mode 100644
index 00000000..501691bb
--- /dev/null
+++ b/libnymea-core/hardware/modbus/modbusrtuhardwareresourceimplementation.cpp
@@ -0,0 +1,124 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 "modbusrtuhardwareresourceimplementation.h"
+#include "loggingcategories.h"
+#include "nymeasettings.h"
+#include "hardware/modbus/modbusrtumanager.h"
+
+NYMEA_LOGGING_CATEGORY(dcModbusRtuResource, "ModbusRtuResource")
+
+namespace nymeaserver {
+
+ModbusRtuHardwareResourceImplementation::ModbusRtuHardwareResourceImplementation(ModbusRtuManager *modbusRtuManager, QObject *parent) :
+ ModbusRtuHardwareResource(parent),
+ m_modbusRtuManager(modbusRtuManager)
+{
+ connect(m_modbusRtuManager, &ModbusRtuManager::modbusRtuMasterAdded, this, [=](ModbusRtuMaster *modbusRtuMaster){
+ emit modbusRtuMasterAdded(modbusRtuMaster->modbusUuid());
+ });
+
+ connect(m_modbusRtuManager, &ModbusRtuManager::modbusRtuMasterRemoved, this, [=](ModbusRtuMaster *modbusRtuMaster){
+ emit modbusRtuMasterRemoved(modbusRtuMaster->modbusUuid());
+ });
+
+ connect(m_modbusRtuManager, &ModbusRtuManager::modbusRtuMasterChanged, this, [=](ModbusRtuMaster *modbusRtuMaster){
+ emit modbusRtuMasterChanged(modbusRtuMaster->modbusUuid());
+ });
+}
+
+QList ModbusRtuHardwareResourceImplementation::modbusRtuMasters() const
+{
+ return m_modbusRtuManager->modbusRtuMasters();
+}
+
+bool ModbusRtuHardwareResourceImplementation::hasModbusRtuMaster(const QUuid &modbusUuid) const
+{
+ return m_modbusRtuManager->hasModbusRtuMaster(modbusUuid);
+}
+
+ModbusRtuMaster *ModbusRtuHardwareResourceImplementation::getModbusRtuMaster(const QUuid &modbusUuid) const
+{
+ return m_modbusRtuManager->getModbusRtuMaster(modbusUuid);
+}
+
+bool ModbusRtuHardwareResourceImplementation::available() const
+{
+ return m_modbusRtuManager->supported();
+}
+
+bool ModbusRtuHardwareResourceImplementation::enabled() const
+{
+ return m_enabled;
+}
+
+bool ModbusRtuHardwareResourceImplementation::enable()
+{
+ qCWarning(dcModbusRtuResource()) << "Enable hardware resource. Not implemented yet.";
+
+ // TODO: enable all modbus clients
+
+ return true;
+}
+
+bool ModbusRtuHardwareResourceImplementation::disable()
+{
+ qCWarning(dcModbusRtuResource()) << "Disable hardware resource. Not implemented yet.";
+
+ // TODO: disable all modbus clients
+
+ return true;
+}
+
+void ModbusRtuHardwareResourceImplementation::setEnabled(bool enabled)
+{
+ qCDebug(dcModbusRtuResource()) << "Set" << (enabled ? "enabled" : "disabled");
+ if (m_enabled && enabled) {
+ qCDebug(dcModbusRtuResource()) << "Already enabled.";
+ return;
+ } else if (!m_enabled && !enabled) {
+ qCDebug(dcModbusRtuResource()) << "Already disabled.";
+ return;
+ }
+
+ bool success = false;
+ if (enabled) {
+ success = enable();
+ } else {
+ success = disable();
+ }
+
+ if (success) {
+ m_enabled = enabled;
+ emit enabledChanged(m_enabled);
+ }
+}
+
+}
diff --git a/libnymea-core/hardware/modbus/modbusrtuhardwareresourceimplementation.h b/libnymea-core/hardware/modbus/modbusrtuhardwareresourceimplementation.h
new file mode 100644
index 00000000..73e480dc
--- /dev/null
+++ b/libnymea-core/hardware/modbus/modbusrtuhardwareresourceimplementation.h
@@ -0,0 +1,72 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 MODBUSRTUHARDWARERESOURCEIMPLEMENTATION_H
+#define MODBUSRTUHARDWARERESOURCEIMPLEMENTATION_H
+
+#include
+
+#include "hardware/modbus/modbusrtumanager.h"
+#include "hardware/modbus/modbusrtumaster.h"
+#include "hardware/modbus/modbusrtuhardwareresource.h"
+
+namespace nymeaserver {
+
+class ModbusRtuHardwareResourceImplementation : public ModbusRtuHardwareResource
+{
+ Q_OBJECT
+public:
+ explicit ModbusRtuHardwareResourceImplementation(ModbusRtuManager *modbusRtuManager, QObject *parent = nullptr);
+ ~ModbusRtuHardwareResourceImplementation() override = default;
+
+ QList modbusRtuMasters() const override;
+ bool hasModbusRtuMaster(const QUuid &modbusUuid) const override;
+ ModbusRtuMaster *getModbusRtuMaster(const QUuid &modbusUuid) const override;
+
+ bool available() const override;
+ bool enabled() const override;
+
+public slots:
+ bool enable();
+ bool disable();
+
+protected:
+ void setEnabled(bool enabled) override;
+
+private:
+ ModbusRtuManager *m_modbusRtuManager = nullptr;
+ bool m_available = false;
+ bool m_enabled = false;
+
+};
+
+}
+
+#endif // MODBUSRTUHARDWARERESOURCEIMPLEMENTATION_H
diff --git a/libnymea-core/hardware/modbus/modbusrtumanager.cpp b/libnymea-core/hardware/modbus/modbusrtumanager.cpp
new file mode 100644
index 00000000..d2cdafa4
--- /dev/null
+++ b/libnymea-core/hardware/modbus/modbusrtumanager.cpp
@@ -0,0 +1,270 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 "modbusrtumanager.h"
+#include "nymeasettings.h"
+#include "loggingcategories.h"
+
+#include "modbusrtumasterimpl.h"
+#include "hardware/serialport/serialportmonitor.h"
+
+NYMEA_LOGGING_CATEGORY(dcModbusRtu, "ModbusRtu")
+
+namespace nymeaserver {
+
+ModbusRtuManager::ModbusRtuManager(SerialPortMonitor *serialPortMonitor, QObject *parent) :
+ QObject(parent),
+ m_serialPortMonitor(serialPortMonitor)
+{
+ // Load uart configurations
+ loadRtuMasters();
+
+ connect(m_serialPortMonitor, &SerialPortMonitor::serialPortAdded, this, [=](const QSerialPortInfo &serialPortInfo){
+ qCDebug(dcModbusRtu()) << "Serial port added. Verify modbus RTU masters...";
+
+ // Check if we have to reconnect any modbus RTU masters
+ foreach (ModbusRtuMaster *modbusMaster, m_modbusRtuMasters.values()) {
+ ModbusRtuMasterImpl *modbusMasterImpl = qobject_cast(modbusMaster);
+
+ // 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 {
+ qCDebug(dcModbusRtu()) << "Reconnected" << modbusMaster << "successfully.";
+ }
+ }
+ }
+ });
+
+ // Try to connect the modbus rtu masters
+ foreach (ModbusRtuMaster *modbusMaster, m_modbusRtuMasters.values()) {
+ ModbusRtuMasterImpl *modbusMasterImpl = qobject_cast(modbusMaster);
+ if (!modbusMasterImpl->connectDevice()) {
+ qCWarning(dcModbusRtu()) << "Failed to connect modbus RTU master. Could not connect to" << modbusMaster;
+ }
+ }
+
+}
+
+SerialPortMonitor *ModbusRtuManager::serialPortMonitor() const
+{
+ return m_serialPortMonitor;
+}
+
+bool ModbusRtuManager::supported() const
+{
+#ifdef WITH_QTSERIALBUS
+ return true;
+#else
+ return false;
+#endif
+}
+
+QList ModbusRtuManager::modbusRtuMasters() const
+{
+ return m_modbusRtuMasters.values();
+}
+
+bool ModbusRtuManager::hasModbusRtuMaster(const QUuid &modbusUuid) const
+{
+ return m_modbusRtuMasters.value(modbusUuid) != nullptr;
+}
+
+ModbusRtuMaster *ModbusRtuManager::getModbusRtuMaster(const QUuid &modbusUuid)
+{
+ if (hasModbusRtuMaster(modbusUuid)) {
+ return m_modbusRtuMasters.value(modbusUuid);
+ }
+
+ return nullptr;
+}
+
+QPair ModbusRtuManager::addNewModbusRtuMaster(const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout)
+{
+ if (!supported()) {
+ qCWarning(dcModbusRtu()) << "Cannot add new modbus RTU master because serialbus is not suppoerted on this platform.";
+ return QPair(ModbusRtuErrorNotSupported, QUuid());
+ }
+
+ // Check if the serial port exists
+ 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, numberOfRetries, timeout, this);
+ ModbusRtuMaster *modbus = qobject_cast(modbusMaster);
+ qCDebug(dcModbusRtu()) << "Adding new" << modbus << parity << dataBits << stopBits;
+
+ // 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(ModbusRtuErrorNoError, modbusUuid);
+}
+
+ModbusRtuManager::ModbusRtuError ModbusRtuManager::reconfigureModbusRtuMaster(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout)
+{
+ if (!supported()) {
+ qCWarning(dcModbusRtu()) << "Cannot reconfigure modbus RTU master because serialbus is not suppoerted on this platform.";
+ return ModbusRtuErrorNotSupported;
+ }
+
+ if (!m_modbusRtuMasters.contains(modbusUuid)) {
+ qCWarning(dcModbusRtu()) << "Could not reconfigure modbus RTU master because no resource could be found with uuid" << modbusUuid.toString();
+ return ModbusRtuErrorUuidNotFound;
+ }
+
+ // Take the modbus masters
+ ModbusRtuMasterImpl *modbusMaster = qobject_cast(m_modbusRtuMasters.value(modbusUuid));
+
+ // Disconnect
+ modbusMaster->disconnectDevice();
+
+ // Reconfigure
+ modbusMaster->setSerialPort(serialPort);
+ modbusMaster->setBaudrate(baudrate);
+ modbusMaster->setParity(parity);
+ modbusMaster->setDataBits(dataBits);
+ modbusMaster->setStopBits(stopBits);
+ modbusMaster->setNumberOfRetries(numberOfRetries);
+ modbusMaster->setTimeout(timeout);
+
+ // Connect again
+ if (!modbusMaster->connectDevice()) {
+ qCWarning(dcModbusRtu()) << "Failed to connect to" << m_modbusRtuMasters.value(modbusUuid);
+ // FIXME: check if we should reload the old configuration
+ emit modbusRtuMasterChanged(m_modbusRtuMasters.value(modbusUuid));
+ return ModbusRtuErrorConnectionFailed;
+ }
+
+ emit modbusRtuMasterChanged(m_modbusRtuMasters.value(modbusUuid));
+
+ qCDebug(dcModbusRtu()) << "Reconfigured successfully" << m_modbusRtuMasters.value(modbusUuid);
+ saveModbusRtuMaster(modbusMaster);
+ return ModbusRtuErrorNoError;
+}
+
+ModbusRtuManager::ModbusRtuError ModbusRtuManager::removeModbusRtuMaster(const QUuid &modbusUuid)
+{
+ if (!supported()) {
+ qCWarning(dcModbusRtu()) << "Cannot remove modbus RTU master because serialbus is not suppoerted on this platform.";
+ return ModbusRtuErrorNotSupported;
+ }
+
+ if (!m_modbusRtuMasters.contains(modbusUuid)) {
+ qCWarning(dcModbusRtu()) << "Could not remove modbus RTU master because no resource could be found with uuid" << modbusUuid.toString();
+ return ModbusRtuErrorUuidNotFound;
+ }
+
+ ModbusRtuMasterImpl *modbusMaster = qobject_cast(m_modbusRtuMasters.take(modbusUuid));
+ qCDebug(dcModbusRtu()) << "Removing modbus RTU master" << qobject_cast(modbusMaster);
+ modbusMaster->disconnectDevice();
+ modbusMaster->deleteLater();
+
+ // Remove from settings
+ NymeaSettings settings(NymeaSettings::SettingsRoleModbusRtu);
+ settings.beginGroup("ModbusRtuMasters");
+ settings.beginGroup(modbusUuid.toString());
+ settings.remove("");
+ settings.endGroup(); // modbusUuid
+ settings.endGroup(); // ModbusRtuMasters
+
+ emit modbusRtuMasterRemoved(modbusMaster);
+
+ return ModbusRtuErrorNoError;
+}
+
+void ModbusRtuManager::loadRtuMasters()
+{
+ if (!supported())
+ return;
+
+ NymeaSettings settings(NymeaSettings::SettingsRoleModbusRtu);
+ qCDebug(dcModbusRtu()) << "Loading modbus RTU resources from" << settings.fileName();
+ settings.beginGroup("ModbusRtuMasters");
+ foreach (const QString &uuidString, settings.childGroups()) {
+ settings.beginGroup(uuidString);
+ QString serialPort = settings.value("serialPort").toString();
+ quint32 baudrate = settings.value("baudrate").toUInt();
+ QSerialPort::Parity parity = static_cast(settings.value("parity").toInt());
+ QSerialPort::DataBits dataBits = static_cast(settings.value("dataBits").toInt());
+ QSerialPort::StopBits stopBits = static_cast(settings.value("stopBits").toInt());
+ int numberOfRetries = settings.value("numberOfRetries").toInt();
+ int timeout = settings.value("timeout").toInt();
+ settings.endGroup(); // uuid
+
+ addModbusRtuMasterInternally(new ModbusRtuMasterImpl(QUuid(uuidString), serialPort, baudrate, parity, dataBits, stopBits, numberOfRetries, timeout, this));
+ }
+
+ settings.endGroup(); // ModbusRtuMasters
+}
+
+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());
+ settings.setValue("baudrate", modbusRtuMaster->baudrate());
+ settings.setValue("parity", static_cast(modbusRtuMaster->parity()));
+ settings.setValue("dataBits", static_cast(modbusRtuMaster->dataBits()));
+ settings.setValue("stopBits", static_cast(modbusRtuMaster->stopBits()));
+ settings.setValue("numberOfRetries", modbusRtuMaster->numberOfRetries());
+ settings.setValue("timeout", modbusRtuMaster->timeout());
+ settings.endGroup(); // uuid
+ 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/hardware/modbus/modbusrtumanager.h b/libnymea-core/hardware/modbus/modbusrtumanager.h
new file mode 100644
index 00000000..837188c6
--- /dev/null
+++ b/libnymea-core/hardware/modbus/modbusrtumanager.h
@@ -0,0 +1,95 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 MODBUSRTUMANAGER_H
+#define MODBUSRTUMANAGER_H
+
+#include
+#include
+#include
+#include
+
+#include "hardware/modbus/modbusrtumaster.h"
+
+namespace nymeaserver {
+
+class SerialPortMonitor;
+class ModbusRtuMasterImpl;
+
+class ModbusRtuManager : public QObject
+{
+ Q_OBJECT
+public:
+ enum ModbusRtuError {
+ ModbusRtuErrorNoError,
+ ModbusRtuErrorNotAvailable,
+ ModbusRtuErrorUuidNotFound,
+ ModbusRtuErrorHardwareNotFound,
+ ModbusRtuErrorResourceBusy,
+ ModbusRtuErrorNotSupported,
+ ModbusRtuErrorInvalidTimeoutValue,
+ ModbusRtuErrorConnectionFailed
+ };
+ Q_ENUM(ModbusRtuError)
+
+ explicit ModbusRtuManager(SerialPortMonitor *serialPortMonitor, QObject *parent = nullptr);
+ ~ModbusRtuManager() = default;
+
+ SerialPortMonitor *serialPortMonitor() const;
+
+ bool supported() 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, int numberOfRetries, int timeout);
+ ModbusRtuError reconfigureModbusRtuMaster(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout);
+ ModbusRtuError removeModbusRtuMaster(const QUuid &modbusUuid);
+
+signals:
+ void modbusRtuMasterAdded(ModbusRtuMaster *modbusRtuMaster);
+ void modbusRtuMasterRemoved(ModbusRtuMaster *modbusRtuMaster);
+ void modbusRtuMasterChanged(ModbusRtuMaster *modbusRtuMaster);
+
+private:
+ QHash m_modbusRtuMasters;
+ SerialPortMonitor *m_serialPortMonitor = nullptr;
+
+ void loadRtuMasters();
+ void saveModbusRtuMaster(ModbusRtuMaster *modbusRtuMaster);
+
+ void addModbusRtuMasterInternally(ModbusRtuMasterImpl *modbusRtuMaster);
+
+};
+
+}
+
+#endif // MODBUSRTUMANAGER_H
diff --git a/libnymea-core/hardware/modbus/modbusrtumasterimpl.cpp b/libnymea-core/hardware/modbus/modbusrtumasterimpl.cpp
new file mode 100644
index 00000000..eab5c4c7
--- /dev/null
+++ b/libnymea-core/hardware/modbus/modbusrtumasterimpl.cpp
@@ -0,0 +1,553 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 "modbusrtumasterimpl.h"
+#include "modbusrtureplyimpl.h"
+
+#include
+
+#ifdef WITH_QTSERIALBUS
+#include
+#include
+#endif
+
+Q_DECLARE_LOGGING_CATEGORY(dcModbusRtu)
+
+namespace nymeaserver {
+
+ModbusRtuMasterImpl::ModbusRtuMasterImpl(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout, QObject *parent) :
+ ModbusRtuMaster(parent),
+ m_modbusUuid(modbusUuid),
+ m_serialPort(serialPort),
+ m_baudrate(baudrate),
+ m_parity(parity),
+ m_dataBits(dataBits),
+ m_stopBits(stopBits),
+ m_numberOfRetries(numberOfRetries),
+ m_timeout(timeout)
+{
+#ifdef WITH_QTSERIALBUS
+ m_modbus = new QModbusRtuSerialMaster(this);
+ m_modbus->setConnectionParameter(QModbusDevice::SerialPortNameParameter, m_serialPort);
+ m_modbus->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, m_baudrate);
+ m_modbus->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, m_dataBits);
+ m_modbus->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, m_stopBits);
+ m_modbus->setConnectionParameter(QModbusDevice::SerialParityParameter, m_parity);
+ m_modbus->setNumberOfRetries(m_numberOfRetries);
+ m_modbus->setTimeout(m_timeout);
+
+ connect(m_modbus, &QModbusTcpClient::stateChanged, this, [=](QModbusDevice::State state){
+ qCDebug(dcModbusRtu()) << "Connection state changed" << m_modbusUuid.toString() << m_serialPort << state;
+ if (state == QModbusDevice::ConnectedState) {
+ if (m_connected != true) {
+ m_connected = true;
+ emit connectedChanged(m_connected);
+ }
+ } else {
+ if (m_connected != false) {
+ m_connected = false;
+ emit connectedChanged(m_connected);
+ }
+ }
+ });
+
+ connect(m_modbus, &QModbusRtuSerialMaster::errorOccurred, this, [=](QModbusDevice::Error error){
+ qCWarning(dcModbusRtu()) << "Error occured for modbus RTU master" << m_modbusUuid.toString() << m_serialPort << error << m_modbus->errorString();
+ if (error != QModbusDevice::NoError) {
+ disconnectDevice();
+ }
+ });
+#endif
+}
+
+QUuid ModbusRtuMasterImpl::modbusUuid() const
+{
+ return m_modbusUuid;
+}
+
+QString ModbusRtuMasterImpl::serialPort() const
+{
+ return m_serialPort;
+}
+
+void ModbusRtuMasterImpl::setSerialPort(const QString &serialPort)
+{
+ if (m_serialPort == serialPort)
+ return;
+
+ m_serialPort = serialPort;
+ emit serialPortChanged(m_serialPort);
+}
+
+qint32 ModbusRtuMasterImpl::baudrate() const
+{
+ return m_baudrate;
+}
+
+void ModbusRtuMasterImpl::setBaudrate(qint32 baudrate)
+{
+ if (m_baudrate == baudrate)
+ return;
+
+ m_baudrate = baudrate;
+ emit baudrateChanged(m_baudrate);
+}
+
+QSerialPort::Parity ModbusRtuMasterImpl::parity() const
+{
+ return m_parity;
+}
+
+void ModbusRtuMasterImpl::setParity(QSerialPort::Parity parity)
+{
+ if (m_parity == parity)
+ return;
+
+ m_parity = parity;
+ emit parityChanged(m_parity);
+}
+
+QSerialPort::DataBits ModbusRtuMasterImpl::dataBits() const
+{
+ return m_dataBits;
+}
+
+void ModbusRtuMasterImpl::setDataBits(QSerialPort::DataBits dataBits)
+{
+ if (m_dataBits == dataBits)
+ return;
+
+ m_dataBits = dataBits;
+ emit dataBitsChanged(m_dataBits);
+}
+
+QSerialPort::StopBits ModbusRtuMasterImpl::stopBits()
+{
+ return m_stopBits;
+}
+
+void ModbusRtuMasterImpl::setStopBits(QSerialPort::StopBits stopBits)
+{
+ if (m_stopBits == stopBits)
+ return;
+
+ m_stopBits = stopBits;
+ emit stopBitsChanged(m_stopBits);
+}
+
+bool ModbusRtuMasterImpl::connected() const
+{
+ return m_connected;
+}
+
+bool ModbusRtuMasterImpl::connectDevice()
+{
+#ifdef WITH_QTSERIALBUS
+ m_modbus->setConnectionParameter(QModbusDevice::SerialPortNameParameter, m_serialPort);
+ m_modbus->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, m_baudrate);
+ m_modbus->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, m_dataBits);
+ m_modbus->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, m_stopBits);
+ m_modbus->setConnectionParameter(QModbusDevice::SerialParityParameter, m_parity);
+ m_modbus->setNumberOfRetries(m_numberOfRetries);
+ m_modbus->setTimeout(m_timeout);
+ return m_modbus->connectDevice();
+#else
+ qCWarning(dcModbusRtu()) << "Modbus is not available on this platform.";
+ return false;
+#endif
+}
+
+void ModbusRtuMasterImpl::disconnectDevice()
+{
+#ifdef WITH_QTSERIALBUS
+ m_modbus->disconnectDevice();
+#else
+ qCWarning(dcModbusRtu()) << "Modbus is not available on this platform.";
+#endif
+}
+
+int ModbusRtuMasterImpl::numberOfRetries() const
+{
+ return m_numberOfRetries;
+}
+
+void ModbusRtuMasterImpl::setNumberOfRetries(int numberOfRetries)
+{
+ if (m_numberOfRetries == numberOfRetries)
+ return;
+
+ m_numberOfRetries = numberOfRetries;
+ emit numberOfRetriesChanged(m_numberOfRetries);
+#ifdef WITH_QTSERIALBUS
+ m_modbus->setNumberOfRetries(m_numberOfRetries);
+#endif
+}
+
+int ModbusRtuMasterImpl::timeout() const
+{
+ return m_timeout;
+}
+
+void ModbusRtuMasterImpl::setTimeout(int timeout)
+{
+ if (m_timeout == timeout)
+ return;
+
+ m_timeout = timeout;
+ emit timeoutChanged(m_timeout);
+#ifdef WITH_QTSERIALBUS
+ m_modbus->setTimeout(m_timeout);
+#endif
+}
+
+ModbusRtuReply *ModbusRtuMasterImpl::readCoil(int slaveAddress, int registerAddress, quint16 size)
+{
+#ifdef WITH_QTSERIALBUS
+ // Create the reply for the plugin
+ ModbusRtuReplyImpl *reply = new ModbusRtuReplyImpl(slaveAddress, registerAddress, this);
+ connect(reply, &ModbusRtuReplyImpl::finished, reply, &ModbusRtuReplyImpl::deleteLater);
+
+ // Create the actual modbus lib reply
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, size);
+ QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress);
+
+ connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){
+ modbusReply->deleteLater();
+
+ // Fill common reply data
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+
+ // Check if the reply finished with an error
+ if (modbusReply->error() != QModbusDevice::NoError) {
+ qCWarning(dcModbusRtu()) << "Read coil request finished with error" << modbusReply->error() << modbusReply->errorString();
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ return;
+ }
+
+ // Parse the data unit and set reply result
+ const QModbusDataUnit unit = modbusReply->result();
+ reply->setResult(unit.values());
+ emit reply->finished();
+ });
+
+ connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
+ qCWarning(dcModbusRtu()) << "Read coil request finished with error" << error << modbusReply->errorString();
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ });
+
+ return qobject_cast(reply);
+#else
+ Q_UNUSED(slaveAddress)
+ Q_UNUSED(registerAddress)
+ Q_UNUSED(size)
+ qCWarning(dcModbusRtu()) << "Modbus is not available on this platform.";
+
+ return nullptr;
+#endif
+}
+
+ModbusRtuReply *ModbusRtuMasterImpl::readDiscreteInput(int slaveAddress, int registerAddress, quint16 size)
+{
+#ifdef WITH_QTSERIALBUS
+ // Create the reply for the plugin
+ ModbusRtuReplyImpl *reply = new ModbusRtuReplyImpl(slaveAddress, registerAddress, this);
+ connect(reply, &ModbusRtuReplyImpl::finished, reply, &ModbusRtuReplyImpl::deleteLater);
+
+ // Create the actual modbus lib reply
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, registerAddress, size);
+ QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress);
+
+ connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){
+ modbusReply->deleteLater();
+
+ // Fill common reply data
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+
+ // Check if the reply finished with an error
+ if (modbusReply->error() != QModbusDevice::NoError) {
+ qCWarning(dcModbusRtu()) << "Read descrete inputs request finished with error" << modbusReply->error() << modbusReply->errorString();
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ return;
+ }
+
+ // Parse the data unit and set reply result
+ const QModbusDataUnit unit = modbusReply->result();
+ reply->setResult(unit.values());
+ emit reply->finished();
+ });
+
+ connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
+ qCWarning(dcModbusRtu()) << "Read descrete inputs request finished with error" << error << modbusReply->errorString();
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ });
+
+ return qobject_cast(reply);
+#else
+ Q_UNUSED(slaveAddress)
+ Q_UNUSED(registerAddress)
+ Q_UNUSED(size)
+ qCWarning(dcModbusRtu()) << "Modbus is not available on this platform.";
+
+ return nullptr;
+#endif
+}
+
+ModbusRtuReply *ModbusRtuMasterImpl::readInputRegister(int slaveAddress, int registerAddress, quint16 size)
+{
+#ifdef WITH_QTSERIALBUS
+ // Create the reply for the plugin
+ ModbusRtuReplyImpl *reply = new ModbusRtuReplyImpl(slaveAddress, registerAddress, this);
+ connect(reply, &ModbusRtuReplyImpl::finished, reply, &ModbusRtuReplyImpl::deleteLater);
+
+ // Create the actual modbus lib reply
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, registerAddress, size);
+ QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress);
+
+ connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){
+ modbusReply->deleteLater();
+
+ // Fill common reply data
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+
+ // Check if the reply finished with an error
+ if (modbusReply->error() != QModbusDevice::NoError) {
+ qCWarning(dcModbusRtu()) << "Read input registers request finished with error" << modbusReply->error() << modbusReply->errorString();
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ return;
+ }
+
+ // Parse the data unit and set reply result
+ const QModbusDataUnit unit = modbusReply->result();
+ reply->setResult(unit.values());
+ emit reply->finished();
+ });
+
+ connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
+ qCWarning(dcModbusRtu()) << "Read input registers request finished with error" << error << modbusReply->errorString();
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ });
+
+ return qobject_cast(reply);
+#else
+ Q_UNUSED(slaveAddress)
+ Q_UNUSED(registerAddress)
+ Q_UNUSED(size)
+ qCWarning(dcModbusRtu()) << "Modbus is not available on this platform.";
+
+ return nullptr;
+#endif
+}
+
+ModbusRtuReply *ModbusRtuMasterImpl::readHoldingRegister(int slaveAddress, int registerAddress, quint16 size)
+{
+#ifdef WITH_QTSERIALBUS
+ // Create the reply for the plugin
+ ModbusRtuReplyImpl *reply = new ModbusRtuReplyImpl(slaveAddress, registerAddress, this);
+ connect(reply, &ModbusRtuReplyImpl::finished, reply, &ModbusRtuReplyImpl::deleteLater);
+
+ // Create the actual modbus lib reply
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, size);
+ QModbusReply *modbusReply = m_modbus->sendReadRequest(request, slaveAddress);
+
+ connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){
+ modbusReply->deleteLater();
+
+ // Fill common reply data
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+
+ // Check if the reply finished with an error
+ if (modbusReply->error() != QModbusDevice::NoError) {
+ qCWarning(dcModbusRtu()) << "Read holding registers request finished with error" << modbusReply->error() << modbusReply->errorString();
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ return;
+ }
+
+ // Parse the data unit and set reply result
+ const QModbusDataUnit unit = modbusReply->result();
+ reply->setResult(unit.values());
+ emit reply->finished();
+ });
+
+ connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
+ qCWarning(dcModbusRtu()) << "Read holding registers request finished with error" << error << modbusReply->errorString();
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ });
+
+ return qobject_cast(reply);
+#else
+ Q_UNUSED(slaveAddress)
+ Q_UNUSED(registerAddress)
+ Q_UNUSED(size)
+ qCWarning(dcModbusRtu()) << "Modbus is not available on this platform.";
+
+ return nullptr;
+#endif
+}
+
+ModbusRtuReply *ModbusRtuMasterImpl::writeCoils(int slaveAddress, int registerAddress, const QVector &values)
+{
+#ifdef WITH_QTSERIALBUS
+ // Create the reply for the plugin
+ ModbusRtuReplyImpl *reply = new ModbusRtuReplyImpl(slaveAddress, registerAddress, this);
+ connect(reply, &ModbusRtuReplyImpl::finished, reply, &ModbusRtuReplyImpl::deleteLater);
+
+ // Create the actual modbus lib reply
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, registerAddress, values.length());
+ request.setValues(values);
+
+ QModbusReply *modbusReply = m_modbus->sendWriteRequest(request, slaveAddress);
+
+ connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){
+ modbusReply->deleteLater();
+
+ // Fill common reply data
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+
+ // Check if the reply finished with an error
+ if (modbusReply->error() != QModbusDevice::NoError) {
+ qCWarning(dcModbusRtu()) << "Read coil request finished with error" << modbusReply->error() << modbusReply->errorString();
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ return;
+ }
+
+ // Parse the data unit and set reply result
+ const QModbusDataUnit unit = modbusReply->result();
+ reply->setResult(unit.values());
+ emit reply->finished();
+ });
+
+ connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
+ qCWarning(dcModbusRtu()) << "Read coil request finished with error" << error << modbusReply->errorString();
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ });
+
+ return qobject_cast(reply);
+#else
+ Q_UNUSED(slaveAddress)
+ Q_UNUSED(registerAddress)
+ Q_UNUSED(values)
+ qCWarning(dcModbusRtu()) << "Modbus is not available on this platform.";
+
+ return nullptr;
+#endif
+}
+
+
+ModbusRtuReply *ModbusRtuMasterImpl::writeHoldingRegisters(int slaveAddress, int registerAddress, const QVector &values)
+{
+#ifdef WITH_QTSERIALBUS
+ // Create the reply for the plugin
+ ModbusRtuReplyImpl *reply = new ModbusRtuReplyImpl(slaveAddress, registerAddress, this);
+ connect(reply, &ModbusRtuReplyImpl::finished, reply, &ModbusRtuReplyImpl::deleteLater);
+
+ // Create the actual modbus lib reply
+ QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, registerAddress, values.length());
+ request.setValues(values);
+
+ QModbusReply *modbusReply = m_modbus->sendWriteRequest(request, slaveAddress);
+
+ connect(modbusReply, &QModbusReply::finished, modbusReply, [=](){
+ modbusReply->deleteLater();
+
+ // Fill common reply data
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+
+ // Check if the reply finished with an error
+ if (modbusReply->error() != QModbusDevice::NoError) {
+ qCWarning(dcModbusRtu()) << "Read coil request finished with error" << modbusReply->error() << modbusReply->errorString();
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ return;
+ }
+
+ // Parse the data unit and set reply result
+ const QModbusDataUnit unit = modbusReply->result();
+ reply->setResult(unit.values());
+ emit reply->finished();
+ });
+
+ connect(modbusReply, &QModbusReply::errorOccurred, modbusReply, [=](QModbusDevice::Error error){
+ qCWarning(dcModbusRtu()) << "Read coil request finished with error" << error << modbusReply->errorString();
+ reply->setFinished(true);
+ reply->setError(static_cast(modbusReply->error()));
+ reply->setErrorString(modbusReply->errorString());
+ emit reply->errorOccurred(reply->error());
+ emit reply->finished();
+ });
+
+ return qobject_cast(reply);
+#else
+ Q_UNUSED(slaveAddress)
+ Q_UNUSED(registerAddress)
+ Q_UNUSED(values)
+ qCWarning(dcModbusRtu()) << "Modbus is not available on this platform.";
+
+ return nullptr;
+#endif
+}
+
+}
diff --git a/libnymea-core/hardware/modbus/modbusrtumasterimpl.h b/libnymea-core/hardware/modbus/modbusrtumasterimpl.h
new file mode 100644
index 00000000..6df0749f
--- /dev/null
+++ b/libnymea-core/hardware/modbus/modbusrtumasterimpl.h
@@ -0,0 +1,108 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 MODBUSRTUMASTERIMPL_H
+#define MODBUSRTUMASTERIMPL_H
+
+#include
+#include
+
+#ifdef WITH_QTSERIALBUS
+#include
+#endif
+
+#include "hardware/modbus/modbusrtumaster.h"
+
+namespace nymeaserver {
+
+class ModbusRtuMasterImpl : public ModbusRtuMaster
+{
+ Q_OBJECT
+public:
+ explicit ModbusRtuMasterImpl(const QUuid &modbusUuid, const QString &serialPort, qint32 baudrate, QSerialPort::Parity parity, QSerialPort::DataBits dataBits, QSerialPort::StopBits stopBits, int numberOfRetries, int timeout, QObject *parent = nullptr);
+ ~ModbusRtuMasterImpl() override = default;
+
+ QUuid modbusUuid() const override;
+
+ QString serialPort() const override;
+ void setSerialPort(const QString &serialPort);
+
+ qint32 baudrate() const override;
+ void setBaudrate(qint32 baudrate);
+
+ QSerialPort::Parity parity() const override;
+ void setParity(QSerialPort::Parity parity);
+
+ QSerialPort::DataBits dataBits() const override;
+ void setDataBits(QSerialPort::DataBits dataBits);
+
+ QSerialPort::StopBits stopBits() override;
+ void setStopBits(QSerialPort::StopBits stopBits);
+
+ bool connected() const override;
+
+ bool connectDevice();
+ void disconnectDevice();
+
+ int numberOfRetries() const override;
+ void setNumberOfRetries(int numberOfRetries);
+
+ int timeout() const override;
+ void setTimeout(int timeout);
+
+ // Requests
+ ModbusRtuReply *readCoil(int slaveAddress, int registerAddress, quint16 size = 1) override;
+ ModbusRtuReply *readDiscreteInput(int slaveAddress, int registerAddress, quint16 size = 1) override;
+ ModbusRtuReply *readInputRegister(int slaveAddress, int registerAddress, quint16 size = 1) override;
+ ModbusRtuReply *readHoldingRegister(int slaveAddress, int registerAddress, quint16 size = 1) override;
+
+ ModbusRtuReply *writeCoils(int slaveAddress, int registerAddress, const QVector &values) override;
+ ModbusRtuReply *writeHoldingRegisters(int slaveAddress, int registerAddress, const QVector &values) override;
+
+private:
+ QUuid m_modbusUuid;
+ bool m_connected = false;
+
+#ifdef WITH_QTSERIALBUS
+ QModbusRtuSerialMaster *m_modbus = nullptr;
+#endif
+
+ QString m_serialPort;
+ qint32 m_baudrate;
+ QSerialPort::Parity m_parity;
+ QSerialPort::DataBits m_dataBits;
+ QSerialPort::StopBits m_stopBits;
+ int m_numberOfRetries = 3;
+ int m_timeout = 100;
+};
+
+}
+
+#endif // MODBUSRTUMASTERIMPL_H
diff --git a/libnymea-core/hardware/modbus/modbusrtureplyimpl.cpp b/libnymea-core/hardware/modbus/modbusrtureplyimpl.cpp
new file mode 100644
index 00000000..dc00ac94
--- /dev/null
+++ b/libnymea-core/hardware/modbus/modbusrtureplyimpl.cpp
@@ -0,0 +1,93 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 "modbusrtureplyimpl.h"
+
+namespace nymeaserver {
+
+ModbusRtuReplyImpl::ModbusRtuReplyImpl(int slaveAddress, int registerAddress, QObject *parent) :
+ ModbusRtuReply(parent),
+ m_slaveAddress(slaveAddress),
+ m_registerAddress(registerAddress)
+{
+
+}
+
+bool ModbusRtuReplyImpl::isFinished() const
+{
+ return m_finished;
+}
+
+void ModbusRtuReplyImpl::setFinished(bool finished)
+{
+ m_finished = finished;
+}
+
+int ModbusRtuReplyImpl::slaveAddress() const
+{
+ return m_slaveAddress;
+}
+
+int ModbusRtuReplyImpl::registerAddress() const
+{
+ return m_registerAddress;
+}
+
+QString ModbusRtuReplyImpl::errorString() const
+{
+ return m_errorString;
+}
+
+void ModbusRtuReplyImpl::setErrorString(const QString &errorString)
+{
+ m_errorString = errorString;
+}
+
+ModbusRtuReply::Error ModbusRtuReplyImpl::error() const
+{
+ return m_error;
+}
+
+void ModbusRtuReplyImpl::setError(ModbusRtuReply::Error error)
+{
+ m_error = error;
+}
+
+QVector ModbusRtuReplyImpl::result() const
+{
+ return m_result;
+}
+
+void ModbusRtuReplyImpl::setResult(const QVector &result)
+{
+ m_result = result;
+}
+
+}
diff --git a/libnymea-core/hardware/modbus/modbusrtureplyimpl.h b/libnymea-core/hardware/modbus/modbusrtureplyimpl.h
new file mode 100644
index 00000000..5851b423
--- /dev/null
+++ b/libnymea-core/hardware/modbus/modbusrtureplyimpl.h
@@ -0,0 +1,73 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 MODBUSRTUREPLYIMPL_H
+#define MODBUSRTUREPLYIMPL_H
+
+#include
+
+#include "hardware/modbus/modbusrtureply.h"
+
+namespace nymeaserver {
+
+class ModbusRtuReplyImpl : public ModbusRtuReply
+{
+ Q_OBJECT
+public:
+ explicit ModbusRtuReplyImpl(int slaveAddress, int registerAddress, QObject *parent = nullptr);
+
+ bool isFinished() const override;
+ void setFinished(bool finished);
+
+ int slaveAddress() const override;
+ int registerAddress() const override;
+
+ QString errorString() const override;
+ void setErrorString(const QString &errorString);
+
+ ModbusRtuReply::Error error() const override;
+ void setError(ModbusRtuReply::Error error);
+
+ QVector result() const override;
+ void setResult(const QVector &result);
+
+private:
+ bool m_finished = false;
+ int m_slaveAddress;
+ int m_registerAddress;
+ Error m_error = UnknownError;
+ QString m_errorString;
+ QVector m_result;
+
+};
+
+}
+
+#endif // MODBUSRTUREPLYIMPL_H
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/hardwaremanagerimplementation.cpp b/libnymea-core/hardwaremanagerimplementation.cpp
index 8d19558c..e873b839 100644
--- a/libnymea-core/hardwaremanagerimplementation.cpp
+++ b/libnymea-core/hardwaremanagerimplementation.cpp
@@ -44,9 +44,12 @@
#include "hardware/i2c/i2cmanagerimplementation.h"
#include "hardware/zigbee/zigbeehardwareresourceimplementation.h"
+#include "hardware/modbus/modbusrtumanager.h"
+#include "hardware/modbus/modbusrtuhardwareresourceimplementation.h"
+
namespace nymeaserver {
-HardwareManagerImplementation::HardwareManagerImplementation(Platform *platform, MqttBroker *mqttBroker, ZigbeeManager *zigbeeManager, QObject *parent) :
+HardwareManagerImplementation::HardwareManagerImplementation(Platform *platform, MqttBroker *mqttBroker, ZigbeeManager *zigbeeManager, ModbusRtuManager *modbusRtuManager, QObject *parent) :
HardwareManager(parent),
m_platform(platform)
{
@@ -73,6 +76,8 @@ HardwareManagerImplementation::HardwareManagerImplementation(Platform *platform,
m_zigbeeResource = new ZigbeeHardwareResourceImplementation(zigbeeManager, this);
+ m_modbusRtuResource = new ModbusRtuHardwareResourceImplementation(modbusRtuManager, this);
+
// Enable all the resources
setResourceEnabled(m_pluginTimerManager, true);
setResourceEnabled(m_radio433, true);
@@ -142,6 +147,11 @@ ZigbeeHardwareResource *HardwareManagerImplementation::zigbeeResource()
return m_zigbeeResource;
}
+ModbusRtuHardwareResource *HardwareManagerImplementation::modbusRtuResource()
+{
+ return m_modbusRtuResource;
+}
+
void HardwareManagerImplementation::thingsLoaded()
{
m_zigbeeResource->thingsLoaded();
diff --git a/libnymea-core/hardwaremanagerimplementation.h b/libnymea-core/hardwaremanagerimplementation.h
index 8a67e54e..c808b0d2 100644
--- a/libnymea-core/hardwaremanagerimplementation.h
+++ b/libnymea-core/hardwaremanagerimplementation.h
@@ -43,13 +43,15 @@ class Platform;
class MqttBroker;
class ZigbeeManager;
class ZigbeeHardwareResourceImplementation;
+class ModbusRtuManager;
+class ModbusRtuHardwareResourceImplementation;
class HardwareManagerImplementation : public HardwareManager
{
Q_OBJECT
public:
- explicit HardwareManagerImplementation(Platform *platform, MqttBroker *mqttBroker, ZigbeeManager *zigbeeManager, QObject *parent = nullptr);
+ explicit HardwareManagerImplementation(Platform *platform, MqttBroker *mqttBroker, ZigbeeManager *zigbeeManager, ModbusRtuManager *modbusRtuManager, QObject *parent = nullptr);
~HardwareManagerImplementation() override;
Radio433 *radio433() override;
@@ -61,6 +63,7 @@ public:
MqttProvider *mqttProvider() override;
I2CManager *i2cManager() override;
ZigbeeHardwareResource *zigbeeResource() override;
+ ModbusRtuHardwareResource *modbusRtuResource() override;
public slots:
void thingsLoaded();
@@ -79,6 +82,7 @@ private:
MqttProvider *m_mqttProvider = nullptr;
I2CManager *m_i2cManager = nullptr;
ZigbeeHardwareResourceImplementation *m_zigbeeResource = nullptr;
+ ModbusRtuHardwareResourceImplementation *m_modbusRtuResource = nullptr;
};
}
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
new file mode 100644
index 00000000..89b169c4
--- /dev/null
+++ b/libnymea-core/jsonrpc/modbusrtuhandler.cpp
@@ -0,0 +1,286 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 "modbusrtuhandler.h"
+#include "hardware/modbus/modbusrtumanager.h"
+#include "hardware/serialport/serialportmonitor.h"
+
+namespace nymeaserver {
+
+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());
+ modbusRtuMasterDescription.insert("numberOfRetries", enumValueName(Uint));
+ modbusRtuMasterDescription.insert("timeout", enumValueName(Uint));
+
+ 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("o:modbusRtuMasters", QVariantList() << objectRef("ModbusRtuMaster"));
+ returns.insert("modbusError", enumRef());
+ 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 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 modbus RTU master has been changed in 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. The timeout value is in milli seconds and the minimum value is 10 ms.";
+ params.insert("serialPort", enumValueName(String));
+ params.insert("baudrate", enumValueName(Uint));
+ params.insert("parity", enumRef());
+ params.insert("dataBits", enumRef());
+ params.insert("stopBits", enumRef());
+ params.insert("numberOfRetries", enumValueName(Uint));
+ params.insert("timeout", enumValueName(Uint));
+ 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 = "Reconfigure 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());
+ params.insert("numberOfRetries", enumValueName(Uint));
+ params.insert("timeout", enumValueName(Uint));
+ 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 SerialPortRemoved(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
+{
+ 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;
+
+ if (m_modbusRtuManager->supported()) {
+ QVariantList modbusList;
+ foreach (ModbusRtuMaster *modbusMaster, m_modbusRtuManager->modbusRtuMasters()) {
+ modbusList << packModbusRtuMaster(modbusMaster);
+ }
+ returnMap.insert("modbusRtuMasters", modbusList);
+ returnMap.insert("modbusError", enumValueName(ModbusRtuManager::ModbusRtuErrorNoError));
+ } else {
+ returnMap.insert("modbusError", enumValueName(ModbusRtuManager::ModbusRtuErrorNotSupported));
+ }
+
+
+ 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()));
+ uint numberOfRetries = params.value("numberOfRetries").toUInt();
+ uint timeout = params.value("timeout").toUInt();
+
+ QVariantMap returnMap;
+ if (timeout < 10) {
+ returnMap.insert("modbusError", enumValueName(ModbusRtuManager::ModbusRtuErrorInvalidTimeoutValue));
+ return createReply(returnMap);
+ }
+
+ QPair result = m_modbusRtuManager->addNewModbusRtuMaster(serialPort, baudrate, parity, dataBits, stopBits, numberOfRetries, timeout);
+ 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()));
+ uint numberOfRetries = params.value("numberOfRetries").toUInt();
+ uint timeout = params.value("timeout").toUInt();
+
+ QVariantMap returnMap;
+ if (timeout < 10) {
+ returnMap.insert("modbusError", enumValueName(ModbusRtuManager::ModbusRtuErrorInvalidTimeoutValue));
+ return createReply(returnMap);
+ }
+
+ ModbusRtuManager::ModbusRtuError result = m_modbusRtuManager->reconfigureModbusRtuMaster(modbusUuid, serialPort, baudrate, parity, dataBits, stopBits, numberOfRetries, timeout);
+ 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())));
+ modbusRtuMasterMap.insert("numberOfRetries", modbusRtuMaster->numberOfRetries());
+ modbusRtuMasterMap.insert("timeout", modbusRtuMaster->timeout());
+ return modbusRtuMasterMap;
+}
+
+}
diff --git a/libnymea-core/jsonrpc/modbusrtuhandler.h b/libnymea-core/jsonrpc/modbusrtuhandler.h
new file mode 100644
index 00000000..171d3540
--- /dev/null
+++ b/libnymea-core/jsonrpc/modbusrtuhandler.h
@@ -0,0 +1,74 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 MODBUSRTUHANDLER_H
+#define MODBUSRTUHANDLER_H
+
+#include
+
+#include "jsonrpc/jsonhandler.h"
+#include "hardware/modbus/modbusrtumaster.h"
+
+namespace nymeaserver {
+
+class ModbusRtuManager;
+
+class ModbusRtuHandler : public JsonHandler
+{
+ Q_OBJECT
+public:
+ explicit ModbusRtuHandler(ModbusRtuManager *modbusRtuManager, QObject *parent = nullptr);
+
+ QString name() const override;
+
+ 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);
+};
+
+}
+
+#endif // MODBUSRTUHANDLER_H
diff --git a/libnymea-core/libnymea-core.pro b/libnymea-core/libnymea-core.pro
index 1d2d0197..7985a191 100644
--- a/libnymea-core/libnymea-core.pro
+++ b/libnymea-core/libnymea-core.pro
@@ -3,7 +3,7 @@ TARGET = nymea-core
include(../nymea.pri)
-QT += bluetooth dbus qml sql websockets
+QT += bluetooth dbus qml sql websockets serialport
INCLUDEPATH += $$top_srcdir/libnymea $$top_builddir
LIBS += -L$$top_builddir/libnymea/ -lnymea -lssl -lcrypto
@@ -35,6 +35,27 @@ CONFIG(withoutpython) {
CONFIG -= python
}
+# Qt serial bus module is officially available since Qt 5.8
+# but not all platforms host the qt serialbus package.
+# Let's check if the package exists, not the qt version
+packagesExist(Qt5SerialBus) {
+ message("Building with QtSerialBus support.")
+ # Qt += serialbus
+ PKGCONFIG += Qt5SerialBus
+ DEFINES += WITH_QTSERIALBUS
+} else {
+ message("Qt5SerialBus package not found. Building without QtSerialBus support.")
+}
+
+# Note: udev is not available on all platforms
+packagesExist(libudev) {
+ message("Building with udev support")
+ PKGCONFIG += libudev
+ DEFINES += WITH_UDEV
+} else {
+ message("Building without udev support.")
+}
+
target.path = $$[QT_INSTALL_LIBS]
INSTALLS += target
@@ -44,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 \
@@ -55,6 +77,7 @@ HEADERS += nymeacore.h \
integrations/thingmanagerimplementation.h \
integrations/translator.h \
experiences/experiencemanager.h \
+ jsonrpc/modbusrtuhandler.h \
jsonrpc/zigbeehandler.h \
ruleengine/ruleengine.h \
ruleengine/rule.h \
@@ -118,6 +141,10 @@ HEADERS += nymeacore.h \
hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.h \
hardware/bluetoothlowenergy/bluetoothlowenergydeviceimplementation.h \
hardware/bluetoothlowenergy/bluetoothdiscoveryreplyimplementation.h \
+ hardware/modbus/modbusrtuhardwareresourceimplementation.h \
+ hardware/modbus/modbusrtumanager.h \
+ hardware/modbus/modbusrtumasterimpl.h \
+ hardware/modbus/modbusrtureplyimpl.h \
hardware/network/networkaccessmanagerimpl.h \
hardware/network/upnp/upnpdiscoveryimplementation.h \
hardware/network/upnp/upnpdiscoveryrequest.h \
@@ -138,11 +165,13 @@ HEADERS += nymeacore.h \
SOURCES += nymeacore.cpp \
+ hardware/serialport/serialportmonitor.cpp \
integrations/apikeysprovidersloader.cpp \
integrations/plugininfocache.cpp \
integrations/thingmanagerimplementation.cpp \
integrations/translator.cpp \
experiences/experiencemanager.cpp \
+ jsonrpc/modbusrtuhandler.cpp \
jsonrpc/zigbeehandler.cpp \
ruleengine/ruleengine.cpp \
ruleengine/rule.cpp \
@@ -205,6 +234,10 @@ SOURCES += nymeacore.cpp \
hardware/bluetoothlowenergy/bluetoothlowenergymanagerimplementation.cpp \
hardware/bluetoothlowenergy/bluetoothlowenergydeviceimplementation.cpp \
hardware/bluetoothlowenergy/bluetoothdiscoveryreplyimplementation.cpp \
+ hardware/modbus/modbusrtuhardwareresourceimplementation.cpp \
+ hardware/modbus/modbusrtumanager.cpp \
+ hardware/modbus/modbusrtumasterimpl.cpp \
+ hardware/modbus/modbusrtureplyimpl.cpp \
hardware/network/networkaccessmanagerimpl.cpp \
hardware/network/upnp/upnpdiscoveryimplementation.cpp \
hardware/network/upnp/upnpdiscoveryrequest.cpp \
diff --git a/libnymea-core/nymeacore.cpp b/libnymea-core/nymeacore.cpp
index a746f8b8..7e6f7d3d 100644
--- a/libnymea-core/nymeacore.cpp
+++ b/libnymea-core/nymeacore.cpp
@@ -53,6 +53,9 @@
#include "zigbee/zigbeemanager.h"
+#include "hardware/modbus/modbusrtumanager.h"
+#include "hardware/serialport/serialportmonitor.h"
+
#include
#include
@@ -108,8 +111,14 @@ 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(m_serialPortMonitor, this);
+
qCDebug(dcCore) << "Creating Hardware Manager";
- m_hardwareManager = new HardwareManagerImplementation(m_platform, m_serverManager->mqttBroker(), m_zigbeeManager, this);
+ m_hardwareManager = new HardwareManagerImplementation(m_platform, m_serverManager->mqttBroker(), m_zigbeeManager, m_modbusRtuManager, this);
qCDebug(dcCore) << "Creating Thing Manager (locale:" << m_configuration->locale() << ")";
m_thingManager = new ThingManagerImplementation(m_hardwareManager, m_configuration->locale(), this);
@@ -645,6 +654,11 @@ ZigbeeManager *NymeaCore::zigbeeManager() const
return m_zigbeeManager;
}
+ModbusRtuManager *NymeaCore::modbusRtuManager() const
+{
+ return m_modbusRtuManager;
+}
+
void NymeaCore::gotEvent(const Event &event)
{
emit eventTriggered(event);
diff --git a/libnymea-core/nymeacore.h b/libnymea-core/nymeacore.h
index 3201b4d6..b67ac29a 100644
--- a/libnymea-core/nymeacore.h
+++ b/libnymea-core/nymeacore.h
@@ -67,6 +67,8 @@ class ExperienceManager;
class ScriptEngine;
class CloudManager;
class ZigbeeManager;
+class ModbusRtuManager;
+class SerialPortMonitor;
class NymeaCore : public QObject
{
@@ -107,6 +109,7 @@ public:
TagsStorage *tagsStorage() const;
Platform *platform() const;
ZigbeeManager *zigbeeManager() const;
+ ModbusRtuManager *modbusRtuManager() const;
static QStringList getAvailableLanguages();
static QStringList loggingFilters();
@@ -151,6 +154,8 @@ private:
System *m_system;
ExperienceManager *m_experienceManager;
ZigbeeManager *m_zigbeeManager;
+ SerialPortMonitor *m_serialPortMonitor;
+ ModbusRtuManager *m_modbusRtuManager;
QList m_executingRules;
diff --git a/libnymea/hardware/modbus/modbusrtuhardwareresource.cpp b/libnymea/hardware/modbus/modbusrtuhardwareresource.cpp
new file mode 100644
index 00000000..ddab12a7
--- /dev/null
+++ b/libnymea/hardware/modbus/modbusrtuhardwareresource.cpp
@@ -0,0 +1,38 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 "modbusrtuhardwareresource.h"
+#include "modbusrtumaster.h"
+
+ModbusRtuHardwareResource::ModbusRtuHardwareResource(QObject *parent) :
+ HardwareResource("Modbus RTU resource", parent)
+{
+
+}
diff --git a/libnymea/hardware/modbus/modbusrtuhardwareresource.h b/libnymea/hardware/modbus/modbusrtuhardwareresource.h
new file mode 100644
index 00000000..9be1a590
--- /dev/null
+++ b/libnymea/hardware/modbus/modbusrtuhardwareresource.h
@@ -0,0 +1,59 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 MODBUSRTUHARDWARERESOURCE_H
+#define MODBUSRTUHARDWARERESOURCE_H
+
+#include
+#include
+
+#include "hardwareresource.h"
+#include "modbusrtumaster.h"
+
+class ModbusRtuHardwareResource : public HardwareResource
+{
+ Q_OBJECT
+public:
+ virtual QList modbusRtuMasters() const = 0;
+ virtual bool hasModbusRtuMaster(const QUuid &modbusUuid) const = 0;
+ virtual ModbusRtuMaster *getModbusRtuMaster(const QUuid &modbusUuid) const = 0;
+
+protected:
+ explicit ModbusRtuHardwareResource(QObject *parent = nullptr);
+ virtual ~ModbusRtuHardwareResource() = default;
+
+signals:
+ void modbusRtuMasterAdded(const QUuid &modbusUuid);
+ void modbusRtuMasterRemoved(const QUuid &modbusUuid);
+ void modbusRtuMasterChanged(const QUuid &modbusUuid);
+
+};
+
+#endif // MODBUSRTUHARDWARERESOURCE_H
diff --git a/libnymea/hardware/modbus/modbusrtumaster.h b/libnymea/hardware/modbus/modbusrtumaster.h
new file mode 100644
index 00000000..39ba94be
--- /dev/null
+++ b/libnymea/hardware/modbus/modbusrtumaster.h
@@ -0,0 +1,95 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 MODBUSRTUMASTER_H
+#define MODBUSRTUMASTER_H
+
+#include
+#include
+#include
+#include
+
+#include "modbusrtureply.h"
+
+class ModbusRtuMaster : public QObject
+{
+ Q_OBJECT
+public:
+ // Properties
+ virtual QUuid modbusUuid() const = 0;
+ virtual QString serialPort() const = 0;
+ virtual qint32 baudrate() const = 0;
+ virtual QSerialPort::Parity parity() const = 0;
+ virtual QSerialPort::DataBits dataBits() const = 0;
+ virtual QSerialPort::StopBits stopBits() = 0;
+ virtual int numberOfRetries() const = 0;
+ virtual int timeout() const = 0;
+
+ virtual bool connected() const = 0;
+
+ // Requests
+ virtual ModbusRtuReply *readCoil(int slaveAddress, int registerAddress, quint16 size = 1) = 0;
+ virtual ModbusRtuReply *readDiscreteInput(int slaveAddress, int registerAddress, quint16 size = 1) = 0;
+ virtual ModbusRtuReply *readInputRegister(int slaveAddress, int registerAddress, quint16 size = 1) = 0;
+ virtual ModbusRtuReply *readHoldingRegister(int slaveAddress, int registerAddress, quint16 size = 1) = 0;
+
+ virtual ModbusRtuReply *writeCoils(int slaveAddress, int registerAddress, const QVector &values) = 0;
+ virtual ModbusRtuReply *writeHoldingRegisters(int slaveAddress, int registerAddress, const QVector &values) = 0;
+
+protected:
+ explicit ModbusRtuMaster(QObject *parent = nullptr) : QObject(parent) { };
+ virtual ~ModbusRtuMaster() = default;
+
+signals:
+ void connectedChanged(bool connected);
+ void serialPortChanged(const QString &serialPort);
+ void baudrateChanged(quint32 baudrate);
+ void parityChanged(QSerialPort::Parity parity);
+ void dataBitsChanged(QSerialPort::DataBits dataBits);
+ void stopBitsChanged(QSerialPort::StopBits stopBits);
+ void numberOfRetriesChanged(int numberOfRetries);
+ void timeoutChanged(int timeout);
+
+};
+
+inline QDebug operator<<(QDebug debug, ModbusRtuMaster *modbusRtuMaster) {
+ debug.nospace() << "ModbusRtuMaster(" << modbusRtuMaster->modbusUuid().toString();
+ debug.nospace() << ", " << modbusRtuMaster->serialPort();
+ debug.nospace() << ", BaudRate: " << modbusRtuMaster->baudrate();
+ debug.nospace() << ", " << modbusRtuMaster->dataBits();
+ debug.nospace() << ", " << modbusRtuMaster->stopBits();
+ debug.nospace() << ", " << modbusRtuMaster->parity();
+ debug.nospace() << ", Retries: " << modbusRtuMaster->numberOfRetries();
+ debug.nospace() << ", Timeout: " << modbusRtuMaster->timeout() << "ms)";
+ return debug.space();
+};
+
+
+#endif // MODBUSRTUMASTER_H
diff --git a/libnymea/hardware/modbus/modbusrtureply.h b/libnymea/hardware/modbus/modbusrtureply.h
new file mode 100644
index 00000000..46e5a86a
--- /dev/null
+++ b/libnymea/hardware/modbus/modbusrtureply.h
@@ -0,0 +1,74 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* 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 MODBUSRTUREPLY_H
+#define MODBUSRTUREPLY_H
+
+#include
+#include
+
+class ModbusRtuReply : public QObject
+{
+ Q_OBJECT
+public:
+ enum Error {
+ NoError,
+ ReadError,
+ WriteError,
+ ConnectionError,
+ ConfigurationError,
+ TimeoutError,
+ ProtocolError,
+ ReplyAbortedError,
+ UnknownError
+ };
+ Q_ENUM(Error)
+
+ virtual bool isFinished() const = 0;
+
+ virtual int slaveAddress() const = 0;
+ virtual int registerAddress() const = 0;
+
+ virtual QString errorString() const = 0;
+ virtual ModbusRtuReply::Error error() const = 0;
+
+ virtual QVector result() const = 0;
+
+protected:
+ explicit ModbusRtuReply(QObject *parent = nullptr) : QObject(parent) { };
+ virtual ~ModbusRtuReply() = default;
+
+signals:
+ void finished();
+ void errorOccurred(ModbusRtuReply::Error error);
+
+};
+
+#endif // MODBUSRTUREPLY_H
diff --git a/libnymea/hardwaremanager.h b/libnymea/hardwaremanager.h
index 745a4ca9..4dab713e 100644
--- a/libnymea/hardwaremanager.h
+++ b/libnymea/hardwaremanager.h
@@ -44,6 +44,7 @@ class MqttProvider;
class I2CManager;
class ZigbeeHardwareResource;
class HardwareResource;
+class ModbusRtuHardwareResource;
class HardwareManager : public QObject
{
@@ -63,6 +64,7 @@ public:
virtual MqttProvider *mqttProvider() = 0;
virtual I2CManager *i2cManager() = 0;
virtual ZigbeeHardwareResource *zigbeeResource() = 0;
+ virtual ModbusRtuHardwareResource *modbusRtuResource() = 0;
protected:
void setResourceEnabled(HardwareResource* resource, bool enabled);
diff --git a/libnymea/libnymea.pro b/libnymea/libnymea.pro
index 02e7b57f..41752d75 100644
--- a/libnymea/libnymea.pro
+++ b/libnymea/libnymea.pro
@@ -3,7 +3,7 @@ include(../nymea.pri)
TARGET = nymea
TEMPLATE = lib
-QT += network bluetooth dbus
+QT += network bluetooth dbus serialport
QT -= gui
DEFINES += LIBNYMEA_LIBRARY
@@ -13,6 +13,9 @@ PKGCONFIG += nymea-zigbee nymea-mqtt
QMAKE_LFLAGS += -fPIC
HEADERS += \
+ hardware/modbus/modbusrtuhardwareresource.h \
+ hardware/modbus/modbusrtumaster.h \
+ hardware/modbus/modbusrtureply.h \
hardware/zigbee/zigbeehandler.h \
hardware/zigbee/zigbeehardwareresource.h \
integrations/browseractioninfo.h \
@@ -108,6 +111,7 @@ HEADERS += \
experiences/experienceplugin.h \
SOURCES += \
+ hardware/modbus/modbusrtuhardwareresource.cpp \
hardware/zigbee/zigbeehandler.cpp \
hardware/zigbee/zigbeehardwareresource.cpp \
integrations/browseractioninfo.cpp \
diff --git a/libnymea/nymeasettings.cpp b/libnymea/nymeasettings.cpp
index 417c7472..e57c3a25 100644
--- a/libnymea/nymeasettings.cpp
+++ b/libnymea/nymeasettings.cpp
@@ -64,6 +64,10 @@
This role will create the \b{mqttpolicies.conf} file and is used to store the \l{MqttPolicy}{MqttPolicies}.
\value SettingsRoleIOConnections
This role will create the \b{ioconnections.conf} file and is used to store the \l{IOConnection}{IOConnections}.
+ \value SettingsRoleZigbee
+ This role will create the \b{zigbee.conf} file and is used to store the Zigbee networks.
+ \value SettingsRoleModbusRtu
+ This role will create the \b{modbusrtu.conf} file and is used to store the modbus RTU resources.
*/
@@ -83,7 +87,7 @@ NymeaSettings::NymeaSettings(const SettingsRole &role, QObject *parent):
QString settingsPrefix = QCoreApplication::instance()->organizationName() + "/";
QString basePath;
- if (!qgetenv("SNAP").isEmpty()) {
+ if (!qEnvironmentVariableIsEmpty("SNAP")) {
basePath = QString(qgetenv("SNAP_DATA")) + "/";
settingsPrefix.clear(); // We don't want that in the snappy case...
} else if (settingsPrefix == "nymea-test/") {
@@ -125,6 +129,9 @@ NymeaSettings::NymeaSettings(const SettingsRole &role, QObject *parent):
case SettingsRoleZigbee:
fileName = "zigbee.conf";
break;
+ case SettingsRoleModbusRtu:
+ fileName = "modbusrtu.conf";
+ break;
}
m_settings = new QSettings(basePath + settingsPrefix + fileName, QSettings::IniFormat, this);
}
@@ -157,7 +164,7 @@ QString NymeaSettings::settingsPath()
QString path;
QString organisationName = QCoreApplication::instance()->organizationName();
- if (!qgetenv("SNAP").isEmpty()) {
+ if (!qEnvironmentVariableIsEmpty("SNAP")) {
path = QString(qgetenv("SNAP_DATA"));
} else if (organisationName == "nymea-test") {
path = "/tmp/" + organisationName;
@@ -174,7 +181,7 @@ QString NymeaSettings::translationsPath()
{
QString organisationName = QCoreApplication::instance()->organizationName();
- if (!qgetenv("SNAP").isEmpty()) {
+ if (!qEnvironmentVariableIsEmpty("SNAP")) {
return QString(qgetenv("SNAP") + "/usr/share/nymea/translations");
} else if (organisationName == "nymea-test") {
return "/tmp/" + organisationName;
@@ -188,7 +195,7 @@ QString NymeaSettings::storagePath()
{
QString path;
QString organisationName = QCoreApplication::instance()->organizationName();
- if (!qgetenv("SNAP").isEmpty()) {
+ if (!qEnvironmentVariableIsEmpty("SNAP")) {
path = QString(qgetenv("SNAP_DATA"));
} else if (organisationName == "nymea-test") {
path = "/tmp/" + organisationName;
diff --git a/libnymea/nymeasettings.h b/libnymea/nymeasettings.h
index 36301eb3..8c514b68 100644
--- a/libnymea/nymeasettings.h
+++ b/libnymea/nymeasettings.h
@@ -53,7 +53,9 @@ public:
SettingsRoleMqttPolicies,
SettingsRoleIOConnections,
SettingsRoleZigbee,
+ SettingsRoleModbusRtu
};
+ Q_ENUM(SettingsRole)
explicit NymeaSettings(const SettingsRole &role = SettingsRoleNone, QObject *parent = nullptr);
~NymeaSettings();
diff --git a/tests/auto/api.json b/tests/auto/api.json
index f088c2c0..b7ded3e8 100644
--- a/tests/auto/api.json
+++ b/tests/auto/api.json
@@ -150,6 +150,16 @@
"MediaBrowserIconSoundCloud",
"MediaBrowserIconRadioParadise"
],
+ "ModbusRtuError": [
+ "ModbusRtuErrorNoError",
+ "ModbusRtuErrorNotAvailable",
+ "ModbusRtuErrorUuidNotFound",
+ "ModbusRtuErrorHardwareNotFound",
+ "ModbusRtuErrorResourceBusy",
+ "ModbusRtuErrorNotSupported",
+ "ModbusRtuErrorInvalidTimeoutValue",
+ "ModbusRtuErrorConnectionFailed"
+ ],
"NetworkDeviceState": [
"NetworkDeviceStateUnknown",
"NetworkDeviceStateUnmanaged",
@@ -232,6 +242,27 @@
"ScriptMessageTypeLog",
"ScriptMessageTypeWarning"
],
+ "SerialPortDataBits": [
+ "SerialPortDataBitsData5",
+ "SerialPortDataBitsData6",
+ "SerialPortDataBitsData7",
+ "SerialPortDataBitsData8",
+ "SerialPortDataBitsUnknownDataBits"
+ ],
+ "SerialPortParity": [
+ "SerialPortParityNoParity",
+ "SerialPortParityEvenParity",
+ "SerialPortParityOddParity",
+ "SerialPortParitySpaceParity",
+ "SerialPortParityMarkParity",
+ "SerialPortParityUnknownParity"
+ ],
+ "SerialPortStopBits": [
+ "SerialPortStopBitsOneStop",
+ "SerialPortStopBitsOneAndHalfStop",
+ "SerialPortStopBitsTwoStop",
+ "SerialPortStopBitsUnknownStopBits"
+ ],
"SetupMethod": [
"SetupMethodJustAdd",
"SetupMethodDisplayPin",
@@ -1491,6 +1522,66 @@
"offset": "Int"
}
},
+ "ModbusRtu.AddModbusRtuMaster": {
+ "description": "Add a new modbus RTU master with the given configuration. The timeout value is in milli seconds and the minimum value is 10 ms.",
+ "params": {
+ "baudrate": "Uint",
+ "dataBits": "$ref:SerialPortDataBits",
+ "numberOfRetries": "Uint",
+ "parity": "$ref:SerialPortParity",
+ "serialPort": "String",
+ "stopBits": "$ref:SerialPortStopBits",
+ "timeout": "Uint"
+ },
+ "returns": {
+ "modbusError": "$ref:ModbusRtuError",
+ "o:modbusUuid": "Uuid"
+ }
+ },
+ "ModbusRtu.GetModbusRtuMasters": {
+ "description": "Get the list of configured modbus RTU masters.",
+ "params": {
+ },
+ "returns": {
+ "modbusError": "$ref:ModbusRtuError",
+ "o:modbusRtuMasters": [
+ "$ref:ModbusRtuMaster"
+ ]
+ }
+ },
+ "ModbusRtu.GetSerialPorts": {
+ "description": "Get the list of available serial ports from the host system.",
+ "params": {
+ },
+ "returns": {
+ "serialPorts": "$ref:SerialPorts"
+ }
+ },
+ "ModbusRtu.ReconfigureModbusRtuMaster": {
+ "description": "Reconfigure the modbus RTU master with the given UUID and configuration.",
+ "params": {
+ "baudrate": "Uint",
+ "dataBits": "$ref:SerialPortDataBits",
+ "modbusUuid": "Uuid",
+ "numberOfRetries": "Uint",
+ "parity": "$ref:SerialPortParity",
+ "serialPort": "String",
+ "stopBits": "$ref:SerialPortStopBits",
+ "timeout": "Uint"
+ },
+ "returns": {
+ "modbusError": "$ref:ModbusRtuError"
+ }
+ },
+ "ModbusRtu.RemoveModbusRtuMaster": {
+ "description": "Remove the modbus RTU master with the given modbus UUID.",
+ "params": {
+ "modbusUuid": "Uuid"
+ },
+ "returns": {
+ "modbusError": "$ref:ModbusRtuError"
+ }
+ },
"NetworkManager.ConnectWifiNetwork": {
"description": "Connect to the wifi network with the given ssid and password.",
"params": {
@@ -2351,6 +2442,36 @@
"logEntry": "$ref:LogEntry"
}
},
+ "ModbusRtu.ModbusRtuMasterAdded": {
+ "description": "Emitted whenever a new modbus RTU master has been added to the system.",
+ "params": {
+ "modbusRtuMaster": "$ref:ModbusRtuMaster"
+ }
+ },
+ "ModbusRtu.ModbusRtuMasterChanged": {
+ "description": "Emitted whenever a modbus RTU master has been changed in the system.",
+ "params": {
+ "modbusRtuMaster": "$ref:ModbusRtuMaster"
+ }
+ },
+ "ModbusRtu.ModbusRtuMasterRemoved": {
+ "description": "Emitted whenever a modbus RTU master has been removed from the system.",
+ "params": {
+ "modbusUuid": "Uuid"
+ }
+ },
+ "ModbusRtu.SerialPortAdded": {
+ "description": "Emitted whenever a serial port has been added to the system.",
+ "params": {
+ "serialPort": "$ref:SerialPort"
+ }
+ },
+ "ModbusRtu.SerialPortRemoved": {
+ "description": "Emitted whenever a serial port has been removed from the system.",
+ "params": {
+ "serialPort": "$ref:SerialPort"
+ }
+ },
"NetworkManager.NetworkStatusChanged": {
"description": "Emitted whenever a status of a NetworkManager changes.",
"params": {
@@ -2765,6 +2886,17 @@
"r:source": "$ref:LoggingSource",
"r:timestamp": "Uint"
},
+ "ModbusRtuMaster": {
+ "baudrate": "Uint",
+ "connected": "Bool",
+ "dataBits": "$ref:SerialPortDataBits",
+ "modbusUuid": "Uuid",
+ "numberOfRetries": "Uint",
+ "parity": "$ref:SerialPortParity",
+ "serialPort": "String",
+ "stopBits": "$ref:SerialPortStopBits",
+ "timeout": "Uint"
+ },
"MqttPolicy": {
"allowedPublishTopicFilters": "StringList",
"allowedSubscribeTopicFilters": "StringList",
@@ -2892,6 +3024,15 @@
"Scripts": [
"$ref:Script"
],
+ "SerialPort": {
+ "r:description": "String",
+ "r:manufacturer": "String",
+ "r:serialNumber": "String",
+ "r:systemLocation": "String"
+ },
+ "SerialPorts": [
+ "$ref:SerialPort"
+ ],
"ServerConfiguration": {
"address": "String",
"authenticationEnabled": "Bool",
diff --git a/tests/auto/autotests.pri b/tests/auto/autotests.pri
index e2572913..5d2153e6 100644
--- a/tests/auto/autotests.pri
+++ b/tests/auto/autotests.pri
@@ -2,6 +2,18 @@ QT += dbus testlib network sql websockets
CONFIG += testcase
CONFIG += link_pkgconfig
+# Qt serial bus module is officially available since Qt 5.8
+# but not all platforms host the qt serialbus package.
+# Let's check if the package exists, not the qt version
+packagesExist(Qt5SerialBus) {
+ message("Building with QtSerialBus support.")
+ # Qt += serialbus
+ PKGCONFIG += Qt5SerialBus
+ DEFINES += WITH_QTSERIALBUS
+} else {
+ message("Qt5SerialBus package not found. Building without QtSerialBus support.")
+}
+
INCLUDEPATH += $$top_srcdir/libnymea \
$$top_srcdir/libnymea-core \
$$top_srcdir/tests/testlib/ \
diff --git a/tests/auto/jsonrpc/testjsonrpc.cpp b/tests/auto/jsonrpc/testjsonrpc.cpp
index 813b7215..ddafd3c0 100644
--- a/tests/auto/jsonrpc/testjsonrpc.cpp
+++ b/tests/auto/jsonrpc/testjsonrpc.cpp
@@ -674,7 +674,7 @@ void TestJSONRPC::enableDisableNotifications_legacy()
QStringList expectedNamespaces;
if (enabled == "true") {
- expectedNamespaces << "Actions" << "NetworkManager" << "Devices" << "Integrations" << "System" << "Rules" << "States" << "Logging" << "Tags" << "AppData" << "JSONRPC" << "Configuration" << "Events" << "Scripts" << "Users" << "Zigbee";
+ expectedNamespaces << "Actions" << "NetworkManager" << "Devices" << "Integrations" << "System" << "Rules" << "States" << "Logging" << "Tags" << "AppData" << "JSONRPC" << "Configuration" << "Events" << "Scripts" << "Users" << "Zigbee" << "ModbusRtu";
}
std::sort(expectedNamespaces.begin(), expectedNamespaces.end());
diff --git a/tests/testlib/testlib.pro b/tests/testlib/testlib.pro
index 2c27f65e..b1768333 100644
--- a/tests/testlib/testlib.pro
+++ b/tests/testlib/testlib.pro
@@ -5,6 +5,14 @@ include(../../nymea.pri)
QT += testlib dbus network sql websockets
+# Qt serial bus module is officially available since Qt 5.8
+# but not all platforms host the qt serialbus package.
+# Let's check if the package exists, not the qt version
+packagesExist(Qt5SerialBus) {
+ Qt += serialbus
+ DEFINES += WITH_QTSERIALBUS
+}
+
INCLUDEPATH += $$top_srcdir/libnymea \
$$top_srcdir/libnymea-core