bacnet_encoding/constructed/
schedule.rs1use bacnet_types::constructed::{BACnetSpecialEvent, BACnetTimeValue, SpecialEventPeriod};
30use bacnet_types::error::Error;
31use bacnet_types::primitives::Time;
32use bytes::BytesMut;
33
34use super::calendar::{decode_calendar_entry, encode_calendar_entry};
35use super::tagged::{
36 contents, decode_ctx_object_id, decode_ctx_unsigned, expect_closing, expect_end,
37 expect_opening, next_is_closing,
38};
39use super::MAX_FRAMED_ITEMS;
40use crate::primitives;
41use crate::tags::{self, TagClass};
42
43pub fn encode_time_value(buf: &mut BytesMut, time_value: &BACnetTimeValue) -> Result<(), Error> {
49 if !time_value.value.is_primitive() {
50 return Err(Error::Encoding(
51 "time-value: the value must be of a primitive datatype".into(),
52 ));
53 }
54 primitives::encode_app_time(buf, &time_value.time);
55 primitives::encode_property_value(buf, &time_value.value)
56}
57
58pub fn decode_time_value(data: &[u8], offset: usize) -> Result<(BACnetTimeValue, usize), Error> {
63 let (tag, content) = tags::decode_tag(data, offset)?;
64 if tag.class != TagClass::Application || tag.number != tags::app_tag::TIME || tag.length != 4 {
65 return Err(Error::decoding(
66 offset,
67 "time-value: expected an application-tagged Time (4 octets)",
68 ));
69 }
70 let (octets, value_start) = contents(data, content, 4)?;
71 let time = Time::decode(octets)?;
72
73 let (tag, _) = tags::decode_tag(data, value_start)?;
74 if tag.class != TagClass::Application {
75 return Err(Error::decoding(
76 value_start,
77 format!(
78 "time-value: expected an application-tagged value, got context tag [{}]",
79 tag.number
80 ),
81 ));
82 }
83 let (value, value_end) = primitives::decode_application_value(data, value_start)?;
84 Ok((BACnetTimeValue { time, value }, value_end))
85}
86
87fn encode_time_values(
89 buf: &mut BytesMut,
90 tag: u8,
91 time_values: &[BACnetTimeValue],
92) -> Result<(), Error> {
93 tags::encode_opening_tag(buf, tag);
94 for time_value in time_values {
95 encode_time_value(buf, time_value)?;
96 }
97 tags::encode_closing_tag(buf, tag);
98 Ok(())
99}
100
101fn decode_time_values(
104 data: &[u8],
105 offset: usize,
106 tag: u8,
107 what: &str,
108) -> Result<(Vec<BACnetTimeValue>, usize), Error> {
109 let mut pos = expect_opening(data, offset, tag, what)?;
110 let mut time_values = Vec::new();
111 loop {
112 if next_is_closing(data, pos, tag)? {
113 return Ok((time_values, expect_closing(data, pos, tag, what)?));
114 }
115 if time_values.len() >= MAX_FRAMED_ITEMS {
116 return Err(Error::overflow(
117 pos,
118 format!("{what}: time-values exceed item limit"),
119 ));
120 }
121 let (time_value, next) = decode_time_value(data, pos)?;
122 time_values.push(time_value);
123 pos = next;
124 }
125}
126
127pub fn encode_special_event_period(buf: &mut BytesMut, period: &SpecialEventPeriod) {
129 match period {
130 SpecialEventPeriod::CalendarEntry(entry) => {
131 tags::encode_opening_tag(buf, 0);
132 encode_calendar_entry(buf, entry);
133 tags::encode_closing_tag(buf, 0);
134 }
135 SpecialEventPeriod::CalendarReference(oid) => primitives::encode_ctx_object_id(buf, 1, oid),
136 }
137}
138
139pub fn decode_special_event_period(
141 data: &[u8],
142 offset: usize,
143) -> Result<(SpecialEventPeriod, usize), Error> {
144 let (tag, content) = tags::decode_tag(data, offset)?;
145 if tag.is_opening_tag(0) {
146 let (entry, after) = decode_calendar_entry(data, content)?;
147 let end = expect_closing(data, after, 0, "special event calendar-entry")?;
148 return Ok((SpecialEventPeriod::CalendarEntry(entry), end));
149 }
150 if tag.is_context(1) {
151 let (oid, end) = decode_ctx_object_id(data, offset, 1, "special event calendar-reference")?;
152 return Ok((SpecialEventPeriod::CalendarReference(oid), end));
153 }
154 Err(Error::decoding(
155 offset,
156 "special event period requires calendar-entry [0] or calendar-reference [1]",
157 ))
158}
159
160pub fn encode_special_event(buf: &mut BytesMut, event: &BACnetSpecialEvent) -> Result<(), Error> {
164 encode_special_event_period(buf, &event.period);
165 encode_time_values(buf, 2, &event.list_of_time_values)?;
166 primitives::encode_ctx_unsigned(buf, 3, event.event_priority);
167 Ok(())
168}
169
170pub fn decode_special_event(
179 data: &[u8],
180 offset: usize,
181) -> Result<(BACnetSpecialEvent, usize), Error> {
182 let (period, pos) = decode_special_event_period(data, offset)?;
183 let (list_of_time_values, pos) =
184 decode_time_values(data, pos, 2, "special event list-of-time-values")?;
185 let (event_priority, end) =
186 decode_ctx_unsigned::<u64>(data, pos, 3, "special event event-priority")?;
187 Ok((
188 BACnetSpecialEvent {
189 period,
190 list_of_time_values,
191 event_priority,
192 },
193 end,
194 ))
195}
196
197pub fn encode_daily_schedule(
200 buf: &mut BytesMut,
201 time_values: &[BACnetTimeValue],
202) -> Result<(), Error> {
203 encode_time_values(buf, 0, time_values)
204}
205
206pub fn decode_daily_schedule(
209 data: &[u8],
210 offset: usize,
211) -> Result<(Vec<BACnetTimeValue>, usize), Error> {
212 decode_time_values(data, offset, 0, "daily schedule")
213}
214
215pub fn encode_weekly_schedule(
217 buf: &mut BytesMut,
218 days: &[Vec<BACnetTimeValue>; 7],
219) -> Result<(), Error> {
220 for day in days {
221 encode_daily_schedule(buf, day)?;
222 }
223 Ok(())
224}
225
226pub fn decode_weekly_schedule(data: &[u8]) -> Result<[Vec<BACnetTimeValue>; 7], Error> {
231 let mut days: [Vec<BACnetTimeValue>; 7] = Default::default();
232 let mut pos = 0;
233 for (index, day) in days.iter_mut().enumerate() {
234 let (time_values, next) = decode_daily_schedule(data, pos)
235 .map_err(|e| Error::decoding(pos, format!("weekly schedule day {index}: {e}")))?;
236 *day = time_values;
237 pos = next;
238 }
239 expect_end(data, pos, pos, "weekly schedule after 7 daily schedules")?;
240 Ok(days)
241}
242
243pub fn encode_exception_schedule(
245 buf: &mut BytesMut,
246 events: &[BACnetSpecialEvent],
247) -> Result<(), Error> {
248 for event in events {
249 encode_special_event(buf, event)?;
250 }
251 Ok(())
252}
253
254pub fn decode_exception_schedule(data: &[u8]) -> Result<Vec<BACnetSpecialEvent>, Error> {
258 let mut events = Vec::new();
259 let mut pos = 0;
260 while pos < data.len() {
261 if events.len() >= MAX_FRAMED_ITEMS {
262 return Err(Error::overflow(
263 pos,
264 "exception schedule exceeds item limit",
265 ));
266 }
267 let (event, next) = decode_special_event(data, pos).map_err(|e| {
268 Error::decoding(
269 pos,
270 format!("exception schedule entry {}: {e}", events.len()),
271 )
272 })?;
273 events.push(event);
274 pos = next;
275 }
276 Ok(events)
277}