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)?;
74 let end = pos + tag.length as usize;
75 if end > data.len() {
76 return Err(Error::decoding(
77 pos,
78 "CreateObject truncated at object-specifier",
79 ));
80 }
81
82 let object_specifier = if tag.is_context(0) {
83 let raw = primitives::decode_unsigned(&data[pos..end])? as u32;
84 ObjectSpecifier::Type(ObjectType::from_raw(raw))
85 } else if tag.is_context(1) {
86 ObjectSpecifier::Identifier(ObjectIdentifier::decode(&data[pos..end])?)
87 } else {
88 return Err(Error::decoding(
89 offset,
90 "CreateObject expected context tag 0 or 1 inside object-specifier",
91 ));
92 };
93 offset = end;
94
95 let (tag, tag_end) = tags::decode_tag(data, offset)?;
97 if !tag.is_closing_tag(0) {
98 return Err(Error::decoding(
99 offset,
100 "CreateObject expected closing tag 0",
101 ));
102 }
103 offset = tag_end;
104
105 let mut values = Vec::new();
107 if offset < data.len() {
108 let (tag, tag_end) = tags::decode_tag(data, offset)?;
109 if tag.is_opening_tag(1) {
110 offset = tag_end;
111 loop {
112 if offset >= data.len() {
113 return Err(Error::decoding(
114 offset,
115 "CreateObject missing closing tag 1",
116 ));
117 }
118 if values.len() >= MAX_DECODED_ITEMS {
119 return Err(Error::decoding(offset, "CreateObject values exceeds max"));
120 }
121 let (tag, _tag_end) = tags::decode_tag(data, offset)?;
122 if tag.is_closing_tag(1) {
123 break;
124 }
125 let (pv, new_offset) = BACnetPropertyValue::decode(data, offset)?;
126 values.push(pv);
127 offset = new_offset;
128 }
129 }
130 }
131
132 Ok(Self {
133 object_specifier,
134 list_of_initial_values: values,
135 })
136 }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
147pub struct DeleteObjectRequest {
148 pub object_identifier: ObjectIdentifier,
149}
150
151impl DeleteObjectRequest {
152 pub fn encode(&self, buf: &mut BytesMut) {
153 primitives::encode_app_object_id(buf, &self.object_identifier);
154 }
155
156 pub fn decode(data: &[u8]) -> Result<Self, Error> {
157 let (tag, pos) = tags::decode_tag(data, 0)?;
158 let end = pos + tag.length as usize;
159 if end > data.len() {
160 return Err(Error::decoding(pos, "DeleteObject truncated at object-id"));
161 }
162 let object_identifier = ObjectIdentifier::decode(&data[pos..end])?;
163 Ok(Self { object_identifier })
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use bacnet_types::enums::{ObjectType, PropertyIdentifier};
171
172 #[test]
173 fn create_object_by_type_round_trip() {
174 let req = CreateObjectRequest {
175 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
176 list_of_initial_values: vec![],
177 };
178 let mut buf = BytesMut::new();
179 req.encode(&mut buf);
180 let decoded = CreateObjectRequest::decode(&buf).unwrap();
181 assert_eq!(req, decoded);
182 }
183
184 #[test]
185 fn create_object_by_id_with_values() {
186 let req = CreateObjectRequest {
187 object_specifier: ObjectSpecifier::Identifier(
188 ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
189 ),
190 list_of_initial_values: vec![BACnetPropertyValue {
191 property_identifier: PropertyIdentifier::OBJECT_NAME,
192 property_array_index: None,
193 value: vec![0x75, 0x06, 0x00, 0x5A, 0x6F, 0x6E, 0x65, 0x31],
194 priority: None,
195 }],
196 };
197 let mut buf = BytesMut::new();
198 req.encode(&mut buf);
199 let decoded = CreateObjectRequest::decode(&buf).unwrap();
200 assert_eq!(req, decoded);
201 }
202
203 #[test]
204 fn delete_object_round_trip() {
205 let req = DeleteObjectRequest {
206 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
207 };
208 let mut buf = BytesMut::new();
209 req.encode(&mut buf);
210 let decoded = DeleteObjectRequest::decode(&buf).unwrap();
211 assert_eq!(req, decoded);
212 }
213
214 #[test]
219 fn test_decode_create_object_empty_input() {
220 assert!(CreateObjectRequest::decode(&[]).is_err());
221 }
222
223 #[test]
224 fn test_decode_create_object_truncated_1_byte() {
225 let req = CreateObjectRequest {
226 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
227 list_of_initial_values: vec![],
228 };
229 let mut buf = BytesMut::new();
230 req.encode(&mut buf);
231 assert!(CreateObjectRequest::decode(&buf[..1]).is_err());
232 }
233
234 #[test]
235 fn test_decode_create_object_truncated_2_bytes() {
236 let req = CreateObjectRequest {
237 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
238 list_of_initial_values: vec![],
239 };
240 let mut buf = BytesMut::new();
241 req.encode(&mut buf);
242 assert!(CreateObjectRequest::decode(&buf[..2]).is_err());
243 }
244
245 #[test]
246 fn test_decode_create_object_truncated_3_bytes() {
247 let req = CreateObjectRequest {
248 object_specifier: ObjectSpecifier::Type(ObjectType::ANALOG_INPUT),
249 list_of_initial_values: vec![],
250 };
251 let mut buf = BytesMut::new();
252 req.encode(&mut buf);
253 if buf.len() > 3 {
254 assert!(CreateObjectRequest::decode(&buf[..3]).is_err());
255 }
256 }
257
258 #[test]
259 fn test_decode_create_object_invalid_tag() {
260 assert!(CreateObjectRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
262 }
263
264 #[test]
265 fn test_decode_create_object_missing_closing_tag() {
266 assert!(CreateObjectRequest::decode(&[0x0E, 0x09, 0x00]).is_err());
268 }
269
270 #[test]
271 fn test_decode_delete_object_empty_input() {
272 assert!(DeleteObjectRequest::decode(&[]).is_err());
273 }
274
275 #[test]
276 fn test_decode_delete_object_truncated_1_byte() {
277 let req = DeleteObjectRequest {
278 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
279 };
280 let mut buf = BytesMut::new();
281 req.encode(&mut buf);
282 assert!(DeleteObjectRequest::decode(&buf[..1]).is_err());
283 }
284
285 #[test]
286 fn test_decode_delete_object_truncated_2_bytes() {
287 let req = DeleteObjectRequest {
288 object_identifier: ObjectIdentifier::new(ObjectType::ANALOG_INPUT, 1).unwrap(),
289 };
290 let mut buf = BytesMut::new();
291 req.encode(&mut buf);
292 assert!(DeleteObjectRequest::decode(&buf[..2]).is_err());
293 }
294
295 #[test]
296 fn test_decode_delete_object_invalid_tag() {
297 assert!(DeleteObjectRequest::decode(&[0xFF, 0xFF, 0xFF]).is_err());
298 }
299}