11"""Modbus RTU frame implementation."""
22from __future__ import annotations
33
4- from collections import namedtuple
5-
64from pymodbus .framer .base import FramerBase
75from pymodbus .logging import Log
86
@@ -39,6 +37,7 @@ class FramerRTU(FramerBase):
3937 this means decoding is always exactly 1 frame request, however some requests
4038 will be for unknown slaves, which must be ignored together with the
4139 response from the unknown slave.
40+ >>>>> NOT IMPLEMENTED <<<<<
4241
4342 Recovery from bad cabling and unstable USB etc is important,
4443 the following scenarios is possible:
@@ -52,17 +51,34 @@ class FramerRTU(FramerBase):
5251 Device drivers will typically flush buffer after 10ms of silence.
5352 If no data is received for 50ms the transmission / frame can be considered
5453 complete.
55- """
5654
57- MIN_SIZE = 5
55+ The following table is a listing of the baud wait times for the specified
56+ baud rates::
57+
58+ ------------------------------------------------------------------
59+ Baud 1.5c (18 bits) 3.5c (38 bits)
60+ ------------------------------------------------------------------
61+ 1200 13333.3 us 31666.7 us
62+ 4800 3333.3 us 7916.7 us
63+ 9600 1666.7 us 3958.3 us
64+ 19200 833.3 us 1979.2 us
65+ 38400 416.7 us 989.6 us
66+ ...
67+ ------------------------------------------------------------------
68+ 1 Byte = start + 8 bits + parity + stop = 11 bits
69+ (1/Baud)(bits) = delay seconds
70+
71+ >>>>> NOT IMPLEMENTED <<<<<
72+ """
5873
59- FC_LEN = namedtuple ( "FC_LEN" , "req_len req_bytepos resp_len resp_bytepos" )
74+ MIN_SIZE = 4 # <slave id><function code><crc 2 bytes>
6075
61- def __init__ (self ) -> None :
76+ def __init__ (self , function_codes = None , decoder = None ) -> None :
6277 """Initialize a ADU instance."""
6378 super ().__init__ ()
64- self .fc_len : dict [int , FramerRTU .FC_LEN ] = {}
65-
79+ self .function_codes = function_codes
80+ self .slaves : list [int ] = []
81+ self .decoder = decoder
6682
6783 @classmethod
6884 def generate_crc16_table (cls ) -> list [int ]:
@@ -84,38 +100,41 @@ def generate_crc16_table(cls) -> list[int]:
84100 crc16_table : list [int ] = [0 ]
85101
86102
87- def setup_fc_len (self , _fc : int ,
88- _req_len : int , _req_byte_pos : int ,
89- _resp_len : int , _resp_byte_pos : int
90- ):
91- """Define request/response lengths pr function code."""
92- return
103+ def set_slaves (self , slaves ):
104+ """Remember allowed slaves."""
105+ self .slaves = slaves
93106
94107 def decode (self , data : bytes ) -> tuple [int , int , int , bytes ]:
95108 """Decode ADU."""
96- if (buf_len := len (data )) < self .MIN_SIZE :
97- Log .debug ("Short frame: {} wait for more data" , data , ":hex" )
98- return 0 , 0 , 0 , b''
99-
100- i = - 1
101- try :
102- while True :
103- i += 1
104- if i > buf_len - self .MIN_SIZE + 1 :
105- break
106- dev_id = int (data [i ])
107- fc_len = 5
108- msg_len = fc_len - 2 if fc_len > 0 else int (data [i - fc_len ])- fc_len + 1
109- if msg_len + i + 2 > buf_len :
110- break
111- crc_val = (int (data [i + msg_len ]) << 8 ) + int (data [i + msg_len + 1 ])
112- if not self .check_CRC (data [i :i + msg_len ], crc_val ):
113- Log .debug ("Skipping frame CRC with len {} at index {}!" , msg_len , i )
114- raise KeyError
115- return i + msg_len + 2 , dev_id , dev_id , data [i + 1 :i + msg_len ]
116- except KeyError :
117- i = buf_len
118- return i , 0 , 0 , b''
109+ msg_len = len (data )
110+ for used_len in range (msg_len ):
111+ if msg_len - used_len < self .MIN_SIZE :
112+ Log .debug ("Short frame: {} wait for more data" , data , ":hex" )
113+ return 0 , 0 , 0 , b''
114+ dev_id = int (data [used_len ])
115+ func_code = int (data [used_len + 1 ])
116+ if (self .slaves [0 ] and dev_id not in self .slaves ) or func_code & 0x7F not in self .function_codes :
117+ continue
118+ if msg_len - used_len < self .MIN_SIZE :
119+ Log .debug ("Garble in front {}, then short frame: {} wait for more data" , used_len , data , ":hex" )
120+ return used_len , 0 , 0 , b''
121+ pdu_class = self .decoder .lookupPduClass (func_code )
122+ try :
123+ size = pdu_class .calculateRtuFrameSize (data [used_len :])
124+ except IndexError :
125+ size = msg_len + 1
126+ if msg_len < used_len + size :
127+ Log .debug ("Frame - not ready" )
128+ return used_len , 0 , 0 , b''
129+ start_crc = used_len + size - 2
130+ crc = data [start_crc : start_crc + 2 ]
131+ crc_val = (int (crc [0 ]) << 8 ) + int (crc [1 ])
132+ if not FramerRTU .check_CRC (data [used_len : start_crc ], crc_val ):
133+ Log .debug ("Frame check failed, ignoring!!" )
134+ return used_len , 0 , 0 , b''
135+
136+ return start_crc + 2 , 0 , dev_id , data [used_len + 1 : start_crc ]
137+ return used_len , 0 , 0 , b''
119138
120139
121140 def encode (self , pdu : bytes , device_id : int , _tid : int ) -> bytes :
0 commit comments