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, 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, **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, address=None, handler=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 ModbusDisonnectedRequestHandler :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 ModbusSocketFramer self.context = context or ModbusServerContext() self.control = ModbusControlBlock() self.address = address or ("", Defaults.Port) self.handler = handler or ModbusDisconnectedRequestHandler 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.ThreadingUDPServer.__init__(self, self.address, self.handler)
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)
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)