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