1use bacnet_encoding::{primitives, tags};
6use bacnet_types::enums::PropertyIdentifier;
7use bacnet_types::error::Error;
8use bacnet_types::primitives::{Date, ObjectIdentifier, Time};
9use bytes::BytesMut;
10
11fn checked_slice<'a>(
13 content: &'a [u8],
14 offset: usize,
15 context: &str,
16) -> Result<(&'a [u8], usize), Error> {
17 let (t, p) = tags::decode_tag(content, offset)?;
18 let end = p + t.length as usize;
19 if end > content.len() {
20 return Err(Error::decoding(p, format!("{context} truncated")));
21 }
22 Ok((&content[p..end], end))
23}
24
25#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct ReadRangeRequest {
28 pub object_identifier: ObjectIdentifier,
29 pub property_identifier: PropertyIdentifier,
30 pub property_array_index: Option<u32>,
31 pub range: Option<RangeSpec>,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
37pub enum RangeSpec {
38 ByPosition { reference_index: u32, count: i32 },
40 BySequenceNumber { reference_seq: u32, count: i32 },
42 ByTime {
44 reference_time: (Date, Time),
45 count: i32,
46 },
47}
48
49impl ReadRangeRequest {
50 pub fn encode(&self, buf: &mut BytesMut) {
51 primitives::encode_ctx_object_id(buf, 0, &self.object_identifier);
53 primitives::encode_ctx_enumerated(buf, 1, self.property_identifier.to_raw());
55 if let Some(idx) = self.property_array_index {
57 primitives::encode_ctx_unsigned(buf, 2, idx as u64);
58 }
59 if let Some(ref range) = self.range {
61 match range {
62 RangeSpec::ByPosition {
63 reference_index,
64 count,
65 } => {
66 tags::encode_opening_tag(buf, 3);
67 primitives::encode_app_unsigned(buf, *reference_index as u64);
68 primitives::encode_app_signed(buf, *count);
69 tags::encode_closing_tag(buf, 3);
70 }
71 RangeSpec::BySequenceNumber {
72 reference_seq,
73 count,
74 } => {
75 tags::encode_opening_tag(buf, 6);
76 primitives::encode_app_unsigned(buf, *reference_seq as u64);
77 primitives::encode_app_signed(buf, *count);
78 tags::encode_closing_tag(buf, 6);
79 }
80 RangeSpec::ByTime {
81 reference_time,
82 count,
83 } => {
84 tags::encode_opening_tag(buf, 7);
85 primitives::encode_app_date(buf, &reference_time.0);
86 primitives::encode_app_time(buf, &reference_time.1);
87 primitives::encode_app_signed(buf, *count);
88 tags::encode_closing_tag(buf, 7);
89 }
90 }
91 }
92 }
93
94 pub fn decode(data: &[u8]) -> Result<Self, Error> {
95 let mut offset = 0;
96
97 let (tag, pos) = tags::decode_tag(data, offset)?;
99 let end = pos + tag.length as usize;
100 if end > data.len() {
101 return Err(Error::decoding(
102 pos,
103 "ReadRange request truncated at object-id",
104 ));
105 }
106 let object_identifier = ObjectIdentifier::decode(&data[pos..end])?;
107 offset = end;
108
109 let (tag, pos) = tags::decode_tag(data, offset)?;
111 let end = pos + tag.length as usize;
112 if end > data.len() {
113 return Err(Error::decoding(
114 pos,
115 "ReadRange request truncated at property-id",
116 ));
117 }
118 let property_identifier =
119 PropertyIdentifier::from_raw(primitives::decode_unsigned(&data[pos..end])? as u32);
120 offset = end;
121
122 let mut property_array_index = None;
124 if offset < data.len() {
125 let (opt_data, new_offset) = tags::decode_optional_context(data, offset, 2)?;
126 if let Some(content) = opt_data {
127 property_array_index = Some(primitives::decode_unsigned(content)? as u32);
128 offset = new_offset;
129 }
130 }
131
132 let mut range = None;
134 if offset < data.len() {
135 let (tag, tag_end) = tags::decode_tag(data, offset)?;
136 if tag.is_opening_tag(3) {
137 let (content, new_offset) = tags::extract_context_value(data, tag_end, 3)?;
139 let (slice, inner_offset) =
140 checked_slice(content, 0, "ReadRange byPosition reference-index")?;
141 let reference_index = primitives::decode_unsigned(slice)? as u32;
142 let (slice, _) =
143 checked_slice(content, inner_offset, "ReadRange byPosition count")?;
144 let count = primitives::decode_signed(slice)?;
145 range = Some(RangeSpec::ByPosition {
146 reference_index,
147 count,
148 });
149 offset = new_offset;
150 } else if tag.is_opening_tag(6) {
151 let (content, new_offset) = tags::extract_context_value(data, tag_end, 6)?;
153 let (slice, inner_offset) =
154 checked_slice(content, 0, "ReadRange bySequenceNumber reference-seq")?;
155 let reference_seq = primitives::decode_unsigned(slice)? as u32;
156 let (slice, _) =
157 checked_slice(content, inner_offset, "ReadRange bySequenceNumber count")?;
158 let count = primitives::decode_signed(slice)?;
159 range = Some(RangeSpec::BySequenceNumber {
160 reference_seq,
161 count,
162 });
163 offset = new_offset;
164 } else if tag.is_opening_tag(7) {
165 let (content, new_offset) = tags::extract_context_value(data, tag_end, 7)?;
167 let (slice, inner_offset) = checked_slice(content, 0, "ReadRange byTime date")?;
168 let date = Date::decode(slice)?;
169 let (slice, inner_offset) =
170 checked_slice(content, inner_offset, "ReadRange byTime time")?;
171 let time = Time::decode(slice)?;
172 let (slice, _) = checked_slice(content, inner_offset, "ReadRange byTime count")?;
173 let count = primitives::decode_signed(slice)?;
174 range = Some(RangeSpec::ByTime {
175 reference_time: (date, time),
176 count,
177 });
178 offset = new_offset;
179 }
180 }
181 let _ = offset;
182
183 Ok(Self {
184 object_identifier,
185 property_identifier,
186 property_array_index,
187 range,
188 })
189 }
190}
191
192#[derive(Debug, Clone)]
194pub struct ReadRangeAck {
195 pub object_identifier: ObjectIdentifier,
196 pub property_identifier: PropertyIdentifier,
197 pub property_array_index: Option<u32>,
198 pub result_flags: (bool, bool, bool),
200 pub item_count: u32,
201 pub item_data: Vec<u8>,
203 pub first_sequence_number: Option<u32>,
205}
206
207impl ReadRangeAck {
208 pub fn encode(&self, buf: &mut BytesMut) {
209 primitives::encode_ctx_object_id(buf, 0, &self.object_identifier);
211 primitives::encode_ctx_enumerated(buf, 1, self.property_identifier.to_raw());
213 if let Some(idx) = self.property_array_index {
215 primitives::encode_ctx_unsigned(buf, 2, idx as u64);
216 }
217 let mut flags: u8 = 0;
219 if self.result_flags.0 {
220 flags |= 0x80;
221 }
222 if self.result_flags.1 {
223 flags |= 0x40;
224 }
225 if self.result_flags.2 {
226 flags |= 0x20;
227 }
228 primitives::encode_ctx_bit_string(buf, 3, 5, &[flags]);
229 primitives::encode_ctx_unsigned(buf, 4, self.item_count as u64);
231 tags::encode_opening_tag(buf, 5);
233 buf.extend_from_slice(&self.item_data);
234 tags::encode_closing_tag(buf, 5);
235 if let Some(seq) = self.first_sequence_number {
237 primitives::encode_ctx_unsigned(buf, 6, seq as u64);
238 }
239 }
240
241 pub fn decode(data: &[u8]) -> Result<Self, Error> {
242 let mut offset = 0;
243
244 let (tag, pos) = tags::decode_tag(data, offset)?;
246 let end = pos + tag.length as usize;
247 if end > data.len() {
248 return Err(Error::decoding(pos, "ReadRange ACK truncated at object-id"));
249 }
250 let object_identifier = ObjectIdentifier::decode(&data[pos..end])?;
251 offset = end;
252
253 let (tag, pos) = tags::decode_tag(data, offset)?;
255 let end = pos + tag.length as usize;
256 if end > data.len() {
257 return Err(Error::decoding(
258 pos,
259 "ReadRange ACK truncated at property-id",
260 ));
261 }
262 let property_identifier =
263 PropertyIdentifier::from_raw(primitives::decode_unsigned(&data[pos..end])? as u32);
264 offset = end;
265
266 let mut property_array_index = None;
268 let (opt_data, new_offset) = tags::decode_optional_context(data, offset, 2)?;
269 if let Some(content) = opt_data {
270 property_array_index = Some(primitives::decode_unsigned(content)? as u32);
271 offset = new_offset;
272 }
273
274 let (tag, pos) = tags::decode_tag(data, offset)?;
276 let end = pos + tag.length as usize;
277 if end > data.len() {
278 return Err(Error::decoding(
279 pos,
280 "ReadRange ACK truncated at result-flags",
281 ));
282 }
283 let (_, bits) = primitives::decode_bit_string(&data[pos..end])?;
284 let b = bits.first().copied().unwrap_or(0);
285 let result_flags = (b & 0x80 != 0, b & 0x40 != 0, b & 0x20 != 0);
286 offset = end;
287
288 let (tag, pos) = tags::decode_tag(data, offset)?;
290 let end = pos + tag.length as usize;
291 if end > data.len() {
292 return Err(Error::decoding(
293 pos,
294 "ReadRange ACK truncated at item-count",
295 ));
296 }
297 let item_count = primitives::decode_unsigned(&data[pos..end])? as u32;
298 offset = end;
299
300 let (_tag, tag_end) = tags::decode_tag(data, offset)?;
302 let (content, _new_offset) = tags::extract_context_value(data, tag_end, 5)?;
303 let item_data = content.to_vec();
304 let mut new_offset = _new_offset;
305
306 let mut first_sequence_number = None;
308 if new_offset < data.len() {
309 let (opt_data, after) = tags::decode_optional_context(data, new_offset, 6)?;
310 if let Some(content) = opt_data {
311 first_sequence_number = Some(primitives::decode_unsigned(content)? as u32);
312 new_offset = after;
313 }
314 }
315 let _ = new_offset;
316
317 Ok(Self {
318 object_identifier,
319 property_identifier,
320 property_array_index,
321 result_flags,
322 item_count,
323 item_data,
324 first_sequence_number,
325 })
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332 use bacnet_types::enums::ObjectType;
333 use bacnet_types::primitives::{Date, Time};
334
335 fn make_oid() -> ObjectIdentifier {
336 ObjectIdentifier::new(ObjectType::TREND_LOG, 1).unwrap()
337 }
338
339 #[test]
340 fn request_round_trip() {
341 let req = ReadRangeRequest {
342 object_identifier: make_oid(),
343 property_identifier: PropertyIdentifier::LOG_BUFFER,
344 property_array_index: None,
345 range: Some(RangeSpec::ByPosition {
346 reference_index: 1,
347 count: 10,
348 }),
349 };
350 let mut buf = BytesMut::new();
351 req.encode(&mut buf);
352 let decoded = ReadRangeRequest::decode(&buf).unwrap();
353 assert_eq!(decoded.object_identifier, req.object_identifier);
354 assert_eq!(decoded.property_identifier, req.property_identifier);
355 assert_eq!(decoded.range, req.range);
356 }
357
358 #[test]
359 fn request_no_range() {
360 let req = ReadRangeRequest {
361 object_identifier: make_oid(),
362 property_identifier: PropertyIdentifier::LOG_BUFFER,
363 property_array_index: None,
364 range: None,
365 };
366 let mut buf = BytesMut::new();
367 req.encode(&mut buf);
368 let decoded = ReadRangeRequest::decode(&buf).unwrap();
369 assert!(decoded.range.is_none());
370 }
371
372 #[test]
373 fn request_by_sequence_number() {
374 let req = ReadRangeRequest {
375 object_identifier: make_oid(),
376 property_identifier: PropertyIdentifier::LOG_BUFFER,
377 property_array_index: None,
378 range: Some(RangeSpec::BySequenceNumber {
379 reference_seq: 100,
380 count: -5,
381 }),
382 };
383 let mut buf = BytesMut::new();
384 req.encode(&mut buf);
385 let decoded = ReadRangeRequest::decode(&buf).unwrap();
386 assert_eq!(decoded.range, req.range);
387 }
388
389 #[test]
390 fn ack_round_trip() {
391 let ack = ReadRangeAck {
392 object_identifier: make_oid(),
393 property_identifier: PropertyIdentifier::LOG_BUFFER,
394 property_array_index: None,
395 result_flags: (true, false, true),
396 item_count: 2,
397 item_data: vec![0xAA, 0xBB, 0xCC],
398 first_sequence_number: None,
399 };
400 let mut buf = BytesMut::new();
401 ack.encode(&mut buf);
402 let decoded = ReadRangeAck::decode(&buf).unwrap();
403 assert_eq!(decoded.object_identifier, ack.object_identifier);
404 assert_eq!(decoded.result_flags, (true, false, true));
405 assert_eq!(decoded.item_count, 2);
406 assert_eq!(decoded.item_data, vec![0xAA, 0xBB, 0xCC]);
407 assert_eq!(decoded.first_sequence_number, None);
408 }
409
410 #[test]
411 fn ack_round_trip_with_first_sequence_number() {
412 let ack = ReadRangeAck {
413 object_identifier: make_oid(),
414 property_identifier: PropertyIdentifier::LOG_BUFFER,
415 property_array_index: None,
416 result_flags: (true, true, false),
417 item_count: 5,
418 item_data: vec![0x01, 0x02],
419 first_sequence_number: Some(42),
420 };
421 let mut buf = BytesMut::new();
422 ack.encode(&mut buf);
423 let decoded = ReadRangeAck::decode(&buf).unwrap();
424 assert_eq!(decoded.object_identifier, ack.object_identifier);
425 assert_eq!(decoded.result_flags, (true, true, false));
426 assert_eq!(decoded.item_count, 5);
427 assert_eq!(decoded.item_data, vec![0x01, 0x02]);
428 assert_eq!(decoded.first_sequence_number, Some(42));
429 }
430
431 #[test]
432 fn request_by_time() {
433 let req = ReadRangeRequest {
434 object_identifier: make_oid(),
435 property_identifier: PropertyIdentifier::LOG_BUFFER,
436 property_array_index: None,
437 range: Some(RangeSpec::ByTime {
438 reference_time: (
439 Date {
440 year: 126, month: 3,
442 day: 1,
443 day_of_week: 7, },
445 Time {
446 hour: 14,
447 minute: 30,
448 second: 0,
449 hundredths: 0,
450 },
451 ),
452 count: -10,
453 }),
454 };
455 let mut buf = BytesMut::new();
456 req.encode(&mut buf);
457 let decoded = ReadRangeRequest::decode(&buf).unwrap();
458 assert_eq!(decoded.range, req.range);
459 }
460
461 #[test]
466 fn test_decode_read_range_request_empty_input() {
467 assert!(ReadRangeRequest::decode(&[]).is_err());
468 }
469
470 #[test]
471 fn test_decode_read_range_request_truncated_1_byte() {
472 let req = ReadRangeRequest {
473 object_identifier: make_oid(),
474 property_identifier: PropertyIdentifier::LOG_BUFFER,
475 property_array_index: None,
476 range: Some(RangeSpec::ByPosition {
477 reference_index: 1,
478 count: 10,
479 }),
480 };
481 let mut buf = BytesMut::new();
482 req.encode(&mut buf);
483 assert!(ReadRangeRequest::decode(&buf[..1]).is_err());
484 }
485
486 #[test]
487 fn test_decode_read_range_request_truncated_3_bytes() {
488 let req = ReadRangeRequest {
489 object_identifier: make_oid(),
490 property_identifier: PropertyIdentifier::LOG_BUFFER,
491 property_array_index: None,
492 range: Some(RangeSpec::ByPosition {
493 reference_index: 1,
494 count: 10,
495 }),
496 };
497 let mut buf = BytesMut::new();
498 req.encode(&mut buf);
499 assert!(ReadRangeRequest::decode(&buf[..3]).is_err());
500 }
501
502 #[test]
503 fn test_decode_read_range_request_invalid_tag() {
504 assert!(ReadRangeRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
505 }
506
507 #[test]
508 fn test_decode_read_range_ack_empty_input() {
509 assert!(ReadRangeAck::decode(&[]).is_err());
510 }
511
512 #[test]
513 fn test_decode_read_range_ack_truncated_1_byte() {
514 let ack = ReadRangeAck {
515 object_identifier: make_oid(),
516 property_identifier: PropertyIdentifier::LOG_BUFFER,
517 property_array_index: None,
518 result_flags: (true, false, true),
519 item_count: 2,
520 item_data: vec![0xAA, 0xBB, 0xCC],
521 first_sequence_number: None,
522 };
523 let mut buf = BytesMut::new();
524 ack.encode(&mut buf);
525 assert!(ReadRangeAck::decode(&buf[..1]).is_err());
526 }
527
528 #[test]
529 fn test_decode_read_range_ack_truncated_3_bytes() {
530 let ack = ReadRangeAck {
531 object_identifier: make_oid(),
532 property_identifier: PropertyIdentifier::LOG_BUFFER,
533 property_array_index: None,
534 result_flags: (true, false, true),
535 item_count: 2,
536 item_data: vec![0xAA, 0xBB, 0xCC],
537 first_sequence_number: None,
538 };
539 let mut buf = BytesMut::new();
540 ack.encode(&mut buf);
541 assert!(ReadRangeAck::decode(&buf[..3]).is_err());
542 }
543
544 #[test]
545 fn test_decode_read_range_ack_truncated_half() {
546 let ack = ReadRangeAck {
547 object_identifier: make_oid(),
548 property_identifier: PropertyIdentifier::LOG_BUFFER,
549 property_array_index: None,
550 result_flags: (true, false, true),
551 item_count: 2,
552 item_data: vec![0xAA, 0xBB, 0xCC],
553 first_sequence_number: None,
554 };
555 let mut buf = BytesMut::new();
556 ack.encode(&mut buf);
557 let half = buf.len() / 2;
558 assert!(ReadRangeAck::decode(&buf[..half]).is_err());
559 }
560
561 #[test]
562 fn test_decode_read_range_ack_invalid_tag() {
563 assert!(ReadRangeAck::decode(&[0xFF, 0xFF, 0xFF]).is_err());
564 }
565
566 #[test]
567 fn read_range_request_truncated_inner_tag() {
568 let data = [
570 0x0C, 0x05, 0x00, 0x00, 0x01, 0x19, 0x83, 0x3E, 0x21, 50, 0x01, 0x3F,
576 ];
577 assert!(ReadRangeRequest::decode(&data).is_err());
578 }
579}