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 if let Some(ref r) = range {
184 let count = match r {
185 RangeSpec::ByPosition { count, .. } => *count,
186 RangeSpec::BySequenceNumber { count, .. } => *count,
187 RangeSpec::ByTime { count, .. } => *count,
188 };
189 if count == 0 {
190 return Err(Error::Encoding("ReadRange count may not be zero".into()));
191 }
192 }
193
194 Ok(Self {
195 object_identifier,
196 property_identifier,
197 property_array_index,
198 range,
199 })
200 }
201}
202
203#[derive(Debug, Clone)]
205pub struct ReadRangeAck {
206 pub object_identifier: ObjectIdentifier,
207 pub property_identifier: PropertyIdentifier,
208 pub property_array_index: Option<u32>,
209 pub result_flags: (bool, bool, bool),
211 pub item_count: u32,
212 pub item_data: Vec<u8>,
214 pub first_sequence_number: Option<u32>,
216}
217
218impl ReadRangeAck {
219 pub fn encode(&self, buf: &mut BytesMut) {
220 primitives::encode_ctx_object_id(buf, 0, &self.object_identifier);
222 primitives::encode_ctx_enumerated(buf, 1, self.property_identifier.to_raw());
224 if let Some(idx) = self.property_array_index {
226 primitives::encode_ctx_unsigned(buf, 2, idx as u64);
227 }
228 let mut flags: u8 = 0;
230 if self.result_flags.0 {
231 flags |= 0x80;
232 }
233 if self.result_flags.1 {
234 flags |= 0x40;
235 }
236 if self.result_flags.2 {
237 flags |= 0x20;
238 }
239 primitives::encode_ctx_bit_string(buf, 3, 5, &[flags]);
240 primitives::encode_ctx_unsigned(buf, 4, self.item_count as u64);
242 tags::encode_opening_tag(buf, 5);
244 buf.extend_from_slice(&self.item_data);
245 tags::encode_closing_tag(buf, 5);
246 if let Some(seq) = self.first_sequence_number {
248 primitives::encode_ctx_unsigned(buf, 6, seq as u64);
249 }
250 }
251
252 pub fn decode(data: &[u8]) -> Result<Self, Error> {
253 let mut offset = 0;
254
255 let (tag, pos) = tags::decode_tag(data, offset)?;
257 let end = pos + tag.length as usize;
258 if end > data.len() {
259 return Err(Error::decoding(pos, "ReadRange ACK truncated at object-id"));
260 }
261 let object_identifier = ObjectIdentifier::decode(&data[pos..end])?;
262 offset = end;
263
264 let (tag, pos) = tags::decode_tag(data, offset)?;
266 let end = pos + tag.length as usize;
267 if end > data.len() {
268 return Err(Error::decoding(
269 pos,
270 "ReadRange ACK truncated at property-id",
271 ));
272 }
273 let property_identifier =
274 PropertyIdentifier::from_raw(primitives::decode_unsigned(&data[pos..end])? as u32);
275 offset = end;
276
277 let mut property_array_index = None;
279 let (opt_data, new_offset) = tags::decode_optional_context(data, offset, 2)?;
280 if let Some(content) = opt_data {
281 property_array_index = Some(primitives::decode_unsigned(content)? as u32);
282 offset = new_offset;
283 }
284
285 let (tag, pos) = tags::decode_tag(data, offset)?;
287 let end = pos + tag.length as usize;
288 if end > data.len() {
289 return Err(Error::decoding(
290 pos,
291 "ReadRange ACK truncated at result-flags",
292 ));
293 }
294 let (_, bits) = primitives::decode_bit_string(&data[pos..end])?;
295 let b = bits.first().copied().unwrap_or(0);
296 let result_flags = (b & 0x80 != 0, b & 0x40 != 0, b & 0x20 != 0);
297 offset = end;
298
299 let (tag, pos) = tags::decode_tag(data, offset)?;
301 let end = pos + tag.length as usize;
302 if end > data.len() {
303 return Err(Error::decoding(
304 pos,
305 "ReadRange ACK truncated at item-count",
306 ));
307 }
308 let item_count = primitives::decode_unsigned(&data[pos..end])? as u32;
309 offset = end;
310
311 let (_tag, tag_end) = tags::decode_tag(data, offset)?;
313 let (content, _new_offset) = tags::extract_context_value(data, tag_end, 5)?;
314 let item_data = content.to_vec();
315 let mut new_offset = _new_offset;
316
317 let mut first_sequence_number = None;
319 if new_offset < data.len() {
320 let (opt_data, after) = tags::decode_optional_context(data, new_offset, 6)?;
321 if let Some(content) = opt_data {
322 first_sequence_number = Some(primitives::decode_unsigned(content)? as u32);
323 new_offset = after;
324 }
325 }
326 let _ = new_offset;
327
328 Ok(Self {
329 object_identifier,
330 property_identifier,
331 property_array_index,
332 result_flags,
333 item_count,
334 item_data,
335 first_sequence_number,
336 })
337 }
338}
339
340#[cfg(test)]
341mod tests {
342 use super::*;
343 use bacnet_types::enums::ObjectType;
344 use bacnet_types::primitives::{Date, Time};
345
346 fn make_oid() -> ObjectIdentifier {
347 ObjectIdentifier::new(ObjectType::TREND_LOG, 1).unwrap()
348 }
349
350 #[test]
351 fn request_round_trip() {
352 let req = ReadRangeRequest {
353 object_identifier: make_oid(),
354 property_identifier: PropertyIdentifier::LOG_BUFFER,
355 property_array_index: None,
356 range: Some(RangeSpec::ByPosition {
357 reference_index: 1,
358 count: 10,
359 }),
360 };
361 let mut buf = BytesMut::new();
362 req.encode(&mut buf);
363 let decoded = ReadRangeRequest::decode(&buf).unwrap();
364 assert_eq!(decoded.object_identifier, req.object_identifier);
365 assert_eq!(decoded.property_identifier, req.property_identifier);
366 assert_eq!(decoded.range, req.range);
367 }
368
369 #[test]
370 fn request_no_range() {
371 let req = ReadRangeRequest {
372 object_identifier: make_oid(),
373 property_identifier: PropertyIdentifier::LOG_BUFFER,
374 property_array_index: None,
375 range: None,
376 };
377 let mut buf = BytesMut::new();
378 req.encode(&mut buf);
379 let decoded = ReadRangeRequest::decode(&buf).unwrap();
380 assert!(decoded.range.is_none());
381 }
382
383 #[test]
384 fn request_by_sequence_number() {
385 let req = ReadRangeRequest {
386 object_identifier: make_oid(),
387 property_identifier: PropertyIdentifier::LOG_BUFFER,
388 property_array_index: None,
389 range: Some(RangeSpec::BySequenceNumber {
390 reference_seq: 100,
391 count: -5,
392 }),
393 };
394 let mut buf = BytesMut::new();
395 req.encode(&mut buf);
396 let decoded = ReadRangeRequest::decode(&buf).unwrap();
397 assert_eq!(decoded.range, req.range);
398 }
399
400 #[test]
401 fn ack_round_trip() {
402 let ack = ReadRangeAck {
403 object_identifier: make_oid(),
404 property_identifier: PropertyIdentifier::LOG_BUFFER,
405 property_array_index: None,
406 result_flags: (true, false, true),
407 item_count: 2,
408 item_data: vec![0xAA, 0xBB, 0xCC],
409 first_sequence_number: None,
410 };
411 let mut buf = BytesMut::new();
412 ack.encode(&mut buf);
413 let decoded = ReadRangeAck::decode(&buf).unwrap();
414 assert_eq!(decoded.object_identifier, ack.object_identifier);
415 assert_eq!(decoded.result_flags, (true, false, true));
416 assert_eq!(decoded.item_count, 2);
417 assert_eq!(decoded.item_data, vec![0xAA, 0xBB, 0xCC]);
418 assert_eq!(decoded.first_sequence_number, None);
419 }
420
421 #[test]
422 fn ack_round_trip_with_first_sequence_number() {
423 let ack = ReadRangeAck {
424 object_identifier: make_oid(),
425 property_identifier: PropertyIdentifier::LOG_BUFFER,
426 property_array_index: None,
427 result_flags: (true, true, false),
428 item_count: 5,
429 item_data: vec![0x01, 0x02],
430 first_sequence_number: Some(42),
431 };
432 let mut buf = BytesMut::new();
433 ack.encode(&mut buf);
434 let decoded = ReadRangeAck::decode(&buf).unwrap();
435 assert_eq!(decoded.object_identifier, ack.object_identifier);
436 assert_eq!(decoded.result_flags, (true, true, false));
437 assert_eq!(decoded.item_count, 5);
438 assert_eq!(decoded.item_data, vec![0x01, 0x02]);
439 assert_eq!(decoded.first_sequence_number, Some(42));
440 }
441
442 #[test]
443 fn request_by_time() {
444 let req = ReadRangeRequest {
445 object_identifier: make_oid(),
446 property_identifier: PropertyIdentifier::LOG_BUFFER,
447 property_array_index: None,
448 range: Some(RangeSpec::ByTime {
449 reference_time: (
450 Date {
451 year: 126, month: 3,
453 day: 1,
454 day_of_week: 7, },
456 Time {
457 hour: 14,
458 minute: 30,
459 second: 0,
460 hundredths: 0,
461 },
462 ),
463 count: -10,
464 }),
465 };
466 let mut buf = BytesMut::new();
467 req.encode(&mut buf);
468 let decoded = ReadRangeRequest::decode(&buf).unwrap();
469 assert_eq!(decoded.range, req.range);
470 }
471
472 #[test]
477 fn test_decode_read_range_request_empty_input() {
478 assert!(ReadRangeRequest::decode(&[]).is_err());
479 }
480
481 #[test]
482 fn test_decode_read_range_request_truncated_1_byte() {
483 let req = ReadRangeRequest {
484 object_identifier: make_oid(),
485 property_identifier: PropertyIdentifier::LOG_BUFFER,
486 property_array_index: None,
487 range: Some(RangeSpec::ByPosition {
488 reference_index: 1,
489 count: 10,
490 }),
491 };
492 let mut buf = BytesMut::new();
493 req.encode(&mut buf);
494 assert!(ReadRangeRequest::decode(&buf[..1]).is_err());
495 }
496
497 #[test]
498 fn test_decode_read_range_request_truncated_3_bytes() {
499 let req = ReadRangeRequest {
500 object_identifier: make_oid(),
501 property_identifier: PropertyIdentifier::LOG_BUFFER,
502 property_array_index: None,
503 range: Some(RangeSpec::ByPosition {
504 reference_index: 1,
505 count: 10,
506 }),
507 };
508 let mut buf = BytesMut::new();
509 req.encode(&mut buf);
510 assert!(ReadRangeRequest::decode(&buf[..3]).is_err());
511 }
512
513 #[test]
514 fn test_decode_read_range_request_invalid_tag() {
515 assert!(ReadRangeRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
516 }
517
518 #[test]
519 fn test_decode_read_range_ack_empty_input() {
520 assert!(ReadRangeAck::decode(&[]).is_err());
521 }
522
523 #[test]
524 fn test_decode_read_range_ack_truncated_1_byte() {
525 let ack = ReadRangeAck {
526 object_identifier: make_oid(),
527 property_identifier: PropertyIdentifier::LOG_BUFFER,
528 property_array_index: None,
529 result_flags: (true, false, true),
530 item_count: 2,
531 item_data: vec![0xAA, 0xBB, 0xCC],
532 first_sequence_number: None,
533 };
534 let mut buf = BytesMut::new();
535 ack.encode(&mut buf);
536 assert!(ReadRangeAck::decode(&buf[..1]).is_err());
537 }
538
539 #[test]
540 fn test_decode_read_range_ack_truncated_3_bytes() {
541 let ack = ReadRangeAck {
542 object_identifier: make_oid(),
543 property_identifier: PropertyIdentifier::LOG_BUFFER,
544 property_array_index: None,
545 result_flags: (true, false, true),
546 item_count: 2,
547 item_data: vec![0xAA, 0xBB, 0xCC],
548 first_sequence_number: None,
549 };
550 let mut buf = BytesMut::new();
551 ack.encode(&mut buf);
552 assert!(ReadRangeAck::decode(&buf[..3]).is_err());
553 }
554
555 #[test]
556 fn test_decode_read_range_ack_truncated_half() {
557 let ack = ReadRangeAck {
558 object_identifier: make_oid(),
559 property_identifier: PropertyIdentifier::LOG_BUFFER,
560 property_array_index: None,
561 result_flags: (true, false, true),
562 item_count: 2,
563 item_data: vec![0xAA, 0xBB, 0xCC],
564 first_sequence_number: None,
565 };
566 let mut buf = BytesMut::new();
567 ack.encode(&mut buf);
568 let half = buf.len() / 2;
569 assert!(ReadRangeAck::decode(&buf[..half]).is_err());
570 }
571
572 #[test]
573 fn test_decode_read_range_ack_invalid_tag() {
574 assert!(ReadRangeAck::decode(&[0xFF, 0xFF, 0xFF]).is_err());
575 }
576
577 #[test]
578 fn read_range_request_truncated_inner_tag() {
579 let data = [
581 0x0C, 0x05, 0x00, 0x00, 0x01, 0x19, 0x83, 0x3E, 0x21, 50, 0x01, 0x3F,
587 ];
588 assert!(ReadRangeRequest::decode(&data).is_err());
589 }
590}