1use bacnet_encoding::primitives;
7use bacnet_encoding::tags;
8use bacnet_types::error::Error;
9use bytes::BytesMut;
10
11use crate::common::MAX_DECODED_ITEMS;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct VTOpenRequest {
22 pub vt_class: u32,
23}
24
25impl VTOpenRequest {
26 pub fn encode(&self, buf: &mut BytesMut) {
27 primitives::encode_app_enumerated(buf, self.vt_class);
28 }
29
30 pub fn decode(data: &[u8]) -> Result<Self, Error> {
31 let (tag, pos) = tags::decode_tag(data, 0)?;
32 let end = pos + tag.length as usize;
33 if end > data.len() {
34 return Err(Error::decoding(pos, "VTOpen truncated at vt-class"));
35 }
36 let vt_class = primitives::decode_unsigned(&data[pos..end])? as u32;
37 Ok(Self { vt_class })
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct VTOpenAck {
46 pub remote_vt_session_identifier: u8,
47}
48
49impl VTOpenAck {
50 pub fn encode(&self, buf: &mut BytesMut) {
51 primitives::encode_app_unsigned(buf, self.remote_vt_session_identifier as u64);
52 }
53
54 pub fn decode(data: &[u8]) -> Result<Self, Error> {
55 let (tag, pos) = tags::decode_tag(data, 0)?;
56 let end = pos + tag.length as usize;
57 if end > data.len() {
58 return Err(Error::decoding(
59 pos,
60 "VTOpenAck truncated at session-identifier",
61 ));
62 }
63 let id = primitives::decode_unsigned(&data[pos..end])? as u8;
64 Ok(Self {
65 remote_vt_session_identifier: id,
66 })
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct VTCloseRequest {
79 pub list_of_remote_vt_session_identifiers: Vec<u8>,
80}
81
82impl VTCloseRequest {
83 pub fn encode(&self, buf: &mut BytesMut) {
84 for &id in &self.list_of_remote_vt_session_identifiers {
85 primitives::encode_app_unsigned(buf, id as u64);
86 }
87 }
88
89 pub fn decode(data: &[u8]) -> Result<Self, Error> {
90 let mut offset = 0;
91 let mut ids = Vec::new();
92 while offset < data.len() {
93 if ids.len() >= MAX_DECODED_ITEMS {
94 return Err(Error::decoding(offset, "VTClose too many session IDs"));
95 }
96 let (tag, pos) = tags::decode_tag(data, offset)?;
97 let end = pos + tag.length as usize;
98 if end > data.len() {
99 return Err(Error::decoding(
100 pos,
101 "VTClose truncated at session-identifier",
102 ));
103 }
104 ids.push(primitives::decode_unsigned(&data[pos..end])? as u8);
105 offset = end;
106 }
107 Ok(Self {
108 list_of_remote_vt_session_identifiers: ids,
109 })
110 }
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
121pub struct VTDataRequest {
122 pub vt_session_identifier: u8,
123 pub vt_new_data: Vec<u8>,
124 pub vt_data_flag: bool,
125}
126
127impl VTDataRequest {
128 pub fn encode(&self, buf: &mut BytesMut) {
129 primitives::encode_app_unsigned(buf, self.vt_session_identifier as u64);
130 primitives::encode_app_octet_string(buf, &self.vt_new_data);
131 primitives::encode_app_boolean(buf, self.vt_data_flag);
132 }
133
134 pub fn decode(data: &[u8]) -> Result<Self, Error> {
135 let mut offset = 0;
136
137 let (tag, pos) = tags::decode_tag(data, offset)?;
138 let end = pos + tag.length as usize;
139 if end > data.len() {
140 return Err(Error::decoding(
141 pos,
142 "VTData truncated at session-identifier",
143 ));
144 }
145 let vt_session_identifier = primitives::decode_unsigned(&data[pos..end])? as u8;
146 offset = end;
147
148 let (tag, pos) = tags::decode_tag(data, offset)?;
149 let end = pos + tag.length as usize;
150 if end > data.len() {
151 return Err(Error::decoding(pos, "VTData truncated at new-data"));
152 }
153 let vt_new_data = data[pos..end].to_vec();
154 offset = end;
155
156 let (tag, pos) = tags::decode_tag(data, offset)?;
157 let vt_data_flag = tag.length != 0;
158 let _ = pos;
159
160 Ok(Self {
161 vt_session_identifier,
162 vt_new_data,
163 vt_data_flag,
164 })
165 }
166}
167
168#[derive(Debug, Clone, PartialEq, Eq)]
170pub struct VTDataAck {
171 pub all_new_data_accepted: Option<bool>,
173 pub accepted_octet_count: Option<u32>,
175}
176
177impl VTDataAck {
178 pub fn encode(&self, buf: &mut BytesMut) {
179 if let Some(v) = self.all_new_data_accepted {
180 primitives::encode_ctx_boolean(buf, 0, v);
181 }
182 if let Some(v) = self.accepted_octet_count {
183 primitives::encode_ctx_unsigned(buf, 1, v as u64);
184 }
185 }
186
187 pub fn decode(data: &[u8]) -> Result<Self, Error> {
188 let mut offset = 0;
189
190 let mut all_new_data_accepted = None;
192 if offset < data.len() {
193 let (opt, new_off) = tags::decode_optional_context(data, offset, 0)?;
194 if let Some(content) = opt {
195 all_new_data_accepted = Some(!content.is_empty() && content[0] != 0);
196 offset = new_off;
197 }
198 }
199
200 let mut accepted_octet_count = None;
202 if offset < data.len() {
203 let (opt, new_off) = tags::decode_optional_context(data, offset, 1)?;
204 if let Some(content) = opt {
205 accepted_octet_count = Some(primitives::decode_unsigned(content)? as u32);
206 offset = new_off;
207 }
208 }
209 let _ = offset;
210
211 Ok(Self {
212 all_new_data_accepted,
213 accepted_octet_count,
214 })
215 }
216}
217
218#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn vt_open_round_trip() {
224 let req = VTOpenRequest { vt_class: 1 };
225 let mut buf = BytesMut::new();
226 req.encode(&mut buf);
227 let decoded = VTOpenRequest::decode(&buf).unwrap();
228 assert_eq!(req, decoded);
229 }
230
231 #[test]
232 fn vt_open_ack_round_trip() {
233 let ack = VTOpenAck {
234 remote_vt_session_identifier: 42,
235 };
236 let mut buf = BytesMut::new();
237 ack.encode(&mut buf);
238 let decoded = VTOpenAck::decode(&buf).unwrap();
239 assert_eq!(ack, decoded);
240 }
241
242 #[test]
243 fn vt_close_round_trip() {
244 let req = VTCloseRequest {
245 list_of_remote_vt_session_identifiers: vec![1, 2, 3],
246 };
247 let mut buf = BytesMut::new();
248 req.encode(&mut buf);
249 let decoded = VTCloseRequest::decode(&buf).unwrap();
250 assert_eq!(req, decoded);
251 }
252
253 #[test]
254 fn vt_close_empty() {
255 let req = VTCloseRequest {
256 list_of_remote_vt_session_identifiers: vec![],
257 };
258 let mut buf = BytesMut::new();
259 req.encode(&mut buf);
260 assert!(buf.is_empty());
261 let decoded = VTCloseRequest::decode(&buf).unwrap();
262 assert_eq!(req, decoded);
263 }
264
265 #[test]
266 fn vt_data_round_trip() {
267 let req = VTDataRequest {
268 vt_session_identifier: 1,
269 vt_new_data: vec![0x48, 0x65, 0x6C, 0x6C, 0x6F], vt_data_flag: true,
271 };
272 let mut buf = BytesMut::new();
273 req.encode(&mut buf);
274 let decoded = VTDataRequest::decode(&buf).unwrap();
275 assert_eq!(req, decoded);
276 }
277
278 #[test]
279 fn vt_data_flag_false() {
280 let req = VTDataRequest {
281 vt_session_identifier: 5,
282 vt_new_data: vec![0x01],
283 vt_data_flag: false,
284 };
285 let mut buf = BytesMut::new();
286 req.encode(&mut buf);
287 let decoded = VTDataRequest::decode(&buf).unwrap();
288 assert_eq!(req, decoded);
289 }
290
291 #[test]
292 fn vt_data_ack_round_trip() {
293 let ack = VTDataAck {
294 all_new_data_accepted: Some(true),
295 accepted_octet_count: Some(100),
296 };
297 let mut buf = BytesMut::new();
298 ack.encode(&mut buf);
299 let decoded = VTDataAck::decode(&buf).unwrap();
300 assert_eq!(ack, decoded);
301 }
302
303 #[test]
304 fn vt_data_ack_empty() {
305 let ack = VTDataAck {
306 all_new_data_accepted: None,
307 accepted_octet_count: None,
308 };
309 let mut buf = BytesMut::new();
310 ack.encode(&mut buf);
311 assert!(buf.is_empty());
312 let decoded = VTDataAck::decode(&buf).unwrap();
313 assert_eq!(ack, decoded);
314 }
315
316 #[test]
317 fn vt_open_empty_input() {
318 assert!(VTOpenRequest::decode(&[]).is_err());
319 }
320
321 #[test]
322 fn vt_data_empty_input() {
323 assert!(VTDataRequest::decode(&[]).is_err());
324 }
325}