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::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::Encoding(
54 "time-value: the value must be of a primitive datatype".into(),
55 ));
56 }
57 primitives::encode_app_time(buf, &time_value.time);
58 primitives::encode_property_value(buf, &time_value.value)
59}
60
61pub fn decode_time_value(data: &[u8], offset: usize) -> Result<(BACnetTimeValue, usize), Error> {
66 let (tag, content) = tags::decode_tag(data, offset)?;
67 if tag.class != TagClass::Application || tag.number != tags::app_tag::TIME || tag.length != 4 {
68 return Err(Error::decoding(
69 offset,
70 "time-value: expected an application-tagged Time (4 octets)",
71 ));
72 }
73 let (octets, value_start) = contents(data, content, 4)?;
74 let time = Time::decode(octets)?;
75
76 let (tag, _) = tags::decode_tag(data, value_start)?;
77 if tag.class != TagClass::Application {
78 return Err(Error::decoding(
79 value_start,
80 format!(
81 "time-value: expected an application-tagged value, got context tag [{}]",
82 tag.number
83 ),
84 ));
85 }
86 let (value, value_end) = primitives::decode_application_value(data, value_start)?;
87 Ok((BACnetTimeValue { time, value }, value_end))
88}
89
90fn encode_time_values(
92 buf: &mut BytesMut,
93 tag: u8,
94 time_values: &[BACnetTimeValue],
95) -> Result<(), Error> {
96 tags::encode_opening_tag(buf, tag);
97 for time_value in time_values {
98 encode_time_value(buf, time_value)?;
99 }
100 tags::encode_closing_tag(buf, tag);
101 Ok(())
102}
103
104fn decode_time_values(
107 data: &[u8],
108 offset: usize,
109 tag: u8,
110 what: &str,
111) -> Result<(Vec<BACnetTimeValue>, usize), Error> {
112 let mut pos = expect_opening(data, offset, tag, what)?;
113 let mut time_values = Vec::new();
114 loop {
115 if next_is_closing(data, pos, tag)? {
116 return Ok((time_values, expect_closing(data, pos, tag, what)?));
117 }
118 if time_values.len() >= MAX_FRAMED_ITEMS {
119 return Err(Error::overflow(
120 pos,
121 format!("{what}: time-values exceed item limit"),
122 ));
123 }
124 let (time_value, next) = decode_time_value(data, pos)?;
125 time_values.push(time_value);
126 pos = next;
127 }
128}
129
130pub fn encode_special_event_period(buf: &mut BytesMut, period: &SpecialEventPeriod) {
132 match period {
133 SpecialEventPeriod::CalendarEntry(entry) => {
134 tags::encode_opening_tag(buf, 0);
135 encode_calendar_entry(buf, entry);
136 tags::encode_closing_tag(buf, 0);
137 }
138 SpecialEventPeriod::CalendarReference(oid) => primitives::encode_ctx_object_id(buf, 1, oid),
139 }
140}
141
142pub fn decode_special_event_period(
144 data: &[u8],
145 offset: usize,
146) -> Result<(SpecialEventPeriod, usize), Error> {
147 let (tag, content) = tags::decode_tag(data, offset)?;
148 if tag.is_opening_tag(0) {
149 let (entry, after) = decode_calendar_entry(data, content)?;
150 let end = expect_closing(data, after, 0, "special event calendar-entry")?;
151 return Ok((SpecialEventPeriod::CalendarEntry(entry), end));
152 }
153 if tag.is_context(1) {
154 let (oid, end) = decode_ctx_object_id(data, offset, 1, "special event calendar-reference")?;
155 return Ok((SpecialEventPeriod::CalendarReference(oid), end));
156 }
157 Err(Error::decoding(
158 offset,
159 "special event period requires calendar-entry [0] or calendar-reference [1]",
160 ))
161}
162
163pub fn encode_special_event(buf: &mut BytesMut, event: &BACnetSpecialEvent) -> Result<(), Error> {
167 encode_special_event_period(buf, &event.period);
168 encode_time_values(buf, 2, &event.list_of_time_values)?;
169 primitives::encode_ctx_unsigned(buf, 3, event.event_priority);
170 Ok(())
171}
172
173pub fn decode_special_event(
182 data: &[u8],
183 offset: usize,
184) -> Result<(BACnetSpecialEvent, usize), Error> {
185 let (period, pos) = decode_special_event_period(data, offset)?;
186 let (list_of_time_values, pos) =
187 decode_time_values(data, pos, 2, "special event list-of-time-values")?;
188 let (event_priority, end) =
189 decode_ctx_unsigned::<u64>(data, pos, 3, "special event event-priority")?;
190 Ok((
191 BACnetSpecialEvent {
192 period,
193 list_of_time_values,
194 event_priority,
195 },
196 end,
197 ))
198}
199
200pub fn encode_daily_schedule(
203 buf: &mut BytesMut,
204 time_values: &[BACnetTimeValue],
205) -> Result<(), Error> {
206 encode_time_values(buf, 0, time_values)
207}
208
209pub fn decode_daily_schedule(
212 data: &[u8],
213 offset: usize,
214) -> Result<(Vec<BACnetTimeValue>, usize), Error> {
215 decode_time_values(data, offset, 0, "daily schedule")
216}
217
218pub fn encode_weekly_schedule(
220 buf: &mut BytesMut,
221 days: &[Vec<BACnetTimeValue>; 7],
222) -> Result<(), Error> {
223 for day in days {
224 encode_daily_schedule(buf, day)?;
225 }
226 Ok(())
227}
228
229pub fn decode_weekly_schedule(data: &[u8]) -> Result<[Vec<BACnetTimeValue>; 7], Error> {
234 let mut days: [Vec<BACnetTimeValue>; 7] = Default::default();
235 let mut pos = 0;
236 for (index, day) in days.iter_mut().enumerate() {
237 let (time_values, next) = decode_daily_schedule(data, pos)
238 .map_err(|e| Error::decoding(pos, format!("weekly schedule day {index}: {e}")))?;
239 *day = time_values;
240 pos = next;
241 }
242 expect_end(data, pos, pos, "weekly schedule after 7 daily schedules")?;
243 Ok(days)
244}
245
246pub fn encode_exception_schedule(
248 buf: &mut BytesMut,
249 events: &[BACnetSpecialEvent],
250) -> Result<(), Error> {
251 for event in events {
252 encode_special_event(buf, event)?;
253 }
254 Ok(())
255}
256
257pub fn decode_exception_schedule(data: &[u8]) -> Result<Vec<BACnetSpecialEvent>, Error> {
261 let mut events = Vec::new();
262 let mut pos = 0;
263 while pos < data.len() {
264 if events.len() >= MAX_FRAMED_ITEMS {
265 return Err(Error::overflow(
266 pos,
267 "exception schedule exceeds item limit",
268 ));
269 }
270 let (event, next) = decode_special_event(data, pos).map_err(|e| {
271 Error::decoding(
272 pos,
273 format!("exception schedule entry {}: {e}", events.len()),
274 )
275 })?;
276 events.push(event);
277 pos = next;
278 }
279 Ok(events)
280}