aquarium_control/water/
refill.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#[cfg(feature = "debug_refill")]
10use log::debug;
11
12use crate::error;
13use log::warn;
14
15#[cfg(not(test))]
16use log::info;
17
18#[cfg(all(not(test), target_os = "linux"))]
19use nix::unistd::gettid;
20
21use spin_sleep::SpinSleeper;
22use std::sync::{Arc, Mutex};
23use std::time::{Duration, Instant};
24
25use crate::database::sql_interface_refill::DatabaseInterfaceRefillTrait;
26use crate::sensors::tank_level_switch::TankLevelSwitchSignals;
27use crate::utilities::acknowledge_signal_handler::AcknowledgeSignalHandlerTrait;
28use crate::utilities::channel_content::InternalCommand;
29use crate::utilities::database_ping_trait::DatabasePingTrait;
30use crate::utilities::proc_ext_req::ProcessExternalRequestTrait;
31use crate::utilities::wait_for_termination::WaitForTerminationTrait;
32use crate::water::refill_channels::RefillChannels;
33use crate::water::refill_config::RefillConfig;
34use crate::water::refill_error_states::RefillErrorStates;
35use crate::water::refill_monitor_view::RefillMonitorView;
36use crate::water::refill_stats_data::RefillStatsData;
37use crate::water::water_injection::WaterInjectionTrait;
38use crate::{log_error_chain, perform_schedule_check};
39
40const MILLIS_PER_SEC: u64 = 1000;
41
42/// Struct contains the status of the refill control for communication to the data logger.
43pub struct RefillStatus {
44    /// Status bit of refill control: It follows the actuation of the refill pump.
45    pub refill_in_progress_live: bool,
46
47    /// Status bit of refill control for data logger:
48    /// - It is set with the start when the refill pump is actuated.
49    /// - It is unset by the data logger to make sure the data logger has captured the refill.
50    pub refill_in_progress_for_database: bool,
51}
52
53/// Contains the configuration and the implementation for the refill control.
54#[cfg_attr(doc, aquamarine::aquamarine)]
55/// Thread communication of this component is as follows:
56/// ```mermaid
57/// graph LR
58/// refill[Refill control] --> water_injection[Water Injection]
59/// tank_level_switch[Tank Level Switch] -.-> refill
60/// tank_level_switch -.-> water_injection
61/// water_injection --> relay_manager[Relay Manager]
62/// relay_manager --> water_injection
63/// refill --> signal_handler[Signal Handler]
64/// signal_handler --> refill
65/// signal_handler --> water_injection
66/// refill --> schedule_check[Schedule Check]
67/// schedule_check --> refill
68/// refill -.-> data_logger[Data Logger]
69/// messaging[Messaging] --> refill
70/// ```
71/// Communication channel to and from the relay manager is forwarded to implementation of WaterInjectionTrait.
72/// Communication channel to and from data logger is forwarded to implementation of WaterInjectionTrait.
73/// Signal handler is communicating both directly with refill as well with implementation of WaterInjectionTrait.
74/// Tank level switch calculation communicates both directly with refill control and with implementation of WaterInjectionTrait.
75///
76/// Architecture of the component is as follows:
77/// ```mermaid
78/// graph LR
79/// execute --> calc_tank_level_switch_position_stabilized;
80/// execute --> get_sql_refill_data;
81/// execute_refill --> process_external_request;
82/// execute --> check_error_condition_switch_stuck_high;
83/// execute --> process_external_request;
84/// execute --> update_tank_level_switch_signals;
85/// execute_refill --> update_tank_level_switch_signals;
86/// execute --> check_volume_count_time_limits;
87/// execute --> execute_refill;
88/// ```
89pub struct Refill {
90    /// configuration of refill control
91    /// Attribute is public because access from within test cases.
92    pub config: RefillConfig,
93
94    /// struct of type RefillErrors which includes flags for different kind of errors
95    pub refill_errors: RefillErrorStates,
96
97    /// inhibition flag to avoid flooding the log file with repeated messages about stop command being received
98    lock_info_stop_command_received: bool,
99
100    /// inhibition flag to avoid flooding the log file with repeated messages about start command being received
101    lock_info_start_command_received: bool,
102
103    /// inhibition flag to avoid flooding the log file with repeated messages about refill volume check of last 24h
104    lock_info_refill_check_volume_last_24h: bool,
105
106    /// inhibition flag to avoid flooding the log file with repeated messages about refill count check of last 24h
107    lock_info_refill_check_count_last_24h: bool,
108
109    /// inhibition flag to avoid flooding the log file with repeated messages about refill volume check of last hour
110    lock_info_refill_check_volume_last_hour: bool,
111
112    /// inhibition flag to avoid flooding the log file with repeated messages about refill count check of last hour
113    lock_info_refill_check_count_last_hour: bool,
114
115    /// inhibition flag to avoid flooding the log file with repeated messages about refill interval check
116    lock_info_refill_check_interval_last_refill: bool,
117
118    /// inhibition flag to avoid flooding the log file with repeated messages about failure to read duration since the last refill
119    lock_error_read_duration_since_last_refill: bool,
120
121    /// inhibition flag to avoid flooding the log file with repeated messages about failure to check if the refill log table is empty
122    lock_error_check_empty: bool,
123
124    /// inhibition flag to avoid flooding the log file with repeated messages about failure to read refill volume of last 24h from SQL database
125    lock_error_read_refill_volume_last_24h: bool,
126
127    /// inhibition flag to avoid flooding the log file with repeated messages about failure to read refill count of last 24h from SQL database
128    lock_error_read_refill_count_last_24h: bool,
129
130    /// inhibition flag to avoid flooding the log file with repeated messages about failure to read refill volume of last hour from SQL database
131    lock_error_read_refill_volume_last_hour: bool,
132
133    /// inhibition flag to avoid flooding the log file with repeated messages about failure to read refill count of last hour from SQL database
134    lock_error_read_refill_count_last_hour: bool,
135
136    /// inhibition flag to avoid flooding the log file with repeated messages about failure to request to schedule check via the channel
137    lock_error_channel_send_schedule_check: bool,
138
139    /// inhibition flag to avoid flooding the log file with repeated messages about failure to receive from schedule check via the channel
140    lock_error_channel_receive_schedule_check: bool,
141
142    /// inhibition flag to avoid flooding the log file with repeated messages about failure to send view to monitor
143    lock_error_channel_send_monitors: bool,
144
145    /// inhibition flag to avoid flooding the log file with repeated messages about having received not applicable command from the signal handler
146    pub lock_warn_inapplicable_command_signal_handler: bool,
147
148    /// inhibition flag to avoid flooding the log file with repeated messages about failure to receive from the signal handler via the channel
149    pub lock_error_channel_receive_termination: bool,
150
151    /// recording when the last database ping happened
152    pub last_ping_instant: Instant,
153
154    /// database ping interval
155    pub database_ping_interval: Duration,
156
157    /// The last monitor view sent, to avoid sending redundant data.
158    last_sent_monitor_view: Option<RefillMonitorView>,
159}
160
161impl Refill {
162    /// Creates a new `Refill` control instance.
163    ///
164    /// This constructor initializes the freshwater refill control module with its specified
165    /// configuration and a dedicated SQL database interface. It sets up various internal
166    /// error flags to the default (inactive) state and initializes many "lock" flags
167    /// to `false`. These lock flags are crucial during operation to prevent log files from
168    /// being flooded with repetitive error or warning messages.
169    ///
170    /// # Arguments
171    /// * `config` - **Configuration data** for the refill control, loaded from a TOML file.
172    ///   This includes parameters such as refill limits, intervals, and pump flow rates.
173    /// * `database_ping_interval` - A `Duration` instance, providing the interval to ping the database.
174    ///
175    /// # Returns
176    /// A new **`Refill` struct**, ready to manage the aquarium's freshwater refill system.
177    pub fn new(config: RefillConfig, database_ping_interval: Duration) -> Refill {
178        Self {
179            config,
180            refill_errors: RefillErrorStates {
181                error_switch_stuck_high: false,
182                error_switch_stuck_low: false,
183                error_receive_tank_level_switch_position: false,
184                error_sql_update_failed: false,
185                error_sql_get_historic_data_failed: false,
186            },
187            lock_info_stop_command_received: false,
188            lock_info_start_command_received: false,
189            lock_info_refill_check_volume_last_24h: false,
190            lock_info_refill_check_count_last_24h: false,
191            lock_info_refill_check_volume_last_hour: false,
192            lock_info_refill_check_count_last_hour: false,
193            lock_info_refill_check_interval_last_refill: false,
194            lock_error_read_duration_since_last_refill: false,
195            lock_error_check_empty: false,
196            lock_error_read_refill_volume_last_24h: false,
197            lock_error_read_refill_count_last_24h: false,
198            lock_error_read_refill_volume_last_hour: false,
199            lock_error_read_refill_count_last_hour: false,
200            lock_error_channel_send_schedule_check: false,
201            lock_error_channel_receive_schedule_check: false,
202            lock_error_channel_send_monitors: false,
203            lock_warn_inapplicable_command_signal_handler: false,
204            lock_error_channel_receive_termination: false,
205            last_ping_instant: Instant::now(),
206            database_ping_interval,
207            last_sent_monitor_view: None,
208        }
209    }
210
211    #[cfg(test)]
212    // Creates a new `Refill` struct, allowing for explicit control over initial error states.
213    //
214    // This constructor is exclusively used in test environments. It initializes the
215    // refill control module with a provided configuration, a specific SQL interface,
216    // and a predefined `RefillErrors` struct. This allows tests to simulate scenarios
217    // where certain error conditions are already active at startup, enabling more
218    // granular and precise testing of error-handling logic.
219    //
220    // # Arguments
221    // * `config` - **Configuration data** for the refill control, loaded from a TOML file.
222    // * `refill_errors` - A **`RefillErrors` struct** containing the initial states of various
223    //   error flags, allowing tests to set up specific error conditions.
224    //
225    // # Returns
226    // A new **`Refill` struct**, with its `refill_errors` field initialized to the provided value.
227    pub fn new_with_errors(config: RefillConfig, refill_errors: RefillErrorStates) -> Refill {
228        Self {
229            config,
230            refill_errors,
231            lock_info_stop_command_received: false,
232            lock_info_start_command_received: false,
233            lock_info_refill_check_volume_last_24h: false,
234            lock_info_refill_check_count_last_24h: false,
235            lock_info_refill_check_volume_last_hour: false,
236            lock_info_refill_check_count_last_hour: false,
237            lock_info_refill_check_interval_last_refill: false,
238            lock_error_read_duration_since_last_refill: false,
239            lock_error_check_empty: false,
240            lock_error_read_refill_volume_last_24h: false,
241            lock_error_read_refill_count_last_24h: false,
242            lock_error_read_refill_volume_last_hour: false,
243            lock_error_read_refill_count_last_hour: false,
244            lock_error_channel_send_schedule_check: false,
245            lock_error_channel_receive_schedule_check: false,
246            lock_error_channel_send_monitors: false,
247            lock_warn_inapplicable_command_signal_handler: false,
248            lock_error_channel_receive_termination: false,
249            last_ping_instant: Instant::now(),
250            database_ping_interval: Duration::from_secs(1000),
251            last_sent_monitor_view: None,
252        }
253    }
254
255    /// Retrieves comprehensive historic refill data from the SQL database and updates the provided container.
256    /// This helper function queries the `sql_interface_refill` for various statistics
257    /// crucial for refill control:
258    /// - Duration since the last refill operation.
259    /// - Total volume and count of refills in the last 24 hours.
260    /// - Total volume and count of refills in the last hour.
261    ///
262    /// It populates the `refill_stats_data` struct with these values. Errors encountered during
263    /// database communication are logged and cause the `error_sql_get_historic_data_failed` flag
264    /// in `refill_errors` to be set, potentially inhibiting further refills. Internal lock flags
265    /// prevent log flooding.
266    ///
267    /// # Arguments
268    /// * `refill_stats_data` - A mutable reference to a `RefillStatsData` struct, which will be
269    ///   populated with the retrieved historical data.
270    fn get_refill_stats_data(
271        &mut self,
272        refill_stats_data: &mut RefillStatsData,
273        sql_interface_refill: &mut dyn DatabaseInterfaceRefillTrait,
274    ) {
275        match sql_interface_refill.check_empty() {
276            Ok(0) => {
277                self.lock_error_check_empty = false;
278                refill_stats_data.duration_since_last_refill =
279                    self.config.min_interval_last_refill.saturating_add(1);
280            }
281            Ok(_) => {
282                self.lock_error_check_empty = false;
283                match sql_interface_refill.get_duration_since_last_refill() {
284                    Ok(duration) => {
285                        self.lock_error_read_duration_since_last_refill = false;
286                        refill_stats_data.duration_since_last_refill = duration;
287                    }
288                    Err(_e) => {
289                        self.refill_errors.error_sql_get_historic_data_failed = true;
290                        #[cfg(not(test))]
291                        if !self.lock_error_read_duration_since_last_refill {
292                            log_error_chain(
293                                module_path!(),
294                                "could not get duration since last refill event",
295                                _e,
296                            );
297                            self.lock_error_read_duration_since_last_refill = true;
298                        }
299                    }
300                }
301            }
302            Err(_e) => {
303                self.refill_errors.error_sql_get_historic_data_failed = true;
304                #[cfg(not(test))]
305                if !self.lock_error_check_empty {
306                    log_error_chain(
307                        module_path!(),
308                        "could not check if refill table is empty or not",
309                        _e,
310                    );
311                    self.lock_error_check_empty = true;
312                }
313            }
314        };
315
316        match sql_interface_refill.get_refill_volume_of_last_24h() {
317            Ok(volume) => {
318                self.lock_error_read_refill_volume_last_24h = false;
319                refill_stats_data.refill_volume_last_24h = volume;
320            }
321            Err(_e) => {
322                self.refill_errors.error_sql_get_historic_data_failed = true;
323                #[cfg(not(test))]
324                if !self.lock_error_read_refill_volume_last_24h {
325                    log_error_chain(
326                        module_path!(),
327                        "could not get refill volume of last 24 hours",
328                        _e,
329                    );
330                    self.lock_error_read_refill_volume_last_24h = true;
331                }
332            }
333        };
334
335        match sql_interface_refill.get_refill_count_of_last_24h() {
336            Ok(count) => {
337                self.lock_error_read_refill_count_last_24h = false;
338                refill_stats_data.refill_count_last_24h = count;
339            }
340            Err(_e) => {
341                self.refill_errors.error_sql_get_historic_data_failed = true;
342                #[cfg(not(test))]
343                if !self.lock_error_read_refill_count_last_24h {
344                    log_error_chain(
345                        module_path!(),
346                        "could not get refill count of last 24 hours",
347                        _e,
348                    );
349                    self.lock_error_read_refill_count_last_24h = true;
350                }
351            }
352        };
353
354        match sql_interface_refill.get_refill_volume_of_last_hour() {
355            Ok(volume) => {
356                self.lock_error_read_refill_volume_last_hour = false;
357                refill_stats_data.refill_volume_last_hour = volume;
358            }
359            Err(_e) => {
360                self.refill_errors.error_sql_get_historic_data_failed = true;
361                #[cfg(not(test))]
362                if !self.lock_error_read_refill_volume_last_hour {
363                    log_error_chain(
364                        module_path!(),
365                        "Could not get refill volume of last hour",
366                        _e,
367                    );
368                    self.lock_error_read_refill_volume_last_hour = true;
369                }
370            }
371        };
372
373        match sql_interface_refill.get_refill_count_of_last_hour() {
374            Ok(count) => {
375                self.lock_error_read_refill_count_last_hour = false;
376                refill_stats_data.refill_count_last_hour = count;
377            }
378            Err(_e) => {
379                self.refill_errors.error_sql_get_historic_data_failed = true;
380                #[cfg(not(test))]
381                if !self.lock_error_read_refill_count_last_hour {
382                    log_error_chain(
383                        module_path!(),
384                        "Could not get refill count of last hour",
385                        _e,
386                    );
387                    self.lock_error_read_refill_count_last_hour = true;
388                }
389            }
390        };
391    }
392
393    /// Checks if the duration since the last refill operation exceeds a configurable safety limit, indicating a potential "switch stuck high" error.
394    ///
395    /// This private helper function compares the `duration_since_last_refill` with `max_duration_since_last_refill` from the configuration.
396    /// If the elapsed time is longer than the configured maximum, it implies that the water level switch might be stuck in a "high"
397    /// position, preventing detection of low water levels and thus inhibiting refills. The corresponding error flag in `refill_errors` is set if this condition is met.
398    /// This helps to detect and log a critical fault condition.
399    ///
400    /// # Arguments
401    /// * `duration_since_last_refill` - The duration (in seconds) that has passed since the last refill operation.
402    fn check_error_condition_switch_stuck_high(&mut self, duration_since_last_refill: u64) {
403        self.refill_errors.error_switch_stuck_high =
404            duration_since_last_refill > self.config.max_duration_since_last_refill;
405    }
406
407    /// Calculates the **inhibition flag** for the refill control based on received start and stop commands.
408    ///
409    /// This private helper function determines the current operational state (inhibited or active)
410    /// of the refill control. A `Stop` command will inhibit refilling, while a `Start` command
411    /// will activate it. The `Start` command takes precedence over `Stop` if both are received
412    /// simultaneously. It uses internal lock flags to prevent log flooding from repeated info messages.
413    ///
414    /// # Arguments
415    /// * `current_state` - The current boolean value of the inhibition flag (`true` if inhibited, `false` if active) before processing new commands.
416    /// * `start_command_received` - A flag indicating whether a `Start` command was received from an external source in the current cycle.
417    /// * `stop_command_received` - A flag indicating whether a `Stop` command was received from an external source in the current cycle.
418    ///
419    /// # Returns
420    /// A `bool` representing the **new state of the inhibition flag** after processing the commands.
421    fn calc_inhibition_flag(
422        &mut self,
423        current_state: bool,
424        start_command_received: bool,
425        stop_command_received: bool,
426    ) -> bool {
427        // initialize return value with current state
428        let mut new_state: bool = current_state;
429
430        if stop_command_received {
431            if !self.lock_info_stop_command_received {
432                #[cfg(not(test))]
433                info!(
434                    target: module_path!(),
435                    "received Stop command. Inhibiting refill control."
436                );
437
438                self.lock_info_stop_command_received = true;
439            }
440            new_state = true;
441        } else {
442            self.lock_info_stop_command_received = false;
443        }
444        // The start command has a higher priority than the stop command.
445        // Therefore, it is evaluated after the stop command.
446        if start_command_received {
447            if !self.lock_info_start_command_received {
448                #[cfg(not(test))]
449                info!(
450                    target: module_path!(),
451                    "received Start command. Restarting refill control."
452                );
453
454                self.lock_info_start_command_received = true;
455            } else {
456                self.lock_info_start_command_received = true;
457            }
458            new_state = false;
459        }
460        new_state
461    }
462
463    /// Checks if historical refill data (volume, count, and time interval) **exceeds configured limitations**.
464    ///
465    /// This private helper function performs a series of checks against the `RefillConfig`'s
466    /// maximum permitted values for:
467    /// - Total refill volume within the last 24 hours.
468    /// - Total refill count within the last 24 hours.
469    /// - Total refill volume within the last hour.
470    /// - Total refill count within the last hour.
471    /// - Minimum time interval since the last refill operation.
472    ///
473    /// If any limit is exceeded, an informational message is logged (once per transgression
474    /// thanks to internal lock flags), indicating a potential issue that might inhibit further refills.
475    ///
476    /// # Arguments
477    /// * `sql_refill_data` - A reference to a `RefillStatsData` struct containing the historic
478    ///   refill data retrieved from the SQL database.
479    /// * `tx_refill_to_monitors` - The sender part of the channel for communication to the monitors.
480    ///
481    /// # Returns
482    /// `true` if **any** of the configured volume, count, or time limits are exceeded; `false` otherwise.
483    fn check_volume_count_time_limits(
484        &mut self,
485        sql_refill_data: &RefillStatsData,
486        refill_channels: &mut RefillChannels,
487    ) -> bool {
488        #[cfg(feature = "debug_refill")]
489        debug!(
490            target: module_path!(),
491            "check_volume_count_time_limits called.",
492        );
493        let refill_check_volume_last_24h =
494            sql_refill_data.refill_volume_last_24h > self.config.max_refill_volume_24h;
495
496        if refill_check_volume_last_24h {
497            if !self.lock_info_refill_check_volume_last_24h {
498                #[cfg(not(test))]
499                info!(
500                    target: module_path!(),
501                    "refill volume of last 24h exceeds permissible refill volume per 24h of {} L",
502                    self.config.max_refill_volume_24h
503                );
504
505                self.lock_info_refill_check_volume_last_24h = true;
506            }
507        } else {
508            self.lock_info_refill_check_volume_last_24h = false;
509        }
510
511        let refill_check_count_last_24h =
512            sql_refill_data.refill_count_last_24h > self.config.max_refill_count_24h;
513        if refill_check_count_last_24h {
514            if !self.lock_info_refill_check_count_last_24h {
515                #[cfg(not(test))]
516                info!(
517                    target: module_path!(),
518                    "refill count of last 24h exceeds permissible refill count per 24h of {} L",
519                    self.config.max_refill_count_24h
520                );
521
522                self.lock_info_refill_check_count_last_24h = true;
523            }
524        } else {
525            self.lock_info_refill_check_count_last_24h = false;
526        }
527
528        let refill_check_volume_last_hour =
529            sql_refill_data.refill_volume_last_hour > self.config.max_refill_volume_hour;
530        if refill_check_volume_last_hour {
531            if !self.lock_info_refill_check_volume_last_hour {
532                #[cfg(not(test))]
533                info!(
534                    target: module_path!(),
535                    "refill volume of last hour exceeds permissible refill volume per hour of {}",
536                    self.config.max_refill_volume_hour
537                );
538
539                self.lock_info_refill_check_volume_last_hour = true;
540            }
541        } else {
542            self.lock_info_refill_check_volume_last_hour = false;
543        }
544
545        let refill_check_count_last_hour =
546            sql_refill_data.refill_count_last_hour > self.config.max_refill_count_hour;
547        if refill_check_count_last_hour {
548            if !self.lock_info_refill_check_count_last_hour {
549                #[cfg(not(test))]
550                info!(
551                    target: module_path!(),
552                    "refill count of last 24h exceeds permissible refill count per hour of {}",
553                    self.config.max_refill_count_hour
554                );
555
556                self.lock_info_refill_check_count_last_hour = true;
557            }
558        } else {
559            self.lock_info_refill_check_count_last_hour = false;
560        }
561
562        let refill_check_interval_last_refill =
563            sql_refill_data.duration_since_last_refill < self.config.min_interval_last_refill;
564        if refill_check_interval_last_refill {
565            if !self.lock_info_refill_check_interval_last_refill {
566                #[cfg(not(test))]
567                info!(
568                    target: module_path!(),
569                    "time interval since last refill {} is too small (limit={}).",
570                    sql_refill_data.duration_since_last_refill, self.config.min_interval_last_refill
571                );
572
573                self.lock_info_refill_check_interval_last_refill = true;
574            }
575        } else {
576            self.lock_info_refill_check_interval_last_refill = false;
577        }
578
579        let refill_monitor_view = RefillMonitorView {
580            refill_check_volume_last_24h,
581            refill_check_count_last_24h,
582            refill_check_volume_last_hour,
583            refill_check_count_last_hour,
584            refill_check_interval_last_refill,
585        };
586
587        // Only send the view if it's different from the last one we sent.
588        let mut monitor_view_is_different = true;
589        if self.last_sent_monitor_view.is_some() {
590            let previous_monitor_view = self.last_sent_monitor_view.clone().unwrap();
591            if refill_monitor_view == previous_monitor_view {
592                monitor_view_is_different = false;
593            }
594        }
595        if monitor_view_is_different {
596            match refill_channels.send_to_monitor(refill_monitor_view) {
597                Ok(_) => {
598                    self.lock_error_channel_send_monitors = false;
599                }
600                Err(e) => {
601                    if !self.lock_error_channel_send_monitors {
602                        log_error_chain(module_path!(), "sending view to monitor failed", e);
603                        self.lock_error_channel_send_monitors = true;
604                    }
605                }
606            }
607        }
608
609        refill_check_volume_last_24h
610            | refill_check_count_last_24h
611            | refill_check_volume_last_hour
612            | refill_check_count_last_hour
613            | refill_check_interval_last_refill
614    }
615
616    /// Executes the **main control loop for the refill module**.
617    ///
618    /// This function runs continuously, managing the aquarium's freshwater refill system
619    /// until a termination signal is received. It periodically checks the tank water
620    /// level, assesses historical refill data against configured limits, and initiates
621    /// refill operations when necessary and permitted by schedules.
622    ///
623    /// **Key Operations:**
624    /// - **Initial Wait**: Observes an initial waiting period after application startup before active control begins.
625    /// - **Sensor Data Acquisition**: Reads raw and stabilized tank level switch signals from a shared mutex.
626    /// - **SQL Data Retrieval**: Fetches historical refill data (counts, volumes, last refill time) from the database.
627    /// - **Safety Checks**: Verifies if refill operations are within configured limits (max count/volume per hour/24h) and checks for a "tank level switch stuck high" error.
628    /// - **Schedule & External Control**: Communicates with the `ScheduleCheck` module and responds to `Start`/`Stop` commands to inhibit or enable refilling.
629    /// - **Water Injection**: If all conditions are met (low water, within limits, allowed by schedule, not inhibited), it triggers water injection via the `WaterInjectionTrait`.
630    /// - **Data Logging Communication**: Periodically communicates the refill pump status to the `DataLogger`.
631    /// - **Cycle Management**: Maintains a fixed cycle time, sleeping as necessary, and warning if execution duration exceeds the cycle limit.
632    /// - **Graceful Shutdown**: Responds to `Quit` and `Terminate` commands from the signal handler, ensuring a safe shutdown.
633    ///
634    /// # Arguments
635    /// * `mutex_device_scheduler_refill` - A clone of the `Arc<Mutex<i32>>` used to coordinate device
636    ///   actuation across the application, preventing conflicts and tracking activity counts.
637    /// * `refill_channels` - A struct containing all sender and receiver channels for communication
638    ///   with modules like `RelayManager`, `SignalHandler`, `DataLogger`, `ScheduleCheck`, and `Messaging`.
639    /// * `water_injection` - A mutable reference to an object implementing `WaterInjectionTrait`,
640    ///   responsible for the physical control of the refill pump.
641    /// * `mutex_tank_level_switch_signals_clone_for_refill` - The `Arc<Mutex<TankLevelSwitchSignals>>`
642    ///   containing the latest tank level switch readings (raw and stabilized).
643    /// * `sql_interface_refill` - The **specific SQL interface** for refill-related database operations.
644    pub fn execute(
645        &mut self,
646        mutex_device_scheduler_refill: Arc<Mutex<i32>>,
647        refill_channels: &mut RefillChannels,
648        water_injection: &mut impl WaterInjectionTrait,
649        mutex_tank_level_switch_signals_clone_for_refill: Arc<Mutex<TankLevelSwitchSignals>>,
650        mutex_refill_status: Arc<Mutex<RefillStatus>>,
651        mut sql_interface_refill: Box<dyn DatabaseInterfaceRefillTrait + Sync + Send>,
652    ) {
653        #[cfg(all(target_os = "linux", not(test)))]
654        info!(target: module_path!(), "Thread started with TID: {}", gettid());
655
656        let cycle_time_duration =
657            Duration::from_millis(self.config.check_interval * MILLIS_PER_SEC);
658        let sleep_duration_hundred_millis = Duration::from_millis(100);
659        let mut i = 0;
660        let spin_sleeper = SpinSleeper::default();
661
662        let mut schedule_check_result: bool;
663        #[cfg(feature = "debug_refill")]
664        {
665            schedule_check_result = false;
666        }
667
668        let mut tank_level_switch_invalid: bool = false;
669        let mut tank_level_switch_position_stabilized: bool = true;
670
671        #[cfg(feature = "debug_refill")]
672        {
673            tank_level_switch_invalid = false;
674            tank_level_switch_position_stabilized = true;
675        }
676
677        let mut lock_warn_cycle_time_exceeded: bool = false;
678        let mut cycle_time_exceeded: bool; // ensures that signal handler/messaging are polled at least once even if cycle time is exceeded
679
680        // retrieving data from SQL data base
681        let mut refill_stats_data = RefillStatsData::new();
682        self.get_refill_stats_data(&mut refill_stats_data, &mut *sql_interface_refill);
683
684        let mut refill_inhibited = false; // a flag indicating the external request to inhibit refill from injecting water
685        let mut quit_command_received = false; // the request to end the application has been received
686        let mut start_command_received; // the request to (re-)start refill control has been received
687        let mut stop_command_received; // the request to (temporarily) stop the refill control has been received
688
689        // initial waiting time
690        if self.config.initial_wait_interval > 0 {
691            while i < self.config.initial_wait_interval * 10 {
692                match i.checked_add(1) {
693                    Some(c) => i = c,
694                    None => {
695                        // overflow
696                        break;
697                    }
698                }
699
700                (
701                    quit_command_received,
702                    start_command_received,
703                    stop_command_received,
704                ) = self.process_external_request(
705                    &mut refill_channels.rx_refill_from_signal_handler,
706                    refill_channels.rx_refill_from_messaging_opt.as_mut(),
707                );
708                refill_inhibited = self.calc_inhibition_flag(
709                    refill_inhibited,
710                    start_command_received,
711                    stop_command_received,
712                );
713                if quit_command_received {
714                    break;
715                }
716                spin_sleeper.sleep(sleep_duration_hundred_millis);
717            }
718        }
719
720        // frequent update required for calculation of the error condition switch stuck high
721        refill_stats_data.duration_since_last_refill = refill_stats_data
722            .duration_since_last_refill
723            .saturating_add(self.config.initial_wait_interval);
724
725        let mut start_time = Instant::now();
726
727        // main control loop for refill
728        loop {
729            // check if either during the main control loop or during refill execution the termination request has been received
730            if quit_command_received {
731                break; // exit outer loop
732            }
733
734            if self.config.active {
735                // check when the last refill operation has taken place
736                // if too much time has passed, issue an error condition - not impeding rest of operation
737                self.check_error_condition_switch_stuck_high(
738                    refill_stats_data.duration_since_last_refill,
739                );
740
741                if !quit_command_received && !refill_inhibited {
742                    // access mutex for reading tank level switch signals
743                    {
744                        match mutex_tank_level_switch_signals_clone_for_refill.lock() {
745                            Ok(c) => {
746                                tank_level_switch_position_stabilized =
747                                    c.tank_level_switch_position_stabilized;
748                                tank_level_switch_invalid = c.tank_level_switch_invalid;
749                            }
750                            Err(_) => {
751                                // Do nothing, keep values as they are.
752                            }
753                        }
754                    }
755
756                    self.get_refill_stats_data(&mut refill_stats_data, &mut *sql_interface_refill);
757
758                    // execute checks before initiating refill
759                    if !self.check_volume_count_time_limits(&refill_stats_data, refill_channels)
760                        && !self.refill_errors.error_switch_stuck_low
761                        && !self.refill_errors.error_sql_update_failed
762                        && !self.refill_errors.error_sql_get_historic_data_failed
763                    {
764                        perform_schedule_check!(
765                            refill_channels,
766                            schedule_check_result,
767                            self.lock_error_channel_send_schedule_check,
768                            self.lock_error_channel_receive_schedule_check,
769                            module_path!()
770                        );
771
772                        if schedule_check_result
773                            && !tank_level_switch_position_stabilized
774                            && !tank_level_switch_invalid
775                        {
776                            #[cfg(feature = "debug_refill")]
777                            debug!(
778                                target: module_path!(),
779                                "acquiring semaphore...",
780                            );
781
782                            // conditions for allowing the refill operation are given
783                            let mut mutex_data = mutex_device_scheduler_refill.lock().unwrap();
784                            // now we are given exclusive access to actuators
785
786                            #[cfg(feature = "debug_refill")]
787                            debug!(
788                                target: module_path!(),
789                                "mutex_data = {}",
790                                mutex_data
791                            );
792
793                            {
794                                // internal scope to limit lifetime of mutex lock
795                                match mutex_refill_status.lock() {
796                                    Ok(mut refill_status) => {
797                                        refill_status.refill_in_progress_live = true;
798                                        refill_status.refill_in_progress_for_database = true;
799                                    }
800                                    Err(e) => {
801                                        log_error_chain(
802                                            module_path!(),
803                                            "error locking mutex for refill status",
804                                            e,
805                                        );
806                                    }
807                                };
808                            }
809
810                            quit_command_received = water_injection.inject_water(
811                                refill_channels,
812                                &mut self.refill_errors,
813                                &mut sql_interface_refill,
814                                &mutex_tank_level_switch_signals_clone_for_refill,
815                            );
816                            *mutex_data = mutex_data.saturating_add(1);
817
818                            {
819                                // internal scope to limit lifetime of mutex lock
820                                match mutex_refill_status.lock() {
821                                    Ok(mut refill_status) => {
822                                        refill_status.refill_in_progress_live = false;
823                                    }
824                                    Err(e) => {
825                                        log_error_chain(
826                                            module_path!(),
827                                            "error locking mutex for refill status",
828                                            e,
829                                        );
830                                    }
831                                };
832                            }
833
834                            #[cfg(feature = "debug_refill")]
835                            debug!(
836                                target: module_path!(),
837                                "releasing semaphore..."
838                            );
839                        }
840                    }
841                }
842            }
843
844            #[cfg(feature = "debug_refill")]
845            debug!(
846                target: module_path!(),
847                "refill_inhibited = {}, error_switch_stuck_low={}, error_sql_update_failed={}, error_sql_get_historic_data_failed={}, schedule_check_result={}, tank_level_switch_position_stabilized={}, tank_level_switch_invalid={}",
848                refill_inhibited,
849                self.refill_errors.error_switch_stuck_low,
850                self.refill_errors.error_sql_update_failed,
851                self.refill_errors.error_sql_get_historic_data_failed,
852                schedule_check_result,
853                tank_level_switch_position_stabilized,
854                tank_level_switch_invalid
855            );
856
857            let stop_time = Instant::now();
858            let execution_duration = stop_time.duration_since(start_time);
859
860            let mut remaining_sleep_time_millis: u64 = 0;
861            if execution_duration >= cycle_time_duration {
862                if !lock_warn_cycle_time_exceeded {
863                    warn!(target: module_path!(),
864                        "execution duration of {} ms exceeds cycle time of {} ms",
865                        execution_duration.as_millis(),
866                        cycle_time_duration.as_millis()
867                    );
868                    lock_warn_cycle_time_exceeded = true;
869                }
870                cycle_time_exceeded = true;
871            } else {
872                // Execution duration is smaller than cycle time in this branch.
873                // No risk of underflow
874                let remaining_sleep_time_duration = cycle_time_duration - execution_duration;
875                remaining_sleep_time_millis = remaining_sleep_time_duration.as_millis() as u64;
876                cycle_time_exceeded = false;
877                lock_warn_cycle_time_exceeded = false;
878            }
879
880            #[cfg(feature = "debug_refill")]
881            debug!(
882                target: module_path!(),
883                "execution duration = {}, cycle time = {}, remaining_sleep_time_millis = {}",
884                execution_duration_millis,
885                self.config.check_interval * MILLIS_PER_SEC,
886                remaining_sleep_time_millis
887            );
888
889            // skip further waiting if the quit command has been received during refilling
890            if !quit_command_received {
891                // wait for the configured time period before rechecking the sensor signal
892                // check for external requests while waiting
893                i = 0;
894                while (i < remaining_sleep_time_millis) || cycle_time_exceeded {
895                    match i.checked_add(100) {
896                        Some(c) => i = c,
897                        None => {
898                            // overflow
899                            break;
900                        }
901                    }
902
903                    (
904                        quit_command_received,
905                        start_command_received,
906                        stop_command_received,
907                    ) = self.process_external_request(
908                        &mut refill_channels.rx_refill_from_signal_handler,
909                        refill_channels.rx_refill_from_messaging_opt.as_mut(),
910                    );
911                    refill_inhibited = self.calc_inhibition_flag(
912                        refill_inhibited,
913                        start_command_received,
914                        stop_command_received,
915                    );
916                    if quit_command_received {
917                        break;
918                    }
919                    spin_sleeper.sleep(sleep_duration_hundred_millis);
920
921                    self.check_timing_and_ping_database(&mut *sql_interface_refill);
922
923                    cycle_time_exceeded = false;
924                }
925            }
926
927            start_time = Instant::now();
928
929            refill_stats_data.duration_since_last_refill = refill_stats_data
930                .duration_since_last_refill
931                .saturating_add(self.config.check_interval);
932        }
933
934        // The application received the request to terminate. That is why the loop was left.
935        // Answer to the thread which sent the request for termination, so that shutdown can proceed further.
936        #[cfg(test)]
937        println!(
938            "{}: sending Quit confirmation to signal handler.",
939            module_path!()
940        );
941
942        refill_channels.acknowledge_signal_handler();
943
944        // This thread has channel connections to underlying threads (relay manager, tank level switch).
945        // Those threads have to stop receiving commands from this thread.
946        // The shutdown sequence is handled by the signal_handler module.
947        self.wait_for_termination(
948            &mut refill_channels.rx_refill_from_signal_handler,
949            sleep_duration_hundred_millis,
950            module_path!(),
951        );
952    }
953}
954
955#[cfg(test)]
956pub mod tests {
957    use crate::database::{sql_interface::SqlInterface, sql_interface_refill::SqlInterfaceRefill};
958    use crate::launch::channels::{channel, AquaChannelError, AquaReceiver, AquaSender, Channels};
959    use crate::sensors::tank_level_switch::TankLevelSwitchSignals;
960    use crate::utilities::channel_content::InternalCommand;
961    use crate::utilities::config::{
962        read_config_file, read_config_file_with_test_database, ConfigData,
963    };
964    use crate::utilities::proc_ext_req::ProcessExternalRequestTrait;
965    use crate::water::refill::Refill;
966    use crate::water::refill::RefillErrorStates;
967    use crate::water::refill::RefillStatus;
968    use all_asserts::{assert_ge, assert_le};
969    use spin_sleep::SpinSleeper;
970    use std::sync::{Arc, Mutex};
971    use std::thread;
972    use std::time::{Duration, Instant};
973
974    use crate::mocks::mock_monitors::tests::MockMonitors;
975    use crate::mocks::mock_schedule_check::tests::mock_schedule_check;
976    use crate::mocks::mock_sql_interface_refill::tests::MockSqlInterfaceRefill;
977    use crate::mocks::mock_water_injection::tests::MockWaterInjection;
978    use crate::mocks::test_command::tests::TestCommand;
979
980    use crate::utilities::logger::setup_logger;
981    use crate::utilities::logger_config::LoggerConfig;
982    use crate::water::refill_stats_data::RefillStatsData;
983
984    // Prepares and returns a configured `Refill` control instance along with its associated interfaces for testing.
985    //
986    // This helper function is designed exclusively for testing the `Refill` module. It sets up
987    // a controlled test environment by:
988    // 1.  Loading a generic or specific test configuration file.
989    // 2.  Overriding the `initial_wait_interval` and `check_interval` within the `RefillConfig`.
990    // 3.  Establishing a connection to and providing an `SqlInterface` and `SqlInterfaceRefill` instance.
991    // 4.  Optionally injecting a predefined `RefillErrors` struct into the `Refill` instance's initial state,
992    //     allowing for tests to start with specific error conditions.
993    //
994    // # Arguments
995    // * `refill_errors` - An `Option<RefillErrors>`. If `Some`, the `Refill` instance is initialized
996    //   with these specific error flags; otherwise, default `RefillErrors` (all flags `false`) are used.
997    // * `initial_wait_interval` - The `u64` value to set for `RefillConfig.initial_wait_interval` in the test setup.
998    // * `check_interval` - The `u64` value to set for `RefillConfig.check_interval` in the test setup.
999    // * `test_db_number_opt` - An `Option<u32>`. If `Some`, a test-specific database (`aquarium_test_XX`)
1000    //   will be used from the configuration; otherwise, the generic test database is used.
1001    //
1002    // # Returns
1003    // A tuple `(ConfigData, Refill, SqlInterface)`:
1004    // - The `ConfigData` struct, containing the loaded and modified configuration used for the test.
1005    // - The fully initialized `Refill` struct, ready for test execution.
1006    // - The `SqlInterface` instance, providing access to the overall SQL database for test assertions or further setup.
1007    //
1008    // # Panics
1009    // This function will panic if:
1010    // - `read_config_file` or `read_config_file_with_test_database` fail (e.g., file not found, invalid DB number).
1011    // - It fails to connect to the SQL database.
1012    // - It fails to get a connection from the SQL pool for `SqlInterfaceRefill` initialization.
1013    pub fn prepare_refill_tests(
1014        refill_errors: Option<RefillErrorStates>,
1015        initial_wait_interval: u64,
1016        check_interval: u64,
1017        test_db_number_opt: Option<u32>,
1018    ) -> (ConfigData, Refill, SqlInterface, SqlInterfaceRefill) {
1019        let config_file_name = "/config/aquarium_control_test_generic.toml";
1020
1021        // prepare struct Refill for subsequent tests
1022        let mut config: ConfigData = match test_db_number_opt {
1023            Some(test_db_number) => {
1024                read_config_file_with_test_database(config_file_name.to_string(), test_db_number)
1025            }
1026            None => read_config_file(config_file_name.to_string()).unwrap(),
1027        };
1028
1029        config.refill.initial_wait_interval = initial_wait_interval;
1030        config.refill.check_interval = check_interval;
1031
1032        let max_rows_refill = config.sql_interface.max_rows_data;
1033
1034        println!("Testing with database {}", config.sql_interface.db_name);
1035        let sql_interface = match SqlInterface::new(config.sql_interface.clone()) {
1036            Ok(c) => c,
1037            Err(e) => {
1038                panic!("Could not connect to SQL database: {e:?}");
1039            }
1040        };
1041        let sql_interface_refill =
1042            SqlInterfaceRefill::new(sql_interface.get_connection().unwrap(), max_rows_refill)
1043                .unwrap();
1044        let refill_config = config.refill.clone();
1045        (
1046            config,
1047            match refill_errors.is_some() {
1048                true => Refill::new_with_errors(refill_config, refill_errors.unwrap()),
1049                false => Refill::new(refill_config, Duration::from_millis(1000)),
1050            },
1051            sql_interface,
1052            *Box::new(sql_interface_refill),
1053        )
1054    }
1055
1056    /// Prepares a `Refill` control instance with a **mock database interface** for isolated testing.
1057    ///
1058    /// This helper function is designed exclusively for unit and integration testing of the `Refill` module.
1059    /// It sets up a controlled environment by:
1060    /// 1.  Loading a specific test configuration file (using `test_db_number` to select the database).
1061    /// 2.  Overriding the `initial_wait_interval` and `check_interval` within the `RefillConfig` to suit test needs.
1062    /// 3.  **Injecting a `MockSqlInterfaceRefill` instance** as the database interface, ensuring that tests
1063    ///     do not interact with a real SQL database. This allows for predictable and fast test execution.
1064    /// 4.  Optionally providing a predefined `RefillErrors` struct to set up specific error conditions
1065    ///     at the `Refill` instance's startup.
1066    ///
1067    /// # Arguments
1068    /// * `refill_errors` - An `Option<RefillErrors>`. If `Some`, the `Refill` instance is initialized
1069    ///   with these specific error flags; otherwise, default `RefillErrors` (all flags `false`) are used.
1070    /// * `initial_wait_interval` - The `u64` value to set for `RefillConfig.initial_wait_interval` in the test setup.
1071    /// * `check_interval` - The `u64` value to set for `RefillConfig.check_interval` in the test setup.
1072    /// * `test_db_number` - A **unique `u32` identifier** used to select a specific test database (`aquarium_test_XX`)
1073    ///   from the configuration. This ensures test isolation even with database-related config.
1074    ///
1075    /// # Returns
1076    /// A tuple `(ConfigData, Refill)`:
1077    /// - The `ConfigData` struct, containing the loaded and modified configuration used for the test.
1078    /// - The fully initialized `Refill` struct, with its database interactions handled by `MockSqlInterfaceRefill`.
1079    ///
1080    /// # Panics
1081    /// This function will panic if:
1082    /// - `read_config_file_with_test_database` fails (e.g., the configuration file is not found or the `test_db_number` is invalid).
1083    /// - There are issues during the initialization of the `Refill` struct (e.g., due to an invalid `refill_config`).
1084    pub fn prepare_refill_tests_with_mock_database_interface(
1085        refill_errors: Option<RefillErrorStates>,
1086        initial_wait_interval: u64,
1087        check_interval: u64,
1088        test_db_number: u32,
1089    ) -> (ConfigData, Refill, MockSqlInterfaceRefill) {
1090        let config_file_name = "/config/aquarium_control_test_generic.toml";
1091
1092        // prepare struct Refill for subsequent tests
1093        let mut config: ConfigData =
1094            read_config_file_with_test_database(config_file_name.to_string(), test_db_number);
1095
1096        config.refill.initial_wait_interval = initial_wait_interval;
1097        config.refill.check_interval = check_interval;
1098
1099        let sql_interface_refill = MockSqlInterfaceRefill {};
1100
1101        let refill_config = config.refill.clone();
1102        (
1103            config,
1104            match refill_errors.is_some() {
1105                true => Refill::new_with_errors(refill_config, refill_errors.unwrap()),
1106                false => Refill::new(refill_config, Duration::from_millis(1000)),
1107            },
1108            sql_interface_refill,
1109        )
1110    }
1111
1112    #[test]
1113    // Test case checks the implementation of ProcessExternalRequestTrait.
1114    // The test case uses test database #46.
1115    pub fn test_refill_process_external_request_empty_channel() {
1116        let refill_errors = RefillErrorStates {
1117            error_switch_stuck_high: false,
1118            error_switch_stuck_low: false,
1119            error_receive_tank_level_switch_position: false,
1120            error_sql_update_failed: false,
1121            error_sql_get_historic_data_failed: false,
1122        };
1123        let (_, mut refill, _, _) = prepare_refill_tests(Some(refill_errors), 2, 60, Some(46));
1124
1125        let (_, mut rx_refill_from_signal_handler): (
1126            AquaSender<InternalCommand>,
1127            AquaReceiver<InternalCommand>,
1128        ) = channel(1);
1129
1130        let (_, mut rx_refill_from_messaging): (
1131            AquaSender<InternalCommand>,
1132            AquaReceiver<InternalCommand>,
1133        ) = channel(1);
1134
1135        let (quit_command_received, start_command_received, stop_command_received) = refill
1136            .process_external_request(
1137                &mut rx_refill_from_signal_handler,
1138                Some(&mut rx_refill_from_messaging),
1139            );
1140
1141        assert_eq!(quit_command_received, false);
1142        assert_eq!(start_command_received, false);
1143        assert_eq!(stop_command_received, false);
1144    }
1145
1146    #[test]
1147    // This test case executes multiple iterations of the loop inside the .execute function, including the triggering of a refill.
1148    // Test case uses test database #20.
1149    pub fn test_refill_trigger_water_injection() {
1150        let sleep_duration_50_millis = Duration::from_millis(50);
1151        let spin_sleeper = SpinSleeper::default();
1152
1153        let (_, mut refill, mut sql_interface, sql_interface_refill) =
1154            prepare_refill_tests(None, 1, 1, Some(20));
1155
1156        let mut channels = Channels::new_for_test();
1157
1158        // test environment-specific channels
1159        let (
1160            tx_mock_water_injection_to_test_environment,
1161            mut rx_test_environment_from_mock_water_injection,
1162        ) = channel(1);
1163        let (mut tx_test_environment_to_monitors, rx_monitors_from_test_environment) =
1164            channel::<InternalCommand>(1);
1165
1166        let mutex_tank_level_switch_signals =
1167            Arc::new(Mutex::new(TankLevelSwitchSignals::new(false, false, false)));
1168        let mutex_tank_level_switch_signals_for_refill = mutex_tank_level_switch_signals.clone();
1169        let refill_status = RefillStatus {
1170            refill_in_progress_live: false,
1171            refill_in_progress_for_database: false,
1172        };
1173        let mutex_refill_status = Arc::new(Mutex::new(refill_status));
1174
1175        let mut mock_monitors = MockMonitors::new();
1176
1177        // empty the refill table
1178        match SqlInterface::truncate_table(&mut sql_interface, "refill".to_string()) {
1179            Ok(_) => {}
1180            Err(e) => {
1181                panic!("Could not prepare test case: {e:?}")
1182            }
1183        }
1184
1185        let mutex_device_scheduler = Arc::new(Mutex::new(0));
1186
1187        let mut mock_water_injection = MockWaterInjection::new(
1188            Some(tx_mock_water_injection_to_test_environment),
1189            RefillErrorStates {
1190                error_switch_stuck_high: false,
1191                error_switch_stuck_low: false,
1192                error_receive_tank_level_switch_position: false,
1193                error_sql_update_failed: false,
1194                error_sql_get_historic_data_failed: false,
1195            },
1196        );
1197
1198        // thread for mock monitors
1199        let join_handle_mock_monitors = thread::Builder::new()
1200            .name("mock_monitor".to_string())
1201            .spawn(move || {
1202                mock_monitors.execute(
1203                    channels.monitors.rx_monitors_from_refill,
1204                    rx_monitors_from_test_environment,
1205                );
1206            })
1207            .unwrap();
1208
1209        // thread for mock schedule check
1210        let join_handle_mock_schedule_check = thread::Builder::new()
1211            .name("mock_schedule_check".to_string())
1212            .spawn(move || {
1213                mock_schedule_check(
1214                    &mut channels.schedule_check.tx_schedule_check_to_refill,
1215                    &mut channels.schedule_check.rx_schedule_check_from_refill,
1216                    None,
1217                    true,
1218                );
1219            })
1220            .unwrap();
1221
1222        let join_handle_test_environment = thread::Builder::new()
1223            .name("test_environment".to_string())
1224            .spawn(move || {
1225                let sleep_duration_two_secs = Duration::from_secs(2);
1226                let spin_sleeper = SpinSleeper::default();
1227
1228                // wait for the test object to trigger water injection
1229                let _ = rx_test_environment_from_mock_water_injection
1230                    .recv()
1231                    .unwrap();
1232
1233                // update of mock tank level position signals
1234                {
1235                    let mut mutex_tank_level_switch_signals =
1236                        mutex_tank_level_switch_signals.lock().unwrap();
1237                    mutex_tank_level_switch_signals.tank_level_switch_position_stabilized = true;
1238                    mutex_tank_level_switch_signals.tank_level_switch_position = true;
1239                }
1240
1241                let start_time = Instant::now();
1242                spin_sleeper.sleep(sleep_duration_two_secs);
1243
1244                let _ = channels
1245                    .signal_handler
1246                    .send_to_refill(InternalCommand::Quit);
1247                channels.signal_handler.receive_from_refill().unwrap();
1248
1249                let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
1250                let _ = channels
1251                    .signal_handler
1252                    .send_to_refill(InternalCommand::Terminate);
1253                let finish_time = Instant::now();
1254                let finalization_duration_millis =
1255                    finish_time.duration_since(start_time).as_millis();
1256
1257                assert_ge!(finalization_duration_millis, 2000u128);
1258                assert_le!(finalization_duration_millis, 2150u128);
1259
1260                // wait for the test object to finish so that mock threads receive
1261                // the termination request before the end of this (test environment) thread
1262                spin_sleeper.sleep(sleep_duration_two_secs);
1263            })
1264            .unwrap();
1265
1266        // give the test environment a little time to trigger the receive-call for signal update
1267        spin_sleeper.sleep(sleep_duration_50_millis);
1268
1269        // thread for the test object
1270        let join_handle_test_object = thread::Builder::new()
1271            .name("test_object".to_string())
1272            .spawn(move || {
1273                let mut tx_refill_to_schedule_check_for_test_case_finish =
1274                    channels.refill.tx_refill_to_schedule_check.clone();
1275
1276                refill.execute(
1277                    mutex_device_scheduler,
1278                    &mut channels.refill,
1279                    &mut mock_water_injection,
1280                    mutex_tank_level_switch_signals_for_refill,
1281                    mutex_refill_status,
1282                    Box::new(sql_interface_refill),
1283                );
1284
1285                // send Quit signal to mock threads because the test object has terminated
1286                let _ =
1287                    tx_refill_to_schedule_check_for_test_case_finish.send(InternalCommand::Quit);
1288            })
1289            .unwrap();
1290
1291        join_handle_mock_monitors
1292            .join()
1293            .expect("Mock monitor check did not finish.");
1294        join_handle_mock_schedule_check
1295            .join()
1296            .expect("Mock schedule check did not finish.");
1297        join_handle_test_environment
1298            .join()
1299            .expect("Test environment thread did not finish.");
1300        join_handle_test_object
1301            .join()
1302            .expect("Test object thread did not finish.");
1303    }
1304
1305    #[test]
1306    // This test case executes multiple iterations of the loop inside the .execute function, including triggering a refill and simulation of the switch stuck low scenario.
1307    // Test case uses test database #47.
1308    pub fn test_refill_trigger_water_injection_switch_stuck_high() {
1309        let sleep_duration_50_millis = Duration::from_millis(50);
1310        let spin_sleeper = SpinSleeper::default();
1311
1312        let (_, mut refill, mut sql_interface, sql_interface_refill) =
1313            prepare_refill_tests(None, 1, 1, Some(47));
1314
1315        let mut channels = Channels::new_for_test();
1316
1317        // test environment-specific channels
1318        let (mut tx_test_environment_to_monitors, rx_monitors_from_test_environment) =
1319            channel::<InternalCommand>(1);
1320        let (
1321            tx_mock_water_injection_to_test_environment,
1322            mut rx_test_environment_from_mock_water_injection,
1323        ) = channel(1);
1324
1325        // this test case shall output debug! macros
1326        setup_logger(LoggerConfig::default()).unwrap();
1327
1328        // empty the refill table
1329        match SqlInterface::truncate_table(&mut sql_interface, "refill".to_string()) {
1330            Ok(_) => {}
1331            Err(e) => {
1332                panic!("Could not prepare test case: {e:?}")
1333            }
1334        }
1335
1336        let mutex_device_scheduler = Arc::new(Mutex::new(0));
1337        let mutex_tank_level_switch_signals =
1338            Arc::new(Mutex::new(TankLevelSwitchSignals::new(false, false, false)));
1339        let refill_status = RefillStatus {
1340            refill_in_progress_live: false,
1341            refill_in_progress_for_database: false,
1342        };
1343        let mutex_refill_status = Arc::new(Mutex::new(refill_status));
1344
1345        let mut mock_monitors = MockMonitors::new();
1346
1347        let mut mock_water_injection = MockWaterInjection::new(
1348            Some(tx_mock_water_injection_to_test_environment),
1349            RefillErrorStates {
1350                error_switch_stuck_high: false,
1351                error_switch_stuck_low: true,
1352                error_receive_tank_level_switch_position: false,
1353                error_sql_update_failed: false,
1354                error_sql_get_historic_data_failed: false,
1355            },
1356        );
1357
1358        // thread for mock monitors
1359        let join_handle_mock_monitors = thread::Builder::new()
1360            .name("mock_monitor".to_string())
1361            .spawn(move || {
1362                mock_monitors.execute(
1363                    channels.monitors.rx_monitors_from_refill,
1364                    rx_monitors_from_test_environment,
1365                );
1366            })
1367            .unwrap();
1368
1369        // thread for mock schedule check
1370        let join_handle_mock_schedule_check = thread::Builder::new()
1371            .name("mock_schedule_check".to_string())
1372            .spawn(move || {
1373                mock_schedule_check(
1374                    &mut channels.schedule_check.tx_schedule_check_to_refill,
1375                    &mut channels.schedule_check.rx_schedule_check_from_refill,
1376                    None,
1377                    true,
1378                );
1379            })
1380            .unwrap();
1381
1382        let join_handle_test_environment = thread::Builder::new()
1383            .name("test_environment".to_string())
1384            .spawn(move || {
1385                let sleep_duration_two_secs = Duration::from_secs(2);
1386                let spin_sleeper = SpinSleeper::default();
1387
1388                // wait for the test object to trigger water injection
1389                let _ = rx_test_environment_from_mock_water_injection
1390                    .recv()
1391                    .unwrap();
1392
1393                let start_time = Instant::now();
1394                spin_sleeper.sleep(sleep_duration_two_secs);
1395
1396                let _ = channels
1397                    .signal_handler
1398                    .send_to_refill(InternalCommand::Quit);
1399                channels.signal_handler.receive_from_refill().unwrap();
1400                let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
1401
1402                let _ = channels
1403                    .signal_handler
1404                    .send_to_refill(InternalCommand::Terminate);
1405                let finish_time = Instant::now();
1406                let finalization_duration_millis =
1407                    finish_time.duration_since(start_time).as_millis();
1408
1409                println!(
1410                    "finalization_duration_millis = {}",
1411                    finalization_duration_millis
1412                );
1413
1414                assert_ge!(finalization_duration_millis, 2000u128);
1415                assert_le!(finalization_duration_millis, 2150u128);
1416
1417                // wait for the test object to finish so that mock threads receive
1418                // the termination request before the end of this (test environment) thread
1419                spin_sleeper.sleep(sleep_duration_two_secs);
1420            })
1421            .unwrap();
1422
1423        // give the test environment a little time to trigger the receive-call for signal update
1424        spin_sleeper.sleep(sleep_duration_50_millis);
1425
1426        // thread for the test object
1427        let join_handle_test_object = thread::Builder::new()
1428            .name("test_object".to_string())
1429            .spawn(move || {
1430                let mut tx_refill_to_schedule_check_for_test_case_finish =
1431                    channels.refill.tx_refill_to_schedule_check.clone();
1432
1433                refill.execute(
1434                    mutex_device_scheduler,
1435                    &mut channels.refill,
1436                    &mut mock_water_injection,
1437                    mutex_tank_level_switch_signals,
1438                    mutex_refill_status,
1439                    Box::new(sql_interface_refill),
1440                );
1441
1442                // send Quit signal to mock threads because the test object has terminated
1443                let _ =
1444                    tx_refill_to_schedule_check_for_test_case_finish.send(InternalCommand::Quit);
1445            })
1446            .unwrap();
1447
1448        join_handle_mock_monitors
1449            .join()
1450            .expect("Mock monitor check did not finish.");
1451        join_handle_mock_schedule_check
1452            .join()
1453            .expect("Mock schedule check did not finish.");
1454        join_handle_test_environment
1455            .join()
1456            .expect("Test environment thread did not finish.");
1457        join_handle_test_object
1458            .join()
1459            .expect("Test object thread did not finish.");
1460    }
1461
1462    #[test]
1463    // This test case executes one iteration of the loop inside the .execute function with triggering a refill
1464    // It checks the timing of the initial wait period and main control interval.
1465    // Test case uses test database #21.
1466    pub fn test_refill_initial_waiting_time() {
1467        let sleep_duration_50_millis = Duration::from_millis(50);
1468        let spin_sleeper = SpinSleeper::default();
1469
1470        let (config, mut refill, mut sql_interface, sql_interface_refill) =
1471            prepare_refill_tests(None, 2, 60, Some(21));
1472
1473        let mut channels = Channels::new_for_test();
1474
1475        // test environment-specific channels
1476        let (
1477            tx_mock_water_injection_to_test_environment,
1478            mut rx_test_environment_from_mock_water_injection,
1479        ) = channel(1);
1480        let (mut tx_test_environment_to_monitors, rx_monitors_from_test_environment) =
1481            channel::<InternalCommand>(1);
1482
1483        // empty the refill table
1484        match SqlInterface::truncate_table(&mut sql_interface, "refill".to_string()) {
1485            Ok(_) => {}
1486            Err(e) => {
1487                panic!("Could not prepare test case: {e:?}")
1488            }
1489        }
1490
1491        let mutex_device_scheduler = Arc::new(Mutex::new(0));
1492        let mutex_tank_level_switch_signals =
1493            Arc::new(Mutex::new(TankLevelSwitchSignals::new(false, false, false)));
1494        let refill_status = RefillStatus {
1495            refill_in_progress_live: false,
1496            refill_in_progress_for_database: false,
1497        };
1498        let mutex_refill_status = Arc::new(Mutex::new(refill_status));
1499
1500        let mut mock_monitors = MockMonitors::new();
1501
1502        let mut mock_water_injection = MockWaterInjection::new(
1503            Some(tx_mock_water_injection_to_test_environment),
1504            RefillErrorStates {
1505                error_switch_stuck_high: false,
1506                error_switch_stuck_low: false,
1507                error_receive_tank_level_switch_position: false,
1508                error_sql_update_failed: false,
1509                error_sql_get_historic_data_failed: false,
1510            },
1511        );
1512
1513        // thread for mock monitors
1514        let join_handle_mock_monitors = thread::Builder::new()
1515            .name("mock_monitor".to_string())
1516            .spawn(move || {
1517                mock_monitors.execute(
1518                    channels.monitors.rx_monitors_from_refill,
1519                    rx_monitors_from_test_environment,
1520                );
1521            })
1522            .unwrap();
1523
1524        // thread for mock schedule check
1525        let join_handle_mock_schedule_check = thread::Builder::new()
1526            .name("mock_schedule_check".to_string())
1527            .spawn(move || {
1528                mock_schedule_check(
1529                    &mut channels.schedule_check.tx_schedule_check_to_refill,
1530                    &mut channels.schedule_check.rx_schedule_check_from_refill,
1531                    None,
1532                    true,
1533                );
1534            })
1535            .unwrap();
1536
1537        let join_handle_test_environment = thread::Builder::new()
1538            .name("test_environment".to_string())
1539            .spawn(move || {
1540                let start_time = Instant::now();
1541
1542                // wait for the test object to trigger water injection
1543                let _ = rx_test_environment_from_mock_water_injection
1544                    .recv()
1545                    .unwrap();
1546
1547                // record time after the test object triggered water injection
1548                let initial_waiting_time_passed = Instant::now();
1549
1550                // now we can send the quit command
1551                let _ = channels
1552                    .signal_handler
1553                    .send_to_refill(InternalCommand::Quit);
1554                channels.signal_handler.receive_from_refill().unwrap();
1555                let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
1556                let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
1557
1558                let _ = channels
1559                    .signal_handler
1560                    .send_to_refill(InternalCommand::Terminate);
1561                let finish_time = Instant::now();
1562                let initial_wait_duration_millis = initial_waiting_time_passed
1563                    .duration_since(start_time)
1564                    .as_millis();
1565                let finalization_duration_millis = finish_time
1566                    .duration_since(initial_waiting_time_passed)
1567                    .as_millis();
1568
1569                assert_le!(
1570                    initial_wait_duration_millis,
1571                    (config.refill.initial_wait_interval * 1000 + 200) as u128
1572                );
1573                assert_ge!(
1574                    initial_wait_duration_millis,
1575                    (config.refill.initial_wait_interval * 1000) as u128
1576                );
1577                assert_le!(finalization_duration_millis, 150u128);
1578            })
1579            .unwrap();
1580
1581        // give the test environment a little time to trigger the receive-call for signal update
1582        spin_sleeper.sleep(sleep_duration_50_millis);
1583
1584        // thread for the test object
1585        let join_handle_test_object = thread::Builder::new()
1586            .name("test_object".to_string())
1587            .spawn(move || {
1588                let mut tx_refill_to_schedule_check_for_test_case_finish =
1589                    channels.refill.tx_refill_to_schedule_check.clone();
1590
1591                refill.execute(
1592                    mutex_device_scheduler,
1593                    &mut channels.refill,
1594                    &mut mock_water_injection,
1595                    mutex_tank_level_switch_signals,
1596                    mutex_refill_status,
1597                    Box::new(sql_interface_refill),
1598                );
1599
1600                // send Quit signal to mock threads because the test object has terminated
1601                let _ =
1602                    tx_refill_to_schedule_check_for_test_case_finish.send(InternalCommand::Quit);
1603            })
1604            .unwrap();
1605
1606        join_handle_mock_monitors
1607            .join()
1608            .expect("Mock monitor check did not finish.");
1609        join_handle_mock_schedule_check
1610            .join()
1611            .expect("Mock schedule check did not finish.");
1612        join_handle_test_environment
1613            .join()
1614            .expect("Test environment thread did not finish.");
1615        join_handle_test_object
1616            .join()
1617            .expect("Test object thread did not finish.");
1618    }
1619
1620    #[test]
1621    // This test case executes multiple iterations of the .execute function with the error flag error_switch_stuck_low set
1622    // during the execution of the test case, the error is reset.
1623    // Refill control shall resume operation upon having reset the error.
1624    // Test case uses test database #22.
1625    pub fn test_refill_conditions_historic_data_error_switch_stuck_low() {
1626        let sleep_duration_50_millis = Duration::from_millis(50);
1627        let spin_sleeper = SpinSleeper::default();
1628        let (_, mut refill, mut sql_interface, sql_interface_refill) =
1629            prepare_refill_tests(None, 1, 1, Some(22));
1630
1631        // set the error
1632        refill.refill_errors.error_switch_stuck_low = true;
1633
1634        let mut channels = Channels::new_for_test();
1635
1636        // test environment-specific channels
1637        let (
1638            tx_mock_water_injection_to_test_environment,
1639            mut rx_test_environment_from_mock_water_injection,
1640        ) = channel(1);
1641        let (mut tx_test_environment_to_monitors, rx_monitors_from_test_environment) =
1642            channel::<InternalCommand>(1);
1643
1644        let refill_status = RefillStatus {
1645            refill_in_progress_live: false,
1646            refill_in_progress_for_database: false,
1647        };
1648        let mutex_refill_status = Arc::new(Mutex::new(refill_status));
1649
1650        // empty the refill table
1651        match SqlInterface::truncate_table(&mut sql_interface, "refill".to_string()) {
1652            Ok(_) => {}
1653            Err(e) => {
1654                panic!("Could not prepare test case: {e:?}")
1655            }
1656        }
1657
1658        let mutex_device_scheduler = Arc::new(Mutex::new(0));
1659        let mutex_tank_level_switch_signals =
1660            Arc::new(Mutex::new(TankLevelSwitchSignals::new(false, false, false)));
1661        let mutex_tank_level_switch_signals_clone_for_test_environment =
1662            mutex_tank_level_switch_signals.clone();
1663
1664        let mut mock_monitors = MockMonitors::new();
1665
1666        let mut mock_water_injection = MockWaterInjection::new(
1667            Some(tx_mock_water_injection_to_test_environment),
1668            RefillErrorStates {
1669                error_switch_stuck_high: false,
1670                error_switch_stuck_low: false,
1671                error_receive_tank_level_switch_position: false,
1672                error_sql_update_failed: false,
1673                error_sql_get_historic_data_failed: false,
1674            },
1675        );
1676
1677        // thread for mock monitors
1678        let join_handle_mock_monitors = thread::Builder::new()
1679            .name("mock_monitor".to_string())
1680            .spawn(move || {
1681                mock_monitors.execute(
1682                    channels.monitors.rx_monitors_from_refill,
1683                    rx_monitors_from_test_environment,
1684                );
1685            })
1686            .unwrap();
1687
1688        // thread for mock schedule check
1689        let join_handle_mock_schedule_check = thread::Builder::new()
1690            .name("mock_schedule_check".to_string())
1691            .spawn(move || {
1692                mock_schedule_check(
1693                    &mut channels.schedule_check.tx_schedule_check_to_refill,
1694                    &mut channels.schedule_check.rx_schedule_check_from_refill,
1695                    None,
1696                    true,
1697                );
1698            })
1699            .unwrap();
1700
1701        let join_handle_test_environment = thread::Builder::new()
1702            .name("test_environment".to_string())
1703            .spawn(move || {
1704                let sleep_duration_two_secs = Duration::from_secs(2);
1705                let sleep_duration_two_millis = Duration::from_millis(2);
1706                let spin_sleeper = SpinSleeper::default();
1707
1708                spin_sleeper.sleep(sleep_duration_two_secs);
1709
1710                for _ in 0..999 {
1711                    assert!(matches!(
1712                        rx_test_environment_from_mock_water_injection.try_recv(),
1713                        Err(AquaChannelError::Empty)
1714                    ));
1715                    spin_sleeper.sleep(sleep_duration_two_millis);
1716                }
1717
1718                // reset errors
1719                println!("resetting errors...");
1720                match channels
1721                    .messaging
1722                    .tx_messaging_to_refill
1723                    .send(InternalCommand::ResetAllErrors)
1724                {
1725                    Ok(_) => { /* do nothing */ }
1726                    Err(e) => {
1727                        panic!(
1728                            "test_refill_conditions_historic_data_error_recent_refill_event:\
1729                        error when trying to reset refill errors from previous test runs: {e:?}"
1730                        );
1731                    }
1732                }
1733
1734                // wait for the test object to trigger water injection
1735                println!("waiting for water injection...");
1736                let _ = rx_test_environment_from_mock_water_injection
1737                    .recv()
1738                    .unwrap();
1739
1740                spin_sleeper.sleep(sleep_duration_two_secs);
1741
1742                // request update of tank level position signals to mock tank level switch
1743                println!("updating tank level switch...");
1744                {
1745                    let mut tank_level_switch_signals =
1746                        mutex_tank_level_switch_signals_clone_for_test_environment
1747                            .lock()
1748                            .unwrap();
1749                    tank_level_switch_signals.tank_level_switch_position = true;
1750                    tank_level_switch_signals.tank_level_switch_position_stabilized = true;
1751                }
1752
1753                spin_sleeper.sleep(sleep_duration_two_secs);
1754
1755                println!("sending quit signal to test object...");
1756                let _ = channels
1757                    .signal_handler
1758                    .send_to_refill(InternalCommand::Quit);
1759                channels.signal_handler.receive_from_refill().unwrap();
1760                let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
1761
1762                let _ = channels
1763                    .signal_handler
1764                    .send_to_refill(InternalCommand::Terminate);
1765
1766                // wait for the test object to finish so that mock threads receive
1767                // the termination request before the end of this (test environment) thread
1768                spin_sleeper.sleep(sleep_duration_two_secs);
1769            })
1770            .unwrap();
1771
1772        // give the test environment a little time to trigger the receive-call for signal update
1773        spin_sleeper.sleep(sleep_duration_50_millis);
1774
1775        // thread for the test object
1776        let join_handle_test_object = thread::Builder::new()
1777            .name("test_object".to_string())
1778            .spawn(move || {
1779                let mut tx_refill_to_schedule_check_for_test_case_finish =
1780                    channels.refill.tx_refill_to_schedule_check.clone();
1781
1782                refill.execute(
1783                    mutex_device_scheduler,
1784                    &mut channels.refill,
1785                    &mut mock_water_injection,
1786                    mutex_tank_level_switch_signals,
1787                    mutex_refill_status,
1788                    Box::new(sql_interface_refill),
1789                );
1790
1791                // check if there was no water injection
1792                assert_eq!(mock_water_injection.injection_recorder.is_empty(), false);
1793
1794                // send Quit signal to mock threads because the test object has terminated
1795                let _ =
1796                    tx_refill_to_schedule_check_for_test_case_finish.send(InternalCommand::Quit);
1797            })
1798            .unwrap();
1799
1800        join_handle_mock_monitors
1801            .join()
1802            .expect("Mock monitor check did not finish.");
1803        join_handle_mock_schedule_check
1804            .join()
1805            .expect("Mock schedule check did not finish.");
1806        join_handle_test_environment
1807            .join()
1808            .expect("Test environment thread did not finish.");
1809        join_handle_test_object
1810            .join()
1811            .expect("Test object thread did not finish.");
1812    }
1813
1814    #[test]
1815    // This test case executes multiple iterations of the loop inside .execute function with error flag error_sql_update_failed
1816    // The refill control shall not initiate a refill operation.
1817    // Test case uses test database #23.
1818    pub fn test_refill_conditions_error_sql_update_failed() {
1819        let sleep_duration_50_millis = Duration::from_millis(50);
1820        let spin_sleeper = SpinSleeper::default();
1821        let (_, mut refill, mut sql_interface, sql_interface_refill) =
1822            prepare_refill_tests(None, 1, 1, Some(23));
1823
1824        // set the error
1825        refill.refill_errors.error_sql_update_failed = true;
1826
1827        let mut channels = Channels::new_for_test();
1828
1829        // test environment-specific channels
1830        let (
1831            tx_mock_water_injection_to_test_environment,
1832            mut rx_test_environment_from_mock_water_injection,
1833        ) = channel(1);
1834        let (mut tx_test_environment_to_monitors, rx_monitors_from_test_environment) =
1835            channel::<InternalCommand>(1);
1836
1837        // empty the refill table
1838        match SqlInterface::truncate_table(&mut sql_interface, "refill".to_string()) {
1839            Ok(_) => {}
1840            Err(e) => {
1841                panic!("Could not prepare test case: {e:?}")
1842            }
1843        }
1844
1845        let mutex_device_scheduler = Arc::new(Mutex::new(0));
1846        let mutex_tank_level_switch_signals =
1847            Arc::new(Mutex::new(TankLevelSwitchSignals::new(false, false, false)));
1848        let refill_status = RefillStatus {
1849            refill_in_progress_live: false,
1850            refill_in_progress_for_database: false,
1851        };
1852        let mutex_refill_status = Arc::new(Mutex::new(refill_status));
1853
1854        let mut mock_monitors = MockMonitors::new();
1855
1856        let mut mock_water_injection = MockWaterInjection::new(
1857            Some(tx_mock_water_injection_to_test_environment),
1858            RefillErrorStates {
1859                error_switch_stuck_high: false,
1860                error_switch_stuck_low: false,
1861                error_receive_tank_level_switch_position: false,
1862                error_sql_update_failed: false,
1863                error_sql_get_historic_data_failed: false,
1864            },
1865        );
1866
1867        // thread for mock monitors
1868        let join_handle_mock_monitors = thread::Builder::new()
1869            .name("mock_monitor".to_string())
1870            .spawn(move || {
1871                mock_monitors.execute(
1872                    channels.monitors.rx_monitors_from_refill,
1873                    rx_monitors_from_test_environment,
1874                );
1875            })
1876            .unwrap();
1877
1878        // thread for mock schedule check
1879        let join_handle_mock_schedule_check = thread::Builder::new()
1880            .name("mock_schedule_check".to_string())
1881            .spawn(move || {
1882                mock_schedule_check(
1883                    &mut channels.schedule_check.tx_schedule_check_to_refill,
1884                    &mut channels.schedule_check.rx_schedule_check_from_refill,
1885                    None,
1886                    true,
1887                );
1888            })
1889            .unwrap();
1890
1891        // thread for mock Test environment
1892        let join_handle_test_environment = thread::Builder::new()
1893            .name("test_environment".to_string())
1894            .spawn(move || {
1895                let sleep_duration_two_secs = Duration::from_secs(2);
1896                let sleep_duration_two_millis = Duration::from_millis(2);
1897                let spin_sleeper = SpinSleeper::default();
1898
1899                spin_sleeper.sleep(sleep_duration_two_secs);
1900
1901                for _ in 0..999 {
1902                    assert!(matches!(
1903                        rx_test_environment_from_mock_water_injection.try_recv(),
1904                        Err(AquaChannelError::Empty)
1905                    ));
1906                    spin_sleeper.sleep(sleep_duration_two_millis);
1907                }
1908
1909                println!("sending quit signal to test object...");
1910                let _ = channels
1911                    .signal_handler
1912                    .send_to_refill(InternalCommand::Quit);
1913                channels.signal_handler.receive_from_refill().unwrap();
1914                let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
1915
1916                let _ = channels
1917                    .signal_handler
1918                    .send_to_refill(InternalCommand::Terminate);
1919
1920                // wait for the test object to finish so that mock threads receive
1921                // the termination request before the end of this (test environment) thread
1922                spin_sleeper.sleep(sleep_duration_two_secs);
1923            })
1924            .unwrap();
1925
1926        // give the test environment a little time to trigger the receive-call for signal update
1927        spin_sleeper.sleep(sleep_duration_50_millis);
1928
1929        // thread for the test object
1930        let join_handle_test_object = thread::Builder::new()
1931            .name("test_object".to_string())
1932            .spawn(move || {
1933                let sleep_duration_two_millis = Duration::from_millis(2);
1934                let spin_sleeper = SpinSleeper::default();
1935
1936                let mut tx_refill_to_schedule_check_for_test_case_finish =
1937                    channels.refill.tx_refill_to_schedule_check.clone();
1938
1939                refill.execute(
1940                    mutex_device_scheduler,
1941                    &mut channels.refill,
1942                    &mut mock_water_injection,
1943                    mutex_tank_level_switch_signals,
1944                    mutex_refill_status,
1945                    Box::new(sql_interface_refill),
1946                );
1947
1948                // check if there was no water injection
1949                assert_eq!(mock_water_injection.injection_recorder.is_empty(), true);
1950
1951                // send Quit signal to mock threads because the test object has terminated
1952                let _ =
1953                    tx_refill_to_schedule_check_for_test_case_finish.send(InternalCommand::Quit);
1954
1955                spin_sleeper.sleep(sleep_duration_two_millis);
1956            })
1957            .unwrap();
1958
1959        join_handle_mock_monitors
1960            .join()
1961            .expect("Mock monitor check did not finish.");
1962        join_handle_mock_schedule_check
1963            .join()
1964            .expect("Mock schedule check did not finish.");
1965        join_handle_test_environment
1966            .join()
1967            .expect("Test environment thread did not finish.");
1968        join_handle_test_object
1969            .join()
1970            .expect("Test object thread did not finish.");
1971    }
1972
1973    #[test]
1974    // This test case checks the process_external_request implementation if it correctly
1975    // processes the ResetAllErrors command.
1976    // The test case checks the struct attribute after having executed the function.
1977    // Test case uses test database #24.
1978    pub fn test_refill_process_external_request_reset_all_errors_command() {
1979        // set the errors
1980        let refill_errors = RefillErrorStates {
1981            error_switch_stuck_high: true,
1982            error_switch_stuck_low: true,
1983            error_receive_tank_level_switch_position: true,
1984            error_sql_update_failed: true,
1985            error_sql_get_historic_data_failed: true,
1986        };
1987
1988        let (_, mut refill, mut sql_interface, sql_interface_refill) =
1989            prepare_refill_tests(Some(refill_errors), 2, 60, Some(24));
1990
1991        let sleep_duration_50_millis = Duration::from_millis(50);
1992        let spin_sleeper = SpinSleeper::default();
1993
1994        let mut channels = Channels::new_for_test();
1995
1996        let (mut tx_test_environment_to_monitors, rx_monitors_from_test_environment) =
1997            channel::<InternalCommand>(1);
1998        let (
1999            tx_mock_water_injection_to_test_environment,
2000            _rx_test_environment_from_mock_water_injection,
2001        ) = channel(1);
2002
2003        let mut mock_monitors = MockMonitors::new();
2004
2005        // empty the refill table
2006        match SqlInterface::truncate_table(&mut sql_interface, "refill".to_string()) {
2007            Ok(_) => {}
2008            Err(e) => {
2009                panic!("Could not prepare test case: {e:?}")
2010            }
2011        }
2012
2013        let mutex_device_scheduler = Arc::new(Mutex::new(0));
2014        let mutex_tank_level_switch_signals =
2015            Arc::new(Mutex::new(TankLevelSwitchSignals::new(false, false, false)));
2016        let refill_status = RefillStatus {
2017            refill_in_progress_live: false,
2018            refill_in_progress_for_database: false,
2019        };
2020        let mutex_refill_status = Arc::new(Mutex::new(refill_status));
2021
2022        let mut mock_water_injection = MockWaterInjection::new(
2023            Some(tx_mock_water_injection_to_test_environment),
2024            RefillErrorStates {
2025                error_switch_stuck_high: false,
2026                error_switch_stuck_low: false,
2027                error_receive_tank_level_switch_position: false,
2028                error_sql_update_failed: false,
2029                error_sql_get_historic_data_failed: false,
2030            },
2031        );
2032
2033        // thread for mock monitors
2034        let join_handle_mock_monitors = thread::Builder::new()
2035            .name("mock_monitor".to_string())
2036            .spawn(move || {
2037                mock_monitors.execute(
2038                    channels.monitors.rx_monitors_from_refill,
2039                    rx_monitors_from_test_environment,
2040                );
2041            })
2042            .unwrap();
2043
2044        // thread for mock schedule check
2045        let join_handle_mock_schedule_check = thread::Builder::new()
2046            .name("mock_schedule_check".to_string())
2047            .spawn(move || {
2048                mock_schedule_check(
2049                    &mut channels.schedule_check.tx_schedule_check_to_refill,
2050                    &mut channels.schedule_check.rx_schedule_check_from_refill,
2051                    None,
2052                    true,
2053                );
2054            })
2055            .unwrap();
2056
2057        let join_handle_test_environment = thread::Builder::new()
2058            .name("test_environment".to_string())
2059            .spawn(move || {
2060                let sleep_duration_two_secs = Duration::from_secs(2);
2061                let spin_sleeper = SpinSleeper::default();
2062
2063                spin_sleeper.sleep(sleep_duration_two_secs);
2064                let result = channels
2065                    .messaging
2066                    .tx_messaging_to_refill
2067                    .send(InternalCommand::ResetAllErrors);
2068                assert!(result.is_ok());
2069                spin_sleeper.sleep(sleep_duration_two_secs);
2070
2071                let _ = channels
2072                    .signal_handler
2073                    .send_to_refill(InternalCommand::Quit);
2074                channels.signal_handler.receive_from_refill().unwrap();
2075                let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
2076
2077                let _ = channels
2078                    .signal_handler
2079                    .send_to_refill(InternalCommand::Terminate);
2080
2081                // wait for the test object to finish so that mock threads receive
2082                // the termination request before the end of this (test environment) thread
2083                spin_sleeper.sleep(sleep_duration_two_secs);
2084            })
2085            .unwrap();
2086
2087        // give the test environment a little time to trigger the receive-call for signal update
2088        spin_sleeper.sleep(sleep_duration_50_millis);
2089
2090        // thread for the test object
2091        let join_handle_test_object = thread::Builder::new()
2092            .name("test_object".to_string())
2093            .spawn(move || {
2094                let sleep_duration_two_millis = Duration::from_millis(2);
2095                let spin_sleeper = SpinSleeper::default();
2096
2097                // check if errors have been set
2098                assert_eq!(refill.refill_errors.has_error(), true);
2099
2100                let mut tx_refill_to_schedule_check_for_test_case_finish =
2101                    channels.refill.tx_refill_to_schedule_check.clone();
2102
2103                refill.execute(
2104                    mutex_device_scheduler,
2105                    &mut channels.refill,
2106                    &mut mock_water_injection,
2107                    mutex_tank_level_switch_signals,
2108                    mutex_refill_status,
2109                    Box::new(sql_interface_refill),
2110                );
2111
2112                // send Quit signal to mock threads because the test object has terminated
2113                let _ =
2114                    tx_refill_to_schedule_check_for_test_case_finish.send(InternalCommand::Quit);
2115
2116                println!("{}", refill.refill_errors);
2117
2118                // check if errors have been reset
2119                assert_eq!(refill.refill_errors.has_error(), false);
2120
2121                spin_sleeper.sleep(sleep_duration_two_millis);
2122            })
2123            .unwrap();
2124
2125        join_handle_mock_monitors
2126            .join()
2127            .expect("Mock monitor check did not finish.");
2128        join_handle_mock_schedule_check
2129            .join()
2130            .expect("Mock schedule check did not finish.");
2131        join_handle_test_environment
2132            .join()
2133            .expect("Test environment thread did not finish.");
2134        join_handle_test_object
2135            .join()
2136            .expect("Test object thread did not finish.");
2137    }
2138
2139    #[test]
2140    // Executes loop of the .execute function manipulating the sensor signals only and giving a negative schedule check.
2141    // Checks if the test object correctly processes the negative schedule check result.
2142    // Test case uses test database #25.
2143    pub fn test_refill_blocked_by_schedule() {
2144        let sleep_duration_50_millis = Duration::from_millis(50);
2145        let spin_sleeper = SpinSleeper::default();
2146        let (_, mut refill, mut sql_interface, sql_interface_refill) =
2147            prepare_refill_tests(None, 1, 1, Some(25));
2148
2149        let mut channels = Channels::new_for_test();
2150
2151        let (
2152            tx_mock_water_injection_to_test_environment,
2153            mut rx_test_environment_from_mock_water_injection,
2154        ) = channel(1);
2155        let (mut tx_test_environment_to_monitors, rx_monitors_from_test_environment) =
2156            channel::<InternalCommand>(1);
2157
2158        // empty the refill table
2159        match SqlInterface::truncate_table(&mut sql_interface, "refill".to_string()) {
2160            Ok(_) => {}
2161            Err(e) => {
2162                panic!("Could not prepare test case: {e:?}")
2163            }
2164        }
2165
2166        let mutex_device_scheduler = Arc::new(Mutex::new(0));
2167        let mutex_tank_level_switch_signals =
2168            Arc::new(Mutex::new(TankLevelSwitchSignals::new(false, false, false)));
2169        let refill_status = RefillStatus {
2170            refill_in_progress_live: false,
2171            refill_in_progress_for_database: false,
2172        };
2173        let mutex_refill_status = Arc::new(Mutex::new(refill_status));
2174
2175        let mut mock_monitors = MockMonitors::new();
2176
2177        let mut mock_water_injection = MockWaterInjection::new(
2178            Some(tx_mock_water_injection_to_test_environment),
2179            RefillErrorStates {
2180                error_switch_stuck_high: false,
2181                error_switch_stuck_low: false,
2182                error_receive_tank_level_switch_position: false,
2183                error_sql_update_failed: false,
2184                error_sql_get_historic_data_failed: false,
2185            },
2186        );
2187
2188        // thread for mock monitors
2189        let join_handle_mock_monitors = thread::Builder::new()
2190            .name("mock_monitor".to_string())
2191            .spawn(move || {
2192                mock_monitors.execute(
2193                    channels.monitors.rx_monitors_from_refill,
2194                    rx_monitors_from_test_environment,
2195                );
2196            })
2197            .unwrap();
2198
2199        // thread for mock schedule check
2200        let join_handle_mock_schedule_check = thread::Builder::new()
2201            .name("mock_schedule_check".to_string())
2202            .spawn(move || {
2203                mock_schedule_check(
2204                    &mut channels.schedule_check.tx_schedule_check_to_refill,
2205                    &mut channels.schedule_check.rx_schedule_check_from_refill,
2206                    None,
2207                    false,
2208                );
2209            })
2210            .unwrap();
2211
2212        let join_handle_test_environment = thread::Builder::new()
2213            .name("test_environment".to_string())
2214            .spawn(move || {
2215                let sleep_duration_two_secs = Duration::from_secs(2);
2216                let sleep_duration_two_millis = Duration::from_millis(2);
2217                let spin_sleeper = SpinSleeper::default();
2218
2219                spin_sleeper.sleep(sleep_duration_two_secs);
2220
2221                for _ in 0..999 {
2222                    assert!(matches!(
2223                        rx_test_environment_from_mock_water_injection.try_recv(),
2224                        Err(AquaChannelError::Empty)
2225                    ));
2226                    spin_sleeper.sleep(sleep_duration_two_millis);
2227                }
2228
2229                println!("sending quit signal to test object...");
2230                let _ = channels
2231                    .signal_handler
2232                    .send_to_refill(InternalCommand::Quit);
2233                channels.signal_handler.receive_from_refill().unwrap();
2234                let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
2235
2236                let _ = channels
2237                    .signal_handler
2238                    .send_to_refill(InternalCommand::Terminate);
2239
2240                // wait for the test object to finish so that mock threads receive
2241                // the termination request before the end of this (test environment) thread
2242                spin_sleeper.sleep(sleep_duration_two_secs);
2243            })
2244            .unwrap();
2245
2246        // give the test environment a little time to trigger the receive-call for signal update
2247        spin_sleeper.sleep(sleep_duration_50_millis);
2248
2249        // thread for the test object
2250        let join_handle_test_object = thread::Builder::new()
2251            .name("test_object".to_string())
2252            .spawn(move || {
2253                let sleep_duration_two_millis = Duration::from_millis(2);
2254                let spin_sleeper = SpinSleeper::default();
2255
2256                let mut tx_refill_to_schedule_check_for_test_case_finish =
2257                    channels.refill.tx_refill_to_schedule_check.clone();
2258
2259                refill.execute(
2260                    mutex_device_scheduler,
2261                    &mut channels.refill,
2262                    &mut mock_water_injection,
2263                    mutex_tank_level_switch_signals,
2264                    mutex_refill_status,
2265                    Box::new(sql_interface_refill),
2266                );
2267
2268                // check if there was no water injection
2269                assert_eq!(mock_water_injection.injection_recorder.is_empty(), true);
2270
2271                // send Quit signal to mock threads because the test object has terminated
2272                let _ =
2273                    tx_refill_to_schedule_check_for_test_case_finish.send(InternalCommand::Quit);
2274
2275                spin_sleeper.sleep(sleep_duration_two_millis);
2276            })
2277            .unwrap();
2278
2279        join_handle_mock_monitors
2280            .join()
2281            .expect("Mock monitor check did not finish.");
2282        join_handle_mock_schedule_check
2283            .join()
2284            .expect("Mock schedule check did not finish.");
2285        join_handle_test_environment
2286            .join()
2287            .expect("Test environment thread did not finish.");
2288        join_handle_test_object
2289            .join()
2290            .expect("Test object thread did not finish.");
2291    }
2292
2293    #[test]
2294    // Test case executes the loop of the .execute function manipulating the sensor signals only
2295    // and giving a positive schedule check.
2296    // Test case checks if the test object correctly processes the inhibition requested via the channel.
2297    // Test case uses test database #26.
2298    pub fn test_messaging_stops_starts_refill() {
2299        let sleep_duration_50_millis = Duration::from_millis(50);
2300        let spin_sleeper = SpinSleeper::default();
2301        let (_, mut refill, mut sql_interface, sql_interface_refill) =
2302            prepare_refill_tests(None, 10, 60, Some(26));
2303
2304        let mut channels = Channels::new_for_test();
2305
2306        let (
2307            tx_mock_water_injection_to_test_environment,
2308            mut rx_test_environment_from_mock_water_injection,
2309        ) = channel(1);
2310        let (mut tx_test_environment_to_monitors, rx_monitors_from_test_environment) =
2311            channel::<InternalCommand>(1);
2312
2313        // empty the refill table
2314        match SqlInterface::truncate_table(&mut sql_interface, "refill".to_string()) {
2315            Ok(_) => {}
2316            Err(e) => {
2317                panic!("Could not prepare test case: {e:?}")
2318            }
2319        }
2320
2321        let mut mock_monitors = MockMonitors::new();
2322
2323        let mutex_device_scheduler = Arc::new(Mutex::new(0));
2324        let mutex_tank_level_switch_signals =
2325            Arc::new(Mutex::new(TankLevelSwitchSignals::new(false, false, false)));
2326        let mutex_tank_level_switch_signals_clone_for_test_environment =
2327            mutex_tank_level_switch_signals.clone();
2328        let refill_status = RefillStatus {
2329            refill_in_progress_live: false,
2330            refill_in_progress_for_database: false,
2331        };
2332        let mutex_refill_status = Arc::new(Mutex::new(refill_status));
2333
2334        let mut mock_water_injection = MockWaterInjection::new(
2335            Some(tx_mock_water_injection_to_test_environment),
2336            RefillErrorStates {
2337                error_switch_stuck_high: false,
2338                error_switch_stuck_low: false,
2339                error_receive_tank_level_switch_position: false,
2340                error_sql_update_failed: false,
2341                error_sql_get_historic_data_failed: false,
2342            },
2343        );
2344
2345        // thread for mock monitors
2346        let join_handle_mock_monitors = thread::Builder::new()
2347            .name("mock_monitor".to_string())
2348            .spawn(move || {
2349                mock_monitors.execute(
2350                    channels.monitors.rx_monitors_from_refill,
2351                    rx_monitors_from_test_environment,
2352                );
2353            })
2354            .unwrap();
2355
2356        // thread for mock schedule check
2357        let join_handle_mock_schedule_check = thread::Builder::new()
2358            .name("mock_schedule_check".to_string())
2359            .spawn(move || {
2360                mock_schedule_check(
2361                    &mut channels.schedule_check.tx_schedule_check_to_refill,
2362                    &mut channels.schedule_check.rx_schedule_check_from_refill,
2363                    None,
2364                    true,
2365                );
2366            })
2367            .unwrap();
2368
2369        let join_handle_test_environment = thread::Builder::new()
2370            .name("test_environment".to_string())
2371            .spawn(move || {
2372                let sleep_duration_one_sec = Duration::from_secs(1);
2373                let sleep_duration_two_secs = Duration::from_secs(2);
2374                let sleep_duration_two_millis = Duration::from_millis(2);
2375                let spin_sleeper = SpinSleeper::default();
2376
2377                spin_sleeper.sleep(sleep_duration_one_sec);
2378
2379                match channels
2380                    .messaging
2381                    .tx_messaging_to_refill
2382                    .send(InternalCommand::Stop)
2383                {
2384                    Ok(()) => { /* do nothing */ }
2385                    Err(e) => {
2386                        panic!(
2387                            "{}: could not send stop command to refill ({e:?})",
2388                            module_path!()
2389                        );
2390                    }
2391                }
2392
2393                spin_sleeper.sleep(sleep_duration_one_sec);
2394
2395                {
2396                    let mut tank_level_switch_signals =
2397                        mutex_tank_level_switch_signals_clone_for_test_environment
2398                            .lock()
2399                            .unwrap();
2400                    tank_level_switch_signals.tank_level_switch_position = false;
2401                    tank_level_switch_signals.tank_level_switch_position_stabilized = false;
2402                }
2403
2404                // expect to see no event
2405                for _ in 0..999 {
2406                    assert!(matches!(
2407                        rx_test_environment_from_mock_water_injection.try_recv(),
2408                        Err(AquaChannelError::Empty)
2409                    ));
2410                    spin_sleeper.sleep(sleep_duration_two_millis);
2411                }
2412
2413                spin_sleeper.sleep(sleep_duration_one_sec);
2414
2415                match channels
2416                    .messaging
2417                    .tx_messaging_to_refill
2418                    .send(InternalCommand::Start)
2419                {
2420                    Ok(()) => { /* do nothing */ }
2421                    Err(e) => {
2422                        panic!(
2423                            "{}: could not send start command to refill ({e:?})",
2424                            module_path!()
2425                        );
2426                    }
2427                }
2428
2429                // expect to see one event
2430                assert!(matches!(
2431                    rx_test_environment_from_mock_water_injection.recv(),
2432                    Ok(TestCommand::InformWaterInjectionEvent)
2433                ));
2434
2435                println!("sending quit signal to test object...");
2436                let _ = channels
2437                    .signal_handler
2438                    .send_to_refill(InternalCommand::Quit);
2439                channels.signal_handler.receive_from_refill().unwrap();
2440                let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
2441
2442                let _ = channels
2443                    .signal_handler
2444                    .send_to_refill(InternalCommand::Terminate);
2445
2446                // wait for the test object to finish so that mock threads receive
2447                // the termination request before the end of this (test environment) thread
2448                spin_sleeper.sleep(sleep_duration_two_secs);
2449            })
2450            .unwrap();
2451
2452        // give the test environment a little time to trigger the receive-call for signal update
2453        spin_sleeper.sleep(sleep_duration_50_millis);
2454
2455        // thread for the test object
2456        let join_handle_test_object = thread::Builder::new()
2457            .name("test_object".to_string())
2458            .spawn(move || {
2459                let sleep_duration_two_millis = Duration::from_millis(2);
2460                let spin_sleeper = SpinSleeper::default();
2461
2462                let mut tx_refill_to_schedule_check_for_test_case_finish =
2463                    channels.refill.tx_refill_to_schedule_check.clone();
2464
2465                refill.execute(
2466                    mutex_device_scheduler,
2467                    &mut channels.refill,
2468                    &mut mock_water_injection,
2469                    mutex_tank_level_switch_signals,
2470                    mutex_refill_status,
2471                    Box::new(sql_interface_refill),
2472                );
2473
2474                // check if there was only one water injection
2475                assert_eq!(mock_water_injection.injection_recorder.len(), 1);
2476
2477                // send Quit signal to mock threads because the test object has terminated
2478                let _ =
2479                    tx_refill_to_schedule_check_for_test_case_finish.send(InternalCommand::Quit);
2480
2481                spin_sleeper.sleep(sleep_duration_two_millis);
2482            })
2483            .unwrap();
2484
2485        join_handle_mock_monitors
2486            .join()
2487            .expect("Mock monitor check did not finish.");
2488        join_handle_mock_schedule_check
2489            .join()
2490            .expect("Mock schedule check did not finish.");
2491        join_handle_test_environment
2492            .join()
2493            .expect("Test environment thread did not finish.");
2494        join_handle_test_object
2495            .join()
2496            .expect("Test object thread did not finish.");
2497    }
2498
2499    #[test]
2500    // This test case executes multiple iterations of the loop inside .execute function
2501    // using a mock version of SQL interface which returns only errors.
2502    // The refill control shall not initiate a refill operation.
2503    // Test case uses test database #27.
2504    pub fn test_refill_conditions_error_get_historic_data_failed() {
2505        let sleep_duration_50_millis = Duration::from_millis(50);
2506        let spin_sleeper = SpinSleeper::default();
2507        let (_, mut refill, sql_interface_refill) =
2508            prepare_refill_tests_with_mock_database_interface(None, 1, 1, 27);
2509        // this test case does not use the SQL database, so no conflict arises
2510        // when concurrently executing other test cases on the same DB.
2511
2512        let mut channels = Channels::new_for_test();
2513
2514        let (
2515            tx_mock_water_injection_to_test_environment,
2516            mut rx_test_environment_from_mock_water_injection,
2517        ) = channel(1);
2518        let (mut tx_test_environment_to_monitors, rx_monitors_from_test_environment) =
2519            channel::<InternalCommand>(1);
2520
2521        let mutex_device_scheduler = Arc::new(Mutex::new(0));
2522        let mutex_tank_level_switch_signals =
2523            Arc::new(Mutex::new(TankLevelSwitchSignals::new(false, false, false)));
2524        let refill_status = RefillStatus {
2525            refill_in_progress_live: false,
2526            refill_in_progress_for_database: false,
2527        };
2528        let mutex_refill_status = Arc::new(Mutex::new(refill_status));
2529
2530        let mut mock_monitors = MockMonitors::new();
2531
2532        let mut mock_water_injection = MockWaterInjection::new(
2533            Some(tx_mock_water_injection_to_test_environment),
2534            RefillErrorStates {
2535                error_switch_stuck_high: false,
2536                error_switch_stuck_low: false,
2537                error_receive_tank_level_switch_position: false,
2538                error_sql_update_failed: false,
2539                error_sql_get_historic_data_failed: false,
2540            },
2541        );
2542
2543        // thread for mock monitors
2544        let join_handle_mock_monitors = thread::Builder::new()
2545            .name("mock_monitor".to_string())
2546            .spawn(move || {
2547                mock_monitors.execute(
2548                    channels.monitors.rx_monitors_from_refill,
2549                    rx_monitors_from_test_environment,
2550                );
2551            })
2552            .unwrap();
2553
2554        // thread for mock schedule check
2555        let join_handle_mock_schedule_check = thread::Builder::new()
2556            .name("mock_schedule_check".to_string())
2557            .spawn(move || {
2558                mock_schedule_check(
2559                    &mut channels.schedule_check.tx_schedule_check_to_refill,
2560                    &mut channels.schedule_check.rx_schedule_check_from_refill,
2561                    None,
2562                    true,
2563                );
2564            })
2565            .unwrap();
2566
2567        // thread for mock Test environment
2568        let join_handle_test_environment = thread::Builder::new()
2569            .name("test_environment".to_string())
2570            .spawn(move || {
2571                let sleep_duration_two_secs = Duration::from_secs(2);
2572                let sleep_duration_two_millis = Duration::from_millis(2);
2573                let spin_sleeper = SpinSleeper::default();
2574
2575                spin_sleeper.sleep(sleep_duration_two_secs);
2576
2577                for _ in 0..999 {
2578                    assert!(matches!(
2579                        rx_test_environment_from_mock_water_injection.try_recv(),
2580                        Err(AquaChannelError::Empty)
2581                    ));
2582                    spin_sleeper.sleep(sleep_duration_two_millis);
2583                }
2584
2585                println!("sending quit signal to test object...");
2586                let _ = channels
2587                    .signal_handler
2588                    .send_to_refill(InternalCommand::Quit);
2589                channels.signal_handler.receive_from_refill().unwrap();
2590                let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
2591
2592                let _ = channels
2593                    .signal_handler
2594                    .send_to_refill(InternalCommand::Terminate);
2595
2596                // wait for the test object to finish so that mock threads receive
2597                // the termination request before the end of this (test environment) thread
2598                spin_sleeper.sleep(sleep_duration_two_secs);
2599            })
2600            .unwrap();
2601
2602        // give the test environment a little time to trigger the receive-call for signal update
2603        spin_sleeper.sleep(sleep_duration_50_millis);
2604
2605        // thread for the test object
2606        let join_handle_test_object = thread::Builder::new()
2607            .name("test_object".to_string())
2608            .spawn(move || {
2609                let sleep_duration_two_millis = Duration::from_millis(2);
2610                let spin_sleeper = SpinSleeper::default();
2611
2612                let mut tx_refill_to_schedule_check_for_test_case_finish =
2613                    channels.refill.tx_refill_to_schedule_check.clone();
2614
2615                refill.execute(
2616                    mutex_device_scheduler,
2617                    &mut channels.refill,
2618                    &mut mock_water_injection,
2619                    mutex_tank_level_switch_signals,
2620                    mutex_refill_status,
2621                    Box::new(sql_interface_refill),
2622                );
2623
2624                // check if there was no water injection
2625                assert_eq!(mock_water_injection.injection_recorder.is_empty(), true);
2626
2627                // send Quit signal to mock threads because the test object has terminated
2628                let _ =
2629                    tx_refill_to_schedule_check_for_test_case_finish.send(InternalCommand::Quit);
2630
2631                spin_sleeper.sleep(sleep_duration_two_millis);
2632            })
2633            .unwrap();
2634
2635        join_handle_mock_monitors
2636            .join()
2637            .expect("Mock monitor check did not finish.");
2638        join_handle_mock_schedule_check
2639            .join()
2640            .expect("Mock schedule check did not finish.");
2641        join_handle_test_environment
2642            .join()
2643            .expect("Test environment thread did not finish.");
2644        join_handle_test_object
2645            .join()
2646            .expect("Test object thread did not finish.");
2647    }
2648
2649    #[test]
2650    // This test case executes the check_volume_count_time_limits function of refill.
2651    // It also instantiates Monitor to capture the monitor view sent via the channel.
2652    pub fn test_refill_check_volume_count_time_limits() {
2653        let mut mock_monitors = MockMonitors::new();
2654        let sleep_duration_200_millis = Duration::from_millis(200);
2655        let spin_sleeper = SpinSleeper::default();
2656
2657        let mut channels = Channels::new_for_test();
2658
2659        let (mut tx_test_environment_to_monitors, rx_monitors_from_test_environment) =
2660            channel::<InternalCommand>(1);
2661
2662        // thread for mock monitors
2663        let join_handle_mock_monitors = thread::Builder::new()
2664            .name("mock_monitor".to_string())
2665            .spawn(move || {
2666                mock_monitors.execute(
2667                    channels.monitors.rx_monitors_from_refill,
2668                    rx_monitors_from_test_environment,
2669                );
2670                println!("{}", mock_monitors);
2671                let (_, refill_monitor_view) = mock_monitors.refill_monitor_views.pop().unwrap();
2672                refill_monitor_view.assert_refill_monitor_view(false, false, false, false, false);
2673
2674                let (_, refill_monitor_view) = mock_monitors.refill_monitor_views.pop().unwrap();
2675                refill_monitor_view.assert_refill_monitor_view(false, false, false, false, true);
2676
2677                let (_, refill_monitor_view) = mock_monitors.refill_monitor_views.pop().unwrap();
2678                refill_monitor_view.assert_refill_monitor_view(false, false, true, false, false);
2679
2680                let (_, refill_monitor_view) = mock_monitors.refill_monitor_views.pop().unwrap();
2681                refill_monitor_view.assert_refill_monitor_view(true, false, false, false, false);
2682
2683                let (_, refill_monitor_view) = mock_monitors.refill_monitor_views.pop().unwrap();
2684                refill_monitor_view.assert_refill_monitor_view(false, false, false, true, false);
2685
2686                let (_, refill_monitor_view) = mock_monitors.refill_monitor_views.pop().unwrap();
2687                refill_monitor_view.assert_refill_monitor_view(false, true, false, false, false);
2688
2689                let (_, refill_monitor_view) = mock_monitors.refill_monitor_views.pop().unwrap();
2690                refill_monitor_view.assert_refill_monitor_view(false, false, false, false, false);
2691            })
2692            .unwrap();
2693
2694        let (config, mut refill, _sql_interface_refill) =
2695            prepare_refill_tests_with_mock_database_interface(None, 1, 1, 27);
2696        // this test case does not use the SQL database, so no conflict arises
2697        // when concurrently executing other test cases on the same DB.
2698
2699        let mut sql_refill_stats_data = RefillStatsData::new();
2700        sql_refill_stats_data.duration_since_last_refill =
2701            config.refill.min_interval_last_refill + 1;
2702
2703        // first execution without any errors
2704        assert_eq!(
2705            refill.check_volume_count_time_limits(&sql_refill_stats_data, &mut channels.refill),
2706            false
2707        );
2708        spin_sleeper.sleep(sleep_duration_200_millis);
2709
2710        // induce errors before executing again
2711        sql_refill_stats_data.refill_count_last_24h = config.refill.max_refill_count_24h + 1;
2712
2713        assert_eq!(
2714            refill.check_volume_count_time_limits(&sql_refill_stats_data, &mut channels.refill),
2715            true
2716        );
2717        spin_sleeper.sleep(sleep_duration_200_millis);
2718
2719        // reset the previous error and induce other error before executing again
2720        sql_refill_stats_data.refill_count_last_24h = 0;
2721        sql_refill_stats_data.refill_count_last_hour = config.refill.max_refill_count_hour + 1;
2722
2723        assert_eq!(
2724            refill.check_volume_count_time_limits(&sql_refill_stats_data, &mut channels.refill),
2725            true
2726        );
2727        spin_sleeper.sleep(sleep_duration_200_millis);
2728
2729        // reset the previous error and induce other error before executing again
2730        sql_refill_stats_data.refill_count_last_hour = 0;
2731        sql_refill_stats_data.refill_volume_last_24h = config.refill.max_refill_volume_24h + 1.0;
2732
2733        assert_eq!(
2734            refill.check_volume_count_time_limits(&sql_refill_stats_data, &mut channels.refill),
2735            true
2736        );
2737        spin_sleeper.sleep(sleep_duration_200_millis);
2738
2739        // reset the previous error and induce other error before executing again
2740        sql_refill_stats_data.refill_volume_last_24h = 0.0;
2741        sql_refill_stats_data.refill_volume_last_hour = config.refill.max_refill_volume_hour + 1.0;
2742
2743        assert_eq!(
2744            refill.check_volume_count_time_limits(&sql_refill_stats_data, &mut channels.refill),
2745            true
2746        );
2747        spin_sleeper.sleep(sleep_duration_200_millis);
2748
2749        // reset the previous error and induce other error before executing again
2750        sql_refill_stats_data.refill_volume_last_hour = 0.0;
2751        sql_refill_stats_data.duration_since_last_refill =
2752            config.refill.min_interval_last_refill - 1;
2753
2754        assert_eq!(
2755            refill.check_volume_count_time_limits(&sql_refill_stats_data, &mut channels.refill),
2756            true
2757        );
2758        spin_sleeper.sleep(sleep_duration_200_millis);
2759
2760        // reset the previous error and do not induce any further error
2761        sql_refill_stats_data.duration_since_last_refill =
2762            config.refill.min_interval_last_refill + 1;
2763        assert_eq!(
2764            refill.check_volume_count_time_limits(&sql_refill_stats_data, &mut channels.refill),
2765            false
2766        );
2767        spin_sleeper.sleep(sleep_duration_200_millis);
2768
2769        let _ = tx_test_environment_to_monitors.send(InternalCommand::Quit);
2770        // give thread time to finish
2771        spin_sleeper.sleep(sleep_duration_200_millis);
2772
2773        join_handle_mock_monitors
2774            .join()
2775            .expect("Mock monitor check did not finish.");
2776    }
2777}