use bacnet_types::constructed::{
BACnetAssignedLandingCalls, BACnetLandingDoorStatus, BACnetLiftCarCallList,
};
use bacnet_types::enums::{DoorStatus, LiftCarDoorCommand, PropertyIdentifier};
use bacnet_types::error::Error;
use bacnet_types::primitives::PropertyValue;
use super::door_values as values;
use crate::common;
#[derive(Debug, Clone, PartialEq)]
pub(super) struct CarDoors {
pub(super) status: Vec<DoorStatus>,
pub(super) landing: Vec<BACnetLandingDoorStatus>,
pub(super) landing_calls: Vec<BACnetAssignedLandingCalls>,
pub(super) making_car_call: Vec<u8>,
pub(super) registered_car_call: Vec<BACnetLiftCarCallList>,
pub(super) command: Vec<LiftCarDoorCommand>,
}
impl CarDoors {
pub(super) fn new() -> Self {
let mut doors = Self {
status: Vec::new(),
landing: Vec::new(),
landing_calls: Vec::new(),
making_car_call: Vec::new(),
registered_car_call: Vec::new(),
command: Vec::new(),
};
doors.resize(vec![DoorStatus::UNKNOWN]);
doors
}
pub(super) fn set_status(&mut self, status: Vec<DoorStatus>) -> Result<(), Error> {
if !status.iter().all(values::door_status_in_range) {
return Err(common::value_out_of_range_error());
}
self.resize(status);
Ok(())
}
fn resize(&mut self, status: Vec<DoorStatus>) {
let count = status.len();
self.landing.resize_with(count, Default::default);
self.landing_calls.resize_with(count, Default::default);
self.making_car_call.resize(count, 0);
self.registered_car_call
.resize_with(count, Default::default);
self.command.resize(count, LiftCarDoorCommand::NONE);
self.status = status;
}
pub(super) fn set_landing(&mut self, array: Vec<BACnetLandingDoorStatus>) -> Result<(), Error> {
fit(
&self.status,
&mut self.landing,
array,
values::landing_doors_in_range,
)
}
pub(super) fn set_landing_calls(
&mut self,
array: Vec<BACnetAssignedLandingCalls>,
) -> Result<(), Error> {
fit(
&self.status,
&mut self.landing_calls,
array,
values::landing_calls_in_range,
)
}
pub(super) fn set_making_car_call(&mut self, array: Vec<u8>) -> Result<(), Error> {
fit(
&self.status,
&mut self.making_car_call,
array,
values::always_in_range,
)
}
pub(super) fn set_registered_car_call(
&mut self,
array: Vec<BACnetLiftCarCallList>,
) -> Result<(), Error> {
fit(
&self.status,
&mut self.registered_car_call,
array,
values::always_in_range,
)
}
pub(super) fn set_command(&mut self, array: Vec<LiftCarDoorCommand>) -> Result<(), Error> {
fit(
&self.status,
&mut self.command,
array,
values::door_command_in_range,
)
}
pub(super) fn read(
&self,
property: PropertyIdentifier,
array_index: Option<u32>,
) -> Option<Result<PropertyValue, Error>> {
let elements: Vec<PropertyValue> = match property {
PropertyIdentifier::CAR_DOOR_STATUS => self
.status
.iter()
.map(|door| PropertyValue::Enumerated(door.to_raw()))
.collect(),
PropertyIdentifier::LANDING_DOOR_STATUS => self
.landing
.iter()
.map(values::encode_landing_doors)
.collect(),
PropertyIdentifier::ASSIGNED_LANDING_CALLS => self
.landing_calls
.iter()
.map(values::encode_landing_calls)
.collect(),
PropertyIdentifier::MAKING_CAR_CALL => self
.making_car_call
.iter()
.map(|&floor| PropertyValue::Unsigned(floor.into()))
.collect(),
PropertyIdentifier::REGISTERED_CAR_CALL => self
.registered_car_call
.iter()
.map(values::encode_car_calls)
.collect(),
PropertyIdentifier::CAR_DOOR_COMMAND => self
.command
.iter()
.map(|command| PropertyValue::Enumerated(command.to_raw()))
.collect(),
_ => return None,
};
Some(common::read_array(elements, array_index))
}
pub(super) fn write(
&mut self,
property: PropertyIdentifier,
array_index: Option<u32>,
value: &PropertyValue,
out_of_service: bool,
) -> Option<Result<(), Error>> {
let write = Simulated {
array_index,
value,
out_of_service,
};
Some(match property {
PropertyIdentifier::CAR_DOOR_STATUS => {
write.apply(&mut self.status, values::decode_car_door)
}
PropertyIdentifier::LANDING_DOOR_STATUS => {
write.apply(&mut self.landing, values::decode_landing_doors)
}
PropertyIdentifier::ASSIGNED_LANDING_CALLS => {
write.apply(&mut self.landing_calls, values::decode_landing_calls)
}
PropertyIdentifier::MAKING_CAR_CALL => {
write.apply(&mut self.making_car_call, values::decode_car_call)
}
PropertyIdentifier::REGISTERED_CAR_CALL => {
write.apply(&mut self.registered_car_call, values::decode_car_calls)
}
PropertyIdentifier::CAR_DOOR_COMMAND => {
write.apply(&mut self.command, values::decode_door_command)
}
_ => return None,
})
}
}
fn fit<T>(
status: &[DoorStatus],
slot: &mut Vec<T>,
array: Vec<T>,
in_range: fn(&T) -> bool,
) -> Result<(), Error> {
if array.len() != status.len() || !array.iter().all(in_range) {
return Err(common::value_out_of_range_error());
}
*slot = array;
Ok(())
}
struct Simulated<'a> {
array_index: Option<u32>,
value: &'a PropertyValue,
out_of_service: bool,
}
impl Simulated<'_> {
fn apply<T: Clone>(
self,
array: &mut Vec<T>,
decode: fn(PropertyValue) -> Result<T, Error>,
) -> Result<(), Error> {
if !self.out_of_service {
return Err(common::write_access_denied_error());
}
*array = written_array(array, self.array_index, self.value.clone(), decode)?;
Ok(())
}
}
fn written_array<T: Clone>(
current: &[T],
array_index: Option<u32>,
value: PropertyValue,
decode: fn(PropertyValue) -> Result<T, Error>,
) -> Result<Vec<T>, Error> {
match array_index {
None => {
let elements = match value {
PropertyValue::List(elements) => elements,
element => vec![element],
};
let array = elements
.into_iter()
.map(decode)
.collect::<Result<Vec<_>, _>>()?;
if array.len() != current.len() {
return Err(common::value_out_of_range_error());
}
Ok(array)
}
Some(0) => Err(common::write_access_denied_error()),
Some(index) => {
let mut array = current.to_vec();
let element = usize::try_from(index - 1)
.ok()
.and_then(|slot| array.get_mut(slot))
.ok_or_else(common::invalid_array_index_error)?;
*element = decode(value)?;
Ok(array)
}
}
}