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 discards packets splitted into two calls to "processIncomingPacket" #394

Closed
RonnyDe1982 opened this issue Mar 8, 2019 · 6 comments · Fixed by #1076
Closed

Comments

@RonnyDe1982
Copy link

Versions

  • Python: 3.6.7
  • OS: Ubuntu 18.10
  • Pymodbus: 2.1.0 (rtu_framer.py function ProcessIncomingPacket patched to 2.2.0)
  • Modbus Hardware (if used):

Pymodbus Specific

  • Server: rtu - async

Description

The RTU Framer had a known issue related to modbus requests not served by the library but other physical modbus devices. This issues seems to be fixed in branch "pymodbus-2.2.0".

However, the suggested bugfix in pymodbus/rtu:framer.py, lines 231-233 introduces another problem for large modbus requests which cannot be passes in one single data frame to the function processIncomingPacket.

Whenever an incomplete packet has been received this packet is discarded since "checkFrame" returns false.

Known workaround:

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:
                if len(self._buffer) > self._header["len"]:
                       _logger.debug("Frame check failed, ignoring!!")
                      self.resetFrame()
        else:
            _logger.debug("Frame - [{}] not ready".format(data))

@meierphil
Copy link

I stumbled upon the same issue:

Versions

Python: 3.6.4, 3.7.3
OS: Windows10

and
Python 3.6.5
OS: Ubuntu 18.04.1 LTS

with
Pymodbus: 2.2.0rc4
Hardware: Voegtlin red-y smart Series GSM mass flow meter

Description

When I integrated @RonnyDe1982's workaround, a key error was raised for self._header['len']:

Received
    self.framer.processIncomingPacket(data, self._handleResponse, unit=unit)
  File "C:\Users\username\venv\lib\site-packages\pymodbus\framer\rtu_framer.py", line 234, in processIncomingPacket
    if len(self._buffer) > self._header["len"]:
KeyError: 'len'

Hence, I adapted the snippet to:

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:
        try:
            if self._header['len']:
                if len(self._buffer) > self._header['len']:
                    _logger.debug("Frame check failed, ignoring!!")
                    self.resetFrame()
        except KeyError:
            pass
else:
    _logger.debug("Frame - [{}] not ready".format(data))

Not the neatest solution, but it worked for my case. I guess it is worth to dig a little bit deeper to find the cause of this error.

@dhoomakethu dhoomakethu added this to the 2.3.0 milestone Mar 27, 2019
@dhoomakethu
Copy link
Contributor

@meierphil Thanks for the hints and workaround. I will try to get a solution for this

@Howlinmoon
Copy link

Howlinmoon commented Sep 13, 2019

I'm trying to figure out if I am seeing this same split packet issue with the RTU framer.

My master device is sending this command:

[2019-09-13T12:09:37.412Z][DEBUG]-ModbusMaster.py:43,Sending request: address=1, function=3, data=[0, 0, 0, 1]

And the Pymodbus Sync server is reporting this error:

2019-09-13 07:51:33,892 MainThread DEBUG sync :45 Client Connected [/dev/ttyUSB1:/dev/ttyUSB1]
2019-09-13 07:51:33,892 MainThread DEBUG sync :522 Started thread to serve client
2019-09-13 07:51:51,159 MainThread DEBUG rtu_framer :232 Frame check failed, ignoring!!
2019-09-13 07:51:51,159 MainThread DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0x1 0x3 0x0 0x0 0x0
2019-09-13 07:51:51,165 MainThread DEBUG rtu_framer :232 Frame check failed, ignoring!!
2019-09-13 07:51:51,165 MainThread DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0x1 0x84 0xa

It looks to me like "0x1 0x3 0x0 0x0 0x0" is missing the final "0x1" which is in the second frame - along with "0x84 & 0xa"

Is this the same issue?

@dhoomakethu dhoomakethu modified the milestones: 2.3.0, 2.4.0 Nov 5, 2019
@maximeLeurent
Copy link

Hi,

I just face the same issue with a TCP device.
I am using a SMA Sunny Tripower device. In SMA FAQ, they are talking about message split https://www.sma.de/fr/produits/surveillance-controle/interface-protocole-modbus.html (section Modbus provides two answers upon request).

Here a piece of code, showing my issue. In sync mode there is no problem; In async mode, the message is split in two piece of code :
-0x0 0x1 0x0 0x0 0x0 0x7 0x3 0x3 0x4 0x0 0x0 0x3 -> ignored
-0x71 -> generated a error; because no header

import logging
from pymodbus.client.sync import ModbusTcpClient as SyncModbusClient
from pymodbus.client.asynchronous.asyncio import AsyncioModbusTcpClient,ModbusClientProtocol
from pymodbus.framer.socket_framer import ModbusSocketFramer
from pymodbus.factory import ClientDecoder
from pymodbus.utilities import hexlify_packets
import asyncio
from pymodbus.client.asynchronous import schedulers
ip = "192.168.0.3"
port = 502
num_reg =30775
nb_reg = 2
unit = 3
FORMAT = ('%(asctime)-15s %(threadName)-15s '
          '%(levelname)-8s %(module)-15s:%(lineno)-8s %(message)s')
logging.basicConfig(format=FORMAT)
log = logging.getLogger()
log.setLevel(logging.DEBUG)


def run_sync_client():
    log.debug("")
    log.debug("-----------Sync client---------------------------")
    client = SyncModbusClient(ip, port=port)
    client.connect()
    rr = client.read_holding_registers(num_reg,nb_reg, unit=unit)
    print(rr)
    print(rr.registers)
    log.debug("--------------Sync client Finish---------------")
    log.debug("")

async def start_async_test(client):
    await client.connect()
    rr = await client.protocol.read_holding_registers(num_reg,nb_reg, unit=unit)
    print(rr)
    print(rr.registers)



def run_async_client():
    log.debug("-----------ASync client---------------------------")
    loop = asyncio.new_event_loop()
    assert not loop.is_running()
    asyncio.set_event_loop(loop)
    client = AsyncioModbusTcpClient( host= ip, port=port, loop=loop)#,protocol_class = MyModbusClientProtocol
    loop.run_until_complete(start_async_test(client))
    loop.close()
    log.debug("--------------ASync client Finish---------------")
    log.debug("")
run_sync_client()
run_async_client()
2019-12-16 15:34:29,878 MainThread      DEBUG    test_TCP_STP   :23       
2019-12-16 15:34:29,878 MainThread      DEBUG    test_TCP_STP   :24       -----------Sync client---------------------------
2019-12-16 15:34:29,881 MainThread      DEBUG    transaction    :115      Current transaction state - IDLE
2019-12-16 15:34:29,881 MainThread      DEBUG    transaction    :120      Running transaction 1
2019-12-16 15:34:29,881 MainThread      DEBUG    transaction    :219      SEND: 0x0 0x1 0x0 0x0 0x0 0x6 0x3 0x3 0x78 0x37 0x0 0x2
2019-12-16 15:34:29,881 MainThread      DEBUG    sync           :75       New Transaction state 'SENDING'
2019-12-16 15:34:29,881 MainThread      DEBUG    transaction    :228      Changing transaction state from 'SENDING' to 'WAITING FOR REPLY'
2019-12-16 15:34:30,061 MainThread      DEBUG    transaction    :304      Changing transaction state from 'WAITING FOR REPLY' to 'PROCESSING REPLY'
2019-12-16 15:34:30,061 MainThread      DEBUG    transaction    :233      RECV: 0x0 0x1 0x0 0x0 0x0 0x7 0x3 0x3 0x4 0x0 0x0 0x6 0x7d
2019-12-16 15:34:30,061 MainThread      DEBUG    socket_framer  :147      Processing: 0x0 0x1 0x0 0x0 0x0 0x7 0x3 0x3 0x4 0x0 0x0 0x6 0x7d
2019-12-16 15:34:30,061 MainThread      DEBUG    factory        :266      Factory Response[ReadHoldingRegistersResponse: 3]
2019-12-16 15:34:30,062 MainThread      DEBUG    transaction    :383      Adding transaction 1
2019-12-16 15:34:30,062 MainThread      DEBUG    transaction    :394      Getting transaction 1
2019-12-16 15:34:30,062 MainThread      DEBUG    transaction    :193      Changing transaction state from 'PROCESSING REPLY' to 'TRANSACTION_COMPLETE'
ReadRegisterResponse (2)
[0, 1661]
2019-12-16 15:34:30,062 MainThread      DEBUG    test_TCP_STP   :30       --------------Sync client Finish---------------
2019-12-16 15:34:30,062 MainThread      DEBUG    test_TCP_STP   :31       
2019-12-16 15:34:30,062 MainThread      DEBUG    test_TCP_STP   :47       -----------ASync client---------------------------
2019-12-16 15:34:30,062 MainThread      DEBUG    selector_events:53       Using selector: EpollSelector
2019-12-16 15:34:30,063 MainThread      DEBUG    __init__       :386      Connecting.
2019-12-16 15:34:30,064 MainThread      DEBUG    __init__       :95       Client connected to modbus server
2019-12-16 15:34:30,064 MainThread      INFO     __init__       :400      Protocol made connection.
2019-12-16 15:34:30,064 MainThread      INFO     __init__       :391      Connected to 192.168.0.3:502.
2019-12-16 15:34:30,064 MainThread      DEBUG    __init__       :129      send: 0x0 0x1 0x0 0x0 0x0 0x6 0x3 0x3 0x78 0x37 0x0 0x2
2019-12-16 15:34:30,064 MainThread      DEBUG    transaction    :383      Adding transaction 1
2019-12-16 15:34:30,336 MainThread      DEBUG    __init__       :138      recv: 0x0 0x1 0x0 0x0 0x0 0x7 0x3 0x3 0x4 0x0 0x0 0x6
2019-12-16 15:34:30,336 MainThread      DEBUG    socket_framer  :147      Processing: 0x0 0x1 0x0 0x0 0x0 0x7 0x3 0x3 0x4 0x0 0x0 0x6
2019-12-16 15:34:30,336 MainThread      DEBUG    socket_framer  :159      Frame check failed, ignoring!!
2019-12-16 15:34:30,337 MainThread      DEBUG    __init__       :138      recv: 0x7d
2019-12-16 15:34:30,338 MainThread      DEBUG    socket_framer  :147      Processing: 0x7d
2019-12-16 15:34:30,338 MainThread      DEBUG    factory        :266      Factory Response[125]
2019-12-16 15:34:30,338 MainThread      ERROR    factory        :245      Unable to decode response Modbus Error: Unknown response 125
2019-12-16 15:34:30,338 MainThread      ERROR    base_events    :1604     Fatal error: protocol.data_received() call failed.
protocol: <pymodbus.client.asynchronous.asyncio.ModbusClientProtocol object at 0x7f3ea4d05d90>
transport: <_SelectorSocketTransport fd=7 read=polling write=<idle, bufsize=0>>
Traceback (most recent call last):
  File "/usr/lib/python3.7/asyncio/selector_events.py", line 816, in _read_ready__data_received
    self._protocol.data_received(data)
  File "/home/ems/.local/lib/python3.7/site-packages/pymodbus/client/asynchronous/asyncio/__init__.py", line 193, in data_received
    self._dataReceived(data)
  File "/home/ems/.local/lib/python3.7/site-packages/pymodbus/client/asynchronous/asyncio/__init__.py", line 140, in _dataReceived
    self.framer.processIncomingPacket(data, self._handleResponse, unit=unit)
  File "/home/ems/.local/lib/python3.7/site-packages/pymodbus/framer/socket_framer.py", line 165, in processIncomingPacket
    self._process(callback, error=True)
  File "/home/ems/.local/lib/python3.7/site-packages/pymodbus/framer/socket_framer.py", line 175, in _process
    raise ModbusIOException("Unable to decode request")
pymodbus.exceptions.ModbusIOException: Modbus Error: [Input/Output] Unable to decode request
2019-12-16 15:34:30,340 MainThread      DEBUG    __init__       :105      Client disconnected from modbus server: Modbus Error: [Input/Output] Unable to decode request
2019-12-16 15:34:30,340 MainThread      DEBUG    transaction    :394      Getting transaction 1
2019-12-16 15:34:30,340 MainThread      INFO     __init__       :413      Protocol lost connection.

I managed to deal my problem with rewriting processIncomingPacket of the Framer.
I don't understand the goal of the while loop in this function, I removed it because the buffer is not reset at each call of processIncomingPacket.
This piece of code is not safe, because if the end of the message is lost, the message will not be process. May be a timeout can be added.

class MyFramer(ModbusSocketFramer):
    def processIncomingPacket(self, data, callback, unit, **kwargs):
        if not isinstance(unit, (list, tuple)):
            unit = [unit]
        single = kwargs.get("single", False)
        logging.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:
                    logging.debug("Not a valid unit id - {}, "
                                  "ignoring!!".format(self._header['uid']))
                    self.resetFrame()
            else:
                logging.debug("Frame check failed, wait end of message")
                #self.resetFrame()
        else:
            if len(self._buffer):
                # Possible error ???
                if self._header['len'] < 2:
                    self._process(callback, error=True)
            #break

class MyModbusClientProtocol(ModbusClientProtocol):
    def __init__(self, *args, **kargs):
         kargs["framer"] = MyFramer(ClientDecoder())
         super().__init__(self,  *args, **kargs)

The corresponding logs are : (the reply message has changed due to the state of my device)

2019-12-16 16:03:42,014 MainThread      DEBUG    test_TCP_STP   :41       -----------ASync client---------------------------
2019-12-16 16:03:42,014 MainThread      DEBUG    selector_events:53       Using selector: EpollSelector
2019-12-16 16:03:42,015 MainThread      DEBUG    __init__       :386      Connecting.
2019-12-16 16:03:42,016 MainThread      DEBUG    __init__       :95       Client connected to modbus server
2019-12-16 16:03:42,016 MainThread      INFO     __init__       :400      Protocol made connection.
2019-12-16 16:03:42,016 MainThread      INFO     __init__       :391      Connected to 192.168.0.3:502.
2019-12-16 16:03:42,017 MainThread      DEBUG    __init__       :129      send: 0x0 0x1 0x0 0x0 0x0 0x6 0x3 0x3 0x78 0x37 0x0 0x2
2019-12-16 16:03:42,017 MainThread      DEBUG    transaction    :383      Adding transaction 1
2019-12-16 16:03:42,163 MainThread      DEBUG    __init__       :138      recv: 0x0 0x1 0x0 0x0 0x0 0x7 0x3 0x3 0x4 0x0 0x0 0x3
2019-12-16 16:03:42,163 MainThread      DEBUG    test_TCP_STP   :61       Processing: 0x0 0x1 0x0 0x0 0x0 0x7 0x3 0x3 0x4 0x0 0x0 0x3
2019-12-16 16:03:42,163 MainThread      DEBUG    test_TCP_STP   :74       Frame check failed, wait end of message
2019-12-16 16:03:42,164 MainThread      DEBUG    __init__       :138      recv: 0x41
2019-12-16 16:03:42,164 MainThread      DEBUG    test_TCP_STP   :61       Processing: 0x41
2019-12-16 16:03:42,164 MainThread      DEBUG    factory        :266      Factory Response[ReadHoldingRegistersResponse: 3]
2019-12-16 16:03:42,164 MainThread      DEBUG    transaction    :394      Getting transaction 1
ReadRegisterResponse (2)
[0, 833]
2019-12-16 16:03:42,164 MainThread      DEBUG    test_TCP_STP   :48       --------------ASync client Finish---------------
2019-12-16 16:03:42,164 MainThread      DEBUG    test_TCP_STP   :49       

@nduongn
Copy link

nduongn commented Dec 29, 2019

I'm trying to figure out if I am seeing this same split packet issue with the RTU framer.

My master device is sending this command:

[2019-09-13T12:09:37.412Z][DEBUG]-ModbusMaster.py:43,Sending request: address=1, function=3, data=[0, 0, 0, 1]

And the Pymodbus Sync server is reporting this error:

2019-09-13 07:51:33,892 MainThread DEBUG sync :45 Client Connected [/dev/ttyUSB1:/dev/ttyUSB1]
2019-09-13 07:51:33,892 MainThread DEBUG sync :522 Started thread to serve client
2019-09-13 07:51:51,159 MainThread DEBUG rtu_framer :232 Frame check failed, ignoring!!
2019-09-13 07:51:51,159 MainThread DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0x1 0x3 0x0 0x0 0x0
2019-09-13 07:51:51,165 MainThread DEBUG rtu_framer :232 Frame check failed, ignoring!!
2019-09-13 07:51:51,165 MainThread DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0x1 0x84 0xa

It looks to me like "0x1 0x3 0x0 0x0 0x0" is missing the final "0x1" which is in the second frame - along with "0x84 & 0xa"

Is this the same issue?

I met the same issue like yours.

@nduongn
Copy link

nduongn commented Dec 30, 2019

I'm trying to figure out if I am seeing this same split packet issue with the RTU framer.

My master device is sending this command:

[2019-09-13T12:09:37.412Z][DEBUG]-ModbusMaster.py:43,Sending request: address=1, function=3, data=[0, 0, 0, 1]

And the Pymodbus Sync server is reporting this error:

2019-09-13 07:51:33,892 MainThread DEBUG sync :45 Client Connected [/dev/ttyUSB1:/dev/ttyUSB1]
2019-09-13 07:51:33,892 MainThread DEBUG sync :522 Started thread to serve client
2019-09-13 07:51:51,159 MainThread DEBUG rtu_framer :232 Frame check failed, ignoring!!
2019-09-13 07:51:51,159 MainThread DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0x1 0x3 0x0 0x0 0x0
2019-09-13 07:51:51,165 MainThread DEBUG rtu_framer :232 Frame check failed, ignoring!!
2019-09-13 07:51:51,165 MainThread DEBUG rtu_framer :128 Resetting frame - Current Frame in buffer - 0x1 0x84 0xa

It looks to me like "0x1 0x3 0x0 0x0 0x0" is missing the final "0x1" which is in the second frame - along with "0x84 & 0xa"

Is this the same issue?

Hi, you can use https://github.com/riptideio/pymodbus/pull/466 to fix your issue. I use this and it works.

@janiversen janiversen linked a pull request Sep 5, 2022 that will close this issue
@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.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants