Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTU Framer not Handling Maximum Charachter Interval #1278

Closed
erezblm opened this issue Jan 19, 2023 · 3 comments
Closed

RTU Framer not Handling Maximum Charachter Interval #1278

erezblm opened this issue Jan 19, 2023 · 3 comments
Labels

Comments

@erezblm
Copy link

erezblm commented Jan 19, 2023

Versions

  • Python: 3.10.6
  • OS: Ubuntu 20.04.1
  • Pymodbus: 3.1.0
  • Modbus Hardware (if used): USB to RS485 Adapter

Pymodbus Specific

  • Server: rtu

Description

I'm using the Modbus server for testing our client implementation.
As part of our client code, it tests the server for a few baud rates. As a consequence of this, some bad messages are sent with a specified interval between them.
After some debugging, I figured out that the RTU Framer is storing the last received frame indefinitely. As a consequence the next frame which was good was added to the last bad frame and then throws altogether because it cannot parse.
See the framer code below.

According to Modbus serial implementation guide (section 2.5.1.1 MODBUS Message RTU Framing):
"If a silent interval of more than 1.5 character times occurs between two characters, the message frame is declared incomplete and
should be discarded by the receiver."

Character time is based on the baud rate, and it seems that the RTU Framer doesn't handle silence between charchters at all.

Code and Logs

Some logs, the second message is good and appended to the previous:

Handling data: 0x0 0x20 0x40
Frame - [b'\x00 @'] not ready
Handling data: 0x1 0x3 0x40 0x0 0x0 0xb 0x11 0xcd
Frame check failed, ignoring!!
Resetting frame - Current Frame in buffer - 0x20 0x0 0x20 0x40 0x1 0x3 0x40 0x0 0x0 0xb 0x11 0xcd

It's all here basically, 'addToFrame' just append the buffer and 'isFrameReady' returns false if the buffer is smaller than the expected function code, and in this case just log 'not ready' and returns.

    def processIncomingPacket(
        self, data, callback, unit, **kwargs
    ):  # pylint: disable=arguments-differ
        """Process new packet 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 kwargs:
        """
        if not isinstance(unit, (list, tuple)):
            unit = [unit]
        self.addToFrame(data)
        single = kwargs.get("single", False)
        while True:
            if self.isFrameReady():
                if self.checkFrame():
                    if self._validate_unit_id(unit, single):
                        self._process(callback)
                    else:
                        header_txt = self._header["uid"]
                        txt = f"Not a valid unit id - {header_txt}, ignoring!!"
                        _logger.debug(txt)
                        self.resetFrame()
                        break
                else:
                    _logger.debug("Frame check failed, ignoring!!")
                    self.resetFrame()
                    break
            else:
                txt = f"Frame - [{data}] not ready"
                _logger.debug(txt)
                break

Suggestion

Add logic to 'addToFrame' checking the last time a buffer was added (stored as a member variable), and if the interval is bigger than expected (according to the baud rate), reset previous buffer and set the buffer to the received one.

@janiversen
Copy link
Collaborator

That looks as a clear bug, which you have solved, your suggestion sounds correct, pull requests are welcome

@janiversen janiversen added this to the Version 3.2 milestone Jan 24, 2023
@janiversen janiversen added the Bug label Jan 27, 2023
@janiversen janiversen modified the milestones: Version 3.2, Version 3.3 Jan 30, 2023
@janiversen janiversen modified the milestones: Version 3.3, Version 3.4 Mar 10, 2023
@janiversen
Copy link
Collaborator

We have just merged #1451, that should have a big effect on your problem.

Please confirm that it is solved.

@janiversen
Copy link
Collaborator

Solved. Feel free to add a comment it you still have problems.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 21, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants