1use super::*;
2use crate::present_value_access::PresentValueAccess;
3use crate::property_metadata::PropertyMetadata;
4
5#[path = "value/metadata.rs"]
6mod metadata;
7
8pub struct BinaryValueObject {
16 audit_policy: crate::audit::ObjectAuditPolicy,
17 oid: ObjectIdentifier,
18 name: String,
19 description: String,
20 present_value: u32, out_of_service: bool,
22 status_flags: StatusFlags,
23 access: PresentValueAccess,
24 priority_array: [Option<u32>; 16],
25 relinquish_default: u32,
26 reliability: Reliability,
28 reliability_before_out_of_service: Option<Reliability>,
29 reliability_inhibit: common::ReliabilityInhibitState,
30 active_text: String,
31 inactive_text: String,
32 event_detector: ChangeOfStateDetector,
34 event_detection_enable: bool,
37 pub(crate) event_history: EventHistory,
38 value_source: crate::command_source::ValueSourceTracking,
40}
41
42impl BinaryValueObject {
43 pub fn set_audit_policy(&mut self, policy: crate::audit::ObjectAuditPolicy) {
47 self.audit_policy = policy;
48 }
49
50 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
52 Self::with_access(instance, name, PresentValueAccess::Commandable)
53 }
54
55 pub fn with_access(
57 instance: u32,
58 name: impl Into<String>,
59 access: PresentValueAccess,
60 ) -> Result<Self, Error> {
61 let oid = ObjectIdentifier::new(ObjectType::BINARY_VALUE, instance)?;
62 Ok(Self {
63 audit_policy: crate::audit::ObjectAuditPolicy::default(),
64 oid,
65 name: name.into(),
66 description: String::new(),
67 present_value: 0, out_of_service: false,
69 status_flags: StatusFlags::empty(),
70 access,
71 priority_array: [None; 16],
72 relinquish_default: 0,
73 reliability: Reliability::NO_FAULT_DETECTED,
74 reliability_before_out_of_service: None,
75 reliability_inhibit: common::ReliabilityInhibitState::default(),
76 active_text: "Active".into(),
77 inactive_text: "Inactive".into(),
78 event_detector: ChangeOfStateDetector {
79 alarm_values: vec![1],
80 ..Default::default()
81 },
82 event_detection_enable: true,
83 event_history: EventHistory::default(),
84 value_source: crate::command_source::ValueSourceTracking::default(),
85 })
86 }
87
88 pub fn set_description(&mut self, desc: impl Into<String>) {
90 self.description = desc.into();
91 }
92
93 fn recalculate_present_value(&mut self) {
94 self.present_value =
95 common::recalculate_from_priority_array(&self.priority_array, self.relinquish_default);
96 }
97
98 pub fn set_relinquish_default(&mut self, value: u32) -> Result<(), Error> {
104 if self.access != PresentValueAccess::Commandable {
105 return Err(common::unknown_property_error());
106 }
107 if value > 1 {
108 return Err(common::value_out_of_range_error());
109 }
110 self.relinquish_default = value;
111 self.recalculate_present_value();
112 Ok(())
113 }
114
115 fn checked_present_value(value: PropertyValue) -> Result<u32, Error> {
116 let PropertyValue::Enumerated(e) = value else {
117 return Err(common::invalid_data_type_error());
118 };
119 if e > 1 {
120 return Err(common::value_out_of_range_error());
121 }
122 Ok(e)
123 }
124}
125
126impl BACnetObject for BinaryValueObject {
127 fn audit_policy_authority_internal(
128 &mut self,
129 ) -> Option<crate::audit::AuditPolicyAuthority<'_>> {
130 Some(crate::audit::AuditPolicyAuthority::new(
131 &mut self.audit_policy,
132 ))
133 }
134
135 fn audit_object_policy_internal(&self) -> crate::audit::ObjectAuditPolicy {
136 self.audit_policy
137 }
138
139 fn object_identifier(&self) -> ObjectIdentifier {
140 self.oid
141 }
142
143 fn object_name(&self) -> &str {
144 &self.name
145 }
146
147 fn supports_cov(&self) -> bool {
148 true
149 }
150
151 crate::event::impl_builtin_intrinsic_reporting!(
152 event_detector,
153 event_history,
154 [present_value],
155 reliability,
156 event_detection_enable,
157 ChangeOfStateDetector::ALGORITHM
158 );
159
160 fn acknowledge_alarm_correlated_internal(
161 &mut self,
162 event_state: EventState,
163 timestamp: &BACnetTimeStamp,
164 ) -> Result<(), Error> {
165 if !self.event_detection_enable {
166 return Err(Error::Protocol {
167 class: ErrorClass::OBJECT.to_raw() as u32,
168 code: ErrorCode::NO_ALARM_CONFIGURED.to_raw() as u32,
169 });
170 }
171 self.event_history.acknowledge_correlated(
172 &mut self.event_detector.acked_transitions,
173 event_state,
174 timestamp,
175 )
176 }
177
178 fn read_property(
179 &self,
180 property: PropertyIdentifier,
181 array_index: Option<u32>,
182 ) -> Result<PropertyValue, Error> {
183 if metadata::excludes(self, property) {
184 return Err(common::unknown_property_error());
185 }
186 if let Some(result) = self
187 .value_source
188 .read(property, array_index, &self.priority_array)
189 {
190 return result;
191 }
192
193 if let Some(result) = self.audit_policy.read(property, array_index) {
194 return result;
195 }
196 if property == PropertyIdentifier::STATUS_FLAGS {
197 return Ok(common::compute_status_flags(
198 self.status_flags,
199 self.reliability,
200 self.out_of_service,
201 self.event_detector.event_state,
202 ));
203 }
204 if let Some(value) = self.reliability_inhibit.read(property) {
205 return Ok(value);
206 }
207 if let Some(result) = read_common_properties!(self, property, array_index) {
208 return result;
209 }
210 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
211 return Ok(PropertyValue::Boolean(self.event_detection_enable));
212 }
213 if let Some(result) = read_generic_event_properties!(self, property) {
214 return result;
215 }
216 if let Some(result) = self.event_history.read(property, array_index) {
217 return result;
218 }
219 match property {
220 p if p == PropertyIdentifier::OBJECT_TYPE => {
221 Ok(PropertyValue::Enumerated(ObjectType::BINARY_VALUE.to_raw()))
222 }
223 p if p == PropertyIdentifier::PRESENT_VALUE => {
224 Ok(PropertyValue::Enumerated(self.present_value))
225 }
226 p if p == PropertyIdentifier::PRIORITY_ARRAY => {
227 common::read_priority_array!(self, array_index, PropertyValue::Enumerated)
228 }
229 p if p == PropertyIdentifier::RELINQUISH_DEFAULT => {
230 Ok(PropertyValue::Enumerated(self.relinquish_default))
231 }
232 p if p == PropertyIdentifier::CURRENT_COMMAND_PRIORITY => {
233 Ok(common::current_command_priority(&self.priority_array))
234 }
235 p if p == PropertyIdentifier::ACTIVE_TEXT => {
236 Ok(PropertyValue::CharacterString(self.active_text.clone()))
237 }
238 p if p == PropertyIdentifier::INACTIVE_TEXT => {
239 Ok(PropertyValue::CharacterString(self.inactive_text.clone()))
240 }
241 p if p == PropertyIdentifier::ALARM_VALUE => {
242 Ok(PropertyValue::Enumerated(
245 self.event_detector
246 .alarm_values
247 .first()
248 .copied()
249 .unwrap_or(1),
250 ))
251 }
252 _ => Err(common::unknown_property_error()),
253 }
254 }
255
256 fn write_property_from(
257 &mut self,
258 property: PropertyIdentifier,
259 array_index: Option<u32>,
260 value: PropertyValue,
261 priority: Option<u8>,
262 origin: &crate::command_source::CommandOrigin,
263 ) -> Result<(), Error> {
264 if matches!(
265 property,
266 PropertyIdentifier::PRESENT_VALUE | PropertyIdentifier::VALUE_SOURCE
267 ) && array_index.is_some()
268 {
269 return Err(common::property_is_not_an_array_error());
270 }
271 if property == PropertyIdentifier::VALUE_SOURCE
272 && self.access == PresentValueAccess::Commandable
273 {
274 return self.value_source.correct(value, priority, origin);
275 }
276 if property == PropertyIdentifier::PRESENT_VALUE {
277 return match self.access {
278 PresentValueAccess::Commandable => {
279 crate::command_source::write_sourced_priority!(
280 self,
281 value,
282 priority,
283 origin,
284 Self::checked_present_value
285 )
286 }
287 PresentValueAccess::ReadOnly | PresentValueAccess::Writable => {
288 self.write_property(property, array_index, value, priority)
289 }
290 };
291 }
292 self.write_property(property, array_index, value, priority)
293 }
294
295 fn write_property(
296 &mut self,
297 property: PropertyIdentifier,
298 array_index: Option<u32>,
299 value: PropertyValue,
300 priority: Option<u8>,
301 ) -> Result<(), Error> {
302 if metadata::excludes(self, property) {
303 return Err(common::unknown_property_error());
304 }
305 if let Some(result) = self
306 .audit_policy
307 .write(property, array_index, &value, priority)
308 {
309 return result;
310 }
311
312 if property == PropertyIdentifier::PRESENT_VALUE {
313 if self.access == PresentValueAccess::Commandable {
314 return Err(common::write_access_denied_error());
315 }
316 if array_index.is_some() {
317 return Err(common::property_is_not_an_array_error());
318 }
319 if self.access == PresentValueAccess::ReadOnly && !self.out_of_service {
320 return Err(common::write_access_denied_error());
321 }
322 if value == PropertyValue::Null {
324 return Ok(());
325 }
326 self.present_value = Self::checked_present_value(value)?;
327 return Ok(());
328 }
329 if property == PropertyIdentifier::VALUE_SOURCE {
330 return Err(common::write_access_denied_error());
331 }
332 if property == PropertyIdentifier::ACTIVE_TEXT {
333 if let PropertyValue::CharacterString(s) = value {
334 self.active_text = s;
335 return Ok(());
336 }
337 return Err(common::invalid_data_type_error());
338 }
339 if property == PropertyIdentifier::INACTIVE_TEXT {
340 if let PropertyValue::CharacterString(s) = value {
341 self.inactive_text = s;
342 return Ok(());
343 }
344 return Err(common::invalid_data_type_error());
345 }
346 if property == PropertyIdentifier::ALARM_VALUE {
347 if let PropertyValue::Enumerated(v) = value {
348 if v > 1 {
349 return Err(common::value_out_of_range_error());
350 }
351 self.event_detector.alarm_values = vec![v];
352 return Ok(());
353 }
354 return Err(common::invalid_data_type_error());
355 }
356 if property == PropertyIdentifier::EVENT_DETECTION_ENABLE {
357 if let PropertyValue::Boolean(v) = value {
358 self.event_detection_enable = v;
359 if !v {
360 self.event_detector.event_state = bacnet_types::enums::EventState::NORMAL;
361 self.event_detector.acked_transitions =
362 bacnet_types::bitstring::EventTransitionBits::all();
363 self.event_detector.pending = None;
364 self.event_detector.fault_reliability = None;
365 self.event_history.reset();
366 }
367 return Ok(());
368 }
369 return Err(common::invalid_data_type_error());
370 }
371 if property == PropertyIdentifier::RELINQUISH_DEFAULT {
372 if let PropertyValue::Enumerated(e) = value {
373 return self.set_relinquish_default(e);
374 }
375 return Err(common::invalid_data_type_error());
376 }
377 if let Some(result) =
379 self.event_history
380 .write(property, array_index, &value, self.event_detection_enable)
381 {
382 return result;
383 }
384 if let Some(result) = write_generic_event_properties!(self, property, value) {
385 return result;
386 }
387 if let Some(result) = self.reliability_inhibit.write_inhibit(
388 &mut self.reliability,
389 self.out_of_service,
390 property,
391 &value,
392 ) {
393 return result;
394 }
395 if let Some(result) = self.reliability_inhibit.write_out_of_service(
396 &mut self.out_of_service,
397 &mut self.reliability,
398 &mut self.reliability_before_out_of_service,
399 property,
400 &value,
401 ) {
402 return result.map(|_| ());
403 }
404 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
405 return result;
406 }
407 if let Some(result) = common::write_description(&mut self.description, property, &value) {
408 return result;
409 }
410 if let Some(result) = self.reliability_inhibit.write_client_reliability(
415 self.out_of_service,
416 &mut self.reliability,
417 property,
418 &value,
419 ) {
420 return result;
421 }
422 Err(crate::common::unhandled_write_error(
423 self.property_metadata().as_ref(),
424 property,
425 array_index,
426 ))
427 }
428
429 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
430 metadata::for_object(self)
431 }
432
433 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
434 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
435 }
436
437 fn is_createable(&self) -> bool {
438 true
439 }
440
441 fn set_reliability_internal(&mut self, reliability: Reliability) -> Result<(), Error> {
442 if self.out_of_service || self.reliability_inhibit.enabled() {
443 return Err(common::write_access_denied_error());
444 }
445 if !common::is_reliability_value_valid(reliability) {
446 return Err(common::value_out_of_range_error());
447 }
448 self.reliability = reliability;
449 Ok(())
450 }
451
452 fn set_present_value_internal(&mut self, value: PropertyValue) -> Result<(), Error> {
453 match self.access {
454 PresentValueAccess::Commandable => {
455 Err(common::optional_functionality_not_supported_error())
456 }
457 _ if self.out_of_service => Err(common::write_access_denied_error()),
458 PresentValueAccess::ReadOnly | PresentValueAccess::Writable => {
459 self.present_value = Self::checked_present_value(value)?;
460 Ok(())
461 }
462 }
463 }
464
465 fn reliability_evaluation_inhibited_internal(&self) -> bool {
466 self.reliability_inhibit.enabled()
467 }
468}
469
470#[cfg(test)]
471#[path = "tests/value.rs"]
472mod value_tests;