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