1use crate::prelude::*;
8use alloc::borrow::Cow;
9use bacnet_encoding::constructed::{
10 encode_property_access_result, encode_read_access_specification,
11};
12use bacnet_types::bitstring::status_flags_from_bacnet;
13use bacnet_types::constructed::{
14 AccessResult, BACnetDeviceObjectPropertyReference, BACnetDeviceObjectReference,
15 BACnetPropertyAccessResult, ReadAccessSpecification,
16};
17use bacnet_types::enums::{EventState, ObjectType, PropertyIdentifier, Reliability};
18use bacnet_types::error::Error;
19use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
20use bytes::BytesMut;
21
22use crate::common::{self, read_common_properties, read_identity_properties};
23use crate::device_reference;
24use crate::traits::BACnetObject;
25
26mod metadata;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum GroupMemberRefusal {
35 NoProperties,
37 NestsGroupPresentValue,
40}
41
42impl core::fmt::Display for GroupMemberRefusal {
43 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44 match self {
45 Self::NoProperties => f.write_str("the member lists no properties"),
46 Self::NestsGroupPresentValue => f.write_str(
47 "the member reports a Group or Global Group's Present_Value, \
48 by name or through ALL or REQUIRED",
49 ),
50 }
51 }
52}
53
54impl core::error::Error for GroupMemberRefusal {}
55
56impl From<GroupMemberRefusal> for Error {
57 fn from(_: GroupMemberRefusal) -> Self {
58 common::value_out_of_range_error()
59 }
60}
61
62pub struct GroupObject {
76 oid: ObjectIdentifier,
77 name: String,
78 description: String,
79 list_of_group_members: Vec<ReadAccessSpecification>,
80}
81
82impl GroupObject {
83 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
85 let oid = ObjectIdentifier::new(ObjectType::GROUP, instance)?;
86 Ok(Self {
87 oid,
88 name: name.into(),
89 description: String::new(),
90 list_of_group_members: Vec::new(),
91 })
92 }
93
94 pub fn add_member(
105 &mut self,
106 member: ReadAccessSpecification,
107 ) -> Result<(), GroupMemberRefusal> {
108 let references = &member.list_of_property_references;
109 if references.is_empty() {
110 return Err(GroupMemberRefusal::NoProperties);
111 }
112 let nests_a_group = matches!(
113 member.object_identifier.object_type(),
114 ObjectType::GROUP | ObjectType::GLOBAL_GROUP
115 ) && references.iter().any(|reference| {
116 matches!(
117 reference.property_identifier,
118 PropertyIdentifier::PRESENT_VALUE
119 | PropertyIdentifier::ALL
120 | PropertyIdentifier::REQUIRED
121 )
122 });
123 if nests_a_group {
124 return Err(GroupMemberRefusal::NestsGroupPresentValue);
125 }
126 self.list_of_group_members.push(member);
127 Ok(())
128 }
129
130 pub fn members(&self) -> &[ReadAccessSpecification] {
132 &self.list_of_group_members
133 }
134
135 pub fn clear_members(&mut self) {
137 self.list_of_group_members.clear();
138 }
139}
140
141impl BACnetObject for GroupObject {
142 fn object_identifier(&self) -> ObjectIdentifier {
143 self.oid
144 }
145
146 fn object_name(&self) -> &str {
147 &self.name
148 }
149
150 fn read_property(
151 &self,
152 property: PropertyIdentifier,
153 array_index: Option<u32>,
154 ) -> Result<PropertyValue, Error> {
155 if let Some(result) = read_identity_properties!(self, property, array_index) {
157 return result;
158 }
159 match property {
160 p if p == PropertyIdentifier::OBJECT_TYPE => {
161 Ok(PropertyValue::Enumerated(ObjectType::GROUP.to_raw()))
162 }
163 p if (p == PropertyIdentifier::LIST_OF_GROUP_MEMBERS
166 || p == PropertyIdentifier::PRESENT_VALUE)
167 && array_index.is_some() =>
168 {
169 Err(common::property_is_not_an_array_error())
170 }
171 p if p == PropertyIdentifier::LIST_OF_GROUP_MEMBERS => Ok(PropertyValue::List(
173 self.list_of_group_members
174 .iter()
175 .map(|member| {
176 let mut encoded = BytesMut::new();
177 encode_read_access_specification(&mut encoded, member);
178 PropertyValue::ApplicationData(encoded.to_vec())
179 })
180 .collect(),
181 )),
182 p if p == PropertyIdentifier::PRESENT_VALUE => Ok(PropertyValue::List(Vec::new())),
184 _ => Err(common::unknown_property_error()),
185 }
186 }
187
188 fn write_property(
189 &mut self,
190 property: PropertyIdentifier,
191 _array_index: Option<u32>,
192 value: PropertyValue,
193 _priority: Option<u8>,
194 ) -> Result<(), Error> {
195 if let Some(result) = common::write_description(&mut self.description, property, &value) {
196 return result;
197 }
198 Err(crate::common::unhandled_write_error(
199 self.property_metadata().as_ref(),
200 property,
201 _array_index,
202 ))
203 }
204
205 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
206 metadata::for_group_object(self)
207 }
208
209 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
210 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
211 }
212}
213
214pub struct GlobalGroupObject {
238 oid: ObjectIdentifier,
239 name: String,
240 description: String,
241 status_flags: StatusFlags,
242 out_of_service: bool,
243 reliability: Reliability,
244 group_members: Vec<BACnetDeviceObjectPropertyReference>,
247 pub present_value: Vec<AccessResult>,
250 pub group_member_names: Vec<String>,
252}
253
254impl GlobalGroupObject {
255 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
257 let oid = ObjectIdentifier::new(ObjectType::GLOBAL_GROUP, instance)?;
258 Ok(Self {
259 oid,
260 name: name.into(),
261 description: String::new(),
262 status_flags: StatusFlags::empty(),
263 out_of_service: false,
264 reliability: Reliability::NO_FAULT_DETECTED,
265 group_members: Vec::new(),
266 present_value: Vec::new(),
267 group_member_names: Vec::new(),
268 })
269 }
270
271 pub fn set_group_members(
280 &mut self,
281 members: Vec<BACnetDeviceObjectPropertyReference>,
282 ) -> Result<(), Error> {
283 device_reference::check_device_members(&members)?;
284 self.group_members = members;
285 Ok(())
286 }
287
288 pub fn add_group_member(
292 &mut self,
293 member: BACnetDeviceObjectPropertyReference,
294 ) -> Result<(), Error> {
295 device_reference::check_device_member(member.device_identifier)?;
296 self.group_members.push(member);
297 Ok(())
298 }
299
300 pub fn group_members(&self) -> &[BACnetDeviceObjectPropertyReference] {
302 &self.group_members
303 }
304
305 pub fn member_status_flags(&self) -> StatusFlags {
313 let status_flags = PropertyIdentifier::STATUS_FLAGS.to_raw();
314 self.group_members
315 .iter()
316 .zip(&self.present_value)
317 .filter(|(member, _)| member.property_identifier == status_flags)
318 .filter_map(|(_, result)| match result {
319 AccessResult::Value(PropertyValue::BitString { data, .. }) => {
320 Some(status_flags_from_bacnet(data))
321 }
322 _ => None,
323 })
324 .fold(StatusFlags::empty(), |combined, flags| combined | flags)
325 }
326
327 fn present_value_elements(&self) -> Result<Vec<PropertyValue>, Error> {
330 self.group_members
331 .iter()
332 .enumerate()
333 .map(|(index, reference)| {
334 let element = BACnetPropertyAccessResult {
335 reference: reference.clone(),
336 access_result: self
337 .present_value
338 .get(index)
339 .cloned()
340 .unwrap_or(AccessResult::NOT_INITIALIZED),
341 };
342 let mut encoded = BytesMut::new();
343 encode_property_access_result(&mut encoded, &element)?;
344 Ok(PropertyValue::ApplicationData(encoded.to_vec()))
345 })
346 .collect()
347 }
348}
349
350impl BACnetObject for GlobalGroupObject {
351 fn object_identifier(&self) -> ObjectIdentifier {
352 self.oid
353 }
354
355 fn object_name(&self) -> &str {
356 &self.name
357 }
358
359 fn read_property(
360 &self,
361 property: PropertyIdentifier,
362 array_index: Option<u32>,
363 ) -> Result<PropertyValue, Error> {
364 if let Some(result) = read_common_properties!(self, property, array_index) {
365 return result;
366 }
367 match property {
368 p if p == PropertyIdentifier::OBJECT_TYPE => {
369 Ok(PropertyValue::Enumerated(ObjectType::GLOBAL_GROUP.to_raw()))
370 }
371 p if p == PropertyIdentifier::GROUP_MEMBERS => common::read_array(
375 device_reference::reference_elements(&self.group_members),
376 array_index,
377 ),
378 p if p == PropertyIdentifier::PRESENT_VALUE => {
379 common::read_array(self.present_value_elements()?, array_index)
380 }
381 p if p == PropertyIdentifier::GROUP_MEMBER_NAMES => common::read_array(
382 self.group_member_names
383 .iter()
384 .map(|n| PropertyValue::CharacterString(n.clone()))
385 .collect(),
386 array_index,
387 ),
388 p if p == PropertyIdentifier::EVENT_STATE => {
389 Ok(PropertyValue::Enumerated(EventState::NORMAL.to_raw()))
390 }
391 p if p == PropertyIdentifier::MEMBER_STATUS_FLAGS => Ok(PropertyValue::BitString {
392 unused_bits: 4,
393 data: vec![self.member_status_flags().bits() << 4],
394 }),
395 _ => Err(common::unknown_property_error()),
396 }
397 }
398
399 fn write_property(
400 &mut self,
401 property: PropertyIdentifier,
402 _array_index: Option<u32>,
403 value: PropertyValue,
404 _priority: Option<u8>,
405 ) -> Result<(), Error> {
406 if let Some(result) =
407 common::write_out_of_service(&mut self.out_of_service, property, &value)
408 {
409 return result;
410 }
411 if let Some(result) = common::write_description(&mut self.description, property, &value) {
412 return result;
413 }
414 Err(crate::common::unhandled_write_error(
415 self.property_metadata().as_ref(),
416 property,
417 _array_index,
418 ))
419 }
420
421 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
422 metadata::for_global_group_object(self)
423 }
424
425 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
426 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
427 }
428}
429
430pub struct StructuredViewObject {
441 oid: ObjectIdentifier,
442 name: String,
443 description: String,
444 pub node_type: u32,
446 pub node_subtype: String,
448 subordinate_list: Vec<BACnetDeviceObjectReference>,
452 subordinate_annotations: Vec<String>,
454}
455
456impl StructuredViewObject {
457 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
459 let oid = ObjectIdentifier::new(ObjectType::STRUCTURED_VIEW, instance)?;
460 Ok(Self {
461 oid,
462 name: name.into(),
463 description: String::new(),
464 node_type: 0,
465 node_subtype: String::new(),
466 subordinate_list: Vec::new(),
467 subordinate_annotations: Vec::new(),
468 })
469 }
470
471 pub fn add_subordinate(
477 &mut self,
478 reference: impl Into<BACnetDeviceObjectReference>,
479 annotation: impl Into<String>,
480 ) -> Result<(), Error> {
481 let reference = reference.into();
482 device_reference::check_device_member(reference.device_identifier)?;
483 self.subordinate_list.push(reference);
484 self.subordinate_annotations.push(annotation.into());
485 Ok(())
486 }
487
488 pub fn set_subordinates(
495 &mut self,
496 subordinates: Vec<(BACnetDeviceObjectReference, String)>,
497 ) -> Result<(), Error> {
498 for (reference, _) in &subordinates {
499 device_reference::check_device_member(reference.device_identifier)?;
500 }
501 (self.subordinate_list, self.subordinate_annotations) = subordinates.into_iter().unzip();
502 Ok(())
503 }
504
505 pub fn subordinates(&self) -> impl Iterator<Item = (&BACnetDeviceObjectReference, &str)> {
507 self.subordinate_list
508 .iter()
509 .zip(self.subordinate_annotations.iter().map(String::as_str))
510 }
511}
512
513impl BACnetObject for StructuredViewObject {
514 fn object_identifier(&self) -> ObjectIdentifier {
515 self.oid
516 }
517
518 fn object_name(&self) -> &str {
519 &self.name
520 }
521
522 fn read_property(
523 &self,
524 property: PropertyIdentifier,
525 array_index: Option<u32>,
526 ) -> Result<PropertyValue, Error> {
527 if let Some(result) = read_identity_properties!(self, property, array_index) {
529 return result;
530 }
531 match property {
532 p if p == PropertyIdentifier::OBJECT_TYPE => Ok(PropertyValue::Enumerated(
533 ObjectType::STRUCTURED_VIEW.to_raw(),
534 )),
535 p if p == PropertyIdentifier::NODE_TYPE => {
536 Ok(PropertyValue::Enumerated(self.node_type))
537 }
538 p if p == PropertyIdentifier::NODE_SUBTYPE => {
539 Ok(PropertyValue::CharacterString(self.node_subtype.clone()))
540 }
541 p if p == PropertyIdentifier::SUBORDINATE_LIST => common::read_array(
542 device_reference::reference_elements(&self.subordinate_list),
543 array_index,
544 ),
545 p if p == PropertyIdentifier::SUBORDINATE_ANNOTATIONS => common::read_array(
546 self.subordinate_annotations
547 .iter()
548 .cloned()
549 .map(PropertyValue::CharacterString)
550 .collect(),
551 array_index,
552 ),
553 _ => Err(common::unknown_property_error()),
554 }
555 }
556
557 fn write_property(
558 &mut self,
559 property: PropertyIdentifier,
560 _array_index: Option<u32>,
561 value: PropertyValue,
562 _priority: Option<u8>,
563 ) -> Result<(), Error> {
564 if let Some(result) = common::write_description(&mut self.description, property, &value) {
565 return result;
566 }
567 Err(crate::common::unhandled_write_error(
568 self.property_metadata().as_ref(),
569 property,
570 _array_index,
571 ))
572 }
573
574 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
575 metadata::for_structured_view_object(self)
576 }
577
578 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
579 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
580 }
581}
582
583#[cfg(test)]
588mod array_tests;
589
590#[cfg(test)]
591mod group_members_tests;
592
593#[cfg(test)]
594mod member_status_flags_tests;
595
596#[cfg(test)]
597mod tests;