def testUdpClientSend(self): ''' Test the udp client send method''' client = ModbusUdpClient() self.assertRaises(ConnectionException, lambda: client._send(None)) client.socket = mockSocket() self.assertEqual(0, client._send(None)) self.assertEqual(4, client._send('1234'))
def testUdpClientRecv(self): ''' Test the udp client receive method''' client = ModbusUdpClient() self.assertRaises(ConnectionException, lambda: client._recv(1024)) client.socket = mockSocket() self.assertEqual('', client._recv(0)) self.assertEqual('\x00'*4, client._recv(4))
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())
class AsynchronousUdpClient(Runner, unittest.TestCase): ''' These are the integration tests for the asynchronous udp client. ''' def setUp(self): ''' Initializes the test environment ''' super(Runner, self).setUp() self.client = ModbusClient() def tearDown(self): ''' Cleans up the test environment ''' self.client.close() super(Runner, self).tearDown()
class SynchronousUdpClient(Runner, unittest.TestCase): """ These are the integration tests for the synchronous udp client. """ def setUp(self): """ Initializes the test environment """ super(Runner, self).setUp() self.client = ModbusClient() def tearDown(self): """ Cleans up the test environment """ self.client.close() super(Runner, self).tearDown()
def setUp(self): """ Initializes the test environment """ super(Runner, self).setUp() self.client = ModbusClient()
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(b'\x00')) self.assertEqual(b'\x00', client._recv(1)) # connect/disconnect self.assertTrue(client.connect()) client.close() # already closed socket client.socket = False client.close() self.assertEqual("ModbusUdpClient(127.0.0.1:502)", str(client))
def testUdpClientAddressFamily(self): ''' Test the Udp client get address family method''' client = ModbusUdpClient() self.assertEqual(socket.AF_INET, client._get_address_family('127.0.0.1')) self.assertEqual(socket.AF_INET6, client._get_address_family('::1'))
def testSyncUdpClientInstantiation(self): client = ModbusUdpClient() self.assertNotEqual(client, None)
def connect(self): # if self.master and not self.master.socket: # self.master = None if self.master is None: self.commError = False try: # as in the following the port is None, no port is opened on creation of the (py)serial object if self.type == 1: # Serial ASCII from pymodbus.client.sync import ModbusSerialClient self.master = ModbusSerialClient(method='ascii', port=self.comport, baudrate=self.baudrate, bytesize=self.bytesize, parity=self.parity, stopbits=self.stopbits, retry_on_empty=True, timeout=self.timeout) elif self.type == 2: # Serial Binary from pymodbus.client.sync import ModbusSerialClient self.master = ModbusSerialClient(method='binary', port=self.comport, baudrate=self.baudrate, bytesize=self.bytesize, parity=self.parity, stopbits=self.stopbits, retry_on_empty=True, timeout=self.timeout) elif self.type == 3: # TCP from pymodbus.client.sync import ModbusTcpClient try: self.master = ModbusTcpClient( host=self.host, port=self.port, retry_on_empty=True, retries=1, timeout=0.9, #self.timeout ) self.readRetries = 0 except: self.master = ModbusTcpClient( host=self.host, port=self.port, ) elif self.type == 4: # UDP from pymodbus.client.sync import ModbusUdpClient try: self.master = ModbusUdpClient( host=self.host, port=self.port, retry_on_empty=True, retries=3, timeout=0.7, #self.timeout ) except: # older versions of pymodbus don't support the retries, timeout nor the retry_on_empty arguments self.master = ModbusUdpClient( host=self.host, port=self.port, ) else: # Serial RTU from pymodbus.client.sync import ModbusSerialClient self.master = ModbusSerialClient(method='rtu', port=self.comport, baudrate=self.baudrate, bytesize=self.bytesize, parity=self.parity, stopbits=self.stopbits, retry_on_empty=False, timeout=self.timeout) self.readRetries = 1 self.master.connect() self.adderror( QApplication.translate("Error Message", "Connected via MODBUS", None)) time.sleep(.5) # avoid possible hickups on startup except Exception as ex: _, _, exc_tb = sys.exc_info() self.adderror( (QApplication.translate("Error Message", "Modbus Error:", None) + " connect() {0}").format( str(ex)), exc_tb.tb_lineno)
def __configure_master(self, config=None): current_config = self.__config if config is None else config host = current_config[HOST_PARAMETER] if current_config.get( HOST_PARAMETER) is not None else self.__config.get( HOST_PARAMETER, "localhost") try: port = int(current_config[PORT_PARAMETER]) if current_config.get( PORT_PARAMETER) is not None else self.__config.get( int(PORT_PARAMETER), 502) except ValueError: port = current_config[PORT_PARAMETER] if current_config.get( PORT_PARAMETER) is not None else self.__config.get( PORT_PARAMETER, 502) baudrate = current_config[BAUDRATE_PARAMETER] if current_config.get( BAUDRATE_PARAMETER) is not None else self.__config.get( BAUDRATE_PARAMETER, 19200) timeout = current_config[TIMEOUT_PARAMETER] if current_config.get( TIMEOUT_PARAMETER) is not None else self.__config.get( TIMEOUT_PARAMETER, 35) method = current_config[METHOD_PARAMETER] if current_config.get( METHOD_PARAMETER) is not None else self.__config.get( METHOD_PARAMETER, "rtu") stopbits = current_config[STOPBITS_PARAMETER] if current_config.get( STOPBITS_PARAMETER) is not None else self.__config.get( STOPBITS_PARAMETER, Defaults.Stopbits) bytesize = current_config[BYTESIZE_PARAMETER] if current_config.get( BYTESIZE_PARAMETER) is not None else self.__config.get( BYTESIZE_PARAMETER, Defaults.Bytesize) parity = current_config[PARITY_PARAMETER] if current_config.get( PARITY_PARAMETER) is not None else self.__config.get( PARITY_PARAMETER, Defaults.Parity) strict = current_config[STRICT_PARAMETER] if current_config.get( STRICT_PARAMETER) is not None else self.__config.get( STRICT_PARAMETER, True) rtu = ModbusRtuFramer if current_config.get( METHOD_PARAMETER) == "rtu" or ( current_config.get(METHOD_PARAMETER) is None and self.__config.get(METHOD_PARAMETER) == "rtu") else ModbusSocketFramer if current_config.get(TYPE_PARAMETER) == 'tcp' or ( current_config.get(TYPE_PARAMETER) is None and self.__config.get(TYPE_PARAMETER) == "tcp"): master = ModbusTcpClient(host, port, rtu, timeout=timeout) elif current_config.get(TYPE_PARAMETER) == 'udp' or ( current_config.get(TYPE_PARAMETER) is None and self.__config.get(TYPE_PARAMETER) == "udp"): master = ModbusUdpClient(host, port, rtu, timeout=timeout) elif current_config.get(TYPE_PARAMETER) == 'serial' or ( current_config.get(TYPE_PARAMETER) is None and self.__config.get(TYPE_PARAMETER) == "serial"): master = ModbusSerialClient(method=method, port=port, timeout=timeout, baudrate=baudrate, stopbits=stopbits, bytesize=bytesize, parity=parity, strict=strict) else: raise Exception("Invalid Modbus transport type.") available_functions = { 1: master.read_coils, 2: master.read_discrete_inputs, 3: master.read_holding_registers, 4: master.read_input_registers, 5: master.write_coil, 6: master.write_register, 15: master.write_coils, 16: master.write_registers, } return master, available_functions
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] self._config_retries = client_config[CONF_RETRIES] self._config_retry_on_empty = client_config[CONF_RETRY_ON_EMPTY] 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, text: str, error_state=True): log_text = f"Pymodbus: {text}" 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, reset_socket=self._config_reset_socket, ) except ModbusException as exception_error: self._log_error(str(exception_error), error_state=False) return False async with self._lock: if not await self.hass.async_add_executor_job(self._pymodbus_connect): self._log_error("initial connect failed, no retry", error_state=False) return False 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 ) return True @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(str(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: return self._client.connect() except ModbusException as exception_error: self._log_error(str(exception_error), error_state=False) return 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(str(exception_error)) return None if not hasattr(result, self._call_type[use_call][ENTRY_ATTR]): self._log_error(str(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 if not self._client.is_socket_open(): return None async with self._lock: result = await self.hass.async_add_executor_job( self._pymodbus_call, unit, address, value, use_call ) if self._config_type == "serial": # small delay until next request/response await asyncio.sleep(30 / 1000) return result
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))
def mqtt_on_connect(client, userdata, flags, rc): if rc == 0 and client.is_connected == False: # birth message mqtt_client.publish(MQTT_AVAILABILITY_TOPIC, "online", retain=True) # set flag client.is_connected = True def mqtt_on_disconnect(client, userdata, rc): client.is_connected = False # initialize mqtt client modbus_client = ModbusUdpClient(MODBUS_SERVER_HOST) mqtt_client = mqtt.Client(MQTT_CLIENT_NAME) mqtt_client.is_connected = False mqtt_client.on_connect = mqtt_on_connect mqtt_client.on_disconnect = mqtt_on_disconnect mqtt_client.username_pw_set(username="******", password="******") # last will mqtt_client.will_set(MQTT_AVAILABILITY_TOPIC, "offline", retain=True) # connect mqtt_client.connect(MQTT_SERVER_HOST) mqtt_client.loop_start() # mqtt related
def setUp(self): ''' Initializes the test environment ''' super(Runner, self).setUp() self.client = ModbusClient()
class CtModbus(Ctui): """Commands that users may use at the application prompt.""" name = 'ctmodbus' version = '0.5' description = 'a security professional\'s swiss army knife for interacting with Modbus devices' prompt = 'ctmodbus> ' session = None unit_id = 1 statusbar = 'Session:{}'.format(session) def do_debug(self, args, output_text): message_dialog( title = 'Debug', text = str(eval(args)) ) def validate_serial_device(self, device): devices = [x.device for x in serial.tools.list_ports.comports()] devices_pretty = '\n'.join([x for x in devices]) assert (device in devices), '{} is not in: \n{}'.format(device, devices_pretty) return device def do_connect_ascii(self, args, output_text): """Connect to a Modbus ASCII serial device""" assert (self.session == None), 'Session already open. Close first.' # ToDo assert session type device = self.validate_serial_device(args) self.session = ModbusSerialClient(method='ascii', port=device, timeout=1) message_dialog( title = 'Success', text = 'Session opened with {}'.format(device) ) def do_connect_rtu(self, args, output_text): """Connect to a Modbus RTU serial device""" assert (self.session == None), 'Session already open. Close first.' # ToDo assert session type device = self.validate_serial_device(args) self.session = ModbusSerialClient(method='rtu', port=device, timeout=1) message_dialog( title = 'Success', text = 'Session opened with {}'.format(device) ) def parse_ip_port(self, ip_port): parts = ip_port.split(':') assert (0 < len(parts) < 3), 'Must be in format ip or host or ip:port or host:port' host = parts[0] port = 502 if len(parts) == 2: port = int(parts[1]) return host, port def do_connect_tcp(self, args, output_text): """Connect to a Modbus TCP device""" assert (self.session == None), 'Session already open. Close first.' # ToDo assert session type host, port = self.parse_ip_port(args) self.session = ModbusTcpClient(host, port) message_dialog( title = 'Success', text = 'Session opened with {}:{}'.format(host, port) ) def do_connect_udp(self, args, output_text): """Connect to a Modbus UDP device""" assert (self.session == None), 'Session already open. Close first.' # ToDo assert session type host, port = self.parse_ip_port(args) self.session = ModbusUdpClient(host, port) message_dialog( title = 'Success', text = 'Session opened with {}:{}'.format(host, port) ) def do_close(self, args, output_text): """Close a session.""" assert (self.session), 'There is not an open session. Connect to one first' # ToDo assert session type self.session.close() message_dialog( title = 'Success', text = 'Session closed with {}'.format(self.session) ) self.session = None return None def do_read_id(self, args, output_text): """Read device identification data.""" assert (self.session), 'There is not an open session. Connect to one first' # ToDo assert session type request = ReadDeviceInformationRequest(unit=1) response = self.session.execute(request) output_text += str(response) + '\n' return output_text def log_and_output_bits(self, desc, start, stop, results): """Log in project database and output to screen""" date, time = str(datetime.today()).split() output_text = '{} {} - {}'.format(date, time, desc) i = 0 for address in range(start, stop): # Finish line after 32 bits of output if i % 32 == 0: output_text += '\n{:>5}: '.format(address) i += 1 # Print next bit output_text += str(results[address]) # Print spaces ever 4 bits if i % 4 == 0: output_text += ' ' # TODO: Add to self.storage output_text += '\n' return output_text def log_and_output_words(self, desc, start, stop, results): """Log in project database and output to screen""" date, time = str(datetime.today()).split() output_text = '{} {} - {}'.format(date, time, desc) i = 0 for address in range(start, stop): # Finish line after 8 words of output if i % 8 == 0: output_text += '\n{:>5}: '.format(address) i += 1 # Print next word output_text += '{:04x}'.format(results[address]) + ' ' # TODO: Add to self.storage output_text += '\n' return output_text def response_message_dialog(self, message, results): la, lr, fa = None, None, None #last_addres, last_result, first_address table = [['Addr', 'Int', 'HEX', 'ASCII']] for address, result in results.items(): if address - 1 == la and result == lr: if fa == None: fa = la elif la != None: if fa == None: table.append([la, lr, '{:04x}'.format(lr), str(chr(lr))]) fa = None else: s = '{0}-{1}'.format(fa, la) table.append([s, lr, '{:04x}'.format(lr), str(chr(lr))]) fa = None la, lr = address, result # Print final output from for loop if fa == None: table.append([la, lr, '{:04x}'.format(lr), str(chr(lr))]) else: s = '{0}-{1}'.format(fa, la) table.append([s, lr, '{:04x}'.format(lr), str(chr(lr))]) message += tabulate(table, headers='firstrow', tablefmt='simple') message_dialog(title='Success', text=message) def read_common(self, args): assert (self.session), 'There is not an open session. Connect to one first' # TODO assert session type parts = args.split(maxsplit=1) csr = parts[0] max_count = 100 if len(parts) == 2: assert (parts[1].isdigit()), 'max_count must be a digit' max_count = int(parts[1]) loops = Loops(csr, minimum=0, maximum=65535) for loop in loops.max_count(max_count): yield loop.values() def do_read_discrete_inputs(self, args, output_text): """Read digital inputs in format: 30,50,70-99,105.""" desc = 'Modbus Function 2, Read Discrete Inputs' results = {} for start, stop, count in self.read_common(args): response = self.session.read_discrete_inputs(start, count, unit=self.unit_id) for address, result in zip(range(start, stop), response.bits): results[address] = int(result) output_text += self.log_and_output_bits(desc, start, stop, results) csr = args.split()[0] message = '{}: {}\n\n'.format(desc, csr) self.response_message_dialog(message, results) return output_text def do_read_coils(self, args, output_text): """Read digital outputs in format: 30,50,70-99,105.""" desc = 'Modbus Function 1, Read Coils' results = {} for start, stop, count in self.read_common(args): response = self.session.read_coils(start, count, unit=self.unit_id) for address, result in zip(range(start, stop), response.bits): results[address] = int(result) output_text += self.log_and_output_bits(desc, start, stop, results) csr = args.split()[0] message = '{}: {}\n\n'.format(desc, csr) self.response_message_dialog(message, results) return output_text def do_read_input_registers(self, args, output_text): """Read analog inputs or internal registers in format: 30,50,70-99,105.""" desc = 'Modbus Function 4, Read Input Registers' results = {} for start, stop, count in self.read_common(args): response = self.session.read_input_registers(start, count, unit=self.unit_id) for address, result in zip(range(start, stop), response.registers): results[address] = result output_text += self.log_and_output_words(desc, start, stop, results) csr = args.split()[0] message = '{}: {}\n\n'.format(desc, csr) self.response_message_dialog(message, results) return output_text def do_read_holding_registers(self, args, output_text): """Read digital inputs in format: 30,50,70-99,105.""" desc = 'Modbus Function 3, Read Holding Registers' results = {} for start, stop, count in self.read_common(args): response = self.session.read_holding_registers(start, count, unit=self.unit_id) for address, result in zip(range(start, stop), response.registers): results[address] = result output_text += self.log_and_output_words(desc, start, stop, results) csr = args.split()[0] message = '{}: {}\n\n'.format(desc, csr) self.response_message_dialog(message, results) return output_text def do_write_registers(self, args, output_text): """Write to registers in format: <address> <int>...""" start, values = args.split(maxsplit=1) assert (start.isdigit()), 'start must be an integer' int_start = int(start) try: list_int_values = [int(x) for x in values.split()] except: raise AssertionError('List of values to write must be decimals') if len(list_int_values) == 1: self.session.write_register(int_start, list_int_values[0], unit=self.unit_id) desc = 'Modbus Function 5, Write Single Register' else: self.session.write_registers(int_start, list_int_values, unit=self.unit_id) desc = 'Modbus Function 5, Write Multiple Registers' message_dialog(title='Success', text=f'Wrote {list_int_values} starting at {int_start}') results = {} stop = int_start + len(list_int_values) for i in range(len(list_int_values)): results[int_start + i] = list_int_values[i] output_text += self.log_and_output_words(desc, int_start, stop, results) return output_text def do_write_coils(self, args, output_text): """Write to registers in format: <address> <int>...""" desc = 'Modbus Function 3, Read Holding Registers' start, values = args.split(maxsplit=1) assert (start.isdigit()), 'start must be an integer' int_start = int(start) try: list_bool_values = [bool(int(x)) for x in values.replace(' ', '')] except: raise AssertionError('List of values to write must be decimals') if len(list_bool_values) == 1: self.session.write_coil(int_start, list_bool_values[0], unit=self.unit_id) desc = 'Modbus Function 5, Write Single Coil' else: self.session.write_coils(int_start, list_bool_values, unit=self.unit_id) desc = 'Modbus Function 15, Write Multiple Coils' message_dialog(title='Success', text=f'Wrote {list_bool_values} starting at {int_start}') results = {} stop = int_start + len(list_bool_values) for i in range(len(list_bool_values)): results[int_start + i] = int(list_bool_values[i]) output_text += self.log_and_output_bits(desc, int_start, stop, results) return output_text
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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, 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, retries=self._config_retries, retry_on_empty=self._config_retry_on_empty, reset_socket=self._config_reset_socket, ) except ModbusException as exception_error: self._log_error(str(exception_error), error_state=False) return False async with self._lock: if not await self.hass.async_add_executor_job(self._pymodbus_connect): self._log_error("initial connect failed, no retry", error_state=False) return False 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 ) return True
def __configure_master(self, config=None): current_config = self.__config if config is None else config master_config = dict() master_config[ "host"] = current_config[HOST_PARAMETER] if current_config.get( HOST_PARAMETER) is not None else self.__config.get( HOST_PARAMETER, "localhost") try: master_config["port"] = int( current_config[PORT_PARAMETER]) if current_config.get( PORT_PARAMETER) is not None else self.__config.get( int(PORT_PARAMETER), 502) except ValueError: master_config[ "port"] = current_config[PORT_PARAMETER] if current_config.get( PORT_PARAMETER) is not None else self.__config.get( PORT_PARAMETER, 502) master_config["baudrate"] = current_config[ BAUDRATE_PARAMETER] if current_config.get( BAUDRATE_PARAMETER) is not None else self.__config.get( BAUDRATE_PARAMETER, 19200) master_config["timeout"] = current_config[ TIMEOUT_PARAMETER] if current_config.get( TIMEOUT_PARAMETER) is not None else self.__config.get( TIMEOUT_PARAMETER, 35) master_config[ "method"] = current_config[METHOD_PARAMETER] if current_config.get( METHOD_PARAMETER) is not None else self.__config.get( METHOD_PARAMETER, "rtu") master_config["stopbits"] = current_config[ STOPBITS_PARAMETER] if current_config.get( STOPBITS_PARAMETER) is not None else self.__config.get( STOPBITS_PARAMETER, Defaults.Stopbits) master_config["bytesize"] = current_config[ BYTESIZE_PARAMETER] if current_config.get( BYTESIZE_PARAMETER) is not None else self.__config.get( BYTESIZE_PARAMETER, Defaults.Bytesize) master_config[ "parity"] = current_config[PARITY_PARAMETER] if current_config.get( PARITY_PARAMETER) is not None else self.__config.get( PARITY_PARAMETER, Defaults.Parity) master_config[ "strict"] = current_config[STRICT_PARAMETER] if current_config.get( STRICT_PARAMETER) is not None else self.__config.get( STRICT_PARAMETER, True) master_config["rtu"] = ModbusRtuFramer if current_config.get( METHOD_PARAMETER) == "rtu" or ( current_config.get(METHOD_PARAMETER) is None and self.__config.get(METHOD_PARAMETER) == "rtu") else ModbusSocketFramer if self.__previous_master_config != master_config: self.__previous_master_config = master_config if current_config.get(TYPE_PARAMETER) == 'tcp' or ( current_config.get(TYPE_PARAMETER) is None and self.__config.get(TYPE_PARAMETER) == "tcp"): master = ModbusTcpClient(master_config["host"], master_config["port"], master_config["rtu"], timeout=master_config["timeout"]) elif current_config.get(TYPE_PARAMETER) == 'udp' or ( current_config.get(TYPE_PARAMETER) is None and self.__config.get(TYPE_PARAMETER) == "udp"): master = ModbusUdpClient(master_config["host"], master_config["port"], master_config["rtu"], timeout=master_config["timeout"]) elif current_config.get(TYPE_PARAMETER) == 'serial' or ( current_config.get(TYPE_PARAMETER) is None and self.__config.get(TYPE_PARAMETER) == "serial"): master = ModbusSerialClient(method=master_config["method"], port=master_config["port"], timeout=master_config["timeout"], baudrate=master_config["baudrate"], stopbits=master_config["stopbits"], bytesize=master_config["bytesize"], parity=master_config["parity"], strict=master_config["strict"]) else: raise Exception("Invalid Modbus transport type.") else: master = self.__current_master available_functions = { 1: master.read_coils, 2: master.read_discrete_inputs, 3: master.read_holding_registers, 4: master.read_input_registers, 5: master.write_coil, 6: master.write_register, 15: master.write_coils, 16: master.write_registers, } return master, available_functions
def testUdpClientIsSocketOpen(self): ''' Test the udp client is_socket_open method''' client = ModbusUdpClient() self.assertTrue(client.is_socket_open())
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, 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, ) 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)
def testUdpClientRepr(self): client = ModbusUdpClient() rep = "<{} at {} socket={}, ipaddr={}, port={}, timeout={}>".format( client.__class__.__name__, hex(id(client)), client.socket, client.host, client.port, client.timeout) self.assertEqual(repr(client), rep)
def __configure_master(self, config=None): current_config = self.__config if config is None else config host = current_config['host'] if current_config.get( "host") is not None else self.__config.get("host", "localhost") try: port = int(current_config['port']) if current_config.get( "port") is not None else self.__config.get(int("port"), 502) except ValueError: port = current_config['port'] if current_config.get( "port") is not None else self.__config.get("port", 502) baudrate = current_config['baudrate'] if current_config.get( 'baudrate') is not None else self.__config.get('baudrate', 19200) timeout = current_config['timeout'] if current_config.get( "timeout") is not None else self.__config.get("timeout", 35) method = current_config['method'] if current_config.get( 'method') is not None else self.__config.get('method', 'rtu') stopbits = current_config['stopbits'] if current_config.get( 'stopbits') is not None else self.__config.get( 'stopbits', Defaults.Stopbits) bytesize = current_config['bytesize'] if current_config.get( 'bytesize') is not None else self.__config.get( 'bytesize', Defaults.Bytesize) parity = current_config['parity'] if current_config.get( 'parity') is not None else self.__config.get( 'parity', Defaults.Parity) strict = current_config["strict"] if current_config.get( "strict") is not None else self.__config.get("strict", True) rtu = ModbusRtuFramer if current_config.get("method") == "rtu" or ( current_config.get("method") is None and self.__config.get("method") == "rtu") else ModbusSocketFramer if current_config.get('type') == 'tcp' or ( current_config.get("type") is None and self.__config.get("type") == "tcp"): master = ModbusTcpClient(host, port, rtu, timeout=timeout) elif current_config.get('type') == 'udp' or ( current_config.get("type") is None and self.__config.get("type") == "udp"): master = ModbusUdpClient(host, port, rtu, timeout=timeout) elif current_config.get('type') == 'serial' or ( current_config.get("type") is None and self.__config.get("type") == "serial"): master = ModbusSerialClient(method=method, port=port, timeout=timeout, baudrate=baudrate, stopbits=stopbits, bytesize=bytesize, parity=parity, strict=strict) else: raise Exception("Invalid Modbus transport type.") available_functions = { 1: master.read_coils, 2: master.read_discrete_inputs, 3: master.read_holding_registers, 4: master.read_input_registers, 5: master.write_coil, 6: master.write_register, 15: master.write_coils, 16: master.write_registers, } return master, available_functions
import paho.mqtt.client as mqtt from functools import partial import logging import threading import queue import time import config import entity logger = logging.getLogger('gateway') logger.setLevel(logging.DEBUG) # MODBUS modbus_udp_client = ModbusUdpClient(config.MODBUS_SERVER_HOST, timeout=3) modbus_tcp_client = ModbusTcpClient(config.MODBUS_SERVER_HOST) # MQTT mqtt_client = mqtt.Client(config.MQTT_CLIENT_NAME) mqtt_client.username_pw_set(username=config.MQTT_USER, password=config.MQTT_PASSWORD) # last will mqtt_client.will_set(config.MQTT_AVAILABILITY_TOPIC, "offline", retain=True) # connect, loop_start will handle reconnections mqtt_client.connect(config.MQTT_HOST) mqtt_client.loop_start()