bacnet_emb/application_protocol/
segment.rs

1// TODO: create an example for this as it is not clear how it should be used in practice
2//   Especially because of the "special case" function in there
3
4#[cfg(feature = "alloc")]
5use {
6    crate::common::spooky::Phantom,
7    alloc::{vec, vec::Vec},
8};
9
10use crate::{
11    application_protocol::application_pdu::{ApduType, PduFlags},
12    common::{
13        error::Error,
14        io::{Reader, Writer},
15    },
16};
17
18#[cfg(not(feature = "alloc"))]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20#[derive(Debug, Clone)]
21pub struct Segment<'a> {
22    pub apdu_type: ApduType,
23    pub more_follows: bool,
24
25    pub invoke_id: u8,
26    pub sequence_number: u8,
27    pub window_size: u8, // number of segments before an Segment-ACK must be sent
28    pub service_choice: u8,
29
30    // apdu data
31    pub data: &'a [u8],
32}
33
34#[cfg(feature = "alloc")]
35#[cfg_attr(feature = "defmt", derive(defmt::Format))]
36#[derive(Debug, Clone)]
37pub struct Segment<'a> {
38    pub apdu_type: ApduType,
39    pub more_follows: bool,
40
41    pub invoke_id: u8,
42    pub sequence_number: u8,
43    pub window_size: u8, // number of segments before an Segment-ACK must be sent
44    pub service_choice: u8,
45
46    // apdu data
47    pub data: Vec<u8>,
48    _phantom: &'a Phantom,
49}
50
51impl<'a> Segment<'a> {
52    #[cfg(feature = "alloc")]
53    pub fn new(
54        apdu_type: ApduType,
55        more_follows: bool,
56        invoke_id: u8,
57        sequence_number: u8,
58        window_size: u8,
59        service_choice: u8,
60        data: Vec<u8>,
61    ) -> Self {
62        use crate::common::spooky::PHANTOM;
63
64        Segment {
65            apdu_type,
66            more_follows,
67            invoke_id,
68            sequence_number,
69            window_size,
70            service_choice,
71            data,
72            _phantom: &PHANTOM,
73        }
74    }
75
76    #[cfg(not(feature = "alloc"))]
77    pub fn new(
78        apdu_type: ApduType,
79        more_follows: bool,
80        invoke_id: u8,
81        sequence_number: u8,
82        window_size: u8,
83        service_choice: u8,
84        data: &'a [u8],
85    ) -> Self {
86        Segment {
87            apdu_type,
88            more_follows,
89            invoke_id,
90            sequence_number,
91            window_size,
92            service_choice,
93            data,
94        }
95    }
96
97    #[cfg_attr(feature = "alloc", bacnet_macros::remove_lifetimes_from_fn_args)]
98    pub fn decode(
99        more_follows: bool,
100        apdu_type: ApduType,
101        reader: &mut Reader,
102        buf: &'a [u8],
103    ) -> Result<Self, Error> {
104        let invoke_id = reader.read_byte(buf)?;
105        let sequence_number = reader.read_byte(buf)?;
106        let window_size = reader.read_byte(buf)?;
107        let service_choice = reader.read_byte(buf)?;
108        let data = Self::decode_data(reader, buf)?;
109
110        let segment = Self::new(
111            apdu_type,
112            more_follows,
113            invoke_id,
114            sequence_number,
115            window_size,
116            service_choice,
117            data,
118        );
119        Ok(segment)
120    }
121
122    #[cfg(feature = "alloc")]
123    fn decode_data(reader: &mut Reader, buf: &[u8]) -> Result<Vec<u8>, Error> {
124        // read bytes into owned datastructure
125        let len = reader.end - reader.index;
126        let mut data = vec![0u8; len];
127        let slice = reader.read_slice(len, buf)?;
128        data.copy_from_slice(slice);
129        Ok(data)
130    }
131
132    #[cfg(not(feature = "alloc"))]
133    fn decode_data(reader: &mut Reader, buf: &'a [u8]) -> Result<&'a [u8], Error> {
134        // read bytes into owned datastructure
135        let len = reader.end - reader.index;
136        let data = reader.read_slice(len, buf)?;
137        Ok(data)
138    }
139
140    pub fn encode(&self, writer: &mut Writer) {
141        let mut control = ((self.apdu_type.clone() as u8) << 4) | PduFlags::SegmentedMessage as u8;
142        if self.more_follows {
143            control |= PduFlags::MoreFollows as u8;
144        }
145        writer.push(control);
146        writer.push(self.invoke_id);
147        writer.push(self.sequence_number);
148        writer.push(self.window_size);
149        writer.push(self.service_choice);
150        writer.extend_from_slice(&self.data);
151    }
152
153    // a special case encoder for when this segment is being accumulated
154    // into an unsegmented APDU.
155    // returns number of bytes written (TODO: why? - this is redundant and can be calculated by the client)
156    pub fn encode_for_accumulation(&self, writer: &mut Writer) -> usize {
157        let start = writer.index;
158        if self.sequence_number == 0 {
159            writer.push((self.apdu_type.clone() as u8) << 4);
160            writer.push(self.invoke_id);
161            writer.push(self.service_choice);
162        }
163        writer.extend_from_slice(&self.data);
164        writer.index - start
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use crate::{
171        application_protocol::application_pdu::ApduType,
172        common::io::{Reader, Writer},
173    };
174
175    use super::Segment;
176
177    #[test]
178    fn reversable() {
179        // decoding
180        let input: [u8; 7] = [60, 1, 0, 1, 1, 2, 3];
181        let mut reader = Reader::new_with_len(input.len() - 1);
182        let decoded =
183            Segment::decode(true, ApduType::ComplexAck, &mut reader, &input[1..]).unwrap();
184
185        // encoding
186        let mut output: [u8; 7] = [0; 7];
187        let mut writer = Writer::new(&mut output);
188        decoded.encode(&mut writer);
189        assert_eq!(input, output);
190    }
191
192    #[test]
193    fn decoding() {
194        // decoding
195        let input: [u8; 6] = [1, 12, 1, 1, 2, 3];
196        let mut reader = Reader::new_with_len(input.len());
197        let decoded = Segment::decode(false, ApduType::ComplexAck, &mut reader, &input).unwrap();
198        assert_eq!(decoded.more_follows, false);
199        assert_eq!(decoded.sequence_number, 12);
200        assert_eq!(decoded.window_size, 1);
201        assert_eq!(decoded.apdu_type, ApduType::ComplexAck);
202    }
203}