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