Skip to main content

mavlink_core/connection/
sync.rs

1use core::fmt::Display;
2use std::io::{self, Read, Write};
3use std::sync::MutexGuard;
4
5use crate::MavlinkReader;
6#[cfg(feature = "mav2-message-signing")]
7use crate::SigningConfig;
8use crate::connection_shared::{
9    ConnectionState, read_message_with_dialect, read_raw_message_with_dialect,
10    write_message_with_dialect, write_raw_message,
11};
12use crate::error::{MessageReadError, MessageWriteError};
13use crate::static_dialect::StaticDialect;
14use crate::{
15    Dialect, MAVLinkMessageRaw, MavFrame, MavHeader, MavlinkVersion, Message,
16    connectable::ConnectionAddress,
17};
18
19/// The byte-oriented half of a blocking transport.
20pub(crate) trait SyncTransport: Send + Sync {
21    /// Incoming bytes.
22    type Reader: Read;
23    /// Outgoing bytes.
24    type Writer: Write;
25
26    /// Locks the reader.
27    fn reader(&self) -> MutexGuard<'_, MavlinkReader<Self::Reader>>;
28
29    /// Locks the writer. Returns `None` to skip sending when there is no writer or peer.
30    fn lock_writer(&self) -> Option<MutexGuard<'_, Self::Writer>>;
31
32    /// Whether `recv` and `recv_raw` should retry an error. Unused by `try_recv`.
33    fn retry_receive(&self, _error: &MessageReadError) -> bool {
34        false
35    }
36
37    /// Toggles nonblocking reads for `try_recv`. Does nothing by default.
38    fn set_nonblocking(
39        &self,
40        _reader: &mut MavlinkReader<Self::Reader>,
41        _enabled: bool,
42    ) -> io::Result<()> {
43        Ok(())
44    }
45}
46
47/// A concrete blocking connection using generated messages.
48pub type Connection<M> = DialectConnection<StaticDialect<M>>;
49
50/// A concrete blocking connection using a dialect.
51pub struct DialectConnection<D> {
52    transport: Transport,
53    dialect: D,
54    state: ConnectionState,
55}
56
57pub(crate) enum Transport {
58    #[cfg(feature = "transport-tcp")]
59    Tcp(super::tcp::TcpConnection),
60    #[cfg(feature = "transport-udp")]
61    Udp(super::udp::UdpConnection),
62    #[cfg(feature = "transport-direct-serial")]
63    Serial(super::direct_serial::SerialConnection),
64    File(super::file::FileConnection),
65}
66
67impl<D> DialectConnection<D> {
68    pub(crate) fn new(transport: Transport, dialect: D) -> Self {
69        Self {
70            transport,
71            dialect,
72            state: ConnectionState::new(),
73        }
74    }
75}
76
77/// A MAVLink connection
78pub trait MavConnection<M>: Send + Sync {
79    /// Receive a MAVLink message.
80    ///
81    /// May blocks until a valid frame is received, ignoring invalid messages.
82    ///
83    /// # Errors
84    ///
85    /// If the connection type blocks until a valid message is received this can not
86    /// return any errors, otherwise return any errors that occured while receiving.
87    fn recv(&self) -> Result<(MavHeader, M), MessageReadError>;
88
89    /// Receive a raw, unparsed MAVLink message.
90    ///
91    /// Blocks until a valid frame is received, ignoring invalid messages.
92    ///
93    /// # Errors
94    ///
95    /// If the connection type blocks until a valid message is received this can not
96    /// return any errors, otherwise return any errors that occured while receiving.
97    fn recv_raw(&self) -> Result<MAVLinkMessageRaw, MessageReadError>;
98
99    /// Try to receive a MAVLink message.
100    ///
101    /// Non-blocking variant of `recv()`, returns immediately with a `MessageReadError`
102    /// if there is an error or no message is available.
103    ///
104    /// # Errors
105    ///
106    /// Returns any eror encounter while receiving or deserializing a message.
107    fn try_recv(&self) -> Result<(MavHeader, M), MessageReadError>;
108
109    /// Send a MAVLink message.
110    ///
111    /// # Errors
112    ///
113    /// This function will return a [`MessageWriteError::Io`] error when sending fails.
114    fn send(&self, header: &MavHeader, data: &M) -> Result<usize, MessageWriteError>;
115
116    /// Send a raw, unparsed MAVLink message.
117    ///
118    /// # Errors
119    ///
120    /// This function will return a [`MessageWriteError::Io`] error when sending fails.
121    fn send_raw(&self, data: &MAVLinkMessageRaw) -> Result<usize, MessageWriteError>;
122
123    /// Sets the MAVLink version to use for receiving (when `allow_recv_any_version()` is `false`) and sending messages.
124    fn set_protocol_version(&mut self, version: MavlinkVersion);
125    /// Gets the currently used MAVLink version
126    fn protocol_version(&self) -> MavlinkVersion;
127
128    /// Set wether MAVLink messages of either version may be received.
129    ///
130    /// If set to false only messages of the version configured with `set_protocol_version()` are received.
131    fn set_allow_recv_any_version(&mut self, allow: bool);
132
133    /// Wether messages of any MAVLink version may be received.
134    fn allow_recv_any_version(&self) -> bool;
135
136    /// Write whole frame.
137    ///
138    /// # Errors
139    ///
140    /// This function will return a [`MessageWriteError::Io`] error when sending fails.
141    fn send_frame(&self, frame: &MavFrame<M>) -> Result<usize, MessageWriteError>
142    where
143        M: Message,
144    {
145        self.send(&frame.header, &frame.msg)
146    }
147
148    /// Read whole frame.
149    ///
150    /// # Errors
151    ///
152    /// Returns any eror encounter while receiving or deserializing a message.
153    fn recv_frame(&self) -> Result<MavFrame<M>, MessageReadError>
154    where
155        M: Message,
156    {
157        let (header, msg) = self.recv()?;
158        Ok(MavFrame {
159            header,
160            msg,
161            protocol_version: self.protocol_version(),
162        })
163    }
164
165    /// Send a message with default header.
166    ///
167    /// # Errors
168    ///
169    /// This function will return a [`MessageWriteError::Io`] error when sending fails.
170    fn send_default(&self, data: &M) -> Result<usize, MessageWriteError> {
171        self.send(&MavHeader::default(), data)
172    }
173
174    /// Setup secret key used for message signing, or disable message signing
175    #[cfg(feature = "mav2-message-signing")]
176    fn setup_signing(&mut self, signing_data: Option<SigningConfig>);
177}
178
179impl<D: Dialect + Send + Sync> MavConnection<D::Message> for DialectConnection<D> {
180    fn recv(&self) -> Result<(MavHeader, D::Message), MessageReadError> {
181        dispatch_transport!(&self.transport, |transport| {
182            let mut reader = transport.reader();
183            loop {
184                match read_message_with_dialect(&mut reader, &self.state, &self.dialect) {
185                    Ok(message) => return Ok(message),
186                    Err(error) if transport.retry_receive(&error) => {}
187                    Err(error) => return Err(error),
188                }
189            }
190        })
191    }
192
193    fn recv_raw(&self) -> Result<MAVLinkMessageRaw, MessageReadError> {
194        dispatch_transport!(&self.transport, |transport| {
195            let mut reader = transport.reader();
196            loop {
197                match read_raw_message_with_dialect(&mut reader, &self.state, &self.dialect) {
198                    Ok(message) => return Ok(message),
199                    Err(error) if transport.retry_receive(&error) => {}
200                    Err(error) => return Err(error),
201                }
202            }
203        })
204    }
205
206    fn try_recv(&self) -> Result<(MavHeader, D::Message), MessageReadError> {
207        dispatch_transport!(&self.transport, |transport| {
208            let mut reader = transport.reader();
209            transport.set_nonblocking(&mut reader, true)?;
210            let result = read_message_with_dialect(&mut reader, &self.state, &self.dialect);
211            transport.set_nonblocking(&mut reader, false)?;
212            result
213        })
214    }
215
216    fn send(&self, header: &MavHeader, data: &D::Message) -> Result<usize, MessageWriteError> {
217        dispatch_transport!(&self.transport, |transport| {
218            let Some(mut writer) = transport.lock_writer() else {
219                return Ok(0);
220            };
221            let header = self.state.next_header(header);
222            write_message_with_dialect(&mut *writer, &self.state, header, &self.dialect, data)
223        })
224    }
225
226    fn send_raw(&self, data: &MAVLinkMessageRaw) -> Result<usize, MessageWriteError> {
227        dispatch_transport!(&self.transport, |transport| {
228            let Some(mut writer) = transport.lock_writer() else {
229                return Ok(0);
230            };
231            write_raw_message(&mut *writer, data)
232        })
233    }
234
235    fn set_protocol_version(&mut self, version: MavlinkVersion) {
236        self.state.set_protocol_version(version);
237    }
238
239    fn protocol_version(&self) -> MavlinkVersion {
240        self.state.protocol_version()
241    }
242
243    fn set_allow_recv_any_version(&mut self, allow: bool) {
244        self.state.set_allow_recv_any_version(allow);
245    }
246
247    fn allow_recv_any_version(&self) -> bool {
248        self.state.allow_recv_any_version()
249    }
250
251    #[cfg(feature = "mav2-message-signing")]
252    fn setup_signing(&mut self, signing_data: Option<SigningConfig>) {
253        self.state.setup_signing(signing_data);
254    }
255}
256
257/// Connect to a MAVLink node by address string.
258///
259/// The address must be in one of the following formats:
260///
261///  * `tcpin:<addr>:<port>` to create a TCP server, listening an incoming connection
262///  * `tcpout:<addr>:<port>` to create a TCP client
263///  * `udpin:<addr>:<port>` to create a UDP server, listening for incoming packets
264///  * `udpout:<addr>:<port>` to create a UDP client
265///  * `udpbcast:<addr>:<port>` to create a UDP broadcast
266///  * `serial:<port>:<baudrate>` to create a serial connection
267///  * `file:<path>` to extract file data, writing to such a connection does nothing
268///
269/// The address selects the transport stored in the returned connection.
270///
271/// # Errors
272///
273/// - [`AddrNotAvailable`] if the address string could not be parsed as a valid MAVLink address
274/// - When the connection could not be established a corresponding [`io::Error`] is returned
275///
276/// [`AddrNotAvailable`]: io::ErrorKind::AddrNotAvailable
277pub fn connect<M: Message + Sync + Send>(address: &str) -> io::Result<Connection<M>> {
278    ConnectionAddress::parse_address(address)?.connect::<M>()
279}
280
281/// Connect to a MAVLink node using a runtime-loaded dialect.
282///
283/// The accepted address formats and errors are the same as [`connect`].
284pub fn connect_with_dialect<D: Dialect + Send + Sync>(
285    address: &str,
286    dialect: D,
287) -> io::Result<DialectConnection<D>> {
288    ConnectionAddress::parse_address(address)?.connect_with_dialect(dialect)
289}
290
291/// A MAVLink connection address that can be connected to, establishing a [`MavConnection`]
292pub trait Connectable: Display {
293    /// Attempt to establish a blocking MAVLink connection
294    ///
295    /// # Errors
296    ///
297    /// When the connection could not be established a corresponding
298    /// [`io::Error`] is returned
299    fn connect<M: Message>(&self) -> io::Result<Connection<M>> {
300        self.connect_with_dialect(StaticDialect::<M>::new())
301    }
302
303    /// Attempt to establish a blocking MAVLink connection with a runtime-loaded dialect.
304    fn connect_with_dialect<D: Dialect + Send + Sync>(
305        &self,
306        dialect: D,
307    ) -> io::Result<DialectConnection<D>>;
308}
309
310impl Connectable for ConnectionAddress {
311    fn connect_with_dialect<D: Dialect + Send + Sync>(
312        &self,
313        dialect: D,
314    ) -> io::Result<DialectConnection<D>> {
315        match self {
316            #[cfg(feature = "transport-tcp")]
317            Self::Tcp(c) => c.connect_with_dialect(dialect),
318            #[cfg(feature = "transport-udp")]
319            Self::Udp(c) => c.connect_with_dialect(dialect),
320            #[cfg(feature = "transport-direct-serial")]
321            Self::Serial(c) => c.connect_with_dialect(dialect),
322            Self::File(c) => c.connect_with_dialect(dialect),
323        }
324    }
325}