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