bacnet_objects/life_safety/
reset.rs1use alloc::sync::Arc;
4
5use bacnet_types::enums::{
6 ErrorClass, ErrorCode, LifeSafetyOperation, LifeSafetyState, SilencedState,
7};
8use bacnet_types::error::Error;
9use bacnet_types::primitives::ObjectIdentifier;
10
11use crate::traits::LifeSafetyOperationEffect;
12
13use super::{
14 LifeSafetyPointObject, LifeSafetyZoneObject, life_safety_error, valid_life_safety_state,
15};
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct LifeSafetyPointResetContext {
23 pub object_identifier: ObjectIdentifier,
25 pub operation: LifeSafetyOperation,
27 pub present_value: LifeSafetyState,
29 pub tracking_value: LifeSafetyState,
31 pub silenced: SilencedState,
33 pub operation_expected: LifeSafetyOperation,
35}
36
37#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
42pub struct LifeSafetyPointResetCommit {
43 pub present_value: Option<LifeSafetyState>,
45 pub tracking_value: Option<LifeSafetyState>,
49 pub silenced: Option<SilencedState>,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub struct LifeSafetyZoneResetContext {
59 pub object_identifier: ObjectIdentifier,
61 pub operation: LifeSafetyOperation,
63 pub present_value: LifeSafetyState,
65 pub tracking_value: LifeSafetyState,
67 pub silenced: SilencedState,
69 pub operation_expected: LifeSafetyOperation,
71}
72
73#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
78pub struct LifeSafetyZoneResetCommit {
79 pub present_value: Option<LifeSafetyState>,
81 pub tracking_value: Option<LifeSafetyState>,
85 pub silenced: Option<SilencedState>,
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
93pub enum LifeSafetyResetError {
94 UnsupportedVariant,
96 InvalidOperationInThisState,
98 ServiceRequestDenied,
100}
101
102pub type LifeSafetyPointResetExecutor = Arc<
110 dyn Fn(&LifeSafetyPointResetContext) -> Result<LifeSafetyPointResetCommit, LifeSafetyResetError>
111 + Send
112 + Sync,
113>;
114
115pub type LifeSafetyZoneResetExecutor = Arc<
123 dyn Fn(&LifeSafetyZoneResetContext) -> Result<LifeSafetyZoneResetCommit, LifeSafetyResetError>
124 + Send
125 + Sync,
126>;
127
128pub(super) fn is_reset_operation(operation: LifeSafetyOperation) -> bool {
129 matches!(
130 operation,
131 LifeSafetyOperation::RESET
132 | LifeSafetyOperation::RESET_ALARM
133 | LifeSafetyOperation::RESET_FAULT
134 )
135}
136
137fn reset_error(error: LifeSafetyResetError) -> Error {
138 match error {
139 LifeSafetyResetError::UnsupportedVariant => {
140 life_safety_error(ErrorCode::VALUE_OUT_OF_RANGE)
141 }
142 LifeSafetyResetError::InvalidOperationInThisState => {
143 life_safety_error(ErrorCode::INVALID_OPERATION_IN_THIS_STATE)
144 }
145 LifeSafetyResetError::ServiceRequestDenied => {
146 Error::protocol(ErrorClass::SERVICES, ErrorCode::SERVICE_REQUEST_DENIED)
147 }
148 }
149}
150
151fn optional_functionality_error() -> Error {
152 life_safety_error(ErrorCode::OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED)
153}
154
155fn invalid_operation_error() -> Error {
156 life_safety_error(ErrorCode::INVALID_OPERATION_IN_THIS_STATE)
157}
158
159fn commit_value_error() -> Error {
160 life_safety_error(ErrorCode::VALUE_OUT_OF_RANGE)
161}
162
163fn valid_silenced_state(state: SilencedState) -> bool {
164 SilencedState::ALL_NAMED
165 .iter()
166 .any(|&(_, named)| named == state)
167}
168
169impl LifeSafetyPointObject {
170 pub(super) fn apply_reset_operation(
171 &mut self,
172 operation: LifeSafetyOperation,
173 ) -> Result<LifeSafetyOperationEffect, Error> {
174 if !is_reset_operation(operation) || self.operation_expected != operation {
175 return Err(invalid_operation_error());
176 }
177 let executor = self
178 .reset_executor
179 .clone()
180 .ok_or_else(optional_functionality_error)?;
181 let context = LifeSafetyPointResetContext {
182 object_identifier: self.oid,
183 operation,
184 present_value: self.present_value,
185 tracking_value: self.tracking_value,
186 silenced: self.silenced,
187 operation_expected: self.operation_expected,
188 };
189 let Some(result) = crate::catch_panic(|| executor(&context)) else {
190 return Err(reset_error(LifeSafetyResetError::ServiceRequestDenied));
191 };
192 let commit = result.map_err(reset_error)?;
193
194 if commit
195 .present_value
196 .is_some_and(|value| !valid_life_safety_state(value))
197 || commit
198 .tracking_value
199 .is_some_and(|value| !valid_life_safety_state(value))
200 || commit
201 .silenced
202 .is_some_and(|value| !valid_silenced_state(value))
203 {
204 return Err(commit_value_error());
205 }
206
207 if let Some(value) = commit.present_value {
208 self.present_value = value;
209 }
210 if let Some(value) = commit.tracking_value {
211 self.simulation().track(value);
212 }
213 if let Some(value) = commit.silenced {
214 self.silenced = value;
215 }
216 self.operation_expected = LifeSafetyOperation::NONE;
217 Ok(LifeSafetyOperationEffect::Applied)
218 }
219}
220
221impl LifeSafetyZoneObject {
222 pub(super) fn apply_reset_operation(
223 &mut self,
224 operation: LifeSafetyOperation,
225 ) -> Result<LifeSafetyOperationEffect, Error> {
226 if !is_reset_operation(operation) || self.operation_expected != operation {
227 return Err(invalid_operation_error());
228 }
229 let executor = self
230 .reset_executor
231 .clone()
232 .ok_or_else(optional_functionality_error)?;
233 let context = LifeSafetyZoneResetContext {
234 object_identifier: self.oid,
235 operation,
236 present_value: self.present_value,
237 tracking_value: self.tracking_value,
238 silenced: self.silenced,
239 operation_expected: self.operation_expected,
240 };
241 let Some(result) = crate::catch_panic(|| executor(&context)) else {
242 return Err(reset_error(LifeSafetyResetError::ServiceRequestDenied));
243 };
244 let commit = result.map_err(reset_error)?;
245
246 if commit
247 .present_value
248 .is_some_and(|value| !valid_life_safety_state(value))
249 || commit
250 .tracking_value
251 .is_some_and(|value| !valid_life_safety_state(value))
252 || commit
253 .silenced
254 .is_some_and(|value| !valid_silenced_state(value))
255 {
256 return Err(commit_value_error());
257 }
258
259 if let Some(value) = commit.present_value {
260 self.present_value = value;
261 }
262 if let Some(value) = commit.tracking_value {
263 self.simulation().track(value);
264 }
265 if let Some(value) = commit.silenced {
266 self.silenced = value;
267 }
268 self.operation_expected = LifeSafetyOperation::NONE;
269 Ok(LifeSafetyOperationEffect::Applied)
270 }
271}