1use super::*;
2use crate::object_profile::{ObjectProfile, ProfileState, TagsPersistence};
3use crate::property_metadata::{
4 PropertyConformance, PropertyMetadata, PropertyPresenceCondition, PropertyWriteCapability,
5};
6use std::sync::Arc;
7
8const BINARY_INPUT_PROPERTY_METADATA: &[PropertyMetadata] = &[
9 PropertyMetadata::new(
10 PropertyIdentifier::OBJECT_IDENTIFIER,
11 PropertyConformance::RequiredRead,
12 None,
13 PropertyWriteCapability::ReadOnly,
14 ),
15 PropertyMetadata::new(
16 PropertyIdentifier::OBJECT_NAME,
17 PropertyConformance::RequiredRead,
18 None,
19 PropertyWriteCapability::Always,
20 ),
21 PropertyMetadata::new(
22 PropertyIdentifier::DESCRIPTION,
23 PropertyConformance::Optional,
24 None,
25 PropertyWriteCapability::Always,
26 ),
27 PropertyMetadata::new(
28 PropertyIdentifier::OBJECT_TYPE,
29 PropertyConformance::RequiredRead,
30 None,
31 PropertyWriteCapability::ReadOnly,
32 ),
33 PropertyMetadata::new(
34 PropertyIdentifier::PRESENT_VALUE,
35 PropertyConformance::RequiredRead,
36 None,
37 PropertyWriteCapability::WhenOutOfService,
38 ),
39 PropertyMetadata::new(
40 PropertyIdentifier::STATUS_FLAGS,
41 PropertyConformance::RequiredRead,
42 None,
43 PropertyWriteCapability::ReadOnly,
44 ),
45 PropertyMetadata::new(
46 PropertyIdentifier::EVENT_STATE,
47 PropertyConformance::RequiredRead,
48 None,
49 PropertyWriteCapability::ReadOnly,
50 ),
51 PropertyMetadata::new(
52 PropertyIdentifier::EVENT_DETECTION_ENABLE,
53 PropertyConformance::Optional,
54 Some(PropertyPresenceCondition::IntrinsicReportingRequired),
55 PropertyWriteCapability::Always,
56 ),
57 PropertyMetadata::new(
58 PropertyIdentifier::EVENT_ENABLE,
59 PropertyConformance::Optional,
60 Some(PropertyPresenceCondition::IntrinsicReportingRequired),
61 PropertyWriteCapability::Always,
62 ),
63 PropertyMetadata::new(
64 PropertyIdentifier::TIME_DELAY,
65 PropertyConformance::Optional,
66 Some(PropertyPresenceCondition::IntrinsicReportingRequired),
67 PropertyWriteCapability::Always,
68 ),
69 PropertyMetadata::new(
70 PropertyIdentifier::TIME_DELAY_NORMAL,
71 PropertyConformance::Optional,
72 Some(PropertyPresenceCondition::IntrinsicReportingOptional),
73 PropertyWriteCapability::Always,
74 ),
75 PropertyMetadata::new(
76 PropertyIdentifier::NOTIFY_TYPE,
77 PropertyConformance::Optional,
78 Some(PropertyPresenceCondition::IntrinsicReportingRequired),
79 PropertyWriteCapability::Always,
80 ),
81 PropertyMetadata::new(
82 PropertyIdentifier::NOTIFICATION_CLASS,
83 PropertyConformance::Optional,
84 Some(PropertyPresenceCondition::IntrinsicReportingRequired),
85 PropertyWriteCapability::Always,
86 ),
87 PropertyMetadata::new(
88 PropertyIdentifier::ACKED_TRANSITIONS,
89 PropertyConformance::Optional,
90 Some(PropertyPresenceCondition::IntrinsicReportingRequired),
91 PropertyWriteCapability::ReadOnly,
92 ),
93 PropertyMetadata::new(
94 PropertyIdentifier::EVENT_TIME_STAMPS,
95 PropertyConformance::Optional,
96 Some(PropertyPresenceCondition::IntrinsicReportingRequired),
97 PropertyWriteCapability::ReadOnly,
98 ),
99 PropertyMetadata::new(
100 PropertyIdentifier::EVENT_MESSAGE_TEXTS,
101 PropertyConformance::Optional,
102 Some(PropertyPresenceCondition::IntrinsicReportingOptional),
103 PropertyWriteCapability::ReadOnly,
104 ),
105 crate::event::options::REPORTING_OPTION_METADATA[0],
107 crate::event::options::REPORTING_OPTION_METADATA[1],
108 crate::event::options::REPORTING_OPTION_METADATA[2],
109 PropertyMetadata::new(
110 PropertyIdentifier::OUT_OF_SERVICE,
111 PropertyConformance::RequiredRead,
112 None,
113 PropertyWriteCapability::Always,
114 ),
115 PropertyMetadata::new(
116 PropertyIdentifier::POLARITY,
117 PropertyConformance::RequiredRead,
118 None,
119 PropertyWriteCapability::ReadOnly,
120 ),
121 PropertyMetadata::new(
122 PropertyIdentifier::RELIABILITY,
123 PropertyConformance::Optional,
124 None,
125 PropertyWriteCapability::WhenOutOfService,
126 ),
127 PropertyMetadata::new(
128 PropertyIdentifier::RELIABILITY_EVALUATION_INHIBIT,
129 PropertyConformance::Optional,
130 None,
131 PropertyWriteCapability::Always,
132 ),
133 PropertyMetadata::new(
134 PropertyIdentifier::ACTIVE_TEXT,
135 PropertyConformance::Optional,
136 Some(PropertyPresenceCondition::PairedText),
137 PropertyWriteCapability::Always,
138 ),
139 PropertyMetadata::new(
140 PropertyIdentifier::INACTIVE_TEXT,
141 PropertyConformance::Optional,
142 Some(PropertyPresenceCondition::PairedText),
143 PropertyWriteCapability::Always,
144 ),
145 PropertyMetadata::new(
146 PropertyIdentifier::ALARM_VALUE,
147 PropertyConformance::Optional,
148 Some(PropertyPresenceCondition::IntrinsicReportingRequired),
149 PropertyWriteCapability::Always,
150 ),
151 PropertyMetadata::new(
152 PropertyIdentifier::PROPERTY_LIST,
153 PropertyConformance::RequiredRead,
154 None,
155 PropertyWriteCapability::ReadOnly,
156 ),
157];
158
159pub struct BinaryInputObject {
168 profile: ProfileState,
169 oid: ObjectIdentifier,
170 name: String,
171 description: String,
172 present_value: u32,
173 out_of_service: bool,
174 status_flags: StatusFlags,
175 polarity: u32,
177 reliability: Reliability,
179 reliability_before_out_of_service: Option<Reliability>,
180 reliability_inhibit: common::ReliabilityInhibitState,
181 active_text: String,
182 inactive_text: String,
183 event_detector: ChangeOfStateDetector,
185 event_detection_enable: bool,
188 pub(crate) event_history: EventHistory,
189}
190
191impl BinaryInputObject {
192 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
194 let oid = ObjectIdentifier::new(ObjectType::BINARY_INPUT, instance)?;
195 Ok(Self {
196 profile: ProfileState::default(),
197 oid,
198 name: name.into(),
199 description: String::new(),
200 present_value: 0,
201 out_of_service: false,
202 status_flags: StatusFlags::empty(),
203 polarity: 0,
204 reliability: Reliability::NO_FAULT_DETECTED,
205 reliability_before_out_of_service: None,
206 reliability_inhibit: common::ReliabilityInhibitState::default(),
207 active_text: "Active".into(),
208 inactive_text: "Inactive".into(),
209 event_detector: ChangeOfStateDetector {
210 alarm_values: vec![1],
211 ..Default::default()
212 },
213 event_detection_enable: true,
214 event_history: EventHistory::default(),
215 })
216 }
217
218 pub fn with_tags_persistence(
222 instance: u32,
223 name: impl Into<String>,
224 persistence: Arc<dyn TagsPersistence>,
225 ) -> Result<Self, Error> {
226 let mut object = Self::new(instance, name)?;
227 object.profile = ProfileState::persistent(object.oid, persistence)?;
228 Ok(object)
229 }
230
231 pub fn set_profile(&mut self, profile: ObjectProfile) -> Result<(), Error> {
236 self.profile.provision(profile)
237 }
238
239 pub fn wait_for_tag_saves(&self) {
242 self.profile.wait_for_saves();
243 }
244
245 pub fn set_present_value(&mut self, value: u32) {
253 self.present_value = value;
254 }
255
256 fn apply_present_value(&mut self, value: PropertyValue) -> Result<(), Error> {
261 let PropertyValue::Enumerated(v) = value else {
262 return Err(common::invalid_data_type_error());
263 };
264 if v > 1 {
265 return Err(common::value_out_of_range_error());
266 }
267 self.present_value = v;
268 Ok(())
269 }
270
271 pub fn set_description(&mut self, desc: impl Into<String>) {
273 self.description = desc.into();
274 }
275}
276
277impl BACnetObject for BinaryInputObject {
278 fn object_identifier(&self) -> ObjectIdentifier {
279 self.oid
280 }
281
282 fn object_name(&self) -> &str {
283 &self.name
284 }
285
286 fn durable_writes_internal(&mut self) -> Option<&mut dyn crate::durable::DurableWrites> {
287 self.profile.capability()
288 }
289
290 fn advance_monotonic_time_internal(&mut self, now: std::time::Duration) -> bool {
291 self.profile.expire(now);
292 false
293 }
294
295 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
296 let mut rows = Cow::Borrowed(BINARY_INPUT_PROPERTY_METADATA);
297 if self.profile.metadata().next().is_some() {
298 rows.to_mut().extend(self.profile.metadata());
299 }
300 rows
301 }
302
303 fn supports_cov(&self) -> bool {
304 true
305 }
306
307 crate::event::impl_builtin_intrinsic_reporting!(
308 event_detector,
309 event_history,
310 [present_value],
311 reliability,
312 event_detection_enable,
313 ChangeOfStateDetector::ALGORITHM
314 );
315
316 fn acknowledge_alarm_correlated_internal(
317 &mut self,
318 event_state: EventState,
319 timestamp: &BACnetTimeStamp,
320 ) -> Result<(), Error> {
321 if !self.event_detection_enable {
322 return Err(Error::Protocol {
323 class: ErrorClass::OBJECT.to_raw() as u32,
324 code: ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
325 });
326 }
327 self.event_history.acknowledge_correlated(
328 &mut self.event_detector.acked_transitions,
329 event_state,
330 timestamp,
331 )
332 }
333
334 fn read_property(
335 &self,
336 property: PropertyIdentifier,
337 array_index: Option<u32>,
338 ) -> Result<PropertyValue, Error> {
339 if property == PropertyIdentifier::STATUS_FLAGS {
340 return Ok(common::compute_status_flags(
341 self.status_flags,
342 self.reliability,
343 self.out_of_service,
344 self.event_detector.event_state,
345 ));
346 }
347 if let Some(value) = self.reliability_inhibit.read(property) {
348 return Ok(value);
349 }
350 if let Some(result) = read_common_properties!(self, property, array_index) {
351 return result;
352 }
353 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
354 return Ok(PropertyValue::Boolean(self.event_detection_enable));
355 }
356 if let Some(result) = read_generic_event_properties!(self, property) {
357 return result;
358 }
359 if let Some(result) = self.event_history.read(property, array_index) {
360 return result;
361 }
362 if let Some(result) = self.profile.read(property, array_index) {
363 return result;
364 }
365 match property {
366 p if p == PropertyIdentifier::OBJECT_TYPE => {
367 Ok(PropertyValue::Enumerated(ObjectType::BINARY_INPUT.to_raw()))
368 }
369 p if p == PropertyIdentifier::PRESENT_VALUE => {
370 Ok(PropertyValue::Enumerated(self.present_value))
371 }
372 p if p == PropertyIdentifier::POLARITY => Ok(PropertyValue::Enumerated(self.polarity)),
373 p if p == PropertyIdentifier::ACTIVE_TEXT => {
374 Ok(PropertyValue::CharacterString(self.active_text.clone()))
375 }
376 p if p == PropertyIdentifier::INACTIVE_TEXT => {
377 Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
378 }
379 p if p == PropertyIdentifier::ALARM_VALUE => {
380 Ok(PropertyValue::Enumerated(
383 self.event_detector
384 .alarm_values
385 .first()
386 .copied()
387 .unwrap_or(1),
388 ))
389 }
390 _ => Err(common::unknown_property_error()),
391 }
392 }
393
394 fn write_property(
395 &mut self,
396 property: PropertyIdentifier,
397 array_index: Option<u32>,
398 value: PropertyValue,
399 _priority: Option<u8>,
400 ) -> Result<(), Error> {
401 if property == PropertyIdentifier::PRESENT_VALUE {
402 if !self.out_of_service {
403 return Err(common::write_access_denied_error());
404 }
405 return self.apply_present_value(value);
406 }
407 if let Some(result) = self.profile.write(property, array_index, &value) {
408 return result;
409 }
410 if property == PropertyIdentifier::ACTIVE_TEXT {
411 if let PropertyValue::CharacterString(s) = value {
412 self.active_text = s;
413 return Ok(());
414 }
415 return Err(common::invalid_data_type_error());
416 }
417 if property == PropertyIdentifier::INACTIVE_TEXT {
418 if let PropertyValue::CharacterString(s) = value {
419 self.inactive_text = s;
420 return Ok(());
421 }
422 return Err(common::invalid_data_type_error());
423 }
424 if property == PropertyIdentifier::ALARM_VALUE {
425 if let PropertyValue::Enumerated(v) = value {
426 if v > 1 {
427 return Err(common::value_out_of_range_error());
428 }
429 self.event_detector.alarm_values = vec![v];
430 return Ok(());
431 }
432 return Err(common::invalid_data_type_error());
433 }
434 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
435 if let PropertyValue::Boolean(v) = value {
436 self.event_detection_enable = v;
437 if !v {
438 self.event_detector.event_state = bacnet_types::enums::EventState::NORMAL;
439 self.event_detector.acked_transitions =
440 bacnet_types::bitstring::EventTransitionBits::all();
441 self.event_detector.pending = None;
442 self.event_detector.fault_reliability = None;
443 self.event_history.reset();
444 }
445 return Ok(());
446 }
447 return Err(common::invalid_data_type_error());
448 }
449 if let Some(result) =
451 self.event_history
452 .write(property, array_index, &value, self.event_detection_enable)
453 {
454 return result;
455 }
456 if let Some(result) = write_generic_event_properties!(self, property, value) {
457 return result;
458 }
459 if let Some(result) = self.reliability_inhibit.write_inhibit(
460 &mut self.reliability,
461 self.out_of_service,
462 property,
463 &value,
464 ) {
465 return result;
466 }
467 if let Some(result) = self.reliability_inhibit.write_out_of_service(
468 &mut self.out_of_service,
469 &mut self.reliability,
470 &mut self.reliability_before_out_of_service,
471 property,
472 &value,
473 ) {
474 return result.map(|_| ());
475 }
476 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
477 return result;
478 }
479 if let Some(result) = common::write_description(&mut self.description, property, &value) {
480 return result;
481 }
482 if let Some(result) = self.reliability_inhibit.write_client_reliability(
483 self.out_of_service,
484 &mut self.reliability,
485 property,
486 &value,
487 ) {
488 return result;
489 }
490 Err(crate::common::unhandled_write_error(
491 self.property_metadata().as_ref(),
492 property,
493 array_index,
494 ))
495 }
496
497 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
498 let metadata = self.property_metadata();
499 crate::property_metadata::property_list_from_metadata(metadata.as_ref())
500 }
501
502 fn is_createable(&self) -> bool {
503 true
504 }
505
506 fn set_reliability_internal(&mut self, reliability: Reliability) -> Result<(), Error> {
507 if self.out_of_service || self.reliability_inhibit.enabled() {
508 return Err(common::write_access_denied_error());
509 }
510 if !common::is_reliability_value_valid(reliability) {
511 return Err(common::value_out_of_range_error());
512 }
513 self.reliability = reliability;
514 Ok(())
515 }
516
517 fn set_present_value_internal(&mut self, value: PropertyValue) -> Result<(), Error> {
518 if self.out_of_service {
520 return Err(common::write_access_denied_error());
521 }
522 self.apply_present_value(value)
523 }
524
525 fn reliability_evaluation_inhibited_internal(&self) -> bool {
526 self.reliability_inhibit.enabled()
527 }
528}
529
530#[cfg(test)]
531#[path = "tests/input_output.rs"]
532mod input_output_tests;