Example #1
0
    def _sendPacket(self, message, callback):
        """
        Sends packets on the bus with 3.5char delay between frames
        :param message: Message to be sent over the bus
        :return:
        """
        @gen.coroutine
        def sleep(timeout):
            yield gen.sleep(timeout)

        try:
            waiting = self.stream.connection.in_waiting
            if waiting:
                result = self.stream.connection.read(waiting)
                LOGGER.info("Cleanup recv buffer before send: " +
                            hexlify_packets(result))
        except OSError as e:
            self.transaction.getTransaction(
                message.transaction_id).set_exception(ModbusIOException(e))
            return

        start = time.time()
        if self.last_frame_end:
            waittime = self.last_frame_end + self.silent_interval - start
            if waittime > 0:
                LOGGER.debug("Waiting for 3.5 char before next send - %f ms",
                             waittime)
                sleep(waittime)

        self.state = ModbusTransactionState.SENDING
        LOGGER.debug("send: " + hexlify_packets(message))
        self.stream.write(message, callback)
Example #2
0
 def _transact(self, packet, response_length, full=False):
     """
     Does a Write and Read transaction
     :param packet: packet to be sent
     :param response_length:  Expected response length
     :param full: the target device was notorious for its no response. Dont
         waste time this time by partial querying
     :return: response
     """
     last_exception = None
     try:
         self.client.connect()
         packet = self.client.framer.buildPacket(packet)
         if _logger.isEnabledFor(logging.DEBUG):
             _logger.debug("SEND: " + hexlify_packets(packet))
         size = self._send(packet)
         if size:
             _logger.debug("Changing transaction state from 'SENDING' "
                           "to 'WAITING FOR REPLY'")
             self.client.state = ModbusTransactionState.WAITING_FOR_REPLY
         result = self._recv(response_length, full)
         if _logger.isEnabledFor(logging.DEBUG):
             _logger.debug("RECV: " + hexlify_packets(result))
     except (socket.error, ModbusIOException,
             InvalidMessageReceivedException) as msg:
         self.client.close()
         _logger.debug("Transaction failed. (%s) " % msg)
         last_exception = msg
         result = b''
     return result, last_exception
Example #3
0
 def _transact(self, packet, response_length, full=False):
     """
     Does a Write and Read transaction
     :param packet: packet to be sent
     :param response_length:  Expected response length
     :param full: the target device was notorious for its no response. Dont
         waste time this time by partial querying
     :return: response
     """
     last_exception = None
     try:
         self.client.connect()
         packet = self.client.framer.buildPacket(packet)
         if _logger.isEnabledFor(logging.DEBUG):
             _logger.debug("SEND: " + hexlify_packets(packet))
         size = self._send(packet)
         if size:
             _logger.debug("Changing transaction state from 'SENDING' "
                           "to 'WAITING FOR REPLY'")
             self.client.state = ModbusTransactionState.WAITING_FOR_REPLY
         result = self._recv(response_length, full)
         if _logger.isEnabledFor(logging.DEBUG):
             _logger.debug("RECV: " + hexlify_packets(result))
     except (socket.error, ModbusIOException,
             InvalidMessageReceivedException) as msg:
         self.client.close()
         _logger.debug("Transaction failed. (%s) " % msg)
         last_exception = msg
         result = b''
     return result, last_exception
Example #4
0
 def _dump(self, data, direction):
     fd = self._debugfd if self._debugfd else sys.stdout
     try:
         fd.write(hexlify_packets(data))
     except Exception as e:
         self._logger.debug(hexlify_packets(data))
         self._logger.exception(e)
Example #5
0
    def _send(self, request):
        """ Sends data on the underlying socket

        If receive buffer still holds some data then flush it.

        Sleep if last send finished less than 3.5 character
        times ago.

        :param request: The encoded request to send
        :return: The number of bytes written
        """
        if not self.socket:
            raise ConnectionException(self.__str__())
        if request:
            try:
                waitingbytes = self._in_waiting()
                if waitingbytes:
                    result = self.socket.read(waitingbytes)
                    if self.state == ModbusTransactionState.RETRYING:
                        _logger.debug("Sending available data in recv "
                                      "buffer {}".format(
                                          hexlify_packets(result)))
                        return result
                    if _logger.isEnabledFor(logging.WARNING):
                        _logger.warning("Cleanup recv buffer before "
                                        "send: " + hexlify_packets(result))
            except NotImplementedError:
                pass
            if self.state != ModbusTransactionState.SENDING:
                _logger.debug("New Transaction state 'SENDING'")
                self.state = ModbusTransactionState.SENDING
            size = self.socket.write(request)
            return size
        return 0
Example #6
0
 def _dump(self, data, direction):
     fd = self._debugfd if self._debugfd else sys.stdout
     try:
         fd.write(hexlify_packets(data))
     except Exception as e:
         self._logger.debug(hexlify_packets(data))
         self._logger.exception(e)
Example #7
0
    def _transact(self, packet, response_length, full=False, broadcast=False):
        """
        Does a Write and Read transaction
        :param packet: packet to be sent
        :param response_length:  Expected response length
        :param full: the target device was notorious for its no response. Dont
            waste time this time by partial querying
        :return: response
        """
        last_exception = None
        try:
            self.client.connect()
            packet = self.client.framer.buildPacket(packet)
            if _logger.isEnabledFor(logging.DEBUG):
                _logger.debug("SEND: " + hexlify_packets(packet))
            size = self._send(packet)
            if isinstance(
                    size, bytes
            ) and self.client.state == ModbusTransactionState.RETRYING:
                _logger.debug("Changing transaction state from "
                              "'RETRYING' to 'PROCESSING REPLY'")
                self.client.state = ModbusTransactionState.PROCESSING_REPLY
                return size, None
            if broadcast:
                if size:
                    _logger.debug("Changing transaction state from 'SENDING' "
                                  "to 'TRANSACTION_COMPLETE'")
                    self.client.state = ModbusTransactionState.TRANSACTION_COMPLETE
                return b'', None
            if size:
                _logger.debug("Changing transaction state from 'SENDING' "
                              "to 'WAITING FOR REPLY'")
                self.client.state = ModbusTransactionState.WAITING_FOR_REPLY
            if hasattr(self.client, "handle_local_echo"
                       ) and self.client.handle_local_echo is True:
                local_echo_packet = self._recv(size, full)
                if local_echo_packet != packet:
                    return b'', "Wrong local echo"
            result = self._recv(response_length, full)
            # result2 = self._recv(response_length, full)
            if _logger.isEnabledFor(logging.DEBUG):
                _logger.debug("RECV: " + hexlify_packets(result))

        except (socket.error, ModbusIOException,
                InvalidMessageReceivedException) as msg:
            if self.reset_socket:
                self.client.close()
            _logger.debug("Transaction failed. (%s) " % msg)
            last_exception = msg
            result = b''
        return result, last_exception
Example #8
0
 def getRawFrame(self):
     """
     Returns the complete buffer
     """
     _logger.debug("Getting Raw Frame - "
                   "{}".format(hexlify_packets(self._buffer)))
     return self._buffer
Example #9
0
    def _send(self, request):
        """ Sends data on the underlying socket

        If receive buffer still holds some data then flush it.

        Sleep if last send finished less than 3.5 character
        times ago.

        :param request: The encoded request to send
        :return: The number of bytes written
        """
        if not self.socket:
            raise ConnectionException(self.__str__())
        if request:
            try:
                waitingbytes = self._in_waiting()
                if waitingbytes:
                    result = self.socket.read(waitingbytes)
                    if _logger.isEnabledFor(logging.WARNING):
                        _logger.warning("Cleanup recv buffer before "
                                        "send: " + hexlify_packets(result))
            except NotImplementedError:
                pass

            size = self.socket.write(request)
            return size
        return 0
Example #10
0
    def execute(self, request=None):
        """
        Executes a transaction
        :param request: Request to be written on to the bus
        :return:
        """
        request.transaction_id = self.transaction.getNextTID()

        def callback(*args):
            LOGGER.debug("in callback - {}".format(request.transaction_id))
            while True:
                waiting = self.stream.connection.in_waiting
                if waiting:
                    data = self.stream.connection.read(waiting)
                    LOGGER.debug("recv: " + hexlify_packets(data))
                    unit = self.framer.decode_data(data).get("uid", 0)
                    self.framer.processIncomingPacket(
                        data,
                        self._handle_response,
                        unit,
                        tid=request.transaction_id)
                    break

        packet = self.framer.buildPacket(request)
        LOGGER.debug("send: " + hexlify_packets(packet))
        self.stream.write(packet, callback=callback)
        f = self._build_response(request.transaction_id)
        return f
Example #11
0
        def _on_receive(fd, events):
            LOGGER.debug("_on_receive: %s, %s", fd, events)

            try:
                waiting = self.stream.connection.in_waiting
                if waiting:
                    LOGGER.debug("waiting = %d", waiting)
                    data = self.stream.connection.read(waiting)
                    LOGGER.debug(
                        "recv: " + hexlify_packets(data))
            except OSError as ex:
                _clear_timer()
                self.close()
                self.transaction.getTransaction(request.transaction_id).set_exception(ModbusIOException(ex))
                return

            self.framer.addToFrame(data)

            # check if we have regular frame or modbus exception
            fcode = self.framer.decode_data(self.framer.getRawFrame()).get("fcode", 0)
            if fcode and (
                  (fcode > 0x80 and len(self.framer.getRawFrame()) == exception_response_length)
                or
                  (len(self.framer.getRawFrame()) == expected_response_length)
            ):
                _clear_timer()
                self.io_loop.remove_handler(fd)
                self.state = ModbusTransactionState.IDLE
                self.framer.processIncomingPacket(
                    b'',            # already sent via addToFrame()
                    self._handle_response,
                    0,              # don't care for `single=True`
                    single=True,
                    tid=request.transaction_id
                )
    def handle(self, event):
        reset_frame = False
        try:
            units = self.context.slaves()
            if isinstance(event, QuicEvent):
                # addr is populated when talking over UDP
                data = event.data
            else:
                print("event is none")
                return

            if not isinstance(units, (list, tuple)):
                units = [units]
            # if broadcast is enabled make sure to
            # process requests to address 0
            if self.broadcast_enable:  # pragma: no cover
                if 0 not in units:
                    units.append(0)

            if _logger.isEnabledFor(logging.DEBUG):
                _logger.debug('Handling data: ' + hexlify_packets(data))

            single = self.context.single
            self.framer.processIncomingPacket(
                data=data,
                callback=lambda x: self.execute(x, event),
                unit=units,
                single=single)
        finally:
            if reset_frame:
                self.framer.resetFrame()
                reset_frame = False
Example #13
0
 def getRawFrame(self):
     """
     Returns the complete buffer
     """
     _logger.debug("Getting Raw Frame - "
                   "{}".format(hexlify_packets(self._buffer)))
     return self._buffer
Example #14
0
 def handle(self):
     """ Callback when we receive any data
     """
     reset_frame = False
     while self.running:
         try:
             data, self.socket = self.request
             if not data:
                 self.running = False
                 data = b''
             if _logger.isEnabledFor(logging.DEBUG):
                 _logger.debug('Handling data: ' + hexlify_packets(data))
             # if not self.server.control.ListenOnly:
             units = self.server.context.slaves()
             single = self.server.context.single
             self.framer.processIncomingPacket(data, self.execute,
                                               units, single=single)
         except socket.timeout: pass
         except socket.error as msg:
             _logger.error("Socket error occurred %s" % msg)
             self.running = False
             reset_frame = True
         except Exception as msg:
             _logger.error(msg)
             self.running = False
             reset_frame = True
         finally:
             # Reset data after processing
             self.request = (None, self.socket)
             if reset_frame:
                 self.framer.resetFrame()
                 reset_frame = False
Example #15
0
 def handle(self):
     """ Callback when we receive any data
     """
     reset_frame = False
     while self.running:
         try:
             data, self.socket = self.request
             if not data:
                 self.running = False
                 data = b''
             if _logger.isEnabledFor(logging.DEBUG):
                 _logger.debug('Handling data: ' + hexlify_packets(data))
             # if not self.server.control.ListenOnly:
             units = self.server.context.slaves()
             single = self.server.context.single
             self.framer.processIncomingPacket(data, self.execute,
                                               units, single=single)
         except socket.timeout:
             pass
         except socket.error as msg:
             _logger.error("Socket error occurred %s" % msg)
             self.running = False
             reset_frame = True
         except Exception as msg:
             _logger.error(msg)
             self.running = False
             reset_frame = True
         finally:
             # Reset data after processing
             self.request = (None, self.socket)
             if reset_frame:
                 self.framer.resetFrame()
                 reset_frame = False
Example #16
0
    def _dataReceived(self, data):
        ''' Get response, check for valid message, decode result

        :param data: The data returned from the server
        '''
        _logger.debug("recv: " + hexlify_packets(data))
        unit = self.framer.decode_data(data).get("unit", 0)
        self.framer.processIncomingPacket(data, self._handleResponse, unit=unit)
Example #17
0
    def handle(self):
        """Callback when we receive any data, until self.running becomes False.
        Blocks indefinitely awaiting data.  If shutdown is required, then the
        global socket.settimeout(<seconds>) may be used, to allow timely
        checking of self.running.  However, since this also affects socket
        connects, if there are outgoing socket connections used in the same
        program, then these will be prevented, if the specfied timeout is too
        short.  Hence, this is unreliable.

        To respond to Modbus...Server.server_close() (which clears each
        handler's self.running), derive from this class to provide an
        alternative handler that awakens from time to time when no input is
        available and checks self.running.
        Use Modbus...Server( handler=... ) keyword to supply the alternative
        request handler class.

        """
        reset_frame = False
        while self.running:
            try:
                units = self.server.context.slaves()
                data = self.request.recv(1024)
                if not data:
                    self.running = False
                else:
                    if not isinstance(units, (list, tuple)):
                        units = [units]
                    # if broadcast is enabled make sure to
                    # process requests to address 0
                    if self.server.broadcast_enable:
                        if 0 not in units:
                            units.append(0)

                if _logger.isEnabledFor(logging.DEBUG):
                    _logger.debug('Handling data: ' + hexlify_packets(data))
                single = self.server.context.single
                self.framer.processIncomingPacket(data,
                                                  self.execute,
                                                  units,
                                                  single=single)
            except socket.timeout as msg:
                if _logger.isEnabledFor(logging.DEBUG):
                    _logger.debug("Socket timeout occurred %s", msg)
                reset_frame = True
            except socket.error as msg:
                _logger.error("Socket error occurred %s" % msg)
                self.running = False
            except:
                _logger.error("Socket exception occurred "
                              "%s" % traceback.format_exc())
                self.running = False
                reset_frame = True
            finally:
                if reset_frame:
                    self.framer.resetFrame()
                    reset_frame = False
Example #18
0
 def _execute(self, request, **kwargs):
     """
     Starts the producer to send the next request to
     consumer.write(Frame(request))
     """
     request.transaction_id = self.transaction.getNextTID()
     packet = self.framer.buildPacket(request)
     _logger.debug("send: " + hexlify_packets(packet))
     self.write_transport(packet)
     return self._buildResponse(request.transaction_id)
Example #19
0
    def handle(self):
        """Callback when we receive any data, until self.running becomes False.
        Blocks indefinitely awaiting data.  If shutdown is required, then the
        global socket.settimeout(<seconds>) may be used, to allow timely
        checking of self.running.  However, since this also affects socket
        connects, if there are outgoing socket connections used in the same
        program, then these will be prevented, if the specfied timeout is too
        short.  Hence, this is unreliable.

        To respond to Modbus...Server.server_close() (which clears each
        handler's self.running), derive from this class to provide an
        alternative handler that awakens from time to time when no input is
        available and checks self.running.
        Use Modbus...Server( handler=... ) keyword to supply the alternative
        request handler class.

        """
        reset_frame = False
        while self.running:
            try:
                units = self.server.context.slaves()
                data = self.request.recv(1024)
                if not data:
                    self.running = False
                else:
                    if not isinstance(units, (list, tuple)):
                        units = [units]
                    # if broadcast is enabled make sure to
                    # process requests to address 0
                    if self.server.broadcast_enable:
                        if 0 not in units:
                            units.append(0)

                if _logger.isEnabledFor(logging.DEBUG):
                    _logger.debug('Handling data: ' + hexlify_packets(data))
                single = self.server.context.single
                self.framer.processIncomingPacket(data, self.execute, units,
                                                  single=single)
            except socket.timeout as msg:
                if _logger.isEnabledFor(logging.DEBUG):
                    _logger.debug("Socket timeout occurred %s", msg)
                reset_frame = True
            except socket.error as msg:
                _logger.error("Socket error occurred %s" % msg)
                self.running = False
            except:
                _logger.error("Socket exception occurred "
                              "%s" % traceback.format_exc() )
                self.running = False
                reset_frame = True
            finally:
                if reset_frame:
                    self.framer.resetFrame()
                    reset_frame = False
Example #20
0
    def datagramReceived(self, data, addr):
        """ Callback when we receive any data

        :param data: The data sent by the client
        """
        _logger.debug("Client Connected [%s]" % addr)
        if _logger.isEnabledFor(logging.DEBUG):
            _logger.debug("Datagram Received: "+ hexlify_packets(data))
        if not self.control.ListenOnly:
            continuation = lambda request: self._execute(request, addr)
            self.framer.processIncomingPacket(data, continuation)
Example #21
0
 def execute(self, request=None):
     """
     Executes a transaction
     :param request:
     :return:
     """
     request.transaction_id = self.transaction.getNextTID()
     packet = self.framer.buildPacket(request)
     LOGGER.debug("send: " + hexlify_packets(packet))
     self.stream.write(packet)
     return self._build_response(request.transaction_id)
Example #22
0
    def datagramReceived(self, data, addr):
        """ Callback when we receive any data

        :param data: The data sent by the client
        """
        _logger.debug("Client Connected [%s]" % addr)
        if _logger.isEnabledFor(logging.DEBUG):
            _logger.debug("Datagram Received: " + hexlify_packets(data))
        if not self.control.ListenOnly:
            continuation = lambda request: self._execute(request, addr)
            self.framer.processIncomingPacket(data, continuation)
Example #23
0
 def resetFrame(self):
     """
     Reset the entire message frame.
     This allows us to skip over errors that may be in the stream.
     It is hard to know if we are simply out of sync or if there is
     an error in the stream as we have no way to check the start or
     end of the message (python just doesn't have the resolution to
     check for millisecond delays).
     """
     _logger.debug("Resetting frame - Current Frame in "
                   "buffer - {}".format(hexlify_packets(self._buffer)))
     self._buffer = b''
     self._header = {}
Example #24
0
    def getFrame(self):
        """
        Get the next frame from the buffer

        :returns: The frame data or ''
        """
        start = self._hsize
        end = self._header['len'] - 2
        buffer = self._buffer[start:end]
        if end > 0:
            _logger.debug("Getting Frame - {}".format(hexlify_packets(buffer)))
            return buffer
        return b''
Example #25
0
    def dataReceived(self, data):
        """ Callback when we receive any data

        :param data: The data sent by the client
        """
        if _logger.isEnabledFor(logging.DEBUG):
            _logger.debug('Data Received: ' + hexlify_packets(data))
        if not self.factory.control.ListenOnly:
            units = self.factory.store.slaves()
            single = self.factory.store.single
            self.framer.processIncomingPacket(data, self._execute,
                                              single=single,
                                              unit=units)
Example #26
0
    def dataReceived(self, data):
        """ Callback when we receive any data

        :param data: The data sent by the client
        """
        if _logger.isEnabledFor(logging.DEBUG):
            _logger.debug('Data Received: ' + hexlify_packets(data))
        if not self.factory.control.ListenOnly:
            units = self.factory.store.slaves()
            single = self.factory.store.single
            self.framer.processIncomingPacket(data, self._execute,
                                              single=single,
                                              unit=units)
Example #27
0
 def resetFrame(self):
     """
     Reset the entire message frame.
     This allows us to skip over errors that may be in the stream.
     It is hard to know if we are simply out of sync or if there is
     an error in the stream as we have no way to check the start or
     end of the message (python just doesn't have the resolution to
     check for millisecond delays).
     """
     _logger.debug("Resetting frame - Current Frame in "
                   "buffer - {}".format(hexlify_packets(self._buffer)))
     self._buffer = b''
     self._header = {}
Example #28
0
    def getFrame(self):
        """
        Get the next frame from the buffer

        :returns: The frame data or ''
        """
        start = self._hsize
        end = self._header['len'] - 2
        buffer = self._buffer[start:end]
        if end > 0:
            _logger.debug("Getting Frame - {}".format(hexlify_packets(buffer)))
            return buffer
        return b''
Example #29
0
 def callback(*args):
     LOGGER.debug("in callback - {}".format(request.transaction_id))
     while True:
         waiting = self.stream.connection.in_waiting
         if waiting:
             data = self.stream.connection.read(waiting)
             LOGGER.debug("recv: " + hexlify_packets(data))
             unit = self.framer.decode_data(data).get("uid", 0)
             self.framer.processIncomingPacket(
                 data,
                 self._handle_response,
                 unit,
                 tid=request.transaction_id)
             break
Example #30
0
    def on_receive(self, *args):
        """
        On data recieve call back
        :param args: data received
        :return:
        """
        data = args[0] if len(args) > 0 else None

        if not data:
            return
        LOGGER.debug("recv: " + hexlify_packets(data))
        unit = self.framer.decode_data(data).get("unit", 0)
        self.framer.processIncomingPacket(data,
                                          self._handle_response,
                                          unit=unit)
Example #31
0
 def dataReceived(self, data):
     """ Callback when we receive any data
     :param data: The data sent by the client
     """
     log.event('Modbus, IPv4Address[{}]: Data Received (Raw): '.format(
         self.transport.getPeer().host) + str(data))
     log.event('Modbus, IPv4Address[{}]: Data Received (Hex): '.format(
         self.transport.getPeer().host) + hexlify_packets(data))
     if not self.factory.control.ListenOnly:
         units = self.factory.store.slaves()
         single = self.factory.store.single
         self.framer.processIncomingPacket(data,
                                           self._execute,
                                           single=single,
                                           unit=units)
Example #32
0
    def _execute(self, request):
        # Build Framer Packet
        request.transaction_id = self.transaction.getNextTID()
        packet = self.framer.buildPacket(message=request)
        logger.debug("send: " + hexlify_packets(packet))

        # Send packet through QUIC
        self.send(packet)

        # Get response
        waiter = self.client._loop.create_future()
        self.transaction.addTransaction(waiter, request.transaction_id)
        self._ack_waiter = waiter
        self.client.transmit()
        return waiter
Example #33
0
    def processIncomingPacket(self, data, callback, unit, **kwargs):
        """
        The new packet processing pattern

        This takes in a new request packet, adds it to the current
        packet stream, and performs framing on it. That is, checks
        for complete messages, and once found, will process all that
        exist.  This handles the case when we read N + 1 or 1 / N
        messages at a time instead of 1.

        The processed and decoded messages are pushed to the callback
        function to process and send.

        :param data: The new packet data
        :param callback: The function to send results to
        :param unit: Process if unit id matches, ignore otherwise (could be a
        list of unit ids (server) or single unit id(client/server)
        :param single: True or False (If True, ignore unit address validation)

        """
        if not isinstance(unit, (list, tuple)):
            unit = [unit]
        single = kwargs.get("single", False)
        _logger.debug("Processing: " + hexlify_packets(data))
        self.addToFrame(data)
        while True:
            if self.isFrameReady():
                if self.checkFrame():
                    if self._validate_unit_id(unit, single):
                        self._process(callback)
                    else:
                        _logger.debug("Not a valid unit id - {}, "
                                      "ignoring!!".format(self._header['uid']))
                        self.resetFrame()
                else:
                    _logger.debug("Frame check failed, ignoring!!")
                    self.resetFrame()
            else:
                if len(self._buffer):
                    # Possible error ???
                    if self._header['len'] < 2:
                        self._process(callback, error=True)
                break
Example #34
0
    def processIncomingPacket(self, data, callback, unit, **kwargs):
        """
        The new packet processing pattern

        This takes in a new request packet, adds it to the current
        packet stream, and performs framing on it. That is, checks
        for complete messages, and once found, will process all that
        exist.  This handles the case when we read N + 1 or 1 // N
        messages at a time instead of 1.

        The processed and decoded messages are pushed to the callback
        function to process and send.

        :param data: The new packet data
        :param callback: The function to send results to
        :param unit: Process if unit id matches, ignore otherwise (could be a \
         list of unit ids (server) or single unit id(client/server)
        :param single: True or False (If True, ignore unit address validation)
        :return:
        """
        if not isinstance(unit, (list, tuple)):
            unit = [unit]
        single = kwargs.get("single", False)
        _logger.debug("Processing: " + hexlify_packets(data))
        self.addToFrame(data)
        while True:
            if self.isFrameReady():
                if self.checkFrame():
                    if self._validate_unit_id(unit, single):
                        self._process(callback)
                    else:
                        _logger.debug("Not a valid unit id - {}, "
                                      "ignoring!!".format(self._header['uid']))
                        self.resetFrame()
                else:
                    _logger.debug("Frame check failed, ignoring!!")
                    self.resetFrame()
            else:
                if len(self._buffer):
                    # Possible error ???
                    if self._header['len'] < 2:
                        self._process(callback, error=True)
                break
Example #35
0
    def testBaseModbusClient(self):
        ''' Test the base class for all the clients '''

        client = BaseModbusClient(None)
        client.transaction = None
        self.assertRaises(NotImplementedException, lambda: client.connect())
        self.assertRaises(NotImplementedException, lambda: client.send(None))
        self.assertRaises(NotImplementedException, lambda: client.recv(None))
        self.assertRaises(NotImplementedException, lambda: client.__enter__())
        self.assertRaises(NotImplementedException, lambda: client.execute())
        self.assertRaises(NotImplementedException,
                          lambda: client.is_socket_open())
        self.assertEqual("Null Transport", str(client))
        client.close()
        client.__exit__(0, 0, 0)

        # Test information methods
        client.last_frame_end = 2
        client.silent_interval = 2
        self.assertEqual(4, client.idle_time())
        client.last_frame_end = None
        self.assertEqual(0, client.idle_time())

        # Test debug/trace/_dump methods
        self.assertEqual(False, client.debug_enabled())
        writable = StringIO()
        client.trace(writable)
        client._dump(b'\x00\x01\x02', None)
        self.assertEqual(hexlify_packets(b'\x00\x01\x02'), writable.getvalue())

        # a successful execute
        client.connect = lambda: True
        client.transaction = Mock(**{'execute.return_value': True})
        self.assertEqual(client, client.__enter__())
        self.assertTrue(client.execute())

        # a unsuccessful connect
        client.connect = lambda: False
        self.assertRaises(ConnectionException, lambda: client.__enter__())
        self.assertRaises(ConnectionException, lambda: client.execute())
Example #36
0
 def _on_timeout():
     LOGGER.warning("timeout")
     _clear_timer()
     if self.stream:
         self.io_loop.remove_handler(self.stream.fileno())
     self.framer.resetFrame()
     transaction = self.transaction.getTransaction(request.transaction_id)
     if self.state != ModbusTransactionState.IDLE:
         self.state = ModbusTransactionState.IDLE
         if self.stream:
             try:
                 waiting = self.stream.connection.in_waiting
                 if waiting:
                     result = self.stream.connection.read(waiting)
                     LOGGER.info(
                         "Cleanup recv buffer after timeout: " + hexlify_packets(result))
             except OSError as ex:
                 self.close()
                 if transaction:
                     transaction.set_exception(ModbusIOException(ex))
                 return
     if transaction:
         transaction.set_exception(TimeOutException())
Example #37
0
        def _on_receive(fd, events):
            """
            New data in serial buffer to read or serial port closed
            """
            if events & IOLoop.ERROR:
                _on_fd_error(fd)
                return

            try:
                waiting = self.stream.connection.in_waiting
                if waiting:
                    data = self.stream.connection.read(waiting)
                    LOGGER.debug("recv: " + hexlify_packets(data))
                    self.last_frame_end = round(time.time(), 6)
            except OSError as ex:
                _on_fd_error(fd, ex)
                return

            self.framer.addToFrame(data)

            # check if we have regular frame or modbus exception
            fcode = self.framer.decode_data(self.framer.getRawFrame()).get(
                "fcode", 0)
            if fcode and (
                (fcode > 0x80 and len(self.framer.getRawFrame())
                 == exception_response_length) or
                (len(self.framer.getRawFrame()) == expected_response_length)):
                _clear_timer()
                self.io_loop.remove_handler(fd)
                self.state = ModbusTransactionState.IDLE
                self.framer.processIncomingPacket(
                    b'',  # already sent via addToFrame()
                    self._handle_response,
                    0,  # don't care when `single=True`
                    single=True,
                    tid=request.transaction_id)
Example #38
0
 def dataReceived(self, data):
     logger.debug("recv: " + hexlify_packets(data))
     unit = self.framer.decode_data(data=data).get("unit", 0)
     self.framer.processIncomingPacket(data,
                                       self._handleResponse,
                                       unit=unit)
Example #39
0
 def execute(self, request):
     """ Starts the producer to send the next request to
     consumer.write(Frame(request))
     """
     with self._transaction_lock:
         try:
             _logger.debug("Current transaction state - {}".format(
                 ModbusTransactionState.to_string(self.client.state))
             )
             retries = self.retries
             request.transaction_id = self.getNextTID()
             _logger.debug("Running transaction "
                           "{}".format(request.transaction_id))
             _buffer = hexlify_packets(self.client.framer._buffer)
             if _buffer:
                 _logger.debug("Clearing current Frame "
                               ": - {}".format(_buffer))
                 self.client.framer.resetFrame()
             broadcast = (self.client.broadcast_enable
                          and request.unit_id == 0)
             if broadcast:
                 self._transact(request, None, broadcast=True)
                 response = b'Broadcast write sent - no response expected'
             else:
                 expected_response_length = None
                 if not isinstance(self.client.framer, ModbusSocketFramer):
                     if hasattr(request, "get_response_pdu_size"):
                         response_pdu_size = request.get_response_pdu_size()
                         if isinstance(self.client.framer, ModbusAsciiFramer):
                             response_pdu_size = response_pdu_size * 2
                         if response_pdu_size:
                             expected_response_length = self._calculate_response_length(response_pdu_size)
                 if request.unit_id in self._no_response_devices:
                     full = True
                 else:
                     full = False
                 c_str = str(self.client)
                 if "modbusudpclient" in c_str.lower().strip():
                     full = True
                     if not expected_response_length:
                         expected_response_length = Defaults.ReadSize
                 response, last_exception = self._transact(
                     request,
                     expected_response_length,
                     full=full,
                     broadcast=broadcast
                 )
                 if not response and (
                         request.unit_id not in self._no_response_devices):
                     self._no_response_devices.append(request.unit_id)
                 elif request.unit_id in self._no_response_devices and response:
                     self._no_response_devices.remove(request.unit_id)
                 if not response and self.retry_on_empty and retries:
                     while retries > 0:
                         if hasattr(self.client, "state"):
                             _logger.debug("RESETTING Transaction state to "
                                           "'IDLE' for retry")
                             self.client.state = ModbusTransactionState.IDLE
                         _logger.debug("Retry on empty - {}".format(retries))
                         response, last_exception = self._transact(
                             request,
                             expected_response_length
                         )
                         if not response:
                             retries -= 1
                             continue
                         # Remove entry
                         self._no_response_devices.remove(request.unit_id)
                         break
                 addTransaction = partial(self.addTransaction,
                                          tid=request.transaction_id)
                 self.client.framer.processIncomingPacket(response,
                                                          addTransaction,
                                                          request.unit_id)
                 response = self.getTransaction(request.transaction_id)
                 if not response:
                     if len(self.transactions):
                         response = self.getTransaction(tid=0)
                     else:
                         last_exception = last_exception or (
                             "No Response received from the remote unit"
                             "/Unable to decode response")
                         response = ModbusIOException(last_exception,
                                                      request.function_code)
                 if hasattr(self.client, "state"):
                     _logger.debug("Changing transaction state from "
                                   "'PROCESSING REPLY' to "
                                   "'TRANSACTION_COMPLETE'")
                     self.client.state = (
                         ModbusTransactionState.TRANSACTION_COMPLETE)
             return response
         except ModbusIOException as ex:
             # Handle decode errors in processIncomingPacket method
             _logger.exception(ex)
             self.client.state = ModbusTransactionState.TRANSACTION_COMPLETE
             return ex
Example #40
0
    async def handle(self):
        """Asyncio coroutine which represents a single conversation between
        the modbus slave and master

        Once the client connection is established, the data chunks will be
        fed to this coroutine via the asyncio.Queue object which is fed by
        the ModbusBaseRequestHandler class's callback Future.

        This callback future gets data from either asyncio.DatagramProtocol.datagram_received
        or from asyncio.BaseProtocol.data_received.

        This function will execute without blocking in the while-loop and
        yield to the asyncio event loop when the frame is exhausted.
        As a result, multiple clients can be interleaved without any
        interference between them.

        For ModbusConnectedRequestHandler, each connection will be given an
        instance of the handle() coroutine and this instance will be put in the
        active_connections dict. Calling server_close will individually cancel
        each running handle() task.

        For ModbusDisconnectedRequestHandler, a single handle() coroutine will
        be started and maintained. Calling server_close will cancel that task.

        """
        reset_frame = False
        while self.running:
            try:
                units = self.server.context.slaves()
                data = await self._recv_(
                )  # this is an asyncio.Queue await, it will never fail
                if isinstance(data, tuple):
                    data, *addr = data  # addr is populated when talking over UDP
                else:
                    addr = (None, )  # empty tuple

                if not isinstance(units, (list, tuple)):
                    units = [units]
                # if broadcast is enabled make sure to
                # process requests to address 0
                if self.server.broadcast_enable:  # pragma: no cover
                    if 0 not in units:
                        units.append(0)

                if _logger.isEnabledFor(logging.DEBUG):
                    _logger.debug('Handling data: ' + hexlify_packets(data))

                single = self.server.context.single
                self.framer.processIncomingPacket(
                    data=data,
                    callback=lambda x: self.execute(x, *addr),
                    unit=units,
                    single=single)

            except asyncio.CancelledError:
                # catch and ignore cancelation errors
                if isinstance(self, ModbusConnectedRequestHandler):
                    _logger.debug(
                        "Handler for stream [%s:%s] has been canceled" %
                        self.client_address)
                else:
                    _logger.debug(
                        "Handler for UDP socket [%s] has been canceled" %
                        self.protocol._sock.getsockname()[1])

            except Exception as e:
                # force TCP socket termination as processIncomingPacket should handle applicaiton layer errors
                # for UDP sockets, simply reset the frame
                if isinstance(self, ModbusConnectedRequestHandler):
                    _logger.info(
                        "Unknown exception '%s' on stream [%s:%s] forcing disconnect"
                        % (e, *self.client_address))
                    self.transport.close()
                else:
                    _logger.error("Unknown error occurred %s" % e)
                    reset_frame = True  # graceful recovery
            finally:
                if reset_frame:
                    self.framer.resetFrame()
                    reset_frame = False
Example #41
0
    def execute(self, request=None):
        """
        Executes a transaction
        :param request: Request to be written on to the bus
        :return:
        """
        request.transaction_id = self.transaction.getNextTID()

        def _clear_timer():
            LOGGER.debug("_clear_timer()")
            if self.timeout_handle:
                self.io_loop.remove_timeout(self.timeout_handle)
                self.timeout_handle = None

        def _on_timeout():
            LOGGER.warning("timeout")
            _clear_timer()
            if self.stream:
                self.io_loop.remove_handler(self.stream.fileno())
            self.framer.resetFrame()
            transaction = self.transaction.getTransaction(request.transaction_id)
            if self.state != ModbusTransactionState.IDLE:
                self.state = ModbusTransactionState.IDLE
                if self.stream:
                    try:
                        waiting = self.stream.connection.in_waiting
                        if waiting:
                            result = self.stream.connection.read(waiting)
                            LOGGER.info(
                                "Cleanup recv buffer after timeout: " + hexlify_packets(result))
                    except OSError as ex:
                        self.close()
                        if transaction:
                            transaction.set_exception(ModbusIOException(ex))
                        return
            if transaction:
                transaction.set_exception(TimeOutException())

        def _on_write_done(*args):
            LOGGER.debug("frame sent, waiting for a reply")
            self.last_frame_end = round(time.time(), 6)
            self.state = ModbusTransactionState.WAITING_FOR_REPLY
            self.io_loop.add_handler(self.stream.fileno(), _on_receive, IOLoop.READ)

        def _on_receive(fd, events):
            LOGGER.debug("_on_receive: %s, %s", fd, events)

            try:
                waiting = self.stream.connection.in_waiting
                if waiting:
                    LOGGER.debug("waiting = %d", waiting)
                    data = self.stream.connection.read(waiting)
                    LOGGER.debug(
                        "recv: " + hexlify_packets(data))
            except OSError as ex:
                _clear_timer()
                self.close()
                self.transaction.getTransaction(request.transaction_id).set_exception(ModbusIOException(ex))
                return

            self.framer.addToFrame(data)

            # check if we have regular frame or modbus exception
            fcode = self.framer.decode_data(self.framer.getRawFrame()).get("fcode", 0)
            if fcode and (
                  (fcode > 0x80 and len(self.framer.getRawFrame()) == exception_response_length)
                or
                  (len(self.framer.getRawFrame()) == expected_response_length)
            ):
                _clear_timer()
                self.io_loop.remove_handler(fd)
                self.state = ModbusTransactionState.IDLE
                self.framer.processIncomingPacket(
                    b'',            # already sent via addToFrame()
                    self._handle_response,
                    0,              # don't care for `single=True`
                    single=True,
                    tid=request.transaction_id
                )

        LOGGER.debug("set timeout for %f sec", self.timeout)
        self.timeout_handle = self.io_loop.add_timeout(time.time() + self.timeout, _on_timeout)

        response_pdu_size = request.get_response_pdu_size()
        expected_response_length = self.transaction._calculate_response_length(response_pdu_size)
        LOGGER.debug("expected_response_length = %d", expected_response_length)
        exception_response_length = self.transaction._calculate_exception_length() # TODO: this does not change

        packet = self.framer.buildPacket(request)
        LOGGER.debug("send: " + hexlify_packets(packet))
        self.state = ModbusTransactionState.SENDING
        f = self._build_response(request.transaction_id)
        self._sendPacket(packet, callback=_on_write_done)
        return f