1use crate::{BacnetClient, ClientDataValue, ClientError};
5use rustbac_core::types::{ObjectId, ObjectType, PropertyId};
6use rustbac_datalink::{DataLink, DataLinkAddress};
7
8#[derive(Debug, Clone)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub struct ObjectSummary {
12 pub object_id: ObjectId,
13 pub object_name: Option<String>,
14 pub object_type: ObjectType,
15 pub present_value: Option<ClientDataValue>,
16 pub description: Option<String>,
17 pub units: Option<u32>,
18 pub status_flags: Option<ClientDataValue>,
19}
20
21#[derive(Debug, Clone)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct DeviceWalkResult {
25 pub device_id: ObjectId,
26 pub objects: Vec<ObjectSummary>,
27}
28
29pub async fn walk_device<D: DataLink>(
32 client: &BacnetClient<D>,
33 addr: DataLinkAddress,
34 device_id: ObjectId,
35) -> Result<DeviceWalkResult, ClientError> {
36 let object_list_value = client
38 .read_property(addr, device_id, PropertyId::ObjectList)
39 .await?;
40
41 let object_ids = extract_object_ids(&object_list_value);
42
43 let properties = &[
45 PropertyId::ObjectName,
46 PropertyId::ObjectType,
47 PropertyId::PresentValue,
48 PropertyId::Description,
49 PropertyId::Units,
50 PropertyId::StatusFlags,
51 ];
52
53 let mut objects = Vec::with_capacity(object_ids.len());
54 for &oid in &object_ids {
55 let props = client.read_property_multiple(addr, oid, properties).await;
56
57 let summary = match props {
58 Ok(prop_values) => build_summary(oid, &prop_values),
59 Err(_) => ObjectSummary {
60 object_id: oid,
61 object_name: None,
62 object_type: oid.object_type(),
63 present_value: None,
64 description: None,
65 units: None,
66 status_flags: None,
67 },
68 };
69 objects.push(summary);
70 }
71
72 Ok(DeviceWalkResult { device_id, objects })
73}
74
75fn extract_object_ids(value: &ClientDataValue) -> Vec<ObjectId> {
76 match value {
77 ClientDataValue::ObjectId(oid) => vec![*oid],
78 ClientDataValue::Constructed { values, .. } => values
79 .iter()
80 .filter_map(|v| {
81 if let ClientDataValue::ObjectId(oid) = v {
82 Some(*oid)
83 } else {
84 None
85 }
86 })
87 .collect(),
88 _ => vec![],
89 }
90}
91
92fn build_summary(oid: ObjectId, props: &[(PropertyId, ClientDataValue)]) -> ObjectSummary {
93 let mut summary = ObjectSummary {
94 object_id: oid,
95 object_name: None,
96 object_type: oid.object_type(),
97 present_value: None,
98 description: None,
99 units: None,
100 status_flags: None,
101 };
102
103 for (pid, val) in props {
104 match pid {
105 PropertyId::ObjectName => {
106 if let ClientDataValue::CharacterString(s) = val {
107 summary.object_name = Some(s.clone());
108 }
109 }
110 PropertyId::ObjectType => {
111 if let ClientDataValue::Enumerated(v) = val {
112 summary.object_type = ObjectType::from_u16(*v as u16);
113 }
114 }
115 PropertyId::PresentValue => {
116 summary.present_value = Some(val.clone());
117 }
118 PropertyId::Description => {
119 if let ClientDataValue::CharacterString(s) = val {
120 summary.description = Some(s.clone());
121 }
122 }
123 PropertyId::Units => {
124 if let ClientDataValue::Enumerated(v) = val {
125 summary.units = Some(*v);
126 }
127 }
128 PropertyId::StatusFlags => {
129 summary.status_flags = Some(val.clone());
130 }
131 _ => {}
132 }
133 }
134
135 summary
136}