1mod bbmd;
20mod discover;
21mod log;
22mod read;
23
24use std::collections::HashSet;
25use std::sync::atomic::AtomicBool;
26use std::sync::{Arc, Mutex, PoisonError};
27
28use bacnet_client::client::BACnetClient;
29use bacnet_client::log_reader::{LogCursor, LogPage, read_log_page};
30use bacnet_services::alarm_event::GetEventInformationAck;
31use bacnet_services::alarm_summary::GetAlarmSummaryAck;
32use bacnet_services::enrollment_summary::{GetEnrollmentSummaryAck, GetEnrollmentSummaryRequest};
33use bacnet_transport::port::TransportPort;
34use bacnet_types::enums::{ObjectType, PropertyIdentifier};
35use bacnet_types::error::Error;
36use bacnet_types::primitives::ObjectIdentifier;
37
38pub use self::discover::WhoIsScope;
39pub use self::log::Naming;
40pub(crate) use self::read::rpm_rows;
41pub use self::read::{
42 Halt, ListRead, MAX_OBJECTS, ReadVia, Reader, RpmFailure, Wanted, network_of, rpm_failure,
43 timed_out, unreachable,
44};
45use crate::format::{RawRow, object_name};
46use crate::limits::{RequestLimits, Turn};
47
48pub const DEVICE_PROPERTIES: [PropertyIdentifier; 13] = [
51 PropertyIdentifier::OBJECT_NAME,
52 PropertyIdentifier::VENDOR_NAME,
53 PropertyIdentifier::VENDOR_IDENTIFIER,
54 PropertyIdentifier::MODEL_NAME,
55 PropertyIdentifier::FIRMWARE_REVISION,
56 PropertyIdentifier::APPLICATION_SOFTWARE_VERSION,
57 PropertyIdentifier::PROTOCOL_VERSION,
58 PropertyIdentifier::PROTOCOL_REVISION,
59 PropertyIdentifier::PROTOCOL_SERVICES_SUPPORTED,
60 PropertyIdentifier::PROTOCOL_OBJECT_TYPES_SUPPORTED,
61 PropertyIdentifier::MAX_APDU_LENGTH_ACCEPTED,
62 PropertyIdentifier::SEGMENTATION_SUPPORTED,
63 PropertyIdentifier::DATABASE_REVISION,
64];
65
66static NEVER_STOPPED: AtomicBool = AtomicBool::new(false);
69
70#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct PropertyRead {
73 pub object: ObjectIdentifier,
75 pub rows: Vec<RawRow>,
78 pub via: ReadVia,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq)]
84pub struct DeviceSummary {
85 pub device: u32,
87 pub rows: Vec<RawRow>,
89 pub via: ReadVia,
91}
92
93impl DeviceSummary {
94 pub fn name(&self) -> Option<String> {
98 object_name(&self.rows)
99 }
100}
101
102pub struct Engine<T: TransportPort + 'static> {
108 pub(crate) client: Arc<BACnetClient<T>>,
109 limits: Arc<RequestLimits>,
110 no_rpm: Mutex<HashSet<u32>>,
114}
115
116impl<T: TransportPort + 'static> Engine<T> {
117 pub fn new(client: Arc<BACnetClient<T>>, limits: Arc<RequestLimits>) -> Self {
120 Self {
121 client,
122 limits,
123 no_rpm: Mutex::new(HashSet::new()),
124 }
125 }
126
127 pub fn limits(&self) -> &RequestLimits {
129 &self.limits
130 }
131
132 pub fn client(&self) -> &Arc<BACnetClient<T>> {
134 &self.client
135 }
136
137 pub fn refused_rpm(&self, device: u32) -> bool {
139 self.no_rpm().contains(&device)
140 }
141
142 fn no_rpm(&self) -> std::sync::MutexGuard<'_, HashSet<u32>> {
143 self.no_rpm.lock().unwrap_or_else(PoisonError::into_inner)
144 }
145
146 pub(crate) fn reader(&self, device: u32) -> Reader<'_, T> {
148 Reader {
149 client: &self.client,
150 device,
151 rate: &self.limits,
152 cancel: &NEVER_STOPPED,
153 rpm: !self.refused_rpm(device),
154 }
155 }
156
157 pub(crate) fn settle(&self, reader: &Reader<'_, T>) {
159 if !reader.rpm {
160 self.no_rpm().insert(reader.device);
161 }
162 }
163
164 pub async fn read(
169 &self,
170 device: u32,
171 object: ObjectIdentifier,
172 wanted: Wanted<'_>,
173 ) -> Result<PropertyRead, Error> {
174 let mut reader = self.reader(device);
175 let read = reader.properties(object, wanted).await;
176 self.settle(&reader);
177 let (rows, via) = read.map_err(device_error)?;
178 Ok(PropertyRead { object, rows, via })
179 }
180
181 pub async fn read_property(
185 &self,
186 device: u32,
187 object: ObjectIdentifier,
188 property: PropertyIdentifier,
189 index: Option<u32>,
190 ) -> Result<Vec<u8>, Error> {
191 self.reader(device)
192 .rp(object, property, index)
193 .await
194 .map_err(device_error)
195 }
196
197 pub async fn list_objects(&self, device: u32) -> Result<ListRead, Error> {
200 self.reader(device)
201 .object_list(|_, _| {})
202 .await
203 .map_err(device_error)
204 }
205
206 pub async fn describe_device(&self, device: u32) -> Result<DeviceSummary, Error> {
208 let object = ObjectIdentifier::new(ObjectType::DEVICE, device)?;
209 let PropertyRead { rows, via, .. } = self
210 .read(device, object, Wanted::List(&DEVICE_PROPERTIES))
211 .await?;
212 Ok(DeviceSummary { device, rows, via })
213 }
214
215 pub async fn read_trend(
223 &self,
224 device: u32,
225 log: ObjectIdentifier,
226 cursor: LogCursor,
227 page_size: u16,
228 ) -> Result<LogPage, Error> {
229 let network = network_of(&self.client, &self.limits, device).await;
230 let _turn = self.limits.turn_on(device, network).await;
231 let requester = log::PagedLog {
232 client: &self.client,
233 limits: &self.limits,
234 device,
235 network,
236 first: AtomicBool::new(true),
237 };
238 read_log_page(&requester, &[], log, cursor, page_size).await
240 }
241
242 async fn turn(&self, device: u32) -> Turn {
244 let network = network_of(&self.client, &self.limits, device).await;
245 self.limits.turn_on(device, network).await
246 }
247
248 pub async fn alarms(
253 &self,
254 device: u32,
255 after: Option<ObjectIdentifier>,
256 ) -> Result<GetEventInformationAck, Error> {
257 let _turn = self.turn(device).await;
258 self.client
259 .get_event_information_from_device(device, after)
260 .await
261 }
262
263 pub async fn alarm_summary(&self, device: u32) -> Result<GetAlarmSummaryAck, Error> {
266 let _turn = self.turn(device).await;
267 self.client.get_alarm_summary_from_device(device).await
268 }
269
270 pub async fn enrollment_summary(
272 &self,
273 device: u32,
274 request: &GetEnrollmentSummaryRequest,
275 ) -> Result<GetEnrollmentSummaryAck, Error> {
276 let _turn = self.turn(device).await;
277 self.client
278 .get_enrollment_summary_from_device(device, request)
279 .await
280 }
281}
282
283fn device_error(halt: Halt) -> Error {
285 match halt {
286 Halt::Device(error) => error,
287 Halt::Cancelled => unreachable!("an engine read is never stopped by its flag"),
290 }
291}
292
293#[cfg(test)]
294mod tests;