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::Error;
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(Error::OutOfRange(format!(
40 "{WHAT}: priority {priority} is outside 1..=16"
41 )));
42 }
43 }
44 let start = buf.len();
45 if let Some(device) = &command.device_identifier {
46 primitives::encode_ctx_object_id(buf, 0, device);
47 }
48 primitives::encode_ctx_object_id(buf, 1, &command.object_identifier);
49 primitives::encode_ctx_enumerated(buf, 2, command.property_identifier.to_raw());
50 if let Some(index) = command.property_array_index {
51 primitives::encode_ctx_unsigned(buf, 3, u64::from(index));
52 }
53 tags::encode_opening_tag(buf, 4);
54 if let Err(error) = primitives::encode_property_value(buf, &command.property_value) {
55 buf.truncate(start);
56 return Err(error);
57 }
58 tags::encode_closing_tag(buf, 4);
59 if let Some(priority) = command.priority {
60 primitives::encode_ctx_unsigned(buf, 5, u64::from(priority));
61 }
62 if let Some(delay) = command.post_delay {
63 primitives::encode_ctx_unsigned(buf, 6, u64::from(delay));
64 }
65 primitives::encode_ctx_boolean(buf, 7, command.quit_on_failure);
66 primitives::encode_ctx_boolean(buf, 8, command.write_successful);
67 Ok(())
68}
69
70pub fn decode_action_command(
81 data: &[u8],
82 offset: usize,
83) -> Result<(BACnetActionCommand, usize), Error> {
84 let (device_identifier, offset) =
85 decode_optional_ctx(data, offset, 0, WHAT, decode_ctx_object_id)?;
86 let (object_identifier, offset) = decode_ctx_object_id(data, offset, 1, WHAT)?;
87 let (property, offset) = decode_ctx_unsigned::<u32>(data, offset, 2, WHAT)?;
88 let property_identifier = PropertyIdentifier::from_raw(property);
89 let (property_array_index, offset) =
90 decode_optional_ctx(data, offset, 3, WHAT, decode_ctx_unsigned::<u32>)?;
91
92 let content = expect_opening(data, offset, 4, WHAT)?;
93 let (property_value, offset) = decode_framed_value(data, content, 4, WHAT)?;
94
95 let (priority, next) = decode_optional_ctx(data, offset, 5, WHAT, decode_ctx_unsigned::<u32>)?;
96 let priority = priority
97 .map(|value| {
98 u8::try_from(value)
99 .ok()
100 .filter(|p| (1..=16).contains(p))
101 .ok_or_else(|| {
102 Error::decoding(
103 offset,
104 format!("{WHAT}: priority {value} is outside 1..=16"),
105 )
106 })
107 })
108 .transpose()?;
109 let (post_delay, offset) =
110 decode_optional_ctx(data, next, 6, WHAT, decode_ctx_unsigned::<u32>)?;
111 let (quit_on_failure, offset) = decode_ctx_boolean(data, offset, 7, WHAT)?;
112 let (write_successful, offset) = decode_ctx_boolean(data, offset, 8, WHAT)?;
113 Ok((
114 BACnetActionCommand {
115 device_identifier,
116 object_identifier,
117 property_identifier,
118 property_array_index,
119 property_value,
120 priority,
121 post_delay,
122 quit_on_failure,
123 write_successful,
124 },
125 offset,
126 ))
127}
128
129pub fn encode_action_list(buf: &mut BytesMut, list: &BACnetActionList) -> Result<(), Error> {
132 let start = buf.len();
133 tags::encode_opening_tag(buf, 0);
134 for command in &list.commands {
135 if let Err(error) = encode_action_command(buf, command) {
136 buf.truncate(start);
137 return Err(error);
138 }
139 }
140 tags::encode_closing_tag(buf, 0);
141 Ok(())
142}
143
144pub fn decode_action_list(data: &[u8], offset: usize) -> Result<(BACnetActionList, usize), Error> {
148 let content = expect_opening(data, offset, 0, "BACnetActionList")?;
149 let (inner, end) = tags::extract_context_value(data, content, 0)?;
150 let body = &data[..content + inner.len()];
151 let mut commands = Vec::new();
152 let mut offset = content;
153 while offset < body.len() {
154 if commands.len() >= MAX_FRAMED_ITEMS {
155 return Err(Error::overflow(
156 offset,
157 "BACnetActionList: commands exceed item limit",
158 ));
159 }
160 let (command, next) = decode_action_command(body, offset)?;
161 commands.push(command);
162 offset = next;
163 }
164 Ok((BACnetActionList { commands }, end))
165}