mirror of https://github.com/nymea/nymea.git
modbus RTU: add platform configuration
parent
c0532a64ea
commit
87ed98b72f
|
|
@ -1,6 +1,6 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2024, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
|
|
@ -34,6 +34,12 @@
|
|||
|
||||
#include "modbusrtumasterimpl.h"
|
||||
#include "hardware/serialport/serialportmonitor.h"
|
||||
#include "qglobal.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
|
||||
NYMEA_LOGGING_CATEGORY(dcModbusRtu, "ModbusRtu")
|
||||
|
||||
|
|
@ -43,18 +49,75 @@ ModbusRtuManager::ModbusRtuManager(SerialPortMonitor *serialPortMonitor, QObject
|
|||
QObject(parent),
|
||||
m_serialPortMonitor(serialPortMonitor)
|
||||
{
|
||||
if (!supported())
|
||||
return;
|
||||
|
||||
// Load the platform config
|
||||
loadPlatformConfiguration();
|
||||
|
||||
// Load uart configurations
|
||||
loadRtuMasters();
|
||||
|
||||
connect(m_serialPortMonitor, &SerialPortMonitor::serialPortAdded, this, [=](const QSerialPortInfo &serialPortInfo){
|
||||
qCDebug(dcModbusRtu()) << "Serial port added. Verify modbus RTU masters...";
|
||||
// Initialize the list of serial ports already available in the system...
|
||||
foreach (const SerialPort &serialPort, m_serialPortMonitor->serialPorts()) {
|
||||
onSerialPortAdded(serialPort);
|
||||
}
|
||||
|
||||
connect(m_serialPortMonitor, &SerialPortMonitor::serialPortAdded, this, &ModbusRtuManager::onSerialPortAdded);
|
||||
connect(m_serialPortMonitor, &SerialPortMonitor::serialPortRemoved, this, &ModbusRtuManager::onSerialPortRemoved);
|
||||
|
||||
// Try to connect the modbus RTU masters
|
||||
foreach (ModbusRtuMaster *modbusMaster, m_modbusRtuMasters.values()) {
|
||||
ModbusRtuMasterImpl *modbusMasterImpl = qobject_cast<ModbusRtuMasterImpl *>(modbusMaster);
|
||||
if (!modbusMasterImpl->connectDevice()) {
|
||||
qCWarning(dcModbusRtu()) << "Failed to connect modbus RTU master. Could not connect to" << modbusMaster;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SerialPorts ModbusRtuManager::serialPorts() const
|
||||
{
|
||||
return m_serialPorts.values();
|
||||
}
|
||||
|
||||
bool ModbusRtuManager::serialPortAvailable(const QString &systemLocation) const
|
||||
{
|
||||
return m_serialPorts.keys().contains(systemLocation);
|
||||
}
|
||||
|
||||
void ModbusRtuManager::onSerialPortAdded(const SerialPort &serialPort)
|
||||
{
|
||||
if (m_serialPorts.contains(serialPort.systemLocation()))
|
||||
return;
|
||||
|
||||
// Depending on the platform configuration we have to filter out those serial ports which are not suitable for modbus RTU communication.
|
||||
foreach (const ModbusRtuPlatformConfiguration &platformConfig, m_platformConfigurations) {
|
||||
if (platformConfig.serialPort == serialPort.systemLocation()) {
|
||||
if (platformConfig.usable) {
|
||||
SerialPort internalPort = serialPort;
|
||||
internalPort.setCustomDescription(platformConfig.description);
|
||||
m_serialPorts.insert(internalPort.systemLocation(), internalPort);
|
||||
emit serialPortAdded(internalPort);
|
||||
} else {
|
||||
qCDebug(dcModbusRtu()) << "Serial port" << serialPort.systemLocation() << "added but not usable for modbus RTU according to the platorm configuration.";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_serialPorts.contains(serialPort.systemLocation())) {
|
||||
m_serialPorts.insert(serialPort.systemLocation(), serialPort);
|
||||
emit serialPortAdded(serialPort);
|
||||
}
|
||||
|
||||
qCDebug(dcModbusRtu()) << "Serial port" << serialPort.systemLocation() << "added. Evaluate modbus RTU masters...";
|
||||
|
||||
// Check if we have to reconnect any modbus RTU masters
|
||||
foreach (ModbusRtuMaster *modbusMaster, m_modbusRtuMasters.values()) {
|
||||
ModbusRtuMasterImpl *modbusMasterImpl = qobject_cast<ModbusRtuMasterImpl *>(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->connected() && modbusMasterImpl->serialPort() == serialPort.systemLocation()) {
|
||||
if (!modbusMasterImpl->connectDevice()) {
|
||||
qCDebug(dcModbusRtu()) << "Reconnect" << modbusMaster << "failed.";
|
||||
} else {
|
||||
|
|
@ -62,21 +125,15 @@ ModbusRtuManager::ModbusRtuManager(SerialPortMonitor *serialPortMonitor, QObject
|
|||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Try to connect the modbus rtu masters
|
||||
foreach (ModbusRtuMaster *modbusMaster, m_modbusRtuMasters.values()) {
|
||||
ModbusRtuMasterImpl *modbusMasterImpl = qobject_cast<ModbusRtuMasterImpl *>(modbusMaster);
|
||||
if (!modbusMasterImpl->connectDevice()) {
|
||||
qCWarning(dcModbusRtu()) << "Failed to connect modbus RTU master. Could not connect to" << modbusMaster;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SerialPortMonitor *ModbusRtuManager::serialPortMonitor() const
|
||||
void ModbusRtuManager::onSerialPortRemoved(const SerialPort &serialPort)
|
||||
{
|
||||
return m_serialPortMonitor;
|
||||
if (m_serialPorts.contains(serialPort.systemLocation())) {
|
||||
qCDebug(dcModbusRtu()) << "Serial port removed" << serialPort.systemLocation();
|
||||
m_serialPorts.remove(serialPort.systemLocation());
|
||||
emit serialPortRemoved(serialPort);
|
||||
}
|
||||
}
|
||||
|
||||
bool ModbusRtuManager::supported() const
|
||||
|
|
@ -211,6 +268,58 @@ ModbusRtuManager::ModbusRtuError ModbusRtuManager::removeModbusRtuMaster(const Q
|
|||
return ModbusRtuErrorNoError;
|
||||
}
|
||||
|
||||
void ModbusRtuManager::loadPlatformConfiguration()
|
||||
{
|
||||
QFileInfo platformConfigurationFileInfo(NymeaSettings::settingsPath() + QDir::separator() + "modbus-rtu-platform.conf");
|
||||
|
||||
if (platformConfigurationFileInfo.exists()) {
|
||||
qCDebug(dcModbusRtu()) << "Loading modbus RTU platform configuration from" << platformConfigurationFileInfo.absoluteFilePath();
|
||||
QFile platformConfigurationFile(platformConfigurationFileInfo.absoluteFilePath());
|
||||
if (!platformConfigurationFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
qCWarning(dcModbusRtu()) << "Failed to open modbus RTU platform configuration file"
|
||||
<< platformConfigurationFileInfo.absoluteFilePath() << ":"
|
||||
<< platformConfigurationFile.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = platformConfigurationFile.readAll();
|
||||
platformConfigurationFile.close();
|
||||
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
|
||||
if (jsonError.error != QJsonParseError::NoError) {
|
||||
qCWarning(dcModbusRtu()) << "Failed to parse JSON data from modbus RTU platform configuration file"
|
||||
<< platformConfigurationFileInfo.absoluteFilePath() << ":"
|
||||
<< jsonError.errorString();
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure we have a clean start
|
||||
m_platformConfigurations.clear();
|
||||
|
||||
QVariantMap platformConfigMap = jsonDoc.toVariant().toMap();
|
||||
foreach (const QVariant &configVariant, platformConfigMap.value("interfaces").toList()) {
|
||||
QVariantMap configMap = configVariant.toMap();
|
||||
ModbusRtuPlatformConfiguration config;
|
||||
config.name = configMap.value("name").toString();
|
||||
config.description = configMap.value("description").toString();
|
||||
config.serialPort = configMap.value("serialPort").toString();
|
||||
config.usable = configMap.value("usable").toBool();
|
||||
m_platformConfigurations.append(config);
|
||||
}
|
||||
|
||||
if (m_platformConfigurations.isEmpty()) {
|
||||
qCDebug(dcModbusRtu()) << "No platform configurations available";
|
||||
} else {
|
||||
qCDebug(dcModbusRtu()) << "Loaded successfully" << m_platformConfigurations.count() << "platform configurations";
|
||||
foreach(const ModbusRtuPlatformConfiguration &config, m_platformConfigurations) {
|
||||
qCDebug(dcModbusRtu()).nospace() << "- Platform configuration: " << config.description << " (" << config.name << ") "
|
||||
<< "Serial port: " << config.serialPort << " usable: " << config.usable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModbusRtuManager::loadRtuMasters()
|
||||
{
|
||||
if (!supported())
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2024, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
|
|
@ -37,10 +37,10 @@
|
|||
#include <QTimer>
|
||||
|
||||
#include "hardware/modbus/modbusrtumaster.h"
|
||||
#include "hardware/serialport/serialportmonitor.h"
|
||||
|
||||
namespace nymeaserver {
|
||||
|
||||
class SerialPortMonitor;
|
||||
class ModbusRtuMasterImpl;
|
||||
|
||||
class ModbusRtuManager : public QObject
|
||||
|
|
@ -62,8 +62,6 @@ public:
|
|||
explicit ModbusRtuManager(SerialPortMonitor *serialPortMonitor, QObject *parent = nullptr);
|
||||
~ModbusRtuManager() = default;
|
||||
|
||||
SerialPortMonitor *serialPortMonitor() const;
|
||||
|
||||
bool supported() const;
|
||||
|
||||
QList<ModbusRtuMaster *> modbusRtuMasters() const;
|
||||
|
|
@ -74,14 +72,37 @@ public:
|
|||
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);
|
||||
|
||||
// Returns the platform specific list of available serial ports for modbus RTU
|
||||
SerialPorts serialPorts() const;
|
||||
bool serialPortAvailable(const QString &systemLocation) const;
|
||||
|
||||
signals:
|
||||
void serialPortAdded(const SerialPort &serialPort);
|
||||
void serialPortRemoved(const SerialPort &serialPort);
|
||||
|
||||
void modbusRtuMasterAdded(ModbusRtuMaster *modbusRtuMaster);
|
||||
void modbusRtuMasterRemoved(ModbusRtuMaster *modbusRtuMaster);
|
||||
void modbusRtuMasterChanged(ModbusRtuMaster *modbusRtuMaster);
|
||||
|
||||
private slots:
|
||||
void onSerialPortAdded(const SerialPort &serialPort);
|
||||
void onSerialPortRemoved(const SerialPort &serialPort);
|
||||
|
||||
private:
|
||||
typedef struct ModbusRtuPlatformConfiguration {
|
||||
QString name;
|
||||
QString description;
|
||||
QString serialPort;
|
||||
bool usable;
|
||||
} ModbusRtuPlatformConfiguration;
|
||||
|
||||
QList<ModbusRtuPlatformConfiguration> m_platformConfigurations;
|
||||
|
||||
QHash<QUuid, ModbusRtuMaster *> m_modbusRtuMasters;
|
||||
SerialPortMonitor *m_serialPortMonitor = nullptr;
|
||||
QHash<QString, SerialPort> m_serialPorts;
|
||||
|
||||
void loadPlatformConfiguration();
|
||||
|
||||
void loadRtuMasters();
|
||||
void saveModbusRtuMaster(ModbusRtuMaster *modbusRtuMaster);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ 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 description READ customDescription) // Note: give the possibility to use a custom description
|
||||
Q_PROPERTY(QString serialNumber READ serialNumber)
|
||||
|
||||
public:
|
||||
|
|
@ -85,6 +85,22 @@ public:
|
|||
SerialPort() : QSerialPortInfo() { };
|
||||
explicit SerialPort(const QSerialPortInfo &other) : QSerialPortInfo(other) { };
|
||||
|
||||
QString customDescription() const {
|
||||
// Note: this creats the possibility to override the desciption of
|
||||
// serial port for the JSON RPC API.
|
||||
if (m_customDescription.isEmpty())
|
||||
return description();
|
||||
|
||||
return m_customDescription;
|
||||
}
|
||||
|
||||
void setCustomDescription(const QString &customDescription) {
|
||||
m_customDescription = customDescription;
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_customDescription;
|
||||
|
||||
};
|
||||
|
||||
class SerialPorts : public QList<SerialPort>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2024, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
|
|
@ -140,13 +140,13 @@ ModbusRtuHandler::ModbusRtuHandler(ModbusRtuManager *modbusRtuManager, QObject *
|
|||
registerMethod("ReconfigureModbusRtuMaster", description, params, returns);
|
||||
|
||||
// Serial port monitor
|
||||
connect(modbusRtuManager->serialPortMonitor(), &SerialPortMonitor::serialPortAdded, this, [=](const SerialPort &serialPort){
|
||||
connect(modbusRtuManager, &ModbusRtuManager::serialPortAdded, this, [=](const SerialPort &serialPort){
|
||||
QVariantMap params;
|
||||
params.insert("serialPort", pack(serialPort));
|
||||
emit SerialPortAdded(params);
|
||||
});
|
||||
|
||||
connect(modbusRtuManager->serialPortMonitor(), &SerialPortMonitor::serialPortRemoved, this, [=](const SerialPort &serialPort){
|
||||
connect(modbusRtuManager, &ModbusRtuManager::serialPortRemoved, this, [=](const SerialPort &serialPort){
|
||||
QVariantMap params;
|
||||
params.insert("serialPort", pack(serialPort));
|
||||
emit SerialPortRemoved(params);
|
||||
|
|
@ -183,7 +183,7 @@ JsonReply *ModbusRtuHandler::GetSerialPorts(const QVariantMap ¶ms)
|
|||
|
||||
QVariantMap returnMap;
|
||||
QVariantList portList;
|
||||
foreach (const SerialPort &serialPort, m_modbusRtuManager->serialPortMonitor()->serialPorts()) {
|
||||
foreach (const SerialPort &serialPort, m_modbusRtuManager->serialPorts()) {
|
||||
portList << pack(serialPort);
|
||||
}
|
||||
returnMap.insert("serialPorts", portList);
|
||||
|
|
|
|||
Loading…
Reference in New Issue