//! Generic UDP TC server. use crate::tmtc::PacketSenderRaw; use crate::ComponentId; use core::fmt::Debug; use std::io::{self, ErrorKind}; use std::net::{SocketAddr, ToSocketAddrs, UdpSocket}; use std::vec; use std::vec::Vec; /// This UDP server can be used to receive CCSDS space packet telecommands or any other telecommand /// format. /// /// It caches all received telecomands into a vector. The maximum expected telecommand size should /// be declared upfront. This avoids dynamic allocation during run-time. The user can specify a TC /// sender in form of a special trait object which implements [PacketSenderRaw]. For example, this /// can be used to send the telecommands to a centralized TC source component for further /// processing and routing. /// /// # Examples /// /// ``` /// use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; /// use std::sync::mpsc; /// use spacepackets::ecss::WritablePusPacket; /// use satrs::hal::std::udp_server::UdpTcServer; /// use satrs::ComponentId; /// use satrs::tmtc::PacketSenderRaw; /// use spacepackets::SpHeader; /// use spacepackets::ecss::tc::PusTcCreator; /// /// const UDP_SERVER_ID: ComponentId = 0x05; /// /// let (packet_sender, packet_receiver) = mpsc::channel(); /// let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777); /// let mut udp_tc_server = UdpTcServer::new(UDP_SERVER_ID, dest_addr, 2048, packet_sender) /// .expect("Creating UDP TMTC server failed"); /// let sph = SpHeader::new_from_apid(0x02); /// let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); /// // Can not fail. /// let ping_tc_raw = pus_tc.to_vec().unwrap(); /// /// // Now create a UDP client and send the ping telecommand to the server. /// let client = UdpSocket::bind("127.0.0.1:0").expect("creating UDP client failed"); /// client /// .send_to(&ping_tc_raw, dest_addr) /// .expect("Error sending PUS TC via UDP"); /// let recv_result = udp_tc_server.try_recv_tc(); /// assert!(recv_result.is_ok()); /// // The packet is received by the UDP TC server and sent via the mpsc channel. /// let sent_packet_with_sender = packet_receiver.try_recv().expect("expected telecommand"); /// assert_eq!(sent_packet_with_sender.packet, ping_tc_raw); /// assert_eq!(sent_packet_with_sender.sender_id, UDP_SERVER_ID); /// // No more packets received. /// matches!(packet_receiver.try_recv(), Err(mpsc::TryRecvError::Empty)); /// ``` /// /// The [satrs-example crate](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/satrs-example) /// server code also includes /// [example code](https://egit.irs.uni-stuttgart.de/rust/sat-rs/src/branch/main/satrs-example/src/tmtc.rs#L67) /// on how to use this TC server. It uses the server to receive PUS telecommands on a specific port /// and then forwards them to a generic CCSDS packet receiver. pub struct UdpTcServer, SendError> { pub id: ComponentId, pub socket: UdpSocket, recv_buf: Vec, sender_addr: Option, pub tc_sender: TcSender, } #[derive(Debug, thiserror::Error)] pub enum ReceiveResult { #[error("nothing was received")] NothingReceived, #[error(transparent)] Io(#[from] io::Error), #[error(transparent)] Send(SendError), } impl, SendError: Debug + 'static> UdpTcServer { pub fn new( id: ComponentId, addr: A, max_recv_size: usize, tc_sender: TcSender, ) -> Result { let server = Self { id, socket: UdpSocket::bind(addr)?, recv_buf: vec![0; max_recv_size], sender_addr: None, tc_sender, }; server.socket.set_nonblocking(true)?; Ok(server) } pub fn try_recv_tc(&mut self) -> Result<(usize, SocketAddr), ReceiveResult> { let res = match self.socket.recv_from(&mut self.recv_buf) { Ok(res) => res, Err(e) => { return if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut { Err(ReceiveResult::NothingReceived) } else { Err(e.into()) } } }; let (num_bytes, from) = res; self.sender_addr = Some(from); self.tc_sender .send_packet(self.id, &self.recv_buf[0..num_bytes]) .map_err(ReceiveResult::Send)?; Ok(res) } pub fn last_sender(&self) -> Option { self.sender_addr } } #[cfg(test)] mod tests { use crate::hal::std::udp_server::{ReceiveResult, UdpTcServer}; use crate::queue::GenericSendError; use crate::tmtc::PacketSenderRaw; use crate::ComponentId; use core::cell::RefCell; use spacepackets::ecss::tc::PusTcCreator; use spacepackets::ecss::WritablePusPacket; use spacepackets::SpHeader; use std::collections::VecDeque; use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; use std::vec::Vec; fn is_send(_: &T) {} const UDP_SERVER_ID: ComponentId = 0x05; #[derive(Default)] struct PingReceiver { pub sent_cmds: RefCell>>, } impl PacketSenderRaw for PingReceiver { type Error = GenericSendError; fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> { assert_eq!(sender_id, UDP_SERVER_ID); let mut sent_data = Vec::new(); sent_data.extend_from_slice(tc_raw); let mut queue = self.sent_cmds.borrow_mut(); queue.push_back(sent_data); Ok(()) } } #[test] fn basic_test() { let mut buf = [0; 32]; let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7777); let ping_receiver = PingReceiver::default(); is_send(&ping_receiver); let mut udp_tc_server = UdpTcServer::new(UDP_SERVER_ID, dest_addr, 2048, ping_receiver) .expect("Creating UDP TMTC server failed"); is_send(&udp_tc_server); let sph = SpHeader::new_from_apid(0x02); let pus_tc = PusTcCreator::new_simple(sph, 17, 1, &[], true); let len = pus_tc .write_to_bytes(&mut buf) .expect("Error writing PUS TC packet"); let client = UdpSocket::bind("127.0.0.1:7778").expect("Connecting to UDP server failed"); client .send_to(&buf[0..len], dest_addr) .expect("Error sending PUS TC via UDP"); let local_addr = client.local_addr().unwrap(); udp_tc_server .try_recv_tc() .expect("Error receiving sent telecommand"); assert_eq!( udp_tc_server.last_sender().expect("No sender set"), local_addr ); let ping_receiver = &mut udp_tc_server.tc_sender; let mut queue = ping_receiver.sent_cmds.borrow_mut(); assert_eq!(queue.len(), 1); let sent_cmd = queue.pop_front().unwrap(); assert_eq!(sent_cmd, buf[0..len]); } #[test] fn test_nothing_received() { let dest_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7779); let ping_receiver = PingReceiver::default(); let mut udp_tc_server = UdpTcServer::new(UDP_SERVER_ID, dest_addr, 2048, ping_receiver) .expect("Creating UDP TMTC server failed"); let res = udp_tc_server.try_recv_tc(); assert!(res.is_err()); let err = res.unwrap_err(); matches!(err, ReceiveResult::NothingReceived); } }