Skip to main content

rusty_modbus_codec/request/
mei.rs

1//! Encapsulated Interface Transport request: FC 2B (MEI).
2
3use rusty_modbus_types::{DeviceIdCode, FunctionCode, MeiType};
4
5use crate::error::{DecodeError, EncodeError};
6use crate::request::Encode;
7
8/// FC 0x2B — Encapsulated Interface Transport request.
9///
10/// The MEI type selects the transport variant (e.g. `CANopen` General Reference
11/// or Read Device Identification). The remaining `data` is MEI-type-specific.
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct EncapsulatedInterfaceRequest<'buf> {
14    /// MEI type code.
15    pub mei_type: MeiType,
16    /// MEI-type-specific data.
17    pub data: &'buf [u8],
18}
19
20impl<'buf> EncapsulatedInterfaceRequest<'buf> {
21    /// Decode from PDU data after the function code byte.
22    ///
23    /// # Errors
24    ///
25    /// Returns [`DecodeError::Truncated`] if `data` is empty.
26    /// Returns [`DecodeError::UnknownMeiType`] if the MEI type byte is not recognized.
27    /// Returns [`DecodeError::LengthMismatch`] if a known fixed-length MEI request has extra bytes.
28    pub fn decode(data: &'buf [u8]) -> Result<Self, DecodeError> {
29        if data.is_empty() {
30            return Err(DecodeError::Truncated {
31                expected: 1,
32                actual: 0,
33            });
34        }
35        let mei_type = MeiType::from_raw(data[0]).ok_or(DecodeError::UnknownMeiType(data[0]))?;
36        let payload = &data[1..];
37        if mei_type == MeiType::ReadDeviceIdentification {
38            DecodeError::check_exact_len(payload, 2)?;
39            DeviceIdCode::from_raw(payload[0])
40                .ok_or(DecodeError::InvalidDeviceIdCode(payload[0]))?;
41        }
42        Ok(Self {
43            mei_type,
44            data: payload,
45        })
46    }
47}
48
49impl Encode for EncapsulatedInterfaceRequest<'_> {
50    fn encode_into(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
51        let len = self.encoded_len();
52        if buf.len() < len {
53            return Err(EncodeError::BufferTooSmall {
54                required: len,
55                available: buf.len(),
56            });
57        }
58        EncodeError::check_pdu_len(len)?;
59        buf[0] = FunctionCode::EncapsulatedInterfaceTransport.code();
60        buf[1] = self.mei_type.code();
61        buf[2..2 + self.data.len()].copy_from_slice(self.data);
62        Ok(len)
63    }
64
65    fn encoded_len(&self) -> usize {
66        // FC(1) + mei_type(1) + data
67        2 + self.data.len()
68    }
69}