bacnet_encoding/constructed/
schedule.rs1#[cfg(not(feature = "std"))]
30use alloc::vec::Vec;
31
32use bacnet_types::constructed::{BACnetSpecialEvent, BACnetTimeValue, SpecialEventPeriod};
33use bacnet_types::error::{DecodeError, DecodingKind, Error};
34use bacnet_types::primitives::Time;
35use bytes::BytesMut;
36
37use super::calendar::{decode_calendar_entry, encode_calendar_entry};
38use super::tagged::{
39 contents, decode_ctx_object_id, decode_ctx_unsigned, expect_closing, expect_end,
40 expect_opening, next_is_closing,
41};
42use super::MAX_FRAMED_ITEMS;
43use crate::primitives;
44use crate::tags::{self, TagClass};
45
46pub fn encode_time_value(buf: &mut BytesMut, time_value: &BACnetTimeValue) -> Result<(), Error> {
52 if !time_value.value.is_primitive() {
53 return Err(Error::encode("time-value: the value must be of a primitive datatype"));
54 }
55 primitives::encode_app_time(buf, &time_value.time);
56 primitives::encode_property_value(buf, &time_value.value)
57}
58
59pub fn decode_time_value(data: &[u8], offset: usize) -> Result<(BACnetTimeValue, usize), Error> {
64 let (tag, content) = tags::decode_tag(data, offset)?;
65 if tag.class != TagClass::Application || tag.number != tags::app_tag::TIME || tag.length != 4 {
66 return Err(Error::decoding(
67 offset,
68 "time-value: expected an application-tagged Time (4 octets)",
69 ));
70 }
71 let (octets, value_start) = contents(data, content, 4)?;
72 let time = Time::decode(octets)?;
73
74 let (tag, _) = tags::decode_tag(data, value_start)?;
75 if tag.class != TagClass::Application {
76 return Err(DecodeError::new(
77 DecodingKind::InvalidEncoding,
78 value_start,
79 "time-value: expected an application-tagged value, got context tag [{}]",
80 )
81 .with_value(tag.number.into())
82 .into());
83 }
84 let (value, value_end) = primitives::decode_application_value(data, value_start)?;
85 Ok((BACnetTimeValue { time, value }, value_end))
86}
87
88fn encode_time_values(
90 buf: &mut BytesMut,
91 tag: u8,
92 time_values: &[BACnetTimeValue],
93) -> Result<(), Error> {
94 tags::encode_opening_tag(buf, tag);
95 for time_value in time_values {
96 encode_time_value(buf, time_value)?;
97 }
98 tags::encode_closing_tag(buf, tag);
99 Ok(())
100}
101
102fn decode_time_values(
105 data: &[u8],
106 offset: usize,
107 tag: u8,
108 what: &'static str,
109) -> Result<(Vec<BACnetTimeValue>, usize), Error> {
110 let mut pos = expect_opening(data, offset, tag, what)?;
111 let mut time_values = Vec::new();
112 loop {
113 if next_is_closing(data, pos, tag)? {
114 return Ok((time_values, expect_closing(data, pos, tag, what)?));
115 }
116 if time_values.len() >= MAX_FRAMED_ITEMS {
117 return Err(DecodeError::new(
118 DecodingKind::Overflow,
119 pos,
120 "time-values exceed item limit",
121 )
122 .within(what)
123 .into());
124 }
125 let (time_value, next) = decode_time_value(data, pos)?;
126 time_values.push(time_value);
127 pos = next;
128 }
129}
130
131pub fn encode_special_event_period(buf: &mut BytesMut, period: &SpecialEventPeriod) {
133 match period {
134 SpecialEventPeriod::CalendarEntry(entry) => {
135 tags::encode_opening_tag(buf, 0);
136 encode_calendar_entry(buf, entry);
137 tags::encode_closing_tag(buf, 0);
138 }
139 SpecialEventPeriod::CalendarReference(oid) => primitives::encode_ctx_object_id(buf, 1, oid),
140 }
141}
142
143pub fn decode_special_event_period(
145 data: &[u8],
146 offset: usize,
147) -> Result<(SpecialEventPeriod, usize), Error> {
148 let (tag, content) = tags::decode_tag(data, offset)?;
149 if tag.is_opening_tag(0) {
150 let (entry, after) = decode_calendar_entry(data, content)?;
151 let end = expect_closing(data, after, 0, "special event calendar-entry")?;
152 return Ok((SpecialEventPeriod::CalendarEntry(entry), end));
153 }
154 if tag.is_context(1) {
155 let (oid, end) = decode_ctx_object_id(data, offset, 1, "special event calendar-reference")?;
156 return Ok((SpecialEventPeriod::CalendarReference(oid), end));
157 }
158 Err(Error::decoding(
159 offset,
160 "special event period requires calendar-entry [0] or calendar-reference [1]",
161 ))
162}
163
164pub fn encode_special_event(buf: &mut BytesMut, event: &BACnetSpecialEvent) -> Result<(), Error> {
168 encode_special_event_period(buf, &event.period);
169 encode_time_values(buf, 2, &event.list_of_time_values)?;
170 primitives::encode_ctx_unsigned(buf, 3, event.event_priority);
171 Ok(())
172}
173
174pub fn decode_special_event(
183 data: &[u8],
184 offset: usize,
185) -> Result<(BACnetSpecialEvent, usize), Error> {
186 let (period, pos) = decode_special_event_period(data, offset)?;
187 let (list_of_time_values, pos) =
188 decode_time_values(data, pos, 2, "special event list-of-time-values")?;
189 let (event_priority, end) =
190 decode_ctx_unsigned::<u64>(data, pos, 3, "special event event-priority")?;
191 Ok((
192 BACnetSpecialEvent {
193 period,
194 list_of_time_values,
195 event_priority,
196 },
197 end,
198 ))
199}
200
201pub fn encode_daily_schedule(
204 buf: &mut BytesMut,
205 time_values: &[BACnetTimeValue],
206) -> Result<(), Error> {
207 encode_time_values(buf, 0, time_values)
208}
209
210pub fn decode_daily_schedule(
213 data: &[u8],
214 offset: usize,
215) -> Result<(Vec<BACnetTimeValue>, usize), Error> {
216 decode_time_values(data, offset, 0, "daily schedule")
217}
218
219pub fn encode_weekly_schedule(
221 buf: &mut BytesMut,
222 days: &[Vec<BACnetTimeValue>; 7],
223) -> Result<(), Error> {
224 for day in days {
225 encode_daily_schedule(buf, day)?;
226 }
227 Ok(())
228}
229
230#[cold]
233fn entry_refusal(error: &Error, offset: usize, what: &'static str, index: u16) -> Error {
234 error
235 .to_decode_error(offset)
236 .within(what)
237 .at_item(index)
238 .into()
239}
240
241pub fn decode_weekly_schedule(data: &[u8]) -> Result<[Vec<BACnetTimeValue>; 7], Error> {
246 let mut days: [Vec<BACnetTimeValue>; 7] = Default::default();
247 let mut pos = 0;
248 for (index, day) in (0u16..).zip(days.iter_mut()) {
249 let (time_values, next) = decode_daily_schedule(data, pos)
250 .map_err(|error| entry_refusal(&error, pos, "weekly schedule day", index))?;
251 *day = time_values;
252 pos = next;
253 }
254 expect_end(data, pos, pos, "weekly schedule after 7 daily schedules")?;
255 Ok(days)
256}
257
258pub fn encode_exception_schedule(
260 buf: &mut BytesMut,
261 events: &[BACnetSpecialEvent],
262) -> Result<(), Error> {
263 for event in events {
264 encode_special_event(buf, event)?;
265 }
266 Ok(())
267}
268
269pub fn decode_exception_schedule(data: &[u8]) -> Result<Vec<BACnetSpecialEvent>, Error> {
273 let mut events = Vec::new();
274 let mut pos = 0;
275 while pos < data.len() {
276 if events.len() >= MAX_FRAMED_ITEMS {
277 return Err(Error::overflow(
278 pos,
279 "exception schedule exceeds item limit",
280 ));
281 }
282 let index = u16::try_from(events.len()).unwrap_or(u16::MAX);
284 let (event, next) = decode_special_event(data, pos)
285 .map_err(|error| entry_refusal(&error, pos, "exception schedule entry", index))?;
286 events.push(event);
287 pos = next;
288 }
289 Ok(events)
290}