use std::io;
use std::net::SocketAddr;
use tokio::net::{ToSocketAddrs, UdpSocket};
use super::link_fault;
use crate::error::{MavlinkError, Result};
use crate::link::ByteLink;
pub struct UdpLink {
socket: UdpSocket,
peer: Option<SocketAddr>,
}
impl UdpLink {
pub async fn bind(local: impl ToSocketAddrs) -> io::Result<Self> {
let socket = UdpSocket::bind(local).await?;
Ok(UdpLink { socket, peer: None })
}
pub async fn connect(local: impl ToSocketAddrs, remote: SocketAddr) -> io::Result<Self> {
let socket = UdpSocket::bind(local).await?;
Ok(UdpLink {
socket,
peer: Some(remote),
})
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.socket.local_addr()
}
pub fn peer(&self) -> Option<SocketAddr> {
self.peer
}
}
impl ByteLink for UdpLink {
async fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
let (n, from) = self.socket.recv_from(buf).await.map_err(link_fault)?;
self.peer = Some(from);
Ok(n)
}
async fn write_all(&mut self, data: &[u8]) -> Result<()> {
let peer = self.peer.ok_or(MavlinkError::Closed)?;
let sent = self.socket.send_to(data, peer).await.map_err(link_fault)?;
if sent != data.len() {
return Err(MavlinkError::Closed);
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::dialect::{Heartbeat, Message};
use crate::link::Connection;
#[tokio::test]
async fn a_frame_crosses_a_real_udp_socket_pair() {
let vehicle = UdpLink::bind("127.0.0.1:0").await.unwrap();
let vehicle_addr = vehicle.local_addr().unwrap();
let gcs = UdpLink::connect("127.0.0.1:0", vehicle_addr).await.unwrap();
let mut vehicle = Connection::new(vehicle, 1, 1);
let mut gcs = Connection::new(gcs, 255, 190);
let heartbeat = Heartbeat {
custom_mode: 0,
type_: 2,
autopilot: 3,
base_mode: 0,
system_status: 4,
mavlink_version: 3,
};
gcs.send(&heartbeat).await.unwrap();
let frame = vehicle.recv().await.unwrap();
assert_eq!(frame.message_id(), Heartbeat::ID);
vehicle.send(&heartbeat).await.unwrap();
let reply = gcs.recv().await.unwrap();
assert_eq!(reply.message_id(), Heartbeat::ID);
}
}