#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use bacnet_types::constructed::{BACnetSpecialEvent, BACnetTimeValue, SpecialEventPeriod};
use bacnet_types::error::Error;
use bacnet_types::primitives::Time;
use bytes::BytesMut;
use super::calendar::{decode_calendar_entry, encode_calendar_entry};
use super::tagged::{
contents, decode_ctx_object_id, decode_ctx_unsigned, expect_closing, expect_end,
expect_opening, next_is_closing,
};
use super::MAX_FRAMED_ITEMS;
use crate::primitives;
use crate::tags::{self, TagClass};
pub fn encode_time_value(buf: &mut BytesMut, time_value: &BACnetTimeValue) -> Result<(), Error> {
if !time_value.value.is_primitive() {
return Err(Error::Encoding(
"time-value: the value must be of a primitive datatype".into(),
));
}
primitives::encode_app_time(buf, &time_value.time);
primitives::encode_property_value(buf, &time_value.value)
}
pub fn decode_time_value(data: &[u8], offset: usize) -> Result<(BACnetTimeValue, usize), Error> {
let (tag, content) = tags::decode_tag(data, offset)?;
if tag.class != TagClass::Application || tag.number != tags::app_tag::TIME || tag.length != 4 {
return Err(Error::decoding(
offset,
"time-value: expected an application-tagged Time (4 octets)",
));
}
let (octets, value_start) = contents(data, content, 4)?;
let time = Time::decode(octets)?;
let (tag, _) = tags::decode_tag(data, value_start)?;
if tag.class != TagClass::Application {
return Err(Error::decoding(
value_start,
format!(
"time-value: expected an application-tagged value, got context tag [{}]",
tag.number
),
));
}
let (value, value_end) = primitives::decode_application_value(data, value_start)?;
Ok((BACnetTimeValue { time, value }, value_end))
}
fn encode_time_values(
buf: &mut BytesMut,
tag: u8,
time_values: &[BACnetTimeValue],
) -> Result<(), Error> {
tags::encode_opening_tag(buf, tag);
for time_value in time_values {
encode_time_value(buf, time_value)?;
}
tags::encode_closing_tag(buf, tag);
Ok(())
}
fn decode_time_values(
data: &[u8],
offset: usize,
tag: u8,
what: &str,
) -> Result<(Vec<BACnetTimeValue>, usize), Error> {
let mut pos = expect_opening(data, offset, tag, what)?;
let mut time_values = Vec::new();
loop {
if next_is_closing(data, pos, tag)? {
return Ok((time_values, expect_closing(data, pos, tag, what)?));
}
if time_values.len() >= MAX_FRAMED_ITEMS {
return Err(Error::overflow(
pos,
format!("{what}: time-values exceed item limit"),
));
}
let (time_value, next) = decode_time_value(data, pos)?;
time_values.push(time_value);
pos = next;
}
}
pub fn encode_special_event_period(buf: &mut BytesMut, period: &SpecialEventPeriod) {
match period {
SpecialEventPeriod::CalendarEntry(entry) => {
tags::encode_opening_tag(buf, 0);
encode_calendar_entry(buf, entry);
tags::encode_closing_tag(buf, 0);
}
SpecialEventPeriod::CalendarReference(oid) => primitives::encode_ctx_object_id(buf, 1, oid),
}
}
pub fn decode_special_event_period(
data: &[u8],
offset: usize,
) -> Result<(SpecialEventPeriod, usize), Error> {
let (tag, content) = tags::decode_tag(data, offset)?;
if tag.is_opening_tag(0) {
let (entry, after) = decode_calendar_entry(data, content)?;
let end = expect_closing(data, after, 0, "special event calendar-entry")?;
return Ok((SpecialEventPeriod::CalendarEntry(entry), end));
}
if tag.is_context(1) {
let (oid, end) = decode_ctx_object_id(data, offset, 1, "special event calendar-reference")?;
return Ok((SpecialEventPeriod::CalendarReference(oid), end));
}
Err(Error::decoding(
offset,
"special event period requires calendar-entry [0] or calendar-reference [1]",
))
}
pub fn encode_special_event(buf: &mut BytesMut, event: &BACnetSpecialEvent) -> Result<(), Error> {
encode_special_event_period(buf, &event.period);
encode_time_values(buf, 2, &event.list_of_time_values)?;
primitives::encode_ctx_unsigned(buf, 3, event.event_priority);
Ok(())
}
pub fn decode_special_event(
data: &[u8],
offset: usize,
) -> Result<(BACnetSpecialEvent, usize), Error> {
let (period, pos) = decode_special_event_period(data, offset)?;
let (list_of_time_values, pos) =
decode_time_values(data, pos, 2, "special event list-of-time-values")?;
let (event_priority, end) =
decode_ctx_unsigned::<u64>(data, pos, 3, "special event event-priority")?;
Ok((
BACnetSpecialEvent {
period,
list_of_time_values,
event_priority,
},
end,
))
}
pub fn encode_daily_schedule(
buf: &mut BytesMut,
time_values: &[BACnetTimeValue],
) -> Result<(), Error> {
encode_time_values(buf, 0, time_values)
}
pub fn decode_daily_schedule(
data: &[u8],
offset: usize,
) -> Result<(Vec<BACnetTimeValue>, usize), Error> {
decode_time_values(data, offset, 0, "daily schedule")
}
pub fn encode_weekly_schedule(
buf: &mut BytesMut,
days: &[Vec<BACnetTimeValue>; 7],
) -> Result<(), Error> {
for day in days {
encode_daily_schedule(buf, day)?;
}
Ok(())
}
pub fn decode_weekly_schedule(data: &[u8]) -> Result<[Vec<BACnetTimeValue>; 7], Error> {
let mut days: [Vec<BACnetTimeValue>; 7] = Default::default();
let mut pos = 0;
for (index, day) in days.iter_mut().enumerate() {
let (time_values, next) = decode_daily_schedule(data, pos)
.map_err(|e| Error::decoding(pos, format!("weekly schedule day {index}: {e}")))?;
*day = time_values;
pos = next;
}
expect_end(data, pos, pos, "weekly schedule after 7 daily schedules")?;
Ok(days)
}
pub fn encode_exception_schedule(
buf: &mut BytesMut,
events: &[BACnetSpecialEvent],
) -> Result<(), Error> {
for event in events {
encode_special_event(buf, event)?;
}
Ok(())
}
pub fn decode_exception_schedule(data: &[u8]) -> Result<Vec<BACnetSpecialEvent>, Error> {
let mut events = Vec::new();
let mut pos = 0;
while pos < data.len() {
if events.len() >= MAX_FRAMED_ITEMS {
return Err(Error::overflow(
pos,
"exception schedule exceeds item limit",
));
}
let (event, next) = decode_special_event(data, pos).map_err(|e| {
Error::decoding(
pos,
format!("exception schedule entry {}: {e}", events.len()),
)
})?;
events.push(event);
pos = next;
}
Ok(events)
}