modbus-tool: Introduce string endianess

master
Simon Stürz 2023-04-05 12:46:06 +02:00
parent fd80c7e99a
commit 7fc32167d6
5 changed files with 71 additions and 6 deletions

View File

@ -133,10 +133,12 @@ qint64 ModbusDataUtils::convertToInt64(const QVector<quint16> &registers, ByteOr
return result; return result;
} }
QString ModbusDataUtils::convertToString(const QVector<quint16> &registers) QString ModbusDataUtils::convertToString(const QVector<quint16> &registers, ByteOrder characterByteOrder)
{ {
QByteArray bytes; QByteArray bytes;
QDataStream stream(&bytes, QIODevice::WriteOnly); QDataStream stream(&bytes, QIODevice::WriteOnly);
// Note: some devices use little endian within the register uint16 representation of the 2 characters.
stream.setByteOrder(characterByteOrder == ByteOrderBigEndian ? QDataStream::BigEndian : QDataStream::LittleEndian);
for (int i = 0; i < registers.count(); i++) { for (int i = 0; i < registers.count(); i++) {
stream << registers.at(i); stream << registers.at(i);
} }
@ -239,11 +241,13 @@ QVector<quint16> ModbusDataUtils::convertFromInt64(qint64 value, ByteOrder byteO
return values; return values;
} }
QVector<quint16> ModbusDataUtils::convertFromString(const QString &value, quint16 stringLength) QVector<quint16> ModbusDataUtils::convertFromString(const QString &value, quint16 stringLength, ByteOrder characterByteOrder)
{ {
Q_ASSERT_X(value.length() <= stringLength, "ModbusDataUtils", "cannot convert a string which is bigger than the desired register vector."); 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()); QByteArray data = value.toLatin1() + QByteArray('\0', stringLength - value.count());
QDataStream stream(&data, QIODevice::ReadOnly); QDataStream stream(&data, QIODevice::ReadOnly);
// Note: some devices use little endian within the register uint16 representation of the 2 characters.
stream.setByteOrder(characterByteOrder == ByteOrderBigEndian ? QDataStream::BigEndian : QDataStream::LittleEndian);
QVector<quint16> values; QVector<quint16> values;
for (int i = 0; i < stringLength; i++) { for (int i = 0; i < stringLength; i++) {
quint16 registerValue = 0; quint16 registerValue = 0;

View File

@ -88,7 +88,7 @@ public:
static qint32 convertToInt32(const QVector<quint16> &registers, ByteOrder byteOrder = ByteOrderLittleEndian); static qint32 convertToInt32(const QVector<quint16> &registers, ByteOrder byteOrder = ByteOrderLittleEndian);
static quint64 convertToUInt64(const QVector<quint16> &registers, ByteOrder byteOrder = ByteOrderLittleEndian); static quint64 convertToUInt64(const QVector<quint16> &registers, ByteOrder byteOrder = ByteOrderLittleEndian);
static qint64 convertToInt64(const QVector<quint16> &registers, ByteOrder byteOrder = ByteOrderLittleEndian); static qint64 convertToInt64(const QVector<quint16> &registers, ByteOrder byteOrder = ByteOrderLittleEndian);
static QString convertToString(const QVector<quint16> &registers); static QString convertToString(const QVector<quint16> &registers, ByteOrder characterByteOrder = ByteOrderLittleEndian);
static float convertToFloat32(const QVector<quint16> &registers, ByteOrder byteOrder = ByteOrderLittleEndian); static float convertToFloat32(const QVector<quint16> &registers, ByteOrder byteOrder = ByteOrderLittleEndian);
static double convertToFloat64(const QVector<quint16> &registers, ByteOrder byteOrder = ByteOrderLittleEndian); static double convertToFloat64(const QVector<quint16> &registers, ByteOrder byteOrder = ByteOrderLittleEndian);
@ -99,7 +99,7 @@ public:
static QVector<quint16> convertFromInt32(qint32 value, ByteOrder byteOrder = ByteOrderLittleEndian); static QVector<quint16> convertFromInt32(qint32 value, ByteOrder byteOrder = ByteOrderLittleEndian);
static QVector<quint16> convertFromUInt64(quint64 value, ByteOrder byteOrder = ByteOrderLittleEndian); static QVector<quint16> convertFromUInt64(quint64 value, ByteOrder byteOrder = ByteOrderLittleEndian);
static QVector<quint16> convertFromInt64(qint64 value, ByteOrder byteOrder = ByteOrderLittleEndian); static QVector<quint16> convertFromInt64(qint64 value, ByteOrder byteOrder = ByteOrderLittleEndian);
static QVector<quint16> convertFromString(const QString &value, quint16 stringLength); static QVector<quint16> convertFromString(const QString &value, quint16 stringLength, ByteOrder characterByteOrder = ByteOrderLittleEndian);
static QVector<quint16> convertFromFloat32(float value, ByteOrder byteOrder = ByteOrderLittleEndian); static QVector<quint16> convertFromFloat32(float value, ByteOrder byteOrder = ByteOrderLittleEndian);
static QVector<quint16> convertFromFloat64(double value, ByteOrder byteOrder = ByteOrderLittleEndian); static QVector<quint16> convertFromFloat64(double value, ByteOrder byteOrder = ByteOrderLittleEndian);
}; };

View File

@ -20,6 +20,7 @@ The basic structure of the modbus register JSON looks like following example:
"className": "MyConnection", "className": "MyConnection",
"protocol": "BOTH", "protocol": "BOTH",
"endianness": "BigEndian", "endianness": "BigEndian",
"stringEndianness": "BigEndian",
"errorLimitUntilNotReachable": 10, "errorLimitUntilNotReachable": 10,
"checkReachableRegister": "registerPropertyName", "checkReachableRegister": "registerPropertyName",
"enums": [ "enums": [
@ -138,6 +139,19 @@ There are 2 possibilities:
* `BigEndian`: default if not specified: register bytes come in following order `[0, 1, 2, 3]`: `ABCD` * `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` * `LittleEndian`: register bytes come in following order `[0, 1, 2, 3]`: `CDAB`
## String endianness
When converting multiple registers to a string, some modbus devices use a different endianess within a register.
One register contains 2 bytes, miltiple registers in a row build up a string. The string endianess tells the generated class how to parse those strings.
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 `[1, 0] [3, 2]`: `BADC`
Please not that the overall endianess of the device does not change the order of the register regarding strings since in modbus a normal register is definded as big endian. Only multiple registers combined to a numeric data type will be taken into account by the `endianess` property.
## Enums ## 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>`. 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>`.

View File

@ -277,7 +277,7 @@ def getConversionToValueMethod(registerDefinition):
elif registerDefinition['type'] == 'float64': elif registerDefinition['type'] == 'float64':
return ('ModbusDataUtils::convertFromFloat64(%s, m_endianness)' % propertyName) return ('ModbusDataUtils::convertFromFloat64(%s, m_endianness)' % propertyName)
elif registerDefinition['type'] == 'string': elif registerDefinition['type'] == 'string':
return ('ModbusDataUtils::convertFromString(%s)' % propertyName) return ('ModbusDataUtils::convertFromString(%s, m_stringEndianness)' % propertyName)
def getValueConversionMethod(registerDefinition): def getValueConversionMethod(registerDefinition):
@ -334,7 +334,7 @@ def getValueConversionMethod(registerDefinition):
elif registerDefinition['type'] == 'float64': elif registerDefinition['type'] == 'float64':
return ('ModbusDataUtils::convertToFloat64(values, m_endianness)') return ('ModbusDataUtils::convertToFloat64(values, m_endianness)')
elif registerDefinition['type'] == 'string': elif registerDefinition['type'] == 'string':
return ('ModbusDataUtils::convertToString(values)') return ('ModbusDataUtils::convertToString(values, m_stringEndianness)')
def writeBlockGetMethodDeclarations(fileDescriptor, registerDefinitions): def writeBlockGetMethodDeclarations(fileDescriptor, registerDefinitions):

View File

@ -71,6 +71,9 @@ def writeTcpHeaderFile():
writeLine(headerFile, ' ModbusDataUtils::ByteOrder endianness() const;') writeLine(headerFile, ' ModbusDataUtils::ByteOrder endianness() const;')
writeLine(headerFile, ' void setEndianness(ModbusDataUtils::ByteOrder endianness);') writeLine(headerFile, ' void setEndianness(ModbusDataUtils::ByteOrder endianness);')
writeLine(headerFile) writeLine(headerFile)
writeLine(headerFile, ' ModbusDataUtils::ByteOrder stringEndianness() const;')
writeLine(headerFile, ' void setStringEndianness(ModbusDataUtils::ByteOrder stringEndianness);')
writeLine(headerFile)
writeLine(headerFile, ' uint checkReachableRetries() const;') writeLine(headerFile, ' uint checkReachableRetries() const;')
writeLine(headerFile, ' void setCheckReachableRetries(uint checkReachableRetries);') writeLine(headerFile, ' void setCheckReachableRetries(uint checkReachableRetries);')
writeLine(headerFile) writeLine(headerFile)
@ -117,6 +120,7 @@ def writeTcpHeaderFile():
writeLine(headerFile, ' void updateFinished();') writeLine(headerFile, ' void updateFinished();')
writeLine(headerFile) writeLine(headerFile)
writeLine(headerFile, ' void endiannessChanged(ModbusDataUtils::ByteOrder endianness);') writeLine(headerFile, ' void endiannessChanged(ModbusDataUtils::ByteOrder endianness);')
writeLine(headerFile, ' void stringEndiannessChanged(ModbusDataUtils::ByteOrder stringEndianness);')
writeLine(headerFile) writeLine(headerFile)
writePropertyChangedSignals(headerFile, registerJson['registers']) writePropertyChangedSignals(headerFile, registerJson['registers'])
@ -148,6 +152,7 @@ def writeTcpHeaderFile():
# Private members # Private members
writeLine(headerFile, 'private:') writeLine(headerFile, 'private:')
writeLine(headerFile, ' ModbusDataUtils::ByteOrder m_endianness = ModbusDataUtils::ByteOrder%s;' % endianness) writeLine(headerFile, ' ModbusDataUtils::ByteOrder m_endianness = ModbusDataUtils::ByteOrder%s;' % endianness)
writeLine(headerFile, ' ModbusDataUtils::ByteOrder m_stringEndianness = ModbusDataUtils::ByteOrder%s;' % stringEndianness)
writeLine(headerFile, ' quint16 m_slaveId = 1;') writeLine(headerFile, ' quint16 m_slaveId = 1;')
writeLine(headerFile) writeLine(headerFile)
writeLine(headerFile, ' bool m_reachable = false;') writeLine(headerFile, ' bool m_reachable = false;')
@ -259,6 +264,22 @@ def writeTcpSourceFile():
writeLine(sourceFile, '}') writeLine(sourceFile, '}')
writeLine(sourceFile) writeLine(sourceFile)
writeLine(sourceFile, 'ModbusDataUtils::ByteOrder %s::stringEndianness() const' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' return m_stringEndianness;')
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'void %s::setStringEndianness(ModbusDataUtils::ByteOrder stringEndianness)' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' if (m_stringEndianness == stringEndianness)')
writeLine(sourceFile, ' return;')
writeLine(sourceFile,)
writeLine(sourceFile, ' m_stringEndianness = stringEndianness;')
writeLine(sourceFile, ' emit stringEndiannessChanged(m_stringEndianness);')
writeLine(sourceFile, '}')
writeLine(sourceFile)
# Property get methods # Property get methods
writePropertyGetSetMethodImplementationsTcp(sourceFile, className, registerJson['registers']) writePropertyGetSetMethodImplementationsTcp(sourceFile, className, registerJson['registers'])
if 'blocks' in registerJson: if 'blocks' in registerJson:
@ -445,6 +466,9 @@ def writeRtuHeaderFile():
writeLine(headerFile, ' ModbusDataUtils::ByteOrder endianness() const;') writeLine(headerFile, ' ModbusDataUtils::ByteOrder endianness() const;')
writeLine(headerFile, ' void setEndianness(ModbusDataUtils::ByteOrder endianness);') writeLine(headerFile, ' void setEndianness(ModbusDataUtils::ByteOrder endianness);')
writeLine(headerFile) writeLine(headerFile)
writeLine(headerFile, ' ModbusDataUtils::ByteOrder stringEndianness() const;')
writeLine(headerFile, ' void setStringEndianness(ModbusDataUtils::ByteOrder stringEndianness);')
writeLine(headerFile)
# Write registers get method declarations # Write registers get method declarations
writePropertyGetSetMethodDeclarationsRtu(headerFile, registerJson['registers']) writePropertyGetSetMethodDeclarationsRtu(headerFile, registerJson['registers'])
@ -486,6 +510,7 @@ def writeRtuHeaderFile():
writeLine(headerFile, ' void updateFinished();') writeLine(headerFile, ' void updateFinished();')
writeLine(headerFile) writeLine(headerFile)
writeLine(headerFile, ' void endiannessChanged(ModbusDataUtils::ByteOrder endianness);') writeLine(headerFile, ' void endiannessChanged(ModbusDataUtils::ByteOrder endianness);')
writeLine(headerFile, ' void stringEndiannessChanged(ModbusDataUtils::ByteOrder stringEndianness);')
writeLine(headerFile) writeLine(headerFile)
writePropertyChangedSignals(headerFile, registerJson['registers']) writePropertyChangedSignals(headerFile, registerJson['registers'])
if 'blocks' in registerJson: if 'blocks' in registerJson:
@ -518,6 +543,7 @@ def writeRtuHeaderFile():
writeLine(headerFile, 'private:') writeLine(headerFile, 'private:')
writeLine(headerFile, ' ModbusRtuMaster *m_modbusRtuMaster = nullptr;') writeLine(headerFile, ' ModbusRtuMaster *m_modbusRtuMaster = nullptr;')
writeLine(headerFile, ' ModbusDataUtils::ByteOrder m_endianness = ModbusDataUtils::ByteOrder%s;' % endianness) writeLine(headerFile, ' ModbusDataUtils::ByteOrder m_endianness = ModbusDataUtils::ByteOrder%s;' % endianness)
writeLine(headerFile, ' ModbusDataUtils::ByteOrder m_stringEndianness = ModbusDataUtils::ByteOrder%s;' % stringEndianness)
writeLine(headerFile, ' quint16 m_slaveId = 1;') writeLine(headerFile, ' quint16 m_slaveId = 1;')
writeLine(headerFile) writeLine(headerFile)
writeLine(headerFile, ' bool m_reachable = false;') writeLine(headerFile, ' bool m_reachable = false;')
@ -645,6 +671,21 @@ def writeRtuSourceFile():
writeLine(sourceFile, ' m_endianness = endianness;') writeLine(sourceFile, ' m_endianness = endianness;')
writeLine(sourceFile, ' emit endiannessChanged(m_endianness);') writeLine(sourceFile, ' emit endiannessChanged(m_endianness);')
writeLine(sourceFile, '}') writeLine(sourceFile, '}')
writeLine(sourceFile, 'ModbusDataUtils::ByteOrder %s::stringEndianness() const' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' return m_stringEndianness;')
writeLine(sourceFile, '}')
writeLine(sourceFile)
writeLine(sourceFile, 'void %s::setStringEndianness(ModbusDataUtils::ByteOrder stringEndianness)' % (className))
writeLine(sourceFile, '{')
writeLine(sourceFile, ' if (m_stringEndianness == stringEndianness)')
writeLine(sourceFile, ' return;')
writeLine(sourceFile,)
writeLine(sourceFile, ' m_stringEndianness = stringEndianness;')
writeLine(sourceFile, ' emit stringEndiannessChanged(m_stringEndianness);')
writeLine(sourceFile, '}')
writeLine(sourceFile) writeLine(sourceFile)
# Property get methods # Property get methods
@ -828,6 +869,11 @@ endianness = 'BigEndian'
if 'endianness' in registerJson: if 'endianness' in registerJson:
endianness = registerJson['endianness'] endianness = registerJson['endianness']
stringEndianness = 'BigEndian'
if 'stringEndianness' in registerJson:
stringEndianness = registerJson['stringEndianness']
errorLimitUntilNotReachable = 10 errorLimitUntilNotReachable = 10
if 'errorLimitUntilNotReachable' in registerJson: if 'errorLimitUntilNotReachable' in registerJson:
errorLimitUntilNotReachable = registerJson['errorLimitUntilNotReachable'] errorLimitUntilNotReachable = registerJson['errorLimitUntilNotReachable']
@ -875,6 +921,7 @@ logger.debug('Script path: %s' % scriptPath)
logger.debug('Output directory: %s' % outputDirectory) logger.debug('Output directory: %s' % outputDirectory)
logger.debug('Class name prefix: %s' % classNamePrefix) logger.debug('Class name prefix: %s' % classNamePrefix)
logger.debug('Endianness: %s' % endianness) logger.debug('Endianness: %s' % endianness)
logger.debug('String endianness: %s' % stringEndianness)
logger.debug('Error limit until not reachable: %s' % errorLimitUntilNotReachable) logger.debug('Error limit until not reachable: %s' % errorLimitUntilNotReachable)
logger.debug('Check reachable register: %s' % checkReachableRegister['id']) logger.debug('Check reachable register: %s' % checkReachableRegister['id'])