aquarium_control/water/
refill_channels.rs

1/* Copyright 2025 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*/
9use crate::launch::channels::{AquaChannelError, AquaReceiver, AquaSender};
10use crate::utilities::channel_content::InternalCommand;
11use crate::water::refill_monitor_view::RefillMonitorView;
12use std::fmt;
13
14/// Struct collects the channels for the communication between the refill control and the other components.
15/// Additionally, the struct contains counters for debug purposes.
16/// The implementation of the struct contains functions for those channels which are frequently used.
17/// The functions increment the usage counters.
18pub struct RefillChannels {
19    /// Sender part of the channel for communication to relay manager
20    pub tx_refill_to_relay_manager: AquaSender<InternalCommand>,
21
22    /// Receiver part of the channel for communication from the relay manager
23    pub rx_refill_from_relay_manager: AquaReceiver<bool>,
24
25    /// Sender part of the channel for communication to the signal handler
26    pub tx_refill_to_signal_handler: AquaSender<bool>,
27
28    /// Receiver part of the channel for communication from the signal handler
29    pub rx_refill_from_signal_handler: AquaReceiver<InternalCommand>,
30
31    /// Sender part of the channel for communication to the schedule checker
32    pub tx_refill_to_schedule_check: AquaSender<InternalCommand>,
33
34    /// Receiver part of the channel for communication from the schedule checker
35    pub rx_refill_from_schedule_check: AquaReceiver<bool>,
36
37    /// Receiver part of the channel for communication from the messaging
38    pub rx_refill_from_messaging_opt: Option<AquaReceiver<InternalCommand>>,
39
40    /// Sender part of the channel for communication to the monitors
41    pub tx_refill_to_monitors: AquaSender<RefillMonitorView>,
42}
43
44impl RefillChannels {
45    pub fn send_to_relay_manager(
46        &mut self,
47        command: InternalCommand,
48    ) -> Result<(), AquaChannelError> {
49        self.tx_refill_to_relay_manager.send(command)
50    }
51
52    pub fn receive_from_relay_manager(&mut self) -> Result<bool, AquaChannelError> {
53        self.rx_refill_from_relay_manager.recv()
54    }
55
56    pub fn send_to_schedule_check(
57        &mut self,
58        command: InternalCommand,
59    ) -> Result<(), AquaChannelError> {
60        self.tx_refill_to_schedule_check.send(command)
61    }
62
63    pub fn receive_from_schedule_check(&mut self) -> Result<bool, AquaChannelError> {
64        self.rx_refill_from_schedule_check.recv()
65    }
66
67    pub fn send_to_monitor(&mut self, view: RefillMonitorView) -> Result<(), AquaChannelError> {
68        self.tx_refill_to_monitors.send(view)
69    }
70
71    pub fn send_to_signal_handler(&mut self, status: bool) -> Result<(), AquaChannelError> {
72        self.tx_refill_to_signal_handler.send(status)
73    }
74}
75
76#[cfg(feature = "debug_channels")]
77impl fmt::Display for RefillChannels {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        writeln!(f, "=== RefillChannels ===")?;
80        writeln!(
81            f,
82            "tx_refill_to_relay_manager: {}",
83            self.tx_refill_to_relay_manager.count
84        )?;
85        writeln!(
86            f,
87            "rx_refill_from_relay_manager: {}",
88            self.rx_refill_from_relay_manager.count
89        )?;
90        writeln!(
91            f,
92            "tx_refill_to_signal_handler: {}",
93            self.tx_refill_to_signal_handler.count
94        )?;
95        writeln!(
96            f,
97            "rx_refill_from_signal_handler: {}",
98            self.rx_refill_from_signal_handler.count
99        )?;
100        writeln!(
101            f,
102            "tx_refill_to_schedule_check: {}",
103            self.tx_refill_to_schedule_check.count
104        )?;
105        writeln!(
106            f,
107            "rx_refill_from_schedule_check: {}",
108            self.rx_refill_from_schedule_check.count
109        )?;
110        writeln!(
111            f,
112            "rx_refill_from_messaging_opt: {}",
113            self.rx_refill_from_messaging_opt.as_ref().unwrap().count
114        )?;
115        write!(
116            f,
117            "tx_refill_to_monitors: {}",
118            self.tx_refill_to_monitors.count
119        )
120    }
121}
122
123#[cfg(not(feature = "debug_channels"))]
124impl fmt::Display for RefillChannels {
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        write!(
127            f,
128            "Channel counters are not active. Use --features \"debug_channels\" to enable them."
129        )
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use crate::launch::channels::Channels;
137
138    /// A helper function to create a `RefillChannels` instance with the mock channel ends for testing.
139    ///
140    /// This function sets up all the necessary channels and returns the `RefillChannels`
141    /// struct along with the "other ends" of the channels that the test needs to interact with
142    /// or assert against.
143    fn setup_channels() -> (
144        RefillChannels,
145        AquaReceiver<InternalCommand>, // Test receiver for schedule_check
146        AquaSender<bool>,              // Test sender for schedule_check response
147        AquaReceiver<RefillMonitorView>, // Test receiver for monitors
148    ) {
149        let channels = Channels::new_for_test();
150        (
151            channels.refill,
152            channels.schedule_check.rx_schedule_check_from_refill,
153            channels.schedule_check.tx_schedule_check_to_refill,
154            channels.monitors.rx_monitors_from_refill,
155        )
156    }
157
158    #[test]
159    fn test_send_to_schedule_check_sends_command_and_increments_counter() {
160        // Arrange
161        let (mut channels, mut rx_schedule_test, _, _) = setup_channels();
162        let command = InternalCommand::Quit; // Use any variant for the test
163
164        // Act
165        let result = channels.send_to_schedule_check(command.clone());
166
167        // Assert
168        assert!(result.is_ok(), "Sending should not fail");
169        let received_command = rx_schedule_test.recv().unwrap();
170        assert_eq!(
171            received_command, command,
172            "The correct command should be received"
173        );
174
175        #[cfg(feature = "debug_channels")]
176        {
177            assert_eq!(
178                channels.tx_refill_to_schedule_check, 1,
179                "Counter should be incremented"
180            );
181        }
182    }
183
184    #[test]
185    fn test_receive_from_schedule_check_receives_response_and_increments_counter() {
186        // Arrange
187        let (mut channels, _, mut tx_schedule_resp_test, _) = setup_channels();
188        let expected_response = true;
189        tx_schedule_resp_test.send(expected_response).unwrap();
190
191        // Act
192        let result = channels.receive_from_schedule_check();
193
194        // Assert
195        assert!(result.is_ok(), "Receiving should not fail");
196        assert_eq!(
197            result.unwrap(),
198            expected_response,
199            "The correct response should be received"
200        );
201
202        #[cfg(feature = "debug_channels")]
203        {
204            assert_eq!(
205                channels.rx_refill_from_schedule_check.count, 1,
206                "Counter should be incremented"
207            );
208        }
209    }
210
211    #[test]
212    fn test_send_to_monitor_sends_view_and_increments_counter() {
213        // Arrange
214        let (mut channels, _, _, mut rx_monitor_test) = setup_channels();
215        let view = RefillMonitorView::default();
216
217        // Act
218        let result = channels.send_to_monitor(view);
219
220        // Assert
221        assert!(result.is_ok(), "Sending should not fail");
222        let received_view = rx_monitor_test.recv().unwrap();
223        assert_eq!(
224            received_view,
225            RefillMonitorView::default(),
226            "The correct monitor view should be received"
227        );
228
229        #[cfg(feature = "debug_channels")]
230        {
231            assert_eq!(
232                channels.tx_refill_to_monitors.count, 1,
233                "Counter should be incremented"
234            );
235        }
236    }
237}