1use super::*;
2use crate::property_metadata::{
3 PropertyConformance, PropertyMetadata, PropertyPresenceCondition, PropertyWriteCapability,
4};
5
6const BINARY_INPUT_PROPERTY_METADATA: &[PropertyMetadata] = &[
7 PropertyMetadata::new(
8 PropertyIdentifier::OBJECT_IDENTIFIER,
9 PropertyConformance::RequiredRead,
10 None,
11 PropertyWriteCapability::ReadOnly,
12 ),
13 PropertyMetadata::new(
14 PropertyIdentifier::OBJECT_NAME,
15 PropertyConformance::RequiredRead,
16 None,
17 PropertyWriteCapability::Always,
18 ),
19 PropertyMetadata::new(
20 PropertyIdentifier::DESCRIPTION,
21 PropertyConformance::Optional,
22 None,
23 PropertyWriteCapability::Always,
24 ),
25 PropertyMetadata::new(
26 PropertyIdentifier::OBJECT_TYPE,
27 PropertyConformance::RequiredRead,
28 None,
29 PropertyWriteCapability::ReadOnly,
30 ),
31 PropertyMetadata::new(
32 PropertyIdentifier::PRESENT_VALUE,
33 PropertyConformance::RequiredRead,
34 None,
35 PropertyWriteCapability::WhenOutOfService,
36 ),
37 PropertyMetadata::new(
38 PropertyIdentifier::STATUS_FLAGS,
39 PropertyConformance::RequiredRead,
40 None,
41 PropertyWriteCapability::ReadOnly,
42 ),
43 PropertyMetadata::new(
44 PropertyIdentifier::EVENT_STATE,
45 PropertyConformance::RequiredRead,
46 None,
47 PropertyWriteCapability::ReadOnly,
48 ),
49 PropertyMetadata::new(
50 PropertyIdentifier::EVENT_DETECTION_ENABLE,
51 PropertyConformance::Optional,
52 Some(PropertyPresenceCondition::IntrinsicReporting),
53 PropertyWriteCapability::Always,
54 ),
55 PropertyMetadata::new(
56 PropertyIdentifier::EVENT_ENABLE,
57 PropertyConformance::Optional,
58 Some(PropertyPresenceCondition::IntrinsicReporting),
59 PropertyWriteCapability::Always,
60 ),
61 PropertyMetadata::new(
62 PropertyIdentifier::TIME_DELAY,
63 PropertyConformance::Optional,
64 Some(PropertyPresenceCondition::IntrinsicReporting),
65 PropertyWriteCapability::Always,
66 ),
67 PropertyMetadata::new(
68 PropertyIdentifier::TIME_DELAY_NORMAL,
69 PropertyConformance::Optional,
70 Some(PropertyPresenceCondition::IntrinsicReporting),
71 PropertyWriteCapability::Always,
72 ),
73 PropertyMetadata::new(
74 PropertyIdentifier::NOTIFY_TYPE,
75 PropertyConformance::Optional,
76 Some(PropertyPresenceCondition::IntrinsicReporting),
77 PropertyWriteCapability::Always,
78 ),
79 PropertyMetadata::new(
80 PropertyIdentifier::NOTIFICATION_CLASS,
81 PropertyConformance::Optional,
82 Some(PropertyPresenceCondition::IntrinsicReporting),
83 PropertyWriteCapability::Always,
84 ),
85 PropertyMetadata::new(
86 PropertyIdentifier::ACKED_TRANSITIONS,
87 PropertyConformance::Optional,
88 Some(PropertyPresenceCondition::IntrinsicReporting),
89 PropertyWriteCapability::ReadOnly,
90 ),
91 PropertyMetadata::new(
92 PropertyIdentifier::EVENT_TIME_STAMPS,
93 PropertyConformance::Optional,
94 Some(PropertyPresenceCondition::IntrinsicReporting),
95 PropertyWriteCapability::ReadOnly,
96 ),
97 PropertyMetadata::new(
98 PropertyIdentifier::EVENT_MESSAGE_TEXTS,
99 PropertyConformance::Optional,
100 Some(PropertyPresenceCondition::IntrinsicReporting),
101 PropertyWriteCapability::ReadOnly,
102 ),
103 PropertyMetadata::new(
104 PropertyIdentifier::OUT_OF_SERVICE,
105 PropertyConformance::RequiredRead,
106 None,
107 PropertyWriteCapability::Always,
108 ),
109 PropertyMetadata::new(
110 PropertyIdentifier::POLARITY,
111 PropertyConformance::RequiredRead,
112 None,
113 PropertyWriteCapability::ReadOnly,
114 ),
115 PropertyMetadata::new(
116 PropertyIdentifier::RELIABILITY,
117 PropertyConformance::Optional,
118 None,
119 PropertyWriteCapability::WhenOutOfService,
120 ),
121 PropertyMetadata::new(
122 PropertyIdentifier::RELIABILITY_EVALUATION_INHIBIT,
123 PropertyConformance::Optional,
124 None,
125 PropertyWriteCapability::Always,
126 ),
127 PropertyMetadata::new(
128 PropertyIdentifier::ACTIVE_TEXT,
129 PropertyConformance::Optional,
130 Some(PropertyPresenceCondition::PairedText),
131 PropertyWriteCapability::Always,
132 ),
133 PropertyMetadata::new(
134 PropertyIdentifier::INACTIVE_TEXT,
135 PropertyConformance::Optional,
136 Some(PropertyPresenceCondition::PairedText),
137 PropertyWriteCapability::Always,
138 ),
139 PropertyMetadata::new(
140 PropertyIdentifier::ALARM_VALUE,
141 PropertyConformance::Optional,
142 Some(PropertyPresenceCondition::IntrinsicReporting),
143 PropertyWriteCapability::Always,
144 ),
145 PropertyMetadata::new(
146 PropertyIdentifier::PROPERTY_LIST,
147 PropertyConformance::RequiredRead,
148 None,
149 PropertyWriteCapability::ReadOnly,
150 ),
151];
152
153pub struct BinaryInputObject {
162 oid: ObjectIdentifier,
163 name: String,
164 description: String,
165 present_value: u32,
166 out_of_service: bool,
167 status_flags: StatusFlags,
168 polarity: u32,
170 reliability: u32,
172 reliability_before_out_of_service: Option<u32>,
173 reliability_inhibit: common::ReliabilityInhibitState,
174 active_text: String,
175 inactive_text: String,
176 event_detector: ChangeOfStateDetector,
178 event_detection_enable: bool,
181 pub(crate) event_history: EventHistory,
182}
183
184impl BinaryInputObject {
185 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
186 let oid = ObjectIdentifier::new(ObjectType::BINARY_INPUT, instance)?;
187 Ok(Self {
188 oid,
189 name: name.into(),
190 description: String::new(),
191 present_value: 0,
192 out_of_service: false,
193 status_flags: StatusFlags::empty(),
194 polarity: 0,
195 reliability: 0,
196 reliability_before_out_of_service: None,
197 reliability_inhibit: common::ReliabilityInhibitState::default(),
198 active_text: "Active".into(),
199 inactive_text: "Inactive".into(),
200 event_detector: ChangeOfStateDetector {
201 alarm_values: vec![1],
202 ..Default::default()
203 },
204 event_detection_enable: true,
205 event_history: EventHistory::default(),
206 })
207 }
208
209 pub fn set_present_value(&mut self, value: u32) {
217 self.present_value = value;
218 }
219
220 fn apply_present_value(&mut self, value: PropertyValue) -> Result<(), Error> {
225 let PropertyValue::Enumerated(v) = value else {
226 return Err(common::invalid_data_type_error());
227 };
228 if v > 1 {
229 return Err(common::value_out_of_range_error());
230 }
231 self.present_value = v;
232 Ok(())
233 }
234
235 pub fn set_description(&mut self, desc: impl Into<String>) {
237 self.description = desc.into();
238 }
239}
240
241impl BACnetObject for BinaryInputObject {
242 fn object_identifier(&self) -> ObjectIdentifier {
243 self.oid
244 }
245
246 fn object_name(&self) -> &str {
247 &self.name
248 }
249
250 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
251 Cow::Borrowed(BINARY_INPUT_PROPERTY_METADATA)
252 }
253
254 fn supports_cov(&self) -> bool {
255 true
256 }
257
258 crate::event::impl_builtin_intrinsic_reporting!(
259 event_detector,
260 event_history,
261 [present_value],
262 reliability,
263 event_detection_enable,
264 ChangeOfStateDetector::ALGORITHM
265 );
266 impl_intrinsic_write_rollback!(
267 event_detector,
268 event_detection_enable,
269 event_history,
270 reliability_inhibit,
271 reliability,
272 out_of_service,
273 reliability_before_out_of_service
274 );
275
276 fn acknowledge_alarm_correlated_internal(
277 &mut self,
278 event_state: EventState,
279 timestamp: &BACnetTimeStamp,
280 ) -> Result<(), Error> {
281 if !self.event_detection_enable {
282 return Err(Error::Protocol {
283 class: ErrorClass::OBJECT.to_raw() as u32,
284 code: ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
285 });
286 }
287 self.event_history.acknowledge_correlated(
288 &mut self.event_detector.acked_transitions,
289 event_state,
290 timestamp,
291 )
292 }
293
294 fn read_property(
295 &self,
296 property: PropertyIdentifier,
297 array_index: Option<u32>,
298 ) -> Result<PropertyValue, Error> {
299 if property == PropertyIdentifier::STATUS_FLAGS {
300 return Ok(common::compute_status_flags(
301 self.status_flags,
302 self.reliability,
303 self.out_of_service,
304 self.event_detector.event_state.to_raw(),
305 ));
306 }
307 if let Some(value) = self.reliability_inhibit.read(property) {
308 return Ok(value);
309 }
310 if let Some(result) = read_common_properties!(self, property, array_index) {
311 return result;
312 }
313 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
314 return Ok(PropertyValue::Boolean(self.event_detection_enable));
315 }
316 if let Some(result) = read_generic_event_properties!(self, property) {
317 return result;
318 }
319 if let Some(result) = self.event_history.read(property, array_index) {
320 return result;
321 }
322 match property {
323 p if p == PropertyIdentifier::OBJECT_TYPE => {
324 Ok(PropertyValue::Enumerated(ObjectType::BINARY_INPUT.to_raw()))
325 }
326 p if p == PropertyIdentifier::PRESENT_VALUE => {
327 Ok(PropertyValue::Enumerated(self.present_value))
328 }
329 p if p == PropertyIdentifier::POLARITY => Ok(PropertyValue::Enumerated(self.polarity)),
330 p if p == PropertyIdentifier::ACTIVE_TEXT => {
331 Ok(PropertyValue::CharacterString(self.active_text.clone()))
332 }
333 p if p == PropertyIdentifier::INACTIVE_TEXT => {
334 Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
335 }
336 p if p == PropertyIdentifier::ALARM_VALUE => {
337 Ok(PropertyValue::Enumerated(
340 self.event_detector
341 .alarm_values
342 .first()
343 .copied()
344 .unwrap_or(1),
345 ))
346 }
347 _ => Err(common::unknown_property_error()),
348 }
349 }
350
351 fn write_property(
352 &mut self,
353 property: PropertyIdentifier,
354 _array_index: Option<u32>,
355 value: PropertyValue,
356 _priority: Option<u8>,
357 ) -> Result<(), Error> {
358 if property == PropertyIdentifier::PRESENT_VALUE {
359 if !self.out_of_service {
360 return Err(common::write_access_denied_error());
361 }
362 return self.apply_present_value(value);
363 }
364 if property == PropertyIdentifier::ACTIVE_TEXT {
365 if let PropertyValue::CharacterString(s) = value {
366 self.active_text = s;
367 return Ok(());
368 }
369 return Err(common::invalid_data_type_error());
370 }
371 if property == PropertyIdentifier::INACTIVE_TEXT {
372 if let PropertyValue::CharacterString(s) = value {
373 self.inactive_text = s;
374 return Ok(());
375 }
376 return Err(common::invalid_data_type_error());
377 }
378 if property == PropertyIdentifier::ALARM_VALUE {
379 if let PropertyValue::Enumerated(v) = value {
380 if v > 1 {
381 return Err(common::value_out_of_range_error());
382 }
383 self.event_detector.alarm_values = vec![v];
384 return Ok(());
385 }
386 return Err(common::invalid_data_type_error());
387 }
388 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
389 if let PropertyValue::Boolean(v) = value {
390 self.event_detection_enable = v;
391 if !v {
392 self.event_detector.event_state = bacnet_types::enums::EventState::NORMAL;
393 self.event_detector.acked_transitions = 0b111;
394 self.event_detector.pending = None;
395 self.event_detector.fault_reliability = None;
396 self.event_history.reset();
397 }
398 return Ok(());
399 }
400 return Err(common::invalid_data_type_error());
401 }
402 if let Some(result) = write_generic_event_properties!(self, property, value) {
403 return result;
404 }
405 if let Some(result) = self.reliability_inhibit.write_inhibit(
406 &mut self.reliability,
407 self.out_of_service,
408 property,
409 &value,
410 ) {
411 return result;
412 }
413 if let Some(result) = self.reliability_inhibit.write_out_of_service(
414 &mut self.out_of_service,
415 &mut self.reliability,
416 &mut self.reliability_before_out_of_service,
417 property,
418 &value,
419 ) {
420 return result;
421 }
422 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
423 return result;
424 }
425 if let Some(result) = common::write_description(&mut self.description, property, &value) {
426 return result;
427 }
428 if let Some(result) = self.reliability_inhibit.write_client_reliability(
429 self.out_of_service,
430 &mut self.reliability,
431 property,
432 &value,
433 ) {
434 return result;
435 }
436 Err(common::write_access_denied_error())
437 }
438
439 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
440 let metadata = self.property_metadata();
441 crate::property_metadata::property_list_from_metadata(metadata.as_ref())
442 }
443
444 fn is_createable(&self) -> bool {
445 true
446 }
447
448 fn set_reliability_internal(&mut self, reliability: u32) -> Result<(), Error> {
449 if self.out_of_service || self.reliability_inhibit.enabled() {
450 return Err(common::write_access_denied_error());
451 }
452 if !common::is_reliability_value_valid(reliability) {
453 return Err(common::value_out_of_range_error());
454 }
455 self.reliability = reliability;
456 Ok(())
457 }
458
459 fn set_present_value_internal(&mut self, value: PropertyValue) -> Result<(), Error> {
460 if self.out_of_service {
462 return Err(common::write_access_denied_error());
463 }
464 self.apply_present_value(value)
465 }
466
467 fn reliability_evaluation_inhibited_internal(&self) -> bool {
468 self.reliability_inhibit.enabled()
469 }
470}
471
472#[cfg(test)]
473#[path = "tests/input_output.rs"]
474mod input_output_tests;