1use std::borrow::Cow;
40use std::sync::Arc;
41
42use bacnet_encoding::constructed::constructed_channel_value;
43use bacnet_types::constructed::BACnetDeviceObjectPropertyReference;
44use bacnet_types::enums::{
45 ErrorClass, ErrorCode, EventState, ObjectType, PropertyIdentifier, Reliability, WriteStatus,
46};
47use bacnet_types::error::Error;
48use bacnet_types::primitives::{ObjectIdentifier, PropertyValue, StatusFlags};
49
50use crate::command::{next_generation, CommandRun, RunPlan, WriteFailure};
51use crate::common::{self, read_identity_properties};
52use crate::property_metadata::{property_list_from_metadata, PropertyMetadata};
53use crate::traits::BACnetObject;
54
55mod arrays;
56mod coercion;
57mod metadata;
58
59pub use arrays::{MAX_CHANNEL_MEMBERS, MAX_CONTROL_GROUPS};
60pub use coercion::{coerce_channel_value, CoercionFailure, MemberDatatype};
61
62const DEFAULT_PRIORITY: u8 = 16;
65
66#[doc(hidden)]
68#[derive(Debug, Clone, PartialEq)]
69pub struct ChannelMember {
70 pub reference: BACnetDeviceObjectPropertyReference,
73 pub delay_ms: u32,
76 pub slot: usize,
78 pub learned: Option<MemberDatatype>,
81}
82
83#[doc(hidden)]
85#[derive(Debug, Clone, PartialEq)]
86pub struct ChannelDistribution {
87 pub value: PropertyValue,
89 pub priority: Option<u8>,
91 pub members: Vec<ChannelMember>,
93}
94
95pub struct ChannelObject {
98 oid: ObjectIdentifier,
99 name: String,
100 description: String,
101 present_value: PropertyValue,
102 last_priority: u8,
103 write_status: WriteStatus,
104 reliability: Reliability,
107 simulated_reliability: Option<Reliability>,
111 out_of_service: bool,
112 members: Vec<BACnetDeviceObjectPropertyReference>,
113 learned: Vec<Option<MemberDatatype>>,
117 execution_delay: Vec<u32>,
118 channel_number: u16,
119 control_groups: Vec<u32>,
120 allow_group_delay_inhibit: bool,
121 generation: u64,
124 pending_run: Option<CommandRun>,
125}
126
127impl ChannelObject {
128 pub fn new(instance: u32, name: impl Into<String>, channel_number: u16) -> Result<Self, Error> {
132 let oid = ObjectIdentifier::new(ObjectType::CHANNEL, instance)?;
133 Ok(Self {
134 oid,
135 name: name.into(),
136 description: String::new(),
137 present_value: PropertyValue::Null,
138 last_priority: DEFAULT_PRIORITY,
139 write_status: WriteStatus::IDLE,
140 reliability: Reliability::NO_FAULT_DETECTED,
141 simulated_reliability: None,
142 out_of_service: false,
143 members: Vec::new(),
144 learned: Vec::new(),
145 execution_delay: Vec::new(),
146 channel_number,
147 control_groups: vec![0],
148 allow_group_delay_inhibit: false,
149 generation: next_generation(),
150 pending_run: None,
151 })
152 }
153
154 pub fn set_members(
163 &mut self,
164 members: Vec<BACnetDeviceObjectPropertyReference>,
165 ) -> Result<(), Error> {
166 if members.len() > MAX_CHANNEL_MEMBERS {
167 return Err(common::protocol_error(
168 ErrorClass::RESOURCES,
169 ErrorCode::NO_SPACE_TO_WRITE_PROPERTY,
170 ));
171 }
172 crate::device_reference::check_device_members(&members)?;
173 self.execution_delay.resize(members.len(), 0);
174 self.learned = vec![None; members.len()];
175 self.members = members;
176 Ok(())
177 }
178
179 pub fn set_execution_delay(&mut self, delays: Vec<u32>) -> Result<(), Error> {
184 if delays.len() != self.members.len() {
185 return Err(common::value_out_of_range_error());
186 }
187 self.execution_delay = delays;
188 Ok(())
189 }
190
191 pub fn set_control_groups(&mut self, groups: Vec<u32>) -> Result<(), Error> {
198 arrays::check_group_count(groups.len())?;
199 self.control_groups = groups;
200 Ok(())
201 }
202
203 pub fn set_allow_group_delay_inhibit(&mut self, allow: bool) {
208 self.allow_group_delay_inhibit = allow;
209 }
210
211 fn write_present_value(
213 &mut self,
214 value: PropertyValue,
215 priority: Option<u8>,
216 ) -> Result<(), Error> {
217 if self.write_status == WriteStatus::IN_PROGRESS {
218 return Err(common::protocol_error(ErrorClass::OBJECT, ErrorCode::BUSY));
219 }
220 check_channel_value(&value)?;
221 let last_priority = match priority {
222 None => DEFAULT_PRIORITY,
223 Some(priority @ 1..=16) => priority,
224 Some(_) => return Err(common::value_out_of_range_error()),
225 };
226 self.present_value = value;
227 self.last_priority = last_priority;
228 if self.out_of_service {
230 return Ok(());
231 }
232 if self.members.is_empty() {
233 self.write_status = WriteStatus::IDLE;
234 self.reliability = Reliability::NO_FAULT_DETECTED;
235 return Ok(());
236 }
237 self.generation = next_generation();
238 let members: Vec<ChannelMember> = self
239 .members
240 .iter()
241 .zip(&self.execution_delay)
242 .zip(&self.learned)
243 .enumerate()
244 .filter(|(_, ((member, _), _))| !arrays::is_empty(member))
245 .map(|(slot, ((member, delay), learned))| ChannelMember {
246 reference: member.clone(),
247 delay_ms: *delay,
248 slot,
249 learned: *learned,
250 })
251 .collect();
252 if members.is_empty() {
253 self.write_status = WriteStatus::SUCCESSFUL;
256 self.reliability = Reliability::NO_FAULT_DETECTED;
257 return Ok(());
258 }
259 self.write_status = WriteStatus::IN_PROGRESS;
260 self.pending_run = Some(CommandRun {
261 source: self.oid,
262 generation: self.generation,
263 plan: RunPlan::Channel(ChannelDistribution {
264 value: self.present_value.clone(),
265 priority,
266 members,
267 }),
268 chain: Arc::from([]),
269 });
270 Ok(())
271 }
272
273 fn served_reliability(&self) -> Reliability {
276 self.simulated_reliability.unwrap_or(self.reliability)
277 }
278
279 fn write_out_of_service(&mut self, value: PropertyValue) -> Result<(), Error> {
280 let PropertyValue::Boolean(value) = value else {
281 return Err(common::invalid_data_type_error());
282 };
283 if value != self.out_of_service {
284 self.simulated_reliability = value.then_some(self.reliability);
287 }
288 self.out_of_service = value;
289 Ok(())
290 }
291
292 fn write_reliability(&mut self, value: PropertyValue) -> Result<(), Error> {
295 if !self.out_of_service {
296 return Err(common::write_access_denied_error());
297 }
298 let PropertyValue::Enumerated(raw) = value else {
299 return Err(common::invalid_data_type_error());
300 };
301 let reliability = Reliability::from_raw(raw);
302 if !common::is_reliability_value_valid(reliability) {
303 return Err(common::value_out_of_range_error());
304 }
305 self.simulated_reliability = Some(reliability);
306 Ok(())
307 }
308}
309
310fn check_channel_value(value: &PropertyValue) -> Result<(), Error> {
316 match value {
317 value if value.is_primitive() => Ok(()),
318 PropertyValue::ApplicationData(octets)
319 if matches!(octets.first(), Some(0x0E | 0x1E | 0x2E)) =>
320 {
321 if constructed_channel_value(octets).is_some() {
322 Ok(())
323 } else {
324 Err(common::invalid_data_encoding_error())
325 }
326 }
327 _ => Err(common::invalid_data_type_error()),
328 }
329}
330
331impl BACnetObject for ChannelObject {
332 fn object_identifier(&self) -> ObjectIdentifier {
333 self.oid
334 }
335
336 fn object_name(&self) -> &str {
337 &self.name
338 }
339
340 fn read_property(
341 &self,
342 property: PropertyIdentifier,
343 array_index: Option<u32>,
344 ) -> Result<PropertyValue, Error> {
345 if let Some(result) = read_identity_properties!(self, property, array_index) {
346 return result;
347 }
348 match property {
349 PropertyIdentifier::OBJECT_TYPE => {
350 Ok(PropertyValue::Enumerated(ObjectType::CHANNEL.to_raw()))
351 }
352 PropertyIdentifier::PRESENT_VALUE => Ok(self.present_value.clone()),
353 PropertyIdentifier::LAST_PRIORITY => {
354 Ok(PropertyValue::Unsigned(self.last_priority.into()))
355 }
356 PropertyIdentifier::WRITE_STATUS => {
357 Ok(PropertyValue::Enumerated(self.write_status.to_raw()))
358 }
359 PropertyIdentifier::STATUS_FLAGS => Ok(common::compute_status_flags(
363 StatusFlags::empty(),
364 self.served_reliability(),
365 self.out_of_service,
366 EventState::NORMAL,
367 )),
368 PropertyIdentifier::RELIABILITY => Ok(PropertyValue::Enumerated(
369 self.served_reliability().to_raw(),
370 )),
371 PropertyIdentifier::OUT_OF_SERVICE => Ok(PropertyValue::Boolean(self.out_of_service)),
372 PropertyIdentifier::LIST_OF_OBJECT_PROPERTY_REFERENCES => common::read_array(
373 crate::device_reference::reference_elements(&self.members),
374 array_index,
375 ),
376 PropertyIdentifier::EXECUTION_DELAY => common::read_array(
377 self.execution_delay
378 .iter()
379 .map(|delay| PropertyValue::Unsigned((*delay).into()))
380 .collect(),
381 array_index,
382 ),
383 PropertyIdentifier::ALLOW_GROUP_DELAY_INHIBIT => {
384 Ok(PropertyValue::Boolean(self.allow_group_delay_inhibit))
385 }
386 PropertyIdentifier::CHANNEL_NUMBER => {
387 Ok(PropertyValue::Unsigned(self.channel_number.into()))
388 }
389 PropertyIdentifier::CONTROL_GROUPS => common::read_array(
390 self.control_groups
391 .iter()
392 .map(|group| PropertyValue::Unsigned((*group).into()))
393 .collect(),
394 array_index,
395 ),
396 _ => Err(common::unknown_property_error()),
397 }
398 }
399
400 fn write_property(
401 &mut self,
402 property: PropertyIdentifier,
403 array_index: Option<u32>,
404 value: PropertyValue,
405 priority: Option<u8>,
406 ) -> Result<(), Error> {
407 if array_index.is_some() && !self.is_array_property(property) {
408 return Err(common::property_is_not_an_array_error());
409 }
410 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
411 return result;
412 }
413 if let Some(result) = common::write_description(&mut self.description, property, &value) {
414 return result;
415 }
416 match property {
417 PropertyIdentifier::PRESENT_VALUE => self.write_present_value(value, priority),
418 PropertyIdentifier::OUT_OF_SERVICE => self.write_out_of_service(value),
419 PropertyIdentifier::RELIABILITY => self.write_reliability(value),
420 PropertyIdentifier::LIST_OF_OBJECT_PROPERTY_REFERENCES => {
421 self.write_members(array_index, value)
422 }
423 PropertyIdentifier::EXECUTION_DELAY => self.write_execution_delay(array_index, value),
424 PropertyIdentifier::ALLOW_GROUP_DELAY_INHIBIT => {
425 let PropertyValue::Boolean(allow) = value else {
426 return Err(common::invalid_data_type_error());
427 };
428 self.allow_group_delay_inhibit = allow;
429 Ok(())
430 }
431 PropertyIdentifier::CHANNEL_NUMBER => {
432 let PropertyValue::Unsigned(raw) = value else {
433 return Err(common::invalid_data_type_error());
434 };
435 self.channel_number =
436 u16::try_from(raw).map_err(|_| common::value_out_of_range_error())?;
437 Ok(())
438 }
439 PropertyIdentifier::CONTROL_GROUPS => self.write_control_groups(array_index, value),
440 _ => Err(common::unhandled_write_error(
441 self.property_metadata().as_ref(),
442 property,
443 array_index,
444 )),
445 }
446 }
447
448 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
449 Cow::Borrowed(metadata::CHANNEL_PROPERTY_METADATA)
450 }
451
452 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
453 property_list_from_metadata(metadata::CHANNEL_PROPERTY_METADATA)
454 }
455
456 fn take_command_run_internal(&mut self) -> Option<CommandRun> {
457 self.pending_run.take()
458 }
459
460 fn command_generation_internal(&self) -> Option<u64> {
461 Some(self.generation)
462 }
463
464 fn remember_member_datatype_internal(
465 &mut self,
466 slot: usize,
467 reference: &BACnetDeviceObjectPropertyReference,
468 datatype: Option<MemberDatatype>,
469 ) {
470 if self.members.get(slot) == Some(reference) {
471 self.learned[slot] = datatype;
472 }
473 }
474
475 fn complete_command_run_internal(
476 &mut self,
477 generation: u64,
478 outcome: Result<(), WriteFailure>,
479 ) -> bool {
480 if generation != self.generation || self.write_status != WriteStatus::IN_PROGRESS {
481 return false;
482 }
483 (self.write_status, self.reliability) = match outcome {
484 Ok(()) => (WriteStatus::SUCCESSFUL, Reliability::NO_FAULT_DETECTED),
485 Err(failure) => (WriteStatus::FAILED, failure.reliability()),
486 };
487 true
488 }
489}
490
491#[cfg(test)]
492mod tests;
493
494#[cfg(test)]
495mod array_tests;
496
497#[cfg(test)]
498mod coercion_tests;
499
500#[cfg(test)]
501mod reliability_tests;
502
503#[cfg(test)]
504mod learned_tests;