658 lines
33 KiB
Python
658 lines
33 KiB
Python
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
#
|
|
# Copyright (C) 2013 - 2024, nymea GmbH
|
|
# Copyright (C) 2024 - 2025, chargebyte austria GmbH
|
|
#
|
|
# This file is part of nymea-plugins-modbus.
|
|
#
|
|
# nymea-plugins-modbus 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 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# nymea-plugins-modbus 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 nymea-plugins-modbus. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
|
|
|
|
import re
|
|
import json
|
|
import datetime
|
|
import logging
|
|
|
|
logger = logging.getLogger('modbus-tools')
|
|
|
|
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()
|
|
logger.debug('--> words', words)
|
|
finalWords = []
|
|
|
|
for i in range(len(words)):
|
|
camelCaseSplit = splitCamelCase(words[i])
|
|
if len(camelCaseSplit) == 0:
|
|
finalWords.append(words[i])
|
|
else:
|
|
logging.debug('Camel case 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:])
|
|
logging.debug('Convert camel case:', text, '-->', finalText)
|
|
return finalText
|
|
|
|
|
|
def loadJsonFile(filePath):
|
|
logger.info('Loading JSON file %s', filePath)
|
|
jsonFile = open(filePath, 'r')
|
|
return json.load(jsonFile)
|
|
|
|
|
|
def writeLine(fileDescriptor, line = ''):
|
|
fileDescriptor.write(line + '\n')
|
|
|
|
|
|
def writeLicenseHeader(fileDescriptor):
|
|
writeLine(fileDescriptor, '// SPDX-License-Identifier: GPL-3.0-or-later')
|
|
writeLine(fileDescriptor)
|
|
writeLine(fileDescriptor, '/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *')
|
|
writeLine(fileDescriptor, '*')
|
|
writeLine(fileDescriptor, '* Copyright (C) 2013 - 2024, nymea GmbH')
|
|
writeLine(fileDescriptor, '* Copyright (C) 2013 - %s, chargebyte austria GmbH' % datetime.datetime.now().year)
|
|
writeLine(fileDescriptor, '*')
|
|
writeLine(fileDescriptor, '* nymea-plugins-modbus is free software: you can redistribute it and/or modify')
|
|
writeLine(fileDescriptor, '* it under the terms of the GNU General Public License as published by')
|
|
writeLine(fileDescriptor, '* the Free Software Foundation, either version 3 of the License, or')
|
|
writeLine(fileDescriptor, '* (at your option) any later version.')
|
|
writeLine(fileDescriptor, '*')
|
|
writeLine(fileDescriptor, '* nymea-plugins-modbus is distributed in the hope that it will be useful,')
|
|
writeLine(fileDescriptor, '* but WITHOUT ANY WARRANTY; without even the implied warranty of')
|
|
writeLine(fileDescriptor, '* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the')
|
|
writeLine(fileDescriptor, '* GNU General Public License for more details.')
|
|
writeLine(fileDescriptor, '*')
|
|
writeLine(fileDescriptor, '* You should have received a copy of the GNU General Public License')
|
|
writeLine(fileDescriptor, '* along with nymea-plugins-modbus. If not, see <https://www.gnu.org/licenses/>.')
|
|
writeLine(fileDescriptor, '*')
|
|
writeLine(fileDescriptor, '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */')
|
|
writeLine(fileDescriptor)
|
|
writeLine(fileDescriptor, '/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *')
|
|
writeLine(fileDescriptor, '*')
|
|
writeLine(fileDescriptor, '* WARNING')
|
|
writeLine(fileDescriptor, '*')
|
|
writeLine(fileDescriptor, '* This file has been autogenerated. Any changes in this file may be overwritten.')
|
|
writeLine(fileDescriptor, '* If you want to change something, update the register json or the tool.')
|
|
writeLine(fileDescriptor, '*')
|
|
writeLine(fileDescriptor, '* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */')
|
|
writeLine(fileDescriptor)
|
|
|
|
|
|
def writeRegistersEnum(fileDescriptor, registerJson):
|
|
logger.debug('Writing enum for all registers')
|
|
|
|
registerEnums = {}
|
|
|
|
# Read all register names and addresses
|
|
if 'blocks' in registerJson:
|
|
for blockDefinition in registerJson['blocks']:
|
|
blockRegisters = blockDefinition['registers']
|
|
for blockRegister in blockRegisters:
|
|
registerName = blockRegister['id']
|
|
registerAddress = blockRegister['address']
|
|
registerEnums[registerAddress] = registerName
|
|
|
|
for registerDefinition in registerJson['registers']:
|
|
registerName = registerDefinition['id']
|
|
registerAddress = registerDefinition['address']
|
|
registerEnums[registerAddress] = registerName
|
|
|
|
|
|
# Sort the enum map
|
|
registersKeys = registerEnums.keys()
|
|
sortedRegistersKeys = sorted(registersKeys)
|
|
sortedRegisterEnumList = []
|
|
|
|
logger.debug('Sorted registers')
|
|
for registerAddress in sortedRegistersKeys:
|
|
logger.debug('--> %s : %s' % (registerAddress, registerEnums[registerAddress]))
|
|
enumData = {}
|
|
enumData['key'] = registerEnums[registerAddress]
|
|
enumData['value'] = registerAddress
|
|
sortedRegisterEnumList.append(enumData)
|
|
|
|
enumName = 'Registers'
|
|
writeLine(fileDescriptor, ' enum %s {' % enumName)
|
|
for i in range(len(sortedRegisterEnumList)):
|
|
enumData = sortedRegisterEnumList[i]
|
|
line = (' Register%s = %s' % (enumData['key'][0].upper() + enumData['key'][1:] , enumData['value']))
|
|
if i < (len(sortedRegisterEnumList) - 1):
|
|
line += ','
|
|
|
|
writeLine(fileDescriptor, line)
|
|
|
|
writeLine(fileDescriptor, ' };')
|
|
writeLine(fileDescriptor, ' Q_ENUM(%s)' % enumName)
|
|
writeLine(fileDescriptor)
|
|
|
|
|
|
def writeEnumDefinition(fileDescriptor, enumDefinition):
|
|
logger.debug('Writing enum %s', 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, rawType = False):
|
|
if not rawType:
|
|
if 'enum' in registerDefinition:
|
|
return registerDefinition['enum']
|
|
|
|
if 'scaleFactor' in registerDefinition or 'staticScaleFactor' 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'
|
|
|
|
if registerDefinition['type'] == 'bytearray':
|
|
return 'QByteArray'
|
|
|
|
if registerDefinition['type'] == 'raw':
|
|
return 'QVector<quint16>'
|
|
|
|
|
|
def getConversionToValueMethod(registerDefinition):
|
|
# Handle enums
|
|
propertyName = registerDefinition['id']
|
|
propertyTyp = getCppDataType(registerDefinition, True)
|
|
|
|
if 'enum' in registerDefinition:
|
|
enumName = registerDefinition['enum']
|
|
if registerDefinition['type'] == 'uint16':
|
|
return ('ModbusDataUtils::convertFromUInt16(static_cast<%s>(%s))' % (propertyTyp, propertyName))
|
|
elif registerDefinition['type'] == 'int16':
|
|
return ('ModbusDataUtils::convertFromInt16(static_cast<%s>(%s))' % (propertyTyp, propertyName))
|
|
elif registerDefinition['type'] == 'uint32':
|
|
return ('ModbusDataUtils::convertFromUInt32(static_cast<%s>(%s), m_endianness)' % (propertyTyp, propertyName))
|
|
elif registerDefinition['type'] == 'int32':
|
|
return ('ModbusDataUtils::convertFromInt32(static_cast<%s>(%s), m_endianness)' % (propertyTyp, propertyName))
|
|
|
|
# Handle scale factors
|
|
if 'scaleFactor' in registerDefinition:
|
|
scaleFactorProperty = 'm_%s' % registerDefinition['scaleFactor']
|
|
if registerDefinition['type'] == 'uint16':
|
|
return ('ModbusDataUtils::convertFromUInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactorProperty))
|
|
elif registerDefinition['type'] == 'int16':
|
|
return ('ModbusDataUtils::convertFromInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactorProperty))
|
|
elif registerDefinition['type'] == 'uint32':
|
|
return ('ModbusDataUtils::convertFromUInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), m_endianness)' % (propertyTyp, propertyName, scaleFactorProperty))
|
|
elif registerDefinition['type'] == 'int32':
|
|
return ('ModbusDataUtils::convertFromInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), m_endianness)' % (propertyTyp, propertyName, scaleFactorProperty))
|
|
elif registerDefinition['type'] == 'uint64':
|
|
return ('ModbusDataUtils::convertFromUInt64(static_cast<%s>(%s * 1.0 / pow(10, %s)), m_endianness)' % (propertyTyp, propertyName, scaleFactorProperty))
|
|
elif registerDefinition['type'] == 'int64':
|
|
return ('ModbusDataUtils::convertFromInt64(static_cast<%s>(%s * 1.0 / pow(10, %s)), m_endianness)' % (propertyTyp, propertyName, scaleFactorProperty))
|
|
|
|
|
|
elif 'staticScaleFactor' in registerDefinition:
|
|
scaleFactor = registerDefinition['staticScaleFactor']
|
|
if registerDefinition['type'] == 'uint16':
|
|
return ('ModbusDataUtils::convertFromUInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactor))
|
|
elif registerDefinition['type'] == 'int16':
|
|
return ('ModbusDataUtils::convertFromInt16(static_cast<%s>(%s * 1.0 / pow(10, %s)))' % (propertyTyp, propertyName, scaleFactor))
|
|
elif registerDefinition['type'] == 'uint32':
|
|
return ('ModbusDataUtils::convertFromUInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), m_endianness)' % (propertyTyp, propertyName, scaleFactor))
|
|
elif registerDefinition['type'] == 'int32':
|
|
return ('ModbusDataUtils::convertFromInt32(static_cast<%s>(%s * 1.0 / pow(10, %s)), m_endianness)' % (propertyTyp, propertyName, scaleFactor))
|
|
elif registerDefinition['type'] == 'uint64':
|
|
return ('ModbusDataUtils::convertFromUInt64(static_cast<%s>(%s * 1.0 / pow(10, %s)), m_endianness)' % (propertyTyp, propertyName, scaleFactor))
|
|
elif registerDefinition['type'] == 'int64':
|
|
return ('ModbusDataUtils::convertFromInt64(static_cast<%s>(%s * 1.0 / pow(10, %s)), m_endianness)' % (propertyTyp, propertyName, scaleFactor))
|
|
|
|
|
|
# Handle default types
|
|
elif registerDefinition['type'] == 'uint16':
|
|
return ('ModbusDataUtils::convertFromUInt16(%s)' % propertyName)
|
|
elif registerDefinition['type'] == 'int16':
|
|
return ('ModbusDataUtils::convertFromInt16(%s)' % propertyName)
|
|
elif registerDefinition['type'] == 'uint32':
|
|
return ('ModbusDataUtils::convertFromUInt32(%s, m_endianness)' % (propertyName))
|
|
elif registerDefinition['type'] == 'int32':
|
|
return ('ModbusDataUtils::convertFromInt32(%s, m_endianness)' % (propertyName))
|
|
elif registerDefinition['type'] == 'uint64':
|
|
return ('ModbusDataUtils::convertFromUInt64(%s, m_endianness)' % (propertyName))
|
|
elif registerDefinition['type'] == 'int64':
|
|
return ('ModbusDataUtils::convertFromInt64(%s, m_endianness)' % (propertyName))
|
|
elif registerDefinition['type'] == 'float':
|
|
return ('ModbusDataUtils::convertFromFloat32(%s, m_endianness)' % propertyName)
|
|
elif registerDefinition['type'] == 'float64':
|
|
return ('ModbusDataUtils::convertFromFloat64(%s, m_endianness)' % propertyName)
|
|
elif registerDefinition['type'] == 'string':
|
|
return ('ModbusDataUtils::convertFromString(%s, m_stringEndianness)' % propertyName)
|
|
|
|
|
|
def getValueConversionMethod(registerDefinition):
|
|
# Handle enums
|
|
if 'enum' in registerDefinition:
|
|
enumName = registerDefinition['enum']
|
|
if registerDefinition['type'] == 'uint16':
|
|
return ('static_cast<%s>(ModbusDataUtils::convertToUInt16(values))' % (enumName))
|
|
elif registerDefinition['type'] == 'int16':
|
|
return ('static_cast<%s>(ModbusDataUtils::convertToInt16(values))' % (enumName))
|
|
elif registerDefinition['type'] == 'uint32':
|
|
return ('static_cast<%s>(ModbusDataUtils::convertToUInt32(values, m_endianness))' % (enumName))
|
|
elif registerDefinition['type'] == 'int32':
|
|
return ('static_cast<%s>(ModbusDataUtils::convertToInt32(values, m_endianness))' % (enumName))
|
|
|
|
# Handle scale factors
|
|
if 'scaleFactor' in registerDefinition:
|
|
scaleFactorProperty = 'm_%s' % registerDefinition['scaleFactor']
|
|
if registerDefinition['type'] == 'uint16':
|
|
return ('ModbusDataUtils::convertToUInt16(values) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
|
|
elif registerDefinition['type'] == 'int16':
|
|
return ('ModbusDataUtils::convertToInt16(values) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
|
|
elif registerDefinition['type'] == 'uint32':
|
|
return ('ModbusDataUtils::convertToUInt32(values, m_endianness) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
|
|
elif registerDefinition['type'] == 'int32':
|
|
return ('ModbusDataUtils::convertToInt32(values, m_endianness) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
|
|
elif registerDefinition['type'] == 'uint64':
|
|
return ('ModbusDataUtils::convertToUInt64(values, m_endianness) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
|
|
elif registerDefinition['type'] == 'int64':
|
|
return ('ModbusDataUtils::convertToInt64(values, m_endianness) * 1.0 * pow(10, %s)' % (scaleFactorProperty))
|
|
|
|
elif 'staticScaleFactor' in registerDefinition:
|
|
scaleFactor = registerDefinition['staticScaleFactor']
|
|
if registerDefinition['type'] == 'uint16':
|
|
return ('ModbusDataUtils::convertToUInt16(values) * 1.0 * pow(10, %s)' % (scaleFactor))
|
|
elif registerDefinition['type'] == 'int16':
|
|
return ('ModbusDataUtils::convertToInt16(values) * 1.0 * pow(10, %s)' % (scaleFactor))
|
|
elif registerDefinition['type'] == 'uint32':
|
|
return ('ModbusDataUtils::convertToUInt32(values, m_endianness) * 1.0 * pow(10, %s)' % (scaleFactor))
|
|
elif registerDefinition['type'] == 'int32':
|
|
return ('ModbusDataUtils::convertToInt32(values, m_endianness) * 1.0 * pow(10, %s)' % (scaleFactor))
|
|
elif registerDefinition['type'] == 'uint64':
|
|
return ('ModbusDataUtils::convertToUInt64(values, m_endianness) * 1.0 * pow(10, %s)' % (scaleFactor))
|
|
elif registerDefinition['type'] == 'int64':
|
|
return ('ModbusDataUtils::convertToInt64(values, m_endianness) * 1.0 * pow(10, %s)' % (scaleFactor))
|
|
|
|
|
|
# Handle default types
|
|
elif registerDefinition['type'] == 'uint16':
|
|
return ('ModbusDataUtils::convertToUInt16(values)')
|
|
elif registerDefinition['type'] == 'int16':
|
|
return ('ModbusDataUtils::convertToInt16(values)')
|
|
elif registerDefinition['type'] == 'uint32':
|
|
return ('ModbusDataUtils::convertToUInt32(values, m_endianness)')
|
|
elif registerDefinition['type'] == 'int32':
|
|
return ('ModbusDataUtils::convertToInt32(values, m_endianness)')
|
|
elif registerDefinition['type'] == 'uint64':
|
|
return ('ModbusDataUtils::convertToUInt64(values, m_endianness)')
|
|
elif registerDefinition['type'] == 'int64':
|
|
return ('ModbusDataUtils::convertToInt64(values, m_endianness)')
|
|
elif registerDefinition['type'] == 'float':
|
|
return ('ModbusDataUtils::convertToFloat32(values, m_endianness)')
|
|
elif registerDefinition['type'] == 'float64':
|
|
return ('ModbusDataUtils::convertToFloat64(values, m_endianness)')
|
|
elif registerDefinition['type'] == 'string':
|
|
return ('ModbusDataUtils::convertToString(values, m_stringEndianness)')
|
|
elif registerDefinition['type'] == 'bytearray':
|
|
return ('ModbusDataUtils::convertToByteArray(values)')
|
|
elif registerDefinition['type'] == 'raw':
|
|
return ('values')
|
|
|
|
|
|
def writeBlockGetMethodDeclarations(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 writePropertyUpdateMethodDeclarations(fileDescriptor, registerDefinitions):
|
|
for registerDefinition in registerDefinitions:
|
|
if 'access' in registerDefinition:
|
|
if not 'R' in registerDefinition['access']:
|
|
continue
|
|
|
|
propertyName = registerDefinition['id']
|
|
writeLine(fileDescriptor, ' void update%s();' % (propertyName[0].upper() + propertyName[1:]))
|
|
|
|
|
|
def validateBlocks(blockDefinitions):
|
|
for blockDefinition in blockDefinitions:
|
|
blockName = blockDefinition['id']
|
|
blockRegisters = blockDefinition['registers']
|
|
|
|
blockStartAddress = 0
|
|
registerCount = 0
|
|
blockSize = 0
|
|
registerAccess = ""
|
|
registerType = ""
|
|
|
|
for i, blockRegister in enumerate(blockRegisters):
|
|
if i == 0:
|
|
blockStartAddress = blockRegister['address']
|
|
registerAccess = blockRegister['access']
|
|
registerType = blockRegister['registerType']
|
|
else:
|
|
previousRegisterAddress = blockRegisters[i - 1]['address']
|
|
previousRegisterSize = blockRegisters[i - 1]['size']
|
|
#previousRegisterType = blockRegisters[i - 1]['registerType']
|
|
if previousRegisterAddress + previousRegisterSize != blockRegister['address']:
|
|
logger.warning('Error: block %s has invalid register order in register %s. There seems to be a gap between the registers.' % (blockName, blockRegister['id']))
|
|
exit(1)
|
|
|
|
if blockRegister['access'] != registerAccess:
|
|
logger.warning('Error: block %s has inconsistent register access in register %s. The block registers dont seem to have the same access rights.' % (blockName, blockRegister['id']))
|
|
exit(1)
|
|
|
|
if blockRegister['registerType'] != registerType:
|
|
logger.warning('Error: block %s has inconsistent register type in register %s. The block registers dont seem to be from the same type.' % (blockName, blockRegister['id']))
|
|
exit(1)
|
|
|
|
registerCount += 1
|
|
blockSize += blockRegister['size']
|
|
|
|
logger.debug('Define valid block \"%s\" starting at %s with length %s containing %s properties to read.' % (blockName, blockStartAddress, blockSize, registerCount))
|
|
|
|
|
|
def writeBlocksUpdateMethodDeclarations(fileDescriptor, blockDefinitions):
|
|
for blockDefinition in blockDefinitions:
|
|
blockName = blockDefinition['id']
|
|
blockRegisters = blockDefinition['registers']
|
|
blockStartAddress = 0
|
|
blockSize = 0
|
|
registerCount = 0
|
|
|
|
for i, blockRegister in enumerate(blockRegisters):
|
|
if i == 0:
|
|
blockStartAddress = blockRegister['address']
|
|
|
|
registerCount += 1
|
|
blockSize += blockRegister['size']
|
|
|
|
# Write the block update method
|
|
writeLine(fileDescriptor, ' /* Read block from start address %s with size of %s registers containing following %s properties:' % (blockStartAddress, blockSize, registerCount))
|
|
for i, registerDefinition in enumerate(blockRegisters):
|
|
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, ' */' )
|
|
writeLine(fileDescriptor, ' void update%sBlock();' % (blockName[0].upper() + blockName[1:]))
|
|
writeLine(fileDescriptor)
|
|
|
|
|
|
def writeRegistersDebugLine(fileDescriptor, debugObjectParamName, registerDefinitions):
|
|
for registerDefinition in registerDefinitions:
|
|
if 'access' in registerDefinition:
|
|
if not 'R' in registerDefinition['access']:
|
|
continue
|
|
|
|
propertyName = registerDefinition['id']
|
|
registerType = registerDefinition['registerType']
|
|
typeString = ''
|
|
if registerType == 'holdingRegister':
|
|
typeString = 'holding '
|
|
elif registerType == 'inputRegister':
|
|
typeString = 'input '
|
|
elif registerType == 'coils':
|
|
typeString = 'coils '
|
|
elif registerType == 'discreteInputs':
|
|
typeString = 'discrete'
|
|
|
|
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))
|
|
|
|
|
|
def writePropertyChangedSignals(fileDescriptor, registerDefinitions):
|
|
for registerDefinition in registerDefinitions:
|
|
if 'access' in registerDefinition:
|
|
if not 'R' in registerDefinition['access']:
|
|
continue
|
|
|
|
propertyName = registerDefinition['id']
|
|
propertyTyp = getCppDataType(registerDefinition)
|
|
if propertyTyp == 'QString':
|
|
writeLine(fileDescriptor, ' void %sChanged(const %s &%s);' % (propertyName, propertyTyp, propertyName))
|
|
writeLine(fileDescriptor, ' void %sReadFinished(const %s &%s);' % (propertyName, propertyTyp, propertyName))
|
|
else:
|
|
writeLine(fileDescriptor, ' void %sChanged(%s %s);' % (propertyName, propertyTyp, propertyName))
|
|
writeLine(fileDescriptor, ' void %sReadFinished(%s %s);' % (propertyName, propertyTyp, propertyName))
|
|
|
|
|
|
def writeProtectedPropertyMembers(fileDescriptor, registerDefinitions):
|
|
for registerDefinition in registerDefinitions:
|
|
if 'access' in registerDefinition:
|
|
if not 'R' in registerDefinition['access']:
|
|
continue
|
|
|
|
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 writePropertyProcessMethodDeclaration(fileDescriptor, registerDefinitions):
|
|
for registerDefinition in registerDefinitions:
|
|
if 'access' in registerDefinition:
|
|
if not 'R' in registerDefinition['access']:
|
|
continue
|
|
|
|
propertyName = registerDefinition['id']
|
|
writeLine(fileDescriptor, ' void process%sRegisterValues(const QVector<quint16> &values);' % (propertyName[0].upper() + propertyName[1:]))
|
|
|
|
writeLine(fileDescriptor)
|
|
|
|
|
|
def writePropertyProcessMethodImplementations(fileDescriptor, className, registerDefinitions):
|
|
for registerDefinition in registerDefinitions:
|
|
if 'access' in registerDefinition:
|
|
if not 'R' in registerDefinition['access']:
|
|
continue
|
|
|
|
propertyTyp = getCppDataType(registerDefinition)
|
|
propertyName = registerDefinition['id']
|
|
|
|
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']))
|
|
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:]))
|
|
writeLine(fileDescriptor)
|
|
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, ' } 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, ' }')
|
|
writeLine(fileDescriptor, '}')
|
|
writeLine(fileDescriptor)
|
|
|
|
##############################################################
|
|
|
|
def writeBlockPropertiesProcessMethodDeclaration(fileDescriptor, blockDefinitions):
|
|
|
|
for blockDefinition in blockDefinitions:
|
|
blockName = blockDefinition['id']
|
|
blockRegisters = blockDefinition['registers']
|
|
blockStartAddress = 0
|
|
blockSize = 0
|
|
registerCount = 0
|
|
|
|
writeLine(fileDescriptor, ' /* Process block data from start address %s with size of %s registers containing following %s properties:' % (blockStartAddress, blockSize, registerCount))
|
|
for i, registerDefinition in enumerate(blockRegisters):
|
|
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, ' */' )
|
|
writeLine(fileDescriptor, ' void processBlock%sRegisterValues(const QVector<quint16> &blockValues);' % (blockName[0].upper() + blockName[1:]))
|
|
writeLine(fileDescriptor)
|
|
|
|
|
|
def writeBlockPropertiesProcessMethodImplementations(fileDescriptor, className, blockDefinitions):
|
|
for blockDefinition in blockDefinitions:
|
|
blockName = blockDefinition['id']
|
|
blockRegisters = blockDefinition['registers']
|
|
blockStartAddress = 0
|
|
blockSize = 0
|
|
registerCount = 0
|
|
|
|
for i, blockRegister in enumerate(blockRegisters):
|
|
if i == 0:
|
|
blockStartAddress = blockRegister['address']
|
|
|
|
registerCount += 1
|
|
blockSize += blockRegister['size']
|
|
|
|
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))
|
|
writeLine(fileDescriptor, ' if (blockValues.size() == %s) {' % (blockSize))
|
|
|
|
# Start parsing the registers using offsets
|
|
offset = 0
|
|
for i, blockRegister in enumerate(blockRegisters):
|
|
propertyName = blockRegister['id']
|
|
writeLine(fileDescriptor, ' process%sRegisterValues(blockValues.mid(%s, %s));' % (propertyName[0].upper() + propertyName[1:], offset, blockRegister['size']))
|
|
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, ' }')
|
|
writeLine(fileDescriptor, '}')
|
|
writeLine(fileDescriptor)
|
|
|
|
##############################################################
|
|
|
|
def writeSendNextQueuedInitRequestMethodImplementation(fileDescriptor, className):
|
|
writeLine(fileDescriptor, 'void %s::sendNextQueuedInitRequest()' % (className))
|
|
writeLine(fileDescriptor, '{')
|
|
writeLine(fileDescriptor, ' if (m_initRequestQueue.isEmpty())')
|
|
writeLine(fileDescriptor, ' return;')
|
|
writeLine(fileDescriptor)
|
|
writeLine(fileDescriptor, ' if (m_currentInitReply)')
|
|
writeLine(fileDescriptor, ' return;')
|
|
writeLine(fileDescriptor)
|
|
writeLine(fileDescriptor, ' %s::Function function = m_initRequestQueue.dequeue();' % (className))
|
|
writeLine(fileDescriptor, ' (this->*function)();')
|
|
writeLine(fileDescriptor, '}')
|
|
writeLine(fileDescriptor)
|
|
|
|
|
|
def writeEnqueueInitRequestMethodImplementation(fileDescriptor, className):
|
|
writeLine(fileDescriptor, 'void %s::enqueueInitRequest(%s::Function function)' % (className, className))
|
|
writeLine(fileDescriptor, '{')
|
|
writeLine(fileDescriptor, ' if (m_initRequestQueue.contains(function))')
|
|
writeLine(fileDescriptor, ' return;')
|
|
writeLine(fileDescriptor)
|
|
writeLine(fileDescriptor, ' m_initRequestQueue.enqueue(function);')
|
|
writeLine(fileDescriptor, '}')
|
|
writeLine(fileDescriptor)
|
|
|
|
|
|
def writeSendNextQueuedRequestMethodImplementation(fileDescriptor, className):
|
|
writeLine(fileDescriptor, 'void %s::sendNextQueuedRequest()' % (className))
|
|
writeLine(fileDescriptor, '{')
|
|
writeLine(fileDescriptor, ' if (m_updateRequestQueue.isEmpty()) {')
|
|
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "Do not send next request since there are no requests left...";' % className)
|
|
writeLine(fileDescriptor, ' return;')
|
|
writeLine(fileDescriptor, ' }')
|
|
writeLine(fileDescriptor)
|
|
writeLine(fileDescriptor, ' if (m_currentUpdateReply) {')
|
|
writeLine(fileDescriptor, ' qCDebug(dc%s()) << "Do not send next request since there is already a request pending...";' % className)
|
|
writeLine(fileDescriptor, ' return;')
|
|
writeLine(fileDescriptor, ' }')
|
|
writeLine(fileDescriptor)
|
|
writeLine(fileDescriptor, ' %s::Function function = m_updateRequestQueue.dequeue();' % (className))
|
|
writeLine(fileDescriptor, ' (this->*function)();')
|
|
writeLine(fileDescriptor, '}')
|
|
writeLine(fileDescriptor)
|
|
|
|
|
|
def writeEnqueueRequestMethodImplementation(fileDescriptor, className):
|
|
writeLine(fileDescriptor, 'void %s::enqueueRequest(%s::Function function)' % (className, className))
|
|
writeLine(fileDescriptor, '{')
|
|
writeLine(fileDescriptor, ' if (m_updateRequestQueue.contains(function))')
|
|
writeLine(fileDescriptor, ' return;')
|
|
writeLine(fileDescriptor)
|
|
writeLine(fileDescriptor, ' m_updateRequestQueue.enqueue(function);')
|
|
writeLine(fileDescriptor, '}')
|
|
writeLine(fileDescriptor) |