1use super::*;
2
3fn validate_atomic_read_file_ack(
4 request: &bacnet_services::file::FileAccessMethod,
5 ack: &bacnet_services::file::AtomicReadFileAck,
6) -> Result<(), Error> {
7 use bacnet_services::file::{FileAccessMethod, FileReadAckMethod};
8
9 match (request, &ack.access) {
10 (
11 FileAccessMethod::Stream {
12 requested_octet_count,
13 ..
14 },
15 FileReadAckMethod::Stream { file_data, .. },
16 ) if file_data.len() <= *requested_octet_count as usize => Ok(()),
17 (FileAccessMethod::Stream { .. }, FileReadAckMethod::Stream { .. }) => Err(
18 Error::decoding(0, "AtomicReadFile ACK exceeds the requested octet window"),
19 ),
20 (
21 FileAccessMethod::Record {
22 requested_record_count,
23 ..
24 },
25 FileReadAckMethod::Record {
26 returned_record_count,
27 file_record_data,
28 ..
29 },
30 ) if returned_record_count <= requested_record_count
31 && *returned_record_count as usize == file_record_data.len() =>
32 {
33 Ok(())
34 }
35 (FileAccessMethod::Record { .. }, FileReadAckMethod::Record { .. }) => Err(
36 Error::decoding(0, "AtomicReadFile ACK exceeds the requested record window"),
37 ),
38 _ => Err(Error::decoding(
39 0,
40 "AtomicReadFile ACK access method does not match the request",
41 )),
42 }
43}
44
45impl<T: TransportPort + 'static> BACnetClient<T> {
46 pub async fn acknowledge_alarm_request(
48 &self,
49 destination_mac: &[u8],
50 request: &bacnet_services::alarm_event::AcknowledgeAlarmRequest,
51 ) -> Result<(), Error> {
52 let mut buf = BytesMut::new();
53 request.encode(&mut buf)?;
54
55 let _ = self
56 .confirmed_request(
57 destination_mac,
58 ConfirmedServiceChoice::ACKNOWLEDGE_ALARM,
59 &buf,
60 )
61 .await?;
62
63 Ok(())
64 }
65
66 pub async fn acknowledge_alarm_request_from_device(
71 &self,
72 device_instance: u32,
73 request: &bacnet_services::alarm_event::AcknowledgeAlarmRequest,
74 ) -> Result<(), Error> {
75 let (mac, routing) = self.resolve_device(device_instance).await?;
76 if let Some((network, address)) = routing {
77 let mut buf = BytesMut::new();
78 request.encode(&mut buf)?;
79 self.confirmed_request_routed(
80 &mac,
81 network,
82 &address,
83 ConfirmedServiceChoice::ACKNOWLEDGE_ALARM,
84 &buf,
85 )
86 .await?;
87 Ok(())
88 } else {
89 self.acknowledge_alarm_request(&mac, request).await
90 }
91 }
92
93 #[deprecated(
100 note = "use acknowledge_alarm_request with caller-supplied correlation timestamps"
101 )]
102 pub async fn acknowledge_alarm(
103 &self,
104 destination_mac: &[u8],
105 acknowledging_process_identifier: u32,
106 event_object_identifier: bacnet_types::primitives::ObjectIdentifier,
107 event_state_acknowledged: bacnet_types::enums::EventState,
108 acknowledgment_source: &str,
109 ) -> Result<(), Error> {
110 use bacnet_services::alarm_event::AcknowledgeAlarmRequest;
111
112 let request = AcknowledgeAlarmRequest {
113 acknowledging_process_identifier,
114 event_object_identifier,
115 event_state_acknowledged,
116 timestamp: bacnet_types::primitives::BACnetTimeStamp::SequenceNumber(0),
117 acknowledgment_source: acknowledgment_source.to_string(),
118 time_of_acknowledgment: bacnet_types::primitives::BACnetTimeStamp::SequenceNumber(0),
119 };
120 self.acknowledge_alarm_request(destination_mac, &request)
121 .await
122 }
123
124 pub async fn atomic_read_file(
126 &self,
127 destination_mac: &[u8],
128 file_identifier: bacnet_types::primitives::ObjectIdentifier,
129 access: bacnet_services::file::FileAccessMethod,
130 ) -> Result<Bytes, Error> {
131 use bacnet_services::file::AtomicReadFileRequest;
132
133 let request = AtomicReadFileRequest {
134 file_identifier,
135 access,
136 };
137 let mut buf = BytesMut::new();
138 request.encode(&mut buf);
139
140 self.confirmed_request(
141 destination_mac,
142 ConfirmedServiceChoice::ATOMIC_READ_FILE,
143 &buf,
144 )
145 .await
146 }
147
148 pub async fn atomic_read_file_decoded(
155 &self,
156 destination_mac: &[u8],
157 file_identifier: bacnet_types::primitives::ObjectIdentifier,
158 access: bacnet_services::file::FileAccessMethod,
159 ) -> Result<bacnet_services::file::AtomicReadFileAck, Error> {
160 let requested_access = access.clone();
161 let response = self
162 .atomic_read_file(destination_mac, file_identifier, access)
163 .await?;
164 let ack = bacnet_services::file::AtomicReadFileAck::decode(&response)?;
165 validate_atomic_read_file_ack(&requested_access, &ack)?;
166 Ok(ack)
167 }
168
169 pub async fn atomic_read_file_from_device(
175 &self,
176 device_instance: u32,
177 file_identifier: bacnet_types::primitives::ObjectIdentifier,
178 access: bacnet_services::file::FileAccessMethod,
179 ) -> Result<Bytes, Error> {
180 let (mac, routing) = self.resolve_device(device_instance).await?;
181 if let Some((network, address)) = routing {
182 let request = bacnet_services::file::AtomicReadFileRequest {
183 file_identifier,
184 access,
185 };
186 let mut buf = BytesMut::new();
187 request.encode(&mut buf);
188 self.confirmed_request_routed(
189 &mac,
190 network,
191 &address,
192 ConfirmedServiceChoice::ATOMIC_READ_FILE,
193 &buf,
194 )
195 .await
196 } else {
197 self.atomic_read_file(&mac, file_identifier, access).await
198 }
199 }
200
201 pub async fn atomic_read_file_decoded_from_device(
207 &self,
208 device_instance: u32,
209 file_identifier: bacnet_types::primitives::ObjectIdentifier,
210 access: bacnet_services::file::FileAccessMethod,
211 ) -> Result<bacnet_services::file::AtomicReadFileAck, Error> {
212 let requested_access = access.clone();
213 let response = self
214 .atomic_read_file_from_device(device_instance, file_identifier, access)
215 .await?;
216 let ack = bacnet_services::file::AtomicReadFileAck::decode(&response)?;
217 validate_atomic_read_file_ack(&requested_access, &ack)?;
218 Ok(ack)
219 }
220
221 pub async fn atomic_write_file(
223 &self,
224 destination_mac: &[u8],
225 file_identifier: bacnet_types::primitives::ObjectIdentifier,
226 access: bacnet_services::file::FileWriteAccessMethod,
227 ) -> Result<Bytes, Error> {
228 use bacnet_services::file::AtomicWriteFileRequest;
229
230 let request = AtomicWriteFileRequest {
231 file_identifier,
232 access,
233 };
234 let mut buf = BytesMut::new();
235 request.encode(&mut buf);
236
237 self.confirmed_request(
238 destination_mac,
239 ConfirmedServiceChoice::ATOMIC_WRITE_FILE,
240 &buf,
241 )
242 .await
243 }
244
245 pub async fn atomic_write_file_from_device(
250 &self,
251 device_instance: u32,
252 file_identifier: bacnet_types::primitives::ObjectIdentifier,
253 access: bacnet_services::file::FileWriteAccessMethod,
254 ) -> Result<Bytes, Error> {
255 let (mac, routing) = self.resolve_device(device_instance).await?;
256 if let Some((network, address)) = routing {
257 let request = bacnet_services::file::AtomicWriteFileRequest {
258 file_identifier,
259 access,
260 };
261 let mut buf = BytesMut::new();
262 request.encode(&mut buf);
263 self.confirmed_request_routed(
264 &mac,
265 network,
266 &address,
267 ConfirmedServiceChoice::ATOMIC_WRITE_FILE,
268 &buf,
269 )
270 .await
271 } else {
272 self.atomic_write_file(&mac, file_identifier, access).await
273 }
274 }
275
276 pub async fn add_list_element(
281 &self,
282 destination_mac: &[u8],
283 object_identifier: bacnet_types::primitives::ObjectIdentifier,
284 property_identifier: bacnet_types::enums::PropertyIdentifier,
285 property_array_index: Option<u32>,
286 list_of_elements: Vec<u8>,
287 ) -> Result<(), Error> {
288 use bacnet_services::list_manipulation::ListElementRequest;
289
290 let request = ListElementRequest {
291 object_identifier,
292 property_identifier,
293 property_array_index,
294 list_of_elements,
295 };
296 let mut buf = BytesMut::new();
297 request.encode(&mut buf)?;
298
299 let _ = self
300 .confirmed_request(
301 destination_mac,
302 ConfirmedServiceChoice::ADD_LIST_ELEMENT,
303 &buf,
304 )
305 .await?;
306
307 Ok(())
308 }
309
310 pub async fn remove_list_element(
315 &self,
316 destination_mac: &[u8],
317 object_identifier: bacnet_types::primitives::ObjectIdentifier,
318 property_identifier: bacnet_types::enums::PropertyIdentifier,
319 property_array_index: Option<u32>,
320 list_of_elements: Vec<u8>,
321 ) -> Result<(), Error> {
322 use bacnet_services::list_manipulation::ListElementRequest;
323
324 let request = ListElementRequest {
325 object_identifier,
326 property_identifier,
327 property_array_index,
328 list_of_elements,
329 };
330 let mut buf = BytesMut::new();
331 request.encode(&mut buf)?;
332
333 let _ = self
334 .confirmed_request(
335 destination_mac,
336 ConfirmedServiceChoice::REMOVE_LIST_ELEMENT,
337 &buf,
338 )
339 .await?;
340
341 Ok(())
342 }
343}
344
345#[cfg(test)]
346#[path = "file_list_decoded_tests.rs"]
347mod decoded_tests;
348
349#[cfg(test)]
350#[path = "routed_file_tests.rs"]
351mod routed_tests;