Skip to main content

mavlink_core/connection/udp/
config.rs

1use core::fmt::Display;
2use std::io;
3use std::net::UdpSocket;
4use std::sync::{Arc, Mutex};
5use std::time::Duration;
6
7/// Type of UDP connection
8///
9/// # Example
10///
11/// ```ignore
12/// use mavlink::{Connectable, UdpConfig, UdpMode};
13///
14/// let config = mavlink::UdpConfig::new("0.0.0.0:14552".to_owned(), UdpMode::Udpin);
15/// config
16///     .connect::<mavlink::ardupilotmega::MavMessage>()
17///     .unwrap();
18/// ```
19#[derive(Debug, Clone, Copy)]
20pub enum UdpMode {
21    /// Server connection waiting for a client connection
22    Udpin,
23    /// Client connection connecting to a server
24    Udpout,
25    /// Client connection that is allowed to send to broadcast addresses
26    UdpBroadcast,
27}
28
29/// MAVLink address for a UDP server client or broadcast connection
30#[derive(Debug, Clone)]
31pub struct UdpConfig {
32    pub(crate) address: String,
33    pub(crate) mode: UdpMode,
34    pub(crate) read_timeout: Option<Duration>,
35    pub(crate) source: UdpSource,
36}
37
38#[derive(Debug, Clone)]
39pub(crate) enum UdpSource {
40    Address,
41    Socket(Arc<Mutex<Option<UdpSocket>>>),
42}
43
44impl UdpConfig {
45    /// Creates a UDP connection address.
46    ///
47    /// The type of connection depends on the [`UdpMode`]
48    pub fn new(address: String, mode: UdpMode) -> Self {
49        Self {
50            address,
51            mode,
52            read_timeout: None,
53            source: UdpSource::Address,
54        }
55    }
56
57    /// Creates a UDP configuration from an existing socket.
58    ///
59    /// Input sockets must be bound. Output and broadcast sockets must also be
60    /// connected; their peer address is used as the destination. Socket-backed
61    /// configurations are one-shot, including across cloned configurations.
62    pub fn from_socket(socket: UdpSocket, mode: UdpMode) -> io::Result<Self> {
63        let address = match mode {
64            UdpMode::Udpin => socket.local_addr()?,
65            UdpMode::Udpout | UdpMode::UdpBroadcast => socket.peer_addr()?,
66        };
67
68        Ok(Self {
69            address: address.to_string(),
70            mode,
71            read_timeout: None,
72            source: UdpSource::Socket(Arc::new(Mutex::new(Some(socket)))),
73        })
74    }
75
76    pub(crate) fn take_socket(&self) -> io::Result<Option<UdpSocket>> {
77        match &self.source {
78            UdpSource::Address => Ok(None),
79            UdpSource::Socket(socket) => socket
80                .lock()
81                .map_err(|_| io::Error::other("UDP socket lock poisoned"))?
82                .take()
83                .map(Some)
84                .ok_or_else(|| io::Error::other("UDP socket-backed configuration already used")),
85        }
86    }
87
88    /// Sets the read timeout on the UDP socket.
89    ///
90    /// When set, `recv()` and `recv_raw()` will return an error after the
91    /// specified duration instead of blocking indefinitely. This is useful
92    /// for implementing graceful shutdown.
93    pub fn read_timeout(mut self, timeout: Duration) -> Self {
94        self.read_timeout = Some(timeout);
95        self
96    }
97}
98
99impl Display for UdpConfig {
100    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
101        let mode = match self.mode {
102            UdpMode::Udpin => "udpin",
103            UdpMode::Udpout => "udpout",
104            UdpMode::UdpBroadcast => "udpbcast",
105        };
106        write!(f, "{mode}:{}", self.address)
107    }
108}