def testUdpClientConnect(self): ''' Test the Udp client connection method''' with patch.object(socket, 'socket') as mock_method: mock_method.return_value = object() client = ModbusUdpClient() self.assertTrue(client.connect()) with patch.object(socket, 'socket') as mock_method: mock_method.side_effect = socket.error() client = ModbusUdpClient() self.assertFalse(client.connect())
def testUdpClientConnect(self): ''' Test the Udp client connection method''' with patch.object(socket, 'socket') as mock_method: class DummySocket(object): def settimeout(self, *a, **kwa): pass mock_method.return_value = DummySocket() client = ModbusUdpClient() self.assertTrue(client.connect()) with patch.object(socket, 'socket') as mock_method: mock_method.side_effect = socket.error() client = ModbusUdpClient() self.assertFalse(client.connect())
def do_connect_udp(host_port: str): """ Connect to a Modbus UDP device :PARAM: host_port: <IP/HOSTNAME>[:<PORT>] """ assert ( ctmodbus.session == None ), "Session already open. Close first." # ToDo assert session type host, port = common.parse_ip_port(host_port) s = ModbusUdpClient(host, port, timeout=3) assert s.connect(), f"Could not connect to {host}:{port}" ctmodbus.session = s date, time = str(datetime.today()).split() return (ctmodbus.output_text + f"UDP session OPENED with {host}:{port} at {date} {time}\n")
def testBasicSyncUdpClient(self): ''' Test the basic methods for the udp sync client''' # receive/send client = ModbusUdpClient() client.socket = mockSocket() self.assertEqual(0, client._send(None)) self.assertEqual(1, client._send('\x00')) self.assertEqual('\x00', client._recv(1)) # connect/disconnect self.assertTrue(client.connect()) client.close() # already closed socket client.socket = False client.close() self.assertEqual("127.0.0.1:502", str(client))
class ModbusHub: """Thread safe wrapper class for pymodbus.""" def __init__(self, client_config): """Initialize the Modbus hub.""" # generic configuration self._client = None self._lock = threading.Lock() self._config_name = client_config[CONF_NAME] self._config_type = client_config[CONF_TYPE] self._config_port = client_config[CONF_PORT] self._config_timeout = client_config[CONF_TIMEOUT] self._config_delay = 0 if self._config_type == "serial": # serial configuration self._config_method = client_config[CONF_METHOD] self._config_baudrate = client_config[CONF_BAUDRATE] self._config_stopbits = client_config[CONF_STOPBITS] self._config_bytesize = client_config[CONF_BYTESIZE] self._config_parity = client_config[CONF_PARITY] else: # network configuration self._config_host = client_config[CONF_HOST] self._config_delay = client_config[CONF_DELAY] if self._config_delay > 0: _LOGGER.warning( "Parameter delay is accepted but not used in this version") @property def name(self): """Return the name of this hub.""" return self._config_name def setup(self): """Set up pymodbus client.""" if self._config_type == "serial": self._client = ModbusSerialClient( method=self._config_method, port=self._config_port, baudrate=self._config_baudrate, stopbits=self._config_stopbits, bytesize=self._config_bytesize, parity=self._config_parity, timeout=self._config_timeout, ) elif self._config_type == "rtuovertcp": self._client = ModbusTcpClient( host=self._config_host, port=self._config_port, framer=ModbusRtuFramer, timeout=self._config_timeout, ) elif self._config_type == "tcp": self._client = ModbusTcpClient( host=self._config_host, port=self._config_port, timeout=self._config_timeout, ) elif self._config_type == "udp": self._client = ModbusUdpClient( host=self._config_host, port=self._config_port, timeout=self._config_timeout, ) else: assert False # Connect device self.connect() def close(self): """Disconnect client.""" with self._lock: self._client.close() def connect(self): """Connect client.""" with self._lock: self._client.connect() def read_coils(self, unit, address, count): """Read coils.""" with self._lock: kwargs = {"unit": unit} if unit else {} return self._client.read_coils(address, count, **kwargs) def read_discrete_inputs(self, unit, address, count): """Read discrete inputs.""" with self._lock: kwargs = {"unit": unit} if unit else {} return self._client.read_discrete_inputs(address, count, **kwargs) def read_input_registers(self, unit, address, count): """Read input registers.""" with self._lock: kwargs = {"unit": unit} if unit else {} return self._client.read_input_registers(address, count, **kwargs) def read_holding_registers(self, unit, address, count): """Read holding registers.""" with self._lock: kwargs = {"unit": unit} if unit else {} return self._client.read_holding_registers(address, count, **kwargs) def write_coil(self, unit, address, value): """Write coil.""" with self._lock: kwargs = {"unit": unit} if unit else {} self._client.write_coil(address, value, **kwargs) def write_register(self, unit, address, value): """Write register.""" with self._lock: kwargs = {"unit": unit} if unit else {} self._client.write_register(address, value, **kwargs) def write_registers(self, unit, address, values): """Write registers.""" with self._lock: kwargs = {"unit": unit} if unit else {} self._client.write_registers(address, values, **kwargs)
class ModbusHub: def __init__(self, protocol, host, port, slave): self._lock = threading.Lock() self._slave = slave if (protocol == "rtuovertcp"): self._client = ModbusTcpClient(host=host, port=port, framer=ModbusRtuFramer, timeout=2, retry_on_empty=True, retry_on_invalid=False) elif (protocol == "rtuoverudp"): self._client = ModbusUdpClient(host=host, port=port, framer=ModbusRtuFramer, timeout=2, retry_on_empty=False, retry_on_invalid=False) def connect(self): with self._lock: self._client.connect() def close(self): with self._lock: self._client.close() def read_holding_register(self): pass def read_input_registers(self, address, count): with self._lock: kwargs = {"unit": self._slave} return self._client.read_input_registers(address, count, **kwargs) def reset_energy(self): with self._lock: kwargs = {"unit": self._slave} request = ModbusResetEnergyRequest(**kwargs) self._client.execute(request) def info_gather(self): data = {} try: result = self.read_input_registers(0, 9) if result is not None and type(result) is not ModbusIOException \ and result.registers is not None and len(result.registers) == 9: data[DEVICE_CLASS_VOLTAGE] = result.registers[0] / 10 data[DEVICE_CLASS_CURRENT] = ( (result.registers[2] << 16) + result.registers[1]) / 1000 data[DEVICE_CLASS_POWER] = ( (result.registers[4] << 16) + result.registers[3]) / 10 data[DEVICE_CLASS_ENERGY] = ( (result.registers[6] << 16) + result.registers[5]) / 1000 data[DEVICE_CLASS_FREQUENCY] = result.registers[7] / 10 data[DEVICE_CLASS_POWER_FACTOR] = result.registers[8] / 100 else: _LOGGER.debug(f"Error in gathering, timed out") except Exception as e: _LOGGER.error(f"Error in gathering, {e}") return data
class ModbusHub: """Thread safe wrapper class for pymodbus.""" def __init__(self, hass, client_config): """Initialize the Modbus hub.""" # generic configuration self._client = None self._async_cancel_listener = None self._in_error = False self._lock = asyncio.Lock() self.hass = hass self._config_name = client_config[CONF_NAME] self._config_type = client_config[CONF_TYPE] self._config_port = client_config[CONF_PORT] self._config_timeout = client_config[CONF_TIMEOUT] self._config_delay = client_config[CONF_DELAY] self._config_reset_socket = client_config[CONF_CLOSE_COMM_ON_ERROR] Defaults.Timeout = client_config[CONF_TIMEOUT] if self._config_type == "serial": # serial configuration self._config_method = client_config[CONF_METHOD] self._config_baudrate = client_config[CONF_BAUDRATE] self._config_stopbits = client_config[CONF_STOPBITS] self._config_bytesize = client_config[CONF_BYTESIZE] self._config_parity = client_config[CONF_PARITY] else: # network configuration self._config_host = client_config[CONF_HOST] self._call_type = { CALL_TYPE_COIL: { ENTRY_ATTR: "bits", ENTRY_FUNC: None, }, CALL_TYPE_DISCRETE: { ENTRY_ATTR: "bits", ENTRY_FUNC: None, }, CALL_TYPE_REGISTER_HOLDING: { ENTRY_ATTR: "registers", ENTRY_FUNC: None, }, CALL_TYPE_REGISTER_INPUT: { ENTRY_ATTR: "registers", ENTRY_FUNC: None, }, CALL_TYPE_WRITE_COIL: { ENTRY_ATTR: "value", ENTRY_FUNC: None, }, CALL_TYPE_WRITE_COILS: { ENTRY_ATTR: "count", ENTRY_FUNC: None, }, CALL_TYPE_WRITE_REGISTER: { ENTRY_ATTR: "value", ENTRY_FUNC: None, }, CALL_TYPE_WRITE_REGISTERS: { ENTRY_ATTR: "count", ENTRY_FUNC: None, }, } def _log_error(self, exception_error: ModbusException, error_state=True): log_text = "Pymodbus: " + str(exception_error) if self._in_error: _LOGGER.debug(log_text) else: _LOGGER.error(log_text) self._in_error = error_state async def async_setup(self): """Set up pymodbus client.""" try: if self._config_type == "serial": self._client = ModbusSerialClient( method=self._config_method, port=self._config_port, baudrate=self._config_baudrate, stopbits=self._config_stopbits, bytesize=self._config_bytesize, parity=self._config_parity, timeout=self._config_timeout, retry_on_empty=True, reset_socket=self._config_reset_socket, ) elif self._config_type == "rtuovertcp": self._client = ModbusTcpClient( host=self._config_host, port=self._config_port, framer=ModbusRtuFramer, timeout=self._config_timeout, reset_socket=self._config_reset_socket, ) elif self._config_type == "tcp": self._client = ModbusTcpClient( host=self._config_host, port=self._config_port, timeout=self._config_timeout, reset_socket=self._config_reset_socket, ) elif self._config_type == "udp": self._client = ModbusUdpClient( host=self._config_host, port=self._config_port, timeout=self._config_timeout, reset_socket=self._config_reset_socket, ) except ModbusException as exception_error: self._log_error(exception_error, error_state=False) return async with self._lock: await self.hass.async_add_executor_job(self._pymodbus_connect) self._call_type[CALL_TYPE_COIL][ENTRY_FUNC] = self._client.read_coils self._call_type[CALL_TYPE_DISCRETE][ ENTRY_FUNC] = self._client.read_discrete_inputs self._call_type[CALL_TYPE_REGISTER_HOLDING][ ENTRY_FUNC] = self._client.read_holding_registers self._call_type[CALL_TYPE_REGISTER_INPUT][ ENTRY_FUNC] = self._client.read_input_registers self._call_type[CALL_TYPE_WRITE_COIL][ ENTRY_FUNC] = self._client.write_coil self._call_type[CALL_TYPE_WRITE_COILS][ ENTRY_FUNC] = self._client.write_coils self._call_type[CALL_TYPE_WRITE_REGISTER][ ENTRY_FUNC] = self._client.write_register self._call_type[CALL_TYPE_WRITE_REGISTERS][ ENTRY_FUNC] = self._client.write_registers # Start counting down to allow modbus requests. if self._config_delay: self._async_cancel_listener = async_call_later( self.hass, self._config_delay, self.async_end_delay) @callback def async_end_delay(self, args): """End startup delay.""" self._async_cancel_listener = None self._config_delay = 0 def _pymodbus_close(self): """Close sync. pymodbus.""" if self._client: try: self._client.close() except ModbusException as exception_error: self._log_error(exception_error) self._client = None async def async_close(self): """Disconnect client.""" if self._async_cancel_listener: self._async_cancel_listener() self._async_cancel_listener = None async with self._lock: return await self.hass.async_add_executor_job(self._pymodbus_close) def _pymodbus_connect(self): """Connect client.""" try: self._client.connect() except ModbusException as exception_error: self._log_error(exception_error, error_state=False) def _pymodbus_call(self, unit, address, value, use_call): """Call sync. pymodbus.""" kwargs = {"unit": unit} if unit else {} try: result = self._call_type[use_call][ENTRY_FUNC](address, value, **kwargs) except ModbusException as exception_error: self._log_error(exception_error) result = exception_error if not hasattr(result, self._call_type[use_call][ENTRY_ATTR]): self._log_error(result) return None self._in_error = False return result async def async_pymodbus_call(self, unit, address, value, use_call): """Convert async to sync pymodbus call.""" if self._config_delay: return None async with self._lock: return await self.hass.async_add_executor_job( self._pymodbus_call, unit, address, value, use_call)
'mean_value': 600, 'noise_range': 50, 'always_positive': False }, 'temperature': { 'binary': False, 'mean_value': 60, 'noise_range': 5, 'always_positive': False }, 'on_off': { 'binary': True, 'mean_value': 0, 'probability': .1 } } fakePLC = FakePLC(8080, example_reading_structure) # Modbus UDP client from pymodbus package client = ModbusClient('localhost', port=8080) plcThread = Thread(target=fakePLC.start_server, daemon=True) plcThread.start() client.connect() while True: try: sleep(2) response = client.read_holding_registers(0, 3) print('Response registers:', response.registers) except KeyboardInterrupt: print('Quiting...') exit(0)
class ModbusHub: """Thread safe wrapper class for pymodbus.""" def __init__(self, client_config): """Initialize the modbus hub.""" # generic configuration self._client = None self._kwargs = {'unit': client_config[CONF_MASTER_UNIT_ID]} self._lock = threading.Lock() self._config_type = client_config[CONF_TYPE] self._config_port = client_config[CONF_PORT] self._config_timeout = client_config[CONF_TIMEOUT] self._config_delay = 0 if self._config_type == "serial": # serial configuration self._config_method = client_config[CONF_METHOD] self._config_baudrate = client_config[CONF_BAUDRATE] self._config_stopbits = client_config[CONF_STOPBITS] self._config_bytesize = client_config[CONF_BYTESIZE] self._config_parity = client_config[CONF_PARITY] else: # network configuration self._config_host = client_config[CONF_HOST] def setup(self): """Set up pymodbus client.""" if self._config_type == "serial": from pymodbus.client.sync import ModbusSerialClient self._client = ModbusSerialClient( method=self._config_method, port=self._config_port, baudrate=self._config_baudrate, stopbits=self._config_stopbits, bytesize=self._config_bytesize, parity=self._config_parity, timeout=self._config_timeout, retry_on_empty=True, ) elif self._config_type == "rtuovertcp": from pymodbus.client.sync import ModbusTcpClient from pymodbus.transaction import ModbusRtuFramer self._client = ModbusTcpClient( host=self._config_host, port=self._config_port, framer=ModbusRtuFramer, timeout=self._config_timeout, ) elif self._config_type == "tcp": from pymodbus.client.sync import ModbusTcpClient self._client = ModbusTcpClient( host=self._config_host, port=self._config_port, timeout=self._config_timeout, ) elif self._config_type == "udp": from pymodbus.client.sync import ModbusUdpClient self._client = ModbusUdpClient( host=self._config_host, port=self._config_port, timeout=self._config_timeout, ) else: raise ValueError(("Unsupported config_type, must be serial, " + "tcp, udp, rtuovertcp")) # Connect device self.connect() def close(self): """Disconnect client.""" with self._lock: self._client.close() def connect(self): """Connect client.""" with self._lock: self._client.connect() def read_coils(self, address, count=1): """Read coils.""" with self._lock: return self._client.read_coils(address, count, **self._kwargs) def read_input_registers(self, address, count=1): """Read input registers.""" with self._lock: return self._client.read_input_registers(address, count, **self._kwargs) def read_holding_registers(self, address, count=1): """Read holding registers.""" with self._lock: return self._client.read_holding_registers(address, count, **self._kwargs) def write_coil(self, address, value): """Write coil.""" with self._lock: self._client.write_coil(address, value, **self._kwargs) def write_register(self, address, value): """Write register.""" with self._lock: self._client.write_register(address, value, **self._kwargs) def write_registers(self, address, values): """Write registers.""" with self._lock: self._client.write_registers(address, values, **self._kwargs)
class ModbusHub: """Thread safe wrapper class for pymodbus.""" def __init__(self, client_config): """Initialize the Modbus hub.""" # generic configuration self._client = None self._in_error = False self._lock = threading.Lock() self._config_name = client_config[CONF_NAME] self._config_type = client_config[CONF_TYPE] self._config_port = client_config[CONF_PORT] self._config_timeout = client_config[CONF_TIMEOUT] self._config_delay = 0 Defaults.Timeout = 10 if self._config_type == "serial": # serial configuration self._config_method = client_config[CONF_METHOD] self._config_baudrate = client_config[CONF_BAUDRATE] self._config_stopbits = client_config[CONF_STOPBITS] self._config_bytesize = client_config[CONF_BYTESIZE] self._config_parity = client_config[CONF_PARITY] else: # network configuration self._config_host = client_config[CONF_HOST] self._config_delay = client_config[CONF_DELAY] if self._config_delay > 0: _LOGGER.warning("Parameter delay is accepted but not used in this version") @property def name(self): """Return the name of this hub.""" return self._config_name def _log_error(self, exception_error: ModbusException, error_state=True): log_text = "Pymodbus: " + str(exception_error) if self._in_error: _LOGGER.debug(log_text) else: _LOGGER.error(log_text) self._in_error = error_state def setup(self): """Set up pymodbus client.""" try: if self._config_type == "serial": self._client = ModbusSerialClient( method=self._config_method, port=self._config_port, baudrate=self._config_baudrate, stopbits=self._config_stopbits, bytesize=self._config_bytesize, parity=self._config_parity, timeout=self._config_timeout, retry_on_empty=True, ) elif self._config_type == "rtuovertcp": self._client = ModbusTcpClient( host=self._config_host, port=self._config_port, framer=ModbusRtuFramer, timeout=self._config_timeout, ) elif self._config_type == "tcp": self._client = ModbusTcpClient( host=self._config_host, port=self._config_port, timeout=self._config_timeout, ) elif self._config_type == "udp": self._client = ModbusUdpClient( host=self._config_host, port=self._config_port, timeout=self._config_timeout, ) except ModbusException as exception_error: self._log_error(exception_error, error_state=False) return # Connect device self.connect() def close(self): """Disconnect client.""" with self._lock: try: if self._client: self._client.close() self._client = None except ModbusException as exception_error: self._log_error(exception_error) return def connect(self): """Connect client.""" with self._lock: try: self._client.connect() except ModbusException as exception_error: self._log_error(exception_error, error_state=False) return def read_coils(self, unit, address, count): """Read coils.""" with self._lock: kwargs = {"unit": unit} if unit else {} try: result = self._client.read_coils(address, count, **kwargs) except ModbusException as exception_error: self._log_error(exception_error) return None if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): self._log_error(result) return None self._in_error = False return result def read_discrete_inputs(self, unit, address, count): """Read discrete inputs.""" with self._lock: kwargs = {"unit": unit} if unit else {} try: result = self._client.read_discrete_inputs(address, count, **kwargs) except ModbusException as exception_error: self._log_error(exception_error) return None if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): self._log_error(result) return None self._in_error = False return result def read_input_registers(self, unit, address, count): """Read input registers.""" with self._lock: kwargs = {"unit": unit} if unit else {} try: result = self._client.read_input_registers(address, count, **kwargs) except ModbusException as exception_error: self._log_error(exception_error) return None if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): self._log_error(result) return None self._in_error = False return result def read_holding_registers(self, unit, address, count): """Read holding registers.""" with self._lock: kwargs = {"unit": unit} if unit else {} try: result = self._client.read_holding_registers(address, count, **kwargs) except ModbusException as exception_error: self._log_error(exception_error) return None if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): self._log_error(result) return None self._in_error = False return result def write_coil(self, unit, address, value) -> bool: """Write coil.""" with self._lock: kwargs = {"unit": unit} if unit else {} try: result = self._client.write_coil(address, value, **kwargs) except ModbusException as exception_error: self._log_error(exception_error) return False if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): self._log_error(result) return False self._in_error = False return True def write_coils(self, unit, address, values) -> bool: """Write coil.""" with self._lock: kwargs = {"unit": unit} if unit else {} try: result = self._client.write_coils(address, values, **kwargs) except ModbusException as exception_error: self._log_error(exception_error) return False if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): self._log_error(result) return False self._in_error = False return True def write_register(self, unit, address, value) -> bool: """Write register.""" with self._lock: kwargs = {"unit": unit} if unit else {} try: result = self._client.write_register(address, value, **kwargs) except ModbusException as exception_error: self._log_error(exception_error) return False if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): self._log_error(result) return False self._in_error = False return True def write_registers(self, unit, address, values) -> bool: """Write registers.""" with self._lock: kwargs = {"unit": unit} if unit else {} try: result = self._client.write_registers(address, values, **kwargs) except ModbusException as exception_error: self._log_error(exception_error) return False if isinstance(result, (ExceptionResponse, IllegalFunctionRequest)): self._log_error(result) return False self._in_error = False return True