bacnet_encoding/constructed/
action_list.rs1#[cfg(not(feature = "std"))]
13use alloc::vec::Vec;
14
15use bacnet_types::constructed::{BACnetActionCommand, BACnetActionList};
16use bacnet_types::enums::PropertyIdentifier;
17use bacnet_types::error::{DecodeError, DecodingKind, Error, RangeError};
18use bytes::BytesMut;
19
20use super::tagged::{
21 decode_ctx_boolean, decode_ctx_object_id, decode_ctx_unsigned, decode_framed_value,
22 decode_optional_ctx, expect_opening,
23};
24use super::MAX_FRAMED_ITEMS;
25use crate::{primitives, tags};
26
27const WHAT: &str = "BACnetActionCommand";
28
29pub fn encode_action_command(
34 buf: &mut BytesMut,
35 command: &BACnetActionCommand,
36) -> Result<(), Error> {
37 if let Some(priority) = command.priority {
38 if !(1..=16).contains(&priority) {
39 return Err(
40 RangeError::new("BACnetActionCommand: priority {} is outside 1..=16")
41 .with_value(priority.into())
42 .into(),
43 );
44 }
45 }
46 let start = buf.len();
47 if let Some(device) = &command.device_identifier {
48 primitives::encode_ctx_object_id(buf, 0, device);
49 }
50 primitives::encode_ctx_object_id(buf, 1, &command.object_identifier);
51 primitives::encode_ctx_enumerated(buf, 2, command.property_identifier.to_raw());
52 if let Some(index) = command.property_array_index {
53 primitives::encode_ctx_unsigned(buf, 3, u64::from(index));
54 }
55 tags::encode_opening_tag(buf, 4);
56 if let Err(error) = primitives::encode_property_value(buf, &command.property_value) {
57 buf.truncate(start);
58 return Err(error);
59 }
60 tags::encode_closing_tag(buf, 4);
61 if let Some(priority) = command.priority {
62 primitives::encode_ctx_unsigned(buf, 5, u64::from(priority));
63 }
64 if let Some(delay) = command.post_delay {
65 primitives::encode_ctx_unsigned(buf, 6, u64::from(delay));
66 }
67 primitives::encode_ctx_boolean(buf, 7, command.quit_on_failure);
68 primitives::encode_ctx_boolean(buf, 8, command.write_successful);
69 Ok(())
70}
71
72pub fn decode_action_command(
83 data: &[u8],
84 offset: usize,
85) -> Result<(BACnetActionCommand, usize), Error> {
86 let (device_identifier, offset) =
87 decode_optional_ctx(data, offset, 0, WHAT, decode_ctx_object_id)?;
88 let (object_identifier, offset) = decode_ctx_object_id(data, offset, 1, WHAT)?;
89 let (property, offset) = decode_ctx_unsigned::<u32>(data, offset, 2, WHAT)?;
90 let property_identifier = PropertyIdentifier::from_raw(property);
91 let (property_array_index, offset) =
92 decode_optional_ctx(data, offset, 3, WHAT, decode_ctx_unsigned::<u32>)?;
93
94 let content = expect_opening(data, offset, 4, WHAT)?;
95 let (property_value, offset) = decode_framed_value(data, content, 4, WHAT)?;
96
97 let (priority, next) = decode_optional_ctx(data, offset, 5, WHAT, decode_ctx_unsigned::<u32>)?;
98 let priority = priority
99 .map(|value| {
100 u8::try_from(value)
101 .ok()
102 .filter(|p| (1..=16).contains(p))
103 .ok_or_else(|| {
104 DecodeError::new(
105 DecodingKind::InvalidEncoding,
106 offset,
107 "priority {} is outside 1..=16",
108 )
109 .within(WHAT)
110 .with_value(value.into())
111 })
112 })
113 .transpose()?;
114 let (post_delay, offset) =
115 decode_optional_ctx(data, next, 6, WHAT, decode_ctx_unsigned::<u32>)?;
116 let (quit_on_failure, offset) = decode_ctx_boolean(data, offset, 7, WHAT)?;
117 let (write_successful, offset) = decode_ctx_boolean(data, offset, 8, WHAT)?;
118 Ok((
119 BACnetActionCommand {
120 device_identifier,
121 object_identifier,
122 property_identifier,
123 property_array_index,
124 property_value,
125 priority,
126 post_delay,
127 quit_on_failure,
128 write_successful,
129 },
130 offset,
131 ))
132}
133
134pub fn encode_action_list(buf: &mut BytesMut, list: &BACnetActionList) -> Result<(), Error> {
137 let start = buf.len();
138 tags::encode_opening_tag(buf, 0);
139 for command in &list.commands {
140 if let Err(error) = encode_action_command(buf, command) {
141 buf.truncate(start);
142 return Err(error);
143 }
144 }
145 tags::encode_closing_tag(buf, 0);
146 Ok(())
147}
148
149pub fn decode_action_list(data: &[u8], offset: usize) -> Result<(BACnetActionList, usize), Error> {
153 let content = expect_opening(data, offset, 0, "BACnetActionList")?;
154 let (inner, end) = tags::extract_context_value(data, content, 0)?;
155 let body = &data[..content + inner.len()];
156 let mut commands = Vec::new();
157 let mut offset = content;
158 while offset < body.len() {
159 if commands.len() >= MAX_FRAMED_ITEMS {
160 return Err(Error::overflow(
161 offset,
162 "BACnetActionList: commands exceed item limit",
163 ));
164 }
165 let (command, next) = decode_action_command(body, offset)?;
166 commands.push(command);
167 offset = next;
168 }
169 Ok((BACnetActionList { commands }, end))
170}