Skip to main content

mavlink_core/connection/
sync.rs

1use core::fmt::Display;
2use core::marker::PhantomData;
3use std::io;
4
5#[cfg(feature = "transport-tcp")]
6use super::tcp::TcpConnection;
7
8#[cfg(feature = "transport-udp")]
9use super::udp::UdpConnection;
10
11#[cfg(feature = "transport-direct-serial")]
12use super::direct_serial::SerialConnection;
13
14use super::file::FileConnection;
15
16#[cfg(feature = "mav2-message-signing")]
17use crate::SigningConfig;
18
19use crate::error::MessageReadError;
20use crate::error::MessageWriteError;
21use crate::{
22    MAVLinkMessageRaw, MavFrame, MavHeader, MavlinkVersion, Message, connectable::ConnectionAddress,
23};
24
25/// A MAVLink connection
26pub trait MavConnection<M: Message> {
27    /// Receive a MAVLink message.
28    ///
29    /// May blocks until a valid frame is received, ignoring invalid messages.
30    ///
31    /// # Errors
32    ///
33    /// If the connection type blocks until a valid message is received this can not
34    /// return any errors, otherwise return any errors that occured while receiving.
35    fn recv(&self) -> Result<(MavHeader, M), MessageReadError>;
36
37    /// Receive a raw, unparsed MAVLink message.
38    ///
39    /// Blocks until a valid frame is received, ignoring invalid messages.
40    ///
41    /// # Errors
42    ///
43    /// If the connection type blocks until a valid message is received this can not
44    /// return any errors, otherwise return any errors that occured while receiving.
45    fn recv_raw(&self) -> Result<MAVLinkMessageRaw, MessageReadError>;
46
47    /// Try to receive a MAVLink message.
48    ///
49    /// Non-blocking variant of `recv()`, returns immediately with a `MessageReadError`
50    /// if there is an error or no message is available.
51    ///
52    /// # Errors
53    ///
54    /// Returns any eror encounter while receiving or deserializing a message.
55    fn try_recv(&self) -> Result<(MavHeader, M), MessageReadError>;
56
57    /// Send a MAVLink message.
58    ///
59    /// # Errors
60    ///
61    /// This function will return a [`MessageWriteError::Io`] error when sending fails.
62    fn send(&self, header: &MavHeader, data: &M) -> Result<usize, MessageWriteError>;
63
64    /// Send a raw, unparsed MAVLink message.
65    ///
66    /// # Errors
67    ///
68    /// This function will return a [`MessageWriteError::Io`] error when sending fails.
69    fn send_raw(&self, data: &MAVLinkMessageRaw) -> Result<usize, MessageWriteError>;
70
71    /// Sets the MAVLink version to use for receiving (when `allow_recv_any_version()` is `false`) and sending messages.
72    fn set_protocol_version(&mut self, version: MavlinkVersion);
73    /// Gets the currently used MAVLink version
74    fn protocol_version(&self) -> MavlinkVersion;
75
76    /// Set wether MAVLink messages of either version may be received.
77    ///
78    /// If set to false only messages of the version configured with `set_protocol_version()` are received.
79    fn set_allow_recv_any_version(&mut self, allow: bool);
80
81    /// Wether messages of any MAVLink version may be received.
82    fn allow_recv_any_version(&self) -> bool;
83
84    /// Write whole frame.
85    ///
86    /// # Errors
87    ///
88    /// This function will return a [`MessageWriteError::Io`] error when sending fails.
89    fn send_frame(&self, frame: &MavFrame<M>) -> Result<usize, MessageWriteError> {
90        self.send(&frame.header, &frame.msg)
91    }
92
93    /// Read whole frame.
94    ///
95    /// # Errors
96    ///
97    /// Returns any eror encounter while receiving or deserializing a message.
98    fn recv_frame(&self) -> Result<MavFrame<M>, MessageReadError> {
99        let (header, msg) = self.recv()?;
100        let protocol_version = self.protocol_version();
101        Ok(MavFrame {
102            header,
103            msg,
104            protocol_version,
105        })
106    }
107
108    /// Send a message with default header.
109    ///
110    /// # Errors
111    ///
112    /// This function will return a [`MessageWriteError::Io`] error when sending fails.
113    fn send_default(&self, data: &M) -> Result<usize, MessageWriteError> {
114        let header = MavHeader::default();
115        self.send(&header, data)
116    }
117
118    /// Setup secret key used for message signing, or disable message signing
119    #[cfg(feature = "mav2-message-signing")]
120    fn setup_signing(&mut self, signing_data: Option<SigningConfig>);
121}
122
123/// Concrete MAVLink connection returned by [`connect`].
124pub struct Connection<M: Message> {
125    inner: ConnectionInner,
126    _p: PhantomData<M>,
127}
128
129enum ConnectionInner {
130    #[cfg(feature = "transport-tcp")]
131    Tcp(TcpConnection),
132    #[cfg(feature = "transport-udp")]
133    Udp(UdpConnection),
134    #[cfg(feature = "transport-direct-serial")]
135    Serial(SerialConnection),
136    File(FileConnection),
137}
138
139impl<M: Message> Connection<M> {
140    fn new(inner: ConnectionInner) -> Self {
141        Self {
142            inner,
143            _p: PhantomData,
144        }
145    }
146}
147
148#[cfg(feature = "transport-tcp")]
149impl<M: Message> From<TcpConnection> for Connection<M> {
150    fn from(value: TcpConnection) -> Self {
151        Self::new(ConnectionInner::Tcp(value))
152    }
153}
154
155#[cfg(feature = "transport-udp")]
156impl<M: Message> From<UdpConnection> for Connection<M> {
157    fn from(value: UdpConnection) -> Self {
158        Self::new(ConnectionInner::Udp(value))
159    }
160}
161
162#[cfg(feature = "transport-direct-serial")]
163impl<M: Message> From<SerialConnection> for Connection<M> {
164    fn from(value: SerialConnection) -> Self {
165        Self::new(ConnectionInner::Serial(value))
166    }
167}
168
169impl<M: Message> From<FileConnection> for Connection<M> {
170    fn from(value: FileConnection) -> Self {
171        Self::new(ConnectionInner::File(value))
172    }
173}
174
175impl<M: Message> MavConnection<M> for Connection<M> {
176    fn recv(&self) -> Result<(MavHeader, M), MessageReadError> {
177        match &self.inner {
178            #[cfg(feature = "transport-tcp")]
179            ConnectionInner::Tcp(conn) => <TcpConnection as MavConnection<M>>::recv(conn),
180            #[cfg(feature = "transport-udp")]
181            ConnectionInner::Udp(conn) => <UdpConnection as MavConnection<M>>::recv(conn),
182            #[cfg(feature = "transport-direct-serial")]
183            ConnectionInner::Serial(conn) => <SerialConnection as MavConnection<M>>::recv(conn),
184            ConnectionInner::File(conn) => <FileConnection as MavConnection<M>>::recv(conn),
185        }
186    }
187
188    fn recv_raw(&self) -> Result<MAVLinkMessageRaw, MessageReadError> {
189        match &self.inner {
190            #[cfg(feature = "transport-tcp")]
191            ConnectionInner::Tcp(conn) => <TcpConnection as MavConnection<M>>::recv_raw(conn),
192            #[cfg(feature = "transport-udp")]
193            ConnectionInner::Udp(conn) => <UdpConnection as MavConnection<M>>::recv_raw(conn),
194            #[cfg(feature = "transport-direct-serial")]
195            ConnectionInner::Serial(conn) => <SerialConnection as MavConnection<M>>::recv_raw(conn),
196            ConnectionInner::File(conn) => <FileConnection as MavConnection<M>>::recv_raw(conn),
197        }
198    }
199
200    fn try_recv(&self) -> Result<(MavHeader, M), MessageReadError> {
201        match &self.inner {
202            #[cfg(feature = "transport-tcp")]
203            ConnectionInner::Tcp(conn) => <TcpConnection as MavConnection<M>>::try_recv(conn),
204            #[cfg(feature = "transport-udp")]
205            ConnectionInner::Udp(conn) => <UdpConnection as MavConnection<M>>::try_recv(conn),
206            #[cfg(feature = "transport-direct-serial")]
207            ConnectionInner::Serial(conn) => <SerialConnection as MavConnection<M>>::try_recv(conn),
208            ConnectionInner::File(conn) => <FileConnection as MavConnection<M>>::try_recv(conn),
209        }
210    }
211
212    fn send(&self, header: &MavHeader, data: &M) -> Result<usize, MessageWriteError> {
213        match &self.inner {
214            #[cfg(feature = "transport-tcp")]
215            ConnectionInner::Tcp(conn) => {
216                <TcpConnection as MavConnection<M>>::send(conn, header, data)
217            }
218            #[cfg(feature = "transport-udp")]
219            ConnectionInner::Udp(conn) => {
220                <UdpConnection as MavConnection<M>>::send(conn, header, data)
221            }
222            #[cfg(feature = "transport-direct-serial")]
223            ConnectionInner::Serial(conn) => {
224                <SerialConnection as MavConnection<M>>::send(conn, header, data)
225            }
226            ConnectionInner::File(conn) => {
227                <FileConnection as MavConnection<M>>::send(conn, header, data)
228            }
229        }
230    }
231
232    fn send_raw(&self, data: &MAVLinkMessageRaw) -> Result<usize, MessageWriteError> {
233        match &self.inner {
234            #[cfg(feature = "transport-tcp")]
235            ConnectionInner::Tcp(conn) => <TcpConnection as MavConnection<M>>::send_raw(conn, data),
236            #[cfg(feature = "transport-udp")]
237            ConnectionInner::Udp(conn) => <UdpConnection as MavConnection<M>>::send_raw(conn, data),
238            #[cfg(feature = "transport-direct-serial")]
239            ConnectionInner::Serial(conn) => {
240                <SerialConnection as MavConnection<M>>::send_raw(conn, data)
241            }
242            ConnectionInner::File(conn) => {
243                <FileConnection as MavConnection<M>>::send_raw(conn, data)
244            }
245        }
246    }
247
248    fn set_protocol_version(&mut self, version: MavlinkVersion) {
249        match &mut self.inner {
250            #[cfg(feature = "transport-tcp")]
251            ConnectionInner::Tcp(conn) => {
252                <TcpConnection as MavConnection<M>>::set_protocol_version(conn, version);
253            }
254            #[cfg(feature = "transport-udp")]
255            ConnectionInner::Udp(conn) => {
256                <UdpConnection as MavConnection<M>>::set_protocol_version(conn, version);
257            }
258            #[cfg(feature = "transport-direct-serial")]
259            ConnectionInner::Serial(conn) => {
260                <SerialConnection as MavConnection<M>>::set_protocol_version(conn, version);
261            }
262            ConnectionInner::File(conn) => {
263                <FileConnection as MavConnection<M>>::set_protocol_version(conn, version);
264            }
265        }
266    }
267
268    fn protocol_version(&self) -> MavlinkVersion {
269        match &self.inner {
270            #[cfg(feature = "transport-tcp")]
271            ConnectionInner::Tcp(conn) => {
272                <TcpConnection as MavConnection<M>>::protocol_version(conn)
273            }
274            #[cfg(feature = "transport-udp")]
275            ConnectionInner::Udp(conn) => {
276                <UdpConnection as MavConnection<M>>::protocol_version(conn)
277            }
278            #[cfg(feature = "transport-direct-serial")]
279            ConnectionInner::Serial(conn) => {
280                <SerialConnection as MavConnection<M>>::protocol_version(conn)
281            }
282            ConnectionInner::File(conn) => {
283                <FileConnection as MavConnection<M>>::protocol_version(conn)
284            }
285        }
286    }
287
288    fn set_allow_recv_any_version(&mut self, allow: bool) {
289        match &mut self.inner {
290            #[cfg(feature = "transport-tcp")]
291            ConnectionInner::Tcp(conn) => {
292                <TcpConnection as MavConnection<M>>::set_allow_recv_any_version(conn, allow);
293            }
294            #[cfg(feature = "transport-udp")]
295            ConnectionInner::Udp(conn) => {
296                <UdpConnection as MavConnection<M>>::set_allow_recv_any_version(conn, allow);
297            }
298            #[cfg(feature = "transport-direct-serial")]
299            ConnectionInner::Serial(conn) => {
300                <SerialConnection as MavConnection<M>>::set_allow_recv_any_version(conn, allow);
301            }
302            ConnectionInner::File(conn) => {
303                <FileConnection as MavConnection<M>>::set_allow_recv_any_version(conn, allow);
304            }
305        }
306    }
307
308    fn allow_recv_any_version(&self) -> bool {
309        match &self.inner {
310            #[cfg(feature = "transport-tcp")]
311            ConnectionInner::Tcp(conn) => {
312                <TcpConnection as MavConnection<M>>::allow_recv_any_version(conn)
313            }
314            #[cfg(feature = "transport-udp")]
315            ConnectionInner::Udp(conn) => {
316                <UdpConnection as MavConnection<M>>::allow_recv_any_version(conn)
317            }
318            #[cfg(feature = "transport-direct-serial")]
319            ConnectionInner::Serial(conn) => {
320                <SerialConnection as MavConnection<M>>::allow_recv_any_version(conn)
321            }
322            ConnectionInner::File(conn) => {
323                <FileConnection as MavConnection<M>>::allow_recv_any_version(conn)
324            }
325        }
326    }
327
328    #[cfg(feature = "mav2-message-signing")]
329    fn setup_signing(&mut self, signing_data: Option<SigningConfig>) {
330        let mut signing_data = signing_data;
331        match &mut self.inner {
332            #[cfg(feature = "transport-tcp")]
333            ConnectionInner::Tcp(conn) => {
334                <TcpConnection as MavConnection<M>>::setup_signing(conn, signing_data.take());
335            }
336            #[cfg(feature = "transport-udp")]
337            ConnectionInner::Udp(conn) => {
338                <UdpConnection as MavConnection<M>>::setup_signing(conn, signing_data.take());
339            }
340            #[cfg(feature = "transport-direct-serial")]
341            ConnectionInner::Serial(conn) => {
342                <SerialConnection as MavConnection<M>>::setup_signing(conn, signing_data.take());
343            }
344            ConnectionInner::File(conn) => {
345                <FileConnection as MavConnection<M>>::setup_signing(conn, signing_data.take());
346            }
347        }
348    }
349}
350
351/// Connect to a MAVLink node by address string.
352///
353/// The address must be in one of the following formats:
354///
355///  * `tcpin:<addr>:<port>` to create a TCP server, listening an incoming connection
356///  * `tcpout:<addr>:<port>` to create a TCP client
357///  * `udpin:<addr>:<port>` to create a UDP server, listening for incoming packets
358///  * `udpout:<addr>:<port>` to create a UDP client
359///  * `udpbcast:<addr>:<port>` to create a UDP broadcast
360///  * `serial:<port>:<baudrate>` to create a serial connection
361///  * `file:<path>` to extract file data, writing to such a connection does nothing
362///
363/// The type of the connection is determined at runtime based on the address type
364/// and the resulting [`Connection`] enum stores the concrete transport.
365///
366/// # Errors
367///
368/// - [`AddrNotAvailable`] if the address string could not be parsed as a valid MAVLink address
369/// - When the connection could not be established a corresponding [`io::Error`] is returned
370///
371/// [`AddrNotAvailable`]: io::ErrorKind::AddrNotAvailable
372pub fn connect<M: Message + Sync + Send>(address: &str) -> io::Result<Connection<M>> {
373    ConnectionAddress::parse_address(address)?.connect::<M>()
374}
375
376/// A MAVLink connection address that can be connected to, establishing a [`MavConnection`]
377pub trait Connectable: Display {
378    /// Attempt to establish a blocking MAVLink connection
379    ///
380    /// # Errors
381    ///
382    /// When the connection could not be established a corresponding
383    /// [`io::Error`] is returned
384    fn connect<M: Message>(&self) -> io::Result<Connection<M>>;
385}
386
387impl Connectable for ConnectionAddress {
388    fn connect<M>(&self) -> std::io::Result<Connection<M>>
389    where
390        M: Message,
391    {
392        match self {
393            #[cfg(feature = "transport-tcp")]
394            Self::Tcp(config) => config.connect::<M>(),
395            #[cfg(feature = "transport-udp")]
396            Self::Udp(config) => config.connect::<M>(),
397            #[cfg(feature = "transport-direct-serial")]
398            Self::Serial(config) => config.connect::<M>(),
399            Self::File(config) => config.connect::<M>(),
400        }
401    }
402}