Re-worked TMTC modules
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
This commit is contained in:
@ -1,21 +1,41 @@
|
||||
use std::{
|
||||
collections::{HashSet, VecDeque},
|
||||
fmt::Debug,
|
||||
marker::PhantomData,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use log::{info, warn};
|
||||
use satrs::{
|
||||
encoding::ccsds::{SpValidity, SpacePacketValidator},
|
||||
hal::std::tcp_server::{HandledConnectionHandler, ServerConfig, TcpSpacepacketsServer},
|
||||
pus::ReceivesEcssPusTc,
|
||||
spacepackets::PacketId,
|
||||
tmtc::{CcsdsDistributor, CcsdsError, ReceivesCcsdsTc, TmPacketSourceCore},
|
||||
spacepackets::{CcsdsPacket, PacketId},
|
||||
tmtc::{PacketSenderRaw, PacketSource},
|
||||
};
|
||||
|
||||
use crate::tmtc::ccsds::CcsdsReceiver;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ConnectionFinishedHandler {}
|
||||
|
||||
pub struct SimplePacketValidator {
|
||||
pub valid_ids: HashSet<PacketId>,
|
||||
}
|
||||
|
||||
impl SpacePacketValidator for SimplePacketValidator {
|
||||
fn validate(
|
||||
&self,
|
||||
sp_header: &satrs::spacepackets::SpHeader,
|
||||
_raw_buf: &[u8],
|
||||
) -> satrs::encoding::ccsds::SpValidity {
|
||||
if self.valid_ids.contains(&sp_header.packet_id()) {
|
||||
return SpValidity::Valid;
|
||||
}
|
||||
log::warn!("ignoring space packet with header {:?}", sp_header);
|
||||
// We could perform a CRC check.. but lets keep this simple and assume that TCP ensures
|
||||
// data integrity.
|
||||
SpValidity::Skip
|
||||
}
|
||||
}
|
||||
|
||||
impl HandledConnectionHandler for ConnectionFinishedHandler {
|
||||
fn handled_connection(&mut self, info: satrs::hal::std::tcp_server::HandledConnectionInfo) {
|
||||
info!(
|
||||
@ -53,7 +73,7 @@ impl SyncTcpTmSource {
|
||||
}
|
||||
}
|
||||
|
||||
impl TmPacketSourceCore for SyncTcpTmSource {
|
||||
impl PacketSource for SyncTcpTmSource {
|
||||
type Error = ();
|
||||
|
||||
fn retrieve_packet(&mut self, buffer: &mut [u8]) -> Result<usize, Self::Error> {
|
||||
@ -81,56 +101,45 @@ impl TmPacketSourceCore for SyncTcpTmSource {
|
||||
}
|
||||
}
|
||||
|
||||
pub type TcpServerType<TcSource, MpscErrorType> = TcpSpacepacketsServer<
|
||||
pub type TcpServer<ReceivesTc, SendError> = TcpSpacepacketsServer<
|
||||
SyncTcpTmSource,
|
||||
CcsdsDistributor<CcsdsReceiver<TcSource, MpscErrorType>, MpscErrorType>,
|
||||
HashSet<PacketId>,
|
||||
ReceivesTc,
|
||||
SimplePacketValidator,
|
||||
ConnectionFinishedHandler,
|
||||
(),
|
||||
CcsdsError<MpscErrorType>,
|
||||
SendError,
|
||||
>;
|
||||
|
||||
pub struct TcpTask<
|
||||
TcSource: ReceivesCcsdsTc<Error = MpscErrorType>
|
||||
+ ReceivesEcssPusTc<Error = MpscErrorType>
|
||||
+ Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
MpscErrorType: 'static,
|
||||
> {
|
||||
server: TcpServerType<TcSource, MpscErrorType>,
|
||||
}
|
||||
pub struct TcpTask<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>(
|
||||
pub TcpServer<TcSender, SendError>,
|
||||
PhantomData<SendError>,
|
||||
);
|
||||
|
||||
impl<
|
||||
TcSource: ReceivesCcsdsTc<Error = MpscErrorType>
|
||||
+ ReceivesEcssPusTc<Error = MpscErrorType>
|
||||
+ Clone
|
||||
+ Send
|
||||
+ 'static,
|
||||
MpscErrorType: 'static + core::fmt::Debug,
|
||||
> TcpTask<TcSource, MpscErrorType>
|
||||
impl<TcSender: PacketSenderRaw<Error = SendError>, SendError: Debug + 'static>
|
||||
TcpTask<TcSender, SendError>
|
||||
{
|
||||
pub fn new(
|
||||
cfg: ServerConfig,
|
||||
tm_source: SyncTcpTmSource,
|
||||
tc_receiver: CcsdsDistributor<CcsdsReceiver<TcSource, MpscErrorType>, MpscErrorType>,
|
||||
packet_id_lookup: HashSet<PacketId>,
|
||||
tc_sender: TcSender,
|
||||
valid_ids: HashSet<PacketId>,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
Ok(Self {
|
||||
server: TcpSpacepacketsServer::new(
|
||||
Ok(Self(
|
||||
TcpSpacepacketsServer::new(
|
||||
cfg,
|
||||
tm_source,
|
||||
tc_receiver,
|
||||
packet_id_lookup,
|
||||
tc_sender,
|
||||
SimplePacketValidator { valid_ids },
|
||||
ConnectionFinishedHandler::default(),
|
||||
None,
|
||||
)?,
|
||||
})
|
||||
PhantomData,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn periodic_operation(&mut self) {
|
||||
loop {
|
||||
let result = self.server.handle_next_connection(None);
|
||||
let result = self.0.handle_all_connections(None);
|
||||
match result {
|
||||
Ok(_conn_result) => (),
|
||||
Err(e) => {
|
||||
|
@ -1,20 +1,22 @@
|
||||
use core::fmt::Debug;
|
||||
use std::net::{SocketAddr, UdpSocket};
|
||||
use std::sync::mpsc;
|
||||
|
||||
use log::{info, warn};
|
||||
use satrs::pus::{PusTmAsVec, PusTmInPool};
|
||||
use satrs::tmtc::{PacketAsVec, PacketInPool, PacketSenderRaw};
|
||||
use satrs::{
|
||||
hal::std::udp_server::{ReceiveResult, UdpTcServer},
|
||||
pool::{PoolProviderWithGuards, SharedStaticMemoryPool},
|
||||
tmtc::CcsdsError,
|
||||
};
|
||||
|
||||
use crate::pus::HandlingStatus;
|
||||
|
||||
pub trait UdpTmHandler {
|
||||
fn send_tm_to_udp_client(&mut self, socket: &UdpSocket, recv_addr: &SocketAddr);
|
||||
}
|
||||
|
||||
pub struct StaticUdpTmHandler {
|
||||
pub tm_rx: mpsc::Receiver<PusTmInPool>,
|
||||
pub tm_rx: mpsc::Receiver<PacketInPool>,
|
||||
pub tm_store: SharedStaticMemoryPool,
|
||||
}
|
||||
|
||||
@ -43,7 +45,7 @@ impl UdpTmHandler for StaticUdpTmHandler {
|
||||
}
|
||||
|
||||
pub struct DynamicUdpTmHandler {
|
||||
pub tm_rx: mpsc::Receiver<PusTmAsVec>,
|
||||
pub tm_rx: mpsc::Receiver<PacketAsVec>,
|
||||
}
|
||||
|
||||
impl UdpTmHandler for DynamicUdpTmHandler {
|
||||
@ -64,42 +66,48 @@ impl UdpTmHandler for DynamicUdpTmHandler {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UdpTmtcServer<TmHandler: UdpTmHandler, SendError> {
|
||||
pub udp_tc_server: UdpTcServer<CcsdsError<SendError>>,
|
||||
pub struct UdpTmtcServer<
|
||||
TcSender: PacketSenderRaw<Error = SendError>,
|
||||
TmHandler: UdpTmHandler,
|
||||
SendError,
|
||||
> {
|
||||
pub udp_tc_server: UdpTcServer<TcSender, SendError>,
|
||||
pub tm_handler: TmHandler,
|
||||
}
|
||||
|
||||
impl<TmHandler: UdpTmHandler, SendError: core::fmt::Debug + 'static>
|
||||
UdpTmtcServer<TmHandler, SendError>
|
||||
impl<
|
||||
TcSender: PacketSenderRaw<Error = SendError>,
|
||||
TmHandler: UdpTmHandler,
|
||||
SendError: Debug + 'static,
|
||||
> UdpTmtcServer<TcSender, TmHandler, SendError>
|
||||
{
|
||||
pub fn periodic_operation(&mut self) {
|
||||
while self.poll_tc_server() {}
|
||||
loop {
|
||||
if self.poll_tc_server() == HandlingStatus::Empty {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let Some(recv_addr) = self.udp_tc_server.last_sender() {
|
||||
self.tm_handler
|
||||
.send_tm_to_udp_client(&self.udp_tc_server.socket, &recv_addr);
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_tc_server(&mut self) -> bool {
|
||||
fn poll_tc_server(&mut self) -> HandlingStatus {
|
||||
match self.udp_tc_server.try_recv_tc() {
|
||||
Ok(_) => true,
|
||||
Err(e) => match e {
|
||||
ReceiveResult::ReceiverError(e) => match e {
|
||||
CcsdsError::ByteConversionError(e) => {
|
||||
warn!("packet error: {e:?}");
|
||||
true
|
||||
Ok(_) => HandlingStatus::HandledOne,
|
||||
Err(e) => {
|
||||
match e {
|
||||
ReceiveResult::NothingReceived => (),
|
||||
ReceiveResult::Io(e) => {
|
||||
warn!("IO error {e}");
|
||||
}
|
||||
CcsdsError::CustomError(e) => {
|
||||
warn!("mpsc custom error {e:?}");
|
||||
true
|
||||
ReceiveResult::Send(send_error) => {
|
||||
warn!("send error {send_error:?}");
|
||||
}
|
||||
},
|
||||
ReceiveResult::IoError(e) => {
|
||||
warn!("IO error {e}");
|
||||
false
|
||||
}
|
||||
ReceiveResult::NothingReceived => false,
|
||||
},
|
||||
HandlingStatus::Empty
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -107,6 +115,7 @@ impl<TmHandler: UdpTmHandler, SendError: core::fmt::Debug + 'static>
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::VecDeque,
|
||||
net::IpAddr,
|
||||
sync::{Arc, Mutex},
|
||||
@ -117,21 +126,26 @@ mod tests {
|
||||
ecss::{tc::PusTcCreator, WritablePusPacket},
|
||||
SpHeader,
|
||||
},
|
||||
tmtc::ReceivesTcCore,
|
||||
tmtc::PacketSenderRaw,
|
||||
ComponentId,
|
||||
};
|
||||
use satrs_example::config::{components, OBSW_SERVER_ADDR};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct TestReceiver {
|
||||
tc_vec: Arc<Mutex<VecDeque<Vec<u8>>>>,
|
||||
const UDP_SERVER_ID: ComponentId = 0x05;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TestSender {
|
||||
tc_vec: RefCell<VecDeque<PacketAsVec>>,
|
||||
}
|
||||
|
||||
impl ReceivesTcCore for TestReceiver {
|
||||
type Error = CcsdsError<()>;
|
||||
fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||
self.tc_vec.lock().unwrap().push_back(tc_raw.to_vec());
|
||||
impl PacketSenderRaw for TestSender {
|
||||
type Error = ();
|
||||
|
||||
fn send_packet(&self, sender_id: ComponentId, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||
let mut mut_queue = self.tc_vec.borrow_mut();
|
||||
mut_queue.push_back(PacketAsVec::new(sender_id, tc_raw.to_vec()));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -150,9 +164,10 @@ mod tests {
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), 0);
|
||||
let test_receiver = TestReceiver::default();
|
||||
let tc_queue = test_receiver.tc_vec.clone();
|
||||
let udp_tc_server = UdpTcServer::new(sock_addr, 2048, Box::new(test_receiver)).unwrap();
|
||||
let test_receiver = TestSender::default();
|
||||
// let tc_queue = test_receiver.tc_vec.clone();
|
||||
let udp_tc_server =
|
||||
UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap();
|
||||
let tm_handler = TestTmHandler::default();
|
||||
let tm_handler_calls = tm_handler.addrs_to_send_to.clone();
|
||||
let mut udp_dyn_server = UdpTmtcServer {
|
||||
@ -160,16 +175,18 @@ mod tests {
|
||||
tm_handler,
|
||||
};
|
||||
udp_dyn_server.periodic_operation();
|
||||
assert!(tc_queue.lock().unwrap().is_empty());
|
||||
let queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow();
|
||||
assert!(queue.is_empty());
|
||||
assert!(tm_handler_calls.lock().unwrap().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_transactions() {
|
||||
let sock_addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), 0);
|
||||
let test_receiver = TestReceiver::default();
|
||||
let tc_queue = test_receiver.tc_vec.clone();
|
||||
let udp_tc_server = UdpTcServer::new(sock_addr, 2048, Box::new(test_receiver)).unwrap();
|
||||
let test_receiver = TestSender::default();
|
||||
// let tc_queue = test_receiver.tc_vec.clone();
|
||||
let udp_tc_server =
|
||||
UdpTcServer::new(UDP_SERVER_ID, sock_addr, 2048, test_receiver).unwrap();
|
||||
let server_addr = udp_tc_server.socket.local_addr().unwrap();
|
||||
let tm_handler = TestTmHandler::default();
|
||||
let tm_handler_calls = tm_handler.addrs_to_send_to.clone();
|
||||
@ -187,10 +204,11 @@ mod tests {
|
||||
client.send(&ping_tc).unwrap();
|
||||
udp_dyn_server.periodic_operation();
|
||||
{
|
||||
let mut tc_queue = tc_queue.lock().unwrap();
|
||||
assert!(!tc_queue.is_empty());
|
||||
let received_tc = tc_queue.pop_front().unwrap();
|
||||
assert_eq!(received_tc, ping_tc);
|
||||
let mut queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow_mut();
|
||||
assert!(!queue.is_empty());
|
||||
let packet_with_sender = queue.pop_front().unwrap();
|
||||
assert_eq!(packet_with_sender.packet, ping_tc);
|
||||
assert_eq!(packet_with_sender.sender_id, UDP_SERVER_ID);
|
||||
}
|
||||
|
||||
{
|
||||
@ -201,7 +219,9 @@ mod tests {
|
||||
assert_eq!(received_addr, client_addr);
|
||||
}
|
||||
udp_dyn_server.periodic_operation();
|
||||
assert!(tc_queue.lock().unwrap().is_empty());
|
||||
let queue = udp_dyn_server.udp_tc_server.tc_sender.tc_vec.borrow();
|
||||
assert!(queue.is_empty());
|
||||
drop(queue);
|
||||
// Still tries to send to the same client.
|
||||
{
|
||||
let mut tm_handler_calls = tm_handler_calls.lock().unwrap();
|
||||
|
Reference in New Issue
Block a user