#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use bacnet_types::constructed::{BACnetDeviceObjectPropertyReference, BACnetEventParameter};
use bacnet_types::enums::{AccessEvent, LifeSafetyState, TimerState};
use bacnet_types::error::{DecodeError, DecodingKind, Error};
use bacnet_types::primitives::StatusFlags;
use crate::primitives;
use super::super::tagged::{
Fixed, decode_app_character_string, decode_app_enumerated, decode_ctx_bit_string,
decode_ctx_fixed, decode_ctx_primitive, decode_ctx_unsigned, expect_closing, expect_opening,
next_is_closing,
};
use super::super::{MAX_FRAMED_ITEMS, decode_dopr_body};
type Decoded = (BACnetEventParameter, usize);
pub(super) fn decode(data: &[u8], pos: usize, tag: u8) -> Result<Decoded, Error> {
use BACnetEventParameter as EP;
let (value, pos) = match tag {
3 => {
let what = "BACnetEventParameter command-failure";
let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (feedback_property_reference, pos) = reference(data, pos, 1, what)?;
let value = EP::CommandFailure {
time_delay,
feedback_property_reference,
};
(value, pos)
}
8 => {
let what = "BACnetEventParameter change-of-life-safety";
let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (life_safety, pos) = enumerations(data, pos, 1, what)?;
let (alarm, pos) = enumerations(data, pos, 2, what)?;
let (mode_property_reference, pos) = reference(data, pos, 3, what)?;
let value = EP::ChangeOfLifeSafety {
time_delay,
list_of_life_safety_alarm_values: life_safety
.into_iter()
.map(LifeSafetyState::from_raw)
.collect(),
list_of_alarm_values: alarm.into_iter().map(LifeSafetyState::from_raw).collect(),
mode_property_reference,
};
(value, pos)
}
10 => {
let what = "BACnetEventParameter buffer-ready";
let (notification_threshold, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (previous_notification_count, pos) =
decode_ctx_unsigned::<u32>(data, pos, 1, what)?;
let value = EP::BufferReady {
notification_threshold,
previous_notification_count,
};
(value, pos)
}
11 => {
let what = "BACnetEventParameter unsigned-range";
let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (low_limit, pos) = decode_ctx_unsigned::<u64>(data, pos, 1, what)?;
let (high_limit, pos) = decode_ctx_unsigned::<u64>(data, pos, 2, what)?;
let value = EP::UnsignedRange {
time_delay,
low_limit,
high_limit,
};
(value, pos)
}
13 => {
let what = "BACnetEventParameter access-event";
let (events, pos) = enumerations(data, pos, 0, what)?;
let (access_event_time_reference, pos) = reference(data, pos, 1, what)?;
let value = EP::AccessEvent {
list_of_access_events: events.into_iter().map(AccessEvent::from_raw).collect(),
access_event_time_reference,
};
(value, pos)
}
14 => {
let what = "BACnetEventParameter double-out-of-range";
let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (low_limit, pos) = double(data, pos, 1, what)?;
let (high_limit, pos) = double(data, pos, 2, what)?;
let (deadband, pos) = double(data, pos, 3, what)?;
let value = EP::DoubleOutOfRange {
time_delay,
low_limit,
high_limit,
deadband,
};
(value, pos)
}
15 => {
let what = "BACnetEventParameter signed-out-of-range";
let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (low_limit, pos) = signed(data, pos, 1, what)?;
let (high_limit, pos) = signed(data, pos, 2, what)?;
let (deadband, pos) = decode_ctx_unsigned::<u64>(data, pos, 3, what)?;
let value = EP::SignedOutOfRange {
time_delay,
low_limit,
high_limit,
deadband,
};
(value, pos)
}
16 => {
let what = "BACnetEventParameter unsigned-out-of-range";
let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (low_limit, pos) = decode_ctx_unsigned::<u64>(data, pos, 1, what)?;
let (high_limit, pos) = decode_ctx_unsigned::<u64>(data, pos, 2, what)?;
let (deadband, pos) = decode_ctx_unsigned::<u64>(data, pos, 3, what)?;
let value = EP::UnsignedOutOfRange {
time_delay,
low_limit,
high_limit,
deadband,
};
(value, pos)
}
17 => {
let what = "BACnetEventParameter change-of-characterstring";
let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (list_of_alarm_values, pos) =
list(data, pos, 1, what, |data, pos| {
decode_app_character_string(data, pos, what)
})?;
let value = EP::ChangeOfCharacterstring {
time_delay,
list_of_alarm_values,
};
(value, pos)
}
18 => {
let what = "BACnetEventParameter change-of-status-flags";
let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (selected_flags, pos) = status_flags(data, pos, 1, what)?;
let value = EP::ChangeOfStatusFlags {
time_delay,
selected_flags,
};
(value, pos)
}
21 => {
let what = "BACnetEventParameter change-of-discrete-value";
let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
(EP::ChangeOfDiscreteValue { time_delay }, pos)
}
22 => {
let what = "BACnetEventParameter change-of-timer";
let (time_delay, pos) = decode_ctx_unsigned::<u32>(data, pos, 0, what)?;
let (states, pos) = enumerations(data, pos, 1, what)?;
let (update_time_reference, pos) = reference(data, pos, 2, what)?;
let value = EP::ChangeOfTimer {
time_delay,
alarm_values: states.into_iter().map(TimerState::from_raw).collect(),
update_time_reference,
};
(value, pos)
}
_ => {
return Err(Error::decoding(
pos,
"BACnetEventParameter: no decoder for this alternative",
));
}
};
let what = "BACnetEventParameter";
let pos = expect_closing(data, pos, tag, what)?;
Ok((value, pos))
}
fn reference(
data: &[u8],
pos: usize,
tag: u8,
what: &'static str,
) -> Result<(BACnetDeviceObjectPropertyReference, usize), Error> {
let pos = expect_opening(data, pos, tag, what)?;
let (reference, pos) = decode_dopr_body(data, pos, what)?;
let pos = expect_closing(data, pos, tag, what)?;
Ok((reference, pos))
}
fn list<T>(
data: &[u8],
pos: usize,
tag: u8,
what: &'static str,
item: impl Fn(&[u8], usize) -> Result<(T, usize), Error>,
) -> Result<(Vec<T>, usize), Error> {
let mut pos = expect_opening(data, pos, tag, what)?;
let mut items = Vec::new();
while !next_is_closing(data, pos, tag)? {
if items.len() >= MAX_FRAMED_ITEMS {
return Err(Error::overflow(pos, "BACnetEventParameter: list exceeds limit"));
}
let (value, next) = item(data, pos)?;
items.push(value);
pos = next;
}
let pos = expect_closing(data, pos, tag, what)?;
Ok((items, pos))
}
fn enumerations(
data: &[u8],
pos: usize,
tag: u8,
what: &'static str,
) -> Result<(Vec<u32>, usize), Error> {
list(data, pos, tag, what, |data, pos| {
decode_app_enumerated::<u32>(data, pos, what)
})
}
fn double(data: &[u8], pos: usize, tag: u8, what: &'static str) -> Result<(f64, usize), Error> {
let (octets, end) = decode_ctx_fixed(data, pos, tag, Fixed::DOUBLE, what)?;
Ok((primitives::decode_double(octets)?, end))
}
fn signed(data: &[u8], pos: usize, tag: u8, what: &'static str) -> Result<(i32, usize), Error> {
let (octets, end) = decode_ctx_primitive(data, pos, tag, what)?;
Ok((primitives::decode_signed(octets)?, end))
}
fn status_flags(
data: &[u8],
pos: usize,
tag: u8,
what: &'static str,
) -> Result<(StatusFlags, usize), Error> {
let ((unused_bits, octets), end) = decode_ctx_bit_string(data, pos, tag, what)?;
match (unused_bits, octets.as_slice()) {
(4, [bits]) if bits & 0x0f == 0 => Ok((StatusFlags::from_bits_retain(bits >> 4), end)),
_ => Err(DecodeError::new(
DecodingKind::InvalidEncoding,
pos,
"[{}] status flags must be four bits with zero padding",
)
.within(what)
.with_value(tag.into())
.into()),
}
}