1use crate::prelude::*;
12use alloc::borrow::Cow;
13use alloc::sync::Arc;
14use bacnet_encoding::constructed::encode_action_list;
15use bacnet_types::constructed::{BACnetActionCommand, BACnetActionList};
16use bacnet_types::enums::{ErrorClass, ErrorCode, ObjectType, PropertyIdentifier, Reliability};
17use bacnet_types::error::Error;
18use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
19use bytes::BytesMut;
20use core::sync::atomic::Ordering;
21
22use crate::sync::AtomicU64;
23
24use crate::common::{self, read_common_properties};
25use crate::traits::BACnetObject;
26
27mod metadata;
28
29#[doc(hidden)]
33#[derive(Debug, Clone, PartialEq)]
34pub struct CommandRun {
35 pub source: ObjectIdentifier,
37 pub generation: u64,
39 pub plan: RunPlan,
41 pub chain: Arc<[ObjectIdentifier]>,
45}
46
47pub(crate) fn next_generation() -> u64 {
51 static NEXT: AtomicU64 = AtomicU64::new(1);
52 NEXT.fetch_add(1, Ordering::Relaxed)
53}
54
55#[doc(hidden)]
57#[derive(Debug, Clone, PartialEq)]
58pub enum RunPlan {
59 Actions(Vec<BACnetActionCommand>),
62 Channel(crate::channel::ChannelDistribution),
64}
65
66#[doc(hidden)]
70#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum WriteFailure {
72 Configuration,
77 Process,
81 Communication,
84}
85
86impl WriteFailure {
87 pub fn reliability(self) -> Reliability {
89 match self {
90 Self::Configuration => Reliability::CONFIGURATION_ERROR,
91 Self::Process => Reliability::PROCESS_ERROR,
92 Self::Communication => Reliability::COMMUNICATION_FAILURE,
93 }
94 }
95}
96
97pub struct CommandObject {
99 oid: ObjectIdentifier,
100 name: String,
101 description: String,
102 present_value: u64,
103 in_process: bool,
104 all_writes_successful: bool,
105 action: Vec<BACnetActionList>,
106 action_text: Option<Vec<String>>,
107 status_flags: StatusFlags,
108 reliability: Reliability,
109 generation: u64,
112 running: Option<usize>,
114 pending_run: Option<CommandRun>,
115}
116
117impl CommandObject {
118 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
120 let oid = ObjectIdentifier::new(ObjectType::COMMAND, instance)?;
121 Ok(Self {
122 oid,
123 name: name.into(),
124 description: String::new(),
125 present_value: 0,
126 in_process: false,
127 all_writes_successful: true,
128 action: Vec::new(),
129 action_text: None,
130 status_flags: StatusFlags::empty(),
131 reliability: Reliability::NO_FAULT_DETECTED,
132 generation: next_generation(),
133 running: None,
134 pending_run: None,
135 })
136 }
137
138 pub fn set_action(&mut self, action: Vec<BACnetActionList>) -> Result<(), Error> {
149 let mut scratch = BytesMut::new();
150 for list in &action {
151 for command in &list.commands {
152 crate::device_reference::check_device_member(command.device_identifier)?;
153 }
154 encode_action_list(&mut scratch, list)
155 .map_err(|_| common::value_out_of_range_error())?;
156 scratch.clear();
157 }
158 if let Some(texts) = &mut self.action_text {
159 texts.resize(action.len(), String::new());
160 }
161 self.action = action;
162 self.generation = next_generation();
163 self.in_process = false;
164 self.running = None;
165 self.pending_run = None;
166 Ok(())
167 }
168
169 pub fn set_action_text(&mut self, texts: Vec<String>) -> Result<(), Error> {
175 if texts.len() != self.action.len() {
176 return Err(common::value_out_of_range_error());
177 }
178 self.action_text = Some(texts);
179 Ok(())
180 }
181
182 fn select(&mut self, selected: u64) -> Result<(), Error> {
185 if self.in_process {
188 return Err(common::protocol_error(ErrorClass::OBJECT, ErrorCode::BUSY));
189 }
190 let index = match usize::try_from(selected) {
191 Ok(0) => None,
192 Ok(n) if n <= self.action.len() => Some(n - 1),
193 _ => return Err(common::value_out_of_range_error()),
194 };
195 self.present_value = selected;
196 self.generation = next_generation();
197 let commands = index.map_or_else(Vec::new, |i| self.action[i].commands.clone());
198 if commands.is_empty() {
199 self.all_writes_successful = true;
200 return Ok(());
201 }
202 self.in_process = true;
203 self.all_writes_successful = false;
204 self.running = index;
205 self.pending_run = Some(CommandRun {
206 source: self.oid,
207 generation: self.generation,
208 plan: RunPlan::Actions(commands),
209 chain: Arc::from([]),
210 });
211 Ok(())
212 }
213}
214
215impl BACnetObject for CommandObject {
216 fn object_identifier(&self) -> ObjectIdentifier {
217 self.oid
218 }
219
220 fn object_name(&self) -> &str {
221 &self.name
222 }
223
224 fn read_property(
225 &self,
226 property: PropertyIdentifier,
227 array_index: Option<u32>,
228 ) -> Result<PropertyValue, Error> {
229 if let Some(result) =
232 read_common_properties!(self, property, array_index, no_out_of_service)
233 {
234 return result;
235 }
236 match property {
237 p if p == PropertyIdentifier::OBJECT_TYPE => {
238 Ok(PropertyValue::Enumerated(ObjectType::COMMAND.to_raw()))
239 }
240 p if p == PropertyIdentifier::PRESENT_VALUE => {
241 Ok(PropertyValue::Unsigned(self.present_value))
242 }
243 p if p == PropertyIdentifier::IN_PROCESS => Ok(PropertyValue::Boolean(self.in_process)),
244 p if p == PropertyIdentifier::ALL_WRITES_SUCCESSFUL => {
245 Ok(PropertyValue::Boolean(self.all_writes_successful))
246 }
247 p if p == PropertyIdentifier::ACTION => {
251 let lists = self
252 .action
253 .iter()
254 .map(|list| {
255 let mut encoded = BytesMut::new();
256 encode_action_list(&mut encoded, list)?;
257 Ok(PropertyValue::ApplicationData(encoded.to_vec()))
258 })
259 .collect::<Result<Vec<_>, Error>>()?;
260 common::read_array(lists, array_index)
261 }
262 p if p == PropertyIdentifier::ACTION_TEXT => match &self.action_text {
263 Some(texts) => common::read_array(
264 texts
265 .iter()
266 .cloned()
267 .map(PropertyValue::CharacterString)
268 .collect(),
269 array_index,
270 ),
271 None => Err(common::unknown_property_error()),
272 },
273 _ => Err(common::unknown_property_error()),
274 }
275 }
276
277 fn write_property(
278 &mut self,
279 property: PropertyIdentifier,
280 array_index: Option<u32>,
281 value: PropertyValue,
282 _priority: Option<u8>,
283 ) -> Result<(), Error> {
284 if let Some(result) = common::write_description(&mut self.description, property, &value) {
285 return result;
286 }
287 match property {
288 p if p == PropertyIdentifier::PRESENT_VALUE => match value {
289 PropertyValue::Unsigned(selected) => self.select(selected),
290 _ => Err(common::invalid_data_type_error()),
291 },
292 _ => Err(crate::common::unhandled_write_error(
295 self.property_metadata().as_ref(),
296 property,
297 array_index,
298 )),
299 }
300 }
301
302 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
303 metadata::for_object(self)
304 }
305
306 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
307 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
308 }
309
310 fn supports_subscribe_cov_property(&self) -> bool {
311 true
315 }
316
317 fn take_command_run_internal(&mut self) -> Option<CommandRun> {
318 self.pending_run.take()
319 }
320
321 fn command_generation_internal(&self) -> Option<u64> {
322 Some(self.generation)
323 }
324
325 fn record_command_write_internal(
326 &mut self,
327 generation: u64,
328 command: usize,
329 success: bool,
330 ) -> bool {
331 if generation != self.generation || !self.in_process {
332 return false;
333 }
334 let Some(list) = self.running.and_then(|index| self.action.get_mut(index)) else {
335 return false;
336 };
337 let Some(made) = list.commands.get_mut(command) else {
338 return false;
339 };
340 made.write_successful = success;
341 if !success && made.quit_on_failure {
344 for skipped in &mut list.commands[command + 1..] {
345 skipped.write_successful = false;
346 }
347 }
348 true
349 }
350
351 fn complete_command_run_internal(
352 &mut self,
353 generation: u64,
354 outcome: Result<(), WriteFailure>,
355 ) -> bool {
356 if generation != self.generation || !self.in_process {
357 return false;
358 }
359 self.in_process = false;
360 self.all_writes_successful = outcome.is_ok();
362 self.running = None;
363 true
364 }
365}
366
367#[cfg(test)]
368mod tests;