Struct TcpCommunication

Source
pub struct TcpCommunication {
    write_stream: TcpStream,
    buf_reader: BufReader<TcpStream>,
    execution_config: ExecutionConfig,
}
Expand description

Contains configuration of implementation for opening, reading, and closing TCP channel. Thread communication is as follows:

graph LR relay_manager[Relay Manager] --> tcp_communication[TCP Communication] sensor_manager[Sensor Manager] --> tcp_communication tcp_communication --> sensor_manager tank_level_switch[Tank Level Switch] --> tcp_communication tcp_communication --> tank_level_switch tcp_communication --> signal_handler[Signal Handler] signal_handler --> tcp_communication

Fields§

§write_stream: TcpStream

the raw TCP stream

§buf_reader: BufReader<TcpStream>

buffered reader for TCP stream

§execution_config: ExecutionConfig

information about which threads are started

Implementations§

Source§

impl TcpCommunication

Source

pub fn new( tcp_connection: &TcpCommunicationConfig, execution_config: ExecutionConfig, ) -> Result<TcpCommunication, TcpCommunicationError>

Creates a new TcpCommunication instance and establishes a TCP connection to a simulator.

This constructor attempts to connect to a TCP server (acting as a simulator) at the specified IP address and port. Upon a successful connection, it prepares both a TcpStream for writing and a BufReader for efficient reading from the stream.

§Arguments
  • tcp_connection - Configuration data for the TCP connection, including the target ip_address and port of the simulator.
  • execution_config - Information about which threads are started
§Returns

A Result containing a new TcpCommunication struct with an established TCP connection on success.

§Errors

Returns a TcpCommunicationError if the connection or setup fails:

  • TcpCommunicationError::ConnectionFailed: If the initial TCP connection to the specified address and port cannot be established. This could be due to the simulator not running, a firewall blocking the connection, or an incorrect address/port.
  • TcpCommunicationError::StreamCloneFailed: If the underlying TcpStream cannot be cloned, which is a necessary step for creating separate read and write handles.
Source

fn get_last_word(sequence: &str) -> Option<&str>

Extracts the last word from a given string slice, using whitespace as the delimiter.

This private helper function is useful for parsing responses where the desired data (e.g., a numeric value) is consistently the final token in a space-separated sequence. This implementation avoids heap allocation by using a reverse iterator.

§Arguments
  • sequence - The string slice from which to extract the last word.
§Returns

An Option<&str>:

  • Some(&str) containing a reference to the last word if the string contains words.
  • None if the input string is empty or contains only whitespace.
Source

fn write_stream_check_for_error( &mut self, request: String, ) -> Result<(), TcpCommunicationError>

Writes a string request to the TCP stream and handles potential errors.

This private helper function sends a request string as bytes over the TCP stream. It verifies that the entire request was written successfully and flushes the stream to ensure the data is sent immediately.

§Arguments
  • request - The String containing the data to be written to the TCP stream.
§Returns

An empty Result (Ok(())) on a successful write-operation and flush.

§Errors

Returns a TcpCommunicationError if the write or flush operation fails:

  • TcpCommunicationError::WritingToStreamFailed: If the underlying OS returns an error while writing to the TCP socket. This usually indicates a connection issue.
  • TcpCommunicationError::FlushFailed: If the OS cannot flush its write buffer to the socket, also indicating a connection problem.
Source

fn change_relay( &mut self, relay_id: u16, state: bool, ) -> Result<(), TcpCommunicationError>

Communicates with the simulator to change the state of a specific relay.

This private helper function constructs a TCP message to command a relay to either switch ON or OFF. It then writes this command to the TCP stream and waits for a response from the simulator to confirm the command was received.

§Arguments
  • relay_id - The numeric ID of the relay whose state is to be changed.
  • state - A boolean value indicating the desired state: true for ON, false for OFF.
§Returns

An empty Result (Ok(())) if the command was sent and a response was received successfully.

§Errors

Returns a TcpCommunicationError if any part of the process fails:

  • TcpCommunicationError::ReadingFromStreamFailed: If a response line cannot be read from the simulator, which likely indicates the connection was dropped.
  • It will also propagate any errors from write_stream_check_for_error.
Source

fn get_signal( &mut self, aquarium_signal: AquariumSignal, ) -> Result<f32, TcpCommunicationError>

Communicates with the simulator to request and retrieve a sensor signal value.

This private helper function constructs a TCP message to ask the simulator for the current reading of a specific AquariumSignal. It then writes this request to the TCP stream, waits for a response, and parses the received string into an f32 value.

§Arguments
  • aquarium_signal - The AquariumSignal enum variant describing the sensor signal (e.g., water temperature, pH) for which the value is requested.
§Returns

A Result containing the retrieved sensor value as an f32 on success.

§Errors

Returns a TcpCommunicationError if any part of the process fails:

  • TcpCommunicationError::IllegalSignalRequestToSimulator: If an unsupported AquariumSignal is requested.
  • TcpCommunicationError::ReadingFromStreamFailed: If a response cannot be read from the TCP stream.
  • TcpCommunicationError::ResponseContainsNoWords: If the simulator’s response is empty or contains no parsable words.
  • TcpCommunicationError::LastWordOfResponseEmpty: If the last word of the response is an empty string.
  • TcpCommunicationError::ResponseConversionError: If the last word of the response cannot be parsed into an f32.
  • It will also propagate any errors from write_stream_check_for_error.
Source

fn receive_and_answer( &mut self, rx: &mut AquaReceiver<InternalCommand>, tx: &mut AquaSender<Result<f32, TcpCommunicationError>>, ) -> Result<(), TcpCommunicationError>

Receives a RequestSignal command without blocking from a channel and sends back the corresponding sensor value via TCP.

This private helper function is used to service requests from various modules that are configured to run in simulator mode. It checks the provided receiver channel (rx) for an InternalCommand::RequestSignal. If such a request is found, it uses self.get_signal() to fetch the simulated sensor value and sends it back through the provided sender channel (tx). Other command types are ignored.

§Arguments
  • rx - A reference to the receiver channel from which InternalCommand requests are received.
  • tx - A reference to the sender channel through which the f32 sensor response is sent back.
§Returns

A Result which is:

  • Ok(f32): In the case of success
  • Err(TcpCommunicationError:): in case of failure
Source

fn handle_simulated_sensor( &mut self, is_active: bool, rx_opt: &mut Option<AquaReceiver<InternalCommand>>, tx_opt: &mut Option<AquaSender<Result<f32, TcpCommunicationError>>>, module_name: &str, )

Handles a request-response cycle for a simulated sensor if it’s active.

Source

pub fn execute( &mut self, tcp_communication_channels: &mut TcpCommunicationChannels, )

Executes the main control loop for the TCP communication module.

This function runs continuously as a server, processing incoming requests from various client threads (e.g., Ambient, Atlas Scientific, Tank Level Switch, Relay Manager). It dispatches these requests to the connected TCP simulator and sends back the responses.

The loop remains active until a Quit command is received from the signal handler. Upon receiving Quit, it breaks out of the loop and sends a confirmation back to the signal handler.

§Arguments
  • tcp_communication_channels - A mutable reference to a struct containing the channels for communication with other threads, including senders/receivers for SensorManager, Tank Level Switch, Relay Manager, and the Signal Handler.

Trait Implementations§

Source§

impl ProcessExternalRequestTrait for TcpCommunication

Source§

fn process_external_request( &mut self, rx_from_signal_handler: &mut AquaReceiver<InternalCommand>, _: Option<&mut AquaReceiver<InternalCommand>>, ) -> (bool, bool, bool)

Checks for and processes new commands relevant to the TCP Communication module from external channels.

This is the specialized implementation of ProcessExternalRequestTrait for TcpCommunication. It delegates directly to process_external_request_without_messaging, indicating that the TcpCommunication module primarily processes commands from the signal handler and does not use a separate messaging channel for external requests.

§Arguments
  • rx_from_signal_handler - A reference to the receiver end of the channel for commands originating from the signal handler.
  • _ - This parameter is ignored, as the TcpCommunication module does not process messages from a messaging channel in this context.
§Returns

A tuple (bool, bool, bool) indicating the status of commands received:

  • The first bool is true if a Quit command was received; otherwise false.
  • The second bool is always false (no “Start” commands processed).
  • The third bool is always false (no “Stop” commands processed).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> MaybeSendSync for T