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