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