bacnet_objects/
load_control.rs1use crate::prelude::*;
7use alloc::borrow::Cow;
8use bacnet_encoding::constructed::{decode_shed_level, encode_shed_level};
9use bacnet_types::constructed::BACnetShedLevel;
10use bacnet_types::enums::{EventState, ObjectType, PropertyIdentifier, Reliability};
11use bacnet_types::error::Error;
12use bacnet_types::primitives::{Date, ObjectIdentifier, PropertyValue, StatusFlags, Time};
13use bytes::BytesMut;
14
15use crate::common::{self, read_common_properties};
16use crate::traits::BACnetObject;
17
18mod metadata;
19#[cfg(test)]
20mod tests;
21
22pub struct LoadControlObject {
31 oid: ObjectIdentifier,
32 name: String,
33 description: String,
34 present_value: u32,
37 requested_shed_level: BACnetShedLevel,
38 expected_shed_level: BACnetShedLevel,
39 actual_shed_level: BACnetShedLevel,
40 shed_duration: u64,
41 start_time: (Date, Time),
42 status_flags: StatusFlags,
43 event_state: EventState,
45 reliability: Reliability,
46}
47
48impl LoadControlObject {
49 pub fn new(instance: u32, name: impl Into<String>) -> Result<Self, Error> {
54 let oid = ObjectIdentifier::new(ObjectType::LOAD_CONTROL, instance)?;
55 Ok(Self {
56 oid,
57 name: name.into(),
58 description: String::new(),
59 present_value: 0,
60 requested_shed_level: BACnetShedLevel::Level(0),
61 expected_shed_level: BACnetShedLevel::Level(0),
62 actual_shed_level: BACnetShedLevel::Level(0),
63 shed_duration: 0,
64 start_time: (
65 Date {
66 year: 0xFF,
67 month: 0xFF,
68 day: 0xFF,
69 day_of_week: 0xFF,
70 },
71 Time {
72 hour: 0xFF,
73 minute: 0xFF,
74 second: 0xFF,
75 hundredths: 0xFF,
76 },
77 ),
78 status_flags: StatusFlags::empty(),
79 event_state: EventState::NORMAL,
80 reliability: Reliability::NO_FAULT_DETECTED,
81 })
82 }
83
84 pub fn set_requested_shed_level(&mut self, level: BACnetShedLevel) -> Result<(), Error> {
94 match level {
95 BACnetShedLevel::Percent(percent) if percent > 100 => {
96 return Err(common::value_out_of_range_error());
97 }
98 BACnetShedLevel::Amount(amount) => {
99 common::reject_non_finite(amount)?;
100 if amount < 0.0 {
101 return Err(common::value_out_of_range_error());
102 }
103 }
104 _ => {}
105 }
106 let default = choice_default(&level);
107 self.expected_shed_level = default.clone();
108 self.actual_shed_level = default;
109 self.requested_shed_level = level;
110 Ok(())
111 }
112
113 pub fn set_actual_shed_level(&mut self, level: BACnetShedLevel) -> Result<(), Error> {
119 if core::mem::discriminant(&level) != core::mem::discriminant(&self.requested_shed_level) {
120 return Err(common::value_out_of_range_error());
121 }
122 if let BACnetShedLevel::Amount(amount) = level {
123 common::reject_non_finite(amount)?;
124 }
125 self.actual_shed_level = level;
126 Ok(())
127 }
128}
129
130fn choice_default(level: &BACnetShedLevel) -> BACnetShedLevel {
133 match level {
134 BACnetShedLevel::Percent(_) => BACnetShedLevel::Percent(100),
135 BACnetShedLevel::Level(_) => BACnetShedLevel::Level(0),
136 BACnetShedLevel::Amount(_) => BACnetShedLevel::Amount(0.0),
137 }
138}
139
140fn shed_level_value(level: &BACnetShedLevel) -> PropertyValue {
142 let mut buf = BytesMut::new();
143 encode_shed_level(&mut buf, level);
144 PropertyValue::ApplicationData(buf.to_vec())
145}
146
147fn decode_shed_level_write(value: PropertyValue) -> Result<BACnetShedLevel, Error> {
152 let PropertyValue::ApplicationData(bytes) = value else {
153 return Err(common::invalid_data_type_error());
154 };
155 match decode_shed_level(&bytes, 0) {
156 Ok((level, end)) if end == bytes.len() => Ok(level),
157 _ => Err(common::invalid_data_type_error()),
158 }
159}
160
161impl BACnetObject for LoadControlObject {
162 fn object_identifier(&self) -> ObjectIdentifier {
163 self.oid
164 }
165
166 fn object_name(&self) -> &str {
167 &self.name
168 }
169
170 fn read_property(
171 &self,
172 property: PropertyIdentifier,
173 array_index: Option<u32>,
174 ) -> Result<PropertyValue, Error> {
175 if let Some(result) =
178 read_common_properties!(self, property, array_index, no_out_of_service)
179 {
180 return result;
181 }
182 match property {
183 p if p == PropertyIdentifier::OBJECT_TYPE => {
184 Ok(PropertyValue::Enumerated(ObjectType::LOAD_CONTROL.to_raw()))
185 }
186 p if p == PropertyIdentifier::PRESENT_VALUE => {
187 Ok(PropertyValue::Enumerated(self.present_value))
188 }
189 p if p == PropertyIdentifier::REQUESTED_SHED_LEVEL => {
190 Ok(shed_level_value(&self.requested_shed_level))
191 }
192 p if p == PropertyIdentifier::EXPECTED_SHED_LEVEL => {
193 Ok(shed_level_value(&self.expected_shed_level))
194 }
195 p if p == PropertyIdentifier::ACTUAL_SHED_LEVEL => {
196 Ok(shed_level_value(&self.actual_shed_level))
197 }
198 p if p == PropertyIdentifier::SHED_DURATION => {
199 Ok(PropertyValue::Unsigned(self.shed_duration))
200 }
201 p if p == PropertyIdentifier::START_TIME => Ok(PropertyValue::List(vec![
202 PropertyValue::Date(self.start_time.0),
203 PropertyValue::Time(self.start_time.1),
204 ])),
205 p if p == PropertyIdentifier::EVENT_STATE => {
206 Ok(PropertyValue::Enumerated(self.event_state.to_raw()))
207 }
208 _ => Err(common::unknown_property_error()),
209 }
210 }
211
212 fn write_property(
213 &mut self,
214 property: PropertyIdentifier,
215 _array_index: Option<u32>,
216 value: PropertyValue,
217 _priority: Option<u8>,
218 ) -> Result<(), Error> {
219 if let Some(result) = common::write_description(&mut self.description, property, &value) {
220 return result;
221 }
222 match property {
223 p if p == PropertyIdentifier::SHED_DURATION => {
224 if let PropertyValue::Unsigned(v) = value {
225 self.shed_duration = v;
226 Ok(())
227 } else {
228 Err(common::invalid_data_type_error())
229 }
230 }
231 p if p == PropertyIdentifier::REQUESTED_SHED_LEVEL => {
232 self.set_requested_shed_level(decode_shed_level_write(value)?)
233 }
234 _ => Err(crate::common::unhandled_write_error(
235 self.property_metadata().as_ref(),
236 property,
237 _array_index,
238 )),
239 }
240 }
241
242 fn property_metadata(&self) -> Cow<'_, [crate::property_metadata::PropertyMetadata]> {
243 metadata::for_object(self)
244 }
245
246 fn property_list(&self) -> Cow<'static, [PropertyIdentifier]> {
247 crate::property_metadata::property_list_from_metadata(self.property_metadata().as_ref())
248 }
249
250 fn supports_cov(&self) -> bool {
253 true
254 }
255}