rodbus/common/
frame.rs

1use crate::common::phys::PhysLayer;
2use std::ops::Range;
3
4use crate::common::buffer::ReadBuffer;
5use crate::common::function::FunctionCode;
6use crate::common::traits::{Loggable, LoggableDisplay, Serialize};
7use crate::error::RequestError;
8use crate::tcp::frame::{MbapDisplay, MbapHeader, MbapParser};
9use crate::types::UnitId;
10use crate::{DecodeLevel, ExceptionCode, FrameDecodeLevel};
11
12use scursor::WriteCursor;
13
14pub(crate) mod constants {
15    const fn max(lhs: usize, rhs: usize) -> usize {
16        if lhs > rhs {
17            lhs
18        } else {
19            rhs
20        }
21    }
22
23    pub(crate) const MAX_ADU_LENGTH: usize = 253;
24
25    #[cfg(feature = "serial")]
26    const fn serial_frame_size() -> usize {
27        crate::serial::frame::constants::MAX_FRAME_LENGTH
28    }
29
30    #[cfg(not(feature = "serial"))]
31    const fn serial_frame_size() -> usize {
32        0
33    }
34
35    /// the maximum size of a TCP or serial frame
36    pub(crate) const MAX_FRAME_LENGTH: usize = max(
37        crate::tcp::frame::constants::MAX_FRAME_LENGTH,
38        serial_frame_size(),
39    );
40}
41
42#[derive(PartialEq, Copy, Clone, Debug)]
43pub(crate) struct TxId {
44    value: u16,
45}
46
47impl TxId {
48    pub(crate) fn new(value: u16) -> Self {
49        TxId { value }
50    }
51
52    pub(crate) fn to_u16(self) -> u16 {
53        self.value
54    }
55
56    pub(crate) fn next(&mut self) -> TxId {
57        if self.value == u16::MAX {
58            self.value = 0;
59            TxId::new(u16::MAX)
60        } else {
61            let ret = self.value;
62            self.value += 1;
63            TxId::new(ret)
64        }
65    }
66}
67
68impl Default for TxId {
69    fn default() -> Self {
70        TxId::new(0)
71    }
72}
73
74impl std::fmt::Display for TxId {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        write!(f, "{:#04X}", self.value)
77    }
78}
79
80#[derive(Debug, Copy, Clone, PartialEq)]
81pub(crate) enum FrameDestination {
82    /// Normal unit ID
83    UnitId(UnitId),
84    /// Broadcast ID (only in RTU)
85    Broadcast,
86}
87
88impl FrameDestination {
89    #[cfg(test)]
90    pub(crate) fn new_unit_id(value: u8) -> Self {
91        Self::UnitId(UnitId::new(value))
92    }
93
94    pub(crate) fn value(&self) -> u8 {
95        match self {
96            Self::UnitId(unit_id) => unit_id.value,
97            Self::Broadcast => UnitId::broadcast().value,
98        }
99    }
100
101    pub(crate) fn into_unit_id(self) -> UnitId {
102        UnitId::new(self.value())
103    }
104
105    pub(crate) fn is_broadcast(&self) -> bool {
106        std::matches!(self, FrameDestination::Broadcast)
107    }
108}
109
110impl std::fmt::Display for FrameDestination {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        match self {
113            Self::UnitId(unit_id) => write!(f, "{unit_id}"),
114            Self::Broadcast => write!(f, "BCAST ({})", UnitId::broadcast()),
115        }
116    }
117}
118
119#[derive(Debug, Copy, Clone)]
120pub(crate) struct FrameHeader {
121    pub(crate) destination: FrameDestination,
122    /// Transaction ids are not used in RTU
123    pub(crate) tx_id: Option<TxId>,
124}
125
126impl FrameHeader {
127    pub(crate) fn new_tcp_header(unit_id: UnitId, tx_id: TxId) -> Self {
128        FrameHeader {
129            destination: FrameDestination::UnitId(unit_id),
130            tx_id: Some(tx_id),
131        }
132    }
133
134    #[cfg(feature = "serial")]
135    pub(crate) fn new_rtu_header(destination: FrameDestination) -> Self {
136        FrameHeader {
137            destination,
138            tx_id: None,
139        }
140    }
141}
142
143pub(crate) struct Frame {
144    pub(crate) header: FrameHeader,
145    length: usize,
146    pdu: [u8; constants::MAX_ADU_LENGTH],
147}
148
149impl Frame {
150    pub(crate) fn new(header: FrameHeader) -> Frame {
151        Frame {
152            header,
153            length: 0,
154            pdu: [0; constants::MAX_ADU_LENGTH],
155        }
156    }
157
158    pub(crate) fn set(&mut self, src: &[u8]) -> bool {
159        if src.len() > self.pdu.len() {
160            return false;
161        }
162
163        self.pdu[0..src.len()].copy_from_slice(src);
164        self.length = src.len();
165        true
166    }
167
168    pub(crate) fn payload(&self) -> &[u8] {
169        &self.pdu[0..self.length]
170    }
171}
172
173///  Defines an interface for parsing frames (TCP or RTU)
174pub(crate) enum FrameParser {
175    #[cfg(feature = "serial")]
176    Rtu(crate::serial::frame::RtuParser),
177    Tcp(MbapParser),
178}
179
180impl FrameParser {
181    /// Parse bytes using the provided cursor. Advancing the cursor always implies that the bytes
182    /// are consumed and can be discarded,
183    ///
184    /// `Err` implies the input data is invalid
185    /// `Ok(None)` implies that more data is required to complete parsing
186    /// `Ok(Some(..))` will contain a fully parsed frame and will advance the cursor appropriately
187    pub(crate) fn parse(
188        &mut self,
189        cursor: &mut ReadBuffer,
190        decode_level: FrameDecodeLevel,
191    ) -> Result<Option<Frame>, RequestError> {
192        match self {
193            #[cfg(feature = "serial")]
194            FrameParser::Rtu(x) => x.parse(cursor, decode_level),
195            FrameParser::Tcp(x) => x.parse(cursor, decode_level),
196        }
197    }
198
199    /// Reset the parser state. Called whenever an error occurs
200    pub(crate) fn reset(&mut self) {
201        match self {
202            #[cfg(feature = "serial")]
203            FrameParser::Rtu(x) => x.reset(),
204            FrameParser::Tcp(x) => x.reset(),
205        }
206    }
207}
208
209pub(crate) enum FrameType {
210    Mbap(MbapHeader),
211    #[cfg(feature = "serial")]
212    // destination and CRC
213    Rtu(FrameDestination, u16),
214}
215
216pub(crate) struct FrameInfo {
217    /// Information about the frame header
218    pub(crate) frame_type: FrameType,
219    /// Range that represents where the PDU body (after function) resides within the buffer
220    pub(crate) pdu_body: Range<usize>,
221}
222
223impl FrameInfo {
224    pub(crate) fn new(frame_type: FrameType, pdu_body: Range<usize>) -> Self {
225        Self {
226            frame_type,
227            pdu_body,
228        }
229    }
230}
231
232enum FormatType {
233    Tcp,
234    #[cfg(feature = "serial")]
235    Rtu,
236}
237
238impl FormatType {
239    fn format(
240        &self,
241        cursor: &mut WriteCursor,
242        header: FrameHeader,
243        function: FunctionField,
244        body: &dyn Serialize,
245    ) -> Result<FrameInfo, RequestError> {
246        match self {
247            FormatType::Tcp => crate::tcp::frame::format_mbap(cursor, header, function, body),
248            #[cfg(feature = "serial")]
249            FormatType::Rtu => crate::serial::frame::format_rtu_pdu(cursor, header, function, body),
250        }
251    }
252}
253
254pub(crate) struct FrameWriter {
255    format_type: FormatType,
256    buffer: [u8; constants::MAX_FRAME_LENGTH],
257}
258
259#[derive(Copy, Clone, Debug)]
260pub(crate) enum FunctionField {
261    Valid(FunctionCode),
262    Exception(FunctionCode),
263    UnknownFunction(u8),
264}
265
266impl std::fmt::Display for FunctionField {
267    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
268        let value = self.get_value();
269        match self {
270            FunctionField::Valid(x) => {
271                write!(f, "{x}")
272            }
273            FunctionField::Exception(x) => {
274                write!(f, "Exception({value}) for {x}")
275            }
276            FunctionField::UnknownFunction(_) => {
277                write!(f, "Unknown Function Exception: {value}")
278            }
279        }
280    }
281}
282
283impl FunctionField {
284    pub(crate) fn unknown(fc: u8) -> Self {
285        Self::UnknownFunction(fc)
286    }
287
288    pub(crate) fn get_value(&self) -> u8 {
289        match self {
290            FunctionField::Valid(x) => x.get_value(),
291            FunctionField::Exception(x) => x.get_value() | 0x80,
292            FunctionField::UnknownFunction(x) => x | 0x80,
293        }
294    }
295}
296
297impl FrameWriter {
298    fn new(format_type: FormatType) -> Self {
299        Self {
300            format_type,
301            buffer: [0; constants::MAX_FRAME_LENGTH],
302        }
303    }
304
305    pub(crate) fn format_reply<T>(
306        &mut self,
307        header: FrameHeader,
308        function: FunctionCode,
309        body: &T,
310        decode_level: DecodeLevel,
311    ) -> Result<&[u8], RequestError>
312    where
313        T: Serialize + Loggable,
314    {
315        match self.format_generic(header, FunctionField::Valid(function), body, decode_level) {
316            Ok(x) => Ok(&self.buffer[x]),
317            Err(RequestError::Exception(ex)) => {
318                self.format_ex(header, FunctionField::Exception(function), ex, decode_level)
319            }
320            Err(err) => Err(err),
321        }
322    }
323
324    pub(crate) fn format_request<T>(
325        &mut self,
326        header: FrameHeader,
327        function: FunctionCode,
328        body: &T,
329        decode_level: DecodeLevel,
330    ) -> Result<&[u8], RequestError>
331    where
332        T: Serialize + Loggable,
333    {
334        let range =
335            self.format_generic(header, FunctionField::Valid(function), body, decode_level)?;
336        Ok(&self.buffer[range])
337    }
338
339    pub(crate) fn format_ex(
340        &mut self,
341        header: FrameHeader,
342        function: FunctionField,
343        ex: ExceptionCode,
344        decode_level: DecodeLevel,
345    ) -> Result<&[u8], RequestError> {
346        let function = match function {
347            FunctionField::Valid(x) => FunctionField::Exception(x),
348            FunctionField::Exception(x) => FunctionField::Exception(x),
349            FunctionField::UnknownFunction(x) => FunctionField::UnknownFunction(x),
350        };
351
352        let range = self.format_generic(header, function, &ex, decode_level)?;
353
354        Ok(&self.buffer[range])
355    }
356
357    fn format_generic<T>(
358        &mut self,
359        header: FrameHeader,
360        function: FunctionField,
361        body: &T,
362        decode_level: DecodeLevel,
363    ) -> Result<Range<usize>, RequestError>
364    where
365        T: Serialize + Loggable,
366    {
367        let (frame_type, frame_bytes, pdu_body) = {
368            let mut cursor = WriteCursor::new(self.buffer.as_mut());
369            let info = self
370                .format_type
371                .format(&mut cursor, header, function, body)?;
372            let end = cursor.position();
373            (info.frame_type, 0..end, &self.buffer[info.pdu_body])
374        };
375
376        if decode_level.app.enabled() {
377            tracing::info!(
378                "PDU TX - {} {}",
379                function,
380                LoggableDisplay::new(body, pdu_body, decode_level.app)
381            );
382        }
383
384        if decode_level.frame.enabled() {
385            let frame_bytes = &self.buffer[frame_bytes.clone()];
386            match frame_type {
387                FrameType::Mbap(header) => {
388                    tracing::info!(
389                        "MBAP TX - {}",
390                        MbapDisplay::new(decode_level.frame, header, frame_bytes)
391                    );
392                }
393                #[cfg(feature = "serial")]
394                FrameType::Rtu(dest, crc) => {
395                    tracing::info!(
396                        "RTU TX - {}",
397                        crate::serial::frame::RtuDisplay::new(
398                            decode_level.frame,
399                            dest,
400                            frame_bytes,
401                            crc
402                        )
403                    );
404                }
405            }
406        }
407
408        Ok(frame_bytes)
409    }
410
411    pub(crate) fn tcp() -> Self {
412        Self::new(FormatType::Tcp)
413    }
414
415    #[cfg(feature = "serial")]
416    pub(crate) fn rtu() -> Self {
417        Self::new(FormatType::Rtu)
418    }
419}
420
421pub(crate) struct FramedReader {
422    parser: FrameParser,
423    buffer: ReadBuffer,
424}
425
426impl FramedReader {
427    pub(crate) fn tcp() -> Self {
428        Self::new(FrameParser::Tcp(MbapParser::new()))
429    }
430
431    #[cfg(feature = "serial")]
432    pub(crate) fn rtu_request() -> Self {
433        Self::new(FrameParser::Rtu(
434            crate::serial::frame::RtuParser::new_request_parser(),
435        ))
436    }
437
438    #[cfg(feature = "serial")]
439    pub(crate) fn rtu_response() -> Self {
440        Self::new(FrameParser::Rtu(
441            crate::serial::frame::RtuParser::new_response_parser(),
442        ))
443    }
444
445    fn new(parser: FrameParser) -> Self {
446        Self {
447            parser,
448            buffer: ReadBuffer::new(),
449        }
450    }
451
452    pub(crate) async fn next_frame(
453        &mut self,
454        io: &mut PhysLayer,
455        decode_level: DecodeLevel,
456    ) -> Result<Frame, RequestError> {
457        loop {
458            match self.parser.parse(&mut self.buffer, decode_level.frame) {
459                Ok(Some(frame)) => return Ok(frame),
460                Ok(None) => {
461                    self.buffer.read_some(io, decode_level.physical).await?;
462                }
463                Err(err) => {
464                    self.parser.reset();
465                    return Err(err);
466                }
467            }
468        }
469    }
470}