aquarium_control/simulator/
get_resp_sim.rs

1/* Copyright 2024 Uwe Martin
2
3Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
5The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
7THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8*/
9
10use crate::launch::channels::{AquaReceiver, AquaSender};
11use crate::simulator::tcp_communication_error::TcpCommunicationError;
12use crate::utilities::channel_content::InternalCommand;
13use log::warn;
14
15/// A trait that provides a generic method for communicating with a simulator.
16pub trait GetResponseFromSimulatorTrait {
17    /// Sends a request to a simulator via a channel and blocks until a response is received.
18    ///
19    /// This function standardizes the request/response pattern for fetching simulated
20    /// sensor data. It sends an `InternalCommand` to a simulator thread (typically
21    /// the `TcpCommunication` module) and waits for a floating-point value in return.
22    ///
23    /// # Arguments
24    /// * `requester` - A `String` identifying the calling module, used for logging and error context.
25    /// * `tx_to_tcp` - The sender end of the channel to the simulator thread.
26    /// * `rx_from_tcp` - The receiver end of the channel from the simulator thread.
27    /// * `internal_command` - The command to send, which should be a `RequestSignal` variant.
28    ///
29    /// # Returns
30    /// A `Result` containing the simulated sensor value:
31    /// - `Ok(f32)`: The floating-point value received from the simulator.
32    /// - `Ok(0.0)`: If a non-`RequestSignal` command is passed, which is logged and ignored.
33    ///
34    /// # Errors
35    /// Returns a `TcpCommunicationError` if the communication fails at any stage:
36    /// - `TcpCommunicationError::SendingToTCPThreadFailed`: If sending the request fails,
37    ///   which typically means the simulator thread has panicked or shut down and the
38    ///   channel is disconnected.
39    /// - `TcpCommunicationError::ReceivingFromTCPThreadFailed`: If receiving the response
40    ///   fails, which also indicates the simulator thread is no longer available.
41    /// - It will also propagate any `TcpCommunicationError` that the simulator
42    ///   thread itself sends back within its `Result`.
43    fn get_response_from_simulator(
44        requester: String,
45        tx_to_tcp: &mut AquaSender<InternalCommand>,
46        rx_from_tcp: &mut AquaReceiver<Result<f32, TcpCommunicationError>>,
47        internal_command: InternalCommand,
48    ) -> Result<f32, TcpCommunicationError> {
49        match internal_command {
50            InternalCommand::RequestSignal(_) => {
51                // 1. Try to send the command, mapping the error if it fails.
52                tx_to_tcp.send(internal_command).map_err(|e| {
53                    TcpCommunicationError::SendingToTCPThreadFailed {
54                        location: module_path!().to_string(),
55                        requester: requester.clone(), // Clone for this error path
56                        source: e,
57                    }
58                })?;
59
60                // 2. Block and wait for the response, mapping the error if it fails.
61                // The inner `Result` is the actual payload from the simulator.
62                rx_from_tcp.recv().map_err(|e| {
63                    TcpCommunicationError::ReceivingFromTCPThreadFailed {
64                        location: module_path!().to_string(),
65                        requester, // Move the original requester string here
66                        source: e,
67                    }
68                })? // This '?' unwraps the outer Result<_, RecvError>
69            }
70            _ => {
71                warn!(
72                    target: module_path!(),
73                    "ignoring not applicable internal command ({internal_command}) from {requester}"
74                );
75                Ok(0.0)
76            }
77        }
78    }
79}