bacnet_objects/schedule/
calendar.rs1use crate::prelude::*;
4use alloc::borrow::Cow;
5use alloc::sync::Arc;
6
7use bacnet_types::calendar::SpecificDate;
8use bacnet_types::constructed::BACnetCalendarEntry;
9use bacnet_types::enums::{ErrorClass, ErrorCode, ObjectType, PropertyIdentifier};
10use bacnet_types::error::Error;
11use bacnet_types::primitives::{ObjectIdentifier, PropertyValue};
12
13use super::{calendar_metadata, date_list};
14use crate::clock::ClockReader;
15use crate::common::{self, read_property_list_property};
16use crate::traits::BACnetObject;
17
18pub struct CalendarObject {
36 oid: ObjectIdentifier,
37 name: String,
38 description: String,
39 date_list: Vec<BACnetCalendarEntry>,
40 clock: Option<Arc<dyn ClockReader>>,
42}
43
44impl CalendarObject {
45 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
47 let oid = ObjectIdentifier::new(ObjectType::CALENDAR, instance)?;
48 Ok(Self {
49 oid,
50 name: name.into(),
51 description: String::new(),
52 date_list: Vec::new(),
53 clock: None,
54 })
55 }
56
57 pub fn set_description(&mut self, desc: impl Into<String>) {
59 self.description = desc.into();
60 }
61
62 pub fn add_date_entry(&mut self, entry: BACnetCalendarEntry) -> Result<(), Error> {
68 if !entry.is_valid() {
69 return Err(common::value_out_of_range_error());
70 }
71 if self.date_list.len() >= date_list::MAX_DATE_LIST_ENTRIES {
72 return Err(date_list::no_space_error());
73 }
74 self.date_list.push(entry);
75 Ok(())
76 }
77
78 pub fn clear_date_list(&mut self) {
80 self.date_list.clear();
81 }
82
83 pub fn date_list(&self) -> &[BACnetCalendarEntry] {
85 &self.date_list
86 }
87
88 pub fn is_active_on(&self, day: SpecificDate) -> bool {
91 self.date_list.iter().any(|entry| entry.matches(day))
92 }
93
94 pub fn present_value(&self) -> bool {
97 self.clock
98 .as_ref()
99 .and_then(|clock| clock.read_clock())
100 .and_then(|frame| SpecificDate::from_date(&frame.local_date))
101 .is_some_and(|today| self.is_active_on(today))
102 }
103}
104
105impl BACnetObject for CalendarObject {
106 fn object_identifier(&self) -> ObjectIdentifier {
107 self.oid
108 }
109
110 fn object_name(&self) -> &str {
111 &self.name
112 }
113
114 fn read_property(
115 &self,
116 property: PropertyIdentifier,
117 array_index: Option<u32>,
118 ) -> Result<PropertyValue, Error> {
119 match property {
120 p if p == PropertyIdentifier::OBJECT_IDENTIFIER => {
121 Ok(PropertyValue::ObjectIdentifier(self.oid))
122 }
123 p if p == PropertyIdentifier::OBJECT_NAME => {
124 Ok(PropertyValue::CharacterString(self.name.clone()))
125 }
126 p if p == PropertyIdentifier::DESCRIPTION => {
127 Ok(PropertyValue::CharacterString(self.description.clone()))
128 }
129 p if p == PropertyIdentifier::OBJECT_TYPE => {
130 Ok(PropertyValue::Enumerated(ObjectType::CALENDAR.to_raw()))
131 }
132 p if p == PropertyIdentifier::PRESENT_VALUE => {
133 Ok(PropertyValue::Boolean(self.present_value()))
134 }
135 p if p == PropertyIdentifier::DATE_LIST => Ok(date_list::read(&self.date_list)),
136 p if p == PropertyIdentifier::PROPERTY_LIST => {
137 read_property_list_property(&self.property_list(), array_index)
138 }
139 _ => Err(Error::protocol(
140 ErrorClass::PROPERTY,
141 ErrorCode::UNKNOWN_PROPERTY,
142 )),
143 }
144 }
145
146 fn write_property(
147 &mut self,
148 property: PropertyIdentifier,
149 array_index: Option<u32>,
150 value: PropertyValue,
151 _priority: Option<u8>,
152 ) -> Result<(), Error> {
153 if let Some(result) = common::write_description(&mut self.description, property, &value) {
154 return result;
155 }
156 if property == PropertyIdentifier::DATE_LIST {
157 if array_index.is_some() {
159 return Err(common::property_is_not_an_array_error());
160 }
161 self.date_list = date_list::decode_write(value)?;
162 return Ok(());
163 }
164 if property == PropertyIdentifier::PRESENT_VALUE {
165 return Err(common::write_access_denied_error());
166 }
167 Err(common::unhandled_write_error(
168 self.property_metadata().as_ref(),
169 property,
170 array_index,
171 ))
172 }
173
174 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
175 calendar_metadata::for_object(self)
176 }
177
178 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
179 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
180 }
181
182 fn bind_clock_internal(&mut self, clock: Option<Arc<dyn ClockReader>>) {
183 self.clock = clock;
184 }
185
186 fn calendar_state_internal(&self, day: SpecificDate) -> Option<bool> {
187 Some(self.is_active_on(day))
188 }
189}