Example #1
0
    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'))
Example #2
0
    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))
Example #3
0
    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())
Example #4
0
    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()
Example #8
0
    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))
Example #9
0
 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'))
Example #10
0
 def testSyncUdpClientInstantiation(self):
     client = ModbusUdpClient()
     self.assertNotEqual(client, None)
Example #11
0
 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
Example #13
0
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
Example #14
0
    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))
Example #15
0
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()
Example #17
0
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
Example #18
0
    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
Example #19
0
    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
Example #20
0
 def testUdpClientIsSocketOpen(self):
     ''' Test the udp client is_socket_open method'''
     client = ModbusUdpClient()
     self.assertTrue(client.is_socket_open())
Example #21
0
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)
Example #22
0
 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)
Example #23
0
    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
Example #24
0
 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'))
Example #25
0
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()