Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions libnymea-modbus/tools/connectiontool/modbusrtu.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ def writePropertyGetSetMethodImplementationsRtu(fileDescriptor, className, regis

# Check if we require a read method
if 'R' in registerDefinition['access']:
if 'enum' in registerDefinition:
if 'enum' in registerDefinition or 'flags' in registerDefinition:
writeLine(fileDescriptor, '%s::%s %s::%s() const' % (className, propertyTyp, className, propertyName))
else:
writeLine(fileDescriptor, '%s %s::%s() const' % (propertyTyp, className, propertyName))
Expand All @@ -71,7 +71,8 @@ def writePropertyGetSetMethodImplementationsRtu(fileDescriptor, className, regis
writeLine(fileDescriptor, '{')

writeLine(fileDescriptor, ' QVector<quint16> values = %s;' % getConversionToValueMethod(registerDefinition))
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Write \\"%s\\" register:" << %s << "size:" << %s << values;' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size']))
loggedValues = getLoggedValuesExpression(registerDefinition, 'values')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Write \\"%s\\" register:" << %s << "size:" << %s << %s;' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size'], loggedValues))
if registerDefinition['registerType'] == 'holdingRegister':
writeLine(fileDescriptor, ' return m_modbusRtuMaster->writeHoldingRegisters(m_slaveId, %s, values);' % (registerDefinition['address']))
elif registerDefinition['registerType'] == 'coils':
Expand Down Expand Up @@ -164,7 +165,8 @@ def writeBlockUpdateMethodImplementationsRtu(fileDescriptor, className, blockDef
writeLine(fileDescriptor, ' handleModbusError(reply->error());')
writeLine(fileDescriptor, ' if (reply->error() == ModbusRtuReply::NoError) {')
writeLine(fileDescriptor, ' QVector<quint16> blockValues = reply->result();')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from reading block \\"%s\\" register" << %s << "size:" << %s << blockValues;' % (className, blockName, blockStartAddress, blockSize))
loggedBlockValues = getLoggedValuesExpression(blockRegisters, 'blockValues')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from reading block \\"%s\\" register" << %s << "size:" << %s << %s;' % (className, blockName, blockStartAddress, blockSize, loggedBlockValues))
writeLine(fileDescriptor, ' if (blockValues.size() == %s) {' % (blockSize))

# Start parsing the registers using offsets
Expand All @@ -175,7 +177,7 @@ def writeBlockUpdateMethodImplementationsRtu(fileDescriptor, className, blockDef
offset += blockRegister['size']

writeLine(fileDescriptor, ' } else {')
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Reading from \\"%s\\" register" << %s << "size:" << %s << "returned different size than requested. Ignoring incomplete data" << blockValues;' % (className, blockName, blockStartAddress, blockSize))
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Reading from \\"%s\\" register" << %s << "size:" << %s << "returned different size than requested. Ignoring incomplete data" << %s;' % (className, blockName, blockStartAddress, blockSize, loggedBlockValues))
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor, ' });')
Expand Down Expand Up @@ -440,7 +442,8 @@ def writeInitMethodImplementationRtu(fileDescriptor, className, registerDefiniti
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' QVector<quint16> blockValues = reply->result();')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from reading init block \\"%s\\" register" << %s << "size:" << %s << blockValues;' % (className, blockName, blockStartAddress, blockSize))
loggedBlockValues = getLoggedValuesExpression(blockRegisters, 'blockValues')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from reading init block \\"%s\\" register" << %s << "size:" << %s << %s;' % (className, blockName, blockStartAddress, blockSize, loggedBlockValues))
writeLine(fileDescriptor, ' if (blockValues.size() == %s) {' % (blockSize))

# Start parsing the registers using offsets
Expand All @@ -452,7 +455,7 @@ def writeInitMethodImplementationRtu(fileDescriptor, className, registerDefiniti
offset += blockRegister['size']

writeLine(fileDescriptor, ' } else {')
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Reading from \\"%s\\" register" << %s << "size:" << %s << "returned different size than requested. Ignoring incomplete data" << blockValues;' % (className, blockName, blockStartAddress, blockSize))
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Reading from \\"%s\\" register" << %s << "size:" << %s << "returned different size than requested. Ignoring incomplete data" << %s;' % (className, blockName, blockStartAddress, blockSize, loggedBlockValues))
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor, ' verifyInitFinished();')
writeLine(fileDescriptor, ' });')
Expand Down Expand Up @@ -590,7 +593,8 @@ def writeUpdateMethodRtu(fileDescriptor, className, registerDefinitions, blockDe
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' QVector<quint16> blockValues = reply->result();')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from reading block \\"%s\\" register" << %s << "size:" << %s << blockValues;' % (className, blockName, blockStartAddress, blockSize))
loggedBlockValues = getLoggedValuesExpression(blockRegisters, 'blockValues')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from reading block \\"%s\\" register" << %s << "size:" << %s << %s;' % (className, blockName, blockStartAddress, blockSize, loggedBlockValues))
writeLine(fileDescriptor, ' if (blockValues.size() == %s) {' % (blockSize))

# Start parsing the registers using offsets
Expand All @@ -602,7 +606,7 @@ def writeUpdateMethodRtu(fileDescriptor, className, registerDefinitions, blockDe
offset += blockRegister['size']

writeLine(fileDescriptor, ' } else {')
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Reading from \\"%s\\" register" << %s << "size:" << %s << "returned different size than requested. Ignoring incomplete data" << blockValues;' % (className, blockName, blockStartAddress, blockSize))
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Reading from \\"%s\\" register" << %s << "size:" << %s << "returned different size than requested. Ignoring incomplete data" << %s;' % (className, blockName, blockStartAddress, blockSize, loggedBlockValues))
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor, ' verifyUpdateFinished();')
writeLine(fileDescriptor, ' });')
Expand Down
2 changes: 1 addition & 1 deletion libnymea-modbus/tools/connectiontool/modbustcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def writePropertyGetSetMethodImplementationsTcp(fileDescriptor, className, regis

# Check if we require a read method
if 'R' in registerDefinition['access']:
if 'enum' in registerDefinition:
if 'enum' in registerDefinition or 'flags' in registerDefinition:
writeLine(fileDescriptor, '%s::%s %s::%s() const' % (className, propertyTyp, className, propertyName))
else:
writeLine(fileDescriptor, '%s %s::%s() const' % (propertyTyp, className, propertyName))
Expand Down
92 changes: 82 additions & 10 deletions libnymea-modbus/tools/connectiontool/toolcommon.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,8 +181,74 @@ def writeEnumDefinition(fileDescriptor, enumDefinition):
writeLine(fileDescriptor)


def writeFlagsDefinition(fileDescriptor, flagsDefinition):
logger.debug('Writing flags %s', flagsDefinition)
flagsBaseName = flagsDefinition['name']
flagName = flagsBaseName + 'Flag'
flagsName = flagsBaseName + 'Flags'
flagValues = flagsDefinition['values']
writeLine(fileDescriptor, ' enum %s {' % flagName)
for i in range(len(flagValues)):
flagData = flagValues[i]
line = (' %s%s = %s' % (flagName, flagData['key'], flagData['value']))
if i < (len(flagValues) - 1):
line += ','
writeLine(fileDescriptor, line)
writeLine(fileDescriptor, ' };')
writeLine(fileDescriptor, ' Q_ENUM(%s)' % flagName)
writeLine(fileDescriptor, ' Q_DECLARE_FLAGS(%s, %s)' % (flagsName, flagName))
writeLine(fileDescriptor, ' Q_FLAG(%s)' % flagsName)
writeLine(fileDescriptor)


def validateFlags(registerJson):
flagsDefinitions = registerJson.get('flags', [])
knownFlags = set()
for flagsDefinition in flagsDefinitions:
flagsName = flagsDefinition.get('name')
if not flagsName or flagsName in knownFlags:
logger.warning('Error: duplicate or missing flags definition name "%s".' % flagsName)
exit(1)
knownFlags.add(flagsName)
masks = set()
for flagData in flagsDefinition.get('values', []):
mask = flagData.get('value')
if isinstance(mask, bool) or not isinstance(mask, int) or mask == 0 or mask > 0xffff or mask & (mask - 1):
logger.warning('Error: flag "%s.%s" must be a literal, nonzero, single-bit uint16 mask.' % (flagsName, flagData.get('key')))
exit(1)
if mask in masks:
logger.warning('Error: duplicate mask %s in flags definition "%s".' % (mask, flagsName))
exit(1)
masks.add(mask)

registerDefinitions = list(registerJson.get('registers', []))
for blockDefinition in registerJson.get('blocks', []):
registerDefinitions.extend(blockDefinition.get('registers', []))
for registerDefinition in registerDefinitions:
if 'enum' in registerDefinition and 'flags' in registerDefinition:
logger.warning('Error: register "%s" cannot declare both enum and flags.' % registerDefinition.get('id'))
exit(1)
if 'flags' not in registerDefinition:
continue
if registerDefinition['flags'] not in knownFlags:
logger.warning('Error: register "%s" references unknown flags definition "%s".' % (registerDefinition.get('id'), registerDefinition['flags']))
exit(1)
if registerDefinition.get('type') != 'uint16':
logger.warning('Error: flags are currently supported only for uint16 registers ("%s").' % registerDefinition.get('id'))
exit(1)


def getLoggedValuesExpression(registerDefinitions, variableName):
if isinstance(registerDefinitions, dict):
registerDefinitions = [registerDefinitions]
return '"[REDACTED]"' if any(registerDefinition.get('sensitive', False) for registerDefinition in registerDefinitions) else variableName


def getCppDataType(registerDefinition, rawType = False):
if not rawType:
if 'flags' in registerDefinition:
return registerDefinition['flags'] + 'Flags'

if 'enum' in registerDefinition:
return registerDefinition['enum']

Expand Down Expand Up @@ -228,8 +294,7 @@ def getConversionToValueMethod(registerDefinition):
propertyName = registerDefinition['id']
propertyTyp = getCppDataType(registerDefinition, True)

if 'enum' in registerDefinition:
enumName = registerDefinition['enum']
if 'enum' in registerDefinition or 'flags' in registerDefinition:
if registerDefinition['type'] == 'uint16':
return ('ModbusDataUtils::convertFromUInt16(static_cast<%s>(%s))' % (propertyTyp, propertyName))
elif registerDefinition['type'] == 'int16':
Expand Down Expand Up @@ -291,12 +356,14 @@ def getConversionToValueMethod(registerDefinition):
return ('ModbusDataUtils::convertFromFloat64(%s, m_endianness)' % propertyName)
elif registerDefinition['type'] == 'string':
return ('ModbusDataUtils::convertFromString(%s, m_stringEndianness)' % propertyName)
elif registerDefinition['type'] == 'raw':
return propertyName


def getValueConversionMethod(registerDefinition):
# Handle enums
if 'enum' in registerDefinition:
enumName = registerDefinition['enum']
if 'enum' in registerDefinition or 'flags' in registerDefinition:
enumName = getCppDataType(registerDefinition)
if registerDefinition['type'] == 'uint16':
return ('static_cast<%s>(ModbusDataUtils::convertToUInt16(values))' % (enumName))
elif registerDefinition['type'] == 'int16':
Expand Down Expand Up @@ -469,7 +536,10 @@ def writeRegistersDebugLine(fileDescriptor, debugObjectParamName, registerDefini
elif registerType == 'discreteInputs':
typeString = 'discrete'

line = ('" - %s %s | %s: " << %s->%s()' % (typeString, registerDefinition['address'], registerDefinition['description'], debugObjectParamName, propertyName))
if registerDefinition.get('sensitive', False):
line = ('" - %s %s | %s: [REDACTED]"' % (typeString, registerDefinition['address'], registerDefinition['description']))
else:
line = ('" - %s %s | %s: " << %s->%s()' % (typeString, registerDefinition['address'], registerDefinition['description'], debugObjectParamName, propertyName))
if 'unit' in registerDefinition and registerDefinition['unit'] != '':
line += (' << " [%s]"' % registerDefinition['unit'])
writeLine(fileDescriptor, ' debug.nospace().noquote() << %s << "\\n";' % (line))
Expand Down Expand Up @@ -529,7 +599,8 @@ def writePropertyProcessMethodImplementations(fileDescriptor, className, registe

writeLine(fileDescriptor, 'void %s::process%sRegisterValues(const QVector<quint16> &values)' % (className, propertyName[0].upper() + propertyName[1:]))
writeLine(fileDescriptor, '{')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from \\"%s\\" register" << %s << "size:" << %s << values;' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size']))
loggedValues = getLoggedValuesExpression(registerDefinition, 'values')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from \\"%s\\" register" << %s << "size:" << %s << %s;' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size'], loggedValues))
writeLine(fileDescriptor, ' if (values.size() == %s) {' % (registerDefinition['size']))
writeLine(fileDescriptor, ' %s received%s = %s;' % (propertyTyp, propertyName[0].upper() + propertyName[1:], getValueConversionMethod(registerDefinition)))
writeLine(fileDescriptor, ' emit %sReadFinished(received%s);' % (propertyName, propertyName[0].upper() + propertyName[1:]))
Expand All @@ -539,7 +610,7 @@ def writePropertyProcessMethodImplementations(fileDescriptor, className, registe
writeLine(fileDescriptor, ' emit %sChanged(m_%s);' % (propertyName, propertyName))
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor, ' } else {')
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Reading from \\"%s\\" registers" << %s << "size:" << %s << "returned different size than requested. Ignoring incomplete data" << values;' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size']))
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Reading from \\"%s\\" registers" << %s << "size:" << %s << "returned different size than requested. Ignoring incomplete data" << %s;' % (className, registerDefinition['description'], registerDefinition['address'], registerDefinition['size'], loggedValues))
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor, '}')
writeLine(fileDescriptor)
Expand Down Expand Up @@ -584,7 +655,8 @@ def writeBlockPropertiesProcessMethodImplementations(fileDescriptor, className,

writeLine(fileDescriptor, 'void %s::processBlock%sRegisterValues(const QVector<quint16> &blockValues)' % (className, blockName[0].upper() + blockName[1:]))
writeLine(fileDescriptor, '{')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from reading block \\"%s\\" register" << %s << "size:" << %s << blockValues;' % (className, blockName, blockStartAddress, blockSize))
loggedBlockValues = getLoggedValuesExpression(blockRegisters, 'blockValues')
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from reading block \\"%s\\" register" << %s << "size:" << %s << %s;' % (className, blockName, blockStartAddress, blockSize, loggedBlockValues))
writeLine(fileDescriptor, ' if (blockValues.size() == %s) {' % (blockSize))

# Start parsing the registers using offsets
Expand All @@ -595,7 +667,7 @@ def writeBlockPropertiesProcessMethodImplementations(fileDescriptor, className,
offset += blockRegister['size']

writeLine(fileDescriptor, ' } else {')
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Reading from \\"%s\\" block registers" << %s << "size:" << %s << "returned different size than requested. Ignoring incomplete data" << blockValues;' % (className, blockName, blockStartAddress, blockSize))
writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Reading from \\"%s\\" block registers" << %s << "size:" << %s << "returned different size than requested. Ignoring incomplete data" << %s;' % (className, blockName, blockStartAddress, blockSize, loggedBlockValues))
writeLine(fileDescriptor, ' }')
writeLine(fileDescriptor, '}')
writeLine(fileDescriptor)
Expand Down Expand Up @@ -655,4 +727,4 @@ def writeEnqueueRequestMethodImplementation(fileDescriptor, className):
writeLine(fileDescriptor)
writeLine(fileDescriptor, ' m_updateRequestQueue.enqueue(function);')
writeLine(fileDescriptor, '}')
writeLine(fileDescriptor)
writeLine(fileDescriptor)
Loading