1use bacnet_encoding::primitives;
7use bacnet_encoding::tags;
8use bacnet_types::enums::ObjectType;
9use bacnet_types::error::Error;
10use bacnet_types::primitives::ObjectIdentifier;
11use bytes::BytesMut;
12
13use crate::common::{BACnetPropertyValue, MAX_DECODED_ITEMS};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum ObjectSpecifier {
22 Type(ObjectType),
24 Identifier(ObjectIdentifier),
26}
27
28#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct CreateObjectRequest {
31 pub object_specifier: ObjectSpecifier,
32 pub list_of_initial_values: Vec<BACnetPropertyValue>,
33}
34
35impl CreateObjectRequest {
36 pub fn encode(&self, buf: &mut BytesMut) {
37 tags::encode_opening_tag(buf, 0);
39 match &self.object_specifier {
40 ObjectSpecifier::Type(obj_type) => {
41 primitives::encode_ctx_enumerated(buf, 0, obj_type.to_raw());
42 }
43 ObjectSpecifier::Identifier(oid) => {
44 primitives::encode_ctx_object_id(buf, 1, oid);
45 }
46 }
47 tags::encode_closing_tag(buf, 0);
48
49 if !self.list_of_initial_values.is_empty() {
51 tags::encode_opening_tag(buf, 1);
52 for pv in &self.list_of_initial_values {
53 pv.encode(buf);
54 }
55 tags::encode_closing_tag(buf, 1);
56 }
57 }
58
59 pub fn decode(data: &[u8]) -> Result<Self, Error> {
60 let mut offset = 0;
61
62 let (tag, tag_end) = tags::decode_tag(data, offset)?;
64 if !tag.is_opening_tag(0) {
65 return Err(Error::decoding(
66 offset,
67 "CreateObject expected opening tag 0",
68 ));
69 }
70 offset = tag_end;
71
72 let (tag, pos) = tags::decode_tag(data, offset)?;
73 let end = pos + tag.length as usize;
74 if end > data.len() {
75 return Err(Error::decoding(
76 pos,
77 "CreateObject truncated at object-specifier",
78 ));
79 }
80
81 let object_specifier = if tag.is_context(0) {
82 let raw = primitives::decode_unsigned(&data[pos..end])? as u32;
83 ObjectSpecifier::Type(ObjectType::from_raw(raw))
84 } else if tag.is_context(1) {
85 ObjectSpecifier::Identifier(ObjectIdentifier::decode(&data[pos..end])?)
86 } else {
87 return Err(Error::decoding(
88 offset,
89 "CreateObject expected context tag 0 or 1 inside object-specifier",
90 ));
91 };
92 offset = end;
93
94 let (tag, tag_end) = tags::decode_tag(data, offset)?;
95 if !tag.is_closing_tag(0) {
96 return Err(Error::decoding(
97 offset,
98 "CreateObject expected closing tag 0",
99 ));
100 }
101 offset = tag_end;
102
103 let mut values = Vec::new();
105 if offset < data.len() {
106 let (tag, tag_end) = tags::decode_tag(data, offset)?;
107 if tag.is_opening_tag(1) {
108 offset = tag_end;
109 loop {
110 if offset >= data.len() {
111 return Err(Error::decoding(
112 offset,
113 "CreateObject missing closing tag 1",
114 ));
115 }
116 if values.len() >= MAX_DECODED_ITEMS {
117 return Err(Error::decoding(offset, "CreateObject values exceeds max"));
118 }
119 let (tag, _tag_end) = tags::decode_tag(data, offset)?;
120 if tag.is_closing_tag(1) {
121 break;
122 }
123 let (pv, new_offset) = BACnetPropertyValue::decode(data, offset)?;
124 values.push(pv);
125 offset = new_offset;
126 }
127 }
128 }
129
130 Ok(Self {
131 object_specifier,
132 list_of_initial_values: values,
133 })
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
145pub struct DeleteObjectRequest {
146 pub object_identifier: ObjectIdentifier,
147}
148
149impl DeleteObjectRequest {
150 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> {
155 let (tag, pos) = tags::decode_tag(data, 0)?;
156 let end = pos + tag.length as usize;
157 if end > data.len() {
158 return Err(Error::decoding(pos, "DeleteObject truncated at object-id"));
159 }
160 let object_identifier = ObjectIdentifier::decode(&data[pos..end])?;
161 Ok(Self { object_identifier })
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use bacnet_types::enums::{ObjectType, PropertyIdentifier};
169
170 #[test]
171 fn create_object_by_type_round_trip() {
172 let req = CreateObjectRequest {
173 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
174 list_of_initial_values: vec![],
175 };
176 let mut buf = BytesMut::new();
177 req.encode(&mut buf);
178 let decoded = CreateObjectRequest::decode(&buf).unwrap();
179 assert_eq!(req, decoded);
180 }
181
182 #[test]
183 fn create_object_by_id_with_values() {
184 let req = CreateObjectRequest {
185 object_specifier: ObjectSpecifier::Identifier(
186 ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
187 ),
188 list_of_initial_values: vec![BACnetPropertyValue {
189 property_identifier: PropertyIdentifier::OBJECT_NAME,
190 property_array_index: None,
191 value: vec![0x75, 0x06, 0x00, 0x5A, 0x6F, 0x6E, 0x65, 0x31],
192 priority: None,
193 }],
194 };
195 let mut buf = BytesMut::new();
196 req.encode(&mut buf);
197 let decoded = CreateObjectRequest::decode(&buf).unwrap();
198 assert_eq!(req, decoded);
199 }
200
201 #[test]
202 fn delete_object_round_trip() {
203 let req = DeleteObjectRequest {
204 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
205 };
206 let mut buf = BytesMut::new();
207 req.encode(&mut buf);
208 let decoded = DeleteObjectRequest::decode(&buf).unwrap();
209 assert_eq!(req, decoded);
210 }
211
212 #[test]
217 fn test_decode_create_object_empty_input() {
218 assert!(CreateObjectRequest::decode(&[]).is_err());
219 }
220
221 #[test]
222 fn test_decode_create_object_truncated_1_byte() {
223 let req = CreateObjectRequest {
224 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
225 list_of_initial_values: vec![],
226 };
227 let mut buf = BytesMut::new();
228 req.encode(&mut buf);
229 assert!(CreateObjectRequest::decode(&buf[..1]).is_err());
230 }
231
232 #[test]
233 fn test_decode_create_object_truncated_2_bytes() {
234 let req = CreateObjectRequest {
235 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
236 list_of_initial_values: vec![],
237 };
238 let mut buf = BytesMut::new();
239 req.encode(&mut buf);
240 assert!(CreateObjectRequest::decode(&buf[..2]).is_err());
241 }
242
243 #[test]
244 fn test_decode_create_object_truncated_3_bytes() {
245 let req = CreateObjectRequest {
246 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
247 list_of_initial_values: vec![],
248 };
249 let mut buf = BytesMut::new();
250 req.encode(&mut buf);
251 if buf.len() > 3 {
252 assert!(CreateObjectRequest::decode(&buf[..3]).is_err());
253 }
254 }
255
256 #[test]
257 fn test_decode_create_object_invalid_tag() {
258 assert!(CreateObjectRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
260 }
261
262 #[test]
263 fn test_decode_create_object_missing_closing_tag() {
264 assert!(CreateObjectRequest::decode(&[0x0E, 0x09, 0x00]).is_err());
266 }
267
268 #[test]
269 fn test_decode_delete_object_empty_input() {
270 assert!(DeleteObjectRequest::decode(&[]).is_err());
271 }
272
273 #[test]
274 fn test_decode_delete_object_truncated_1_byte() {
275 let req = DeleteObjectRequest {
276 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
277 };
278 let mut buf = BytesMut::new();
279 req.encode(&mut buf);
280 assert!(DeleteObjectRequest::decode(&buf[..1]).is_err());
281 }
282
283 #[test]
284 fn test_decode_delete_object_truncated_2_bytes() {
285 let req = DeleteObjectRequest {
286 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
287 };
288 let mut buf = BytesMut::new();
289 req.encode(&mut buf);
290 assert!(DeleteObjectRequest::decode(&buf[..2]).is_err());
291 }
292
293 #[test]
294 fn test_decode_delete_object_invalid_tag() {
295 assert!(DeleteObjectRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
296 }
297}