1use bacnet_encoding::primitives;
9use bacnet_encoding::tags;
10use bacnet_types::enums::{EnableDisable, ReinitializedState};
11use bacnet_types::error::Error;
12use bacnet_types::primitives::{Date, Time};
13use bytes::BytesMut;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct DeviceCommunicationControlRequest {
22 pub time_duration: Option<u16>,
23 pub enable_disable: EnableDisable,
24 pub password: Option<String>,
25}
26
27impl DeviceCommunicationControlRequest {
28 pub fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
29 if let Some(dur) = self.time_duration {
31 primitives::encode_ctx_unsigned(buf, 0, dur as u64);
32 }
33 primitives::encode_ctx_enumerated(buf, 1, self.enable_disable.to_raw());
35 if let Some(ref pw) = self.password {
37 primitives::encode_ctx_character_string(buf, 2, pw)?;
38 }
39 Ok(())
40 }
41
42 pub fn decode(data: &[u8]) -> Result<Self, Error> {
43 let mut offset = 0;
44
45 let mut time_duration = None;
47 let (opt_data, new_offset) = tags::decode_optional_context(data, offset, 0)?;
48 if let Some(content) = opt_data {
49 time_duration = Some(primitives::decode_unsigned(content)? as u16);
50 offset = new_offset;
51 }
52
53 let (tag, pos) = tags::decode_tag(data, offset)?;
55 let end = pos + tag.length as usize;
56 if end > data.len() {
57 return Err(Error::decoding(pos, "DCC truncated at enable-disable"));
58 }
59 let enable_disable =
60 EnableDisable::from_raw(primitives::decode_unsigned(&data[pos..end])? as u32);
61 offset = end;
62
63 let mut password = None;
65 if offset < data.len() {
66 let (opt_data, _new_offset) = tags::decode_optional_context(data, offset, 2)?;
67 if let Some(content) = opt_data {
68 let s = primitives::decode_character_string(content)?;
69 password = Some(s);
70 }
71 }
72
73 Ok(Self {
74 time_duration,
75 enable_disable,
76 password,
77 })
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct ReinitializeDeviceRequest {
88 pub reinitialized_state: ReinitializedState,
89 pub password: Option<String>,
90}
91
92impl ReinitializeDeviceRequest {
93 pub fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
94 primitives::encode_ctx_enumerated(buf, 0, self.reinitialized_state.to_raw());
96 if let Some(ref pw) = self.password {
98 primitives::encode_ctx_character_string(buf, 1, pw)?;
99 }
100 Ok(())
101 }
102
103 pub fn decode(data: &[u8]) -> Result<Self, Error> {
104 let mut offset = 0;
105
106 let (tag, pos) = tags::decode_tag(data, offset)?;
108 let end = pos + tag.length as usize;
109 if end > data.len() {
110 return Err(Error::decoding(pos, "Reinitialize truncated at state"));
111 }
112 let reinitialized_state =
113 ReinitializedState::from_raw(primitives::decode_unsigned(&data[pos..end])? as u32);
114 offset = end;
115
116 let mut password = None;
118 if offset < data.len() {
119 let (opt_data, _new_offset) = tags::decode_optional_context(data, offset, 1)?;
120 if let Some(content) = opt_data {
121 let s = primitives::decode_character_string(content)?;
122 password = Some(s);
123 }
124 }
125
126 Ok(Self {
127 reinitialized_state,
128 password,
129 })
130 }
131}
132
133#[derive(Debug, Clone, PartialEq, Eq)]
141pub struct TimeSynchronizationRequest {
142 pub date: Date,
143 pub time: Time,
144}
145
146impl TimeSynchronizationRequest {
147 pub fn encode(&self, buf: &mut BytesMut) {
148 primitives::encode_app_date(buf, &self.date);
149 primitives::encode_app_time(buf, &self.time);
150 }
151
152 pub fn decode(data: &[u8]) -> Result<Self, Error> {
153 let mut offset = 0;
154
155 let (tag, pos) = tags::decode_tag(data, offset)?;
157 let end = pos + tag.length as usize;
158 if end > data.len() {
159 return Err(Error::decoding(pos, "TimeSync truncated at date"));
160 }
161 let date = Date::decode(&data[pos..end])?;
162 offset = end;
163
164 let (tag, pos) = tags::decode_tag(data, offset)?;
166 let end = pos + tag.length as usize;
167 if end > data.len() {
168 return Err(Error::decoding(pos, "TimeSync truncated at time"));
169 }
170 let time = Time::decode(&data[pos..end])?;
171
172 Ok(Self { date, time })
173 }
174}
175
176#[cfg(test)]
177mod tests {
178 use super::*;
179
180 #[test]
181 fn dcc_round_trip() {
182 let req = DeviceCommunicationControlRequest {
183 time_duration: Some(60),
184 enable_disable: EnableDisable::DISABLE,
185 password: Some("secret".into()),
186 };
187 let mut buf = BytesMut::new();
188 req.encode(&mut buf).unwrap();
189 let decoded = DeviceCommunicationControlRequest::decode(&buf).unwrap();
190 assert_eq!(req, decoded);
191 }
192
193 #[test]
194 fn dcc_no_optionals() {
195 let req = DeviceCommunicationControlRequest {
196 time_duration: None,
197 enable_disable: EnableDisable::ENABLE,
198 password: None,
199 };
200 let mut buf = BytesMut::new();
201 req.encode(&mut buf).unwrap();
202 let decoded = DeviceCommunicationControlRequest::decode(&buf).unwrap();
203 assert_eq!(req, decoded);
204 }
205
206 #[test]
207 fn reinitialize_round_trip() {
208 let req = ReinitializeDeviceRequest {
209 reinitialized_state: ReinitializedState::WARMSTART,
210 password: Some("admin".into()),
211 };
212 let mut buf = BytesMut::new();
213 req.encode(&mut buf).unwrap();
214 let decoded = ReinitializeDeviceRequest::decode(&buf).unwrap();
215 assert_eq!(req, decoded);
216 }
217
218 #[test]
219 fn time_sync_round_trip() {
220 let req = TimeSynchronizationRequest {
221 date: Date {
222 year: 124,
223 month: 6,
224 day: 15,
225 day_of_week: 6,
226 },
227 time: Time {
228 hour: 14,
229 minute: 30,
230 second: 0,
231 hundredths: 0,
232 },
233 };
234 let mut buf = BytesMut::new();
235 req.encode(&mut buf);
236 let decoded = TimeSynchronizationRequest::decode(&buf).unwrap();
237 assert_eq!(req, decoded);
238 }
239
240 #[test]
245 fn test_decode_dcc_empty_input() {
246 assert!(DeviceCommunicationControlRequest::decode(&[]).is_err());
247 }
248
249 #[test]
250 fn test_decode_dcc_truncated_1_byte() {
251 let req = DeviceCommunicationControlRequest {
252 time_duration: Some(60),
253 enable_disable: EnableDisable::DISABLE,
254 password: Some("secret".into()),
255 };
256 let mut buf = BytesMut::new();
257 req.encode(&mut buf).unwrap();
258 assert!(DeviceCommunicationControlRequest::decode(&buf[..1]).is_err());
259 }
260
261 #[test]
262 fn test_decode_dcc_truncated_3_bytes() {
263 let req = DeviceCommunicationControlRequest {
264 time_duration: Some(60),
265 enable_disable: EnableDisable::DISABLE,
266 password: Some("secret".into()),
267 };
268 let mut buf = BytesMut::new();
269 req.encode(&mut buf).unwrap();
270 assert!(DeviceCommunicationControlRequest::decode(&buf[..3]).is_err());
271 }
272
273 #[test]
274 fn test_decode_dcc_invalid_tag() {
275 assert!(DeviceCommunicationControlRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
276 }
277
278 #[test]
279 fn test_decode_reinitialize_empty_input() {
280 assert!(ReinitializeDeviceRequest::decode(&[]).is_err());
281 }
282
283 #[test]
284 fn test_decode_reinitialize_truncated_1_byte() {
285 let req = ReinitializeDeviceRequest {
286 reinitialized_state: ReinitializedState::WARMSTART,
287 password: Some("admin".into()),
288 };
289 let mut buf = BytesMut::new();
290 req.encode(&mut buf).unwrap();
291 assert!(ReinitializeDeviceRequest::decode(&buf[..1]).is_err());
292 }
293
294 #[test]
295 fn test_decode_reinitialize_invalid_tag() {
296 assert!(ReinitializeDeviceRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
297 }
298
299 #[test]
300 fn test_decode_time_sync_empty_input() {
301 assert!(TimeSynchronizationRequest::decode(&[]).is_err());
302 }
303
304 #[test]
305 fn test_decode_time_sync_truncated_1_byte() {
306 let req = TimeSynchronizationRequest {
307 date: Date {
308 year: 124,
309 month: 6,
310 day: 15,
311 day_of_week: 6,
312 },
313 time: Time {
314 hour: 14,
315 minute: 30,
316 second: 0,
317 hundredths: 0,
318 },
319 };
320 let mut buf = BytesMut::new();
321 req.encode(&mut buf);
322 assert!(TimeSynchronizationRequest::decode(&buf[..1]).is_err());
323 }
324
325 #[test]
326 fn test_decode_time_sync_truncated_3_bytes() {
327 let req = TimeSynchronizationRequest {
328 date: Date {
329 year: 124,
330 month: 6,
331 day: 15,
332 day_of_week: 6,
333 },
334 time: Time {
335 hour: 14,
336 minute: 30,
337 second: 0,
338 hundredths: 0,
339 },
340 };
341 let mut buf = BytesMut::new();
342 req.encode(&mut buf);
343 assert!(TimeSynchronizationRequest::decode(&buf[..3]).is_err());
344 }
345
346 #[test]
347 fn test_decode_time_sync_truncated_half() {
348 let req = TimeSynchronizationRequest {
349 date: Date {
350 year: 124,
351 month: 6,
352 day: 15,
353 day_of_week: 6,
354 },
355 time: Time {
356 hour: 14,
357 minute: 30,
358 second: 0,
359 hundredths: 0,
360 },
361 };
362 let mut buf = BytesMut::new();
363 req.encode(&mut buf);
364 let half = buf.len() / 2;
365 assert!(TimeSynchronizationRequest::decode(&buf[..half]).is_err());
366 }
367
368 #[test]
369 fn test_decode_time_sync_invalid_tag() {
370 assert!(TimeSynchronizationRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
371 }
372}