1use super::*;
2use crate::object_profile::{ObjectProfile, ProfileState, TagsPersistence};
3use crate::prelude::*;
4use crate::property_metadata::PropertyMetadata;
5use alloc::sync::Arc;
6
7#[path = "output/metadata.rs"]
8mod metadata;
9
10pub struct BinaryOutputObject {
19 profile: ProfileState,
20 oid: ObjectIdentifier,
21 name: String,
22 description: String,
23 present_value: u32,
24 feedback_value: u32,
25 out_of_service: bool,
26 status_flags: StatusFlags,
27 priority_array: [Option<u32>; 16],
28 relinquish_default: u32,
29 polarity: u32,
31 reliability: Reliability,
33 reliability_before_out_of_service: Option<Reliability>,
34 reliability_inhibit: common::ReliabilityInhibitState,
35 event_detection_enable: bool,
36 active_text: String,
37 inactive_text: String,
38 event_detector: CommandFailureDetector,
40 pub(crate) event_history: EventHistory,
41 value_source: crate::command_source::ValueSourceTracking,
43}
44
45impl BinaryOutputObject {
46 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
48 let oid = ObjectIdentifier::new(ObjectType::BINARY_OUTPUT, instance)?;
49 Ok(Self {
50 profile: ProfileState::default(),
51 oid,
52 name: name.into(),
53 description: String::new(),
54 present_value: 0,
55 feedback_value: 0,
56 out_of_service: false,
57 status_flags: StatusFlags::empty(),
58 priority_array: [None; 16],
59 relinquish_default: 0,
60 polarity: 0,
61 reliability: Reliability::NO_FAULT_DETECTED,
62 reliability_before_out_of_service: None,
63 reliability_inhibit: common::ReliabilityInhibitState::default(),
64 event_detection_enable: false,
65 active_text: "Active".into(),
66 inactive_text: "Inactive".into(),
67 event_detector: CommandFailureDetector::default(),
68 event_history: EventHistory::default(),
69 value_source: crate::command_source::ValueSourceTracking::default(),
70 })
71 }
72
73 pub fn with_tags_persistence(
77 instance: u32,
78 name: impl Into<String>,
79 persistence: Arc<dyn TagsPersistence>,
80 ) -> Result<Self, Error> {
81 let mut object = Self::new(instance, name)?;
82 object.profile = ProfileState::persistent(object.oid, persistence)?;
83 Ok(object)
84 }
85
86 pub fn set_profile(&mut self, profile: ObjectProfile) -> Result<(), Error> {
91 self.profile.provision(profile)
92 }
93
94 pub fn wait_for_tag_saves(&self) {
97 self.profile.wait_for_saves();
98 }
99
100 pub fn set_description(&mut self, desc: impl Into<String>) {
102 self.description = desc.into();
103 }
104
105 fn recalculate_present_value(&mut self) {
106 self.present_value =
107 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
108 }
109
110 pub fn set_relinquish_default(&mut self, value: u32) -> Result<(), Error> {
116 if value > 1 {
117 return Err(common::value_out_of_range_error());
118 }
119 self.relinquish_default = value;
120 self.recalculate_present_value();
121 Ok(())
122 }
123}
124
125impl BACnetObject for BinaryOutputObject {
126 fn object_identifier(&self) -> ObjectIdentifier {
127 self.oid
128 }
129
130 fn object_name(&self) -> &str {
131 &self.name
132 }
133
134 fn supports_cov(&self) -> bool {
135 true
136 }
137
138 crate::event::impl_builtin_intrinsic_reporting!(
139 event_detector,
140 event_history,
141 [present_value, feedback_value],
142 reliability,
143 event_detection_enable,
144 CommandFailureDetector::ALGORITHM
145 );
146
147 fn acknowledge_alarm_correlated_internal(
148 &mut self,
149 event_state: EventState,
150 timestamp: &BACnetTimeStamp,
151 ) -> Result<(), Error> {
152 if !self.event_detection_enable {
153 return Err(Error::protocol(
154 ErrorClass::OBJECT,
155 ErrorCode::NO_ALARM_CONFIGURED,
156 ));
157 }
158 self.event_history.acknowledge_correlated(
159 &mut self.event_detector.acked_transitions,
160 event_state,
161 timestamp,
162 )
163 }
164
165 fn read_property(
166 &self,
167 property: PropertyIdentifier,
168 array_index: Option<u32>,
169 ) -> Result<PropertyValue, Error> {
170 if let Some(result) = self
171 .value_source
172 .read(property, array_index, &self.priority_array)
173 {
174 return result;
175 }
176
177 if property == PropertyIdentifier::STATUS_FLAGS {
178 return Ok(common::compute_status_flags(
179 self.status_flags,
180 self.reliability,
181 self.out_of_service,
182 self.event_detector.event_state,
183 ));
184 }
185 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
186 return Ok(PropertyValue::Boolean(self.event_detection_enable));
187 }
188 if let Some(value) = self.reliability_inhibit.read(property) {
189 return Ok(value);
190 }
191 if let Some(result) = read_common_properties!(self, property, array_index) {
192 return result;
193 }
194 if let Some(result) = read_generic_event_properties!(self, property) {
195 return result;
196 }
197 if let Some(result) = self.event_history.read(property, array_index) {
198 return result;
199 }
200 if let Some(result) = self.profile.read(property, array_index) {
201 return result;
202 }
203 match property {
204 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
205 ObjectType::BINARY_OUTPUT.to_raw(),
206 )),
207 p if p == PropertyIdentifier::PRESENT_VALUE => {
208 Ok(PropertyValue::Enumerated(self.present_value))
209 }
210 p if p == PropertyIdentifier::FEEDBACK_VALUE => {
211 Ok(PropertyValue::Enumerated(self.feedback_value))
212 }
213 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
214 common::read_priority_array!(self, array_index, PropertyValue::Enumerated)
215 }
216 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
217 Ok(PropertyValue::Enumerated(self.relinquish_default))
218 }
219 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
220 Ok(common::current_command_priority(&self.priority_array))
221 }
222 p if p == PropertyIdentifier::POLARITY => Ok(PropertyValue::Enumerated(self.polarity)),
223 p if p == PropertyIdentifier::ACTIVE_TEXT => {
224 Ok(PropertyValue::CharacterString(self.active_text.clone()))
225 }
226 p if p == PropertyIdentifier::INACTIVE_TEXT => {
227 Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
228 }
229 _ => Err(common::unknown_property_error()),
230 }
231 }
232
233 fn write_property_from(
234 &mut self,
235 property: PropertyIdentifier,
236 array_index: Option<u32>,
237 value: PropertyValue,
238 priority: Option<u8>,
239 origin: &crate::command_source::CommandOrigin,
240 ) -> Result<(), Error> {
241 if matches!(
242 property,
243 PropertyIdentifier::PRESENT_VALUE | PropertyIdentifier::VALUE_SOURCE
244 ) && array_index.is_some()
245 {
246 return Err(common::property_is_not_an_array_error());
247 }
248 if property == PropertyIdentifier::VALUE_SOURCE {
249 return self.value_source.correct(value, priority, origin);
250 }
251 if property == PropertyIdentifier::PRESENT_VALUE {
252 return crate::command_source::write_sourced_priority!(
253 self,
254 value,
255 priority,
256 origin,
257 |v| {
258 if let PropertyValue::Enumerated(e) = v {
259 if e > 1 {
260 Err(common::value_out_of_range_error())
261 } else {
262 Ok(e)
263 }
264 } else {
265 Err(common::invalid_data_type_error())
266 }
267 }
268 );
269 }
270 self.write_property(property, array_index, value, priority)
271 }
272
273 fn write_property(
274 &mut self,
275 property: PropertyIdentifier,
276 array_index: Option<u32>,
277 value: PropertyValue,
278 _priority: Option<u8>,
279 ) -> Result<(), Error> {
280 if matches!(
281 property,
282 PropertyIdentifier::PRESENT_VALUE | PropertyIdentifier::VALUE_SOURCE
283 ) {
284 return Err(common::write_access_denied_error());
285 }
286 if property == PropertyIdentifier::FEEDBACK_VALUE {
287 if let PropertyValue::Enumerated(e) = value {
288 if e > 1 {
289 return Err(common::value_out_of_range_error());
290 }
291 self.feedback_value = e;
292 return Ok(());
293 }
294 return Err(common::invalid_data_type_error());
295 }
296 if property == PropertyIdentifier::ACTIVE_TEXT {
297 if let PropertyValue::CharacterString(s) = value {
298 self.active_text = s;
299 return Ok(());
300 }
301 return Err(common::invalid_data_type_error());
302 }
303 if property == PropertyIdentifier::INACTIVE_TEXT {
304 if let PropertyValue::CharacterString(s) = value {
305 self.inactive_text = s;
306 return Ok(());
307 }
308 return Err(common::invalid_data_type_error());
309 }
310 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
311 if let PropertyValue::Boolean(v) = value {
312 self.event_detection_enable = v;
313 if !v {
314 self.event_detector.event_state = bacnet_types::enums::EventState::NORMAL;
315 self.event_detector.acked_transitions =
316 bacnet_types::bitstring::EventTransitionBits::all();
317 self.event_detector.pending = None;
318 self.event_detector.fault_reliability = None;
319 self.event_history.reset();
320 }
321 return Ok(());
322 }
323 return Err(common::invalid_data_type_error());
324 }
325 if property == PropertyIdentifier::RELINQUISH_DEFAULT {
326 if let PropertyValue::Enumerated(e) = value {
327 return self.set_relinquish_default(e);
328 }
329 return Err(common::invalid_data_type_error());
330 }
331 if let Some(result) =
333 self.event_history
334 .write(property, array_index, &value, self.event_detection_enable)
335 {
336 return result;
337 }
338 if let Some(result) = write_generic_event_properties!(self, property, value) {
339 return result;
340 }
341 if let Some(result) = self.reliability_inhibit.write_inhibit(
342 &mut self.reliability,
343 self.out_of_service,
344 property,
345 &value,
346 ) {
347 return result;
348 }
349 if let Some(result) = self.reliability_inhibit.write_out_of_service(
350 &mut self.out_of_service,
351 &mut self.reliability,
352 &mut self.reliability_before_out_of_service,
353 property,
354 &value,
355 ) {
356 return result.map(|_| ());
357 }
358 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
359 return result;
360 }
361 if let Some(result) = common::write_description(&mut self.description, property, &value) {
362 return result;
363 }
364 if let Some(result) = self.reliability_inhibit.write_client_reliability(
369 self.out_of_service,
370 &mut self.reliability,
371 property,
372 &value,
373 ) {
374 return result;
375 }
376 if let Some(result) = self.profile.write(property, array_index, &value) {
377 return result;
378 }
379 Err(crate::common::unhandled_write_error(
380 self.property_metadata().as_ref(),
381 property,
382 array_index,
383 ))
384 }
385
386 fn durable_writes_internal(&mut self) -> Option<&mut dyn crate::durable::DurableWrites> {
387 self.profile.capability()
388 }
389
390 fn advance_monotonic_time_internal(&mut self, now: core::time::Duration) -> bool {
391 self.profile.expire(now);
392 false
393 }
394
395 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
396 metadata::for_object(self)
397 }
398
399 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
400 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
401 }
402
403 fn is_createable(&self) -> bool {
404 true
405 }
406
407 fn set_reliability_internal(&mut self, reliability: Reliability) -> Result<(), Error> {
408 if self.out_of_service || self.reliability_inhibit.enabled() {
409 return Err(common::write_access_denied_error());
410 }
411 if !common::is_reliability_value_valid(reliability) {
412 return Err(common::value_out_of_range_error());
413 }
414 self.reliability = reliability;
415 Ok(())
416 }
417
418 fn reliability_evaluation_inhibited_internal(&self) -> bool {
419 self.reliability_inhibit.enabled()
420 }
421}
422
423#[cfg(test)]
424#[path = "tests/command_failure.rs"]
425mod command_failure_tests;