1#[cfg(not(feature = "std"))]
7use alloc::vec::Vec;
8
9use bacnet_encoding::constructed::tagged::{
10 decode_app_object_id, decode_ctx_object_id, decode_ctx_unsigned, expect_closing, expect_end,
11 expect_opening, misplaced_tag, next_is_context, next_is_opening,
12};
13use bacnet_encoding::constructed::{
14 decode_bacnet_property_value_in_list, encode_bacnet_property_value,
15};
16use bacnet_encoding::primitives;
17use bacnet_encoding::tags;
18use bacnet_types::enums::ObjectType;
19use bacnet_types::error::Error;
20use bacnet_types::primitives::ObjectIdentifier;
21use bytes::BytesMut;
22
23use crate::common::{BACnetPropertyValue, MAX_DECODED_ITEMS};
24
25mod error;
26pub use error::CreateObjectError;
27
28#[derive(Debug, Clone, PartialEq, Eq)]
34pub enum ObjectSpecifier {
35 Type(ObjectType),
37 Identifier(ObjectIdentifier),
39}
40
41#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct CreateObjectRequest {
44 pub object_specifier: ObjectSpecifier,
47 pub list_of_initial_values: Vec<BACnetPropertyValue>,
49}
50
51impl CreateObjectRequest {
52 pub fn encode(&self, buf: &mut BytesMut) {
54 tags::encode_opening_tag(buf, 0);
56 match &self.object_specifier {
57 ObjectSpecifier::Type(obj_type) => {
58 primitives::encode_ctx_enumerated(buf, 0, obj_type.to_raw());
59 }
60 ObjectSpecifier::Identifier(oid) => {
61 primitives::encode_ctx_object_id(buf, 1, oid);
62 }
63 }
64 tags::encode_closing_tag(buf, 0);
65
66 if !self.list_of_initial_values.is_empty() {
68 tags::encode_opening_tag(buf, 1);
69 for pv in &self.list_of_initial_values {
70 encode_bacnet_property_value(pv, buf);
71 }
72 tags::encode_closing_tag(buf, 1);
73 }
74 }
75
76 pub fn decode(data: &[u8]) -> Result<Self, Error> {
78 let what = "CreateObject object-specifier";
81 let offset = expect_opening(data, 0, 0, what)?;
82 let (object_specifier, offset) = if next_is_context(data, offset, 0)? {
83 let (raw, end) =
84 decode_ctx_unsigned::<u32>(data, offset, 0, "CreateObject object-type")?;
85 (ObjectSpecifier::Type(ObjectType::from_raw(raw)), end)
86 } else if next_is_context(data, offset, 1)? {
87 let (oid, end) =
88 decode_ctx_object_id(data, offset, 1, "CreateObject object-identifier")?;
89 (ObjectSpecifier::Identifier(oid), end)
90 } else {
91 let (found, _) = tags::decode_tag(data, offset)?;
94 return Err(misplaced_tag(
95 data,
96 &found,
97 None,
98 offset,
99 "CreateObject expected context tag 0 or 1 inside object-specifier",
100 ));
101 };
102 let mut offset = expect_closing(data, offset, 0, what)?;
103
104 let mut values = Vec::new();
106 if next_is_opening(data, offset, 1)? {
107 offset = expect_opening(data, offset, 1, "CreateObject list-of-initial-values")?;
108 loop {
109 if offset >= data.len() {
110 return Err(Error::missing(offset, "CreateObject missing closing tag 1"));
111 }
112 let (tag, closing_end) = tags::decode_tag(data, offset)?;
113 if tag.is_closing_tag(1) {
114 offset = closing_end;
115 break;
116 }
117 if values.len() >= MAX_DECODED_ITEMS {
118 return Err(Error::overflow(offset, "CreateObject values exceeds max"));
119 }
120 let (pv, new_offset) = decode_bacnet_property_value_in_list(data, offset, 1)?;
121 values.push(pv);
122 offset = new_offset;
123 }
124 }
125
126 expect_end(data, offset, offset, "CreateObject")?;
127
128 Ok(Self {
129 object_specifier,
130 list_of_initial_values: values,
131 })
132 }
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143pub struct CreateObjectAck {
144 pub object_identifier: ObjectIdentifier,
146}
147
148impl CreateObjectAck {
149 pub fn encode(&self, buf: &mut BytesMut) {
151 primitives::encode_app_object_id(buf, &self.object_identifier);
152 }
153
154 pub fn decode(data: &[u8]) -> Result<Self, Error> {
157 let (object_identifier, end) =
158 decode_app_object_id(data, 0, "CreateObject-ACK object-identifier")?;
159 expect_end(data, end, end, "CreateObject-ACK")?;
160 Ok(Self { object_identifier })
161 }
162}
163
164#[derive(Debug, Clone, PartialEq, Eq)]
172pub struct DeleteObjectRequest {
173 pub object_identifier: ObjectIdentifier,
175}
176
177impl DeleteObjectRequest {
178 pub fn encode(&self, buf: &mut BytesMut) {
180 primitives::encode_app_object_id(buf, &self.object_identifier);
181 }
182
183 pub fn decode(data: &[u8]) -> Result<Self, Error> {
186 let (object_identifier, end) =
187 decode_app_object_id(data, 0, "DeleteObject object-identifier")?;
188 expect_end(data, end, end, "DeleteObject")?;
189 Ok(Self { object_identifier })
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use bacnet_types::enums::{ObjectType, PropertyIdentifier};
197
198 fn create_object_type_bytes(object_type: &[u8]) -> BytesMut {
199 let mut buf = BytesMut::new();
200 tags::encode_opening_tag(&mut buf, 0);
201 primitives::encode_ctx_octet_string(&mut buf, 0, object_type);
202 tags::encode_closing_tag(&mut buf, 0);
203 buf
204 }
205
206 #[test]
207 fn create_object_by_type_round_trip() {
208 let req = CreateObjectRequest {
209 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
210 list_of_initial_values: vec![],
211 };
212 let mut buf = BytesMut::new();
213 req.encode(&mut buf);
214 let decoded = CreateObjectRequest::decode(&buf).unwrap();
215 assert_eq!(req, decoded);
216 }
217
218 #[test]
219 fn create_object_by_id_with_values() {
220 let req = CreateObjectRequest {
221 object_specifier: ObjectSpecifier::Identifier(
222 ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
223 ),
224 list_of_initial_values: vec![BACnetPropertyValue {
225 property_identifier: PropertyIdentifier::OBJECT_NAME,
226 property_array_index: None,
227 value: vec![0x75, 0x06, 0x00, 0x5A, 0x6F, 0x6E, 0x65, 0x31],
228 priority: None,
229 }],
230 };
231 let mut buf = BytesMut::new();
232 req.encode(&mut buf);
233 let decoded = CreateObjectRequest::decode(&buf).unwrap();
234 assert_eq!(req, decoded);
235 }
236
237 #[test]
238 fn create_object_type_must_fit_u32() {
239 let max_with_leading_zero = [0, 0xFF, 0xFF, 0xFF, 0xFF];
240 let decoded =
241 CreateObjectRequest::decode(&create_object_type_bytes(&max_with_leading_zero)).unwrap();
242 assert_eq!(
243 decoded.object_specifier,
244 ObjectSpecifier::Type(ObjectType::from_raw(u32::MAX))
245 );
246
247 for overflow in [u32::MAX as u64 + 1, u64::MAX] {
248 assert!(CreateObjectRequest::decode(&create_object_type_bytes(
249 &overflow.to_be_bytes(),
250 ))
251 .is_err());
252 }
253 }
254
255 #[test]
256 fn create_object_rejects_unexpected_or_trailing_fields() {
257 let encoded = create_object_type_bytes(&[0]);
258
259 let mut empty_values = encoded.clone();
260 tags::encode_opening_tag(&mut empty_values, 1);
261 tags::encode_closing_tag(&mut empty_values, 1);
262 assert!(CreateObjectRequest::decode(&empty_values).is_ok());
263
264 let mut unexpected = encoded.clone();
265 primitives::encode_ctx_unsigned(&mut unexpected, 1, 1);
266 assert!(CreateObjectRequest::decode(&unexpected).is_err());
267
268 let mut trailing = empty_values;
269 primitives::encode_app_null(&mut trailing);
270 assert!(CreateObjectRequest::decode(&trailing).is_err());
271 }
272
273 #[test]
274 fn create_object_accepts_exact_initial_value_limit() {
275 let value = BACnetPropertyValue {
276 property_identifier: PropertyIdentifier::PRESENT_VALUE,
277 property_array_index: None,
278 value: vec![0],
279 priority: None,
280 };
281 let request = CreateObjectRequest {
282 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
283 list_of_initial_values: vec![value.clone(); MAX_DECODED_ITEMS],
284 };
285 let mut encoded = BytesMut::new();
286 request.encode(&mut encoded);
287 assert_eq!(
288 CreateObjectRequest::decode(&encoded)
289 .unwrap()
290 .list_of_initial_values
291 .len(),
292 MAX_DECODED_ITEMS
293 );
294
295 let request = CreateObjectRequest {
296 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
297 list_of_initial_values: vec![value; MAX_DECODED_ITEMS + 1],
298 };
299 encoded.clear();
300 request.encode(&mut encoded);
301 assert!(CreateObjectRequest::decode(&encoded).is_err());
302 }
303
304 #[test]
305 fn create_object_ack_round_trip_is_one_application_object_id() {
306 let ack = CreateObjectAck {
307 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_VALUE, 7).unwrap(),
308 };
309 let mut buf = BytesMut::new();
310 ack.encode(&mut buf);
311 assert_eq!(&buf[..], &[0xC4, 0x00, 0x80, 0x00, 0x07]);
313 assert_eq!(CreateObjectAck::decode(&buf).unwrap(), ack);
314 }
315
316 #[test]
317 fn create_object_ack_rejects_malformed_input() {
318 let ack = CreateObjectAck {
319 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_VALUE, 7).unwrap(),
320 };
321 let mut buf = BytesMut::new();
322 ack.encode(&mut buf);
323 assert!(CreateObjectAck::decode(&[]).is_err());
324 for cut in 1..buf.len() {
325 assert!(CreateObjectAck::decode(&buf[..cut]).is_err(), "cut {cut}");
326 }
327 let mut trailing = buf.to_vec();
329 trailing.push(0x00);
330 assert!(CreateObjectAck::decode(&trailing).is_err());
331 let mut context = BytesMut::new();
333 primitives::encode_ctx_object_id(&mut context, 0, &ack.object_identifier);
334 assert!(CreateObjectAck::decode(&context).is_err());
335 assert!(CreateObjectAck::decode(&[0x21, 0x07]).is_err());
337 }
338
339 #[test]
340 fn delete_object_round_trip() {
341 let req = DeleteObjectRequest {
342 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
343 };
344 let mut buf = BytesMut::new();
345 req.encode(&mut buf);
346 let decoded = DeleteObjectRequest::decode(&buf).unwrap();
347 assert_eq!(req, decoded);
348 }
349
350 #[test]
355 fn test_decode_create_object_empty_input() {
356 assert!(CreateObjectRequest::decode(&[]).is_err());
357 }
358
359 #[test]
360 fn test_decode_create_object_truncated_1_byte() {
361 let req = CreateObjectRequest {
362 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
363 list_of_initial_values: vec![],
364 };
365 let mut buf = BytesMut::new();
366 req.encode(&mut buf);
367 assert!(CreateObjectRequest::decode(&buf[..1]).is_err());
368 }
369
370 #[test]
371 fn test_decode_create_object_truncated_2_bytes() {
372 let req = CreateObjectRequest {
373 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
374 list_of_initial_values: vec![],
375 };
376 let mut buf = BytesMut::new();
377 req.encode(&mut buf);
378 assert!(CreateObjectRequest::decode(&buf[..2]).is_err());
379 }
380
381 #[test]
382 fn test_decode_create_object_truncated_3_bytes() {
383 let req = CreateObjectRequest {
384 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
385 list_of_initial_values: vec![],
386 };
387 let mut buf = BytesMut::new();
388 req.encode(&mut buf);
389 if buf.len() > 3 {
390 assert!(CreateObjectRequest::decode(&buf[..3]).is_err());
391 }
392 }
393
394 #[test]
395 fn test_decode_create_object_invalid_tag() {
396 assert!(CreateObjectRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
398 }
399
400 #[test]
401 fn test_decode_create_object_missing_closing_tag() {
402 assert!(CreateObjectRequest::decode(&[0x0E, 0x09, 0x00]).is_err());
404 }
405
406 #[test]
407 fn test_decode_delete_object_empty_input() {
408 assert!(DeleteObjectRequest::decode(&[]).is_err());
409 }
410
411 #[test]
412 fn test_decode_delete_object_truncated_1_byte() {
413 let req = DeleteObjectRequest {
414 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
415 };
416 let mut buf = BytesMut::new();
417 req.encode(&mut buf);
418 assert!(DeleteObjectRequest::decode(&buf[..1]).is_err());
419 }
420
421 #[test]
422 fn test_decode_delete_object_truncated_2_bytes() {
423 let req = DeleteObjectRequest {
424 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
425 };
426 let mut buf = BytesMut::new();
427 req.encode(&mut buf);
428 assert!(DeleteObjectRequest::decode(&buf[..2]).is_err());
429 }
430
431 #[test]
432 fn test_decode_delete_object_invalid_tag() {
433 assert!(DeleteObjectRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
434 }
435}