aquarium_control/utilities/channel_content.rs
1/* Copyright 2024 Uwe Martin
2
3Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
5The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
7THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8*/
9
10#[cfg(target_os = "linux")]
11use crate::dispatch::messaging::Message;
12use std::fmt;
13
14/// Enum is used for internal thread messaging from control circuits to Controllino.
15/// These values are nested into enum InternalCommand. They represent all actuation devices.
16#[derive(Clone, Debug, PartialEq)]
17pub enum AquariumDevice {
18 // protein skimmer
19 Skimmer,
20
21 // main pump #1
22 MainPump1,
23
24 // main pump #2
25 MainPump2,
26
27 // auxiliary pump #1
28 AuxPump1,
29
30 // auxiliary pump #2
31 AuxPump2,
32
33 // heater
34 Heater,
35
36 // ventilation
37 Ventilation,
38
39 // refill pump
40 RefillPump,
41
42 // feeder motor
43 Feeder,
44
45 // Balling dosing pump #1
46 PeristalticPump1,
47
48 // Balling dosing pump #2
49 PeristalticPump2,
50
51 // Balling dosing pump #3
52 PeristalticPump3,
53
54 // Balling dosing pump #4
55 PeristalticPump4,
56}
57
58impl fmt::Display for AquariumDevice {
59 /// Formats the `AquariumDevice` enum into its human-readable string representation.
60 ///
61 /// This implementation enables `AquariumDevice` variants to be printed directly
62 /// using macros like `println!` or `format!`, returning the name of the device.
63 ///
64 /// # Arguments
65 /// * `f` - A mutable reference to the formatter, as required by the `fmt::Display` trait.
66 ///
67 /// # Returns
68 /// A `fmt::Result` indicating whether the formatting operation was successful.
69 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
70 match self {
71 AquariumDevice::Skimmer => write!(f, "Skimmer"),
72 AquariumDevice::MainPump1 => write!(f, "MainPump1"),
73 AquariumDevice::MainPump2 => write!(f, "MainPump2"),
74 AquariumDevice::AuxPump1 => write!(f, "AuxPump1"),
75 AquariumDevice::AuxPump2 => write!(f, "AuxPump2"),
76 AquariumDevice::Heater => write!(f, "Heater"),
77 AquariumDevice::Ventilation => write!(f, "Ventilation"),
78 AquariumDevice::RefillPump => write!(f, "RefillPump"),
79 AquariumDevice::Feeder => write!(f, "Feeder"),
80 AquariumDevice::PeristalticPump1 => write!(f, "PeristalticPump1"),
81 AquariumDevice::PeristalticPump2 => write!(f, "PeristalticPump2"),
82 AquariumDevice::PeristalticPump3 => write!(f, "PeristalticPump3"),
83 AquariumDevice::PeristalticPump4 => write!(f, "PeristalticPump4"),
84 }
85 }
86}
87
88/// Enum is used for internal thread messaging between various threads
89/// The values indicate which information the thread shall return.
90#[derive(Clone, Debug, PartialEq)]
91pub enum AquariumSignal {
92 /// water temperature in °C
93 WaterTemperature,
94
95 #[allow(non_camel_case_types)]
96 /// pH Value
97 pH,
98
99 /// conductivity im uS/cm
100 Conductivity,
101
102 /// ambient (air) temperature in °C
103 AmbientTemperature,
104
105 /// ambient humidity in %
106 AmbientHumidity,
107
108 /// position of tank level switch
109 TankLevelSwitchPosition,
110
111 #[allow(unused)]
112 /// status bit of tank level switch signal
113 TankLevelSwitchInvalid,
114
115 #[allow(unused)]
116 /// stabilized position of tank level switch
117 TankLevelSwitchPositionStabilized,
118
119 #[allow(unused)]
120 /// heating control status
121 HeatingControlStatus,
122
123 #[allow(unused)]
124 /// ventilation control status
125 VentilationControlStatus,
126
127 #[allow(unused)]
128 /// refill pump status
129 RefillPumpStatus,
130
131 #[cfg(test)]
132 MockInvalidSignal,
133}
134
135impl AquariumSignal {
136 /// Formats a given floating-point signal value into a `String` based on the specific `AquariumSignal` type.
137 ///
138 /// This function applies specific formatting rules (e.g., number of decimal places)
139 /// for common aquarium sensor signals to ensure consistent output strings,
140 /// suitable for display or logging.
141 ///
142 /// # Arguments
143 /// * `signal` - The `f32` floating-point value of the signal to be formatted.
144 ///
145 /// # Returns
146 /// A `String` representation of the signal, formatted according to its `AquariumSignal` type.
147 /// For example, `WaterTemperature` is formatted to one decimal place, `pH` to two, etc.
148 pub fn output_file_string(&self, signal: f32) -> String {
149 match self {
150 AquariumSignal::WaterTemperature => format!("{signal:.1}"),
151 AquariumSignal::Conductivity => format!("{signal:.0}"),
152 AquariumSignal::pH => format!("{signal:.2}"),
153 AquariumSignal::AmbientTemperature => format!("{signal:.1}"),
154 AquariumSignal::AmbientHumidity => format!("{signal:.1}"),
155 #[cfg(all(target_os = "linux", feature = "target_hw", test))]
156 AquariumSignal::MockInvalidSignal => "MockInvalid".to_string(),
157 _ => signal.to_string(),
158 }
159 }
160
161 /// Defines the sequence of measurement used by AtlasScientific
162 #[allow(unused)] // used in conditionally compiled code
163 pub fn get_next_atlas_scientific_signal(&self) -> AquariumSignal {
164 match self {
165 AquariumSignal::WaterTemperature => AquariumSignal::pH,
166 AquariumSignal::pH => AquariumSignal::Conductivity,
167 AquariumSignal::Conductivity => AquariumSignal::WaterTemperature,
168 _ => AquariumSignal::WaterTemperature, // default value
169 }
170 }
171}
172
173impl fmt::Display for AquariumSignal {
174 /// Formats the `AquariumSignal` enum into its human-readable string representation.
175 ///
176 /// This implementation enables `AquariumSignal` variants to be printed directly
177 /// using macros like `println!` or `format!`, returning the descriptive name of the signal.
178 ///
179 /// # Arguments
180 /// * `f` - A mutable reference to the formatter, as required by the `fmt::Display` trait.
181 ///
182 /// # Returns
183 /// A `fmt::Result` indicating whether the formatting operation was successful.
184 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185 match self {
186 AquariumSignal::WaterTemperature => write!(f, "WaterTemperature"),
187 AquariumSignal::pH => write!(f, "pH"),
188 AquariumSignal::Conductivity => write!(f, "Conductivity"),
189 AquariumSignal::AmbientTemperature => write!(f, "AmbientTemperature"),
190 AquariumSignal::AmbientHumidity => write!(f, "AmbientHumidity"),
191 AquariumSignal::TankLevelSwitchPosition => write!(f, "TankLevelSwitchPosition"),
192 AquariumSignal::TankLevelSwitchInvalid => write!(f, "TankLevelSwitchInvalid"),
193 AquariumSignal::TankLevelSwitchPositionStabilized => {
194 write!(f, "TankLevelSwitchPositionStabilized")
195 }
196 AquariumSignal::HeatingControlStatus => write!(f, "HeatingControlStatus"),
197 AquariumSignal::VentilationControlStatus => write!(f, "VentilationControlStatus"),
198 AquariumSignal::RefillPumpStatus => write!(f, "RefillPumpStatus"),
199
200 #[cfg(test)]
201 AquariumSignal::MockInvalidSignal => write!(f, "MockInvalidSignal"),
202 }
203 }
204}
205
206/// Enum is used for internal thread messaging.
207/// Note: Relay state and device state do not necessarily correlate.
208#[derive(Clone, Debug, PartialEq)]
209pub enum InternalCommand {
210 /// Switch on a device
211 SwitchOn(AquariumDevice),
212
213 /// Switch off a device
214 SwitchOff(AquariumDevice),
215
216 /// Pulse a device (switch on, pause, switch off)
217 #[allow(dead_code)]
218 Pulse(AquariumDevice, u16),
219
220 /// Set a relay
221 SetRelay(u16),
222
223 /// Unset a relay
224 UnsetRelay(u16),
225
226 /// Pulse a relay (switch on, pause, switch off)
227 #[allow(dead_code)]
228 PulseRelay(u16, u16),
229
230 /// Request a signal
231 RequestSignal(AquariumSignal),
232
233 #[allow(dead_code)]
234 /// Reset existing errors (used for refill control)
235 ResetAllErrors,
236
237 /// Quit the application - first step to terminate the application
238 Quit,
239
240 /// Terminate - second step to terminating the application
241 Terminate,
242
243 /// ScheduleCheck - request permission to run based on time of day
244 ScheduleCheck,
245
246 #[allow(dead_code)]
247 /// (Re-)start controller (Balling, Ventilation, Refill, Heating)
248 Start,
249
250 #[allow(dead_code)]
251 /// (Temporarily) stop controller (Balling, Ventilation, Refill, Heating)
252 Stop,
253
254 #[allow(dead_code)]
255 /// Execute a (feed) profile with a given ID
256 Execute(i32),
257
258 #[allow(dead_code)]
259 /// Default value used when unknown command is received via IPC-message
260 Unknown,
261}
262
263impl fmt::Display for InternalCommand {
264 /// Formats the `InternalCommand` enum into its human-readable string representation.
265 ///
266 /// This implementation allows `InternalCommand` variants to be displayed directly
267 /// using macros like `println!` or `format!`, returning the name of the command.
268 /// For variants that hold data (e.g., `SwitchOn(_)`), only the command name is displayed,
269 /// not the associated data.
270 ///
271 /// # Arguments
272 /// * `f` - A mutable reference to the formatter, as required by the `fmt::Display` trait.
273 ///
274 /// # Returns
275 /// A `fmt::Result` indicating whether the formatting operation was successful.
276 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277 match self {
278 InternalCommand::SwitchOn(_) => write!(f, "SwitchOn"),
279 InternalCommand::SwitchOff(_) => write!(f, "SwitchOff"),
280 InternalCommand::Pulse(_, _) => write!(f, "Pulse"),
281 InternalCommand::SetRelay(_) => write!(f, "SetRelay"),
282 InternalCommand::UnsetRelay(_) => write!(f, "UnsetRelay"),
283 InternalCommand::PulseRelay(_, _) => write!(f, "PulseRelay"),
284 InternalCommand::RequestSignal(_) => write!(f, "RequestSignal"),
285 InternalCommand::ResetAllErrors => write!(f, "ResetAllErrors"),
286 InternalCommand::Quit => write!(f, "Quit"),
287 InternalCommand::Terminate => write!(f, "Terminate"),
288 InternalCommand::ScheduleCheck => write!(f, "ScheduleCheck"),
289 InternalCommand::Start => write!(f, "Start"),
290 InternalCommand::Stop => write!(f, "Stop"),
291 InternalCommand::Execute(_) => write!(f, "Execute"),
292 InternalCommand::Unknown => write!(f, "Unknown"),
293 }
294 }
295}
296
297#[cfg(target_os = "linux")]
298impl InternalCommand {
299 /// Converts contents of a `Message` into an `InternalCommand`.
300 pub fn from_msg(msg: &Message) -> Self {
301 match msg.command {
302 // Only convert variants that are implemented.
303 1 => InternalCommand::ResetAllErrors,
304 3 => InternalCommand::Stop,
305 4 => InternalCommand::Start,
306 10 => InternalCommand::Execute(msg.command_param1),
307 _ => InternalCommand::Unknown,
308 }
309 }
310
311 #[cfg(test)]
312 pub fn to_numeric(&self) -> i32 {
313 match self {
314 InternalCommand::ResetAllErrors => 1,
315 InternalCommand::Stop => 3,
316 InternalCommand::Start => 4,
317 InternalCommand::Execute(_id) => 10,
318 _ => -1,
319 }
320 }
321}
322
323/// Enum is used for representing the relay states.
324#[derive(PartialEq, Debug)]
325pub enum ActuatorState {
326 /// The actuator is on.
327 On,
328
329 /// The actuator is off.
330 Off,
331
332 /// The application has not yet sent a command to the actuator.
333 Undefined,
334}