use std::sync::Arc;
use bacnet_types::enums::AuthorizationMode;
use super::point_authorization::Authorization;
use super::*;
use crate::clock::ClockReader;
pub struct AccessPointObject {
oid: ObjectIdentifier,
name: String,
description: String,
access_event: AccessEvent,
access_event_tag: u64,
access_event_time: BACnetTimeStamp,
access_event_credential: BACnetDeviceObjectReference,
authentication_status: AuthenticationStatus,
access_doors: Vec<BACnetDeviceObjectReference>,
authorization: Authorization,
event_state: EventState,
status_flags: StatusFlags,
out_of_service: bool,
reliability: Reliability,
clock: Option<Arc<dyn ClockReader>>,
}
impl AccessPointObject {
pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
let oid = ObjectIdentifier::new(ObjectType::ACCESS_POINT, instance)?;
Ok(Self {
oid,
name: name.into(),
description: String::new(),
access_event: AccessEvent::NONE,
access_event_tag: 0,
access_event_time: never_updated(),
access_event_credential: no_credential(),
authentication_status: AuthenticationStatus::READY,
access_doors: Vec::new(),
authorization: Authorization::new(),
event_state: EventState::NORMAL,
status_flags: StatusFlags::empty(),
out_of_service: false,
reliability: Reliability::NO_FAULT_DETECTED,
clock: None,
})
}
pub fn set_access_event(
&mut self,
event: AccessEvent,
tag: u64,
time: BACnetTimeStamp,
credential: Option<BACnetDeviceObjectReference>,
) -> Result<(), Error> {
let credential = credential.unwrap_or_else(no_credential);
crate::device_reference::check_device_member(credential.device_identifier)?;
if credential.object_identifier.object_type() != ObjectType::ACCESS_CREDENTIAL {
return Err(common::value_out_of_range_error());
}
let empty = |instance: u32| instance == ObjectIdentifier::MAX_INSTANCE;
if credential.device_identifier.is_some_and(|device| {
empty(device.instance_number()) != empty(credential.object_identifier.instance_number())
}) {
return Err(common::value_out_of_range_error());
}
self.access_event = event;
self.access_event_tag = tag;
self.access_event_time = time;
self.access_event_credential = credential;
Ok(())
}
pub fn set_authentication_status(&mut self, status: AuthenticationStatus) -> Result<(), Error> {
if status.to_raw() > AuthenticationStatus::IN_PROGRESS.to_raw() {
return Err(common::value_out_of_range_error());
}
self.authentication_status = status;
Ok(())
}
pub fn authentication_status(&self) -> AuthenticationStatus {
if self.out_of_service {
AuthenticationStatus::DISABLED
} else {
self.authentication_status
}
}
pub fn set_access_doors(
&mut self,
doors: impl IntoIterator<Item = impl Into<BACnetDeviceObjectReference>>,
) -> Result<(), Error> {
self.access_doors = references_to(ObjectType::ACCESS_DOOR, doors)?;
Ok(())
}
pub fn set_number_of_authentication_policies(&mut self, count: u32) -> Result<(), Error> {
self.authorization.set_policies(count)
}
pub fn set_supported_authorization_modes(
&mut self,
modes: impl IntoIterator<Item = AuthorizationMode>,
) -> Result<(), Error> {
self.authorization.set_supported_modes(modes)
}
pub fn set_priority_for_writing(&mut self, priority: u8) -> Result<(), Error> {
self.authorization.set_priority_for_writing(priority)
}
fn record_out_of_service_event(&mut self, event: AccessEvent) {
let tag = self.access_event_tag.wrapping_add(1);
self.access_event = event;
self.access_event_tag = tag;
self.access_event_time = update_stamp(self.clock.as_deref(), || tag_sequence(tag));
self.access_event_credential = no_credential();
}
}
fn no_credential() -> BACnetDeviceObjectReference {
ObjectIdentifier::new(
ObjectType::ACCESS_CREDENTIAL,
ObjectIdentifier::MAX_INSTANCE,
)
.expect("4194303 is within the instance range")
.into()
}
fn tag_sequence(tag: u64) -> u16 {
const RANGE: u64 = u16::MAX as u64;
(tag.wrapping_sub(1) % RANGE) as u16 + 1
}
impl BACnetObject for AccessPointObject {
fn object_identifier(&self) -> ObjectIdentifier {
self.oid
}
fn object_name(&self) -> &str {
&self.name
}
fn read_property(
&self,
property: PropertyIdentifier,
array_index: Option<u32>,
) -> Result<PropertyValue, Error> {
if let Some(result) = read_common_properties!(self, property, array_index) {
return result;
}
if let Some(value) = self.authorization.read(property) {
return Ok(value);
}
match property {
p if p == PropertyIdentifier::OBJECT_TYPE => {
Ok(PropertyValue::Enumerated(ObjectType::ACCESS_POINT.to_raw()))
}
p if p == PropertyIdentifier::ACCESS_EVENT => {
Ok(PropertyValue::Enumerated(self.access_event.to_raw()))
}
p if p == PropertyIdentifier::ACCESS_EVENT_TAG => {
Ok(PropertyValue::Unsigned(self.access_event_tag))
}
p if p == PropertyIdentifier::ACCESS_EVENT_TIME => {
timestamp_value(&self.access_event_time)
}
p if p == PropertyIdentifier::ACCESS_EVENT_CREDENTIAL => Ok(
crate::device_reference::reference_value(&self.access_event_credential),
),
p if p == PropertyIdentifier::AUTHENTICATION_STATUS => Ok(PropertyValue::Enumerated(
self.authentication_status().to_raw(),
)),
p if p == PropertyIdentifier::ACCESS_DOORS => common::read_array(
crate::device_reference::reference_elements(&self.access_doors),
array_index,
),
p if p == PropertyIdentifier::EVENT_STATE => {
Ok(PropertyValue::Enumerated(self.event_state.to_raw()))
}
_ => Err(common::unknown_property_error()),
}
}
fn write_property(
&mut self,
property: PropertyIdentifier,
array_index: Option<u32>,
value: PropertyValue,
_priority: Option<u8>,
) -> Result<(), Error> {
let was_out_of_service = self.out_of_service;
if let Some(result) =
common::write_out_of_service(&mut self.out_of_service, property, &value)
{
match (was_out_of_service, self.out_of_service) {
(false, true) => self.record_out_of_service_event(AccessEvent::OUT_OF_SERVICE),
(true, false) => {
self.record_out_of_service_event(AccessEvent::OUT_OF_SERVICE_RELINQUISHED)
}
_ => {}
}
return result;
}
if let Some(result) = common::write_description(&mut self.description, property, &value) {
return result;
}
if let Some(result) = self.authorization.write(property, array_index, &value) {
return result;
}
Err(crate::common::unhandled_write_error(
self.property_metadata().as_ref(),
property,
array_index,
))
}
fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
super::metadata_topology::for_access_point_object(self)
}
fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
}
fn supports_cov(&self) -> bool {
true
}
fn bind_clock_internal(&mut self, clock: Option<Arc<dyn ClockReader>>) {
self.clock = clock;
}
}