modbus-tool: Introduce string endianess

pull/122/head
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;
}
QString ModbusDataUtils::convertToString(const QVector<quint16> &registers)
QString ModbusDataUtils::convertToString(const QVector<quint16> &registers, ByteOrder characterByteOrder)
{
QByteArray bytes;
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++) {
stream << registers.at(i);
}
@ -239,11 +241,13 @@ QVector<quint16> ModbusDataUtils::convertFromInt64(qint64 value, ByteOrder byteO
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.");
QByteArray data = value.toLatin1() + QByteArray('\0', stringLength - value.count());
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;
for (int i = 0; i < stringLength; i++) {
quint16 registerValue = 0;

View File

@ -88,7 +88,7 @@ public:
static qint32 convertToInt32(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 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 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> convertFromUInt64(quint64 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> 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",
"protocol": "BOTH",
"endianness": "BigEndian",
"stringEndianness": "BigEndian",
"errorLimitUntilNotReachable": 10,
"checkReachableRegister": "registerPropertyName",
"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`
* `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
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':
return ('ModbusDataUtils::convertFromFloat64(%s, m_endianness)' % propertyName)
elif registerDefinition['type'] == 'string':
return ('ModbusDataUtils::convertFromString(%s)' % propertyName)
return ('ModbusDataUtils::convertFromString(%s, m_stringEndianness)' % propertyName)
def getValueConversionMethod(registerDefinition):
@ -334,7 +334,7 @@ def getValueConversionMethod(registerDefinition):
elif registerDefinition['type'] == 'float64':
return ('ModbusDataUtils::convertToFloat64(values, m_endianness)')
elif registerDefinition['type'] == 'string':
return ('ModbusDataUtils::convertToString(values)')
return ('ModbusDataUtils::convertToString(values, m_stringEndianness)')
def writeBlockGetMethodDeclarations(fileDescriptor, registerDefinitions):

View File

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