aquarium_control/utilities/proc_ext_req.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
10use log::{error, warn};
11
12#[cfg(not(test))]
13use log::info;
14
15use crate::food::{feed::Feed, food_injection::FoodInjection};
16use crate::recorder::data_logger::DataLogger;
17use crate::relays::relay_manager::RelayManager;
18use crate::utilities::channel_content::InternalCommand;
19
20#[cfg(target_os = "linux")]
21use crate::dispatch::messaging::Messaging;
22use crate::launch::channels::{AquaChannelError, AquaReceiver};
23use crate::mineral::{balling::Balling, mineral_injection::MineralInjection};
24use crate::permission::schedule_check::ScheduleCheck;
25use crate::sensors::dht::Dht;
26use crate::sensors::i2c_interface::I2cInterface;
27use crate::simulator::tcp_communication::TcpCommunication;
28use crate::watchmen::memory::Memory;
29use crate::watchmen::watchdog::Watchdog;
30use crate::water::{refill::Refill, water_injection::WaterInjection};
31
32/// This trait defines a common interface for processing external requests, such as
33/// commands received from a signal handler or a messaging system. It's designed
34/// to be implemented by various control modules in the system.
35///
36/// The default implementation provided here handles basic "Quit," "Start," and "Stop"
37/// commands, logging warnings for other inapplicable commands and errors for channel
38/// disconnection. This default is used by modules like heating control, ventilation control,
39/// and monitors.
40///
41/// More specialized modules, including Refill control, Data logger, Balling, Food injection,
42/// Water injection, Mineral injection, Schedule check, Controllino, Gpio handler, Feed,
43/// Messaging, and TCP communication, provide their own specific implementations of this trait.
44pub trait ProcessExternalRequestTrait {
45 /// Checks for and processes new commands received from the signal handler and an optional messaging channel.
46 ///
47 /// This function attempts to receive `InternalCommand` messages without blocking from two potential sources:
48 /// a required channel from the signal handler and an optional channel from a messaging system.
49 /// It specifically looks for `Quit` commands from the signal handler and `Start`/`Stop` commands
50 /// from the messaging channel, ignoring other commands and logging warnings for them.
51 /// Channel disconnection errors are also logged.
52 ///
53 /// # Arguments
54 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
55 /// for commands originating from the signal handler.
56 /// * `rx_from_messaging_opt` - An `Option` containing a reference to the receiver end
57 /// of the channel for commands from the messaging system. This channel is optional.
58 ///
59 /// # Returns
60 /// A tuple `(bool, bool, bool)` indicating the status of specific commands received:
61 /// - The first `bool` is `true` if a `Quit` command was received from the signal handler; otherwise `false`.
62 /// - The second `bool` is `true` if a `Start` command was received from messaging; otherwise `false`.
63 /// - The third `bool` is `true` if a `Stop` command was received from messaging; otherwise `false`.
64 fn process_external_request(
65 &mut self,
66 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
67 rx_from_messaging_opt: Option<&mut AquaReceiver<InternalCommand>>,
68 ) -> (bool, bool, bool) {
69 let quit_command_received = match rx_from_signal_handler.try_recv() {
70 Ok(c) => match c {
71 InternalCommand::Quit => true,
72 _ => {
73 warn!(
74 target: module_path!(),
75 "ProcessExternalRequestTrait: ignoring inapplicable command from signal handler."
76 );
77 false
78 }
79 },
80 Err(e) => {
81 match e {
82 #[cfg(feature = "debug_channels")]
83 AquaChannelError::Full => { /* Not applicable. Do nothing */ }
84 AquaChannelError::Empty => { /* empty buffer - no action required */ }
85 AquaChannelError::Disconnected => {
86 #[cfg(all(not(test), not(feature = "debug_signal_handler")))]
87 error!(
88 target: module_path!(),
89 "receiving command from signal handler failed ({e:?})"
90 );
91 }
92 }
93 false
94 }
95 };
96
97 let (start_command_received, stop_command_received) = match rx_from_messaging_opt {
98 Some(rx_from_messaging) => {
99 match rx_from_messaging.try_recv() {
100 Ok(c) => match c {
101 InternalCommand::Start => (true, false),
102 InternalCommand::Stop => (false, true),
103 _ => {
104 warn!(
105 target: module_path!(),
106 "ProcessExternalRequestTrait: ignoring inapplicable command from messaging."
107 );
108 (false, false)
109 }
110 },
111 Err(e) => {
112 match e {
113 #[cfg(feature = "debug_channels")]
114 AquaChannelError::Full => { /* Not applicable. Do nothing */ }
115 AquaChannelError::Empty => { /* empty buffer - no action required */ }
116 AquaChannelError::Disconnected => {
117 #[cfg(not(test))]
118 error!(
119 target: module_path!(),
120 "(standard) receiving command from messaging failed ({e:?})"
121 );
122 }
123 }
124 (false, false)
125 }
126 }
127 }
128 None => (false, false),
129 };
130
131 (
132 quit_command_received,
133 start_command_received,
134 stop_command_received,
135 )
136 }
137}
138
139impl ProcessExternalRequestTrait for Refill {
140 /// Checks for and processes new commands relevant to the Refill control module from external channels.
141 ///
142 /// This is the specialized implementation of `ProcessExternalRequestTrait` for the `Refill` module.
143 /// In addition to handling standard "Quit", "Start", and "Stop" commands, it specifically
144 /// intercepts the `InternalCommand::ResetAllErrors` command from the messaging channel
145 /// to reset the refill control's internal error states.
146 ///
147 /// # Arguments
148 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
149 /// for commands originating from the signal handler.
150 /// * `rx_from_messaging_opt` - An `Option` containing a reference to the receiver end
151 /// of the channel for commands from the messaging system. This channel is optional.
152 ///
153 /// # Returns
154 /// A tuple `(bool, bool, bool)` indicating the status of specific commands received:
155 /// - The first `bool` is `true` if a `Quit` command was received from the signal handler; otherwise `false`.
156 /// - The second `bool` is `true` if a `Start` command was received from messaging; otherwise `false`.
157 /// - The third `bool` is `true` if a `Stop` command was received from messaging; otherwise `false`.
158 fn process_external_request(
159 &mut self,
160 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
161 rx_from_messaging_opt: Option<&mut AquaReceiver<InternalCommand>>,
162 ) -> (bool, bool, bool) {
163 let quit_command_received = match rx_from_signal_handler.try_recv() {
164 Ok(c) => match c {
165 InternalCommand::Quit => true,
166 _ => {
167 warn!(
168 target: module_path!(),
169 "ignoring inapplicable command from signal handler."
170 );
171 false
172 }
173 },
174 Err(e) => {
175 match e {
176 #[cfg(feature = "debug_channels")]
177 AquaChannelError::Full => { /* Not applicable. Do nothing */ }
178 AquaChannelError::Empty => { /* empty buffer - no action required */ }
179 AquaChannelError::Disconnected => {
180 #[cfg(not(test))]
181 error!(
182 target: module_path!(),
183 "receiving command from signal handler failed ({e:?})"
184 );
185 }
186 }
187 false
188 }
189 };
190
191 let (start_command_received, stop_command_received) = match rx_from_messaging_opt {
192 Some(rx_from_messaging) => {
193 match rx_from_messaging.try_recv() {
194 Ok(c) => match c {
195 InternalCommand::Start => (true, false),
196 InternalCommand::Stop => (false, true),
197 InternalCommand::ResetAllErrors => {
198 #[cfg(not(test))]
199 info!(
200 target: module_path!(),
201 "Received reset command for errors of refill control."
202 );
203 self.refill_errors.reset_all_errors();
204 (false, false)
205 }
206 _ => {
207 warn!(
208 target: module_path!(),
209 "ignoring inapplicable command from messaging."
210 );
211 (false, false)
212 }
213 },
214 Err(e) => {
215 match e {
216 #[cfg(feature = "debug_channels")]
217 AquaChannelError::Full => { /* Not applicable. Do nothing */ }
218 AquaChannelError::Empty => { /* empty buffer - no action required */ }
219 AquaChannelError::Disconnected => {
220 #[cfg(not(test))]
221 error!(
222 target: module_path!(),
223 "(refill) receiving command from messaging failed ({e:?})"
224 );
225 }
226 }
227 (false, false)
228 }
229 }
230 }
231 None => (false, false),
232 };
233
234 (
235 quit_command_received,
236 start_command_received,
237 stop_command_received,
238 )
239 }
240}
241
242/// Checks for new commands from the signal handler, specifically for modules that don't use messaging.
243///
244/// This associated function provides a streamlined way to process external requests
245/// for components that only listen to the signal handler channel. It attempts to
246/// receive without blocking `InternalCommand` messages and specifically looks for
247/// a `Quit` command.
248///
249/// # Arguments
250/// * `rx_from_signal_handler` - A reference to the receiver end of the channel
251/// for commands originating from the signal handler.
252///
253/// # Returns
254/// A tuple `(bool, bool, bool)` indicating the status of commands received:
255/// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
256/// - The second `bool` is always `false` as "Start" commands are not processed by this function.
257/// - The third `bool` is always `false` as "Stop" commands are not processed by this function.
258fn process_external_request_without_messaging(
259 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
260 sender: String,
261) -> (bool, bool, bool) {
262 let quit_command_received = match rx_from_signal_handler.try_recv() {
263 Ok(c) => match c {
264 InternalCommand::Quit => true,
265 _ => {
266 warn!(
267 target: module_path!(),
268 "ignoring inapplicable command from signal handler."
269 );
270 false
271 }
272 },
273 Err(e) => {
274 match e {
275 #[cfg(feature = "debug_channels")]
276 AquaChannelError::Full => { /* Not applicable. Do nothing */ }
277 AquaChannelError::Empty => { /* empty buffer - no action required */ }
278 AquaChannelError::Disconnected => {
279 error!(
280 target: module_path!(),
281 "({sender}) receiving command from signal handler failed ({e:?})"
282 );
283 }
284 }
285 false
286 }
287 };
288
289 (quit_command_received, false, false)
290}
291
292impl ProcessExternalRequestTrait for DataLogger {
293 /// Checks for and processes new commands relevant to the Data Logger module from external channels.
294 ///
295 /// This is the specialized implementation of `ProcessExternalRequestTrait` for the `DataLogger`.
296 /// It delegates directly to `process_external_request_without_messaging`, indicating
297 /// that the `DataLogger` only processes commands from the signal handler and
298 /// ignores any input from a messaging channel.
299 ///
300 /// # Arguments
301 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
302 /// for commands originating from the signal handler.
303 /// * `_` - This parameter is ignored, as the `DataLogger` does not process
304 /// messages from a messaging channel.
305 ///
306 /// # Returns
307 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
308 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
309 /// - The second `bool` is always `false` (no "Start" commands processed).
310 /// - The third `bool` is always `false` (no "Stop" commands processed).
311 fn process_external_request(
312 &mut self,
313 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
314 _: Option<&mut AquaReceiver<InternalCommand>>,
315 ) -> (bool, bool, bool) {
316 process_external_request_without_messaging(rx_from_signal_handler, "DataLogger".to_string())
317 }
318}
319
320impl ProcessExternalRequestTrait for Dht {
321 /// Checks for and processes new commands relevant to the Dht module from external channels.
322 ///
323 /// This is the specialized implementation of `ProcessExternalRequestTrait` for the `DataLogger`.
324 /// It delegates directly to `process_external_request_without_messaging`, indicating
325 /// that the `DataLogger` only processes commands from the signal handler and
326 /// ignores any input from a messaging channel.
327 ///
328 /// # Arguments
329 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
330 /// for commands originating from the signal handler.
331 /// * `_` - This parameter is ignored, as the `DataLogger` does not process
332 /// messages from a messaging channel.
333 ///
334 /// # Returns
335 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
336 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
337 /// - The second `bool` is always `false` (no "Start" commands processed).
338 /// - The third `bool` is always `false` (no "Stop" commands processed).
339 fn process_external_request(
340 &mut self,
341 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
342 _: Option<&mut AquaReceiver<InternalCommand>>,
343 ) -> (bool, bool, bool) {
344 process_external_request_without_messaging(rx_from_signal_handler, "Dht".to_string())
345 }
346}
347
348impl ProcessExternalRequestTrait for FoodInjection {
349 /// Checks for and processes new commands relevant to the Food Injection module from external channels.
350 ///
351 /// This is the specialized implementation of `ProcessExternalRequestTrait` for `FoodInjection`.
352 /// It delegates directly to `process_external_request_without_messaging`, indicating
353 /// that the `FoodInjection` module only processes commands from the signal handler
354 /// and ignores any input from a messaging channel.
355 ///
356 /// # Arguments
357 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
358 /// for commands originating from the signal handler.
359 /// * `_` - This parameter is ignored, as the `FoodInjection` module does not process
360 /// messages from a messaging channel.
361 ///
362 /// # Returns
363 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
364 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
365 /// - The second `bool` is always `false` (no "Start" commands processed).
366 /// - The third `bool` is always `false` (no "Stop" commands processed).
367 fn process_external_request(
368 &mut self,
369 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
370 _: Option<&mut AquaReceiver<InternalCommand>>,
371 ) -> (bool, bool, bool) {
372 process_external_request_without_messaging(
373 rx_from_signal_handler,
374 "FoodInjection".to_string(),
375 )
376 }
377}
378
379impl ProcessExternalRequestTrait for MineralInjection {
380 /// Checks for and processes new commands relevant to the Mineral Injection module from external channels.
381 ///
382 /// This is the specialized implementation of `ProcessExternalRequestTrait` for `MineralInjection`.
383 /// It delegates directly to `process_external_request_without_messaging`, indicating
384 /// that the `MineralInjection` module only processes commands from the signal handler
385 /// and ignores any input from a messaging channel.
386 ///
387 /// # Arguments
388 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
389 /// for commands originating from the signal handler.
390 /// * `_` - This parameter is ignored, as the `MineralInjection` module does not process
391 /// messages from a messaging channel.
392 ///
393 /// # Returns
394 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
395 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
396 /// - The second `bool` is always `false` (no "Start" commands processed).
397 /// - The third `bool` is always `false` (no "Stop" commands processed).
398 fn process_external_request(
399 &mut self,
400 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
401 _: Option<&mut AquaReceiver<InternalCommand>>,
402 ) -> (bool, bool, bool) {
403 process_external_request_without_messaging(
404 rx_from_signal_handler,
405 "MineralInjection".to_string(),
406 )
407 }
408}
409
410impl ProcessExternalRequestTrait for WaterInjection {
411 /// Checks for and processes new commands relevant to the Water Injection module from external channels.
412 ///
413 /// This is the specialized implementation of `ProcessExternalRequestTrait` for `WaterInjection`.
414 /// It delegates directly to `process_external_request_without_messaging`, indicating
415 /// that the `WaterInjection` module only processes commands from the signal handler
416 /// and ignores any input from a messaging channel.
417 ///
418 /// # Arguments
419 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
420 /// for commands originating from the signal handler.
421 /// * `_` - This parameter is ignored, as the `WaterInjection` module does not process
422 /// messages from a messaging channel.
423 ///
424 /// # Returns
425 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
426 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
427 /// - The second `bool` is always `false` (no "Start" commands processed).
428 /// - The third `bool` is always `false` (no "Stop" commands processed).
429 fn process_external_request(
430 &mut self,
431 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
432 _: Option<&mut AquaReceiver<InternalCommand>>,
433 ) -> (bool, bool, bool) {
434 process_external_request_without_messaging(
435 rx_from_signal_handler,
436 "WaterInjection".to_string(),
437 )
438 }
439}
440
441impl ProcessExternalRequestTrait for I2cInterface {
442 /// Checks for and processes new commands relevant to the I2cInterface module from external channels.
443 ///
444 /// This is the specialized implementation of `ProcessExternalRequestTrait` for `I2cInterface`.
445 /// It delegates directly to `process_external_request_without_messaging`, indicating
446 /// that the `I2cInterface` module only processes commands from the signal handler
447 /// and ignores any input from a messaging channel.
448 ///
449 /// # Arguments
450 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
451 /// for commands originating from the signal handler.
452 /// * `_` - This parameter is ignored, as the `I2cInterface` module does not process
453 /// messages from a messaging channel.
454 ///
455 /// # Returns
456 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
457 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
458 /// - The second `bool` is always `false` (no "Start" commands processed).
459 /// - The third `bool` is always `false` (no "Stop" commands processed).
460 fn process_external_request(
461 &mut self,
462 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
463 _: Option<&mut AquaReceiver<InternalCommand>>,
464 ) -> (bool, bool, bool) {
465 process_external_request_without_messaging(
466 rx_from_signal_handler,
467 "I2cInterface".to_string(),
468 )
469 }
470}
471
472impl ProcessExternalRequestTrait for ScheduleCheck {
473 /// Checks for and processes new commands relevant to the Schedule Check module from external channels.
474 ///
475 /// This is the specialized implementation of `ProcessExternalRequestTrait` for `ScheduleCheck`.
476 /// It delegates directly to `process_external_request_without_messaging`, indicating
477 /// that the `ScheduleCheck` module only processes commands from the signal handler
478 /// and ignores any input from a messaging channel.
479 ///
480 /// # Arguments
481 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
482 /// for commands originating from the signal handler.
483 /// * `_` - This parameter is ignored, as the `ScheduleCheck` module does not process
484 /// messages from a messaging channel.
485 ///
486 /// # Returns
487 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
488 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
489 /// - The second `bool` is always `false` (no "Start" commands processed).
490 /// - The third `bool` is always `false` (no "Stop" commands processed).
491 fn process_external_request(
492 &mut self,
493 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
494 _: Option<&mut AquaReceiver<InternalCommand>>,
495 ) -> (bool, bool, bool) {
496 process_external_request_without_messaging(
497 rx_from_signal_handler,
498 "ScheduleCheck".to_string(),
499 )
500 }
501}
502
503impl ProcessExternalRequestTrait for RelayManager {
504 /// Checks for and processes new commands relevant to the Relay Manager module from external channels.
505 ///
506 /// This is the specialized implementation of `ProcessExternalRequestTrait` for `RelayManager`.
507 /// It delegates directly to `process_external_request_without_messaging`, indicating
508 /// that the `RelayManager` (which often interacts with hardware like Controllino)
509 /// only processes commands from the signal handler and ignores any input from a messaging channel.
510 ///
511 /// # Arguments
512 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
513 /// for commands originating from the signal handler.
514 /// * `_` - This parameter is ignored, as the `RelayManager` does not process
515 /// messages from a messaging channel.
516 ///
517 /// # Returns
518 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
519 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
520 /// - The second `bool` is always `false` (no "Start" commands processed).
521 /// - The third `bool` is always `false` (no "Stop" commands processed).
522 fn process_external_request(
523 &mut self,
524 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
525 _: Option<&mut AquaReceiver<InternalCommand>>,
526 ) -> (bool, bool, bool) {
527 process_external_request_without_messaging(
528 rx_from_signal_handler,
529 "RelayManager".to_string(),
530 )
531 }
532}
533
534#[cfg(target_os = "linux")]
535impl ProcessExternalRequestTrait for Messaging {
536 /// Checks for and processes new commands relevant to the Messaging module from external channels.
537 ///
538 /// This is the specialized implementation of `ProcessExternalRequestTrait` for `Messaging`.
539 /// It delegates directly to `process_external_request_without_messaging`, indicating
540 /// that the `Messaging` module only processes commands from the signal handler
541 /// and ignores any input from a separate messaging channel.
542 ///
543 /// # Arguments
544 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
545 /// for commands originating from the signal handler.
546 /// * `_` - This parameter is ignored, as the `Messaging` module does not process
547 /// messages from a secondary messaging channel.
548 ///
549 /// # Returns
550 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
551 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
552 /// - The second `bool` is always `false` (no "Start" commands processed).
553 /// - The third `bool` is always `false` (no "Stop" commands processed).
554 ///
555 /// This code is specific for a Linux operating system because the messaging system
556 /// is only implemented for that platform.
557 fn process_external_request(
558 &mut self,
559 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
560 _: Option<&mut AquaReceiver<InternalCommand>>,
561 ) -> (bool, bool, bool) {
562 process_external_request_without_messaging(rx_from_signal_handler, "Messaging".to_string())
563 }
564}
565
566impl ProcessExternalRequestTrait for TcpCommunication {
567 /// Checks for and processes new commands relevant to the TCP Communication module from external channels.
568 ///
569 /// This is the specialized implementation of `ProcessExternalRequestTrait` for `TcpCommunication`.
570 /// It delegates directly to `process_external_request_without_messaging`, indicating
571 /// that the `TcpCommunication` module primarily processes commands from the signal handler
572 /// and does not use a separate messaging channel for external requests.
573 ///
574 /// # Arguments
575 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
576 /// for commands originating from the signal handler.
577 /// * `_` - This parameter is ignored, as the `TcpCommunication` module does not process
578 /// messages from a messaging channel in this context.
579 ///
580 /// # Returns
581 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
582 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
583 /// - The second `bool` is always `false` (no "Start" commands processed).
584 /// - The third `bool` is always `false` (no "Stop" commands processed).
585 fn process_external_request(
586 &mut self,
587 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
588 _: Option<&mut AquaReceiver<InternalCommand>>,
589 ) -> (bool, bool, bool) {
590 process_external_request_without_messaging(
591 rx_from_signal_handler,
592 "TcpCommunication".to_string(),
593 )
594 }
595}
596
597/// Checks for specific standard commands (Terminate or Quit) from the signal handler channel.
598///
599/// This associated function is a streamlined helper for implementations that
600/// primarily need to react to global termination signals. It attempts
601/// to receive without blocking `InternalCommand` messages and returns `true` if either
602/// a `Terminate` or `Quit` command is found. Other commands are logged as warnings.
603///
604/// # Arguments
605/// * `rx_from_signal_handler` - A reference to the receiver end of the channel
606/// for commands originating from the signal handler.
607///
608/// # Returns
609/// `true` if either a `Terminate` or `Quit` command was received; otherwise `false`.
610fn process_standard_request(rx_from_signal_handler: &mut AquaReceiver<InternalCommand>) -> bool {
611 match rx_from_signal_handler.try_recv() {
612 Ok(c) => match c {
613 InternalCommand::Terminate => true,
614 InternalCommand::Quit => true,
615 _ => {
616 warn!(
617 target: module_path!(),
618 "ignoring inapplicable command {c} from signal handler."
619 );
620 false
621 }
622 },
623 Err(e) => {
624 match e {
625 #[cfg(feature = "debug_channels")]
626 AquaChannelError::Full => { /* Not applicable. Do nothing */ }
627 AquaChannelError::Empty => { /* empty buffer - no action required */ }
628 AquaChannelError::Disconnected => {
629 #[cfg(not(test))]
630 error!(
631 target: module_path!(),
632 "receiving command from signal handler failed ({e:?})"
633 );
634 }
635 }
636 false
637 }
638 }
639}
640
641impl ProcessExternalRequestTrait for Feed {
642 /// Checks for and processes new commands relevant to the Feed control module from external channels.
643 ///
644 /// This is the specialized implementation of `ProcessExternalRequestTrait` for the `Feed` module.
645 /// It handles standard "Terminate" or "Quit" commands from the signal handler.
646 /// From the messaging channel, it specifically processes "Start," "Stop," "ResetAllErrors,"
647 /// and "Execute" commands, updating internal state variables based on these commands.
648 /// Inapplicable commands are ignored with warnings.
649 ///
650 /// # Arguments
651 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
652 /// for commands originating from the signal handler.
653 /// * `rx_from_messaging_opt` - An `Option` containing a reference to the receiver end
654 /// of the channel for commands from the messaging system. This channel is optional.
655 ///
656 /// # Returns
657 /// A tuple `(bool, bool, bool)` indicating the status of specific commands received:
658 /// - The first `bool` is `true` if a `Quit` or `Terminate` command was received from the signal handler; otherwise `false`.
659 /// - The second `bool` is `true` if a `Start` command was received from messaging; otherwise `false`.
660 /// - The third `bool` is `true` if a `Stop` command was received from messaging; otherwise `false`.
661 fn process_external_request(
662 &mut self,
663 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
664 rx_from_messaging_opt: Option<&mut AquaReceiver<InternalCommand>>,
665 ) -> (bool, bool, bool) {
666 let quit_command_received = process_standard_request(rx_from_signal_handler);
667
668 self.execute_command_received = false;
669 self.profile_id_requested = -1;
670 let (start_command_received, stop_command_received, execute_command_received, profile_id) =
671 match rx_from_messaging_opt {
672 Some(rx_from_messaging) => {
673 match rx_from_messaging.try_recv() {
674 Ok(c) => match c {
675 InternalCommand::Start => (true, false, false, 0),
676 InternalCommand::Stop => (false, true, false, 0),
677 InternalCommand::ResetAllErrors => {
678 #[cfg(not(test))]
679 info!(
680 target: module_path!(),
681 "Received reset command for errors of feed control."
682 );
683 self.execution_blocked = false;
684 (false, false, false, 0)
685 }
686 InternalCommand::Execute(c) => (false, false, true, c),
687 _ => {
688 warn!(
689 target: module_path!(),
690 "ignoring inapplicable command {c} from messaging."
691 );
692 (false, false, false, 0)
693 }
694 },
695 Err(e) => {
696 match e {
697 #[cfg(feature = "debug_channels")]
698 AquaChannelError::Full => { /* Not applicable. Do nothing */ }
699 AquaChannelError::Empty => { /* empty buffer - no action required */
700 }
701 AquaChannelError::Disconnected => {
702 #[cfg(not(test))]
703 error!(
704 target: module_path!(),
705 "(feed) - receiving command from messaging failed ({e:?})"
706 );
707 }
708 }
709 (false, false, false, 0)
710 }
711 }
712 }
713 None => (false, false, false, 0),
714 };
715
716 self.execute_command_received = execute_command_received;
717 self.profile_id_requested = profile_id;
718
719 (
720 quit_command_received,
721 start_command_received,
722 stop_command_received,
723 )
724 }
725}
726
727impl ProcessExternalRequestTrait for Balling {
728 /// Checks for and processes new commands relevant to the Balling control module from external channels.
729 ///
730 /// This is the specialized implementation of `ProcessExternalRequestTrait` for the `Balling` module.
731 /// It handles standard "Terminate" or "Quit" commands from the signal handler.
732 /// From the messaging channel, it specifically processes "Start," "Stop," and "Execute" commands,
733 /// updating internal state variables (`execute_command_received`, `pump_id_requested`) based on these commands.
734 /// Inapplicable commands are ignored with warnings.
735 ///
736 /// # Arguments
737 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
738 /// for commands originating from the signal handler.
739 /// * `rx_from_messaging_opt` - An `Option` containing a reference to the receiver end
740 /// of the channel for commands from the messaging system. This channel is optional.
741 ///
742 /// # Returns
743 /// A tuple `(bool, bool, bool)` indicating the status of specific commands received:
744 /// - The first `bool` is `true` if a `Quit` or `Terminate` command was received from the signal handler; otherwise `false`.
745 /// - The second `bool` is `true` if a `Start` command was received from messaging; otherwise `false`.
746 /// - The third `bool` is `true` if a `Stop` command was received from messaging; otherwise `false`.
747 fn process_external_request(
748 &mut self,
749 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
750 rx_from_messaging_opt: Option<&mut AquaReceiver<InternalCommand>>,
751 ) -> (bool, bool, bool) {
752 let quit_command_received = process_standard_request(rx_from_signal_handler);
753
754 self.execute_command_received = false;
755 self.pump_id_requested = -1;
756 let (start_command_received, stop_command_received, execute_command_received, pump_id) =
757 match rx_from_messaging_opt {
758 Some(rx_from_messaging) => {
759 match rx_from_messaging.try_recv() {
760 Ok(c) => match c {
761 InternalCommand::Start => (true, false, false, 0),
762 InternalCommand::Stop => (false, true, false, 0),
763 InternalCommand::Execute(c) => (false, false, true, c),
764 _ => {
765 warn!(
766 target: module_path!(),
767 "ignoring inapplicable command {c} from messaging."
768 );
769 (false, false, false, 0)
770 }
771 },
772 Err(e) => {
773 match e {
774 #[cfg(feature = "debug_channels")]
775 AquaChannelError::Full => { /* Not applicable. Do nothing */ }
776 AquaChannelError::Empty => { /* empty buffer - no action required */
777 }
778 AquaChannelError::Disconnected => {
779 #[cfg(not(test))]
780 error!(
781 target: module_path!(),
782 "(balling) receiving command from messaging failed ({e:?})"
783 );
784 }
785 }
786 (false, false, false, 0)
787 }
788 }
789 }
790 None => (false, false, false, 0),
791 };
792
793 self.execute_command_received = execute_command_received;
794 self.pump_id_requested = pump_id;
795
796 (
797 quit_command_received,
798 start_command_received,
799 stop_command_received,
800 )
801 }
802}
803
804impl ProcessExternalRequestTrait for Watchdog {
805 /// Checks for and processes new commands relevant to the Watchdog module from external channels.
806 ///
807 /// This is the specialized implementation of `ProcessExternalRequestTrait` for the `Watchdog`.
808 /// It handles standard "Terminate" or "Quit" commands from the signal handler.
809 /// From the messaging channel, it specifically processes "Start" and "Stop" commands,
810 /// ignoring other inapplicable commands with warnings.
811 ///
812 /// # Arguments
813 /// * `rx_from_signal_handler` - A reference to the receiver end of the channel
814 /// for commands originating from the signal handler.
815 /// * `rx_from_messaging_opt` - An `Option` containing a reference to the receiver end
816 /// of the channel for commands from the messaging system. This channel is optional.
817 ///
818 /// # Returns
819 /// A tuple `(bool, bool, bool)` indicating the status of specific commands received:
820 /// - The first `bool` is `true` if a `Quit` or `Terminate` command was received from the signal handler; otherwise `false`.
821 /// - The second `bool` is `true` if a `Start` command was received from messaging; otherwise `false`.
822 /// - The third `bool` is `true` if a `Stop` command was received from messaging; otherwise `false`.
823 fn process_external_request(
824 &mut self,
825 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
826 rx_from_messaging_opt: Option<&mut AquaReceiver<InternalCommand>>,
827 ) -> (bool, bool, bool) {
828 let quit_command_received = process_standard_request(rx_from_signal_handler);
829
830 let (start_command_received, stop_command_received) = match rx_from_messaging_opt {
831 Some(rx_from_messaging) => {
832 match rx_from_messaging.try_recv() {
833 Ok(c) => match c {
834 InternalCommand::Start => (true, false),
835 InternalCommand::Stop => (false, true),
836 _ => {
837 warn!(
838 target: module_path!(),
839 "ignoring inapplicable command {c} from messaging."
840 );
841 (false, false)
842 }
843 },
844 Err(e) => {
845 match e {
846 #[cfg(feature = "debug_channels")]
847 AquaChannelError::Full => { /* Not applicable. Do nothing */ }
848 AquaChannelError::Empty => { /* empty buffer - no action required */ }
849 AquaChannelError::Disconnected => {
850 #[cfg(not(test))]
851 error!(
852 target: module_path!(),
853 "(watchdog) receiving command from messaging failed ({e:?})"
854 );
855 }
856 }
857 (false, false)
858 }
859 }
860 }
861 None => (false, false),
862 };
863
864 (
865 quit_command_received,
866 start_command_received,
867 stop_command_received,
868 )
869 }
870}
871
872impl ProcessExternalRequestTrait for Memory {
873 /// Checks for and processes new commands relevant to the Memory module from external channels.
874 ///
875 /// This is the specialized implementation of `ProcessExternalRequestTrait` for `TcpCommunication`.
876 /// It delegates directly to `process_external_request_without_messaging`, indicating
877 /// that the `Memory` module primarily processes commands from the signal handler
878 /// and does not use a separate messaging channel for external requests.
879 ///
880 /// # Arguments
881 /// * `rx_from_memory` - A reference to the receiver end of the channel
882 /// for commands originating from the signal handler.
883 /// * `_` - This parameter is ignored, as the `Memory` module does not process
884 /// messages from a messaging channel in this context.
885 ///
886 /// # Returns
887 /// A tuple `(bool, bool, bool)` indicating the status of commands received:
888 /// - The first `bool` is `true` if a `Quit` command was received; otherwise `false`.
889 /// - The second `bool` is always `false` (no "Start" commands processed).
890 /// - The third `bool` is always `false` (no "Stop" commands processed).
891 fn process_external_request(
892 &mut self,
893 rx_from_signal_handler: &mut AquaReceiver<InternalCommand>,
894 _: Option<&mut AquaReceiver<InternalCommand>>,
895 ) -> (bool, bool, bool) {
896 process_external_request_without_messaging(rx_from_signal_handler, "Memory".to_string())
897 }
898}