def encode_field(self, value, mb_type='unit16'): builder = BinaryPayloadBuilder(endian=self.endian) if mb_type == 'bit' or mb_type == 'bits': builder.add_bits(value) elif mb_type == 'uint8': builder.add_8bit_uint(value) elif mb_type == 'uint16': builder.add_16bit_uint(value) elif mb_type == 'uint32': builder.add_32bit_uint(value) elif mb_type == 'uint64': builder.add_64bit_uint(value) elif mb_type == 'int8': builder.add_8bit_int(value) elif mb_type == 'int16': builder.add_16bit_int(value) elif mb_type == 'int32': builder.add_32bit_int(value) elif mb_type == 'int64': builder.add_64bit_int(value) elif mb_type == 'float32': builder.add_32bit_float(value) elif mb_type == 'float64': builder.add_64bit_float(value) elif mb_type == 'string' or mb_type == 'str': builder.add_string(value) else: log.warn('Not supported DataType: "%s"' % mb_type) return builder.build()
def set_value(self, value): if value is not None: builder = BinaryPayloadBuilder(byteorder=self.byteorder, wordorder=self.wordorder) if self.encoding in ['int8', 'uint8']: builder.add_8bit_int( value ) if self.encoding == 'int8' else builder.add_8bit_uint( value) elif self.encoding in ['int16', 'uint16']: builder.add_16bit_int( value ) if self.encoding == 'int16' else builder.add_16bit_uint( value) elif self.encoding in ['int32', 'uint32']: builder.add_32bit_int( value ) if self.encoding == 'int32' else builder.add_32bit_uint( value) elif self.encoding in ['float32', 'float64']: builder.add_32bit_float( value ) if self.encoding == 'float32' else builder.add_64bit_float( value) elif self.encoding in ['int64', 'uint64']: builder.add_64bit_int( value ) if self.encoding == 'int64' else builder.add_64bit_uint( value) elif self.encoding == 'boolean': builder.add_16bit_uint(value) elif self.encoding == 'string': builder.add_string(value) else: log.error("Unhandled encoding exception {enc}".format( enc=self.encoding)) payload = builder.to_registers() log.info( "Setting {type} {addr} to {enc} {val} as {list}".format( type=self.type, addr=self.address, enc=self.encoding, val=value, list=str(payload))) self.parent.context.setValues(self.get_function_code(), self.address, payload) self.value = value else: log.warning( "Attempt to set {type} {addr} to None (default={default})". format(type=self.type, addr=self.address, default=self.default))
def make_block(section, test_val, test_params): #print("Format", section) builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big) # TYPES TO BE ADDED value = 0 data_size = 0 for s in section: field_name = test_params["field_name"].format(source=s[4], block=s[5], field=s[6] ) #print(field_name, test_val[field_name], s) print("Add: ", data_size, s, end="") value = test_val[field_name] #value += 1 if s[0] == "float16": builder.add_16bit_uint(round(value) ) data_size +=1 elif s[0] == "uint16": #print(value, float(value), s[2] , s[3] ) #print("values:", s, value) val = int( ( float(value) - float(s[3])) / float(s[2]) ) #print("values:", s, value, val) builder.add_16bit_uint(val) data_size+=1 elif s[0] == "uint32": val = int(float(value) / float(s[2]) - float(s[3]) ) builder.add_32bit_uint(val) data_size += 2 elif s[0] in ["int32", "sint32"]: val = int(float(value) / float(s[2]) - float(s[3]) ) builder.add_32bit_int(val) data_size +=2 elif s[0] == "float32": val = float(value) / float(s[2]) - float(s[3]) builder.add_32bit_float(val) data_size +=2 elif s[0] == "bit16": builder.add_bits([0, 0, 0, 0, 0, 0, 0, 0] ) builder.add_bits([0, 0, 0, 0, 0, 0, 0, 0] ) val = 0 data_size += 1 elif s[0][:3] == "str": data_length = int(s[1]) val = " " * (data_length - len(str(value))) + str(value) builder.add_string(val) data_size += data_length/2 else: print(" ------ ", s) print(val) #value += 1 block = ModbusSequentialDataBlock(1, builder.to_registers()) return block
def run_payload_server(): # ----------------------------------------------------------------------- # # build your payload # ----------------------------------------------------------------------- # builder = BinaryPayloadBuilder(byteorder=Endian.Little, wordorder=Endian.Little) builder.add_string('abcdefgh') builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0]) builder.add_8bit_int(-0x12) builder.add_8bit_uint(0x12) builder.add_16bit_int(-0x5678) builder.add_16bit_uint(0x1234) builder.add_32bit_int(-0x1234) builder.add_32bit_uint(0x12345678) builder.add_16bit_float(12.34) builder.add_16bit_float(-12.34) builder.add_32bit_float(22.34) builder.add_32bit_float(-22.34) builder.add_64bit_int(-0xDEADBEEF) builder.add_64bit_uint(0x12345678DEADBEEF) builder.add_64bit_uint(0xDEADBEEFDEADBEED) builder.add_64bit_float(123.45) builder.add_64bit_float(-123.45) # ----------------------------------------------------------------------- # # use that payload in the data store # ----------------------------------------------------------------------- # # Here we use the same reference block for each underlying store. # ----------------------------------------------------------------------- # block = ModbusSequentialDataBlock(1, builder.to_registers()) store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block) context = ModbusServerContext(slaves=store, single=True) # ----------------------------------------------------------------------- # # initialize the server information # ----------------------------------------------------------------------- # # If you don't set this or any fields, they are defaulted to empty strings. # ----------------------------------------------------------------------- # identity = ModbusDeviceIdentification() identity.VendorName = 'Pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/riptideio/pymodbus/' identity.ProductName = 'Pymodbus Server' identity.ModelName = 'Pymodbus Server' identity.MajorMinorRevision = version.short() # ----------------------------------------------------------------------- # # run the server you want # ----------------------------------------------------------------------- # StartTcpServer(context, identity=identity, address=("localhost", 5020))
def _write(self, value): builder = BinaryPayloadBuilder(byteorder='>', wordorder='<') builder.add_16bit_uint(value) payload = builder.to_registers() try: with ModbusTcpClient(self.host, self.port) as client: result = client.write_registers(self.COMMAND_ADDRESS, payload) except ConnectionException as e: raise ttypes.LesediException(str(e)) if result.isError(): raise ttypes.LesediException(str(result))
def run_payload_server(): # ----------------------------------------------------------------------- # # build your payload # ----------------------------------------------------------------------- # builder = BinaryPayloadBuilder(byteorder=Endian.Little, wordorder=Endian.Little) builder.add_string('abcdefgh') builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0]) builder.add_8bit_int(-0x12) builder.add_8bit_uint(0x12) builder.add_16bit_int(-0x5678) builder.add_16bit_uint(0x1234) builder.add_32bit_int(-0x1234) builder.add_32bit_uint(0x12345678) builder.add_32bit_float(22.34) builder.add_32bit_float(-22.34) builder.add_64bit_int(-0xDEADBEEF) builder.add_64bit_uint(0x12345678DEADBEEF) builder.add_64bit_uint(0xDEADBEEFDEADBEED) builder.add_64bit_float(123.45) builder.add_64bit_float(-123.45) # ----------------------------------------------------------------------- # # use that payload in the data store # ----------------------------------------------------------------------- # # Here we use the same reference block for each underlying store. # ----------------------------------------------------------------------- # block = ModbusSequentialDataBlock(1, builder.to_registers()) store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block) context = ModbusServerContext(slaves=store, single=True) # ----------------------------------------------------------------------- # # initialize the server information # ----------------------------------------------------------------------- # # If you don't set this or any fields, they are defaulted to empty strings. # ----------------------------------------------------------------------- # identity = ModbusDeviceIdentification() identity.VendorName = 'Pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/bashwork/pymodbus/' identity.ProductName = 'Pymodbus Server' identity.ModelName = 'Pymodbus Server' identity.MajorMinorRevision = '1.5' # ----------------------------------------------------------------------- # # run the server you want # ----------------------------------------------------------------------- # StartTcpServer(context, identity=identity, address=("localhost", 5020))
def push_buffer(self, buff): builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) [builder.add_8bit_uint(i) for i in buff] builder.add_16bit_uint(self.calc_crc(buff)) payload = builder.build() rq = self.client.write_registers(self.MB_STREAM_REG, payload, skip_encode=True, unit=self.id) #logging.info(f"Push {binascii.hexlify(buff)}") # logging.info(rq.isError()) if not rq.isError(): return True else: logging.warning(f"Can't write stream data") return False
def testBigEndianPayloadBuilder(self): ''' Test basic bit message encoding/decoding ''' builder = BinaryPayloadBuilder(endian=Endian.Big) builder.add_8bit_uint(1) builder.add_16bit_uint(2) builder.add_32bit_uint(3) builder.add_64bit_uint(4) builder.add_8bit_int(-1) builder.add_16bit_int(-2) builder.add_32bit_int(-3) builder.add_64bit_int(-4) builder.add_32bit_float(1.25) builder.add_64bit_float(6.25) builder.add_string(b'test') builder.add_bits(self.bitstring) self.assertEqual(self.big_endian_payload, builder.to_string())
def testLittleEndianPayloadBuilder(self): ''' Test basic bit message encoding/decoding ''' builder = BinaryPayloadBuilder(endian=Endian.Little) builder.add_8bit_uint(1) builder.add_16bit_uint(2) builder.add_32bit_uint(3) builder.add_64bit_uint(4) builder.add_8bit_int(-1) builder.add_16bit_int(-2) builder.add_32bit_int(-3) builder.add_64bit_int(-4) builder.add_32bit_float(1.25) builder.add_64bit_float(6.25) builder.add_string('test') builder.add_bits(self.bitstring) self.assertEqual(self.little_endian_payload, str(builder))
def write_small_buff(self, buff): if len(buff) == self.mb_transfer_size: self.reset_state() # reset error state builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) [builder.add_16bit_uint(i) for i in buff] builder.add_16bit_uint(self.calc_crc(buff)) payload = builder.build() #print(payload) self.client.write_registers(self.MB_START_STREAM, payload, skip_encode=True, unit=self.id) if self.update_state() == 0: #check errors self.client.write_register(self.MB_ACTION_REG, self.MB_ACTION_WRITE, unit=self.id) if self.update_state() == 0: return True return False
def write_register(self,register_name,value, unit=None): """ :param register_name: register key from holding register dictionary generated by yaml config :param value: value to write to register :returns: -- Nothing """ # TODO add the ability to discern which settings will be appropriate for # the device that is being written to if (unit is None): unit = self.UNIT_ID ''' builder = BinaryPayloadBuilder(byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER_DICT[unit]) ''' builder = BinaryPayloadBuilder(byteorder=self.BYTE_ORDER_DICT[unit], wordorder=self.WORD_ORDER_DICT[unit]) # This will change depending on the device that is being connected # potentially so it has to be correleated to the device ID if (self.holding_register_dict[register_name][1] == '8int'): builder.add_8bit_int(value) elif (self.holding_register_dict[register_name][1] == '8uint'): builder.add_8bit_uint(value) elif (self.holding_register_dict[register_name][1] == '16int'): builder.add_16bit_int(value) elif (self.holding_register_dict[register_name][1] == '16uint'): builder.add_16bit_uint(value) elif (self.holding_register_dict[register_name][1] == '32int'): builder.add_32bit_int(value) elif (self.holding_register_dict[register_name][1] == '32uint'): builder.add_32bit_uint(value) elif (self.holding_register_dict[register_name][1] == '32float'): builder.add_32bit_float(value) elif (self.holding_register_dict[register_name][1] == '64int'): builder.add_64bit_int(value) elif (self.holding_register_dict[register_name][1] == '64uint'): builder.add_64bit_uint(value) elif (self.holding_register_dict[register_name][1] == '64float'): builder.add_64bit_float(value) else: print("Bad type") exit() payload = builder.build() self.client.write_registers(self.holding_register_dict[register_name][0], payload, skip_encode=True, unit = self.UNIT_ID)
def testLittleEndianPayloadBuilder(self): """ Test basic bit message encoding/decoding """ builder = BinaryPayloadBuilder(byteorder=Endian.Little, wordorder=Endian.Little) builder.add_8bit_uint(1) builder.add_16bit_uint(2) builder.add_32bit_uint(3) builder.add_64bit_uint(4) builder.add_8bit_int(-1) builder.add_16bit_int(-2) builder.add_32bit_int(-3) builder.add_64bit_int(-4) builder.add_32bit_float(1.25) builder.add_64bit_float(6.25) builder.add_16bit_uint(1) # placeholder builder.add_string(b'test') builder.add_bits(self.bitstring) self.assertEqual(self.little_endian_payload, builder.to_string())
def setUp(self): ''' Initializes the test environment and builds request/result encoding pairs ''' self.value = 0xabcd self.values = [0xa, 0xb, 0xc] builder = BinaryPayloadBuilder(endian=Endian.Big) builder.add_16bit_uint(0x1234) self.payload = builder.build() self.write = { WriteSingleRegisterRequest(1, self.value) : b'\x00\x01\xab\xcd', WriteSingleRegisterResponse(1, self.value) : b'\x00\x01\xab\xcd', WriteMultipleRegistersRequest(1, self.values) : b'\x00\x01\x00\x03\x06\x00\n\x00\x0b\x00\x0c', WriteMultipleRegistersResponse(1, 5) : b'\x00\x01\x00\x05', WriteSingleRegisterRequest(1, self.payload[0], skip_encode=True): b'\x00\x01\x12\x34', WriteMultipleRegistersRequest(1, self.payload, skip_encode=True): b'\x00\x01\x00\x01\x02\x12\x34', }
def setUp(self): ''' Initializes the test environment and builds request/result encoding pairs ''' self.value = 0xabcd self.values = [0xa, 0xb, 0xc] builder = BinaryPayloadBuilder(byteorder=Endian.Big) builder.add_16bit_uint(0x1234) self.payload = builder.build() self.write = { WriteSingleRegisterRequest(1, self.value) : b'\x00\x01\xab\xcd', WriteSingleRegisterResponse(1, self.value) : b'\x00\x01\xab\xcd', WriteMultipleRegistersRequest(1, self.values) : b'\x00\x01\x00\x03\x06\x00\n\x00\x0b\x00\x0c', WriteMultipleRegistersResponse(1, 5) : b'\x00\x01\x00\x05', WriteSingleRegisterRequest(1, self.payload[0], skip_encode=True): b'\x00\x01\x12\x34', WriteMultipleRegistersRequest(1, self.payload, skip_encode=True): b'\x00\x01\x00\x01\x02\x12\x34', }
class PayloadHandler: """ encodes/decodes values according to the way it is stored in registry SCALE stands for multiplying/dividing by a scaling factor COMB stands for storing the value of one field in two registers if none of those provided encodes only based on the type """ def __init__(self, env, store): self.byte_order = env["byte_order"] self.word_order = env["word_order"] self.d_s_factor = env["default_scaling_factor"] self.battery_store = store self.builder = BinaryPayloadBuilder(byteorder=self.byte_order, wordorder=self.word_order) def encode(self, value, encoding): self.builder.reset() encode_type = { INT8: lambda x: self.builder.add_8bit_int(x), UINT8: lambda x: self.builder.add_8bit_uint(x), INT16: lambda x: self.builder.add_16bit_int(x), UINT16: lambda x: self.builder.add_16bit_uint(x), INT32: lambda x: self.builder.add_32bit_int(x), UINT32: lambda x: self.builder.add_32bit_uint(x), FLOAT32: lambda x: self.builder.add_32bit_float(x), } if 'e_type' not in encoding or encoding['e_type'] == COMB: encode_type[encoding['d_type']](value) else: encode_type[encoding['d_type']](round( value * encoding.get('s_factor', self.d_s_factor))) return self.builder.to_registers() def decode(self, fx, addr, encoding): encoded_value = self.battery_store.getValues(fx, addr, 2) decoder = BinaryPayloadDecoder.fromRegisters(encoded_value, byteorder=self.byte_order, wordorder=self.word_order) decode_type = { INT8: lambda: decoder.decode_8bit_int(), UINT8: lambda: decoder.decode_8bit_uint(), INT16: lambda: decoder.decode_16bit_int(), UINT16: lambda: decoder.decode_16bit_uint(), INT32: lambda: decoder.decode_32bit_int(), UINT32: lambda: decoder.decode_32bit_uint(), FLOAT32: lambda: decoder.decode_32bit_float(), } if 'e_type' not in encoding or encoding['e_type'] == COMB: return decode_type[encoding['d_type']]() else: return decode_type[encoding['d_type']]() / encoding.get( 's_factor', self.d_s_factor)
def run(self) -> None: builder = BinaryPayloadBuilder(byteorder=self.endian, wordorder=self.endian) builder.add_32bit_uint(42) builder.add_16bit_uint(12) builder.add_32bit_int(64) builder.add_16bit_int(128) builder.add_32bit_float(256) store = ModbusSlaveContext( di=ModbusSequentialDataBlock(18476, builder.to_registers()), co=ModbusSequentialDataBlock(18476, builder.to_registers()), hr=ModbusSequentialDataBlock(18476, builder.to_registers()), ir=ModbusSequentialDataBlock(18476, builder.to_registers()), zero_mode=True ) slaves = { 0x01: store, 0x02: store, 0x03: store, 0x04: store, } # context = ModbusServerContext(slaves=store, single=True) context = ModbusServerContext(slaves=slaves, single=False) identity = ModbusDeviceIdentification() identity.VendorName = 'Pymodbus' identity.ProductCode = 'PM' identity.VendorUrl = 'http://github.com/riptideio/pymodbus/' identity.ProductName = 'Pymodbus Server' identity.ModelName = 'Pymodbus Server' identity.MajorMinorRevision = '2.3.0' framer = ModbusSocketFramer self.server = ModbusTcpServer(context, framer, identity, address=("127.0.0.1", self.port)) self.server.serve_forever()
def build_args(self, arg, kwargs): builder = BinaryPayloadBuilder( byteorder=Endian.Big) #, wordorder=Endian.Big) if 'arg_type' in kwargs: if kwargs['arg_type'] == 'float': builder.add_32bit_float(arg) elif kwargs['arg_type'] == 'uint64': builder.add_64bit_uint(arg) elif kwargs['arg_type'] == 'int64': builder.add_64bit_int(arg) elif kwargs['arg_type'] == 'uint32': builder.add_32bit_uint(arg) elif kwargs['arg_type'] == 'int32': builder.add_32bit_int(arg) elif kwargs['arg_type'] == 'uint16': builder.add_16bit_uint(arg) elif kwargs['arg_type'] == 'int16': builder.add_16bit_int(arg) elif kwargs['arg_type'] == 'int8': builder.add_8bit_int(arg) elif kwargs['arg_type'] == 'uint8': builder.add_8bit_uint(arg) elif kwargs['arg_type'] == 'string': builder.add_string(arg) else: raise Exception("unknown parameter type given: %s" % (kwargs['arg_type'])) else: builder.add_16bit_uint(arg) val = builder.build() return val
def write_register(self,register_name,value, unit=None): """ :param register_name: register key from holding register dictionary generated by yaml config :param value: value to write to register :returns: -- Nothing """ if (unit is None): unit = self.UNIT_ID builder = BinaryPayloadBuilder(byteorder=self.BYTE_ORDER, wordorder=self.WORD_ORDER) if (self.holding_register_dict[register_name][1] == '8int'): builder.add_8bit_int(value) elif (self.holding_register_dict[register_name][1] == '8uint'): builder.add_8bit_uint(value) elif (self.holding_register_dict[register_name][1] == '16int'): builder.add_16bit_int(value) elif (self.holding_register_dict[register_name][1] == '16uint'): builder.add_16bit_uint(value) elif (self.holding_register_dict[register_name][1] == '32int'): builder.add_32bit_int(value) elif (self.holding_register_dict[register_name][1] == '32uint'): builder.add_32bit_uint(value) elif (self.holding_register_dict[register_name][1] == '32float'): builder.add_32bit_float(value) elif (self.holding_register_dict[register_name][1] == '64int'): builder.add_64bit_int(value) elif (self.holding_register_dict[register_name][1] == '64uint'): builder.add_64bit_uint(value) elif (self.holding_register_dict[register_name][1] == '64float'): builder.add_64bit_float(value) else: print("Bad type") exit() payload = builder.build() self.client.write_registers(self.holding_register_dict[register_name][0], payload, skip_encode=True, unit = self.UNIT_ID)
def write(self): try: lb = 00000000 hb = 00000000 #### byte besteht immer aus 16 bits for byte in self._db['out']: for bit in sorted(self._db['out'][byte]): if bit in self._db['out'][byte]: bitpos = bit[0] #startbit/bitposition des binärwertes type = bit[1] value = bit[2] name = bit[3] bit[2] = bit[3]() ##aktueller wert des items abrufen und value updaten! builder = BinaryPayloadBuilder(endian=Endian.Little) ##unterscheidung dateityp if type == '5' or type == '5.001' or type == '6' : ##8bit uint / int length = 8 if bitpos < 8: #lb lb = value else: #hb hb = value if type == '5': builder.add_8bit_uint(lb) builder.add_8bit_uint(hb) #logger.debug('MODBUS: 8bit uint {0} ; {1}'.format(lb,hb)) elif type == '5.001': ##0-100 in 0-255 umwandeln! #print(dpts.en5001(lb)) #print(dpts.en5001(hb)) lb = self.de5001(lb) hb = self.de5001(hb) #print("lb geschrieben", lb ) #print("hb geschrieben", hb ) builder.add_8bit_uint(lb) builder.add_8bit_uint(hb) #logger.debug('MODBUS: 8bit uint {0} ; {1}'.format(lb,hb)) elif type == '6': if lb > 127: lb = 127 elif lb < -128: lb = -128 if hb > 127: hb = 127 elif hb < -128: hb = -128 builder.add_8bit_int(lb) builder.add_8bit_int(hb) #logger.debug('MODBUS: 8bit int {0} ; {1}'.format(lb.hb)) elif type == '7' or type == '8': #16bit uint / int length = 16 if type == '7': #0...65535 builder.add_16bit_uint(value) #logger.debug('MODBUS: 16bit uint {0} '.format(value)) else: #-32768...32767 builder.add_16bit_int(value) #logger.debug('MODBUS: 16bit int {0}'.format(value)) elif type == '1': length = 1 #nur pro byte einmal die bits wandeln if bitpos < 8: #lb lb = lb | int(value) << bitpos #logger.debug('MODBUS: 8bit int{0}'.format(lb)) else: #hb hb = hb | int(value) << bitpos #logger.debug('MODBUS: 8bit int{0}'.format(hb)) builder.add_8bit_uint(lb) builder.add_8bit_uint(hb) payload = builder.build() logger.debug('MODBUS: write to PLC: WORD {0} set to {1} '.format(byte,payload)) self._modbuspy.write_registers(byte, payload, skip_encode=True) builder.reset() except Exception as e: logger.error('MODBUS: Could not write an OutWord, because {}'.format(e)) self._lock.release() return None
#---------------------------------------------------------------------------# # configure the service logging #---------------------------------------------------------------------------# import logging logging.basicConfig() log = logging.getLogger() log.setLevel(logging.DEBUG) #---------------------------------------------------------------------------# # build your payload #---------------------------------------------------------------------------# builder = BinaryPayloadBuilder(endian=Endian.Little) builder.add_string('abcdefgh') builder.add_32bit_float(22.34) builder.add_16bit_uint(0x1234) builder.add_8bit_int(0x12) builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0]) #---------------------------------------------------------------------------# # use that payload in the data store #---------------------------------------------------------------------------# # Here we use the same reference block for each underlying store. #---------------------------------------------------------------------------# block = ModbusSequentialDataBlock(1, builder.to_registers()) store = ModbusSlaveContext(di=block, co=block, hr=block, ir=block) context = ModbusServerContext(slaves=store, single=True) #---------------------------------------------------------------------------# # initialize the server information
def __write_Registers(self, regPara, value): objectType = regPara['objectType'] address = regPara['regAddr'] slaveUnit = regPara['slaveUnit'] bo = regPara['byteOrder'] wo = regPara['wordOrder'] dataTypeStr = regPara['dataType'] dataType = ''.join( filter(str.isalpha, dataTypeStr )) # vom dataType die Ziffen entfernen z.B. uint16 = uint registerCount = 0 # Anzahl der zu schreibenden Register (Words) try: bits = int(''.join(filter( str.isdigit, dataTypeStr))) # bit-Zahl aus aus dataType z.B. uint16 = 16 except: bits = 16 if dataType.lower() == 'string': registerCount = int( bits / 2 ) # bei string: bits = bytes !! string16 -> 16Byte - 8 registerCount else: registerCount = int(bits / 16) if regPara['factor'] != 1: #self.logger.debug("value {0} divided by: {1}".format(value, regPara['factor'])) value = value * (1 / regPara['factor']) self.logger.debug( "write {0} to {1}.{2}.{3} (address.slaveUnit) dataType:{4}".format( value, objectType, address, slaveUnit, dataTypeStr)) builder = BinaryPayloadBuilder(byteorder=bo, wordorder=wo) if dataType.lower() == 'uint': if bits == 16: builder.add_16bit_uint(int(value)) elif bits == 32: builder.add_32bit_uint(int(value)) elif bits == 64: builder.add_64bit_uint(int(value)) else: self.logger.error( "Number of bits or datatype not supportet : {0}".format( typeStr)) elif dataType.lower() == 'int': if bits == 16: builder.add_16bit_int(int(value)) elif bits == 32: builder.add_32bit_int(int(value)) elif bits == 64: builder.add_64bit_int(int(value)) else: self.logger.error( "Number of bits or datatype not supportet : {0}".format( typeStr)) elif dataType.lower() == 'float': if bits == 32: builder.add_32bit_float(value) if bits == 64: builder.add_64bit_float(value) else: self.logger.error( "Number of bits or datatype not supportet : {0}".format( typeStr)) elif dataType.lower() == 'string': builder.add_string(value) elif dataType.lower() == 'bit': if objectType == 'Coil' or objectType == 'DiscreteInput': if not type(value) == type(True): # test is boolean self.logger.error( "Value is not boolean: {0}".format(value)) return else: if set(bitstr).issubset({ '0', '1' }) and bool(bitstr): # test is bit-string '00110101' builder.add_bits(value) else: self.logger.error( "Value is not a bitstring: {0}".format(value)) else: self.logger.error( "Number of bits or datatype not supportet : {0}".format( typeStr)) return None if objectType == 'Coil': result = self._Mclient.write_coil(address, value, unit=slaveUnit) elif objectType == 'HoldingRegister': registers = builder.to_registers() result = self._Mclient.write_registers(address, registers, unit=slaveUnit) elif objectType == 'DiscreteInput': self.logger.warning( "this object type cannot be written {0}:{1} slaveUnit:{2}". format(objectType, address, slaveUnit)) return elif objectType == 'InputRegister': self.logger.warning( "this object type cannot be written {0}:{1} slaveUnit:{2}". format(objectType, address, slaveUnit)) return else: return if result.isError(): self.logger.error( "write error: {0} {1}.{2}.{3} (address.slaveUnit)".format( result, objectType, address, slaveUnit)) return None if 'write_dt' in regPara: regPara['last_write_dt'] = regPara['write_dt'] regPara['write_dt'] = datetime.now() else: regPara.update({'write_dt': datetime.now()}) if 'write_value' in regPara: regPara['last_write_value'] = regPara['write_value'] regPara['write_value'] = value else: regPara.update({'write_value': value})
def on_message(client, userdata, message): global gatewayID newDev = 1 i = 0 date_array = [0, 0, 0, 0, 0, 0, 0] modbus_client = ModbusClient(host="localhost", port=502) #Get the JSON message as a string jsonStr = str(message.payload.decode('utf-8')) #Get the topic gatewayTopic = message.topic #Check if message is uplink if ((gatewayTopic.find("event/up") != -1)): #get devEUI devEUI_str = gatewayTopic.split('/', 6)[3] #encode devEUI to hex devEUI_hex = int(devEUI_str, 16) #Check if message devEUI has a matching address in MODBUS table for dev in devEUItab: #print ("device tab EUI: " + dev) #print ("device message EUI : " + devEUI_str) if (dev == devEUI_str): #print("Uplink received - devEUI : " + devEUItab[i] + " - devADR : " + str(devADRtab[i])) logging.info('Uplink received - devEUI : %s - devADR : %s', devEUItab[i], str(devADRtab[i])) newDev = 0 break i = i + 1 #If there is no match in MODBUS table if (newDev == 1): #Add new device to device file #print("Adding new device in list :" + devEUI_str) logging.info('New device %s added in list', devEUI_str) devices = open("/home/ogate/MODBUS/modbus.dev", "a") devices.write(devEUI_str + ',,\n') devices.close() #And update MODBUS table once device added to list makeTab() #Parse the json to get loRa uplink message payload jsonDat = json.loads(jsonStr) #Encode Chirpstack default base64 payload to hexadecimal loraPayload_bytes = base64.b64decode(jsonDat['data']) #Get payload byte number bytesNumber = len(loraPayload_bytes) #Create array of 0 bytes to fill the 32 bytes gap zeroArray = bytearray(32 - bytesNumber) #Convert to hex #loraPayload_hex = int.from_bytes(loraPayload_bytes, "big") #Get timestamp with format below #[----byte1][----byte2]... #[--Century][-----Year]... now_tm = datetime.datetime.now() date_array[0] = int(now_tm.year / 100) #century date_array[1] = now_tm.year % 100 date_array[2] = now_tm.month date_array[3] = now_tm.day date_array[4] = now_tm.hour date_array[5] = now_tm.minute date_array[6] = now_tm.second #Prepare the content to be stored in MODBUS table #Format below is set for MODBUS table register (10 bytes per device) : #[byte #1-2][byte #3-4][byte #5-6][byte #7-8][byte #9-10][byte #11-12][byte #13-14][byte #15-16][byte #13-14][byte #15-16][byte #17-18][byte #19-20][byte #21-22][byte #23-24][byte #25-26][byte #27-28] #[------------------------------------devEUI][----------------------------------------Timestamp][-------------------------------------------------------------------------------------------------------------------- #Use builder to format MODBUS table content builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big) #First 4 bytes are used to store devEUI builder.add_64bit_uint(devEUI_hex) #Then 8 bytes are used to store timestamp builder.add_16bit_uint(date_array[0]) builder.add_8bit_uint(date_array[1]) builder.add_8bit_uint(date_array[2]) builder.add_8bit_uint(date_array[3]) builder.add_8bit_uint(date_array[4]) builder.add_8bit_uint(date_array[5]) builder.add_8bit_uint(date_array[6]) #Add the zero bytes to fill the 32 bytes field for id1 in range(0, len(zeroArray)): builder.add_8bit_uint(zeroArray[id1]) #Add the payload bytes for id2 in range(0, bytesNumber): builder.add_8bit_uint(loraPayload_bytes[id2]) content = builder.build() #Write to corresponding register result = modbus_client.write_registers(devADRtab[i], content, skip_encode=True) if result: #print('Uplink stored in register address: ' + str(devADRtab[i])) logging.info('Uplink stored in register address: %s', str(devADRtab[i])) else: #print('Problem storing uplink message in register') logging.error('Problem storing uplink message in register') #Get devices file content devices = open("/home/ogate/MODBUS/modbus.dev", "r") devicesLines = devices.readlines() devices.close() #Update the device file with new content and new date devices = open("/home/ogate/MODBUS/modbus.dev", "w") payloadStr = loraPayload_bytes.hex() payloadLen = len(payloadStr) #Prepare timestamp string date_str = "00" for k in range(0, 7): if (len(hex(date_array[k])) <= 3): date_str = date_str + '0' date_str = date_str + hex(date_array[k])[2:] print("Date string : " + date_str) #Fill the payload string with 0 if necessary (to get 16 bits) for i in range(payloadLen, 64): payloadStr = '0' + payloadStr for line in devicesLines: if (line.split(',')[0] == devEUI_str): devices.write(devEUI_str + ',' + devEUI_str + date_str + payloadStr + ',' + str(now_tm) + '\n') else: devices.write(line) devices.close()
def run_binary_payload_ex(): # ----------------------------------------------------------------------- # # We are going to use a simple client to send our requests # ----------------------------------------------------------------------- # client = ModbusClient('127.0.0.1', port=5440) client.connect() # ----------------------------------------------------------------------- # # If you need to build a complex message to send, you can use the payload # builder to simplify the packing logic. # # Here we demonstrate packing a random payload layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - another 16 bit unsigned int 0x5678 # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] # - an 32 bit uint 0x12345678 # - an 32 bit signed int -0x1234 # - an 64 bit signed int 0x12345678 # The packing can also be applied to the word (wordorder) and bytes in each # word (byteorder) # The wordorder is applicable only for 32 and 64 bit values # Lets say we need to write a value 0x12345678 to a 32 bit register # The following combinations could be used to write the register # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # Word Order - Big Byte Order - Big # word1 =0x1234 word2 = 0x5678 # Word Order - Big Byte Order - Little # word1 =0x3412 word2 = 0x7856 # Word Order - Little Byte Order - Big # word1 = 0x5678 word2 = 0x1234 # Word Order - Little Byte Order - Little # word1 =0x7856 word2 = 0x3412 # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # ----------------------------------------------------------------------- # builder = BinaryPayloadBuilder(byteorder=Endian.Little, wordorder=Endian.Big) builder.add_string('abcdefgh') builder.add_32bit_float(22.34) builder.add_16bit_uint(0x1234) builder.add_16bit_uint(0x5678) builder.add_8bit_int(0x12) builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0]) builder.add_32bit_uint(0x12345678) builder.add_32bit_int(-0x1234) builder.add_64bit_int(0x1234567890ABCDEF) payload = builder.build() address = 0 client.write_registers(address, payload, skip_encode=True, unit=1) # ----------------------------------------------------------------------- # # If you need to decode a collection of registers in a weird layout, the # payload decoder can help you as well. # # Here we demonstrate decoding a random register layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - another 16 bit unsigned int which we will ignore # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] # ----------------------------------------------------------------------- # address = 0x00 count = len(payload) result = client.read_holding_registers(address, count, unit=1) print("-" * 60) print("Registers") print("-" * 60) print(result.registers) print("\n") decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=Endian.Little, wordorder=Endian.Big) decoded = { 'string': decoder.decode_string(8), 'float': decoder.decode_32bit_float(), '16uint': decoder.decode_16bit_uint(), 'ignored': decoder.skip_bytes(2), '8int': decoder.decode_8bit_int(), 'bits': decoder.decode_bits(), "32uints": decoder.decode_32bit_uint(), "32ints": decoder.decode_32bit_int(), "64ints": decoder.decode_64bit_int(), } print("-" * 60) print("Decoded Data") print("-" * 60) for name, value in iteritems(decoded): print("%s\t" % name, hex(value) if isinstance(value, int) else value) # ----------------------------------------------------------------------- # # close the client # ----------------------------------------------------------------------- # client.close()
class MbVariable: def __init__(self, name, var_type, encoding, scale, offset): self.name = name # do not allow invalid encoding if encoding not in [ "BB", "LB", "BL", "LL", "ABCD", "CDAB", "BADC", "DCBA" ]: logging.error("invalid Encoding:" + encoding + " For " + name) raise KeyError self.encoding = encoding self.type, self.size = compute_size(var_type) self.value = 0 self.bpb = None self.bpd = None self.scale = float(scale) self.offset = float(offset) self.registers = [0] * self.size self.set_builder() self.register = None def __str__(self): return self.name def set_var_register(self, reg): # print("decoding ", self.name, self.type, reg) if self.encoding in ['BB', 'ABCD']: self.bpd = BinaryPayloadDecoder.fromRegisters(reg, byteorder=Endian.Big, wordorder=Endian.Big) elif self.encoding in ['BL', 'CDAB']: self.bpd = BinaryPayloadDecoder.fromRegisters( reg, byteorder=Endian.Big, wordorder=Endian.Little) elif self.encoding in ['LL', 'DCBA']: self.bpd = BinaryPayloadDecoder.fromRegisters( reg, byteorder=Endian.Little, wordorder=Endian.Little) elif self.encoding in ['LB', 'BADC']: self.bpd = BinaryPayloadDecoder.fromRegisters( reg, byteorder=Endian.Little, wordorder=Endian.Big) else: raise KeyError self.register = reg if self.type == "uint16": val = self.bpd.decode_16bit_uint() * self.scale + int(self.offset) elif self.type in ["sint16", "int16"]: val = self.bpd.decode_16bit_int() * self.scale + int(self.offset) elif self.type in ["uint32"]: val = self.bpd.decode_32bit_uint() * self.scale + int(self.offset) elif self.type in ["sint32", "int32"]: val = self.bpd.decode_32bit_int() * self.scale + int(self.offset) elif self.type in ["float32"]: val = self.bpd.decode_32bit_float() * self.scale + int(self.offset) elif self.type == "str": val1 = self.bpd.decode_string(self.size) val = val1.decode() else: logging.error("Unknown data type" + self.type) raise KeyError if math.isnan(val): self.value = 0 else: self.value = val def set_builder(self): if self.encoding in ['BB', 'ABCD']: self.bpb = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big) elif self.encoding in ['BL', 'CDAB']: self.bpb = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) elif self.encoding in ['LL', 'DCBA']: self.bpb = BinaryPayloadBuilder(byteorder=Endian.Little, wordorder=Endian.Little) elif self.encoding in ['LB', 'BADC']: self.bpb = BinaryPayloadBuilder(byteorder=Endian.Little, wordorder=Endian.Big) else: raise KeyError def set_value(self, value): self.set_builder() self.set_reg_value(value) self.value = value def set_reg_value(self, value): if self.type == "uint16": v_raw = int((float(value) - self.offset) / self.scale) if v_raw < 0: logging.warning("Negative value for " + self.name + ":" + str(v_raw)) v_raw = 0 self.bpb.add_16bit_uint(v_raw) elif self.type == "int16": self.bpb.add_16bit_int( int((float(value) - self.offset) / self.scale)) elif self.type == "uint32": v_raw = int((float(value) - self.offset) / self.scale) if v_raw < 0: logging.warning("Negative value for " + self.name + ":" + str(v_raw)) v_raw = 0 self.bpb.add_32bit_uint(v_raw) elif self.type == "int32": self.bpb.add_32bit_int( int((float(value) - self.offset) / self.scale)) elif self.type == "float32": self.bpb.add_32bit_float( int((float(value) - self.offset) / self.scale)) elif self.type == "str": value_str = " " * (self.size - len(str(value))) + str(value) self.bpb.add_string(value_str) else: logging.error("Unknown format:" + self.type + " for " + self.name) raise ValueError self.registers = self.bpb.to_registers() logging.debug("Updated var register " + str(self.registers))
endian=client.read_holding_registers(49991,4,unit=1) #endian.registers[0] = byteswap(endian.registers[0]) #endian.registers[1] = byteswap(endian.registers[1]) #endian.registers[2] = byteswap(endian.registers[2]) #endian.registers[3] = byteswap(endian.registers[3]) decoder=BinaryPayloadDecoder.fromRegisters(endian.registers,endian=Endian.Big) decoded={ 'val':hex(decoder.decode_32bit_uint()), 'v100':decoder.decode_32bit_float() } print decoded encoder = BinaryPayloadBuilder(endian=Endian.Big) encoder.add_16bit_uint(0x1234) buf = encoder.build() #buf[0] = charswap(buf[0]) client.write_registers(51234, buf, unit=1, skip_encode=True) encoder = BinaryPayloadBuilder(endian=Endian.Big) encoder.add_32bit_float(1.0114) encoder.add_32bit_float(-6) buf = encoder.build() #buf[0] = charswap(buf[0]) #buf[1] = charswap(buf[1]) #buf[2] = charswap(buf[2]) #buf[3] = charswap(buf[3]) client.write_registers(50000 + 8 * 5, buf, unit=1, skip_encode=True) while True:
elements_total = page_len * page_cnt empty_rest = elements_total - a_len #--------- #---------Send start message of firmware crc32_func = crcmod.mkCrcFun(0x104C11DB7, initCrc=0, xorOut=0xFFFFFFFF) #Poly CRC-32: 0x104C11DB7 http://crcmod.sourceforge.net/crcmod.predefined.html crc_sum32 = crc32_func(b) crc_sum32_a = (crc_sum32>>16) & 0xFFFF crc_sum32_b = crc_sum32 & 0xFFFF crc_sum32_c = (crc_sum32_a>>8) & 0xFF crc_sum32_d = crc_sum32_a & 0xFF crc_sum32_e = (crc_sum32_b >> 8) & 0xFF crc_sum32_f = crc_sum32_b & 0xFF builder.add_16bit_uint(2) #Type of packet builder.add_16bit_uint(1) #Reserved builder.add_16bit_uint(aa_len) builder.add_16bit_uint(ab_len) builder.add_16bit_uint(crc_sum32_c) #CRC32 sum of firmware body builder.add_16bit_uint(crc_sum32_d) builder.add_16bit_uint(crc_sum32_e) builder.add_16bit_uint(crc_sum32_f) pg_num_h = (page_cnt >> 8 ) & 0xFF pg_num_l = page_cnt & 0xFF builder.add_16bit_uint(pg_num_h) #total number of pages builder.add_16bit_uint(pg_num_l)
def run_binary_payload_ex(): # ----------------------------------------------------------------------- # # We are going to use a simple client to send our requests # ----------------------------------------------------------------------- # client = ModbusClient('127.0.0.1', port=5020) client.connect() # ----------------------------------------------------------------------- # # If you need to build a complex message to send, you can use the payload # builder to simplify the packing logic. # # Here we demonstrate packing a random payload layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - another 16 bit unsigned int 0x5678 # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] # - an 32 bit uint 0x12345678 # - an 32 bit signed int -0x1234 # - an 64 bit signed int 0x12345678 # The packing can also be applied to the word (wordorder) and bytes in each # word (byteorder) # The wordorder is applicable only for 32 and 64 bit values # Lets say we need to write a value 0x12345678 to a 32 bit register # The following combinations could be used to write the register # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # Word Order - Big Byte Order - Big # word1 =0x1234 word2 = 0x5678 # Word Order - Big Byte Order - Little # word1 =0x3412 word2 = 0x7856 # Word Order - Little Byte Order - Big # word1 = 0x5678 word2 = 0x1234 # Word Order - Little Byte Order - Little # word1 =0x7856 word2 = 0x3412 # +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ # # ----------------------------------------------------------------------- # combos = [(wo, bo) for wo in [Endian.Big, Endian.Little] for bo in [Endian.Big, Endian.Little]] for wo, bo in combos: print("-" * 60) print("Word Order: {}".format(ORDER_DICT[wo])) print("Byte Order: {}".format(ORDER_DICT[bo])) print() builder = BinaryPayloadBuilder(byteorder=bo, wordorder=wo) strng = "abcdefgh" builder.add_string(strng) builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0]) builder.add_8bit_int(-0x12) builder.add_8bit_uint(0x12) builder.add_16bit_int(-0x5678) builder.add_16bit_uint(0x1234) builder.add_32bit_int(-0x1234) builder.add_32bit_uint(0x12345678) builder.add_16bit_float(12.34) builder.add_16bit_float(-12.34) builder.add_32bit_float(22.34) builder.add_32bit_float(-22.34) builder.add_64bit_int(-0xDEADBEEF) builder.add_64bit_uint(0x12345678DEADBEEF) builder.add_64bit_uint(0x12345678DEADBEEF) builder.add_64bit_float(123.45) builder.add_64bit_float(-123.45) payload = builder.to_registers() print("-" * 60) print("Writing Registers") print("-" * 60) print(payload) print("\n") payload = builder.build() address = 0 # Can write registers # registers = builder.to_registers() # client.write_registers(address, registers, unit=1) # Or can write encoded binary string client.write_registers(address, payload, skip_encode=True, unit=1) # ----------------------------------------------------------------------- # # If you need to decode a collection of registers in a weird layout, the # payload decoder can help you as well. # # Here we demonstrate decoding a random register layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - another 16 bit unsigned int which we will ignore # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] # ----------------------------------------------------------------------- # address = 0x0 count = len(payload) result = client.read_holding_registers(address, count, unit=1) print("-" * 60) print("Registers") print("-" * 60) print(result.registers) print("\n") decoder = BinaryPayloadDecoder.fromRegisters(result.registers, byteorder=bo, wordorder=wo) assert decoder._byteorder == builder._byteorder, \ "Make sure byteorder is consistent between BinaryPayloadBuilder and BinaryPayloadDecoder" assert decoder._wordorder == builder._wordorder, \ "Make sure wordorder is consistent between BinaryPayloadBuilder and BinaryPayloadDecoder" decoded = OrderedDict([ ('string', decoder.decode_string(len(strng))), ('bits', decoder.decode_bits()), ('8int', decoder.decode_8bit_int()), ('8uint', decoder.decode_8bit_uint()), ('16int', decoder.decode_16bit_int()), ('16uint', decoder.decode_16bit_uint()), ('32int', decoder.decode_32bit_int()), ('32uint', decoder.decode_32bit_uint()), ('16float', decoder.decode_16bit_float()), ('16float2', decoder.decode_16bit_float()), ('32float', decoder.decode_32bit_float()), ('32float2', decoder.decode_32bit_float()), ('64int', decoder.decode_64bit_int()), ('64uint', decoder.decode_64bit_uint()), ('ignore', decoder.skip_bytes(8)), ('64float', decoder.decode_64bit_float()), ('64float2', decoder.decode_64bit_float()), ]) print("-" * 60) print("Decoded Data") print("-" * 60) for name, value in iteritems(decoded): print("%s\t" % name, hex(value) if isinstance(value, int) else value) # ----------------------------------------------------------------------- # # close the client # ----------------------------------------------------------------------- # client.close()
client = ModbusClient(method="rtu", port="/dev/ttyUSB0", stopbits=1, bytesize=8, parity='N', baudrate=115200, timeout=0.5) #Connect to the serial modbus server connection = client.connect() print(connection) builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Little) builder.add_16bit_uint(5) #Type of packet payload = builder.to_registers() payload = builder.build() client.write_registers(0, payload, skip_encode=True, unit=mb_unit) builder.reset() #time.sleep(20) builder.add_16bit_uint(6) #Type of packet payload = builder.to_registers() payload = builder.build() client.write_registers(0, payload, skip_encode=True, unit=mb_unit) builder.reset() #time.sleep(20) builder.add_16bit_uint(0) #Type of packet payload = builder.to_registers()
def handle_control_tags(client, readings_from_rtu, mysql_signals, hr_control_tag_floats=True): """ Function to test control tag feedback values read from RTU against corresponding in MySQL scada_signals, and if necessary, write new values to RTU. If hr_control_tag_floats=False then HR control tags will be written to as 16Bit integers """ new_coil_control_tags = {} new_hr_control_tags = {} try: # use config control_tags to read corresponding COILS out of readings_from_rtu for key in conf.coils_control_tags.keys(): fb_reading = key + '_fb' if readings_from_rtu[fb_reading] == bool(mysql_signals.__dict__[key]): logger.debug( "Control tag fb '{}' value {} is equal to scada_signals {}" .format(fb_reading, readings_from_rtu[fb_reading], bool(mysql_signals.__dict__[key]))) else: new_coil_control_tags[key] = mysql_signals.__dict__[key] logger.debug( "Control tag fb '{}' value {} does not match new value in scada_signals {}, listed for over-write" .format(fb_reading, readings_from_rtu[fb_reading], bool(mysql_signals.__dict__[key]))) # use config control_tags to read corresponding HR out of readings_from_rtu for key in conf.holding_registers_control_tags.keys(): fb_reading = key + '_fb' if readings_from_rtu[fb_reading] == mysql_signals.__dict__[key]: logger.debug( "Control tag fb '{}' value {} is equal to scada_signals {}" .format(fb_reading, readings_from_rtu[fb_reading], bool(mysql_signals.__dict__[key]))) else: new_hr_control_tags[key] = mysql_signals.__dict__[key] logger.debug( "Control tag fb '{}' value {} does not match new value in scada_signals {}, listed for over-write" .format(fb_reading, readings_from_rtu[fb_reading], bool(mysql_signals.__dict__[key]))) except Exception as e: logger.exception('Exception occurred comparing control tags - {}'.format(e)) # If necessary, write new control tag values from scada_signals to RTU try: if new_coil_control_tags: for key, value in new_coil_control_tags.items(): client.write_coil(conf.coils_control_tags[key], value) logger.info("New control tag value {} for '{}' written to RTU".format(value, key)) except Exception as e: logger.exception('Exception occurred writing new COIL control tags to RTU - {}'.format(e)) try: if new_hr_control_tags: builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=Endian.Big) if hr_control_tag_floats: for key, value in new_hr_control_tags.items(): builder.add_32bit_float(value) payload = builder.build() client.write_registers(conf.holding_registers_control_tags[key][0], payload, skip_encode=True) logger.info("New control tag value {} for '{}' written to RTU".format(value, key)) else: for key, value in new_hr_control_tags.items(): builder.add_16bit_uint(value) payload = builder.build() client.write_registers(conf.holding_registers_control_tags[key][0], payload, skip_encode=True) logger.info("New control tag value {} for '{}' written to RTU".format(value, key)) # Pretty sure will never have to write 32Bit integers, but should be able to if need be. # BinaryPayloadBuilder would need little endian if 32Bit read is anything to go by. # Note: if we had consecutive addresses we could write all at once, instead of building payload # individually and writing for each signal individually. But seeing as it's only max of # 8 signals at present, it's safer to just write them individually, so address order doesn't matter except Exception as e: logger.exception('Exception occurred writing new HR control tags to RTU - {}'.format(e))
def write(self, name, value): client = self.mbclient(host=self.host, port=self.port, retries=self.retries, backoff=self.backoff, timeout=self.timeout, framer=self.mbframer, retry_on_empty=True, retry_on_invalid=True) if not client.connect(): logger.error("Cannot connect to bridge %s" % (self.host)) return False row = list(filter(lambda r: r[0] == name, self.mapping)) if len(row): (name, descr, unit, datatype, rw, scale, offset, register) = row[0] if not rw: logger.error( "Error writing to bridge %s slave %d register %d: read only" % (self.host, self.slaveid, register)) client.close() return False register = int(register) try: builder = BinaryPayloadBuilder(byteorder=Endian.Big, wordorder=self.endian) if datatype == 'b': builder.add_8bit_int(value) if datatype == 'B': builder.add_8bit_uint(value) if datatype == 'h': builder.add_16bit_int(value) if datatype == 'H': builder.add_16bit_uint(value) if datatype == 'i': builder.add_32bit_int(value) if datatype == 'I': builder.add_32bit_uint(value) if datatype == 'q': builder.add_64bit_int(value) if datatype == 'Q': builder.add_64bit_uint(value) if datatype == 'f': builder.add_32bit_float(value) if datatype == 'd': builder.add_64bit_float(value) if re.match(r'^s(\d*)$', datatype): builder.add_string(value) registers = builder.to_registers() except (AttributeError, ValueError, struct.error) as e: logger.error( "Error writing to bridge %s slave %d register %d: %s" % (self.host, self.slaveid, register, str(e))) client.close() return False try: if register > 40000: addr = register - 40001 if len(registers) > 1: result = client.write_registers(addr, registers, unit=self.slaveid) else: result = client.write_register(addr, value, unit=self.slaveid) else: addr = register - 1 result = client.write_coil(addr, bool(value), unit=self.slaveid) except ConnectionException as e: logger.error( "Error writing to bridge %s slave %d register %d: %s" % (self.host, self.slaveid, register, str(e))) client.close() return False if type(result) == ExceptionResponse: logger.error( "Error writing to bridge %s slave %d register %d: %s" % (self.host, self.slaveid, register, result)) client.close() return False if result.isError(): logger.error( "Error writing to bridge %s slave %d register %d" % (self.host, self.slaveid, register)) client.close() return False logger.debug( 'Modbus bridge: %s slave: %s register: %s (%s) value: %s' % (self.host, self.slaveid, register, name, value)) client.close() return True
# If you need to build a complex message to send, you can use the payload # builder to simplify the packing logic. # # Here we demonstrate packing a random payload layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34 # - a 16 bit unsigned int 0x1234 # - an 8 bit int 0x12 # - an 8 bit bitstring [0,1,0,1,1,0,1,0] # ---------------------------------------------------------------------------# builder = BinaryPayloadBuilder(endian=Endian.Little) builder.add_string("abcdefgh") builder.add_32bit_float(22.34) builder.add_16bit_uint(0x1234) builder.add_8bit_int(0x12) builder.add_bits([0, 1, 0, 1, 1, 0, 1, 0]) payload = builder.build() address = 0x01 result = client.write_registers(address, payload, skip_encode=True) # ---------------------------------------------------------------------------# # If you need to decode a collection of registers in a weird layout, the # payload decoder can help you as well. # # Here we demonstrate decoding a random register layout, unpacked it looks # like the following: # # - a 8 byte string 'abcdefgh' # - a 32 bit float 22.34
print("-" * 60) print("Decoded Data") print("-" * 60) for name, value in decoded.items(): print ("%s\t" % name, value) print("\n") # Try to write registers 2000 print("-" * 60) print("Writing Registers") print("-" * 60) builder = BinaryPayloadBuilder(byteorder='>', wordorder='>') builder.add_16bit_uint(1234) builder.add_16bit_uint(0) # Skip 1 byte builder.add_32bit_int(-1234) builder.add_32bit_float(-1.234) builder.add_32bit_float(54961.85) builder.add_16bit_int(-5678) builder.add_16bit_uint(0) # Skip 1 byte builder.add_32bit_uint(1) payload = builder.to_registers() print("\n") print(payload) print("\n") payload = builder.build() # Can write registers registers = builder.to_registers()