def testPayloadDecoderReset(self): """ Test the payload decoder reset functionality """ decoder = BinaryPayloadDecoder(b'\x12\x34') self.assertEqual(0x12, decoder.decode_8bit_uint()) self.assertEqual(0x34, decoder.decode_8bit_uint()) decoder.reset() self.assertEqual(0x3412, decoder.decode_16bit_uint())
def bytes_to_status(bytes_, status_type): """Convert bytes to trip and digital input statuses """ # The 4 bits for a state is contained i 4 bytes, gather them up into one list all_states = [] for byte_ in bytes_: pay = BinaryPayloadDecoder(byte_) bits_ = pay.decode_bits() # 8 booleans all_states.extend([bits_[:4], bits_[4:]]) # The 3 bit indicates whether status is used, sort out the rest all_states = [state for state in all_states if state[3]] states = {} for state_num, state_bits in enumerate(all_states, start=1): # Enumeration starts at 1 # The first 3 bits has 3 different meanings: on, inhibit and override # First make sure only one is set if sum(state_bits[:3]) > 1: raise ValueError('Bad state: {}'.format(state_bits)) # Then translate, if none is set, default to off for bit_num, bit_meaning in enumerate(['on', 'inhibit', 'override']): if state_bits[bit_num]: states[status_type + str(state_num)] = bit_meaning break else: states[status_type + str(state_num)] = 'off' return states
def fromCoils(coils, endian=Endian.Little): """ Initialize a payload decoder with the result of reading a collection of coils from a modbus device. The coils are treated as a list of bit(boolean) values. :param coils: The coil results to initialize with :param endian: The endianess of the payload :returns: An initialized PayloadDecoder """ if isinstance(coils, list): payload = pack_bitstring(coils) return BinaryPayloadDecoder(payload, endian) raise ParameterException('Invalid collection of coils supplied')
def testBigEndianPayloadDecoder(self): ''' Test basic bit message encoding/decoding ''' decoder = BinaryPayloadDecoder(self.big_endian_payload, endian=Endian.Big) self.assertEqual(1, decoder.decode_8bit_uint()) self.assertEqual(2, decoder.decode_16bit_uint()) self.assertEqual(3, decoder.decode_32bit_uint()) self.assertEqual(4, decoder.decode_64bit_uint()) self.assertEqual(-1, decoder.decode_8bit_int()) self.assertEqual(-2, decoder.decode_16bit_int()) self.assertEqual(-3, decoder.decode_32bit_int()) self.assertEqual(-4, decoder.decode_64bit_int()) self.assertEqual(1.25, decoder.decode_32bit_float()) self.assertEqual(6.25, decoder.decode_64bit_float()) self.assertEqual(b'test', decoder.decode_string(4)) self.assertEqual(self.bitstring, decoder.decode_bits())
def fromRegisters(registers, endian=Endian.Little): """ Initialize a payload decoder with the result of reading a collection of registers from a modbus device. The registers are treated as a list of 2 byte values. We have to do this because of how the data has already been decoded by the rest of the library. :param registers: The register results to initialize with :param endian: The endianess of the payload :returns: An initialized PayloadDecoder """ if isinstance(registers, list): # repack into flat binary payload = ''.join(pack('>H', x) for x in registers) return BinaryPayloadDecoder(payload, endian) raise ParameterException('Invalid collection of registers supplied')
def testLittleEndianPayloadDecoder(self): ''' Test basic bit message encoding/decoding ''' decoder = BinaryPayloadDecoder(self.little_endian_payload, endian=Endian.Little) self.assertEqual(1, decoder.decode_8bit_uint()) self.assertEqual(2, decoder.decode_16bit_uint()) self.assertEqual(3, decoder.decode_32bit_uint()) self.assertEqual(4, decoder.decode_64bit_uint()) self.assertEqual(-1, decoder.decode_8bit_int()) self.assertEqual(-2, decoder.decode_16bit_int()) self.assertEqual(-3, decoder.decode_32bit_int()) self.assertEqual(-4, decoder.decode_64bit_int()) self.assertEqual(1.25, decoder.decode_32bit_float()) self.assertEqual(6.25, decoder.decode_64bit_float()) self.assertEqual(None, decoder.skip_bytes(2)) self.assertEqual('test', decoder.decode_string(4).decode()) self.assertEqual(self.bitstring, decoder.decode_bits())
def decode(self, raw, size, mb_type, mb_funcall=3): log.debug('decode param (raw=%s, size=%s, mb_type=%s, mb_funcall=%s)' % (raw, size, mb_type, mb_funcall)) if mb_funcall == 1: # Read Coil Status (FC=01) log.debug("decoder FC1 (raw: %s)" % raw) decoder = BinaryPayloadDecoder.fromCoils(raw, endian=self.endian) elif mb_funcall == 2: # Read Discrete Input (FC=02) log.debug("decoder FC2 (raw: %s)" % raw) decoder = BinaryPayloadDecoder(raw, endian=self.endian) elif mb_funcall == 3: # Read Holding Registers (FC=03) log.debug("decoder FC3 (raw: %s)" % raw) decoder = BinaryPayloadDecoder.fromRegisters(raw, endian=self.endian) elif mb_funcall == 4: # Read Input Registers (FC=04) log.debug("decoder stub FC4 (raw: %s)" % raw) decoder = BinaryPayloadDecoder(raw, endian=self.endian) else: log.debug("Function call not supported: %s" % mb_funcall) decoder = None result = "" if mb_type == 'bitmap': if size == 1: mb_type = 'int8' elif size == 2: mb_type = 'int16' elif size == 2: mb_type = 'int32' elif size == 4: mb_type = 'int64' if decoder is None: log.debug("decode none") result = raw else: try: if mb_type == 'string' or mb_type == 'utf8': result = decoder.decode_string(size) #elif mb_type == 'bitmap': # result = decoder.decode_string(size) elif mb_type == 'datetime': result = decoder.decode_string(size) elif mb_type == 'uint8': result = int(decoder.decode_8bit_uint()) elif mb_type == 'int8': result = int(decoder.decode_8bit_int()) elif mb_type == 'uint16': result = int(decoder.decode_16bit_uint()) elif mb_type == 'int16': result = int(decoder.decode_16bit_int()) elif mb_type == 'uint32': result = int(decoder.decode_32bit_uint()) elif mb_type == 'int32': result = int(decoder.decode_32bit_int()) elif mb_type == 'uint64': result = int(decoder.decode_64bit_uint()) elif mb_type == 'int64': result = int(decoder.decode_64bit_int()) elif mb_type == 'float32' or mb_type == 'float': result = float(decoder.decode_32bit_float()) elif mb_type == 'float64': result = float(decoder.decode_64bit_float()) elif mb_type == 'bit': result = int(decoder.decode_bits()) elif mb_type == 'bool': result = bool(raw[0]) elif mb_type == 'raw': result = raw[0] else: result = raw except ValueError as e: log.exception(e) result = raw return result
def start(self, host, port, update_period): self.modbus_client = ModbusClient(host, port=port) self.iterate_blocks() while not self.closed: try: if not self.connected: self.connect() for method, starting_address, size, slave_id, points in self.executions: result = method(starting_address, size, unit=slave_id) if result: for point in points: value = None if isinstance( result, ReadCoilsResponse) or isinstance( result, ReadDiscreteInputsResponse): value = result.bits[point.address - starting_address] elif isinstance(result, ReadHoldingRegistersResponse): start = point.address - starting_address value = result.registers[start:start + point.count] if not point.encoding == 'none' and not point.encoding == 'custom': endian = Endian.Auto if point.endian == 'Little': endian = Endian.Little elif point.endian == 'Big': endian = Endian.Big payload = ''.join( pack(endian + 'H', x) for x in value) decoder = BinaryPayloadDecoder( payload, endian) encoding_map = { 'bits': decoder.decode_bits, '8unit': decoder.decode_8bit_uint, '16unit': decoder.decode_16bit_uint, '32unit': decoder.decode_32bit_uint, '64unit': decoder.decode_64bit_uint, '8int': decoder.decode_8bit_int, '16int': decoder.decode_16bit_int, '32int': decoder.decode_32bit_int, '64int': decoder.decode_64bit_int, '32float': decoder.decode_32bit_float, '64float': decoder.decode_64bit_float, 'string': decoder.decode_string } value = encoding_map[point.encoding]() elif point.encoding == 'custom' and point.custom_decoder: decode = eval("lambda value: " + point.custom_decoder) value = decode(value) if point.condition: if point.condition[0] == '=': if str(value) == point.condition[1:]: conpot_core.get_databus().set_value( 'r ' + point.point_id, point.get_read_value(value), index=point.index) elif point.condition[0] == '!': if not str(value) == point.condition[1:]: conpot_core.get_databus().set_value( 'r ' + point.point_id, point.get_read_value(value), index=point.index) elif point.condition[0] == '>': if str(value) > point.condition[1:]: conpot_core.get_databus().set_value( 'r ' + point.point_id, point.get_read_value(value), index=point.index) elif point.condition[0] == '<': if str(value) < point.condition[1:]: conpot_core.get_databus().set_value( 'r ' + point.point_id, point.get_read_value(value), index=point.index) else: conpot_core.get_databus().set_value( 'r ' + point.point_id, point.get_read_value(value), index=point.index) except ConnectionException, e: logger.error('Error because: %s' % e) self.connected = False time.sleep(update_period)
def byte_to_bits(byte): """Convert a byte to a list of bits""" return list(reversed(BinaryPayloadDecoder(byte).decode_bits()))
def read_remaining_time(self): res = self.client.read_holding_registers(address=128, count=2) decoder = BinaryPayloadDecoder(res.registers, byteorder=Endian.Little, wordorder=Ending.Little) time = decoder.decode_32bit_int # value in minutes return time