diff --git a/fsrc-core/src/hal/host/udp_server.rs b/fsrc-core/src/hal/host/udp_server.rs index e880b5f..f112ea7 100644 --- a/fsrc-core/src/hal/host/udp_server.rs +++ b/fsrc-core/src/hal/host/udp_server.rs @@ -16,8 +16,8 @@ use std::vec::Vec; /// /// # Examples /// -/// The [fsrc-example crate](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/obsw-client-example/fsrc-example) server code includes -/// [example code](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/obsw-client-example/fsrc-example/src/bin/obsw/tmtc.rs) +/// The [fsrc-example crate](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-example) server code includes +/// [example code](https://egit.irs.uni-stuttgart.de/rust/fsrc-launchpad/src/branch/main/fsrc-example/src/bin/obsw/tmtc.rs) /// 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 { @@ -29,7 +29,7 @@ pub struct UdpTcServer { #[derive(Debug)] pub enum ReceiveResult { - WouldBlock, + NothingReceived, IoError(Error), ReceiverError(E), } @@ -40,7 +40,21 @@ impl From for ReceiveResult { } } -impl UdpTcServer { +impl PartialEq for ReceiveResult { + fn eq(&self, other: &Self) -> bool { + use ReceiveResult::*; + match (self, other) { + (IoError(ref e), IoError(ref other_e)) => e.kind() == other_e.kind(), + (NothingReceived, NothingReceived) => true, + (ReceiverError(e), ReceiverError(other_e)) => e == other_e, + _ => false, + } + } +} + +impl Eq for ReceiveResult {} + +impl UdpTcServer { pub fn new( addr: A, max_recv_size: usize, @@ -61,7 +75,7 @@ impl UdpTcServer { Ok(res) => res, Err(e) => { return if e.kind() == ErrorKind::WouldBlock || e.kind() == ErrorKind::TimedOut { - Err(ReceiveResult::WouldBlock) + Err(ReceiveResult::NothingReceived) } else { Err(e.into()) } @@ -79,3 +93,73 @@ impl UdpTcServer { self.sender_addr } } + +#[cfg(test)] +mod tests { + use crate::hal::host::udp_server::{ReceiveResult, UdpTcServer}; + use crate::tmtc::ReceivesTc; + use spacepackets::tc::PusTc; + use spacepackets::SpHeader; + use std::boxed::Box; + use std::collections::VecDeque; + use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket}; + use std::vec::Vec; + + #[derive(Default)] + struct PingReceiver { + pub sent_cmds: VecDeque>, + } + + impl ReceivesTc for PingReceiver { + type Error = (); + + fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> { + let mut sent_data = Vec::new(); + sent_data.extend_from_slice(tc_raw); + self.sent_cmds.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(); + let mut udp_tc_server = UdpTcServer::new(dest_addr, 2048, Box::new(ping_receiver)) + .expect("Creating UDP TMTC server failed"); + let mut sph = SpHeader::tc(0x02, 0, 0).unwrap(); + let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true); + let len = pus_tc + .write_to(&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 PingReceiver = udp_tc_server.tc_receiver.downcast_mut().unwrap(); + assert_eq!(ping_receiver.sent_cmds.len(), 1); + let sent_cmd = ping_receiver.sent_cmds.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(dest_addr, 2048, Box::new(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(); + assert_eq!(err, ReceiveResult::NothingReceived); + } +} diff --git a/fsrc-core/src/hal/mod.rs b/fsrc-core/src/hal/mod.rs index 0c9616d..ddaf8f3 100644 --- a/fsrc-core/src/hal/mod.rs +++ b/fsrc-core/src/hal/mod.rs @@ -1,2 +1,3 @@ +//! # Hardware Abstraction Layer module #[cfg(feature = "std")] pub mod host; diff --git a/fsrc-core/src/tmtc/mod.rs b/fsrc-core/src/tmtc/mod.rs index d9e954a..b1d0b3d 100644 --- a/fsrc-core/src/tmtc/mod.rs +++ b/fsrc-core/src/tmtc/mod.rs @@ -6,6 +6,7 @@ //! Application Process ID (APID) or the ECSS PUS service type. This allows for fast packet //! routing without the overhead and complication of using message queues. However, it also requires use crate::error::{FsrcErrorRaw, FsrcGroupIds}; +use downcast_rs::{impl_downcast, Downcast}; use spacepackets::tc::PusTc; use spacepackets::SpHeader; @@ -43,11 +44,13 @@ const _FROM_BYTES_ZEROCOPY_ERROR: FsrcErrorRaw = FsrcErrorRaw::new( /// This trait is implemented by both the [crate::tmtc::pus_distrib::PusDistributor] and the /// [crate::tmtc::ccsds_distrib::CcsdsDistributor] which allows to pass the respective packets in /// raw byte format into them. -pub trait ReceivesTc { +pub trait ReceivesTc: Downcast { type Error; fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error>; } +impl_downcast!(ReceivesTc assoc Error); + /// Generic trait for object which can receive CCSDS space packets, for fsrc-example ECSS PUS packets /// for CCSDS File Delivery Protocol (CFDP) packets. /// diff --git a/fsrc-example/src/bin/obsw/tmtc.rs b/fsrc-example/src/bin/obsw/tmtc.rs index 4ac7237..5a34d1e 100644 --- a/fsrc-example/src/bin/obsw/tmtc.rs +++ b/fsrc-example/src/bin/obsw/tmtc.rs @@ -87,7 +87,7 @@ fn core_tc_handling(udp_tmtc_server: &mut UdpTmtcServer) -> bool { println!("IO error {e}"); false } - ReceiveResult::WouldBlock => false, + ReceiveResult::NothingReceived => false, }, } }