bacnet_encoding/constructed/
event_log_record.rs1use super::log_fields::{decode_log_status, decode_timestamp, encode_log_status, encode_timestamp};
12use super::tagged::{contents, decode_ctx_constructed, expect_end};
13use super::{decode_event_notification_tolerant, encode_event_notification};
14use crate::{primitives, tags};
15use bacnet_types::constructed::{BACnetEventLogRecord, EventLogDatum};
16use bacnet_types::error::Error;
17use bytes::BytesMut;
18
19const RECORD: &str = "BACnetEventLogRecord";
20const TIMESTAMP: &str = "BACnetEventLogRecord timestamp";
21const DATUM: &str = "BACnetEventLogRecord log-datum";
22
23const LOG_STATUS: u8 = 0;
24const NOTIFICATION: u8 = 1;
25const TIME_CHANGE: u8 = 2;
26
27pub fn encode_event_log_record(
33 record: &BACnetEventLogRecord,
34 buf: &mut BytesMut,
35) -> Result<(), Error> {
36 let mut out = BytesMut::new();
37 encode_timestamp(&mut out, &record.date, &record.time);
38 tags::encode_opening_tag(&mut out, 1);
39 match &record.log_datum {
40 EventLogDatum::LogStatus(status) => encode_log_status(&mut out, LOG_STATUS, *status),
41 EventLogDatum::Notification(notification) => {
42 tags::encode_opening_tag(&mut out, NOTIFICATION);
43 encode_event_notification(notification, &mut out)?;
44 tags::encode_closing_tag(&mut out, NOTIFICATION);
45 }
46 EventLogDatum::TimeChange(seconds) => {
47 primitives::encode_ctx_real(&mut out, TIME_CHANGE, *seconds)
48 }
49 }
50 tags::encode_closing_tag(&mut out, 1);
51 buf.extend_from_slice(&out);
52 Ok(())
53}
54
55pub fn decode_event_log_record(
64 data: &[u8],
65 offset: usize,
66) -> Result<(BACnetEventLogRecord, usize), Error> {
67 let (date, time, datum_start) = decode_timestamp(data, offset, TIMESTAMP)?;
68 let (body, end) = decode_ctx_constructed(data, datum_start, 1, DATUM)?;
69 let log_datum = decode_datum(body, datum_start)?;
70 Ok((
71 BACnetEventLogRecord {
72 date,
73 time,
74 log_datum,
75 },
76 end,
77 ))
78}
79
80fn decode_datum(data: &[u8], offset: usize) -> Result<EventLogDatum, Error> {
81 let (tag, start) = tags::decode_tag(data, 0)?;
82 let (datum, end) = if tag.is_context(LOG_STATUS) {
83 let (octets, end) = contents(data, start, tag.length)?;
84 let status = decode_log_status(octets, offset, RECORD)?;
85 (EventLogDatum::LogStatus(status), end)
86 } else if tag.is_opening_tag(NOTIFICATION) {
87 let (parameters, end) = tags::extract_context_value(data, start, NOTIFICATION)?;
88 let notification = decode_event_notification_tolerant(parameters)?;
89 (EventLogDatum::Notification(notification), end)
90 } else if tag.is_context(TIME_CHANGE) {
91 let (octets, end) = contents(data, start, tag.length)?;
92 (
93 EventLogDatum::TimeChange(primitives::decode_real(octets)?),
94 end,
95 )
96 } else {
97 return Err(Error::decoding(
98 offset,
99 "BACnetEventLogRecord log-datum expected context [0], constructed [1], or context [2]",
100 ));
101 };
102 expect_end(data, end, offset, DATUM)?;
103 Ok(datum)
104}