From e95d3f089d971b5fb09ac6b4b96c2a31574f2dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 13 Jan 2022 12:20:05 +0100 Subject: [PATCH 01/10] Add modbus tcp block update implementation --- modbus/tools/generate-connection.py | 73 +++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index 73e9d26..0b81976 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -602,7 +602,77 @@ def writeBlockUpdateMethodImplementationsRtu(fileDescriptor, className, blockDef writeLine(fileDescriptor) +def writeBlockUpdateMethodImplementationsTcp(fileDescriptor, className, blockDefinitions): + for blockDefinition in blockDefinitions: + blockName = blockDefinition['id'] + blockRegisters = blockDefinition['registers'] + blockStartAddress = 0 + registerCount = 0 + blockSize = 0 + registerType = "" + for i, blockRegister in enumerate(blockRegisters): + if i == 0: + blockStartAddress = blockRegister['address'] + registerType = blockRegister['registerType'] + + registerCount += 1 + blockSize += blockRegister['size'] + + writeLine(fileDescriptor, 'void %s::update%sBlock()' % (className, blockName[0].upper() + blockName[1:])) + writeLine(fileDescriptor, '{') + writeLine(fileDescriptor, ' // Update register block \"%s\"' % blockName) + writeLine(fileDescriptor, ' qCDebug(dc%s()) << "--> Read block \\"%s\\" registers from:" << %s << "size:" << %s;' % (className, blockName, blockStartAddress, blockSize)) + + # Build request depending on the register type + # Build request depending on the register type + if registerType == 'inputRegister': + writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::InputRegisters, %s, %s);' % (blockStartAddress, blockSize)) + elif registerType == 'discreteInputs': + writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::DiscreteInputs, %s, %s);' % (blockStartAddress, blockSize)) + elif registerType == 'coils': + writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::Coils, %s, %s);' % (blockStartAddress, blockSize)) + else: + #Default to holdingRegister + writeLine(fileDescriptor, ' QModbusDataUnit request = QModbusDataUnit(QModbusDataUnit::RegisterType::HoldingRegisters, %s, %s);' % (blockStartAddress, blockSize)) + + writeLine(fileDescriptor, ' QModbusReply *reply = sendReadRequest(request, m_slaveId);') + + writeLine(fileDescriptor, ' if (reply) {') + writeLine(fileDescriptor, ' if (!reply->isFinished()) {') + writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, this, [this, reply](){') + writeLine(fileDescriptor, ' if (reply->error() == QModbusReply::NoError) {') + writeLine(fileDescriptor, ' QVector blockValues = reply->result();') + writeLine(fileDescriptor, ' QVector values;') + writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from reading block \\"%s\\" register" << %s << "size:" << %s << blockValues;' % (className, blockName, blockStartAddress, blockSize)) + + # Start parsing the registers using offsets + offset = 0 + for i, blockRegister in enumerate(blockRegisters): + propertyName = blockRegister['id'] + propertyTyp = getCppDataType(blockRegister) + writeLine(fileDescriptor, ' values = blockValues.mid(%s, %s);' % (offset, blockRegister['size'])) + writeLine(fileDescriptor, ' %s received%s = %s;' % (propertyTyp, propertyName[0].upper() + propertyName[1:], getValueConversionMethod(blockRegister))) + 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) + offset += blockRegister['size'] + + writeLine(fileDescriptor, ' }') + writeLine(fileDescriptor, ' });') + writeLine(fileDescriptor) + writeLine(fileDescriptor, ' connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusReply::Error error){') + writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Modbus reply error occurred while updating block \\"%s\\" registers" << error << reply->errorString();' % (className, blockName)) + writeLine(fileDescriptor, ' emit reply->finished();') + writeLine(fileDescriptor, ' });') + writeLine(fileDescriptor, ' }') + writeLine(fileDescriptor, ' } else {') + writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Error occurred while reading block \\"%s\\" registers";' % (className, blockName)) + writeLine(fileDescriptor, ' }') + writeLine(fileDescriptor, '}') + writeLine(fileDescriptor) def writeInternalPropertyReadMethodDeclarationsTcp(fileDescriptor, registerDefinitions): for registerDefinition in registerDefinitions: @@ -885,6 +955,9 @@ def writeTcpSourceFile(): # Write update methods writePropertyUpdateMethodImplementationsTcp(sourceFile, className, registerJson['registers']) + # Write block update method + writeBlockUpdateMethodImplementationsTcp(sourceFile, className, registerJson['blocks']) + # Write property read method implementations writeInternalPropertyReadMethodImplementationsTcp(sourceFile, className, registerJson['registers']) From 7663d176c58b7dd73626a3ccb6f2485d78848376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 13 Jan 2022 12:33:06 +0100 Subject: [PATCH 02/10] Fix tcp block reading --- modbus/tools/generate-connection.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index 0b81976..9f955cd 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -641,8 +641,9 @@ def writeBlockUpdateMethodImplementationsTcp(fileDescriptor, className, blockDef writeLine(fileDescriptor, ' if (reply) {') writeLine(fileDescriptor, ' if (!reply->isFinished()) {') writeLine(fileDescriptor, ' connect(reply, &QModbusReply::finished, this, [this, reply](){') - writeLine(fileDescriptor, ' if (reply->error() == QModbusReply::NoError) {') - writeLine(fileDescriptor, ' QVector blockValues = reply->result();') + writeLine(fileDescriptor, ' if (reply->error() == QModbusDevice::NoError) {') + writeLine(fileDescriptor, ' const QModbusDataUnit unit = reply->result();') + writeLine(fileDescriptor, ' const QVector blockValues = unit.values();') writeLine(fileDescriptor, ' QVector values;') writeLine(fileDescriptor, ' qCDebug(dc%s()) << "<-- Response from reading block \\"%s\\" register" << %s << "size:" << %s << blockValues;' % (className, blockName, blockStartAddress, blockSize)) @@ -663,7 +664,7 @@ def writeBlockUpdateMethodImplementationsTcp(fileDescriptor, className, blockDef writeLine(fileDescriptor, ' }') writeLine(fileDescriptor, ' });') writeLine(fileDescriptor) - writeLine(fileDescriptor, ' connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusReply::Error error){') + writeLine(fileDescriptor, ' connect(reply, &QModbusReply::errorOccurred, this, [reply] (QModbusDevice::Error error){') writeLine(fileDescriptor, ' qCWarning(dc%s()) << "Modbus reply error occurred while updating block \\"%s\\" registers" << error << reply->errorString();' % (className, blockName)) writeLine(fileDescriptor, ' emit reply->finished();') writeLine(fileDescriptor, ' });') From 51024f7bdc2fad64be9dc06b071ce47c4fef8137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Thu, 13 Jan 2022 12:46:16 +0100 Subject: [PATCH 03/10] Add missing block property get methods --- modbus/tools/generate-connection.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index 9f955cd..0cd9e61 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -949,6 +949,10 @@ def writeTcpSourceFile(): # Property get methods writePropertyGetSetMethodImplementationsTcp(sourceFile, className, registerJson['registers']) + # Block property get methods + for blockDefinition in registerJson['blocks']: + writePropertyGetSetMethodImplementationsTcp(sourceFile, className, blockDefinition['registers']) + # Write init and update method implementation writeInitializeMethod(sourceFile, className, registerJson['registers']) writeUpdateMethod(sourceFile, className, registerJson['registers']) From 27553ed64c6da6f1847fad2e7d399613d8048a94 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 27 Jan 2022 17:25:18 +0100 Subject: [PATCH 04/10] Fix wrong return type of generated getter methods --- modbus/tools/generate-connection.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index 0cd9e61..d279b18 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -282,7 +282,7 @@ def getValueConversionMethod(registerDefinition): return ('ModbusDataUtils::convertToString(values)') -def writePropertyGetSetMethodDeclarations(fileDescriptor, registerDefinitions): +def writePropertyGetSetMethodDeclarationsTcp(fileDescriptor, registerDefinitions): for registerDefinition in registerDefinitions: propertyName = registerDefinition['id'] propertyTyp = getCppDataType(registerDefinition) @@ -300,6 +300,24 @@ def writePropertyGetSetMethodDeclarations(fileDescriptor, registerDefinitions): writeLine(fileDescriptor) +def writePropertyGetSetMethodDeclarationsRtu(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)) + + # Check if we require a set method + if registerDefinition['access'] == 'RW' or registerDefinition['access'] == 'WO': + writeLine(fileDescriptor, ' ModbusRtuReply *set%s(%s %s);' % (propertyName[0].upper() + propertyName[1:], propertyTyp, propertyName)) + + writeLine(fileDescriptor) + + def writePropertyGetSetMethodImplementationsTcp(fileDescriptor, className, registerDefinitions): for registerDefinition in registerDefinitions: propertyName = registerDefinition['id'] @@ -1031,7 +1049,7 @@ def writeRtuHeaderFile(): writeLine(headerFile) # Write registers get/set method declarations - writePropertyGetSetMethodDeclarations(headerFile, registerJson['registers']) + writePropertyGetSetMethodDeclarationsRtu(headerFile, registerJson['registers']) # Write block get/set method declarations writeBlocksUpdateMethodDeclarations(headerFile, registerJson['blocks']) From 9fcfa037e356221e8bbd27bb1f939e558ed5a117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 28 Jan 2022 09:58:33 +0100 Subject: [PATCH 05/10] Fix block properties get method declarations --- modbus/tools/generate-connection.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index d279b18..9c4e66f 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -300,6 +300,7 @@ def writePropertyGetSetMethodDeclarationsTcp(fileDescriptor, registerDefinitions writeLine(fileDescriptor) + def writePropertyGetSetMethodDeclarationsRtu(fileDescriptor, registerDefinitions): for registerDefinition in registerDefinitions: propertyName = registerDefinition['id'] @@ -318,6 +319,19 @@ def writePropertyGetSetMethodDeclarationsRtu(fileDescriptor, registerDefinitions writeLine(fileDescriptor) +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 writePropertyGetSetMethodImplementationsTcp(fileDescriptor, className, registerDefinitions): for registerDefinition in registerDefinitions: propertyName = registerDefinition['id'] @@ -442,7 +456,7 @@ def writeBlocksUpdateMethodDeclarations(fileDescriptor, blockDefinitions): blockRegisters = blockDefinition['registers'] # Write the property get / set methods for the block registers - writePropertyGetSetMethodDeclarations(fileDescriptor, blockRegisters) + writeBlockGetMethodDeclarations(fileDescriptor, blockRegisters) blockStartAddress = 0 blockSize = 0 @@ -897,7 +911,7 @@ def writeTcpHeaderFile(): writeLine(headerFile) # Write registers get method declarations - writePropertyGetSetMethodDeclarations(headerFile, registerJson['registers']) + writePropertyGetSetMethodDeclarationsTcp(headerFile, registerJson['registers']) # Write block get/set method declarations writeBlocksUpdateMethodDeclarations(headerFile, registerJson['blocks']) From 1e34b07cf4649ea960b8c3dc4ccca10f0a2483dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 28 Jan 2022 10:14:03 +0100 Subject: [PATCH 06/10] Make internal property read methods protected --- modbus/tools/generate-connection.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index 9c4e66f..81b7c2d 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -933,6 +933,11 @@ def writeTcpHeaderFile(): writePropertyChangedSignals(headerFile, blockDefinition['registers']) writeLine(headerFile) + # Protected members + writeLine(headerFile, 'protected:') + writeInternalPropertyReadMethodDeclarationsTcp(headerFile, registerJson['registers']) + writeLine(headerFile) + # Private members writeLine(headerFile, 'private:') writeLine(headerFile, ' quint16 m_slaveId = 1;') @@ -944,8 +949,7 @@ def writeTcpHeaderFile(): writeLine(headerFile) writeLine(headerFile, ' void verifyInitFinished();') writeLine(headerFile) - writeInternalPropertyReadMethodDeclarationsTcp(headerFile, registerJson['registers']) - writeLine(headerFile) + # End of class writeLine(headerFile) @@ -995,7 +999,7 @@ def writeTcpSourceFile(): # Write block update method writeBlockUpdateMethodImplementationsTcp(sourceFile, className, registerJson['blocks']) - # Write property read method implementations + # Write internal protected property read method implementations writeInternalPropertyReadMethodImplementationsTcp(sourceFile, className, registerJson['registers']) writeLine(sourceFile, 'void %s::verifyInitFinished()' % (className)) @@ -1085,6 +1089,11 @@ def writeRtuHeaderFile(): writePropertyChangedSignals(headerFile, blockDefinition['registers']) writeLine(headerFile) + # Protected members + writeLine(headerFile, 'protected:') + writeInternalPropertyReadMethodDeclarationsRtu(headerFile, registerJson['registers']) + writeLine(headerFile) + # Private members writeLine(headerFile, 'private:') writeLine(headerFile, ' ModbusRtuMaster *m_modbusRtuMaster = nullptr;') @@ -1098,8 +1107,6 @@ def writeRtuHeaderFile(): writeLine(headerFile) writeLine(headerFile, ' void verifyInitFinished();') writeLine(headerFile) - writeInternalPropertyReadMethodDeclarationsRtu(headerFile, registerJson['registers']) - writeLine(headerFile) # End of class writeLine(headerFile) @@ -1161,7 +1168,7 @@ def writeRtuSourceFile(): # Write block update method writeBlockUpdateMethodImplementationsRtu(sourceFile, className, registerJson['blocks']) - # Write property read method implementations + # Write internal protected property read method implementations writeInternalPropertyReadMethodImplementationsRtu(sourceFile, className, registerJson['registers']) writeLine(sourceFile, 'void %s::verifyInitFinished()' % (className)) From 4e9f98ddeefaa1710799a0240c60bc93f19b2797 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 28 Jan 2022 10:19:17 +0100 Subject: [PATCH 07/10] Make block definition optional for register json file --- modbus/tools/generate-connection.py | 59 ++++++++++++++++++----------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index 81b7c2d..37bf520 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -914,7 +914,8 @@ def writeTcpHeaderFile(): writePropertyGetSetMethodDeclarationsTcp(headerFile, registerJson['registers']) # Write block get/set method declarations - writeBlocksUpdateMethodDeclarations(headerFile, registerJson['blocks']) + if 'blocks' in registerJson: + writeBlocksUpdateMethodDeclarations(headerFile, registerJson['blocks']) # Write init and update method declarations writeLine(headerFile, ' virtual void initialize();') @@ -929,8 +930,10 @@ def writeTcpHeaderFile(): writeLine(headerFile, ' void initializationFinished();') writeLine(headerFile) writePropertyChangedSignals(headerFile, registerJson['registers']) - for blockDefinition in registerJson['blocks']: - writePropertyChangedSignals(headerFile, blockDefinition['registers']) + if 'blocks' in registerJson: + for blockDefinition in registerJson['blocks']: + writePropertyChangedSignals(headerFile, blockDefinition['registers']) + writeLine(headerFile) # Protected members @@ -944,8 +947,10 @@ def writeTcpHeaderFile(): writeLine(headerFile, ' QVector m_pendingInitReplies;') writeLine(headerFile) writePrivatePropertyMembers(headerFile, registerJson['registers']) - for blockDefinition in registerJson['blocks']: - writePrivatePropertyMembers(headerFile, blockDefinition['registers']) + if 'blocks' in registerJson: + for blockDefinition in registerJson['blocks']: + writePrivatePropertyMembers(headerFile, blockDefinition['registers']) + writeLine(headerFile) writeLine(headerFile, ' void verifyInitFinished();') writeLine(headerFile) @@ -986,8 +991,9 @@ def writeTcpSourceFile(): writePropertyGetSetMethodImplementationsTcp(sourceFile, className, registerJson['registers']) # Block property get methods - for blockDefinition in registerJson['blocks']: - writePropertyGetSetMethodImplementationsTcp(sourceFile, className, blockDefinition['registers']) + if 'blocks' in registerJson: + for blockDefinition in registerJson['blocks']: + writePropertyGetSetMethodImplementationsTcp(sourceFile, className, blockDefinition['registers']) # Write init and update method implementation writeInitializeMethod(sourceFile, className, registerJson['registers']) @@ -996,8 +1002,9 @@ def writeTcpSourceFile(): # Write update methods writePropertyUpdateMethodImplementationsTcp(sourceFile, className, registerJson['registers']) - # Write block update method - writeBlockUpdateMethodImplementationsTcp(sourceFile, className, registerJson['blocks']) + # Write block update method + if 'blocks' in registerJson: + writeBlockUpdateMethodImplementationsTcp(sourceFile, className, registerJson['blocks']) # Write internal protected property read method implementations writeInternalPropertyReadMethodImplementationsTcp(sourceFile, className, registerJson['registers']) @@ -1018,8 +1025,9 @@ def writeTcpSourceFile(): writeLine(sourceFile, ' debug.nospace().noquote() << "%s(" << %s->hostAddress().toString() << ":" << %s->port() << ")" << "\\n";' % (className, debugObjectParamName, debugObjectParamName)) writeRegistersDebugLine(sourceFile, debugObjectParamName, registerJson['registers']) - for blockDefinition in registerJson['blocks']: - writeRegistersDebugLine(sourceFile, debugObjectParamName, blockDefinition['registers']) + if 'blocks' in registerJson: + for blockDefinition in registerJson['blocks']: + writeRegistersDebugLine(sourceFile, debugObjectParamName, blockDefinition['registers']) writeLine(sourceFile, ' return debug.quote().space();') writeLine(sourceFile, '}') @@ -1070,7 +1078,8 @@ def writeRtuHeaderFile(): writePropertyGetSetMethodDeclarationsRtu(headerFile, registerJson['registers']) # Write block get/set method declarations - writeBlocksUpdateMethodDeclarations(headerFile, registerJson['blocks']) + if 'blocks' in registerJson: + writeBlocksUpdateMethodDeclarations(headerFile, registerJson['blocks']) writePropertyUpdateMethodDeclarations(headerFile, registerJson['registers']) writeLine(headerFile) @@ -1085,8 +1094,9 @@ def writeRtuHeaderFile(): writeLine(headerFile, ' void initializationFinished();') writeLine(headerFile) writePropertyChangedSignals(headerFile, registerJson['registers']) - for blockDefinition in registerJson['blocks']: - writePropertyChangedSignals(headerFile, blockDefinition['registers']) + if 'blocks' in registerJson: + for blockDefinition in registerJson['blocks']: + writePropertyChangedSignals(headerFile, blockDefinition['registers']) writeLine(headerFile) # Protected members @@ -1101,8 +1111,9 @@ def writeRtuHeaderFile(): writeLine(headerFile, ' QVector m_pendingInitReplies;') writeLine(headerFile) writePrivatePropertyMembers(headerFile, registerJson['registers']) - for blockDefinition in registerJson['blocks']: - writePrivatePropertyMembers(headerFile, blockDefinition['registers']) + if 'blocks' in registerJson: + for blockDefinition in registerJson['blocks']: + writePrivatePropertyMembers(headerFile, blockDefinition['registers']) writeLine(headerFile) writeLine(headerFile, ' void verifyInitFinished();') @@ -1155,8 +1166,9 @@ def writeRtuSourceFile(): writePropertyGetSetMethodImplementationsRtu(sourceFile, className, registerJson['registers']) # Block property get methods - for blockDefinition in registerJson['blocks']: - writePropertyGetSetMethodImplementationsRtu(sourceFile, className, blockDefinition['registers']) + if 'blocks' in registerJson: + for blockDefinition in registerJson['blocks']: + writePropertyGetSetMethodImplementationsRtu(sourceFile, className, blockDefinition['registers']) # Write init and update method implementation writeInitializeMethod(sourceFile, className, registerJson['registers']) @@ -1166,7 +1178,8 @@ def writeRtuSourceFile(): writePropertyUpdateMethodImplementationsRtu(sourceFile, className, registerJson['registers']) # Write block update method - writeBlockUpdateMethodImplementationsRtu(sourceFile, className, registerJson['blocks']) + if 'blocks' in registerJson: + writeBlockUpdateMethodImplementationsRtu(sourceFile, className, registerJson['blocks']) # Write internal protected property read method implementations writeInternalPropertyReadMethodImplementationsRtu(sourceFile, className, registerJson['registers']) @@ -1187,8 +1200,9 @@ def writeRtuSourceFile(): writeLine(sourceFile, ' debug.nospace().noquote() << "%s(" << %s->modbusRtuMaster()->modbusUuid().toString() << ", " << %s->modbusRtuMaster()->serialPort() << ", slave ID:" << %s->slaveId() << ")" << "\\n";' % (className, debugObjectParamName, debugObjectParamName, debugObjectParamName)) writeRegistersDebugLine(sourceFile, debugObjectParamName, registerJson['registers']) - for blockDefinition in registerJson['blocks']: - writeRegistersDebugLine(sourceFile, debugObjectParamName, blockDefinition['registers']) + if 'blocks' in registerJson: + for blockDefinition in registerJson['blocks']: + writeRegistersDebugLine(sourceFile, debugObjectParamName, blockDefinition['registers']) writeLine(sourceFile, ' return debug.quote().space();') writeLine(sourceFile, '}') @@ -1234,7 +1248,8 @@ protocol = 'TCP' if 'protocol' in registerJson: protocol = registerJson['protocol'] -validateBlocks(registerJson['blocks']) +if 'blocks' in registerJson: + validateBlocks(registerJson['blocks']) if protocol == 'TCP': writeTcpHeaderFile() From d22b5b6e919af744d0daf79d5ec26f347886f90c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 28 Jan 2022 12:05:10 +0100 Subject: [PATCH 08/10] Add registers enum for convinience --- modbus/tools/generate-connection.py | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index 37bf520..dda0863 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -111,6 +111,54 @@ def writeLicenseHeader(fileDescriptor): writeLine(fileDescriptor) +def writeRegistersEnum(fileDescriptor, registerJson): + print('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 = [] + + print('Sorted registers') + for registerAddress in sortedRegistersKeys: + print('--> %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): print('Writing enum', enumDefinition) enumName = enumDefinition['name'] @@ -900,6 +948,9 @@ def writeTcpHeaderFile(): # Public members writeLine(headerFile, 'public:') + # Write enum for all register values + writeRegistersEnum(headerFile, registerJson) + # Enum declarations if 'enums' in registerJson: for enumDefinition in registerJson['enums']: @@ -1060,6 +1111,9 @@ def writeRtuHeaderFile(): # Public members writeLine(headerFile, 'public:') + # Write enum for all register values + writeRegistersEnum(headerFile, registerJson) + # Enum declarations if 'enums' in registerJson: for enumDefinition in registerJson['enums']: From 00915e3dc17db0246c2af138ebf199762cf00a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Fri, 28 Jan 2022 12:18:16 +0100 Subject: [PATCH 09/10] Make properties protected instead of private --- modbus/tools/generate-connection.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index dda0863..6191388 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -991,22 +991,21 @@ def writeTcpHeaderFile(): writeLine(headerFile, 'protected:') writeInternalPropertyReadMethodDeclarationsTcp(headerFile, registerJson['registers']) writeLine(headerFile) - - # Private members - writeLine(headerFile, 'private:') - writeLine(headerFile, ' quint16 m_slaveId = 1;') - writeLine(headerFile, ' QVector m_pendingInitReplies;') - writeLine(headerFile) writePrivatePropertyMembers(headerFile, registerJson['registers']) if 'blocks' in registerJson: for blockDefinition in registerJson['blocks']: writePrivatePropertyMembers(headerFile, blockDefinition['registers']) writeLine(headerFile) + + # Private members + writeLine(headerFile, 'private:') + writeLine(headerFile, ' quint16 m_slaveId = 1;') + writeLine(headerFile, ' QVector m_pendingInitReplies;') + writeLine(headerFile) writeLine(headerFile, ' void verifyInitFinished();') writeLine(headerFile) - # End of class writeLine(headerFile) writeLine(headerFile, '};') @@ -1157,18 +1156,18 @@ def writeRtuHeaderFile(): writeLine(headerFile, 'protected:') writeInternalPropertyReadMethodDeclarationsRtu(headerFile, registerJson['registers']) writeLine(headerFile) + writePrivatePropertyMembers(headerFile, registerJson['registers']) + if 'blocks' in registerJson: + for blockDefinition in registerJson['blocks']: + writePrivatePropertyMembers(headerFile, blockDefinition['registers']) + + writeLine(headerFile) # Private members writeLine(headerFile, 'private:') writeLine(headerFile, ' ModbusRtuMaster *m_modbusRtuMaster = nullptr;') writeLine(headerFile, ' quint16 m_slaveId = 1;') writeLine(headerFile, ' QVector m_pendingInitReplies;') - writeLine(headerFile) - writePrivatePropertyMembers(headerFile, registerJson['registers']) - if 'blocks' in registerJson: - for blockDefinition in registerJson['blocks']: - writePrivatePropertyMembers(headerFile, blockDefinition['registers']) - writeLine(headerFile) writeLine(headerFile, ' void verifyInitFinished();') writeLine(headerFile) From b8994ca5f5dfc790cc9061fba85487a547bbf564 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=BCrz?= Date: Tue, 22 Feb 2022 17:35:16 +0100 Subject: [PATCH 10/10] Fix init method writing for TCP and warn about missing RTU feature for now --- modbus/tools/generate-connection.py | 91 +++++++++++++++-------------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/modbus/tools/generate-connection.py b/modbus/tools/generate-connection.py index 6191388..39c0969 100644 --- a/modbus/tools/generate-connection.py +++ b/modbus/tools/generate-connection.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright (C) 2021 nymea GmbH +# Copyright (C) 2021 - 2022 nymea GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License @@ -843,51 +843,56 @@ def writeInitializeMethod(fileDescriptor, className, registerDefinitions): break if initRequired: - # FIXME: distinguish between RTU and TCP - 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, ' }') + if protocol == 'TCP': + # Init implementation for TCP + 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, ' }') - for registerDefinition in registerDefinitions: - propertyName = registerDefinition['id'] - propertyTyp = getCppDataType(registerDefinition) + for registerDefinition in registerDefinitions: + propertyName = registerDefinition['id'] + propertyTyp = getCppDataType(registerDefinition) - if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init': - writeLine(fileDescriptor) - 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, ' }') + if 'readSchedule' in registerDefinition and registerDefinition['readSchedule'] == 'init': + writeLine(fileDescriptor) + 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, ' const QVector values = unit.values();') + 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, ' }') + else: + print('TODO: this has not been implemented yet for RTU') + exit(1) else: writeLine(fileDescriptor, ' // No init registers defined. Nothing to be done and we are finished.')