def testModbusSingleRequestHandlerSend(self): handler = socketserver.BaseRequestHandler(None, None, None) handler.__class__ = ModbusSingleRequestHandler handler.framer = Mock() handler.framer.buildPacket.return_value = b"message" handler.request = Mock() request = ReadCoilsResponse([1]) handler.send(request) self.assertEqual(handler.request.send.call_count, 1) request.should_respond = False handler.send(request) self.assertEqual(handler.request.send.call_count, 1)
def testModbusSingleRequestHandlerSend(self): handler = socketserver.BaseRequestHandler(None, None, None) handler.__class__ = ModbusSingleRequestHandler handler.framer = Mock() handler.framer.buildPacket.return_value = "message" handler.request = Mock() request = ReadCoilsResponse([1]) handler.send(request) self.assertEqual(handler.request.send.call_count, 1) request.should_respond = False handler.send(request) self.assertEqual(handler.request.send.call_count, 1)
async def _request(self, method, *args, **kwargs): if method == 'read_coils': address, count = args return ReadCoilsResponse( [self._coils[address + i] for i in range(count)]) if method == 'read_discrete_inputs': address, count = args return ReadDiscreteInputsResponse( [self._discrete_inputs[address + i] for i in range(count)]) elif method == 'read_holding_registers': address, count = args return ReadHoldingRegistersResponse([ int.from_bytes(self._registers[address + i], byteorder='big') for i in range(count) ]) elif method == 'write_coil': address, data = args self._coils[address] = data return WriteSingleCoilResponse(address, data) elif method == 'write_coils': address, data = args for i, d in enumerate(data): self._coils[address + i] = d return WriteMultipleCoilsResponse(address, len(data)) elif method == 'write_register': address, data = args self._registers[address] = data return WriteSingleRegisterResponse(address, data) elif method == 'write_registers': address, data = args for i, d in enumerate(data): self._registers[address + i] = d return WriteMultipleRegistersResponse(address, len(data)) return NotImplementedError(f'Unrecognised method: {method}')
class LibmodbusClient(ModbusClientMixin): ''' A facade around the raw level 1 libmodbus client that implements the pymodbus protocol on top of the lower level client. ''' #-----------------------------------------------------------------------# # these are used to convert from the pymodbus request types to the # libmodbus operations (overloaded operator). #-----------------------------------------------------------------------# __methods = { 'ReadCoilsRequest': lambda c, r: c.read_bits(r.address, r.count), 'ReadDiscreteInputsRequest': lambda c, r: c.read_input_bits(r.address, r.count), 'WriteSingleCoilRequest': lambda c, r: c.write_bit(r.address, r.value), 'WriteMultipleCoilsRequest': lambda c, r: c.write_bits(r.address, r.values), 'WriteSingleRegisterRequest': lambda c, r: c.write_register(r.address, r.value), 'WriteMultipleRegistersRequest': lambda c, r: c.write_registers(r.address, r.values), 'ReadHoldingRegistersRequest': lambda c, r: c.read_registers(r.address, r.count), 'ReadInputRegistersRequest': lambda c, r: c.read_input_registers(r.address, r.count), 'ReadWriteMultipleRegistersRequest': lambda c, r: c.read_and_write_registers( r.read_address, r.read_count, r.write_address, r.write_registers), } #-----------------------------------------------------------------------# # these are used to convert from the libmodbus result to the # pymodbus response type #-----------------------------------------------------------------------# __adapters = { 'ReadCoilsRequest': lambda tx, rx: ReadCoilsResponse(list(rx)), 'ReadDiscreteInputsRequest': lambda tx, rx: ReadDiscreteInputsResponse(list(rx)), 'WriteSingleCoilRequest': lambda tx, rx: WriteSingleCoilResponse(tx.address, rx), 'WriteMultipleCoilsRequest': lambda tx, rx: WriteMultipleCoilsResponse(tx.address, rx), 'WriteSingleRegisterRequest': lambda tx, rx: WriteSingleRegisterResponse(tx.address, rx), 'WriteMultipleRegistersRequest': lambda tx, rx: WriteMultipleRegistersResponse(tx.address, rx), 'ReadHoldingRegistersRequest': lambda tx, rx: ReadHoldingRegistersResponse(list(rx)), 'ReadInputRegistersRequest': lambda tx, rx: ReadInputRegistersResponse(list(rx)), 'ReadWriteMultipleRegistersRequest': lambda tx, rx: ReadWriteMultipleRegistersResponse(list(rx)), } def __init__(self, client): ''' Initalize a new instance of the LibmodbusClient. This should be initialized with one of the LibmodbusLevel1Client instances: * LibmodbusLevel1Client.create_rtu_client(...) * LibmodbusLevel1Client.create_tcp_client(...) :param client: The underlying client instance to operate with. ''' self.client = client #-----------------------------------------------------------------------# # We use the client mixin to implement the api methods which are all # forwarded to this method. It is implemented using the previously # defined lookup tables. Any method not defined simply throws. #-----------------------------------------------------------------------# def execute(self, request): ''' Execute the supplied request against the server. :param request: The request to process :returns: The result of the request execution ''' if self.client.slave != request.unit_id: self.client.set_slave(request.unit_id) method = request.__class__.__name__ operation = self.__methods.get(method, None) adapter = self.__adapters.get(method, None) if not operation or not adapter: raise NotImplementedException("Method not implemented: " + name) response = operation(self.client, request) return adapter(request, response) #-----------------------------------------------------------------------# # Other methods can simply be forwarded using the decorator pattern #-----------------------------------------------------------------------# def connect(self): return self.client.connect() def close(self): return self.client.close() #-----------------------------------------------------------------------# # magic methods #-----------------------------------------------------------------------# def __enter__(self): ''' Implement the client with enter block :returns: The current instance of the client ''' self.client.connect() return self def __exit__(self, klass, value, traceback): ''' Implement the client with exit block ''' self.client.close()