use bacnet_types::constructed::BACnetDeviceObjectPropertyReference;
use bacnet_types::enums::{ErrorClass, ErrorCode, ObjectType};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use super::ChannelObject;
use crate::{common, device_reference};
pub const MAX_CHANNEL_MEMBERS: usize = 1024;
pub const MAX_CONTROL_GROUPS: usize = 64;
pub(super) fn empty_reference() -> BACnetDeviceObjectPropertyReference {
device_reference::unset_reference(ObjectType::ANALOG_INPUT)
}
pub(super) fn is_empty(member: &BACnetDeviceObjectPropertyReference) -> bool {
member.is_unset()
}
fn no_space_error() -> Error {
common::protocol_error(ErrorClass::RESOURCES, ErrorCode::NO_SPACE_TO_WRITE_PROPERTY)
}
fn new_size(value: PropertyValue, cap: usize) -> Result<usize, Error> {
let PropertyValue::Unsigned(size) = value else {
return Err(common::invalid_data_type_error());
};
usize::try_from(size)
.ok()
.filter(|size| *size <= cap)
.ok_or_else(no_space_error)
}
fn slot(index: u32, len: usize) -> Result<usize, Error> {
usize::try_from(index - 1)
.ok()
.filter(|slot| *slot < len)
.ok_or_else(common::invalid_array_index_error)
}
fn decode_members(
value: &PropertyValue,
) -> Result<Vec<BACnetDeviceObjectPropertyReference>, Error> {
let members = device_reference::decode_references(value)?;
if members.len() > MAX_CHANNEL_MEMBERS {
return Err(no_space_error());
}
device_reference::check_device_members(&members)?;
Ok(members)
}
fn unsigned_elements(value: PropertyValue) -> Result<Vec<u32>, Error> {
let elements = match value {
PropertyValue::List(elements) => elements,
single => vec![single],
};
elements.into_iter().map(unsigned_element).collect()
}
fn unsigned_element(value: PropertyValue) -> Result<u32, Error> {
let PropertyValue::Unsigned(raw) = value else {
return Err(common::invalid_data_type_error());
};
u32::try_from(raw).map_err(|_| common::value_out_of_range_error())
}
impl ChannelObject {
fn resize_members(&mut self, size: usize) {
self.members.resize_with(size, empty_reference);
self.learned.resize(size, None);
self.execution_delay.resize(size, 0);
}
pub(super) fn write_members(
&mut self,
array_index: Option<u32>,
value: PropertyValue,
) -> Result<(), Error> {
match array_index {
None => {
let members = decode_members(&value)?;
let size = members.len();
self.members = members;
self.learned.clear();
self.resize_members(size);
}
Some(0) => {
let size = new_size(value, MAX_CHANNEL_MEMBERS)?;
self.resize_members(size);
}
Some(index) => {
let at = slot(index, self.members.len())?;
let member: BACnetDeviceObjectPropertyReference =
device_reference::decode_reference(&value)?;
device_reference::check_device_member(member.device_identifier)?;
self.members[at] = member;
self.learned[at] = None;
}
}
Ok(())
}
pub(super) fn write_execution_delay(
&mut self,
array_index: Option<u32>,
value: PropertyValue,
) -> Result<(), Error> {
match array_index {
None => {
let delays = unsigned_elements(value)?;
if delays.len() != self.members.len() {
return Err(common::value_out_of_range_error());
}
self.execution_delay = delays;
}
Some(0) => {
let size = new_size(value, MAX_CHANNEL_MEMBERS)?;
self.resize_members(size);
}
Some(index) => {
let at = slot(index, self.execution_delay.len())?;
self.execution_delay[at] = unsigned_element(value)?;
}
}
Ok(())
}
pub(super) fn write_control_groups(
&mut self,
array_index: Option<u32>,
value: PropertyValue,
) -> Result<(), Error> {
match array_index {
None => {
let groups = unsigned_elements(value)?;
check_group_count(groups.len())?;
self.control_groups = groups;
}
Some(0) => {
let size = new_size(value, MAX_CONTROL_GROUPS)?;
check_group_count(size)?;
self.control_groups.resize(size, 0);
}
Some(index) => {
let at = slot(index, self.control_groups.len())?;
self.control_groups[at] = unsigned_element(value)?;
}
}
Ok(())
}
}
pub(super) fn check_group_count(count: usize) -> Result<(), Error> {
match count {
0 => Err(common::value_out_of_range_error()),
count if count > MAX_CONTROL_GROUPS => Err(no_space_error()),
_ => Ok(()),
}
}