Add first version of modbus tools
parent
c153bc990d
commit
1fb1c7ed8c
|
|
@ -0,0 +1,231 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#include "modbusdatautils.h"
|
||||
|
||||
#include <QDataStream>
|
||||
|
||||
ModbusDataUtils::ModbusDataUtils()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
quint16 ModbusDataUtils::convertToUInt16(const QVector<quint16> ®isters)
|
||||
{
|
||||
Q_ASSERT_X(registers.count() == 1, "ModbusDataUtils", "invalid raw data size for converting value to quint16");
|
||||
return registers.at(0);
|
||||
}
|
||||
|
||||
qint16 ModbusDataUtils::convertToInt16(const QVector<quint16> ®isters)
|
||||
{
|
||||
Q_ASSERT_X(registers.count() == 1, "ModbusDataUtils", "invalid raw data size for converting value to qint16");
|
||||
return static_cast<qint16>(registers.at(0));
|
||||
}
|
||||
|
||||
quint32 ModbusDataUtils::convertToUInt32(const QVector<quint16> ®isters)
|
||||
{
|
||||
Q_ASSERT_X(registers.count() == 2, "ModbusDataUtils", "invalid raw data size for converting value to quint32");
|
||||
QByteArray data;
|
||||
QDataStream inputStream(&data, QIODevice::WriteOnly);
|
||||
inputStream << registers.at(1);
|
||||
inputStream << registers.at(0);
|
||||
|
||||
QDataStream outputStream(&data, QIODevice::ReadOnly);
|
||||
quint32 result = 0;
|
||||
outputStream >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
qint32 ModbusDataUtils::convertToInt32(const QVector<quint16> ®isters)
|
||||
{
|
||||
Q_ASSERT_X(registers.count() == 2, "ModbusDataUtils", "invalid raw data size for converting value to quint32");
|
||||
QByteArray data;
|
||||
QDataStream inputStream(&data, QIODevice::WriteOnly);
|
||||
inputStream << registers.at(1);
|
||||
inputStream << registers.at(0);
|
||||
|
||||
QDataStream outputStream(&data, QIODevice::ReadOnly);
|
||||
qint32 result = 0;
|
||||
outputStream >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
quint64 ModbusDataUtils::convertToUInt64(const QVector<quint16> ®isters)
|
||||
{
|
||||
Q_ASSERT_X(registers.count() == 4, "ModbusDataUtils", "invalid raw data size for converting value to quint64");
|
||||
QByteArray data;
|
||||
QDataStream inputStream(&data, QIODevice::WriteOnly);
|
||||
inputStream << registers.at(3);
|
||||
inputStream << registers.at(2);
|
||||
inputStream << registers.at(1);
|
||||
inputStream << registers.at(0);
|
||||
|
||||
QDataStream outputStream(&data, QIODevice::ReadOnly);
|
||||
quint64 result = 0;
|
||||
outputStream >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
qint64 ModbusDataUtils::convertToInt64(const QVector<quint16> ®isters)
|
||||
{
|
||||
Q_ASSERT_X(registers.count() == 4, "ModbusDataUtils", "invalid raw data size for converting value to qint64");
|
||||
QByteArray data;
|
||||
QDataStream inputStream(&data, QIODevice::WriteOnly);
|
||||
inputStream << registers.at(3);
|
||||
inputStream << registers.at(2);
|
||||
inputStream << registers.at(1);
|
||||
inputStream << registers.at(0);
|
||||
|
||||
QDataStream outputStream(&data, QIODevice::ReadOnly);
|
||||
qint64 result = 0;
|
||||
outputStream >> result;
|
||||
return result;
|
||||
}
|
||||
|
||||
QString ModbusDataUtils::convertToString(const QVector<quint16> ®isters)
|
||||
{
|
||||
QByteArray bytes;
|
||||
QDataStream stream(&bytes, QIODevice::WriteOnly);
|
||||
for (int i = 0; i < registers.count(); i++) {
|
||||
stream << registers.at(i);
|
||||
}
|
||||
|
||||
return QString::fromUtf8(bytes).trimmed();
|
||||
}
|
||||
|
||||
float ModbusDataUtils::convertToFloat32(const QVector<quint16> ®isters)
|
||||
{
|
||||
Q_ASSERT_X(registers.count() == 2, "ModbusDataUtils", "invalid raw data size for converting value to float32");
|
||||
quint32 rawValue = ModbusDataUtils::convertToUInt32(registers);
|
||||
float value = 0;
|
||||
memcpy(&value, &rawValue, sizeof(quint32));
|
||||
return value;
|
||||
}
|
||||
|
||||
double ModbusDataUtils::convertToFloat64(const QVector<quint16> ®isters)
|
||||
{
|
||||
|
||||
Q_ASSERT_X(registers.count() == 4, "ModbusDataUtils", "invalid raw data size for converting value to float64");
|
||||
quint64 rawValue = ModbusDataUtils::convertToUInt64(registers);
|
||||
double value = 0;
|
||||
memcpy(&value, &rawValue, sizeof(quint64));
|
||||
return value;
|
||||
}
|
||||
|
||||
QVector<quint16> ModbusDataUtils::convertFromUInt16(quint16 value)
|
||||
{
|
||||
return QVector<quint16>() << value;
|
||||
}
|
||||
|
||||
QVector<quint16> ModbusDataUtils::convertFromInt16(qint16 value)
|
||||
{
|
||||
return ModbusDataUtils::convertFromUInt16(static_cast<quint16>(value));
|
||||
}
|
||||
|
||||
QVector<quint16> ModbusDataUtils::convertFromUInt32(quint32 value)
|
||||
{
|
||||
QByteArray data;
|
||||
QDataStream inputStream(&data, QIODevice::WriteOnly);
|
||||
inputStream << value;
|
||||
|
||||
QDataStream outputStream(&data, QIODevice::ReadOnly);
|
||||
QVector<quint16> values;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
quint16 registerValue = 0;
|
||||
outputStream >> registerValue;
|
||||
values.prepend(registerValue);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
QVector<quint16> ModbusDataUtils::convertFromInt32(qint32 value)
|
||||
{
|
||||
return ModbusDataUtils::convertFromUInt32(static_cast<quint32>(value));
|
||||
}
|
||||
|
||||
QVector<quint16> ModbusDataUtils::convertFromUInt64(quint64 value)
|
||||
{
|
||||
QByteArray data;
|
||||
QDataStream inputStream(&data, QIODevice::WriteOnly);
|
||||
inputStream << value;
|
||||
|
||||
QDataStream outputStream(&data, QIODevice::ReadOnly);
|
||||
QVector<quint16> values;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quint16 registerValue = 0;
|
||||
outputStream >> registerValue;
|
||||
values.prepend(registerValue);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
QVector<quint16> ModbusDataUtils::convertFromInt64(qint64 value)
|
||||
{
|
||||
QByteArray data;
|
||||
QDataStream inputStream(&data, QIODevice::WriteOnly);
|
||||
inputStream << value;
|
||||
|
||||
QDataStream outputStream(&data, QIODevice::ReadOnly);
|
||||
QVector<quint16> values;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quint16 registerValue = 0;
|
||||
outputStream >> registerValue;
|
||||
values.prepend(registerValue);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
QVector<quint16> ModbusDataUtils::convertFromString(const QString &value, quint16 stringLength)
|
||||
{
|
||||
Q_ASSERT_X(value.length() <= stringLength, "ModbusDataUtils", "cannot convert a string which is bigger than the desired register vector.");
|
||||
QByteArray data = value.toLatin1() + QByteArray('\0', stringLength - value.count());
|
||||
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||
QVector<quint16> values;
|
||||
for (int i = 0; i < stringLength; i++) {
|
||||
quint16 registerValue = 0;
|
||||
stream >> registerValue;
|
||||
values.append(registerValue);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
QVector<quint16> ModbusDataUtils::convertFromFloat32(float value)
|
||||
{
|
||||
quint32 rawValue = 0;
|
||||
memcpy(&rawValue, &value, sizeof(float));
|
||||
return ModbusDataUtils::convertFromUInt32(rawValue);
|
||||
}
|
||||
|
||||
QVector<quint16> ModbusDataUtils::convertFromFloat64(double value)
|
||||
{
|
||||
quint64 rawValue = 0;
|
||||
memcpy(&rawValue, &value, sizeof(double));
|
||||
return ModbusDataUtils::convertFromUInt64(rawValue);
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#ifndef MODBUSDATAUTILS_H
|
||||
#define MODBUSDATAUTILS_H
|
||||
|
||||
#include <QVector>
|
||||
#include <QObject>
|
||||
|
||||
class ModbusDataUtils
|
||||
{
|
||||
Q_GADGET
|
||||
public:
|
||||
enum Access {
|
||||
AccessReadOnly,
|
||||
AccessWriteOnly,
|
||||
AccessReadWrite
|
||||
};
|
||||
Q_ENUM(Access)
|
||||
|
||||
enum DataType {
|
||||
UInt8,
|
||||
UInt16,
|
||||
Uint32,
|
||||
Uint64,
|
||||
Int8,
|
||||
Int16,
|
||||
Int32,
|
||||
Int64,
|
||||
Float,
|
||||
Float64,
|
||||
String,
|
||||
Bool
|
||||
};
|
||||
Q_ENUM(DataType)
|
||||
|
||||
typedef struct ModbusRegister {
|
||||
quint16 address;
|
||||
quint16 size;
|
||||
DataType dataType;
|
||||
Access access;
|
||||
QString description;
|
||||
QString unit;
|
||||
QVector<quint16> rawData;
|
||||
} ModbusRegister;
|
||||
|
||||
typedef QVector<ModbusRegister> ModbusRegisters;
|
||||
|
||||
explicit ModbusDataUtils();
|
||||
|
||||
// Convert to
|
||||
static quint16 convertToUInt16(const QVector<quint16> ®isters);
|
||||
static qint16 convertToInt16(const QVector<quint16> ®isters);
|
||||
static quint32 convertToUInt32(const QVector<quint16> ®isters);
|
||||
static qint32 convertToInt32(const QVector<quint16> ®isters);
|
||||
static quint64 convertToUInt64(const QVector<quint16> ®isters);
|
||||
static qint64 convertToInt64(const QVector<quint16> ®isters);
|
||||
static QString convertToString(const QVector<quint16> ®isters);
|
||||
static float convertToFloat32(const QVector<quint16> ®isters);
|
||||
static double convertToFloat64(const QVector<quint16> ®isters);
|
||||
|
||||
// Convert from
|
||||
static QVector<quint16> convertFromUInt16(quint16 value);
|
||||
static QVector<quint16> convertFromInt16(qint16 value);
|
||||
static QVector<quint16> convertFromUInt32(quint32 value);
|
||||
static QVector<quint16> convertFromInt32(qint32 value);
|
||||
static QVector<quint16> convertFromUInt64(quint64 value);
|
||||
static QVector<quint16> convertFromInt64(qint64 value);
|
||||
static QVector<quint16> convertFromString(const QString &value, quint16 stringLength);
|
||||
static QVector<quint16> convertFromFloat32(float value);
|
||||
static QVector<quint16> convertFromFloat64(double value);
|
||||
};
|
||||
|
||||
#endif // MODBUSDATAUTILS_H
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
# Generate a modbus read class
|
||||
|
||||
In order to make the plugin development for modbus TCP devices much easier and faster, a small tool has been developed to generate a modbus TCP master based class providing get and set methods for the registers and property changed signals.
|
||||
|
||||
The workflow looks like this:
|
||||
|
||||
* Write the `registers.json` file containing all register information you are interested to.
|
||||
* Run the script and provide the class name, output directory and the path to the JSON file
|
||||
* Include the generated class in your plugin, connect the `<propertyName>Changed()` signal and update the thing state within the plugin.
|
||||
|
||||
|
||||
The class will provide 2 main methods for fetching information from the modbus device:
|
||||
|
||||
* `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.
|
||||
|
||||
|
||||
# JSON format
|
||||
|
||||
The basic structure of the modbus register JSON looks like following example:
|
||||
|
||||
```
|
||||
{
|
||||
"enums": [
|
||||
{
|
||||
"name": "NameOfEnum",
|
||||
"values": [
|
||||
{
|
||||
"key": "EnumValue1",
|
||||
"value": 0
|
||||
},
|
||||
{
|
||||
"key": "EnumValue2",
|
||||
"value": 1
|
||||
},
|
||||
....
|
||||
]
|
||||
}
|
||||
],
|
||||
"registers": [
|
||||
{
|
||||
"id": "registerPropertyName",
|
||||
"address": 4,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"readSchedule": "init",
|
||||
"description": "Description of the register",
|
||||
"unit": "V",
|
||||
"defaultValue": "0",
|
||||
"access": "RO"
|
||||
},
|
||||
{
|
||||
"id": "registerWithEnumValues",
|
||||
"address": 5,
|
||||
"size": 1,
|
||||
"type": "uint16",
|
||||
"readSchedule": "update",
|
||||
"enum": "NameOfEnum",
|
||||
"defaultValue": "NameOfEnumEnumValue1",
|
||||
"description": "Description of the enum register like states",
|
||||
"access": "RO"
|
||||
},
|
||||
...
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
## 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>`.
|
||||
|
||||
If a register represets an enum, you simply add the property `"enum": "NameOfEnum"` in the register map and the property will be defined using the resulting enum type. All convertion between enum and resulting modbus register value will be done automatically.
|
||||
|
||||
## Registers
|
||||
|
||||
Earch register will be defined as a property in the resulting class modbus TCP class providing easy access to the register data.
|
||||
|
||||
* `id`: Mandatory. The id defines the name of the property used in the resulting class.
|
||||
* `address`: Mandatory. The modbus address of the register.
|
||||
* `size`: Mandatory. The amount of registers to read for the property.
|
||||
* `type`: Mandatory. The data type of this property. Available data types are:
|
||||
* `uint16` : will be converted to `quint16`
|
||||
* `int16` : will be converted to `qint16`
|
||||
* `uint32` : will be converted to `quint32`
|
||||
* `int32` : will be converted to `qint32`
|
||||
* `uint64` : will be converted to `quint64`
|
||||
* `int64` : will be converted to `qint64`
|
||||
* `float`: will be converted to `float`
|
||||
* `float64`: will be converted to `double`
|
||||
* `string` : will be converted to `QString`
|
||||
* `readSchedule`: Optional. Defines when the register needs to be fetched. If no read schedule has been defined, the class will provide only the update methods, but will not read the value during `initialize()` or `update()` calls. Possible values are:
|
||||
* `init`: The register will be fetched during initialization. Once all `init `registers have been fetched, the `initializationFinished()` signal will be emitted.
|
||||
* `update`: The register will be feched each time the `update()` method will be called.
|
||||
* `enum`: Optional: If the given data type represents an enum value, this propery can be set to the name of the used enum from the `enum` definition. The class will take care internally about the data convertion from and to the enum values.
|
||||
* `description`: Mandatory. A clear description of the register.
|
||||
* `unit`: Optional. Represents the unit of this register value.
|
||||
* `registerType`: Optional. Represents the type of the register and how to read/write it. Default is `holdingRegister`. Possible values are:
|
||||
* `holdingRegister`
|
||||
* `inputRegister`
|
||||
* `coils`
|
||||
* `discreteInputs`
|
||||
* `access`: Mandatory. Describes the access to this register. Possible valies are:
|
||||
* `RO`: Read only access. Only the get method and the changed singal will be defined.
|
||||
* `RW`: Read and write access. Also a set mehtod will be defined.
|
||||
* `WO`: Write only. Only the set method will be defined.
|
||||
* `scaleFactor`: Optional. The name of the scale factor register to convert this value to float. `floatValue = intValue * 10^scaleFactor value`. The scale factor value is normally a `int16` value, i.e. -10 or 10
|
||||
* `staticScaleFactor`: Optional. Use this static scale factor to convert this register value to float. `floatValue = registerValue * 10^staticScaleFactor`. The scale factor value is normally a `int16` value, i.e. -10 or 10
|
||||
* `defaultValue`: Optional. The value for initializing the property.
|
||||
|
||||
# Example
|
||||
|
||||
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`
|
||||
|
||||
You the result will be a header and a source file called:
|
||||
|
||||
* `mymodbusconnection.h`
|
||||
* `mymodbusconnection.cpp`
|
||||
|
||||
You can include this class in your project and provide one connection per thing.
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,557 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2021 nymea GmbH <developer@nymea.io>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
# To lazy to type all those register plugins, let's make live much easier and generate code from a json register definition
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import argparse
|
||||
import datetime
|
||||
|
||||
def convertToAlphaNumeric(text):
|
||||
finalText = ''
|
||||
for character in text:
|
||||
if character.isalnum():
|
||||
finalText += character
|
||||
else:
|
||||
finalText += ' '
|
||||
return finalText
|
||||
|
||||
|
||||
def splitCamelCase(text):
|
||||
return re.sub('([A-Z][a-z]+)', r' \1', re.sub('([A-Z]+)', r' \1', text)).split()
|
||||
|
||||
|
||||
def convertToCamelCase(text, capitalize = False):
|
||||
s = convertToAlphaNumeric(text)
|
||||
s = s.replace("-", " ").replace("_", " ")
|
||||
words = s.split()
|
||||
#print('--> words', words)
|
||||
finalWords = []
|
||||
|
||||
for i in range(len(words)):
|
||||
camelCaseSplit = splitCamelCase(words[i])
|
||||
if len(camelCaseSplit) == 0:
|
||||
finalWords.append(words[i])
|
||||
else:
|
||||
#print('--> camel split words', camelCaseSplit)
|
||||
for j in range(len(camelCaseSplit)):
|
||||
finalWords.append(camelCaseSplit[j])
|
||||
|
||||
if len(finalWords) == 0:
|
||||
return text
|
||||
|
||||
finalText = ''
|
||||
if capitalize:
|
||||
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)
|
||||
return finalText
|
||||
|
||||
|
||||
def loadJsonFile(filePath):
|
||||
print('--> Loading JSON file', filePath)
|
||||
jsonFile = open(filePath, 'r')
|
||||
return json.load(jsonFile)
|
||||
|
||||
|
||||
def writeLine(fileDescriptor, line = ''):
|
||||
fileDescriptor.write(line + '\n')
|
||||
|
||||
|
||||
def writeLicenseHeader(fileDescriptor):
|
||||
writeLine(fileDescriptor, '/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *')
|
||||
writeLine(fileDescriptor, '*')
|
||||
writeLine(fileDescriptor, '* Copyright 2013 - %s, nymea GmbH' % datetime.datetime.now().year)
|
||||
writeLine(fileDescriptor, '* Contact: contact@nymea.io')
|
||||
writeLine(fileDescriptor, '*')
|
||||
writeLine(fileDescriptor, '* This fileDescriptor is part of nymea.')
|
||||
writeLine(fileDescriptor, '* This project including source code and documentation is protected by')
|
||||
writeLine(fileDescriptor, '* copyright law, and remains the property of nymea GmbH. All rights, including')
|
||||
writeLine(fileDescriptor, '* reproduction, publication, editing and translation, are reserved. The use of')
|
||||
writeLine(fileDescriptor, '* this project is subject to the terms of a license agreement to be concluded')
|
||||
writeLine(fileDescriptor, '* with nymea GmbH in accordance with the terms of use of nymea GmbH, available')
|
||||
writeLine(fileDescriptor, '* under https://nymea.io/license')
|
||||
writeLine(fileDescriptor, '*')
|
||||
writeLine(fileDescriptor, '* GNU Lesser General Public License Usage')
|
||||
writeLine(fileDescriptor, '* Alternatively, this project may be redistributed and/or modified under the')
|
||||
writeLine(fileDescriptor, '* terms of the GNU Lesser General Public License as published by the Free')
|
||||
writeLine(fileDescriptor, '* Software Foundation; version 3. This project is distributed in the hope that')
|
||||
writeLine(fileDescriptor, '* it will be useful, but WITHOUT ANY WARRANTY; without even the implied')
|
||||
writeLine(fileDescriptor, '* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU')
|
||||
writeLine(fileDescriptor, '* Lesser General Public License for more details.')
|
||||
writeLine(fileDescriptor, '*')
|
||||
writeLine(fileDescriptor, '* You should have received a copy of the GNU Lesser General Public License')
|
||||
writeLine(fileDescriptor, '* along with this project. If not, see <https://www.gnu.org/licenses/>.')
|
||||
writeLine(fileDescriptor, '*')
|
||||
writeLine(fileDescriptor, '* For any further details and any questions please contact us under')
|
||||
writeLine(fileDescriptor, '* contact@nymea.io or see our FAQ/Licensing Information on')
|
||||
writeLine(fileDescriptor, '* https://nymea.io/license/faq')
|
||||
writeLine(fileDescriptor, '*')
|
||||
writeLine(fileDescriptor, '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */')
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
|
||||
def writeEnumDefinition(fileDescriptor, enumDefinition):
|
||||
print('Writing enum', enumDefinition)
|
||||
enumName = enumDefinition['name']
|
||||
enumValues = enumDefinition['values']
|
||||
writeLine(fileDescriptor, ' enum %s {' % enumName)
|
||||
for i in range(len(enumValues)):
|
||||
enumData = enumValues[i]
|
||||
line = (' %s%s = %s' % (enumName, enumData['key'], enumData['value']))
|
||||
if i < (len(enumValues) - 1):
|
||||
line += ','
|
||||
|
||||
writeLine(fileDescriptor, line)
|
||||
|
||||
writeLine(fileDescriptor, ' };')
|
||||
writeLine(fileDescriptor, ' Q_ENUM(%s)' % enumName)
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
|
||||
def getCppDataType(registerDefinition):
|
||||
if 'enum' in registerDefinition:
|
||||
return registerDefinition['enum']
|
||||
|
||||
if 'scaleFactor' in registerDefinition:
|
||||
return 'float'
|
||||
|
||||
if registerDefinition['type'] == 'uint16':
|
||||
return 'quint16'
|
||||
|
||||
if registerDefinition['type'] == 'int16':
|
||||
return 'qint16'
|
||||
|
||||
if registerDefinition['type'] == 'uint32':
|
||||
return 'quint32'
|
||||
|
||||
if registerDefinition['type'] == 'int32':
|
||||
return 'qint32'
|
||||
|
||||
if registerDefinition['type'] == 'uint64':
|
||||
return 'quint64'
|
||||
|
||||
if registerDefinition['type'] == 'int64':
|
||||
return 'qint64'
|
||||
|
||||
if registerDefinition['type'] == 'float':
|
||||
return 'float'
|
||||
|
||||
if registerDefinition['type'] == 'float64':
|
||||
return 'double'
|
||||
|
||||
if registerDefinition['type'] == 'string':
|
||||
return 'QString'
|
||||
|
||||
|
||||
def getValueConversionMethod(registerDefinition):
|
||||
if 'enum' in registerDefinition:
|
||||
enumName = registerDefinition['enum']
|
||||
if registerDefinition['type'] == 'uint16':
|
||||
return ('static_cast<%s>(ModbusDataUtils::convertToUInt16(unit.values()))' % (enumName))
|
||||
elif registerDefinition['type'] == 'int16':
|
||||
return ('static_cast<%s>(ModbusDataUtils::convertToInt16(unit.values()))' % (enumName))
|
||||
elif registerDefinition['type'] == 'uint32':
|
||||
return ('static_cast<%s>(ModbusDataUtils::convertToUInt32(unit.values()))' % (enumName))
|
||||
elif registerDefinition['type'] == 'int32':
|
||||
return ('static_cast<%s>(ModbusDataUtils::convertToInt32(unit.values()))' % (enumName))
|
||||
|
||||
if 'scaleFactor' in registerDefinition:
|
||||
scaleFactorProperty = 'm_%s' % registerDefinition['scaleFactor']
|
||||
if registerDefinition['type'] == 'uint16':
|
||||
return ('ModbusDataUtils::convertToUInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
|
||||
elif registerDefinition['type'] == 'int16':
|
||||
return ('ModbusDataUtils::convertToInt16(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
|
||||
elif registerDefinition['type'] == 'uint32':
|
||||
return ('ModbusDataUtils::convertToUInt32(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
|
||||
elif registerDefinition['type'] == 'int32':
|
||||
return ('ModbusDataUtils::convertToInt32(unit.values()) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
|
||||
|
||||
|
||||
elif registerDefinition['type'] == 'uint16':
|
||||
return ('ModbusDataUtils::convertToUInt16(unit.values())')
|
||||
elif registerDefinition['type'] == 'int16':
|
||||
return ('ModbusDataUtils::convertToInt16(unit.values())')
|
||||
elif registerDefinition['type'] == 'uint32':
|
||||
return ('ModbusDataUtils::convertToUInt32(unit.values())')
|
||||
elif registerDefinition['type'] == 'int32':
|
||||
return ('ModbusDataUtils::convertToInt32(unit.values())')
|
||||
elif registerDefinition['type'] == 'uint64':
|
||||
return ('ModbusDataUtils::convertToUInt64(unit.values())')
|
||||
elif registerDefinition['type'] == 'int64':
|
||||
return ('ModbusDataUtils::convertToInt64(unit.values())')
|
||||
elif registerDefinition['type'] == 'float':
|
||||
return ('ModbusDataUtils::convertToFloat32(unit.values())')
|
||||
elif registerDefinition['type'] == 'float64':
|
||||
return ('ModbusDataUtils::convertToFloat64(unit.values())')
|
||||
elif registerDefinition['type'] == 'string':
|
||||
return ('ModbusDataUtils::convertToString(unit.values())')
|
||||
|
||||
|
||||
def writePropertyGetMethodDeclarations(fileDescriptor, registerDefinitions):
|
||||
for registerDefinition in registerDefinitions:
|
||||
propertyName = registerDefinition['id']
|
||||
propertyTyp = getCppDataType(registerDefinition)
|
||||
if 'unit' in registerDefinition and registerDefinition['unit'] != '':
|
||||
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, ' %s %s() const;' % (propertyTyp, propertyName))
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
|
||||
def writePropertyGetMethodImplementations(fileDescriptor, className, registerDefinitions):
|
||||
for registerDefinition in registerDefinitions:
|
||||
propertyName = registerDefinition['id']
|
||||
propertyTyp = getCppDataType(registerDefinition)
|
||||
if 'enum' in registerDefinition:
|
||||
writeLine(fileDescriptor, '%s::%s %s::%s() const' % (className, propertyTyp, className, propertyName))
|
||||
else:
|
||||
writeLine(fileDescriptor, '%s %s::%s() const' % (propertyTyp, className, propertyName))
|
||||
|
||||
writeLine(fileDescriptor, '{')
|
||||
writeLine(fileDescriptor, ' return m_%s;' % propertyName)
|
||||
writeLine(fileDescriptor, '}')
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
|
||||
def writePropertyUpdateMethodDeclarations(fileDescriptor, registerDefinitions):
|
||||
for registerDefinition in registerDefinitions:
|
||||
if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init':
|
||||
continue
|
||||
|
||||
propertyName = registerDefinition['id']
|
||||
propertyTyp = getCppDataType(registerDefinition)
|
||||
writeLine(fileDescriptor, ' void update%s();' % (propertyName[0].upper() + propertyName[1:]))
|
||||
|
||||
|
||||
def writePropertyUpdateMethodImplementations(fileDescriptor, className, registerDefinitions):
|
||||
for registerDefinition in registerDefinitions:
|
||||
if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init':
|
||||
continue
|
||||
|
||||
propertyName = registerDefinition['id']
|
||||
propertyTyp = getCppDataType(registerDefinition)
|
||||
writeLine(fileDescriptor, 'void %s::update%s()' % (className, propertyName[0].upper() + propertyName[1:]))
|
||||
writeLine(fileDescriptor, '{')
|
||||
writeLine(fileDescriptor, ' // Update registers from %s' % registerDefinition['description'])
|
||||
writeLine(fileDescriptor, ' QModbusReply *reply = read%s();' % (propertyName[0].upper() + propertyName[1:]))
|
||||
writeLine(fileDescriptor, ' if (reply) {')
|
||||
writeLine(fileDescriptor, ' if (!reply->isFinished()) {')
|
||||
writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);')
|
||||
writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, this, [this, reply](){')
|
||||
writeLine(fileDescriptor, ' if (reply->error() == QModbusDevice::NoError) {')
|
||||
writeLine(fileDescriptor, ' const QModbusDataUnit unit = reply->result();')
|
||||
writeLine(fileDescriptor, ' %s received%s = %s;' % (propertyTyp, propertyName[0].upper() + propertyName[1:], getValueConversionMethod(registerDefinition)))
|
||||
writeLine(fileDescriptor, ' if (m_%s != received%s) {' % (propertyName, propertyName[0].upper() + propertyName[1:]))
|
||||
writeLine(fileDescriptor, ' m_%s = received%s;' % (propertyName, propertyName[0].upper() + propertyName[1:]))
|
||||
writeLine(fileDescriptor, ' emit %sChanged(m_%s);' % (propertyName, propertyName))
|
||||
writeLine(fileDescriptor, ' }')
|
||||
writeLine(fileDescriptor, ' }')
|
||||
writeLine(fileDescriptor, ' });')
|
||||
writeLine(fileDescriptor)
|
||||
writeLine(fileDescriptor, ' connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){')
|
||||
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Modbus reply error occurred while updating \\"%s\\" registers from" << hostAddress().toString() << error << reply->errorString();' % (className, registerDefinition['description']))
|
||||
writeLine(fileDescriptor, ' emit reply->finished(); // To make sure it will be deleted')
|
||||
writeLine(fileDescriptor, ' });')
|
||||
writeLine(fileDescriptor, ' } else {')
|
||||
writeLine(fileDescriptor, ' delete reply; // Broadcast reply returns immediatly')
|
||||
writeLine(fileDescriptor, ' }')
|
||||
writeLine(fileDescriptor, ' } else {')
|
||||
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Error occurred while reading \\"%s\\" registers from" << hostAddress().toString() << errorString();' % (className, registerDefinition['description']))
|
||||
writeLine(fileDescriptor, ' }')
|
||||
writeLine(fileDescriptor, '}')
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
|
||||
def writeInternalPropertyReadMethodDeclarations(fileDescriptor, registerDefinitions):
|
||||
for registerDefinition in registerDefinitions:
|
||||
propertyName = registerDefinition['id']
|
||||
writeLine(fileDescriptor, ' QModbusReply *read%s();' % (propertyName[0].upper() + propertyName[1:]))
|
||||
|
||||
|
||||
def writeInternalPropertyReadMethodImplementations(fileDescriptor, className, registerDefinitions):
|
||||
for registerDefinition in registerDefinitions:
|
||||
propertyName = registerDefinition['id']
|
||||
writeLine(fileDescriptor, 'QModbusReply *%s::read%s()' % (className, propertyName[0].upper() + propertyName[1:]))
|
||||
writeLine(fileDescriptor, '{')
|
||||
writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, %s, %s);' % (registerDefinition['address'], registerDefinition['size']))
|
||||
writeLine(fileDescriptor, ' return sendReadRequest(request, m_slaveId);')
|
||||
writeLine(fileDescriptor, '}')
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
|
||||
def writePropertyChangedSignals(fileDescriptor, registerDefinitions):
|
||||
for registerDefinition in registerDefinitions:
|
||||
propertyName = registerDefinition['id']
|
||||
propertyTyp = getCppDataType(registerDefinition)
|
||||
if propertyTyp == 'QString':
|
||||
writeLine(fileDescriptor, ' void %sChanged(const %s &%s);' % (propertyName, propertyTyp, propertyName))
|
||||
else:
|
||||
writeLine(fileDescriptor, ' void %sChanged(%s %s);' % (propertyName, propertyTyp, propertyName))
|
||||
|
||||
|
||||
def writePrivatePropertyMembers(fileDescriptor, registerDefinitions):
|
||||
for registerDefinition in registerDefinitions:
|
||||
propertyName = registerDefinition['id']
|
||||
propertyTyp = getCppDataType(registerDefinition)
|
||||
if 'defaultValue' in registerDefinition:
|
||||
writeLine(fileDescriptor, ' %s m_%s = %s;' % (propertyTyp, propertyName, registerDefinition['defaultValue']))
|
||||
else:
|
||||
writeLine(fileDescriptor, ' %s m_%s;' % (propertyTyp, propertyName))
|
||||
|
||||
|
||||
def writeInitializeMethod(fileDescriptor, className, registerDefinitions):
|
||||
writeLine(fileDescriptor, 'void %s::initialize()' % (className))
|
||||
writeLine(fileDescriptor, '{')
|
||||
|
||||
writeLine(fileDescriptor, ' QModbusReply *reply = nullptr;')
|
||||
writeLine(fileDescriptor)
|
||||
writeLine(fileDescriptor, ' if (!m_pendingInitReplies.isEmpty()) {')
|
||||
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Tried to initialize but there are still some init replies pending.";' % className)
|
||||
writeLine(fileDescriptor, ' return;')
|
||||
writeLine(fileDescriptor, ' }')
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
for registerDefinition in registerDefinitions:
|
||||
propertyName = registerDefinition['id']
|
||||
propertyTyp = getCppDataType(registerDefinition)
|
||||
|
||||
if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init':
|
||||
writeLine(fileDescriptor, ' // Read %s' % registerDefinition['description'])
|
||||
writeLine(fileDescriptor, ' reply = read%s();' % (propertyName[0].upper() + propertyName[1:]))
|
||||
writeLine(fileDescriptor, ' if (reply) {')
|
||||
writeLine(fileDescriptor, ' if (!reply->isFinished()) {')
|
||||
writeLine(fileDescriptor, ' m_pendingInitReplies.append(reply);')
|
||||
writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, reply, &QModbusReply::deleteLater);')
|
||||
writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, this, [this, reply](){')
|
||||
writeLine(fileDescriptor, ' if (reply->error() == QModbusDevice::NoError) {')
|
||||
writeLine(fileDescriptor, ' const QModbusDataUnit unit = reply->result();')
|
||||
writeLine(fileDescriptor, ' %s received%s = %s;' % (propertyTyp, propertyName[0].upper() + propertyName[1:], getValueConversionMethod(registerDefinition)))
|
||||
writeLine(fileDescriptor, ' if (m_%s != received%s) {' % (propertyName, propertyName[0].upper() + propertyName[1:]))
|
||||
writeLine(fileDescriptor, ' m_%s = received%s;' % (propertyName, propertyName[0].upper() + propertyName[1:]))
|
||||
writeLine(fileDescriptor, ' emit %sChanged(m_%s);' % (propertyName, propertyName))
|
||||
writeLine(fileDescriptor, ' }')
|
||||
writeLine(fileDescriptor, ' }')
|
||||
writeLine(fileDescriptor)
|
||||
writeLine(fileDescriptor, ' m_pendingInitReplies.removeAll(reply);')
|
||||
writeLine(fileDescriptor, ' verifyInitFinished();')
|
||||
writeLine(fileDescriptor, ' });')
|
||||
writeLine(fileDescriptor)
|
||||
writeLine(fileDescriptor, ' connect(reply, &QModbusReply::errorOccurred, this, [this, reply] (QModbusDevice::Error error){')
|
||||
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Modbus reply error occurred while reading \\"%s\\" registers from" << hostAddress().toString() << error << reply->errorString();' % (className, registerDefinition['description']))
|
||||
writeLine(fileDescriptor, ' emit reply->finished(); // To make sure it will be deleted')
|
||||
writeLine(fileDescriptor, ' });')
|
||||
writeLine(fileDescriptor, ' } else {')
|
||||
writeLine(fileDescriptor, ' delete reply; // Broadcast reply returns immediatly')
|
||||
writeLine(fileDescriptor, ' }')
|
||||
writeLine(fileDescriptor, ' } else {')
|
||||
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Error occurred while reading \\"%s\\" registers from" << hostAddress().toString() << errorString();' % (className, registerDefinition['description']))
|
||||
writeLine(fileDescriptor, ' }')
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
writeLine(fileDescriptor, ' ')
|
||||
writeLine(fileDescriptor, '}')
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
|
||||
def writeUpdateMethod(fileDescriptor, className, registerDefinitions):
|
||||
writeLine(fileDescriptor, 'void %s::update()' % (className))
|
||||
writeLine(fileDescriptor, '{')
|
||||
for registerDefinition in registerDefinitions:
|
||||
propertyName = registerDefinition['id']
|
||||
if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'update':
|
||||
writeLine(fileDescriptor, ' update%s();' % (propertyName[0].upper() + propertyName[1:]))
|
||||
|
||||
writeLine(fileDescriptor, '}')
|
||||
writeLine(fileDescriptor)
|
||||
|
||||
|
||||
############################################################################################
|
||||
# Main
|
||||
############################################################################################
|
||||
|
||||
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.')
|
||||
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'
|
||||
|
||||
headerFilePath = os.path.join(outputDirectory, headerFileName)
|
||||
sourceFilePath = os.path.join(outputDirectory, sourceFileName)
|
||||
|
||||
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)
|
||||
|
||||
#############################################################################
|
||||
# Write header file
|
||||
#############################################################################
|
||||
|
||||
headerFile = open(headerFilePath, 'w')
|
||||
|
||||
writeLicenseHeader(headerFile)
|
||||
writeLine(headerFile, '#ifndef %s_H' % className.upper())
|
||||
writeLine(headerFile, '#define %s_H' % className.upper())
|
||||
writeLine(headerFile)
|
||||
writeLine(headerFile, '#include <QObject>')
|
||||
writeLine(headerFile)
|
||||
writeLine(headerFile, '#include "../modbus/modbusdatautils.h"')
|
||||
writeLine(headerFile, '#include "../modbus/modbustcpmaster.h"')
|
||||
|
||||
writeLine(headerFile)
|
||||
|
||||
# Begin of class
|
||||
writeLine(headerFile, 'class %s : public ModbusTCPMaster' % className)
|
||||
writeLine(headerFile, '{')
|
||||
writeLine(headerFile, ' Q_OBJECT')
|
||||
|
||||
# Public members
|
||||
writeLine(headerFile, 'public:')
|
||||
|
||||
# Enum declarations
|
||||
for enumDefinition in registerJson['enums']:
|
||||
writeEnumDefinition(headerFile, enumDefinition)
|
||||
|
||||
# Constructor
|
||||
writeLine(headerFile, ' explicit %s(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent = nullptr);' % className)
|
||||
writeLine(headerFile, ' ~%s() = default;' % className)
|
||||
writeLine(headerFile)
|
||||
|
||||
# Write registers get method declarations
|
||||
writePropertyGetMethodDeclarations(headerFile, registerJson['registers'])
|
||||
|
||||
# Write init and update method declarations
|
||||
writeLine(headerFile, ' virtual void initialize();')
|
||||
writeLine(headerFile, ' virtual void update();')
|
||||
writeLine(headerFile)
|
||||
|
||||
writePropertyUpdateMethodDeclarations(headerFile, registerJson['registers'])
|
||||
writeLine(headerFile)
|
||||
|
||||
# Write registers value changed signals
|
||||
writeLine(headerFile, 'signals:')
|
||||
writeLine(headerFile, ' void initializationFinished();')
|
||||
writeLine(headerFile)
|
||||
writePropertyChangedSignals(headerFile, registerJson['registers'])
|
||||
writeLine(headerFile)
|
||||
|
||||
# Private members
|
||||
writeLine(headerFile, 'private:')
|
||||
writeLine(headerFile, ' quint16 m_slaveId = 1;')
|
||||
writeLine(headerFile, ' QVector<QModbusReply *> m_pendingInitReplies;')
|
||||
writeLine(headerFile)
|
||||
writePrivatePropertyMembers(headerFile, registerJson['registers'])
|
||||
writeLine(headerFile)
|
||||
writeLine(headerFile, ' void verifyInitFinished();')
|
||||
writeLine(headerFile)
|
||||
writeInternalPropertyReadMethodDeclarations(headerFile, registerJson['registers'])
|
||||
writeLine(headerFile)
|
||||
|
||||
# End of class
|
||||
writeLine(headerFile)
|
||||
writeLine(headerFile, '};')
|
||||
writeLine(headerFile)
|
||||
writeLine(headerFile, 'QDebug operator<<(QDebug debug, %s *%s);' % (className, className[0].lower() + className[1:]))
|
||||
writeLine(headerFile)
|
||||
writeLine(headerFile, '#endif // %s_H' % className.upper())
|
||||
|
||||
headerFile.close()
|
||||
|
||||
|
||||
|
||||
#############################################################################
|
||||
# Write source file
|
||||
#############################################################################
|
||||
|
||||
sourceFile = open(sourceFilePath, 'w')
|
||||
writeLicenseHeader(sourceFile)
|
||||
writeLine(sourceFile)
|
||||
writeLine(sourceFile, '#include "%s"' % headerFileName)
|
||||
writeLine(sourceFile, '#include "loggingcategories.h"')
|
||||
writeLine(sourceFile)
|
||||
writeLine(sourceFile, 'NYMEA_LOGGING_CATEGORY(dc%s, "%s")' % (className, className))
|
||||
writeLine(sourceFile)
|
||||
|
||||
# Constructor
|
||||
writeLine(sourceFile, '%s::%s(const QHostAddress &hostAddress, uint port, quint16 slaveId, QObject *parent) :' % (className, className))
|
||||
writeLine(sourceFile, ' ModbusTCPMaster(hostAddress, port, parent),')
|
||||
writeLine(sourceFile, ' m_slaveId(slaveId)')
|
||||
writeLine(sourceFile, '{')
|
||||
writeLine(sourceFile, ' ')
|
||||
writeLine(sourceFile, '}')
|
||||
writeLine(sourceFile)
|
||||
|
||||
# Property get methods
|
||||
writePropertyGetMethodImplementations(sourceFile, className, registerJson['registers'])
|
||||
|
||||
# Write init and update method implementation
|
||||
writeInitializeMethod(sourceFile, className, registerJson['registers'])
|
||||
writeUpdateMethod(sourceFile, className, registerJson['registers'])
|
||||
|
||||
# Write update methods
|
||||
writePropertyUpdateMethodImplementations(sourceFile, className, registerJson['registers'])
|
||||
|
||||
# Write property read method implementations
|
||||
writeInternalPropertyReadMethodImplementations(sourceFile, className, registerJson['registers'])
|
||||
|
||||
writeLine(sourceFile, 'void %s::verifyInitFinished()' % (className))
|
||||
writeLine(sourceFile, '{')
|
||||
writeLine(sourceFile, ' if (m_pendingInitReplies.isEmpty()) {')
|
||||
writeLine(sourceFile, ' qCDebug(dc%s()) << "Initialization finished of %s" << hostAddress().toString();' % (className, className))
|
||||
writeLine(sourceFile, ' emit initializationFinished();')
|
||||
writeLine(sourceFile, ' }')
|
||||
writeLine(sourceFile, '}')
|
||||
writeLine(sourceFile)
|
||||
|
||||
# Write the debug print
|
||||
debugObjectParamName = className[0].lower() + className[1:]
|
||||
writeLine(sourceFile, 'QDebug operator<<(QDebug debug, %s *%s)' % (className, debugObjectParamName))
|
||||
writeLine(sourceFile, '{')
|
||||
writeLine(sourceFile, ' debug.nospace().noquote() << "%s(" << %s->hostAddress().toString() << ":" << %s->port() << ")" << "\\n";' % (className, debugObjectParamName, debugObjectParamName))
|
||||
for registerDefinition in registerJson['registers']:
|
||||
propertyName = registerDefinition['id']
|
||||
propertyTyp = getCppDataType(registerDefinition)
|
||||
line = ('" - %s:" << %s->%s()' % (registerDefinition['description'], debugObjectParamName, propertyName))
|
||||
if 'unit' in registerDefinition and registerDefinition['unit'] != '':
|
||||
line += (' << " [%s]"' % registerDefinition['unit'])
|
||||
writeLine(sourceFile, ' debug.nospace().noquote() << %s << "\\n";' % (line))
|
||||
|
||||
writeLine(sourceFile, ' return debug.quote().space();')
|
||||
writeLine(sourceFile, '}')
|
||||
writeLine(sourceFile)
|
||||
|
||||
sourceFile.close()
|
||||
Loading…
Reference in New Issue