aquarium_control/launch/
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*/
9
10//! Defines and constructs the entire inter-thread communication infrastructure for the application.
11//!
12//! This module serves as the central nervous system of the aquarium controller. It is responsible
13//! for two primary tasks:
14//!
15//! 1.  **Defining Channel Wrappers**: It provides `AquaSender` and `AquaReceiver`, which are
16//!     custom wrappers around `std::sync::mpsc` channels. These wrappers add optional
17//!     debugging capabilities.
18//!
19//! 2.  **Constructing the Communication Graph**: The `Channels::build` method instantiates every
20//!     single channel required for the application's threads to communicate. It then bundles
21//!     them into the main `Channels` struct.
22//!
23//! ## Key Components
24//!
25//! - **`AquaSender` / `AquaReceiver`**: These wrappers provide a consistent channel interface.
26//!   When the `debug_channels` feature is enabled, they use bounded `sync_channel`s and
27//!   count the number of messages sent and received. In release builds,
28//!   they compile down to lightweight wrappers around standard, unbounded channels.
29//!
30//! - **`Channels` Struct**: A struct that aggregates all the individual `...Channels`
31//!   structs (e.g., `FeedChannels`, `RelayManagerChannels`). This provides a single, clean
32//!   object that can be used to distribute the correct channel endpoints to each thread
33//!   at startup.
34//!
35//! - **`Channels::build()`**: This is the most critical function in the module. It contains the
36//!   complete "wiring diagram" of the application. It creates all bidirectional and
37//!   unidirectional channels, carefully handling conditional compilation for features like
38//!   the IPC `messaging` system (on Linux) and various simulators used during testing.
39//!
40//! ## Design and Architecture
41//!
42//! - **Centralized Construction**: By creating every channel in one place, this module acts as
43//!   a single source of truth for the application's communication topology. Any change to
44//!   inter-thread communication can be understood by looking at this file.
45//!
46//! - **Decoupling via Aggregation**: Modules like `feed` or `refill` don't need to know about
47//!   the entire system's channel graph. They are simply given their own small `FeedChannels`
48//!   or `RefillChannels` struct, which contains only the endpoints they care about. This
49//!   dramatically reduces coupling between modules.
50//!
51//! - **Compile-Time Flexibility**: The extensive use of `#[cfg]` attributes allows the
52//!   communication graph to be adapted at compile time. For example, channels to the
53//!   `TcpCommunication` simulator are only created and wired up if a simulator is enabled,
54//!   and IPC channels are only created on Linux.
55
56#[cfg(any(test, target_os = "linux"))]
57use crate::dispatch::messaging_channels::MessagingChannels;
58use crate::food::feed_channels::FeedChannels;
59use crate::mineral::balling_channels::BallingChannels;
60use crate::permission::schedule_check_channels::ScheduleCheckChannels;
61use crate::recorder::data_logger_channels::DataLoggerChannels;
62use crate::relays::actuate_simulator_channels::ActuateSimulatorChannels;
63use crate::relays::relay_manager_channels::RelayManagerChannels;
64#[allow(unused)] // used in conditionally compiled code
65use crate::sensors::atlas_scientific_channels::AtlasScientificChannels;
66use crate::sensors::dht_channels::DhtChannels;
67use crate::sensors::ds18b20_channels::Ds18b20Channels;
68use crate::sensors::i2c_interface::{I2cRequest, I2cResult};
69use crate::sensors::i2c_interface_channels::I2cInterfaceChannels;
70use crate::sensors::sensor_manager_channels::SensorManagerChannels;
71use crate::sensors::tank_level_switch_channels::TankLevelSwitchChannels;
72use crate::simulator::tcp_communication::TcpCommunicationError;
73use crate::simulator::tcp_communication_channels::TcpCommunicationChannels;
74use crate::thermal::heating_channels::HeatingChannels;
75use crate::thermal::ventilation_channels::VentilationChannels;
76use crate::utilities::channel_content::InternalCommand;
77use crate::utilities::signal_handler::SignalHandlerChannels;
78use crate::watchmen::memory_channels::MemoryChannels;
79use crate::watchmen::monitors_channels::MonitorsChannels;
80use crate::watchmen::watchdog_channels::WatchDogChannels;
81use crate::water::refill_channels::RefillChannels;
82use crate::water::refill_monitor_view::RefillMonitorView;
83use std::sync::mpsc;
84#[cfg(feature = "debug_channels")]
85use std::sync::mpsc::SyncSender;
86use std::sync::mpsc::TryRecvError;
87#[cfg(feature = "debug_channels")]
88use std::sync::mpsc::TrySendError;
89use thiserror::Error;
90
91#[cfg(feature = "debug_channels")]
92pub struct AquaSender<T: Send> {
93    sender: SyncSender<T>,
94    pub count: u64,
95}
96
97#[cfg(not(feature = "debug_channels"))]
98#[derive(Debug, Clone)]
99pub struct AquaSender<T: Send> {
100    sender: mpsc::Sender<T>,
101}
102
103pub struct AquaReceiver<T> {
104    receiver: mpsc::Receiver<T>,
105    #[cfg(feature = "debug_channels")]
106    pub count: u64,
107}
108
109pub fn channel<T: Send>(bound: usize) -> (AquaSender<T>, AquaReceiver<T>) {
110    #[cfg(feature = "debug_channels")]
111    {
112        let (sender, receiver) = mpsc::sync_channel::<T>(bound);
113        (
114            AquaSender { sender, count: 0 },
115            AquaReceiver { receiver, count: 0 },
116        )
117    }
118    #[cfg(not(feature = "debug_channels"))]
119    {
120        let (sender, receiver) = {
121            let _ = bound;
122            mpsc::channel::<T>()
123        };
124        (AquaSender { sender }, AquaReceiver { receiver })
125    }
126}
127
128#[derive(Debug, Error, Clone)]
129pub enum AquaChannelError {
130    #[cfg(feature = "debug_channels")]
131    #[error("Channel is full.")]
132    Full,
133
134    #[error("Channel is disconnected.")]
135    Disconnected,
136
137    #[error("Channel is empty.")]
138    Empty,
139}
140
141impl<T: Send> AquaSender<T> {
142    /// Send a value into a channel, and on builds with "debug_channel" feature enabled,
143    /// propagate outward an error to whatever thread is over-eagerly sending content.
144    pub fn send(&mut self, data: T) -> Result<(), AquaChannelError> {
145        #[cfg(feature = "debug_channels")]
146        {
147            match self.sender.try_send(data) {
148                Ok(_) => {
149                    self.count += 1;
150                    Ok(())
151                }
152                Err(TrySendError::Full(_)) => Err(AquaChannelError::Full),
153                Err(TrySendError::Disconnected(_)) => Err(AquaChannelError::Disconnected),
154            }
155        }
156        #[cfg(not(feature = "debug_channels"))]
157        {
158            self.sender
159                .send(data)
160                .map_err(|_| AquaChannelError::Disconnected)?;
161            Ok(())
162        }
163    }
164}
165
166impl<T> AquaReceiver<T> {
167    /// Receive a value from a channel (blocking), and on builds with "debug_channel" feature enabled,
168    /// count each call for each receiver
169    pub fn recv(&mut self) -> Result<T, AquaChannelError> {
170        #[cfg(feature = "debug_channels")]
171        {
172            self.count += 1;
173        }
174        self.receiver
175            .recv()
176            .map_err(|_| AquaChannelError::Disconnected)
177    }
178
179    /// Receive a value from a channel (non-blocking), and on builds with "debug_channel" feature enabled,
180    /// count each call for each receiver
181    pub fn try_recv(&mut self) -> Result<T, AquaChannelError> {
182        #[cfg(feature = "debug_channels")]
183        {
184            self.count += 1;
185        }
186        match self.receiver.try_recv() {
187            Ok(c) => Ok(c),
188            Err(TryRecvError::Empty) => Err(AquaChannelError::Empty),
189            Err(TryRecvError::Disconnected) => Err(AquaChannelError::Disconnected),
190        }
191    }
192}
193
194/// Creates a pair of bidirectional channels for request/response communication.
195macro_rules! create_bidirectional_channel {
196    ($req:ty, $resp:ty) => {{
197        let (tx_req, rx_req): (AquaSender<$req>, AquaReceiver<$req>) = channel(1);
198        let (tx_resp, rx_resp): (AquaSender<$resp>, AquaReceiver<$resp>) = channel(1);
199        ((tx_req, rx_req), (tx_resp, rx_resp))
200    }};
201}
202
203/// Creates a channel for one-way communication.
204macro_rules! create_unidirectional_channel {
205    ($req:ty) => {{
206        let (tx, rx): (AquaSender<$req>, AquaReceiver<$req>) = channel(1);
207        (tx, rx)
208    }};
209}
210pub struct Channels {
211    pub feed: FeedChannels,
212    pub schedule_check: ScheduleCheckChannels,
213    pub actuate_simulator: ActuateSimulatorChannels,
214    pub relay_manager: RelayManagerChannels,
215
216    #[allow(unused)] // used in conditionally compiled code
217    pub atlas_scientific: AtlasScientificChannels,
218
219    #[allow(unused)] // used in conditionally compiled code
220    pub dht: DhtChannels,
221    pub ds18b20: Ds18b20Channels,
222    pub balling: BallingChannels,
223    pub data_logger: DataLoggerChannels,
224
225    #[allow(unused)] // used in conditionally compiled code
226    pub i2c_interface: I2cInterfaceChannels,
227    pub tank_level_switch: TankLevelSwitchChannels,
228
229    pub tcp_communication: TcpCommunicationChannels,
230    pub heating: HeatingChannels,
231    pub ventilation: VentilationChannels,
232
233    pub signal_handler: SignalHandlerChannels,
234    pub monitors: MonitorsChannels,
235    pub watchdog: WatchDogChannels,
236    pub memory: MemoryChannels,
237    pub refill: RefillChannels,
238    pub sensor_manager: SensorManagerChannels,
239
240    // used in conditionally compiled implementation and platform-independent testing
241    #[cfg(any(test, target_os = "linux"))]
242    pub messaging: MessagingChannels,
243}
244
245impl Channels {
246    /// Creates a new set of channels for the application based on the provided simulator configuration.
247    pub fn new(
248        relay_manager_use_simulator: bool,
249        sensor_manager_use_simulator: bool,
250        tank_level_switch_use_simulator: bool,
251    ) -> Channels {
252        // The public `new` function is now just a clean wrapper.
253        Self::build(
254            relay_manager_use_simulator,
255            sensor_manager_use_simulator,
256            tank_level_switch_use_simulator,
257        )
258    }
259
260    /// Creates a new set of channels with all simulators enabled for comprehensive unit testing.
261    #[cfg(test)]
262    pub fn new_for_test() -> Channels {
263        Self::build(true, true, true)
264    }
265
266    pub fn build(
267        relay_manager_use_simulator: bool,
268        sensor_manager_use_simulator: bool,
269        tank_level_switch_use_simulator: bool,
270    ) -> Channels {
271        let use_simulator = relay_manager_use_simulator
272            | sensor_manager_use_simulator
273            | tank_level_switch_use_simulator;
274
275        // create channels for communication between threads
276        let tx_signal_handler_to_messaging_opt: Option<AquaSender<InternalCommand>>;
277        let rx_signal_handler_from_messaging_opt: Option<AquaReceiver<bool>>;
278        let rx_refill_from_messaging_opt: Option<AquaReceiver<InternalCommand>>;
279        let rx_ventilation_from_messaging_opt: Option<AquaReceiver<InternalCommand>>;
280        let rx_heating_from_messaging_opt: Option<AquaReceiver<InternalCommand>>;
281        let rx_feed_from_messaging_opt: Option<AquaReceiver<InternalCommand>>;
282        let rx_balling_from_messaging_opt: Option<AquaReceiver<InternalCommand>>;
283        let rx_monitors_from_messaging_opt: Option<AquaReceiver<InternalCommand>>;
284
285        #[allow(clippy::needless_late_init)]
286        let rx_watchdog_from_messaging_opt: Option<AquaReceiver<InternalCommand>>;
287
288        // channels for messaging used in Linux implementation and on unit testing on any platform
289        cfg_if::cfg_if! {
290            if #[cfg(any(test, target_os = "linux"))] {
291                // *** signal_handler <=> messaging ***
292                let ((tx_signal_handler_to_messaging, rx_messaging_from_signal_handler),
293                    (tx_messaging_to_signal_handler, rx_signal_handler_from_messaging))
294                    = create_bidirectional_channel!(InternalCommand, bool);
295
296                tx_signal_handler_to_messaging_opt = Some(tx_signal_handler_to_messaging);
297                rx_signal_handler_from_messaging_opt = Some(rx_signal_handler_from_messaging);
298
299                // *** messaging => refill ***
300                let (tx_messaging_to_refill, rx_refill_from_messaging) = create_unidirectional_channel!(InternalCommand);
301                rx_refill_from_messaging_opt = Some(rx_refill_from_messaging);
302
303                // *** messaging => ventilation ***
304                let (tx_messaging_to_ventilation, rx_ventilation_from_messaging) = create_unidirectional_channel!(InternalCommand);
305                rx_ventilation_from_messaging_opt = Some(rx_ventilation_from_messaging);
306
307                // *** messaging => heating ***
308                let (tx_messaging_to_heating, rx_heating_from_messaging) = create_unidirectional_channel!(InternalCommand);
309                rx_heating_from_messaging_opt = Some(rx_heating_from_messaging);
310
311                // *** messaging => feed ***
312                let (tx_messaging_to_feed, rx_feed_from_messaging) = create_unidirectional_channel!(InternalCommand);
313                rx_feed_from_messaging_opt = Some(rx_feed_from_messaging);
314
315                // *** messaging => balling ***
316                let (tx_messaging_to_balling, rx_balling_from_messaging) = create_unidirectional_channel!(InternalCommand);
317                rx_balling_from_messaging_opt = Some(rx_balling_from_messaging);
318
319                // *** messaging => monitors ***
320                let (tx_messaging_to_monitors, rx_monitors_from_messaging) = create_unidirectional_channel!(InternalCommand);
321                rx_monitors_from_messaging_opt = Some(rx_monitors_from_messaging);
322
323                // *** messaging => monitors ***
324                let (tx_messaging_to_watchdog, rx_watchdog_from_messaging) = create_unidirectional_channel!(InternalCommand);
325                rx_watchdog_from_messaging_opt = Some(rx_watchdog_from_messaging);
326            }
327            else {
328                tx_signal_handler_to_messaging_opt = None;
329                rx_signal_handler_from_messaging_opt = None;
330                rx_refill_from_messaging_opt = None;
331                rx_ventilation_from_messaging_opt = None;
332                rx_heating_from_messaging_opt = None;
333                rx_feed_from_messaging_opt = None;
334                rx_balling_from_messaging_opt = None;
335                rx_monitors_from_messaging_opt = None;
336                rx_watchdog_from_messaging_opt = None;
337            }
338        }
339
340        // *** refill <=> relay_manager ***
341        let (
342            (tx_refill_to_relay_manager, rx_relay_manager_from_refill),
343            (tx_relay_manager_to_refill, rx_refill_from_relay_manager),
344        ) = create_bidirectional_channel!(InternalCommand, bool);
345        // *******************************
346
347        // *** refill <=> schedule_check ***
348        let (
349            (tx_refill_to_schedule_check, rx_schedule_check_from_refill),
350            (tx_schedule_check_to_refill, rx_refill_from_schedule_check),
351        ) = create_bidirectional_channel!(InternalCommand, bool);
352        // *******************************
353
354        // *** heating <=> relay_manager ***
355        let (
356            (tx_heating_to_relay_manager, rx_relay_manager_from_heating),
357            (tx_relay_manager_to_heating, rx_heating_from_relay_manager),
358        ) = create_bidirectional_channel!(InternalCommand, bool);
359        // *******************************
360
361        // *** heating <=> schedule_check ***
362        let (
363            (tx_heating_to_schedule_check, rx_schedule_check_from_heating),
364            (tx_schedule_check_to_heating, rx_heating_from_schedule_check),
365        ) = create_bidirectional_channel!(InternalCommand, bool);
366        // *******************************
367
368        // *** ventilation <=> relay_manager ***
369        let (
370            (tx_ventilation_to_relay_manager, rx_relay_manager_from_ventilation),
371            (tx_relay_manager_to_ventilation, rx_ventilation_from_relay_manager),
372        ) = create_bidirectional_channel!(InternalCommand, bool);
373        // *******************************
374
375        // *** ventilation <=> schedule_check ***
376        let (
377            (tx_ventilation_to_schedule_check, rx_schedule_check_from_ventilation),
378            (tx_schedule_check_to_ventilation, rx_ventilation_from_schedule_check),
379        ) = create_bidirectional_channel!(InternalCommand, bool);
380        // *******************************
381
382        // *** feed <=> relay_manager ***
383        let (
384            (tx_feed_to_relay_manager, rx_relay_manager_from_feed),
385            (tx_relay_manager_to_feed, rx_feed_from_relay_manager),
386        ) = create_bidirectional_channel!(InternalCommand, bool);
387        // *******************************
388
389        // *** balling <=> relay_manager ***
390        let (
391            (tx_balling_to_relay_manager, rx_relay_manager_from_balling),
392            (tx_relay_manager_to_balling, rx_balling_from_relay_manager),
393        ) = create_bidirectional_channel!(InternalCommand, bool);
394        // *******************************
395
396        // *** balling <=> schedule_check ***
397        let (
398            (tx_balling_to_schedule_check, rx_schedule_check_from_balling),
399            (tx_schedule_check_to_balling, rx_balling_from_schedule_check),
400        ) = create_bidirectional_channel!(InternalCommand, bool);
401        // *******************************
402
403        // *** refill => monitors ***
404        let (tx_refill_to_monitors, rx_monitors_from_refill) =
405            create_unidirectional_channel!(RefillMonitorView);
406        //********************************
407
408        // *** signal handler <=> refill ***
409        let (
410            (tx_signal_handler_to_refill, rx_refill_from_signal_handler),
411            (tx_refill_to_signal_handler, rx_signal_handler_from_refill),
412        ) = create_bidirectional_channel!(InternalCommand, bool);
413        // ***********************************
414
415        // *** signal handler <=> heating ***
416        let (
417            (tx_signal_handler_to_heating, rx_heating_from_signal_handler),
418            (tx_heating_to_signal_handler, rx_signal_handler_from_heating),
419        ) = create_bidirectional_channel!(InternalCommand, bool);
420        // ***********************************
421
422        // *** signal handler <=> feed ***
423        let (
424            (tx_signal_handler_to_feed, rx_feed_from_signal_handler),
425            (tx_feed_to_signal_handler, rx_signal_handler_from_feed),
426        ) = create_bidirectional_channel!(InternalCommand, bool);
427        // ***********************************
428
429        // *** signal handler <=> balling ***
430        let (
431            (tx_signal_handler_to_balling, rx_balling_from_signal_handler),
432            (tx_balling_to_signal_handler, rx_signal_handler_from_balling),
433        ) = create_bidirectional_channel!(InternalCommand, bool);
434        // ***********************************
435
436        // *** signal handler <=> monitors ***
437        let (
438            (tx_signal_handler_to_monitors, rx_monitors_from_signal_handler),
439            (tx_monitors_to_signal_handler, rx_signal_handler_from_monitors),
440        ) = create_bidirectional_channel!(InternalCommand, bool);
441
442        // ***********************************
443
444        // *** signal handler <=> ventilation ***
445        let (
446            (tx_signal_handler_to_ventilation, rx_ventilation_from_signal_handler),
447            (tx_ventilation_to_signal_handler, rx_signal_handler_from_ventilation),
448        ) = create_bidirectional_channel!(InternalCommand, bool);
449
450        // ***********************************
451
452        // *** signal handler <=> schedule_check ***
453        let (
454            (tx_signal_handler_to_schedule_check, rx_schedule_check_from_signal_handler),
455            (tx_schedule_check_to_signal_handler, rx_signal_handler_from_schedule_check),
456        ) = create_bidirectional_channel!(InternalCommand, bool);
457
458        // ***********************************
459
460        // *** signal handler <=> gpio ***
461        let (
462            (tx_signal_handler_to_tank_level_switch, rx_tank_level_switch_from_signal_handler),
463            (tx_tank_level_switch_to_signal_handler, rx_signal_handler_from_tank_level_switch),
464        ) = create_bidirectional_channel!(InternalCommand, bool);
465        // *********************************
466
467        // *** signal handler <=> relay_manager ***
468        let (
469            (tx_signal_handler_to_relay_manager, rx_relay_manager_from_signal_handler),
470            (tx_relay_manager_to_signal_handler, rx_signal_handler_from_relay_manager),
471        ) = create_bidirectional_channel!(InternalCommand, bool);
472        // ***************************************
473
474        // *** signal handler <=> data_logger ***
475        let (
476            (tx_signal_handler_to_data_logger, rx_data_logger_from_signal_handler),
477            (tx_data_logger_to_signal_handler, rx_signal_handler_from_data_logger),
478        ) = create_bidirectional_channel!(InternalCommand, bool);
479        // **************************************
480
481        // *** signal handler <=> atlas_scientific ***
482        #[allow(unused)] // used in conditionally compiled code
483        let (
484            (tx_signal_handler_to_atlas_scientific, rx_atlas_scientific_from_signal_handler),
485            (tx_atlas_scientific_to_signal_handler, rx_signal_handler_from_atlas_scientific),
486        ) = create_bidirectional_channel!(InternalCommand, bool);
487        // **************************************
488
489        // *** signal handler <=> watchdog ***
490        let (
491            (tx_signal_handler_to_watchdog, rx_watchdog_from_signal_handler),
492            (tx_watchdog_to_signal_handler, rx_signal_handler_from_watchdog),
493        ) = create_bidirectional_channel!(InternalCommand, bool);
494        // **************************************
495
496        // *** signal handler <=> sensor_manager ***
497        let (
498            (tx_signal_handler_to_sensor_manager, rx_sensor_manager_from_signal_handler),
499            (tx_sensor_manager_to_signal_handler, rx_signal_handler_from_sensor_manager),
500        ) = create_bidirectional_channel!(InternalCommand, bool);
501        // **************************************
502
503        // *** signal handler <=> tcp ***
504        let (
505            (tx_signal_handler_to_tcp, rx_tcp_communication_from_signal_handler),
506            (tx_tcp_communication_to_signal_handler, rx_signal_handler_from_tcp),
507        ) = create_bidirectional_channel!(InternalCommand, bool);
508        // *******************************
509
510        // *** signal handler => ds18b20 ***
511        let (tx_signal_handler_to_ds18b20, rx_ds18b20_from_signal_handler) =
512            create_unidirectional_channel!(InternalCommand);
513        // *******************************
514
515        // *** relay_manager => tcp ***
516        let (tx_actuate_simulator_to_tcp, rx_actuate_simulator_from_relay_manager) =
517            create_unidirectional_channel!(InternalCommand);
518        // ****************************
519
520        // *** sensor_manager <=> tcp ***
521        let (
522            (tx_sensor_manager_to_tcp, rx_tcp_communication_from_sensor_manager),
523            (tx_tcp_communication_to_sensor_manager, rx_sensor_manager_from_tcp),
524        ) = create_bidirectional_channel!(InternalCommand, Result<f32, TcpCommunicationError>);
525        // **********************
526
527        // *** tank_level_switch <=> tcp ***
528        let (
529            (tx_tank_level_switch_to_tcp, rx_tcp_communication_from_tank_level_switch),
530            (tx_tcp_communication_to_tank_level_switch, rx_tank_level_switch_from_tcp),
531        ) = create_bidirectional_channel!(InternalCommand, Result<f32, TcpCommunicationError>);
532        // **********************
533
534        // wrapping sender/receiver for communication with TCP thread
535        let tx_signal_handler_to_tcp_opt = use_simulator.then_some(tx_signal_handler_to_tcp);
536        let rx_signal_handler_from_tcp_opt = use_simulator.then_some(rx_signal_handler_from_tcp);
537        let tx_actuate_simulator_to_tcp_opt = use_simulator.then_some(tx_actuate_simulator_to_tcp);
538        let rx_tcp_communication_from_relay_manager_opt =
539            relay_manager_use_simulator.then_some(rx_actuate_simulator_from_relay_manager);
540        let tx_tank_level_switch_to_tcp_opt =
541            tank_level_switch_use_simulator.then_some(tx_tank_level_switch_to_tcp);
542        let rx_tcp_communication_from_tank_level_switch_opt =
543            tank_level_switch_use_simulator.then_some(rx_tcp_communication_from_tank_level_switch);
544        let tx_tcp_communication_to_tank_level_switch_opt =
545            tank_level_switch_use_simulator.then_some(tx_tcp_communication_to_tank_level_switch);
546        let rx_tank_level_switch_from_tcp_opt =
547            tank_level_switch_use_simulator.then_some(rx_tank_level_switch_from_tcp);
548        let tx_sensor_manager_to_tcp_opt =
549            sensor_manager_use_simulator.then_some(tx_sensor_manager_to_tcp);
550        let rx_tcp_communication_from_sensor_manager_opt =
551            sensor_manager_use_simulator.then_some(rx_tcp_communication_from_sensor_manager);
552        let tx_tcp_communication_to_sensor_manager_opt =
553            sensor_manager_use_simulator.then_some(tx_tcp_communication_to_sensor_manager);
554        let rx_sensor_manager_from_tcp_opt =
555            sensor_manager_use_simulator.then_some(rx_sensor_manager_from_tcp);
556
557        #[allow(unused)] // channel used in conditionally compiled code
558        let (tx_signal_handler_to_dht, rx_dht_from_signal_handler) =
559            create_unidirectional_channel!(InternalCommand);
560
561        #[allow(unused)] // channel used in conditionally compiled code
562        let (tx_signal_handler_to_i2c_interface, rx_i2c_interface_from_signal_handler) =
563            create_unidirectional_channel!(InternalCommand);
564
565        // Atlas Scientific <=> i2c_interface
566        #[allow(unused)] // channel used in conditionally compiled code
567        let (
568            (tx_atlas_scientific_to_i2c_interface, rx_i2c_interface_from_atlas_scientific),
569            (tx_i2c_interface_to_atlas_scientific, rx_atlas_scientific_from_i2c_interface),
570        ) = create_bidirectional_channel!(I2cRequest, I2cResult);
571
572        // Signal handler => Memory
573        let (tx_signal_handler_to_memory, rx_memory_from_signal_handler) =
574            create_unidirectional_channel!(InternalCommand);
575
576        let relay_manager = RelayManagerChannels {
577            tx_relay_manager_to_refill,
578            rx_relay_manager_from_refill,
579            tx_relay_manager_to_heating,
580            rx_relay_manager_from_heating,
581            tx_relay_manager_to_ventilation,
582            rx_relay_manager_from_ventilation,
583            tx_relay_manager_to_balling,
584            rx_relay_manager_from_balling,
585            tx_relay_manager_to_feed,
586            rx_relay_manager_from_feed,
587            tx_relay_manager_to_signal_handler,
588            rx_relay_manager_from_signal_handler,
589        };
590
591        let atlas_scientific = AtlasScientificChannels {
592            tx_atlas_scientific_to_i2c_interface,
593            rx_atlas_scientific_from_i2c_interface,
594            tx_atlas_scientific_to_signal_handler,
595            rx_atlas_scientific_from_signal_handler,
596        };
597
598        let refill = RefillChannels {
599            tx_refill_to_relay_manager,
600            rx_refill_from_relay_manager,
601            tx_refill_to_signal_handler,
602            rx_refill_from_signal_handler,
603            tx_refill_to_schedule_check,
604            rx_refill_from_schedule_check,
605            tx_refill_to_monitors,
606            rx_refill_from_messaging_opt,
607        };
608
609        let balling = BallingChannels {
610            tx_balling_to_relay_manager,
611            rx_balling_from_relay_manager,
612            tx_balling_to_signal_handler,
613            rx_balling_from_signal_handler,
614            tx_balling_to_schedule_check,
615            rx_balling_from_schedule_check,
616            rx_balling_from_messaging_opt,
617        };
618
619        let ventilation = VentilationChannels {
620            tx_ventilation_to_relay_manager,
621            rx_ventilation_from_relay_manager,
622            tx_ventilation_to_signal_handler,
623            rx_ventilation_from_signal_handler,
624            tx_ventilation_to_schedule_check,
625            rx_ventilation_from_schedule_check,
626            rx_ventilation_from_messaging_opt,
627        };
628
629        let feed = FeedChannels {
630            tx_feed_to_relay_manager,
631            rx_feed_from_relay_manager,
632            tx_feed_to_signal_handler,
633            rx_feed_from_signal_handler,
634            rx_feed_from_messaging_opt,
635        };
636
637        let heating = HeatingChannels {
638            tx_heating_to_relay_manager,
639            rx_heating_from_relay_manager,
640            tx_heating_to_signal_handler,
641            rx_heating_from_signal_handler,
642            tx_heating_to_schedule_check,
643            rx_heating_from_schedule_check,
644            rx_heating_from_messaging_opt,
645        };
646
647        let tcp_communication = TcpCommunicationChannels {
648            rx_tcp_communication_from_relay_manager_opt,
649            rx_tcp_communication_from_sensor_manager_opt,
650            tx_tcp_communication_to_sensor_manager_opt,
651            rx_tcp_communication_from_tank_level_switch_opt,
652            tx_tcp_communication_to_tank_level_switch_opt,
653            tx_tcp_communication_to_signal_handler,
654            rx_tcp_communication_from_signal_handler,
655        };
656
657        let schedule_check = ScheduleCheckChannels {
658            tx_schedule_check_to_ventilation,
659            rx_schedule_check_from_ventilation,
660            tx_schedule_check_to_heating,
661            rx_schedule_check_from_heating,
662            tx_schedule_check_to_refill,
663            rx_schedule_check_from_refill,
664            tx_schedule_check_to_balling,
665            rx_schedule_check_from_balling,
666            tx_schedule_check_to_signal_handler,
667            rx_schedule_check_from_signal_handler,
668        };
669
670        let i2c_interface = I2cInterfaceChannels {
671            tx_i2c_interface_to_atlas_scientific,
672            rx_i2c_interface_from_atlas_scientific,
673            rx_i2c_interface_from_signal_handler,
674        };
675
676        let signal_handler = SignalHandlerChannels {
677            tx_signal_handler_to_messaging_opt,
678            rx_signal_handler_from_messaging_opt,
679            tx_signal_handler_to_refill,
680            rx_signal_handler_from_refill,
681            tx_signal_handler_to_tank_level_switch,
682            rx_signal_handler_from_tank_level_switch,
683            tx_signal_handler_to_heating,
684            rx_signal_handler_from_heating,
685            tx_signal_handler_to_relay_manager,
686            rx_signal_handler_from_relay_manager,
687            tx_signal_handler_to_atlas_scientific,
688            rx_signal_handler_from_atlas_scientific,
689            tx_signal_handler_to_sensor_manager,
690            rx_signal_handler_from_sensor_manager,
691            tx_signal_handler_to_data_logger,
692            rx_signal_handler_from_data_logger,
693            tx_signal_handler_to_balling,
694            rx_signal_handler_from_balling,
695            tx_signal_handler_to_ventilation,
696            rx_signal_handler_from_ventilation,
697            tx_signal_handler_to_monitors,
698            rx_signal_handler_from_monitors,
699            tx_signal_handler_to_feed,
700            rx_signal_handler_from_feed,
701            tx_signal_handler_to_schedule_check,
702            rx_signal_handler_from_schedule_check,
703            tx_signal_handler_to_tcp_opt,
704            rx_signal_handler_from_tcp_opt,
705            tx_signal_handler_to_dht,
706            tx_signal_handler_to_i2c_interface,
707            tx_signal_handler_to_watchdog,
708            rx_signal_handler_from_watchdog,
709            tx_signal_handler_to_ds18b20,
710            tx_signal_handler_to_memory,
711        };
712
713        let monitors = MonitorsChannels {
714            rx_monitors_from_refill,
715            tx_monitors_to_signal_handler,
716            rx_monitors_from_signal_handler,
717            rx_monitors_from_messaging_opt,
718        };
719
720        #[cfg(any(test, target_os = "linux"))]
721        let messaging = MessagingChannels {
722            tx_messaging_to_signal_handler,
723            rx_messaging_from_signal_handler,
724            tx_messaging_to_refill,
725            tx_messaging_to_ventilation,
726            tx_messaging_to_heating,
727            tx_messaging_to_feed,
728            tx_messaging_to_balling,
729            tx_messaging_to_monitors,
730            tx_messaging_to_watchdog,
731        };
732
733        let tank_level_switch = TankLevelSwitchChannels {
734            tx_tank_level_switch_to_tcp_opt,
735            rx_tank_level_switch_from_tcp_opt,
736            tx_tank_level_switch_to_signal_handler,
737            rx_tank_level_switch_from_signal_handler,
738        };
739
740        let watchdog = WatchDogChannels {
741            tx_watchdog_to_signal_handler,
742            rx_watchdog_from_signal_handler,
743            rx_watchdog_from_messaging_opt,
744        };
745
746        let memory = MemoryChannels {
747            rx_memory_from_signal_handler,
748        };
749
750        let ds18b20 = Ds18b20Channels {
751            rx_ds18b20_from_signal_handler,
752        };
753
754        let dht = DhtChannels {
755            rx_dht_from_signal_handler,
756        };
757
758        let actuate_simulator = ActuateSimulatorChannels {
759            tx_actuate_simulator_to_tcp_opt,
760        };
761
762        let sensor_manager = SensorManagerChannels {
763            tx_sensor_manager_to_tcp_opt,
764            rx_sensor_manager_from_tcp_opt,
765            tx_sensor_manager_to_signal_handler,
766            rx_sensor_manager_from_signal_handler,
767        };
768
769        let data_logger = DataLoggerChannels {
770            tx_data_logger_to_signal_handler,
771            rx_data_logger_from_signal_handler,
772        };
773
774        Channels {
775            feed,
776            schedule_check,
777            actuate_simulator,
778            relay_manager,
779            atlas_scientific,
780            dht,
781            ds18b20,
782            balling,
783            data_logger,
784            tank_level_switch,
785            tcp_communication,
786            heating,
787            i2c_interface,
788            ventilation,
789            signal_handler,
790            monitors,
791            watchdog,
792            memory,
793            refill,
794            sensor_manager,
795
796            #[cfg(any(test, target_os = "linux"))]
797            messaging,
798        }
799    }
800}