1use std::borrow::Cow;
40use std::sync::Arc;
41
42use bacnet_encoding::constructed::is_lighting_command_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> {
313 match value {
314 value if value.is_primitive() => Ok(()),
315 PropertyValue::ApplicationData(octets) if octets.first() == Some(&0x0E) => {
316 if is_lighting_command_channel_value(octets) {
317 Ok(())
318 } else {
319 Err(common::invalid_data_encoding_error())
320 }
321 }
322 _ => Err(common::invalid_data_type_error()),
323 }
324}
325
326impl BACnetObject for ChannelObject {
327 fn object_identifier(&self) -> ObjectIdentifier {
328 self.oid
329 }
330
331 fn object_name(&self) -> &str {
332 &self.name
333 }
334
335 fn read_property(
336 &self,
337 property: PropertyIdentifier,
338 array_index: Option<u32>,
339 ) -> Result<PropertyValue, Error> {
340 if let Some(result) = read_identity_properties!(self, property, array_index) {
341 return result;
342 }
343 match property {
344 PropertyIdentifier::OBJECT_TYPE => {
345 Ok(PropertyValue::Enumerated(ObjectType::CHANNEL.to_raw()))
346 }
347 PropertyIdentifier::PRESENT_VALUE => Ok(self.present_value.clone()),
348 PropertyIdentifier::LAST_PRIORITY => {
349 Ok(PropertyValue::Unsigned(self.last_priority.into()))
350 }
351 PropertyIdentifier::WRITE_STATUS => {
352 Ok(PropertyValue::Enumerated(self.write_status.to_raw()))
353 }
354 PropertyIdentifier::STATUS_FLAGS => Ok(common::compute_status_flags(
358 StatusFlags::empty(),
359 self.served_reliability(),
360 self.out_of_service,
361 EventState::NORMAL,
362 )),
363 PropertyIdentifier::RELIABILITY => Ok(PropertyValue::Enumerated(
364 self.served_reliability().to_raw(),
365 )),
366 PropertyIdentifier::OUT_OF_SERVICE => Ok(PropertyValue::Boolean(self.out_of_service)),
367 PropertyIdentifier::LIST_OF_OBJECT_PROPERTY_REFERENCES => common::read_array(
368 crate::device_reference::reference_elements(&self.members),
369 array_index,
370 ),
371 PropertyIdentifier::EXECUTION_DELAY => common::read_array(
372 self.execution_delay
373 .iter()
374 .map(|delay| PropertyValue::Unsigned((*delay).into()))
375 .collect(),
376 array_index,
377 ),
378 PropertyIdentifier::ALLOW_GROUP_DELAY_INHIBIT => {
379 Ok(PropertyValue::Boolean(self.allow_group_delay_inhibit))
380 }
381 PropertyIdentifier::CHANNEL_NUMBER => {
382 Ok(PropertyValue::Unsigned(self.channel_number.into()))
383 }
384 PropertyIdentifier::CONTROL_GROUPS => common::read_array(
385 self.control_groups
386 .iter()
387 .map(|group| PropertyValue::Unsigned((*group).into()))
388 .collect(),
389 array_index,
390 ),
391 _ => Err(common::unknown_property_error()),
392 }
393 }
394
395 fn write_property(
396 &mut self,
397 property: PropertyIdentifier,
398 array_index: Option<u32>,
399 value: PropertyValue,
400 priority: Option<u8>,
401 ) -> Result<(), Error> {
402 if array_index.is_some() && !self.is_array_property(property) {
403 return Err(common::property_is_not_an_array_error());
404 }
405 if let Some(result) = common::write_object_name(&mut self.name, property, &value) {
406 return result;
407 }
408 if let Some(result) = common::write_description(&mut self.description, property, &value) {
409 return result;
410 }
411 match property {
412 PropertyIdentifier::PRESENT_VALUE => self.write_present_value(value, priority),
413 PropertyIdentifier::OUT_OF_SERVICE => self.write_out_of_service(value),
414 PropertyIdentifier::RELIABILITY => self.write_reliability(value),
415 PropertyIdentifier::LIST_OF_OBJECT_PROPERTY_REFERENCES => {
416 self.write_members(array_index, value)
417 }
418 PropertyIdentifier::EXECUTION_DELAY => self.write_execution_delay(array_index, value),
419 PropertyIdentifier::ALLOW_GROUP_DELAY_INHIBIT => {
420 let PropertyValue::Boolean(allow) = value else {
421 return Err(common::invalid_data_type_error());
422 };
423 self.allow_group_delay_inhibit = allow;
424 Ok(())
425 }
426 PropertyIdentifier::CHANNEL_NUMBER => {
427 let PropertyValue::Unsigned(raw) = value else {
428 return Err(common::invalid_data_type_error());
429 };
430 self.channel_number =
431 u16::try_from(raw).map_err(|_| common::value_out_of_range_error())?;
432 Ok(())
433 }
434 PropertyIdentifier::CONTROL_GROUPS => self.write_control_groups(array_index, value),
435 _ => Err(common::unhandled_write_error(
436 self.property_metadata().as_ref(),
437 property,
438 array_index,
439 )),
440 }
441 }
442
443 fn property_metadata(&self) -> Cow<'_, [PropertyMetadata]> {
444 Cow::Borrowed(metadata::CHANNEL_PROPERTY_METADATA)
445 }
446
447 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
448 property_list_from_metadata(metadata::CHANNEL_PROPERTY_METADATA)
449 }
450
451 fn take_command_run_internal(&mut self) -> Option<CommandRun> {
452 self.pending_run.take()
453 }
454
455 fn command_generation_internal(&self) -> Option<u64> {
456 Some(self.generation)
457 }
458
459 fn remember_member_datatype_internal(
460 &mut self,
461 slot: usize,
462 reference: &BACnetDeviceObjectPropertyReference,
463 datatype: Option<MemberDatatype>,
464 ) {
465 if self.members.get(slot) == Some(reference) {
466 self.learned[slot] = datatype;
467 }
468 }
469
470 fn complete_command_run_internal(
471 &mut self,
472 generation: u64,
473 outcome: Result<(), WriteFailure>,
474 ) -> bool {
475 if generation != self.generation || self.write_status != WriteStatus::IN_PROGRESS {
476 return false;
477 }
478 (self.write_status, self.reliability) = match outcome {
479 Ok(()) => (WriteStatus::SUCCESSFUL, Reliability::NO_FAULT_DETECTED),
480 Err(failure) => (WriteStatus::FAILED, failure.reliability()),
481 };
482 true
483 }
484}
485
486#[cfg(test)]
487mod tests;
488
489#[cfg(test)]
490mod array_tests;
491
492#[cfg(test)]
493mod coercion_tests;
494
495#[cfg(test)]
496mod reliability_tests;
497
498#[cfg(test)]
499mod learned_tests;