Introduce libnymea-modbus
Improve tool and prepare autogeneration of connection classes
This commit is contained in:
parent
03c77ef3a3
commit
62f78f5e90
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@
|
||||
builddir
|
||||
doc/html
|
||||
*.qm
|
||||
__pycache__
|
||||
|
||||
30
debian/control
vendored
30
debian/control
vendored
@ -2,6 +2,7 @@ Source: nymea-plugins-modbus
|
||||
Section: utils
|
||||
Priority: options
|
||||
Maintainer: nymea GmbH <developer@nymea.io>
|
||||
Standards-Version: 3.9.3
|
||||
Build-depends: debhelper (>= 9.0.0),
|
||||
libnymea-dev (>= 0.17),
|
||||
libnymea-gpio-dev,
|
||||
@ -10,8 +11,8 @@ Build-depends: debhelper (>= 9.0.0),
|
||||
nymea-dev-tools:native,
|
||||
pkg-config,
|
||||
qtbase5-dev,
|
||||
libi2c-dev
|
||||
Standards-Version: 3.9.3
|
||||
libi2c-dev,
|
||||
python3:native
|
||||
|
||||
|
||||
Package: libnymea-sunspec1
|
||||
@ -22,6 +23,7 @@ Depends: ${shlibs:Depends},
|
||||
Description: nymea.io sunspec library
|
||||
This package contains the nymea sunspec library.
|
||||
|
||||
|
||||
Package: libnymea-sunspec-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
@ -34,6 +36,30 @@ Depends: ${shlibs:Depends},
|
||||
Description: The main libraries and header files for developing with nymea sunspec.
|
||||
This package contains the nymea sunspec library - development files.
|
||||
|
||||
|
||||
Package: libnymea-modbus
|
||||
Architecture: any
|
||||
Section: libs
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends}
|
||||
Description: nymea modbus integration plugins library
|
||||
This package contains the nymea modbus library for integration plugins.
|
||||
|
||||
|
||||
Package: libnymea-modbus-dev
|
||||
Section: libdevel
|
||||
Architecture: any
|
||||
Multi-Arch: same
|
||||
Depends: ${shlibs:Depends},
|
||||
${misc:Depends},
|
||||
libnymea-modbus (= ${binary:Version}),
|
||||
pkg-config,
|
||||
qtbase5-dev,
|
||||
python3,
|
||||
Description: The main libraries and header files for developing with modbus based nymea integration plugins.
|
||||
This package contains the nymea modbus integration plugin library - development files.
|
||||
|
||||
|
||||
Package: nymea-plugin-alphainnotec
|
||||
Architecture: any
|
||||
Section: libs
|
||||
|
||||
4
debian/libnymea-modbus-dev.install.in
vendored
Normal file
4
debian/libnymea-modbus-dev.install.in
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/libnymea-modbus.so
|
||||
usr/include/nymea-modbus/
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/pkgconfig/nymea-modbus.pc
|
||||
|
||||
4
debian/libnymea-modbus.install.in
vendored
Normal file
4
debian/libnymea-modbus.install.in
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/libnymea-modbus.so.1
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/libnymea-modbus.so.1.0
|
||||
usr/lib/@DEB_HOST_MULTIARCH@/libnymea-modbus.so.1.0.0
|
||||
|
||||
1
debian/rules
vendored
1
debian/rules
vendored
@ -21,5 +21,6 @@ override_dh_auto_clean:
|
||||
dh_auto_clean
|
||||
find -name *plugininfo.h -exec rm {} \;
|
||||
find -name *.qm -exec rm {} \;
|
||||
find -name "autogenerated" -type d -exec rm -rvf {} +
|
||||
rm -rf $(PREPROCESS_FILES:.in=)
|
||||
|
||||
|
||||
57
libnymea-modbus/libnymea-modbus.pro
Normal file
57
libnymea-modbus/libnymea-modbus.pro
Normal file
@ -0,0 +1,57 @@
|
||||
QMAKE_CXXFLAGS += -Werror -std=c++11 -z defs
|
||||
QMAKE_LFLAGS += -std=c++11 -z defs
|
||||
|
||||
QT += network serialbus
|
||||
|
||||
CONFIG += link_pkgconfig
|
||||
PKGCONFIG += nymea
|
||||
|
||||
TARGET = nymea-modbus
|
||||
TEMPLATE = lib
|
||||
|
||||
gcc {
|
||||
COMPILER_VERSION = $$system($$QMAKE_CXX " -dumpversion")
|
||||
COMPILER_MAJOR_VERSION = $$str_member($$COMPILER_VERSION)
|
||||
greaterThan(COMPILER_MAJOR_VERSION, 7): QMAKE_CXXFLAGS += -Wno-deprecated-copy
|
||||
}
|
||||
|
||||
HEADERS += \
|
||||
modbusdatautils.h \
|
||||
modbustcpmaster.h
|
||||
|
||||
SOURCES += \
|
||||
modbusdatautils.cpp \
|
||||
modbustcpmaster.cpp
|
||||
|
||||
|
||||
# define install target
|
||||
target.path = $$[QT_INSTALL_LIBS]
|
||||
INSTALLS += target
|
||||
|
||||
# install modbustool for external plugins
|
||||
modbustoolpri.files = modbus-tool.pri
|
||||
modbustoolpri.path = $$[QT_INSTALL_PREFIX]/include/nymea-modbus/
|
||||
modbustool.files = tools/generate-connection.py
|
||||
modbustool.path = $$[QT_INSTALL_PREFIX]/include/nymea-modbus/tools/
|
||||
modbustoolmodules.files = tools/connectiontool/*.py
|
||||
modbustoolmodules.path = $$[QT_INSTALL_PREFIX]/include/nymea-modbus/tools/connectiontool/
|
||||
INSTALLS += modbustoolpri modbustool modbustoolmodules
|
||||
|
||||
# install header file with relative subdirectory
|
||||
for (header, HEADERS) {
|
||||
path = $$[QT_INSTALL_PREFIX]/include/nymea-modbus/$${dirname(header)}
|
||||
eval(headers_$${path}.files += $${header})
|
||||
eval(headers_$${path}.path = $${path})
|
||||
eval(INSTALLS *= headers_$${path})
|
||||
}
|
||||
|
||||
# Create pkgconfig file
|
||||
CONFIG += create_pc create_prl no_install_prl
|
||||
QMAKE_PKGCONFIG_NAME = libnymea-modbus
|
||||
QMAKE_PKGCONFIG_DESCRIPTION = nymea modbus integrations development library
|
||||
QMAKE_PKGCONFIG_PREFIX = $$[QT_INSTALL_PREFIX]
|
||||
QMAKE_PKGCONFIG_INCDIR = $$[QT_INSTALL_PREFIX]/include/nymea-modbus/
|
||||
QMAKE_PKGCONFIG_LIBDIR = $$target.path
|
||||
QMAKE_PKGCONFIG_VERSION = 1.0.0
|
||||
QMAKE_PKGCONFIG_FILE = nymea-modbus
|
||||
QMAKE_PKGCONFIG_DESTDIR = pkgconfig
|
||||
56
libnymea-modbus/modbus-tool.pri
Normal file
56
libnymea-modbus/modbus-tool.pri
Normal file
@ -0,0 +1,56 @@
|
||||
# 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 <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
# For any further details and any questions please contact us under
|
||||
# contact@nymea.io or see our FAQ/Licensing Information on
|
||||
# https://nymea.io/license/faq
|
||||
|
||||
# This project include file is meant to be used by nymea modbus integration plugins.
|
||||
# For external plugins you can generate connection by including this project like following:
|
||||
|
||||
# # Generate modbus connection
|
||||
# MODBUS_CONNECTIONS += modbus-registers.json
|
||||
# MODBUS_TOOLS_CONFIG += VERBOSE
|
||||
# include($$[QT_INSTALL_PREFIX]/include/nymea-modbus/modbus-tool.pri)
|
||||
|
||||
# On each qmake run the classes will be generated in the build directory.
|
||||
|
||||
GENERATE_MODBUS_CONNECTION_BINARY=$${PWD}/tools/generate-connection.py
|
||||
|
||||
for(registerDefinition, MODBUS_CONNECTIONS) {
|
||||
contains(MODBUS_TOOLS_CONFIG, VERBOSE) {
|
||||
message("Generating modbus connection class for $${registerDefinition} (verbose)")
|
||||
system(python3 $${GENERATE_MODBUS_CONNECTION_BINARY} -j $${_PRO_FILE_PWD_}/$${registerDefinition} -o $${OUT_PWD}/autogenerated -v)
|
||||
} else {
|
||||
message("Generating class for $${registerDefinition}")
|
||||
system(python3 $${GENERATE_MODBUS_CONNECTION_BINARY} -j $${_PRO_FILE_PWD_}/$${registerDefinition} -o $${OUT_PWD}/autogenerated)
|
||||
}
|
||||
}
|
||||
|
||||
# Add all generated pri files to the project
|
||||
MODBUS_CONNECTIONS_INCLUDES = $$files($${OUT_PWD}/autogenerated/*.pri)
|
||||
for(MODBUS_CONNECTION, MODBUS_CONNECTIONS_INCLUDES) {
|
||||
message("Adding generated connection to project $${MODBUS_CONNECTION}")
|
||||
include($${MODBUS_CONNECTION})
|
||||
INCLUDEPATH += $${OUT_PWD}/autogenerated
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -30,8 +30,7 @@
|
||||
|
||||
#include "modbustcpmaster.h"
|
||||
|
||||
#include <loggingcategories.h>
|
||||
NYMEA_LOGGING_CATEGORY(dcModbusTCP, "ModbusTCP")
|
||||
Q_LOGGING_CATEGORY(dcModbusTcpMaster, "ModbusTcpMaster")
|
||||
|
||||
ModbusTCPMaster::ModbusTCPMaster(const QHostAddress &hostAddress, uint port, QObject *parent) :
|
||||
QObject(parent),
|
||||
@ -91,7 +90,7 @@ bool ModbusTCPMaster::connectDevice() {
|
||||
|
||||
// Only connect if we are in the unconnected state
|
||||
if (m_modbusTcpClient->state() == QModbusDevice::UnconnectedState) {
|
||||
qCDebug(dcModbusTCP()) << "Connecting modbus TCP client to" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port);
|
||||
qCDebug(dcModbusTcpMaster()) << "Connecting modbus TCP client to" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port);
|
||||
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkPortParameter, m_port);
|
||||
m_modbusTcpClient->setConnectionParameter(QModbusDevice::NetworkAddressParameter, m_hostAddress.toString());
|
||||
m_modbusTcpClient->setTimeout(m_timeout);
|
||||
@ -101,7 +100,7 @@ bool ModbusTCPMaster::connectDevice() {
|
||||
// Restart the timer in case of connecting not finished yet or closing
|
||||
m_reconnectTimer->start();
|
||||
} else {
|
||||
qCWarning(dcModbusTCP()) << "Connect modbus TCP device" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port) << "called, but the socket is currently in the" << m_modbusTcpClient->state();
|
||||
qCWarning(dcModbusTcpMaster()) << "Connect modbus TCP device" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port) << "called, but the socket is currently in the" << m_modbusTcpClient->state();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -119,7 +118,7 @@ void ModbusTCPMaster::disconnectDevice()
|
||||
|
||||
bool ModbusTCPMaster::reconnectDevice()
|
||||
{
|
||||
qCWarning(dcModbusTCP()) << "Reconnecting modbus TCP device" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port);
|
||||
qCWarning(dcModbusTcpMaster()) << "Reconnecting modbus TCP device" << QString("%1:%2").arg(m_hostAddress.toString()).arg(m_port);
|
||||
if (!m_modbusTcpClient)
|
||||
return false;
|
||||
|
||||
@ -184,12 +183,12 @@ QUuid ModbusTCPMaster::readCoil(uint slaveAddress, uint registerAddress, uint si
|
||||
emit receivedCoil(reply->serverAddress(), modbusAddress, unit.values());
|
||||
} else {
|
||||
emit readRequestExecuted(requestId, false);
|
||||
qCWarning(dcModbusTCP()) << "Read response error:" << reply->error();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read response error:" << reply->error();
|
||||
}
|
||||
});
|
||||
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
qCWarning(dcModbusTCP()) << "Modbus reply error:" << error;
|
||||
qCWarning(dcModbusTcpMaster()) << "Modbus reply error:" << error;
|
||||
emit readRequestError(requestId, reply->errorString());
|
||||
emit reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
@ -200,7 +199,7 @@ QUuid ModbusTCPMaster::readCoil(uint slaveAddress, uint registerAddress, uint si
|
||||
return QUuid();
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return QUuid();
|
||||
}
|
||||
return requestId;
|
||||
@ -226,13 +225,13 @@ QUuid ModbusTCPMaster::writeHoldingRegisters(uint slaveAddress, uint registerAdd
|
||||
emit receivedHoldingRegister(reply->serverAddress(), modbusAddress, unit.values());
|
||||
} else {
|
||||
emit writeRequestExecuted(requestId, false);
|
||||
qCWarning(dcModbusTCP()) << "Read response error:" << reply->error();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
qCWarning(dcModbusTCP()) << "Modbus replay error:" << error;
|
||||
qCWarning(dcModbusTcpMaster()) << "Modbus replay error:" << error;
|
||||
emit writeRequestError(requestId, reply->errorString());
|
||||
emit reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
@ -243,7 +242,7 @@ QUuid ModbusTCPMaster::writeHoldingRegisters(uint slaveAddress, uint registerAdd
|
||||
return QUuid();
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return QUuid();
|
||||
}
|
||||
return requestId;
|
||||
@ -289,12 +288,12 @@ QUuid ModbusTCPMaster::readDiscreteInput(uint slaveAddress, uint registerAddress
|
||||
emit receivedDiscreteInput(reply->serverAddress(), modbusAddress, unit.values());
|
||||
} else {
|
||||
emit readRequestExecuted(requestId, false);
|
||||
qCWarning(dcModbusTCP()) << "Read response error:" << reply->error();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read response error:" << reply->error();
|
||||
}
|
||||
});
|
||||
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [requestId, this] (QModbusDevice::Error error){
|
||||
qCWarning(dcModbusTCP()) << "Modbus replay error:" << error;
|
||||
qCWarning(dcModbusTcpMaster()) << "Modbus replay error:" << error;
|
||||
QModbusReply *reply = qobject_cast<QModbusReply *>(sender());
|
||||
emit readRequestError(requestId, reply->errorString());
|
||||
emit reply->finished(); // To make sure it will be deleted
|
||||
@ -306,7 +305,7 @@ QUuid ModbusTCPMaster::readDiscreteInput(uint slaveAddress, uint registerAddress
|
||||
return QUuid();
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return QUuid();
|
||||
}
|
||||
return requestId;
|
||||
@ -333,12 +332,12 @@ QUuid ModbusTCPMaster::readInputRegister(uint slaveAddress, uint registerAddress
|
||||
emit receivedInputRegister(reply->serverAddress(), modbusAddress, unit.values());
|
||||
} else {
|
||||
emit readRequestExecuted(requestId, false);
|
||||
qCWarning(dcModbusTCP()) << "Read response error:" << reply->error();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read response error:" << reply->error();
|
||||
}
|
||||
});
|
||||
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
qCWarning(dcModbusTCP()) << "Modbus reply error:" << error;
|
||||
qCWarning(dcModbusTcpMaster()) << "Modbus reply error:" << error;
|
||||
emit readRequestError(requestId, reply->errorString());
|
||||
emit reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
@ -350,7 +349,7 @@ QUuid ModbusTCPMaster::readInputRegister(uint slaveAddress, uint registerAddress
|
||||
return QUuid();
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return QUuid();
|
||||
}
|
||||
return requestId;
|
||||
@ -378,7 +377,7 @@ QUuid ModbusTCPMaster::readHoldingRegister(uint slaveAddress, uint registerAddre
|
||||
|
||||
} else {
|
||||
emit writeRequestExecuted(requestId, false);
|
||||
qCWarning(dcModbusTCP()) << "Read response error:" << reply->error();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read response error:" << reply->error();
|
||||
emit readRequestError(requestId, reply->errorString());
|
||||
}
|
||||
reply->deleteLater();
|
||||
@ -386,7 +385,7 @@ QUuid ModbusTCPMaster::readHoldingRegister(uint slaveAddress, uint registerAddre
|
||||
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
|
||||
qCWarning(dcModbusTCP()) << "Modbus reply error:" << error;
|
||||
qCWarning(dcModbusTcpMaster()) << "Modbus reply error:" << error;
|
||||
emit readRequestError(requestId, reply->errorString());
|
||||
emit reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
@ -397,7 +396,7 @@ QUuid ModbusTCPMaster::readHoldingRegister(uint slaveAddress, uint registerAddre
|
||||
return QUuid();
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return QUuid();
|
||||
}
|
||||
return requestId;
|
||||
@ -431,13 +430,13 @@ QUuid ModbusTCPMaster::writeCoils(uint slaveAddress, uint registerAddress, const
|
||||
|
||||
} else {
|
||||
emit writeRequestExecuted(requestId, false);
|
||||
qCWarning(dcModbusTCP()) << "Write response error:" << reply->error();
|
||||
qCWarning(dcModbusTcpMaster()) << "Write response error:" << reply->error();
|
||||
}
|
||||
reply->deleteLater();
|
||||
});
|
||||
|
||||
connect(reply, &QModbusReply::errorOccurred, this, [reply, requestId, this] (QModbusDevice::Error error){
|
||||
qCWarning(dcModbusTCP()) << "Modbus reply error:" << error;
|
||||
qCWarning(dcModbusTcpMaster()) << "Modbus reply error:" << error;
|
||||
emit writeRequestError(requestId, reply->errorString());
|
||||
emit reply->finished(); // To make sure it will be deleted
|
||||
});
|
||||
@ -448,7 +447,7 @@ QUuid ModbusTCPMaster::writeCoils(uint slaveAddress, uint registerAddress, const
|
||||
return QUuid();
|
||||
}
|
||||
} else {
|
||||
qCWarning(dcModbusTCP()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
qCWarning(dcModbusTcpMaster()) << "Read error: " << m_modbusTcpClient->errorString();
|
||||
return QUuid();
|
||||
}
|
||||
return requestId;
|
||||
@ -461,12 +460,12 @@ QUuid ModbusTCPMaster::writeHoldingRegister(uint slaveAddress, uint registerAddr
|
||||
|
||||
void ModbusTCPMaster::onModbusErrorOccurred(QModbusDevice::Error error)
|
||||
{
|
||||
qCWarning(dcModbusTCP()) << "An error occured" << error;
|
||||
qCWarning(dcModbusTcpMaster()) << "An error occured" << error;
|
||||
}
|
||||
|
||||
void ModbusTCPMaster::onModbusStateChanged(QModbusDevice::State state)
|
||||
{
|
||||
qCDebug(dcModbusTCP()) << "Connection state changed for" << m_hostAddress << state;
|
||||
qCDebug(dcModbusTcpMaster()) << "Connection state changed for" << m_hostAddress << state;
|
||||
bool connected = (state == QModbusDevice::ConnectedState);
|
||||
if (m_connected != connected) {
|
||||
m_connected = connected;
|
||||
@ -1,6 +1,6 @@
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2021, nymea GmbH
|
||||
* Copyright 2013 - 2022, nymea GmbH
|
||||
* Contact: contact@nymea.io
|
||||
*
|
||||
* This file is part of nymea.
|
||||
@ -31,11 +31,14 @@
|
||||
#ifndef MODBUSTCPMASTER_H
|
||||
#define MODBUSTCPMASTER_H
|
||||
|
||||
#include <QUuid>
|
||||
#include <QTimer>
|
||||
#include <QObject>
|
||||
#include <QHostAddress>
|
||||
#include <QtSerialBus>
|
||||
#include <QTimer>
|
||||
#include <QUuid>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcModbusTcpMaster)
|
||||
|
||||
class ModbusTCPMaster : public QObject
|
||||
{
|
||||
@ -112,6 +115,7 @@ signals:
|
||||
void receivedDiscreteInput(uint slaveAddress, uint modbusRegister, const QVector<quint16> &values);
|
||||
void receivedHoldingRegister(uint slaveAddress, uint modbusRegister, const QVector<quint16> &values);
|
||||
void receivedInputRegister(uint slaveAddress, uint modbusRegister, const QVector<quint16> &values);
|
||||
|
||||
};
|
||||
|
||||
#endif // MODBUSTCPMASTER_H
|
||||
@ -14,7 +14,7 @@ The class will provide 2 main methods for fetching information from the modbus d
|
||||
* `initialize()` will read all registers with `"readSchedule": "init"` and emits the signal `initializationFinished()` once all replies returned.
|
||||
* `update()` can be used to update all registers with `"readSchedule": "update"`. The class will then fetch each register and update the specified value internally. If the value has changed, the `<propertyName>Changed()` signal will be emitted.
|
||||
|
||||
The reulting class will inhert from the `ModbusTCPMaster` class, providing easy access to all possible modbus operations and inform about the connected state.
|
||||
The resulting class will inhert from the `ModbusTCPMaster` class, providing easy access to all possible modbus operations and inform about the connected state.
|
||||
|
||||
|
||||
# JSON format
|
||||
@ -23,7 +23,8 @@ The basic structure of the modbus register JSON looks like following example:
|
||||
|
||||
```
|
||||
{
|
||||
"protocol": "TCP",
|
||||
"className": "MyConnection",
|
||||
"protocols": [ "TCP" ],
|
||||
"endianness": "BigEndian",
|
||||
"enums": [
|
||||
{
|
||||
@ -65,11 +66,65 @@ The basic structure of the modbus register JSON looks like following example:
|
||||
"access": "RO"
|
||||
},
|
||||
...
|
||||
],
|
||||
"blocks": [
|
||||
{
|
||||
"id": "blockName",
|
||||
"readSchedule": "update",
|
||||
"registers": [
|
||||
{
|
||||
"id": "registerOne",
|
||||
"address": 0,
|
||||
"size": 2,
|
||||
...
|
||||
},
|
||||
{
|
||||
"id": "registerOne",
|
||||
"address": 0,
|
||||
"size": 2,
|
||||
...
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## Class name
|
||||
|
||||
If no name class name has been passed to the generator script, the classname defined in the JSON file will be used.
|
||||
|
||||
The naming convention for the classname and the resulting source code files looks like this:
|
||||
|
||||
The class will be defined as
|
||||
|
||||
* `<ClassName><Protocol>Connection`.
|
||||
|
||||
The source code files will be calld:
|
||||
|
||||
* `classnameprotocolconnection.h`
|
||||
* `classnameprotocolconnection.cpp`
|
||||
|
||||
|
||||
|
||||
## Protocol
|
||||
|
||||
Depending on the communication protocol, a different base class will be used for the resulting output class.
|
||||
|
||||
There are 2 protocol types:
|
||||
|
||||
* `RTU`: a communication based on the RS485 serial RTU transport protocol
|
||||
* `TCP`: a communication based on the TCP transport protocol
|
||||
|
||||
If the modbus device supports both protocols and you want to generate a class for each protocol you can set the protocol to `BOTH` and a class for `RTU` and one for `TCP` will be generated.
|
||||
|
||||
...
|
||||
"protocol": "TCP",
|
||||
...
|
||||
|
||||
|
||||
## Endianness
|
||||
|
||||
When converting multiple registers to one data type (i.e. 2 registers uint16 values to one uint32), the order of the registers are important to align with the endianness of the data receiving.
|
||||
@ -79,15 +134,6 @@ There are 2 possibilities:
|
||||
* `BigEndian`: default if not specified: register bytes come in following order `[0, 1, 2, 3]`: `ABCD`
|
||||
* `LittleEndian`: register bytes come in following order `[0, 1, 2, 3]`: `CDAB`
|
||||
|
||||
## Protocol
|
||||
|
||||
Depending on the communication protocol, a different base class will be used for the resulting output class.
|
||||
|
||||
There are 2 possibilities:
|
||||
|
||||
* `RTU`: a communication based on the RS485 serial RTU transport protocol
|
||||
* `TCP`: a communication based on the TCP transport protocol
|
||||
|
||||
## Enums
|
||||
|
||||
Many modbus devices provide inforation using `Enums`, indicating a special state trough a defined list of values. If a register implements an enum, you can define it in the `enums` section. The `name` property defines the name of the enum, and the script will generate a c++ enum definition from this section. Each enum value will then be generated using `<EnumName><EnumValueName> = <value>`.
|
||||
@ -179,12 +225,12 @@ Example block:
|
||||
Change into your plugin sub directory.
|
||||
Assuming you wrote the registers.json file you can run now following command to generate your modbus class:
|
||||
|
||||
`$ python3 ../modbus/tools/generate-connection.py -j registers.json -o . -c MyModbusConnection`
|
||||
`$ python3 ../modbus/tools/generate-connection.py -j registers.json -o . -c MyModbus`
|
||||
|
||||
You the result will be a header and a source file called:
|
||||
|
||||
* `mymodbusconnection.h`
|
||||
* `mymodbusconnection.cpp`
|
||||
* `mymodbustcpconnection.h`
|
||||
* `mymodbustcpconnection.cpp`
|
||||
|
||||
You can include this class in your project and provide one connection per thing.
|
||||
|
||||
@ -14,6 +14,8 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import logging
|
||||
|
||||
from .toolcommon import *
|
||||
|
||||
##############################################################
|
||||
@ -63,7 +65,7 @@ def writePropertyGetSetMethodImplementationsRtu(fileDescriptor, className, regis
|
||||
elif registerDefinition['registerType'] == 'coils':
|
||||
writeLine(fileDescriptor, ' return m_modbusRtuMaster->writeCoils(m_slaveId, %s, values);' % (registerDefinition['address']))
|
||||
else:
|
||||
print('Error: invalid register type for writing.')
|
||||
logger.warning('Error: invalid register type for writing.')
|
||||
exit(1)
|
||||
|
||||
writeLine(fileDescriptor, '}')
|
||||
@ -222,7 +224,7 @@ def writeInternalBlockReadMethodDeclarationsRtu(fileDescriptor, blockDefinitions
|
||||
writeLine(fileDescriptor, ' - %s [%s] - Address: %s, Size: %s' % (registerDefinition['description'], registerDefinition['unit'], registerDefinition['address'], registerDefinition['size']))
|
||||
else:
|
||||
writeLine(fileDescriptor, ' -- %s - Address: %s, Size: %s' % (registerDefinition['description'], registerDefinition['address'], registerDefinition['size']))
|
||||
writeLine(fileDescriptor, ' */ ' )
|
||||
writeLine(fileDescriptor, ' */' )
|
||||
writeLine(fileDescriptor, ' ModbusRtuReply *readBlock%s();' % (blockName[0].upper() + blockName[1:]))
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
@ -63,7 +63,7 @@ def writePropertyGetSetMethodImplementationsTcp(fileDescriptor, className, regis
|
||||
elif registerDefinition['registerType'] == 'coils':
|
||||
writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, %s, values.count());' % (registerDefinition['address']))
|
||||
else:
|
||||
print('Error: invalid register type for writing.')
|
||||
logger.warning('Error: invalid register type for writing.')
|
||||
exit(1)
|
||||
|
||||
writeLine(fileDescriptor, ' request.setValues(values);')
|
||||
@ -218,7 +218,7 @@ def writeInternalBlockReadMethodDeclarationsTcp(fileDescriptor, blockDefinitions
|
||||
writeLine(fileDescriptor, ' - %s [%s] - Address: %s, Size: %s' % (registerDefinition['description'], registerDefinition['unit'], registerDefinition['address'], registerDefinition['size']))
|
||||
else:
|
||||
writeLine(fileDescriptor, ' - %s - Address: %s, Size: %s' % (registerDefinition['description'], registerDefinition['address'], registerDefinition['size']))
|
||||
writeLine(fileDescriptor, ' */ ' )
|
||||
writeLine(fileDescriptor, ' */' )
|
||||
writeLine(fileDescriptor, ' QModbusReply *readBlock%s();' % (blockName[0].upper() + blockName[1:]))
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
@ -19,8 +19,10 @@ import re
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import argparse
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('modbus-tools')
|
||||
|
||||
def convertToAlphaNumeric(text):
|
||||
finalText = ''
|
||||
@ -40,7 +42,7 @@ def convertToCamelCase(text, capitalize = False):
|
||||
s = convertToAlphaNumeric(text)
|
||||
s = s.replace("-", " ").replace("_", " ")
|
||||
words = s.split()
|
||||
#print('--> words', words)
|
||||
logger.debug('--> words', words)
|
||||
finalWords = []
|
||||
|
||||
for i in range(len(words)):
|
||||
@ -48,7 +50,7 @@ def convertToCamelCase(text, capitalize = False):
|
||||
if len(camelCaseSplit) == 0:
|
||||
finalWords.append(words[i])
|
||||
else:
|
||||
#print('--> camel split words', camelCaseSplit)
|
||||
logging.debug('Camel calse split words', camelCaseSplit)
|
||||
for j in range(len(camelCaseSplit)):
|
||||
finalWords.append(camelCaseSplit[j])
|
||||
|
||||
@ -60,12 +62,12 @@ def convertToCamelCase(text, capitalize = False):
|
||||
finalText = finalWords[0].capitalize() + ''.join(i.capitalize() for i in finalWords[1:])
|
||||
else:
|
||||
finalText = finalWords[0].lower() + ''.join(i.capitalize() for i in finalWords[1:])
|
||||
#print('Convert camel case:', text, '-->', finalText)
|
||||
logging.debug('Convert camel case:', text, '-->', finalText)
|
||||
return finalText
|
||||
|
||||
|
||||
def loadJsonFile(filePath):
|
||||
print('--> Loading JSON file', filePath)
|
||||
logger.info('Loading JSON file %s', filePath)
|
||||
jsonFile = open(filePath, 'r')
|
||||
return json.load(jsonFile)
|
||||
|
||||
@ -117,7 +119,7 @@ def writeLicenseHeader(fileDescriptor):
|
||||
|
||||
|
||||
def writeRegistersEnum(fileDescriptor, registerJson):
|
||||
print('Writing enum for all registers')
|
||||
logger.debug('Writing enum for all registers')
|
||||
|
||||
registerEnums = {}
|
||||
|
||||
@ -141,9 +143,9 @@ def writeRegistersEnum(fileDescriptor, registerJson):
|
||||
sortedRegistersKeys = sorted(registersKeys)
|
||||
sortedRegisterEnumList = []
|
||||
|
||||
print('Sorted registers')
|
||||
logger.debug('Sorted registers')
|
||||
for registerAddress in sortedRegistersKeys:
|
||||
print('--> %s : %s' % (registerAddress, registerEnums[registerAddress]))
|
||||
logger.debug('--> %s : %s' % (registerAddress, registerEnums[registerAddress]))
|
||||
enumData = {}
|
||||
enumData['key'] = registerEnums[registerAddress]
|
||||
enumData['value'] = registerAddress
|
||||
@ -165,7 +167,7 @@ def writeRegistersEnum(fileDescriptor, registerJson):
|
||||
|
||||
|
||||
def writeEnumDefinition(fileDescriptor, enumDefinition):
|
||||
print('Writing enum', enumDefinition)
|
||||
logger.debug('Writing enum %s', enumDefinition)
|
||||
enumName = enumDefinition['name']
|
||||
enumValues = enumDefinition['values']
|
||||
writeLine(fileDescriptor, ' enum %s {' % enumName)
|
||||
@ -379,21 +381,21 @@ def validateBlocks(blockDefinitions):
|
||||
previouseRegisterSize = blockRegisters[i - 1]['size']
|
||||
previouseRegisterType = blockRegisters[i - 1]['registerType']
|
||||
if previouseRegisterAddress + previouseRegisterSize != blockRegister['address']:
|
||||
print('Error: block %s has invalid register order in register %s. There seems to be a gap between the registers.' % (blockName, blockRegister['id']))
|
||||
logger.warning('Error: block %s has invalid register order in register %s. There seems to be a gap between the registers.' % (blockName, blockRegister['id']))
|
||||
exit(1)
|
||||
|
||||
if blockRegister['access'] != registerAccess:
|
||||
print('Error: block %s has inconsistent register access in register %s. The block registers dont seem to have the same access rights.' % (blockName, blockRegister['id']))
|
||||
logger.warning('Error: block %s has inconsistent register access in register %s. The block registers dont seem to have the same access rights.' % (blockName, blockRegister['id']))
|
||||
exit(1)
|
||||
|
||||
if blockRegister['registerType'] != registerType:
|
||||
print('Error: block %s has inconsistent register type in register %s. The block registers dont seem to be from the same type.' % (blockName, blockRegister['id']))
|
||||
logger.warning('Error: block %s has inconsistent register type in register %s. The block registers dont seem to be from the same type.' % (blockName, blockRegister['id']))
|
||||
exit(1)
|
||||
|
||||
registerCount += 1
|
||||
blockSize += blockRegister['size']
|
||||
|
||||
print('Define valid block \"%s\" starting at %s with length %s containing %s properties to read.' % (blockName, blockStartAddress, blockSize, registerCount))
|
||||
logger.debug('Define valid block \"%s\" starting at %s with length %s containing %s properties to read.' % (blockName, blockStartAddress, blockSize, registerCount))
|
||||
|
||||
|
||||
def writeBlocksUpdateMethodDeclarations(fileDescriptor, blockDefinitions):
|
||||
@ -418,7 +420,7 @@ def writeBlocksUpdateMethodDeclarations(fileDescriptor, blockDefinitions):
|
||||
writeLine(fileDescriptor, ' - %s [%s] - Address: %s, Size: %s' % (registerDefinition['description'], registerDefinition['unit'], registerDefinition['address'], registerDefinition['size']))
|
||||
else:
|
||||
writeLine(fileDescriptor, ' - %s - Address: %s, Size: %s' % (registerDefinition['description'], registerDefinition['address'], registerDefinition['size']))
|
||||
writeLine(fileDescriptor, ' */ ' )
|
||||
writeLine(fileDescriptor, ' */' )
|
||||
writeLine(fileDescriptor, ' void update%sBlock();' % (blockName[0].upper() + blockName[1:]))
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
114
libnymea-modbus/tools/examples/example-registers.json
Normal file
114
libnymea-modbus/tools/examples/example-registers.json
Normal file
@ -0,0 +1,114 @@
|
||||
{
|
||||
"className": "Example",
|
||||
"protocol": "BOTH",
|
||||
"endianness": "BigEndian",
|
||||
"enums": [
|
||||
{
|
||||
"name": "NameOfEnum",
|
||||
"values": [
|
||||
{
|
||||
"key": "EnumValue1",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"key": "EnumValue2",
|
||||
"value": 1
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"registers": [
|
||||
{
|
||||
"id": "foo",
|
||||
"address": 10,
|
||||
"size": 2,
|
||||
"type": "float",
|
||||
"registerType": "inputRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Foo register",
|
||||
"unit": "ValueUnit",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "bar",
|
||||
"address": 20,
|
||||
"size": 2,
|
||||
"type": "float",
|
||||
"registerType": "inputRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "Bar register",
|
||||
"unit": "ValueUnit",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
}
|
||||
],
|
||||
"blocks": [
|
||||
{
|
||||
"id": "testBlock",
|
||||
"readSchedule": "update",
|
||||
"registers": [
|
||||
{
|
||||
"id": "A",
|
||||
"address": 0,
|
||||
"size": 2,
|
||||
"type": "float",
|
||||
"registerType": "inputRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "A register",
|
||||
"unit": "X",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "B",
|
||||
"address": 2,
|
||||
"size": 2,
|
||||
"type": "float",
|
||||
"registerType": "inputRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "B register",
|
||||
"unit": "X",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "C",
|
||||
"address": 4,
|
||||
"size": 2,
|
||||
"type": "float",
|
||||
"registerType": "inputRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "C register",
|
||||
"unit": "X",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "D",
|
||||
"address": 6,
|
||||
"size": 2,
|
||||
"type": "float",
|
||||
"registerType": "inputRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "D register",
|
||||
"unit": "X",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "E",
|
||||
"address": 8,
|
||||
"size": 2,
|
||||
"type": "float",
|
||||
"registerType": "inputRegister",
|
||||
"readSchedule": "update",
|
||||
"description": "E register",
|
||||
"unit": "X",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -25,13 +25,14 @@ import json
|
||||
import shutil
|
||||
import argparse
|
||||
import datetime
|
||||
import logging
|
||||
|
||||
from connectiontool.toolcommon import *
|
||||
from connectiontool.modbusrtu import *
|
||||
from connectiontool.modbustcp import *
|
||||
|
||||
def writeTcpHeaderFile():
|
||||
print('Writing modbus TCP hader file %s' % headerFilePath)
|
||||
logger.info('Writing modbus TCP header file %s' % headerFilePath)
|
||||
headerFile = open(headerFilePath, 'w')
|
||||
|
||||
writeLicenseHeader(headerFile)
|
||||
@ -40,8 +41,8 @@ def writeTcpHeaderFile():
|
||||
writeLine(headerFile)
|
||||
writeLine(headerFile, '#include <QObject>')
|
||||
writeLine(headerFile)
|
||||
writeLine(headerFile, '#include "../modbus/modbusdatautils.h"')
|
||||
writeLine(headerFile, '#include "../modbus/modbustcpmaster.h"')
|
||||
writeLine(headerFile, '#include <modbusdatautils.h>')
|
||||
writeLine(headerFile, '#include <modbustcpmaster.h>')
|
||||
|
||||
writeLine(headerFile)
|
||||
|
||||
@ -153,12 +154,12 @@ def writeTcpHeaderFile():
|
||||
|
||||
|
||||
def writeTcpSourceFile():
|
||||
print('Writing modbus TCP source file %s' % sourceFilePath)
|
||||
logger.info('Writing modbus TCP source file %s' % sourceFilePath)
|
||||
sourceFile = open(sourceFilePath, 'w')
|
||||
writeLicenseHeader(sourceFile)
|
||||
writeLine(sourceFile)
|
||||
writeLine(sourceFile, '#include "%s"' % headerFileName)
|
||||
writeLine(sourceFile, '#include "loggingcategories.h"')
|
||||
writeLine(sourceFile, '#include <loggingcategories.h>')
|
||||
writeLine(sourceFile)
|
||||
writeLine(sourceFile, 'NYMEA_LOGGING_CATEGORY(dc%s, "%s")' % (className, className))
|
||||
writeLine(sourceFile)
|
||||
@ -249,7 +250,7 @@ def writeTcpSourceFile():
|
||||
|
||||
##########################################################################################################
|
||||
def writeRtuHeaderFile():
|
||||
print('Writing modbus TCP hader file %s' % headerFilePath)
|
||||
logger.info('Writing modbus RTU header file %s' % headerFilePath)
|
||||
headerFile = open(headerFilePath, 'w')
|
||||
|
||||
writeLicenseHeader(headerFile)
|
||||
@ -258,7 +259,7 @@ def writeRtuHeaderFile():
|
||||
writeLine(headerFile)
|
||||
writeLine(headerFile, '#include <QObject>')
|
||||
writeLine(headerFile)
|
||||
writeLine(headerFile, '#include "../modbus/modbusdatautils.h"')
|
||||
writeLine(headerFile, '#include <modbusdatautils.h>')
|
||||
writeLine(headerFile, '#include <hardware/modbus/modbusrtumaster.h>')
|
||||
|
||||
writeLine(headerFile)
|
||||
@ -373,12 +374,12 @@ def writeRtuHeaderFile():
|
||||
|
||||
|
||||
def writeRtuSourceFile():
|
||||
print('Writing modbus RTU source file %s' % sourceFilePath)
|
||||
logger.info('Writing modbus RTU source file %s' % sourceFilePath)
|
||||
sourceFile = open(sourceFilePath, 'w')
|
||||
writeLicenseHeader(sourceFile)
|
||||
|
||||
writeLine(sourceFile, '#include "%s"' % headerFileName)
|
||||
writeLine(sourceFile, '#include "loggingcategories.h"')
|
||||
writeLine(sourceFile, '#include <loggingcategories.h>')
|
||||
writeLine(sourceFile, '#include <math.h>')
|
||||
writeLine(sourceFile)
|
||||
writeLine(sourceFile, 'NYMEA_LOGGING_CATEGORY(dc%s, "%s")' % (className, className))
|
||||
@ -466,7 +467,6 @@ def writeRtuSourceFile():
|
||||
writeLine(sourceFile, '{')
|
||||
writeLine(sourceFile, ' debug.nospace().noquote() << "%s(" << %s->modbusRtuMaster()->modbusUuid().toString() << ", " << %s->modbusRtuMaster()->serialPort() << ", slave ID:" << %s->slaveId() << ")" << "\\n";' % (className, debugObjectParamName, debugObjectParamName, debugObjectParamName))
|
||||
writeRegistersDebugLine(sourceFile, debugObjectParamName, registerJson['registers'])
|
||||
|
||||
if 'blocks' in registerJson:
|
||||
for blockDefinition in registerJson['blocks']:
|
||||
writeRegistersDebugLine(sourceFile, debugObjectParamName, blockDefinition['registers'])
|
||||
@ -482,35 +482,49 @@ def writeRtuSourceFile():
|
||||
# Main
|
||||
############################################################################################
|
||||
|
||||
logger = logging.getLogger('modbus-tools')
|
||||
logger.setLevel(logging.INFO)
|
||||
ch = logging.StreamHandler(sys.stdout)
|
||||
ch.setLevel(logging.INFO)
|
||||
formatter = logging.Formatter('%(name)s: %(message)s')
|
||||
ch.setFormatter(formatter)
|
||||
logger.addHandler(ch)
|
||||
|
||||
parser = argparse.ArgumentParser(description='Generate modbus tcp connection class from JSON register definitions file.')
|
||||
parser.add_argument('-j', '--json', metavar='<file>', help='The JSON file containing the register definitions.')
|
||||
parser.add_argument('-o', '--output-directory', metavar='<directory>', help='The output directory for the resulting class.')
|
||||
parser.add_argument('-c', '--class-name', metavar='<name>', help='The name of the resulting class.')
|
||||
parser.add_argument('-v', '--verbose', dest='verboseOutput', action='store_true', help='More verbose output.')
|
||||
args = parser.parse_args()
|
||||
|
||||
registerJson = loadJsonFile(args.json)
|
||||
scriptPath = os.path.dirname(os.path.realpath(sys.argv[0]))
|
||||
outputDirectory = os.path.realpath(args.output_directory)
|
||||
className = args.class_name
|
||||
|
||||
headerFileName = className.lower() + '.h'
|
||||
sourceFileName = className.lower() + '.cpp'
|
||||
if not os.path.exists(outputDirectory):
|
||||
logger.debug("Output directory does not exist. Creating directory %s", outputDirectory)
|
||||
os.makedirs(outputDirectory)
|
||||
|
||||
headerFilePath = os.path.join(outputDirectory, headerFileName)
|
||||
sourceFilePath = os.path.join(outputDirectory, sourceFileName)
|
||||
if args.verboseOutput:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
ch.setLevel(logging.DEBUG)
|
||||
|
||||
print('Scrip path: %s' % scriptPath)
|
||||
print('Output directory: %s' % outputDirectory)
|
||||
print('Class name: %s' % className)
|
||||
print('Header file: %s' % headerFileName)
|
||||
print('Source file: %s' % sourceFileName)
|
||||
print('Header file path: %s' % headerFilePath)
|
||||
print('Source file path: %s' % sourceFilePath)
|
||||
logger.debug("Verbose output enabled")
|
||||
|
||||
if not 'className' in registerJson:
|
||||
logger.warning('Classname missing. Please specify the classname in the json file or pass it to the generatori using -c .')
|
||||
exit(1)
|
||||
|
||||
classNamePrefix = registerJson['className']
|
||||
|
||||
endianness = 'BigEndian'
|
||||
if 'endianness' in registerJson:
|
||||
endianness = registerJson['endianness']
|
||||
|
||||
logger.debug('Scrip path: %s' % scriptPath)
|
||||
logger.debug('Output directory: %s' % outputDirectory)
|
||||
logger.debug('Class name prefix: %s' % classNamePrefix)
|
||||
logger.debug('Endianness: %s' % endianness)
|
||||
|
||||
protocol = 'TCP'
|
||||
if 'protocol' in registerJson:
|
||||
protocol = registerJson['protocol']
|
||||
@ -518,12 +532,77 @@ if 'protocol' in registerJson:
|
||||
if 'blocks' in registerJson:
|
||||
validateBlocks(registerJson['blocks'])
|
||||
|
||||
# Create classes depending on the protocol
|
||||
writeTcp = True
|
||||
writeRtu = False
|
||||
|
||||
if protocol == 'TCP':
|
||||
writeTcp = True
|
||||
writeRtu = False
|
||||
elif protocol == 'RTU':
|
||||
writeTcp = False
|
||||
writeRtu = True
|
||||
else:
|
||||
# Any other value generates both classes
|
||||
writeTcp = True
|
||||
writeRtu = True
|
||||
|
||||
headerFiles = []
|
||||
sourceFiles = []
|
||||
|
||||
if writeTcp:
|
||||
className = classNamePrefix + 'ModbusTcpConnection'
|
||||
headerFileName = className.lower() + '.h'
|
||||
headerFiles.append(headerFileName)
|
||||
sourceFileName = className.lower() + '.cpp'
|
||||
sourceFiles.append(sourceFileName)
|
||||
|
||||
headerFilePath = os.path.join(outputDirectory, headerFileName)
|
||||
sourceFilePath = os.path.join(outputDirectory, sourceFileName)
|
||||
logger.debug('=======================================================')
|
||||
logger.debug('Class name: %s' % className)
|
||||
logger.debug('Header file: %s' % headerFileName)
|
||||
logger.debug('Source file: %s' % sourceFileName)
|
||||
logger.debug('Header file path: %s' % headerFilePath)
|
||||
logger.debug('Source file path: %s' % sourceFilePath)
|
||||
writeTcpHeaderFile()
|
||||
writeTcpSourceFile()
|
||||
else:
|
||||
|
||||
if writeRtu:
|
||||
className = classNamePrefix + 'ModbusRtuConnection'
|
||||
headerFileName = className.lower() + '.h'
|
||||
headerFiles.append(headerFileName)
|
||||
sourceFileName = className.lower() + '.cpp'
|
||||
sourceFiles.append(sourceFileName)
|
||||
headerFilePath = os.path.join(outputDirectory, headerFileName)
|
||||
sourceFilePath = os.path.join(outputDirectory, sourceFileName)
|
||||
logger.debug('=======================================================')
|
||||
logger.debug('Class name: %s' % className)
|
||||
logger.debug('Header file: %s' % headerFileName)
|
||||
logger.debug('Source file: %s' % sourceFileName)
|
||||
logger.debug('Header file path: %s' % headerFilePath)
|
||||
logger.debug('Source file path: %s' % sourceFilePath)
|
||||
writeRtuHeaderFile()
|
||||
writeRtuSourceFile()
|
||||
|
||||
# Write pri file
|
||||
projectIncludeFileName = classNamePrefix.lower() + '.pri'
|
||||
projectIncludeFilePath = os.path.join(outputDirectory, projectIncludeFileName)
|
||||
|
||||
|
||||
logger.info('Writing connection project include file %s' % projectIncludeFileName)
|
||||
projectIncludeFile = open(projectIncludeFilePath, 'w')
|
||||
writeLine(projectIncludeFile, '# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #')
|
||||
writeLine(projectIncludeFile, '#')
|
||||
writeLine(projectIncludeFile, '# This file has been autogenerated.')
|
||||
writeLine(projectIncludeFile, '# Any changes in this file may be overwritten from qmake.')
|
||||
writeLine(projectIncludeFile, '#')
|
||||
writeLine(projectIncludeFile, '# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #')
|
||||
writeLine(projectIncludeFile)
|
||||
writeLine(projectIncludeFile, 'HEADERS = \\')
|
||||
for generatedHeaderFileName in headerFiles:
|
||||
writeLine(projectIncludeFile, ' $${PWD}/%s \\' % generatedHeaderFileName)
|
||||
|
||||
writeLine(projectIncludeFile)
|
||||
writeLine(projectIncludeFile, "SOURCES = \\")
|
||||
for generatedSourceFileName in sourceFiles:
|
||||
writeLine(projectIncludeFile, ' $${PWD}/%s \\' % generatedSourceFileName)
|
||||
12
modbus.pri
Normal file
12
modbus.pri
Normal file
@ -0,0 +1,12 @@
|
||||
QT += network serialport serialbus
|
||||
|
||||
top_srcdir=$$PWD
|
||||
top_builddir=$$shadowed($$PWD)
|
||||
|
||||
INCLUDEPATH += $$top_srcdir/libnymea-modbus
|
||||
LIBS += -L$$top_builddir/libnymea-modbus/ -lnymea-modbus
|
||||
|
||||
OTHER_FILES += $${MODBUS_CONNECTIONS}
|
||||
|
||||
include(libnymea-modbus/modbus-tool.pri)
|
||||
|
||||
@ -2,7 +2,7 @@ TEMPLATE = subdirs
|
||||
|
||||
# Note keep it ordered so the lib will be built first
|
||||
CONFIG += ordered
|
||||
SUBDIRS += libnymea-sunspec
|
||||
SUBDIRS += libnymea-modbus libnymea-sunspec
|
||||
|
||||
PLUGIN_DIRS = \
|
||||
alphainnotec \
|
||||
|
||||
Reference in New Issue
Block a user