1use crate::prelude::*;
7use alloc::borrow::Cow;
8use bacnet_types::enums::{EventState, ObjectType, PropertyIdentifier, Reliability};
9use bacnet_types::error::Error;
10use bacnet_types::primitives::{Date, ObjectIdentifier, PropertyValue, StatusFlags, Time};
11
12use crate::common::{self, read_common_properties};
13use crate::traits::BACnetObject;
14
15mod metadata;
16
17const TIMER_STATE_IDLE: u32 = 0;
19const TIMER_STATE_RUNNING: u32 = 1;
20const TIMER_STATE_EXPIRED: u32 = 2;
21
22pub struct TimerObject {
24 oid: ObjectIdentifier,
25 name: String,
26 description: String,
27 present_value: u32,
28 timer_running: bool,
29 initial_timeout: u64,
30 update_time: (Date, Time),
31 expiration_time: (Date, Time),
32 status_flags: StatusFlags,
33 event_state: EventState,
35 out_of_service: bool,
36 reliability: Reliability,
37}
38
39impl TimerObject {
40 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
42 let oid = ObjectIdentifier::new(ObjectType::TIMER, instance)?;
43 Ok(Self {
44 oid,
45 name: name.into(),
46 description: String::new(),
47 present_value: TIMER_STATE_IDLE,
48 timer_running: false,
49 initial_timeout: 0,
50 update_time: (
51 Date {
52 year: 0xFF,
53 month: 0xFF,
54 day: 0xFF,
55 day_of_week: 0xFF,
56 },
57 Time {
58 hour: 0xFF,
59 minute: 0xFF,
60 second: 0xFF,
61 hundredths: 0xFF,
62 },
63 ),
64 expiration_time: (
65 Date {
66 year: 0xFF,
67 month: 0xFF,
68 day: 0xFF,
69 day_of_week: 0xFF,
70 },
71 Time {
72 hour: 0xFF,
73 minute: 0xFF,
74 second: 0xFF,
75 hundredths: 0xFF,
76 },
77 ),
78 status_flags: StatusFlags::empty(),
79 event_state: EventState::NORMAL,
80 out_of_service: false,
81 reliability: Reliability::NO_FAULT_DETECTED,
82 })
83 }
84
85 pub fn start(&mut self) {
87 self.present_value = TIMER_STATE_RUNNING;
88 self.timer_running = true;
89 }
90
91 pub fn stop(&mut self) {
93 self.present_value = TIMER_STATE_IDLE;
94 self.timer_running = false;
95 }
96
97 pub fn set_initial_timeout(&mut self, timeout_ms: u64) {
99 self.initial_timeout = timeout_ms;
100 }
101
102 pub fn set_update_time(&mut self, date: Date, time: Time) {
104 self.update_time = (date, time);
105 }
106
107 pub fn set_expiration_time(&mut self, date: Date, time: Time) {
109 self.expiration_time = (date, time);
110 }
111}
112
113impl BACnetObject for TimerObject {
114 fn object_identifier(&self) -> ObjectIdentifier {
115 self.oid
116 }
117
118 fn object_name(&self) -> &str {
119 &self.name
120 }
121
122 fn read_property(
123 &self,
124 property: PropertyIdentifier,
125 array_index: Option<u32>,
126 ) -> Result<PropertyValue, Error> {
127 if let Some(result) = read_common_properties!(self, property, array_index) {
128 return result;
129 }
130 match property {
131 p if p == PropertyIdentifier::OBJECT_TYPE => {
132 Ok(PropertyValue::Enumerated(ObjectType::TIMER.to_raw()))
133 }
134 p if p == PropertyIdentifier::PRESENT_VALUE => {
135 Ok(PropertyValue::Enumerated(self.present_value))
136 }
137 p if p == PropertyIdentifier::TIMER_STATE => {
138 Ok(PropertyValue::Enumerated(self.present_value))
139 }
140 p if p == PropertyIdentifier::TIMER_RUNNING => {
141 Ok(PropertyValue::Boolean(self.timer_running))
142 }
143 p if p == PropertyIdentifier::INITIAL_TIMEOUT => {
144 Ok(PropertyValue::Unsigned(self.initial_timeout))
145 }
146 p if p == PropertyIdentifier::UPDATE_TIME => Ok(PropertyValue::List(vec![
147 PropertyValue::Date(self.update_time.0),
148 PropertyValue::Time(self.update_time.1),
149 ])),
150 p if p == PropertyIdentifier::EXPIRATION_TIME => Ok(PropertyValue::List(vec![
151 PropertyValue::Date(self.expiration_time.0),
152 PropertyValue::Time(self.expiration_time.1),
153 ])),
154 p if p == PropertyIdentifier::EVENT_STATE => {
155 Ok(PropertyValue::Enumerated(self.event_state.to_raw()))
156 }
157 _ => Err(common::unknown_property_error()),
158 }
159 }
160
161 fn write_property(
162 &mut self,
163 property: PropertyIdentifier,
164 _array_index: Option<u32>,
165 value: PropertyValue,
166 _priority: Option<u8>,
167 ) -> Result<(), Error> {
168 if let Some(result) =
169 common::write_out_of_service(&mut self.out_of_service, property, &value)
170 {
171 return result;
172 }
173 if let Some(result) = common::write_description(&mut self.description, property, &value) {
174 return result;
175 }
176 match property {
177 p if p == PropertyIdentifier::PRESENT_VALUE => {
178 if let PropertyValue::Enumerated(v) = value {
179 if v > TIMER_STATE_EXPIRED {
180 return Err(common::value_out_of_range_error());
181 }
182 self.present_value = v;
183 self.timer_running = v == TIMER_STATE_RUNNING;
184 Ok(())
185 } else {
186 Err(common::invalid_data_type_error())
187 }
188 }
189 p if p == PropertyIdentifier::INITIAL_TIMEOUT => {
190 if let PropertyValue::Unsigned(v) = value {
191 self.initial_timeout = v;
192 Ok(())
193 } else {
194 Err(common::invalid_data_type_error())
195 }
196 }
197 _ => Err(crate::common::unhandled_write_error(
198 self.property_metadata().as_ref(),
199 property,
200 _array_index,
201 )),
202 }
203 }
204
205 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
206 metadata::for_object(self)
207 }
208
209 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
210 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
211 }
212}
213
214#[cfg(test)]
219mod tests {
220 use super::*;
221
222 #[test]
223 fn timer_create_and_read_defaults() {
224 let timer = TimerObject::new(1, "TMR-1").unwrap();
225 assert_eq!(timer.object_name(), "TMR-1");
226 assert_eq!(
227 timer
228 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
229 .unwrap(),
230 PropertyValue::Enumerated(TIMER_STATE_IDLE)
231 );
232 assert_eq!(
233 timer
234 .read_property(PropertyIdentifier::TIMER_RUNNING, None)
235 .unwrap(),
236 PropertyValue::Boolean(false)
237 );
238 }
239
240 #[test]
241 fn timer_object_type() {
242 let timer = TimerObject::new(1, "TMR-1").unwrap();
243 assert_eq!(
244 timer
245 .read_property(PropertyIdentifier::OBJECT_TYPE, None)
246 .unwrap(),
247 PropertyValue::Enumerated(ObjectType::TIMER.to_raw())
248 );
249 }
250
251 #[test]
252 fn timer_start_stop() {
253 let mut timer = TimerObject::new(1, "TMR-1").unwrap();
254 timer.start();
255 assert_eq!(
256 timer
257 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
258 .unwrap(),
259 PropertyValue::Enumerated(TIMER_STATE_RUNNING)
260 );
261 assert_eq!(
262 timer
263 .read_property(PropertyIdentifier::TIMER_RUNNING, None)
264 .unwrap(),
265 PropertyValue::Boolean(true)
266 );
267
268 timer.stop();
269 assert_eq!(
270 timer
271 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
272 .unwrap(),
273 PropertyValue::Enumerated(TIMER_STATE_IDLE)
274 );
275 assert_eq!(
276 timer
277 .read_property(PropertyIdentifier::TIMER_RUNNING, None)
278 .unwrap(),
279 PropertyValue::Boolean(false)
280 );
281 }
282
283 #[test]
284 fn timer_write_present_value() {
285 let mut timer = TimerObject::new(1, "TMR-1").unwrap();
286 timer
287 .write_property(
288 PropertyIdentifier::PRESENT_VALUE,
289 None,
290 PropertyValue::Enumerated(TIMER_STATE_RUNNING),
291 None,
292 )
293 .unwrap();
294 assert_eq!(
295 timer
296 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
297 .unwrap(),
298 PropertyValue::Enumerated(TIMER_STATE_RUNNING)
299 );
300 assert_eq!(
301 timer
302 .read_property(PropertyIdentifier::TIMER_RUNNING, None)
303 .unwrap(),
304 PropertyValue::Boolean(true)
305 );
306 }
307
308 #[test]
309 fn timer_write_present_value_out_of_range() {
310 let mut timer = TimerObject::new(1, "TMR-1").unwrap();
311 let result = timer.write_property(
312 PropertyIdentifier::PRESENT_VALUE,
313 None,
314 PropertyValue::Enumerated(99),
315 None,
316 );
317 assert!(result.is_err());
318 }
319
320 #[test]
321 fn timer_write_present_value_wrong_type() {
322 let mut timer = TimerObject::new(1, "TMR-1").unwrap();
323 let result = timer.write_property(
324 PropertyIdentifier::PRESENT_VALUE,
325 None,
326 PropertyValue::Unsigned(1),
327 None,
328 );
329 assert!(result.is_err());
330 }
331
332 #[test]
333 fn timer_read_initial_timeout() {
334 let mut timer = TimerObject::new(1, "TMR-1").unwrap();
335 timer.set_initial_timeout(5000);
336 assert_eq!(
337 timer
338 .read_property(PropertyIdentifier::INITIAL_TIMEOUT, None)
339 .unwrap(),
340 PropertyValue::Unsigned(5000)
341 );
342 }
343
344 #[test]
345 fn timer_write_initial_timeout() {
346 let mut timer = TimerObject::new(1, "TMR-1").unwrap();
347 timer
348 .write_property(
349 PropertyIdentifier::INITIAL_TIMEOUT,
350 None,
351 PropertyValue::Unsigned(10000),
352 None,
353 )
354 .unwrap();
355 assert_eq!(
356 timer
357 .read_property(PropertyIdentifier::INITIAL_TIMEOUT, None)
358 .unwrap(),
359 PropertyValue::Unsigned(10000)
360 );
361 }
362
363 #[test]
364 fn timer_read_update_time() {
365 let timer = TimerObject::new(1, "TMR-1").unwrap();
366 let val = timer
367 .read_property(PropertyIdentifier::UPDATE_TIME, None)
368 .unwrap();
369 let unspec_date = Date {
370 year: 0xFF,
371 month: 0xFF,
372 day: 0xFF,
373 day_of_week: 0xFF,
374 };
375 let unspec_time = Time {
376 hour: 0xFF,
377 minute: 0xFF,
378 second: 0xFF,
379 hundredths: 0xFF,
380 };
381 assert_eq!(
382 val,
383 PropertyValue::List(vec![
384 PropertyValue::Date(unspec_date),
385 PropertyValue::Time(unspec_time),
386 ])
387 );
388 }
389
390 #[test]
391 fn timer_read_timer_state_matches_pv() {
392 let mut timer = TimerObject::new(1, "TMR-1").unwrap();
393 timer.start();
394 let pv = timer
395 .read_property(PropertyIdentifier::PRESENT_VALUE, None)
396 .unwrap();
397 let ts = timer
398 .read_property(PropertyIdentifier::TIMER_STATE, None)
399 .unwrap();
400 assert_eq!(pv, ts);
401 }
402
403 #[test]
404 fn timer_property_list() {
405 let timer = TimerObject::new(1, "TMR-1").unwrap();
406 let list = timer.property_list();
407 assert!(list.contains(&PropertyIdentifier::PRESENT_VALUE));
408 assert!(list.contains(&PropertyIdentifier::TIMER_STATE));
409 assert!(list.contains(&PropertyIdentifier::TIMER_RUNNING));
410 assert!(list.contains(&PropertyIdentifier::INITIAL_TIMEOUT));
411 assert!(list.contains(&PropertyIdentifier::UPDATE_TIME));
412 assert!(list.contains(&PropertyIdentifier::EXPIRATION_TIME));
413 }
414}