//! UDP transport. //! //! Single-process MAVLink links over UDP behave like a connected datagram pair: //! the autopilot binds a local port and exchanges datagrams with the peer. //! Here we bind to the OS-chosen local port `0.0.0.0:0` and `connect` to the //! configured peer so the socket can be used like a stream. use async_trait::async_trait; use tokio::net::UdpSocket; use shared::error::{AutopilotError, Result}; use super::Transport; #[derive(Debug)] pub struct UdpTransport { socket: UdpSocket, } impl UdpTransport { /// Bind a local UDP socket and `connect` it to `peer`, so subsequent /// `send` / `recv` calls behave like a stream. pub async fn connect(peer: &str) -> Result { let socket = UdpSocket::bind("0.0.0.0:0") .await .map_err(|e| AutopilotError::Network(format!("udp bind failed: {e}")))?; socket .connect(peer) .await .map_err(|e| AutopilotError::Network(format!("udp connect failed: {e}")))?; Ok(Self { socket }) } #[allow(dead_code)] // Used by the AZ-641 UDP integration tests in `tests/`. pub fn local_addr(&self) -> Result { self.socket .local_addr() .map_err(|e| AutopilotError::Network(format!("udp local_addr failed: {e}"))) } } #[async_trait] impl Transport for UdpTransport { async fn read(&mut self, buf: &mut [u8]) -> Result { self.socket .recv(buf) .await .map_err(|e| AutopilotError::Network(format!("udp recv: {e}"))) } async fn write_all(&mut self, buf: &[u8]) -> Result<()> { self.socket .send(buf) .await .map_err(|e| AutopilotError::Network(format!("udp send: {e}")))?; Ok(()) } }