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