def setUp(self): ''' Initializes the test environment ''' self.client = ClientDecoder() self.server = ServerDecoder() self.request = ( (0x01, '\x01\x00\x01\x00\x01'), # read coils (0x02, '\x02\x00\x01\x00\x01'), # read discrete inputs (0x03, '\x03\x00\x01\x00\x01'), # read holding registers (0x04, '\x04\x00\x01\x00\x01'), # read input registers (0x05, '\x05\x00\x01\x00\x01'), # write single coil (0x06, '\x06\x00\x01\x00\x01'), # write single register (0x07, '\x07'), # read exception status (0x08, '\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, '\x0b'), # get comm event counters (0x0c, '\x0c'), # get comm event log (0x0f, '\x0f\x00\x01\x00\x08\x01\x00\xff'), # write multiple coils (0x10, '\x10\x00\x01\x00\x02\x04\0xff\xff'), # write multiple registers (0x11, '\x11'), # report slave id (0x14, '\x14\x0e\x06\x00\x04\x00\x01\x00\x02' \ '\x06\x00\x03\x00\x09\x00\x02'), # read file record (0x15, '\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ '\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, '\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, '\x17\x00\x01\x00\x01\x00\x01\x00\x01\x02\x12\x34'),# read/write multiple registers (0x18, '\x18\x00\x01'), # read fifo queue (0x2b, '\x2b\x0e\x01\x00'), # read device identification ) self.response = ( (0x01, '\x01\x01\x01'), # read coils (0x02, '\x02\x01\x01'), # read discrete inputs (0x03, '\x03\x02\x01\x01'), # read holding registers (0x04, '\x04\x02\x01\x01'), # read input registers (0x05, '\x05\x00\x01\x00\x01'), # write single coil (0x06, '\x06\x00\x01\x00\x01'), # write single register (0x07, '\x07\x00'), # read exception status (0x08, '\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, '\x0b\x00\x00\x00\x00'), # get comm event counters (0x0c, '\x0c\x08\x00\x00\x01\x08\x01\x21\x20\x00'), # get comm event log (0x0f, '\x0f\x00\x01\x00\x08'), # write multiple coils (0x10, '\x10\x00\x01\x00\x02'), # write multiple registers (0x11, '\x11\x03\x05\x01\x54'), # report slave id (device specific) (0x14, '\x14\x0c\x05\x06\x0d\xfe\x00\x20\x05' \ '\x06\x33\xcd\x00\x40'), # read file record (0x15, '\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ '\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, '\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, '\x17\x02\x12\x34'), # read/write multiple registers (0x18, '\x18\x00\x01\x00\x01\x00\x00'), # read fifo queue (0x2b, '\x2b\x0e\x01\x01\x00\x00\x01\x00\x01\x77'), # read device identification ) self.bad = ( (0x80, '\x80\x00\x00\x00'), # Unknown Function (0x81, '\x81\x00\x00\x00'), # error message )
def __init__(self, context, framer=None, identity=None, address=None, handler=None, allow_reuse_address=False, **kwargs): """ Overloaded initializer for the socket server If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param context: The ModbusServerContext datastore :param framer: The framer strategy to use :param identity: An optional identify structure :param address: An optional (interface, port) to bind to. :param handler: A handler for each client session; default is ModbusConnectedRequestHandler :param allow_reuse_address: Whether the server will allow the reuse of an address. :param ignore_missing_slaves: True to not send errors on a request to a missing slave :param broadcast_enable: True to treat unit_id 0 as broadcast address, False to treat 0 as any other unit_id """ self.threads = [] self.allow_reuse_address = allow_reuse_address self.decoder = ServerDecoder() self.framer = framer or ModbusSocketFramer self.context = context or ModbusServerContext() self.control = ModbusControlBlock() self.address = address or ("", Defaults.Port) self.handler = handler or ModbusConnectedRequestHandler self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves) self.broadcast_enable = kwargs.get('broadcast_enable', Defaults.broadcast_enable) if isinstance(identity, ModbusDeviceIdentification): self.control.Identity.update(identity) socketserver.ThreadingTCPServer.__init__(self, self.address, self.handler)
def __init__(self, context, framer=None, identity=None, **kwargs): """ Overloaded initializer for the socket server If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param context: The ModbusServerContext datastore :param framer: The framer strategy to use :param identity: An optional identify structure :param port: The serial port to attach to :param stopbits: The number of stop bits to use :param bytesize: The bytesize of the serial messages :param parity: Which kind of parity to use :param baudrate: The baud rate to use for the serial device :param timeout: The timeout to use for the serial device :param ignore_missing_slaves: True to not send errors on a request to a missing slave :param broadcast_enable: True to treat unit_id 0 as broadcast address, False to treat 0 as any other unit_id """ self.threads = [] self.decoder = ServerDecoder() self.framer = framer or ModbusAsciiFramer self.context = context or ModbusServerContext() self.control = ModbusControlBlock() if isinstance(identity, ModbusDeviceIdentification): self.control.Identity.update(identity) self.device = kwargs.get('port', 0) self.stopbits = kwargs.get('stopbits', Defaults.Stopbits) self.bytesize = kwargs.get('bytesize', Defaults.Bytesize) self.parity = kwargs.get('parity', Defaults.Parity) self.baudrate = kwargs.get('baudrate', Defaults.Baudrate) self.timeout = kwargs.get('timeout', Defaults.Timeout) self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves) self.broadcast_enable = kwargs.get('broadcast_enable', Defaults.broadcast_enable) self.socket = None if self._connect(): self.is_running = True self._build_handler()
def __init__(self, store, framer=None, identity=None): ''' Overloaded initializer for the modbus factory If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param store: The ModbusServerContext datastore :param framer: The framer strategy to use :param identity: An optional identify structure ''' self.decoder = ServerDecoder() self.framer = framer or ModbusSocketFramer self.store = store or ModbusServerContext() self.control = ModbusControlBlock() self.access = ModbusAccessControl() if isinstance(identity, ModbusDeviceIdentification): self.control.Identity.update(identity)
def __init__(self, context, framer=None, **kwargs): # pragma: no cover """ Overloaded initializer for the socket server If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param context: The ModbusServerContext datastore :param framer: The framer strategy to use :param port: The serial port to attach to :param stopbits: The number of stop bits to use :param bytesize: The bytesize of the serial messages :param parity: Which kind of parity to use :param baudrate: The baud rate to use for the serial device :param timeout: The timeout to use for the serial device :param ignore_missing_slaves: True to not send errors on a request to a missing slave :param broadcast_enable: True to treat unit_id 0 as broadcast address, False to treat 0 as any other unit_id :param autoreonnect: True to enable automatic reconnection, False otherwise :param reconnect_delay: reconnect delay in seconds :param response_manipulator: Callback method for manipulating the response """ self.device = kwargs.get('port', 0) self.stopbits = kwargs.get('stopbits', Defaults.Stopbits) self.bytesize = kwargs.get('bytesize', Defaults.Bytesize) self.parity = kwargs.get('parity', Defaults.Parity) self.baudrate = kwargs.get('baudrate', Defaults.Baudrate) self.timeout = kwargs.get('timeout', Defaults.Timeout) self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves) self.broadcast_enable = kwargs.get('broadcast_enable', Defaults.broadcast_enable) self.auto_reconnect = kwargs.get('auto_reconnect', False) self.reconnect_delay = kwargs.get('reconnect_delay', 2) self.reconnecting_task = None self.handler = kwargs.get("handler") or ModbusSingleRequestHandler self.framer = framer or ModbusRtuFramer self.decoder = ServerDecoder() self.context = context or ModbusServerContext() self.response_manipulator = kwargs.get("response_manipulator", None) self.protocol = None self.transport = None
def __init__(self, store, framer=None, identity=None, **kwargs): """ Overloaded initializer for the modbus factory If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param store: The ModbusServerContext datastore :param framer: The framer strategy to use :param identity: An optional identify structure :param ignore_missing_slaves: True to not send errors on a request to a missing slave """ self.decoder = ServerDecoder() self.framer = framer or ModbusSocketFramer self.store = store or ModbusServerContext() self.control = ModbusControlBlock() self.access = ModbusAccessControl() self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves) if isinstance(identity, ModbusDeviceIdentification): self.control.Identity.update(identity)
def __init__(self, context, framer=None, identity=None): ''' Overloaded initializer for the socket server If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param context: The ModbusServerContext datastore :param framer: The framer strategy to use :param identity: An optional identify structure ''' self.threads = [] self.decoder = ServerDecoder() self.framer = framer or ModbusSocketFramer self.context = context or ModbusServerContext() self.control = ModbusControlBlock() if isinstance(identity, ModbusDeviceIdentification): self.control.Identity.update(identity) SocketServer.ThreadingUDPServer.__init__( self, ("", Defaults.Port), ModbusDisconnectedRequestHandler)
class SimpleFactoryTest(unittest.TestCase): ''' This is the unittest for the pymod.exceptions module ''' def setUp(self): ''' Initializes the test environment ''' self.client = ClientDecoder() self.server = ServerDecoder() self.request = ( (0x01, b'\x01\x00\x01\x00\x01'), # read coils (0x02, b'\x02\x00\x01\x00\x01'), # read discrete inputs (0x03, b'\x03\x00\x01\x00\x01'), # read holding registers (0x04, b'\x04\x00\x01\x00\x01'), # read input registers (0x05, b'\x05\x00\x01\x00\x01'), # write single coil (0x06, b'\x06\x00\x01\x00\x01'), # write single register (0x07, b'\x07'), # read exception status (0x08, b'\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, b'\x0b'), # get comm event counters (0x0c, b'\x0c'), # get comm event log (0x0f, b'\x0f\x00\x01\x00\x08\x01\x00\xff'), # write multiple coils (0x10, b'\x10\x00\x01\x00\x02\x04\0xff\xff'), # write multiple registers (0x11, b'\x11'), # report slave id (0x14, b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02' \ b'\x06\x00\x03\x00\x09\x00\x02'), # read file record (0x15, b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ b'\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, b'\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, b'\x17\x00\x01\x00\x01\x00\x01\x00\x01\x02\x12\x34'),# read/write multiple registers (0x18, b'\x18\x00\x01'), # read fifo queue (0x2b, b'\x2b\x0e\x01\x00'), # read device identification ) self.response = ( (0x01, b'\x01\x01\x01'), # read coils (0x02, b'\x02\x01\x01'), # read discrete inputs (0x03, b'\x03\x02\x01\x01'), # read holding registers (0x04, b'\x04\x02\x01\x01'), # read input registers (0x05, b'\x05\x00\x01\x00\x01'), # write single coil (0x06, b'\x06\x00\x01\x00\x01'), # write single register (0x07, b'\x07\x00'), # read exception status (0x08, b'\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, b'\x0b\x00\x00\x00\x00'), # get comm event counters (0x0c, b'\x0c\x08\x00\x00\x01\x08\x01\x21\x20\x00'), # get comm event log (0x0f, b'\x0f\x00\x01\x00\x08'), # write multiple coils (0x10, b'\x10\x00\x01\x00\x02'), # write multiple registers (0x11, b'\x11\x03\x05\x01\x54'), # report slave id (device specific) (0x14, b'\x14\x0c\x05\x06\x0d\xfe\x00\x20\x05' \ b'\x06\x33\xcd\x00\x40'), # read file record (0x15, b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ b'\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, b'\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, b'\x17\x02\x12\x34'), # read/write multiple registers (0x18, b'\x18\x00\x01\x00\x01\x00\x00'), # read fifo queue (0x2b, b'\x2b\x0e\x01\x01\x00\x00\x01\x00\x01\x77'), # read device identification ) self.exception = ( (0x81, b'\x81\x01\xd0\x50'), # illegal function exception (0x82, b'\x82\x02\x90\xa1'), # illegal data address exception (0x83, b'\x83\x03\x50\xf1'), # illegal data value exception (0x84, b'\x84\x04\x13\x03'), # skave device failure exception (0x85, b'\x85\x05\xd3\x53'), # acknowledge exception (0x86, b'\x86\x06\x93\xa2'), # slave device busy exception (0x87, b'\x87\x08\x53\xf2'), # memory parity exception (0x88, b'\x88\x0a\x16\x06'), # gateway path unavailable exception (0x89, b'\x89\x0b\xd6\x56'), # gateway target failed exception ) self.bad = ( (0x80, b'\x80\x00\x00\x00'), # Unknown Function (0x81, b'\x81\x00\x00\x00'), # error message ) def tearDown(self): ''' Cleans up the test environment ''' del self.bad del self.request del self.response def testExceptionLookup(self): ''' Test that we can look up exception messages ''' for func, _ in self.exception: response = self.client.lookupPduClass(func) self.assertNotEqual(response, None) for func, _ in self.exception: response = self.server.lookupPduClass(func) self.assertNotEqual(response, None) def testResponseLookup(self): ''' Test a working response factory lookup ''' for func, _ in self.response: response = self.client.lookupPduClass(func) self.assertNotEqual(response, None) def testRequestLookup(self): ''' Test a working request factory lookup ''' for func, _ in self.request: request = self.client.lookupPduClass(func) self.assertNotEqual(request, None) def testResponseWorking(self): ''' Test a working response factory decoders ''' for func, msg in self.response: try: self.client.decode(msg) except ModbusException: self.fail("Failed to Decode Response Message", func) def testResponseErrors(self): ''' Test a response factory decoder exceptions ''' self.assertRaises(ModbusException, self.client._helper, self.bad[0][1]) self.assertEqual(self.client.decode(self.bad[1][1]).function_code, self.bad[1][0], "Failed to decode error PDU") def testRequestsWorking(self): ''' Test a working request factory decoders ''' for func, msg in self.request: try: self.server.decode(msg) except ModbusException: self.fail("Failed to Decode Request Message", func) def testClientFactoryFails(self): ''' Tests that a client factory will fail to decode a bad message ''' self.client._helper = _raise_exception actual = self.client.decode(None) self.assertEqual(actual, None) def testServerFactoryFails(self): ''' Tests that a server factory will fail to decode a bad message ''' self.server._helper = _raise_exception actual = self.server.decode(None) self.assertEqual(actual, None) def testServerRegisterCustomRequest(self): class CustomRequest(ModbusRequest): function_code = 0xff self.server.register(CustomRequest) assert self.client.lookupPduClass(CustomRequest.function_code) CustomRequest.sub_function_code = 0xff self.server.register(CustomRequest) assert self.server.lookupPduClass(CustomRequest.function_code) def testClientRegisterCustomResponse(self): class CustomResponse(ModbusResponse): function_code = 0xff self.client.register(CustomResponse) assert self.client.lookupPduClass(CustomResponse.function_code) CustomResponse.sub_function_code = 0xff self.client.register(CustomResponse) assert self.client.lookupPduClass(CustomResponse.function_code) #---------------------------------------------------------------------------# # I don't actually know what is supposed to be returned here, I assume that # since the high bit is set, it will simply echo the resulting message #---------------------------------------------------------------------------# def testRequestErrors(self): ''' Test a request factory decoder exceptions ''' for func, msg in self.bad: result = self.server.decode(msg) self.assertEqual(result.ErrorCode, 1, "Failed to decode invalid requests") self.assertEqual(result.execute(None).function_code, func, "Failed to create correct response message")
def setUp(self): ''' Initializes the test environment ''' self.client = ClientDecoder() self.server = ServerDecoder() self.request = ( (0x01, b'\x01\x00\x01\x00\x01'), # read coils (0x02, b'\x02\x00\x01\x00\x01'), # read discrete inputs (0x03, b'\x03\x00\x01\x00\x01'), # read holding registers (0x04, b'\x04\x00\x01\x00\x01'), # read input registers (0x05, b'\x05\x00\x01\x00\x01'), # write single coil (0x06, b'\x06\x00\x01\x00\x01'), # write single register (0x07, b'\x07'), # read exception status (0x08, b'\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, b'\x0b'), # get comm event counters (0x0c, b'\x0c'), # get comm event log (0x0f, b'\x0f\x00\x01\x00\x08\x01\x00\xff'), # write multiple coils (0x10, b'\x10\x00\x01\x00\x02\x04\0xff\xff'), # write multiple registers (0x11, b'\x11'), # report slave id (0x14, b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02' \ b'\x06\x00\x03\x00\x09\x00\x02'), # read file record (0x15, b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ b'\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, b'\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, b'\x17\x00\x01\x00\x01\x00\x01\x00\x01\x02\x12\x34'),# read/write multiple registers (0x18, b'\x18\x00\x01'), # read fifo queue (0x2b, b'\x2b\x0e\x01\x00'), # read device identification ) self.response = ( (0x01, b'\x01\x01\x01'), # read coils (0x02, b'\x02\x01\x01'), # read discrete inputs (0x03, b'\x03\x02\x01\x01'), # read holding registers (0x04, b'\x04\x02\x01\x01'), # read input registers (0x05, b'\x05\x00\x01\x00\x01'), # write single coil (0x06, b'\x06\x00\x01\x00\x01'), # write single register (0x07, b'\x07\x00'), # read exception status (0x08, b'\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, b'\x0b\x00\x00\x00\x00'), # get comm event counters (0x0c, b'\x0c\x08\x00\x00\x01\x08\x01\x21\x20\x00'), # get comm event log (0x0f, b'\x0f\x00\x01\x00\x08'), # write multiple coils (0x10, b'\x10\x00\x01\x00\x02'), # write multiple registers (0x11, b'\x11\x03\x05\x01\x54'), # report slave id (device specific) (0x14, b'\x14\x0c\x05\x06\x0d\xfe\x00\x20\x05' \ b'\x06\x33\xcd\x00\x40'), # read file record (0x15, b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ b'\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, b'\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, b'\x17\x02\x12\x34'), # read/write multiple registers (0x18, b'\x18\x00\x01\x00\x01\x00\x00'), # read fifo queue (0x2b, b'\x2b\x0e\x01\x01\x00\x00\x01\x00\x01\x77'), # read device identification ) self.exception = ( (0x81, b'\x81\x01\xd0\x50'), # illegal function exception (0x82, b'\x82\x02\x90\xa1'), # illegal data address exception (0x83, b'\x83\x03\x50\xf1'), # illegal data value exception (0x84, b'\x84\x04\x13\x03'), # skave device failure exception (0x85, b'\x85\x05\xd3\x53'), # acknowledge exception (0x86, b'\x86\x06\x93\xa2'), # slave device busy exception (0x87, b'\x87\x08\x53\xf2'), # memory parity exception (0x88, b'\x88\x0a\x16\x06'), # gateway path unavailable exception (0x89, b'\x89\x0b\xd6\x56'), # gateway target failed exception ) self.bad = ( (0x80, b'\x80\x00\x00\x00'), # Unknown Function (0x81, b'\x81\x00\x00\x00'), # error message )
class SimpleFactoryTest(unittest.TestCase): ''' This is the unittest for the pymod.exceptions module ''' def setUp(self): ''' Initializes the test environment ''' self.client = ClientDecoder() self.server = ServerDecoder() self.request = ( (0x01, '\x01\x00\x01\x00\x01'), # read coils (0x02, '\x02\x00\x01\x00\x01'), # read discrete inputs (0x03, '\x03\x00\x01\x00\x01'), # read holding registers (0x04, '\x04\x00\x01\x00\x01'), # read input registers (0x05, '\x05\x00\x01\x00\x01'), # write single coil (0x06, '\x06\x00\x01\x00\x01'), # write single register (0x07, '\x07'), # read exception status (0x08, '\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, '\x0b'), # get comm event counters (0x0c, '\x0c'), # get comm event log (0x0f, '\x0f\x00\x01\x00\x08\x01\x00\xff'), # write multiple coils (0x10, '\x10\x00\x01\x00\x02\x04\0xff\xff'), # write multiple registers (0x11, '\x11'), # report slave id (0x14, '\x14\x0e\x06\x00\x04\x00\x01\x00\x02' \ '\x06\x00\x03\x00\x09\x00\x02'), # read file record (0x15, '\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ '\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, '\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, '\x17\x00\x01\x00\x01\x00\x01\x00\x01\x02\x12\x34'),# read/write multiple registers (0x18, '\x18\x00\x01'), # read fifo queue (0x2b, '\x2b\x0e\x01\x00'), # read device identification ) self.response = ( (0x01, '\x01\x01\x01'), # read coils (0x02, '\x02\x01\x01'), # read discrete inputs (0x03, '\x03\x02\x01\x01'), # read holding registers (0x04, '\x04\x02\x01\x01'), # read input registers (0x05, '\x05\x00\x01\x00\x01'), # write single coil (0x06, '\x06\x00\x01\x00\x01'), # write single register (0x07, '\x07\x00'), # read exception status (0x08, '\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, '\x0b\x00\x00\x00\x00'), # get comm event counters (0x0c, '\x0c\x08\x00\x00\x01\x08\x01\x21\x20\x00'), # get comm event log (0x0f, '\x0f\x00\x01\x00\x08'), # write multiple coils (0x10, '\x10\x00\x01\x00\x02'), # write multiple registers (0x11, '\x11\x03\x05\x01\x54'), # report slave id (device specific) (0x14, '\x14\x0c\x05\x06\x0d\xfe\x00\x20\x05' \ '\x06\x33\xcd\x00\x40'), # read file record (0x15, '\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ '\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, '\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, '\x17\x02\x12\x34'), # read/write multiple registers (0x18, '\x18\x00\x01\x00\x01\x00\x00'), # read fifo queue (0x2b, '\x2b\x0e\x01\x01\x00\x00\x01\x00\x01\x77'), # read device identification ) self.bad = ( (0x80, '\x80\x00\x00\x00'), # Unknown Function (0x81, '\x81\x00\x00\x00'), # error message ) def tearDown(self): ''' Cleans up the test environment ''' del self.bad del self.request del self.response def testResponseLookup(self): ''' Test a working response factory lookup ''' for func, _ in self.response: response = self.client.lookupPduClass(func) self.assertNotEqual(response, None) def testRequestLookup(self): ''' Test a working request factory lookup ''' for func, _ in self.request: request = self.client.lookupPduClass(func) self.assertNotEqual(request, None) def testResponseWorking(self): ''' Test a working response factory decoders ''' for func, msg in self.response: try: self.client.decode(msg) except ModbusException: self.fail("Failed to Decode Response Message", func) def testResponseErrors(self): ''' Test a response factory decoder exceptions ''' self.assertRaises(ModbusException, self.client._helper, self.bad[0][1]) self.assertEqual( self.client.decode(self.bad[1][1]).function_code, self.bad[1][0], "Failed to decode error PDU") def testRequestsWorking(self): ''' Test a working request factory decoders ''' for func, msg in self.request: try: self.server.decode(msg) except ModbusException: self.fail("Failed to Decode Request Message", func) def testClientFactoryFails(self): ''' Tests that a client factory will fail to decode a bad message ''' self.client._helper = _raise_exception actual = self.client.decode(None) self.assertEquals(actual, None) def testServerFactoryFails(self): ''' Tests that a server factory will fail to decode a bad message ''' self.server._helper = _raise_exception actual = self.server.decode(None) self.assertEquals(actual, None) #---------------------------------------------------------------------------# # I don't actually know what is supposed to be returned here, I assume that # since the high bit is set, it will simply echo the resulting message #---------------------------------------------------------------------------# def testRequestErrors(self): ''' Test a request factory decoder exceptions ''' for func, msg in self.bad: result = self.server.decode(msg) self.assertEqual(result.ErrorCode, 1, "Failed to decode invalid requests") self.assertEqual( result.execute(None).function_code, func, "Failed to create correct response message")
class SimpleFactoryTest(unittest.TestCase): ''' This is the unittest for the pymod.exceptions module ''' def setUp(self): ''' Initializes the test environment ''' self.client = ClientDecoder() self.server = ServerDecoder() self.request = ( (0x01, '\x01\x00\x01\x00\x01'), # read coils (0x02, '\x02\x00\x01\x00\x01'), # read discrete inputs (0x03, '\x03\x00\x01\x00\x01'), # read holding registers (0x04, '\x04\x00\x01\x00\x01'), # read input registers (0x05, '\x05\x00\x01\x00\x01'), # write single coil (0x06, '\x06\x00\x01\x00\x01'), # write single register (0x07, '\x07'), # read exception status (0x08, '\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, '\x0b'), # get comm event counters (0x0c, '\x0c'), # get comm event log (0x0f, '\x0f\x00\x01\x00\x08\x01\x00\xff'), # write multiple coils (0x10, '\x10\x00\x01\x00\x02\x04\0xff\xff'), # write multiple registers (0x11, '\x11'), # report slave id (0x14, '\x14\x0e\x06\x00\x04\x00\x01\x00\x02' \ '\x06\x00\x03\x00\x09\x00\x02'), # read file record (0x15, '\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ '\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, '\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, '\x17\x00\x01\x00\x01\x00\x01\x00\x01\x02\x12\x34'),# read/write multiple registers (0x18, '\x18\x00\x01'), # read fifo queue (0x2b, '\x2b\x0e\x01\x00'), # read device identification ) self.response = ( (0x01, '\x01\x01\x01'), # read coils (0x02, '\x02\x01\x01'), # read discrete inputs (0x03, '\x03\x02\x01\x01'), # read holding registers (0x04, '\x04\x02\x01\x01'), # read input registers (0x05, '\x05\x00\x01\x00\x01'), # write single coil (0x06, '\x06\x00\x01\x00\x01'), # write single register (0x07, '\x07\x00'), # read exception status (0x08, '\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, '\x0b\x00\x00\x00\x00'), # get comm event counters (0x0c, '\x0c\x08\x00\x00\x01\x08\x01\x21\x20\x00'), # get comm event log (0x0f, '\x0f\x00\x01\x00\x08'), # write multiple coils (0x10, '\x10\x00\x01\x00\x02'), # write multiple registers (0x11, '\x11\x03\x05\x01\x54'), # report slave id (device specific) (0x14, '\x14\x0c\x05\x06\x0d\xfe\x00\x20\x05' \ '\x06\x33\xcd\x00\x40'), # read file record (0x15, '\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ '\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, '\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, '\x17\x02\x12\x34'), # read/write multiple registers (0x18, '\x18\x00\x01\x00\x01\x00\x00'), # read fifo queue (0x2b, '\x2b\x0e\x01\x01\x00\x00\x01\x00\x01\x77'), # read device identification ) self.bad = ( (0x80, '\x80\x00\x00\x00'), # Unknown Function (0x81, '\x81\x00\x00\x00'), # error message ) def tearDown(self): ''' Cleans up the test environment ''' del self.bad del self.request del self.response def testResponseLookup(self): ''' Test a working response factory lookup ''' for func, _ in self.response: response = self.client.lookupPduClass(func) self.assertNotEqual(response, None) def testRequestLookup(self): ''' Test a working request factory lookup ''' for func, _ in self.request: request = self.client.lookupPduClass(func) self.assertNotEqual(request, None) def testResponseWorking(self): ''' Test a working response factory decoders ''' for func, msg in self.response: try: self.client.decode(msg) except ModbusException: self.fail("Failed to Decode Response Message", func) def testResponseErrors(self): ''' Test a response factory decoder exceptions ''' self.assertRaises(ModbusException, self.client._helper, self.bad[0][1]) self.assertEqual(self.client.decode(self.bad[1][1]).function_code, self.bad[1][0], "Failed to decode error PDU") def testRequestsWorking(self): ''' Test a working request factory decoders ''' for func, msg in self.request: try: self.server.decode(msg) except ModbusException: self.fail("Failed to Decode Request Message", func) def testClientFactoryFails(self): ''' Tests that a client factory will fail to decode a bad message ''' self.client._helper = _raise_exception actual = self.client.decode(None) self.assertEquals(actual, None) def testServerFactoryFails(self): ''' Tests that a server factory will fail to decode a bad message ''' self.server._helper = _raise_exception actual = self.server.decode(None) self.assertEquals(actual, None) #---------------------------------------------------------------------------# # I don't actually know what is supposed to be returned here, I assume that # since the high bit is set, it will simply echo the resulting message #---------------------------------------------------------------------------# def testRequestErrors(self): ''' Test a request factory decoder exceptions ''' for func, msg in self.bad: result = self.server.decode(msg) self.assertEqual(result.ErrorCode, 1, "Failed to decode invalid requests") self.assertEqual(result.execute(None).function_code, func, "Failed to create correct response message")
def __init__(self, context, framer=None, identity=None, address=None, sslctx=None, certfile=None, keyfile=None, handler=None, allow_reuse_address=False, allow_reuse_port=False, defer_start=False, backlog=20, loop=None, **kwargs): """ Overloaded initializer for the socket server If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param context: The ModbusServerContext datastore :param framer: The framer strategy to use :param identity: An optional identify structure :param address: An optional (interface, port) to bind to. :param sslctx: The SSLContext to use for TLS (default None and auto create) :param certfile: The cert file path for TLS (used if sslctx is None) :param keyfile: The key file path for TLS (used if sslctx is None) :param handler: A handler for each client session; default is ModbusConnectedRequestHandler. The handler class receives connection create/teardown events :param allow_reuse_address: Whether the server will allow the reuse of an address. :param allow_reuse_port: Whether the server will allow the reuse of a port. :param backlog: is the maximum number of queued connections passed to listen(). Defaults to 20, increase if many connections are being made and broken to your Modbus slave :param loop: optional asyncio event loop to run in. Will default to asyncio.get_event_loop() supplied value if None. :param ignore_missing_slaves: True to not send errors on a request to a missing slave :param broadcast_enable: True to treat unit_id 0 as broadcast address, False to treat 0 as any other unit_id """ self.active_connections = {} self.loop = loop or asyncio.get_event_loop() self.allow_reuse_address = allow_reuse_address self.decoder = ServerDecoder() self.framer = framer or ModbusTlsFramer self.context = context or ModbusServerContext() self.control = ModbusControlBlock() self.address = address or ("", Defaults.Port) self.handler = handler or ModbusConnectedRequestHandler self.handler.server = self self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves) self.broadcast_enable = kwargs.get('broadcast_enable', Defaults.broadcast_enable) if isinstance(identity, ModbusDeviceIdentification): self.control.Identity.update(identity) self.sslctx = sslctx if self.sslctx is None: self.sslctx = ssl.create_default_context() self.sslctx.load_cert_chain(certfile=certfile, keyfile=keyfile) # According to MODBUS/TCP Security Protocol Specification, it is # TLSv2 at least self.sslctx.options |= ssl.OP_NO_TLSv1_1 self.sslctx.options |= ssl.OP_NO_TLSv1 self.sslctx.options |= ssl.OP_NO_SSLv3 self.sslctx.options |= ssl.OP_NO_SSLv2 self.sslctx.verify_mode = ssl.CERT_OPTIONAL self.sslctx.check_hostname = False self.serving = self.loop.create_future( ) # asyncio future that will be done once server has started self.server = None # constructors cannot be declared async, so we have to defer the initialization of the server if PYTHON_VERSION >= (3, 7): # start_serving is new in version 3.7 self.server_factory = self.loop.create_server( lambda: self.handler(self), *self.address, ssl=self.sslctx, reuse_address=allow_reuse_address, reuse_port=allow_reuse_port, backlog=backlog, start_serving=not defer_start) else: self.server_factory = self.loop.create_server( lambda: self.handler(self), *self.address, ssl=self.sslctx, reuse_address=allow_reuse_address, reuse_port=allow_reuse_port, backlog=backlog)
def setUp(self): ''' Initializes the test environment ''' self.client = ClientDecoder() self.server = ServerDecoder() self.request = ( (0x01, '\x01\x00\x01\x00\x01'), # read coils (0x02, '\x02\x00\x01\x00\x01'), # read discrete inputs (0x03, '\x03\x00\x01\x00\x01'), # read holding registers (0x04, '\x04\x00\x01\x00\x01'), # read input registers (0x05, '\x05\x00\x01\x00\x01'), # write single coil (0x06, '\x06\x00\x01\x00\x01'), # write single register (0x07, '\x07'), # read exception status (0x08, '\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, '\x0b'), # get comm event counters (0x0c, '\x0c'), # get comm event log (0x0f, '\x0f\x00\x01\x00\x08\x01\x00\xff'), # write multiple coils (0x10, '\x10\x00\x01\x00\x02\x04\0xff\xff'), # write multiple registers (0x11, '\x11'), # report slave id (0x14, '\x14\x0e\x06\x00\x04\x00\x01\x00\x02' \ '\x06\x00\x03\x00\x09\x00\x02'), # read file record (0x15, '\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ '\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, '\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, '\x17\x00\x01\x00\x01\x00\x01\x00\x01\x02\x12\x34'),# read/write multiple registers (0x18, '\x18\x00\x01'), # read fifo queue (0x2b, '\x2b\x0e\x01\x00'), # read device identification ) self.response = ( (0x01, '\x01\x01\x01'), # read coils (0x02, '\x02\x01\x01'), # read discrete inputs (0x03, '\x03\x02\x01\x01'), # read holding registers (0x04, '\x04\x02\x01\x01'), # read input registers (0x05, '\x05\x00\x01\x00\x01'), # write single coil (0x06, '\x06\x00\x01\x00\x01'), # write single register (0x07, '\x07\x00'), # read exception status (0x08, '\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, '\x0b\x00\x00\x00\x00'), # get comm event counters (0x0c, '\x0c\x08\x00\x00\x01\x08\x01\x21\x20\x00'), # get comm event log (0x0f, '\x0f\x00\x01\x00\x08'), # write multiple coils (0x10, '\x10\x00\x01\x00\x02'), # write multiple registers (0x11, '\x11\x03\x05\x01\x54'), # report slave id (device specific) (0x14, '\x14\x0c\x05\x06\x0d\xfe\x00\x20\x05' \ '\x06\x33\xcd\x00\x40'), # read file record (0x15, '\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ '\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, '\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, '\x17\x02\x12\x34'), # read/write multiple registers (0x18, '\x18\x00\x01\x00\x01\x00\x00'), # read fifo queue (0x2b, '\x2b\x0e\x01\x01\x00\x00\x01\x00\x01\x77'), # read device identification ) self.exception = ( (0x81, '\x81\x01\xd0\x50'), # illegal function exception (0x82, '\x82\x02\x90\xa1'), # illegal data address exception (0x83, '\x83\x03\x50\xf1'), # illegal data value exception (0x84, '\x84\x04\x13\x03'), # skave device failure exception (0x85, '\x85\x05\xd3\x53'), # acknowledge exception (0x86, '\x86\x06\x93\xa2'), # slave device busy exception (0x87, '\x87\x08\x53\xf2'), # memory parity exception (0x88, '\x88\x0a\x16\x06'), # gateway path unavailable exception (0x89, '\x89\x0b\xd6\x56'), # gateway target failed exception ) self.bad = ( (0x80, '\x80\x00\x00\x00'), # Unknown Function (0x81, '\x81\x00\x00\x00'), # error message )
def __init__(self, context, framer=None, identity=None, address=None, handler=None, allow_reuse_address=False, allow_reuse_port=False, defer_start=False, backlog=20, loop=None, **kwargs): """ Overloaded initializer for the socket server If the identify structure is not passed in, the ModbusControlBlock uses its own empty structure. :param context: The ModbusServerContext datastore :param framer: The framer strategy to use :param identity: An optional identify structure :param address: An optional (interface, port) to bind to. :param handler: A handler for each client session; default is ModbusConnectedRequestHandler. The handler class receives connection create/teardown events :param allow_reuse_address: Whether the server will allow the reuse of an address. :param allow_reuse_port: Whether the server will allow the reuse of a port. :param backlog: is the maximum number of queued connections passed to listen(). Defaults to 20, increase if many connections are being made and broken to your Modbus slave :param loop: optional asyncio event loop to run in. Will default to asyncio.get_event_loop() supplied value if None. :param ignore_missing_slaves: True to not send errors on a request to a missing slave :param broadcast_enable: True to treat unit_id 0 as broadcast address, False to treat 0 as any other unit_id :param response_manipulator: Callback method for manipulating the response """ self.active_connections = {} self.loop = loop or asyncio.get_event_loop() self.allow_reuse_address = allow_reuse_address self.decoder = ServerDecoder() self.framer = framer or ModbusSocketFramer self.context = context or ModbusServerContext() self.control = ModbusControlBlock() self.address = address or ("", Defaults.Port) self.handler = handler or ModbusConnectedRequestHandler self.handler.server = self self.ignore_missing_slaves = kwargs.get('ignore_missing_slaves', Defaults.IgnoreMissingSlaves) self.broadcast_enable = kwargs.get('broadcast_enable', Defaults.broadcast_enable) self.response_manipulator = kwargs.get("response_manipulator", None) if isinstance(identity, ModbusDeviceIdentification): self.control.Identity.update(identity) # asyncio future that will be done once server has started self.serving = self.loop.create_future() # constructors cannot be declared async, so we have to # defer the initialization of the server self.server = None if PYTHON_VERSION >= (3, 7): # start_serving is new in version 3.7 self.server_factory = self.loop.create_server( lambda: self.handler(self), *self.address, reuse_address=allow_reuse_address, reuse_port=allow_reuse_port, backlog=backlog, start_serving=not defer_start) else: self.server_factory = self.loop.create_server( lambda: self.handler(self), *self.address, reuse_address=allow_reuse_address, reuse_port=allow_reuse_port, backlog=backlog)
class SimpleFactoryTest(unittest.TestCase): ''' This is the unittest for the pymod.exceptions module ''' def setUp(self): ''' Initializes the test environment ''' self.client = ClientDecoder() self.server = ServerDecoder() self.request = ( (0x01, b'\x01\x00\x01\x00\x01'), # read coils (0x02, b'\x02\x00\x01\x00\x01'), # read discrete inputs (0x03, b'\x03\x00\x01\x00\x01'), # read holding registers (0x04, b'\x04\x00\x01\x00\x01'), # read input registers (0x05, b'\x05\x00\x01\x00\x01'), # write single coil (0x06, b'\x06\x00\x01\x00\x01'), # write single register (0x07, b'\x07'), # read exception status (0x08, b'\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, b'\x0b'), # get comm event counters (0x0c, b'\x0c'), # get comm event log (0x0f, b'\x0f\x00\x01\x00\x08\x01\x00\xff'), # write multiple coils (0x10, b'\x10\x00\x01\x00\x02\x04\0xff\xff'), # write multiple registers (0x11, b'\x11'), # report slave id (0x14, b'\x14\x0e\x06\x00\x04\x00\x01\x00\x02' \ b'\x06\x00\x03\x00\x09\x00\x02'), # read file record (0x15, b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ b'\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, b'\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, b'\x17\x00\x01\x00\x01\x00\x01\x00\x01\x02\x12\x34'),# read/write multiple registers (0x18, b'\x18\x00\x01'), # read fifo queue (0x2b, b'\x2b\x0e\x01\x00'), # read device identification ) self.response = ( (0x01, b'\x01\x01\x01'), # read coils (0x02, b'\x02\x01\x01'), # read discrete inputs (0x03, b'\x03\x02\x01\x01'), # read holding registers (0x04, b'\x04\x02\x01\x01'), # read input registers (0x05, b'\x05\x00\x01\x00\x01'), # write single coil (0x06, b'\x06\x00\x01\x00\x01'), # write single register (0x07, b'\x07\x00'), # read exception status (0x08, b'\x08\x00\x00\x00\x00'), # read diagnostic (0x0b, b'\x0b\x00\x00\x00\x00'), # get comm event counters (0x0c, b'\x0c\x08\x00\x00\x01\x08\x01\x21\x20\x00'), # get comm event log (0x0f, b'\x0f\x00\x01\x00\x08'), # write multiple coils (0x10, b'\x10\x00\x01\x00\x02'), # write multiple registers (0x11, b'\x11\x03\x05\x01\x54'), # report slave id (device specific) (0x14, b'\x14\x0c\x05\x06\x0d\xfe\x00\x20\x05' \ b'\x06\x33\xcd\x00\x40'), # read file record (0x15, b'\x15\x0d\x06\x00\x04\x00\x07\x00\x03' \ b'\x06\xaf\x04\xbe\x10\x0d'), # write file record (0x16, b'\x16\x00\x01\x00\xff\xff\x00'), # mask write register (0x17, b'\x17\x02\x12\x34'), # read/write multiple registers (0x18, b'\x18\x00\x01\x00\x01\x00\x00'), # read fifo queue (0x2b, b'\x2b\x0e\x01\x01\x00\x00\x01\x00\x01\x77'), # read device identification ) self.exception = ( (0x81, b'\x81\x01\xd0\x50'), # illegal function exception (0x82, b'\x82\x02\x90\xa1'), # illegal data address exception (0x83, b'\x83\x03\x50\xf1'), # illegal data value exception (0x84, b'\x84\x04\x13\x03'), # skave device failure exception (0x85, b'\x85\x05\xd3\x53'), # acknowledge exception (0x86, b'\x86\x06\x93\xa2'), # slave device busy exception (0x87, b'\x87\x08\x53\xf2'), # memory parity exception (0x88, b'\x88\x0a\x16\x06'), # gateway path unavailable exception (0x89, b'\x89\x0b\xd6\x56'), # gateway target failed exception ) self.bad = ( (0x80, b'\x80\x00\x00\x00'), # Unknown Function (0x81, b'\x81\x00\x00\x00'), # error message ) def tearDown(self): ''' Cleans up the test environment ''' del self.bad del self.request del self.response def testExceptionLookup(self): ''' Test that we can look up exception messages ''' for func, _ in self.exception: response = self.client.lookupPduClass(func) self.assertNotEqual(response, None) for func, _ in self.exception: response = self.server.lookupPduClass(func) self.assertNotEqual(response, None) def testResponseLookup(self): ''' Test a working response factory lookup ''' for func, _ in self.response: response = self.client.lookupPduClass(func) self.assertNotEqual(response, None) def testRequestLookup(self): ''' Test a working request factory lookup ''' for func, _ in self.request: request = self.client.lookupPduClass(func) self.assertNotEqual(request, None) def testResponseWorking(self): ''' Test a working response factory decoders ''' for func, msg in self.response: try: self.client.decode(msg) except ModbusException: self.fail("Failed to Decode Response Message", func) def testResponseErrors(self): ''' Test a response factory decoder exceptions ''' self.assertRaises(ModbusException, self.client._helper, self.bad[0][1]) self.assertEqual( self.client.decode(self.bad[1][1]).function_code, self.bad[1][0], "Failed to decode error PDU") def testRequestsWorking(self): ''' Test a working request factory decoders ''' for func, msg in self.request: try: self.server.decode(msg) except ModbusException: self.fail("Failed to Decode Request Message", func) def testClientFactoryFails(self): ''' Tests that a client factory will fail to decode a bad message ''' self.client._helper = _raise_exception actual = self.client.decode(None) self.assertEqual(actual, None) def testServerFactoryFails(self): ''' Tests that a server factory will fail to decode a bad message ''' self.server._helper = _raise_exception actual = self.server.decode(None) self.assertEqual(actual, None) def testServerRegisterCustomRequest(self): class CustomRequest(ModbusRequest): function_code = 0xff self.server.register(CustomRequest) assert self.client.lookupPduClass(CustomRequest.function_code) CustomRequest.sub_function_code = 0xff self.server.register(CustomRequest) assert self.server.lookupPduClass(CustomRequest.function_code) def testClientRegisterCustomResponse(self): class CustomResponse(ModbusResponse): function_code = 0xff self.client.register(CustomResponse) assert self.client.lookupPduClass(CustomResponse.function_code) CustomResponse.sub_function_code = 0xff self.client.register(CustomResponse) assert self.client.lookupPduClass(CustomResponse.function_code) #---------------------------------------------------------------------------# # I don't actually know what is supposed to be returned here, I assume that # since the high bit is set, it will simply echo the resulting message #---------------------------------------------------------------------------# def testRequestErrors(self): ''' Test a request factory decoder exceptions ''' for func, msg in self.bad: result = self.server.decode(msg) self.assertEqual(result.ErrorCode, 1, "Failed to decode invalid requests") self.assertEqual( result.execute(None).function_code, func, "Failed to create correct response message")