Merge pull request 'CCSDS and ECSS packet routing' (#8) from mueller/packet-routing into main
Reviewed-on: rust/fsrc-launchpad#8
This commit is contained in:
commit
83d7aad757
@ -1,12 +1,13 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
<component name="ProjectRunConfigurationManager">
|
||||||
<configuration default="false" name="Check" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
<configuration default="false" name="Check" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
<option name="command" value="run" />
|
<option name="command" value="check" />
|
||||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||||
<option name="channel" value="DEFAULT" />
|
<option name="channel" value="DEFAULT" />
|
||||||
<option name="requiredFeatures" value="true" />
|
<option name="requiredFeatures" value="false" />
|
||||||
<option name="allFeatures" value="true" />
|
<option name="allFeatures" value="false" />
|
||||||
<option name="emulateTerminal" value="false" />
|
<option name="emulateTerminal" value="false" />
|
||||||
<option name="withSudo" value="false" />
|
<option name="withSudo" value="false" />
|
||||||
|
<option name="buildTarget" value="REMOTE" />
|
||||||
<option name="backtrace" value="SHORT" />
|
<option name="backtrace" value="SHORT" />
|
||||||
<envs />
|
<envs />
|
||||||
<option name="isRedirectInput" value="false" />
|
<option name="isRedirectInput" value="false" />
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||||
<option name="channel" value="DEFAULT" />
|
<option name="channel" value="DEFAULT" />
|
||||||
<option name="requiredFeatures" value="true" />
|
<option name="requiredFeatures" value="true" />
|
||||||
<option name="allFeatures" value="false" />
|
<option name="allFeatures" value="true" />
|
||||||
<option name="emulateTerminal" value="false" />
|
<option name="emulateTerminal" value="false" />
|
||||||
<option name="withSudo" value="false" />
|
<option name="withSudo" value="false" />
|
||||||
<option name="buildTarget" value="REMOTE" />
|
<option name="buildTarget" value="REMOTE" />
|
||||||
|
19
.idea/runConfigurations/Doctest.xml
Normal file
19
.idea/runConfigurations/Doctest.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<component name="ProjectRunConfigurationManager">
|
||||||
|
<configuration default="false" name="Doctest" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||||
|
<option name="command" value="test --doc" />
|
||||||
|
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||||
|
<option name="channel" value="DEFAULT" />
|
||||||
|
<option name="requiredFeatures" value="true" />
|
||||||
|
<option name="allFeatures" value="false" />
|
||||||
|
<option name="emulateTerminal" value="false" />
|
||||||
|
<option name="withSudo" value="false" />
|
||||||
|
<option name="buildTarget" value="REMOTE" />
|
||||||
|
<option name="backtrace" value="SHORT" />
|
||||||
|
<envs />
|
||||||
|
<option name="isRedirectInput" value="false" />
|
||||||
|
<option name="redirectInputPath" value="" />
|
||||||
|
<method v="2">
|
||||||
|
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||||
|
</method>
|
||||||
|
</configuration>
|
||||||
|
</component>
|
@ -4,9 +4,10 @@
|
|||||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||||
<option name="channel" value="DEFAULT" />
|
<option name="channel" value="DEFAULT" />
|
||||||
<option name="requiredFeatures" value="true" />
|
<option name="requiredFeatures" value="true" />
|
||||||
<option name="allFeatures" value="false" />
|
<option name="allFeatures" value="true" />
|
||||||
<option name="emulateTerminal" value="false" />
|
<option name="emulateTerminal" value="false" />
|
||||||
<option name="withSudo" value="false" />
|
<option name="withSudo" value="false" />
|
||||||
|
<option name="buildTarget" value="REMOTE" />
|
||||||
<option name="backtrace" value="SHORT" />
|
<option name="backtrace" value="SHORT" />
|
||||||
<envs />
|
<envs />
|
||||||
<option name="isRedirectInput" value="false" />
|
<option name="isRedirectInput" value="false" />
|
||||||
|
60
Cargo.lock
generated
60
Cargo.lock
generated
@ -110,7 +110,6 @@ dependencies = [
|
|||||||
"js-sys",
|
"js-sys",
|
||||||
"num-integer",
|
"num-integer",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
"time",
|
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
@ -201,6 +200,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "downcast-rs"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-hal"
|
name = "embedded-hal"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
@ -216,14 +221,23 @@ name = "fsrc-core"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bus",
|
"bus",
|
||||||
|
"downcast-rs",
|
||||||
"num",
|
"num",
|
||||||
"postcard 1.0.1",
|
"once_cell",
|
||||||
|
"postcard",
|
||||||
"serde",
|
"serde",
|
||||||
"spacepackets",
|
"spacepackets",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fsrc-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"spacepackets",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hash32"
|
name = "hash32"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@ -411,9 +425,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.13.0"
|
version = "1.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot_core"
|
name = "parking_lot_core"
|
||||||
@ -429,17 +443,6 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "postcard"
|
|
||||||
version = "0.7.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a25c0b0ae06fcffe600ad392aabfa535696c8973f2253d9ac83171924c58a858"
|
|
||||||
dependencies = [
|
|
||||||
"heapless",
|
|
||||||
"postcard-cobs",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "postcard"
|
name = "postcard"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -451,12 +454,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "postcard-cobs"
|
|
||||||
version = "0.1.5-pre"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.39"
|
version = "1.0.39"
|
||||||
@ -597,8 +594,8 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"crc",
|
"crc",
|
||||||
"delegate",
|
"delegate",
|
||||||
"num",
|
"num-traits",
|
||||||
"postcard 0.7.3",
|
"postcard",
|
||||||
"serde",
|
"serde",
|
||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
@ -661,17 +658,6 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "time"
|
|
||||||
version = "0.1.44"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"wasi",
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-ident"
|
name = "unicode-ident"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -705,12 +691,6 @@ dependencies = [
|
|||||||
"vcell",
|
"vcell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "wasi"
|
|
||||||
version = "0.10.0+wasi-snapshot-preview1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasm-bindgen"
|
name = "wasm-bindgen"
|
||||||
version = "0.2.82"
|
version = "0.2.82"
|
||||||
|
@ -3,4 +3,5 @@
|
|||||||
members = [
|
members = [
|
||||||
"fsrc-core",
|
"fsrc-core",
|
||||||
"spacepackets",
|
"spacepackets",
|
||||||
|
"fsrc-example"
|
||||||
]
|
]
|
||||||
|
@ -9,13 +9,20 @@ edition = "2021"
|
|||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
bus = "2.2.3"
|
bus = "2.2.3"
|
||||||
num = "0.4"
|
num = "0.4"
|
||||||
spacepackets = { path = "../spacepackets"}
|
|
||||||
|
[dependencies.spacepackets]
|
||||||
|
path = "../spacepackets"
|
||||||
|
|
||||||
|
[dependencies.downcast-rs]
|
||||||
|
version = "1.2.0"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
postcard = { version = "1.0.1", features = ["use-std"] }
|
postcard = { version = "1.0.1", features = ["use-std"] }
|
||||||
serde = "1.0.143"
|
serde = "1.0.143"
|
||||||
zerocopy = "0.6.1"
|
zerocopy = "0.6.1"
|
||||||
|
once_cell = "1.13.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["use_std"]
|
default = ["use_std"]
|
||||||
use_std = []
|
use_std = ["downcast-rs/std"]
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
extern crate core;
|
|
||||||
|
|
||||||
use std::net::{IpAddr, Ipv4Addr, SocketAddr, UdpSocket};
|
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let server_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 7301);
|
|
||||||
let socket = UdpSocket::bind(&server_addr.clone()).expect("Error opening UDP socket");
|
|
||||||
let mut recv_buf = [0; 1024];
|
|
||||||
let jh = thread::spawn(move || {
|
|
||||||
let dummy_data = [1, 2, 3, 4];
|
|
||||||
let client = UdpSocket::bind("127.0.0.1:7300").expect("Connecting to UDP server failed");
|
|
||||||
client
|
|
||||||
.send_to(&dummy_data, &server_addr)
|
|
||||||
.expect(&*format!("Sending to {:?} failed", server_addr));
|
|
||||||
});
|
|
||||||
let (num_bytes, src) = socket.recv_from(&mut recv_buf).expect("UDP Receive error");
|
|
||||||
println!(
|
|
||||||
"Received {num_bytes} bytes from {src}: {:x?}",
|
|
||||||
&recv_buf[0..num_bytes]
|
|
||||||
);
|
|
||||||
jh.join().expect("Joining thread failed");
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
use postcard::{from_bytes, to_stdvec};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use zerocopy::byteorder::{I32, U16};
|
|
||||||
use zerocopy::{AsBytes, FromBytes, NetworkEndian, Unaligned};
|
|
||||||
|
|
||||||
#[derive(AsBytes, FromBytes, Unaligned, Debug, Eq, PartialEq)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
struct ZeroCopyTest {
|
|
||||||
some_bool: u8,
|
|
||||||
some_u16: U16<NetworkEndian>,
|
|
||||||
some_i32: I32<NetworkEndian>,
|
|
||||||
some_float: [u8; 4],
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
|
||||||
struct PostcardTest {
|
|
||||||
some_bool: u8,
|
|
||||||
some_u16: u16,
|
|
||||||
some_i32: i32,
|
|
||||||
some_float: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
|
||||||
struct SliceSerTest<'slice> {
|
|
||||||
some_u8: u8,
|
|
||||||
some_u32: u32,
|
|
||||||
some_slice: &'slice [u8],
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let pc_test = PostcardTest {
|
|
||||||
some_bool: true as u8,
|
|
||||||
some_u16: 0x42,
|
|
||||||
some_i32: -200,
|
|
||||||
some_float: 7.7_f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
let out = to_stdvec(&pc_test).unwrap();
|
|
||||||
println!("{:#04x?}", out);
|
|
||||||
|
|
||||||
let sample_hk = ZeroCopyTest {
|
|
||||||
some_bool: true as u8,
|
|
||||||
some_u16: U16::from(0x42),
|
|
||||||
some_i32: I32::from(-200),
|
|
||||||
some_float: 7.7_f32.to_be_bytes(),
|
|
||||||
};
|
|
||||||
let mut slice = [0; 11];
|
|
||||||
sample_hk.write_to(slice.as_mut_slice());
|
|
||||||
println!("{:#04x?}", slice);
|
|
||||||
|
|
||||||
let ser_vec;
|
|
||||||
{
|
|
||||||
let test_buf = [0, 1, 2, 3];
|
|
||||||
let test_with_slice = SliceSerTest {
|
|
||||||
some_u8: 12,
|
|
||||||
some_u32: 1,
|
|
||||||
some_slice: test_buf.as_slice(),
|
|
||||||
};
|
|
||||||
ser_vec = to_stdvec(&test_with_slice).unwrap();
|
|
||||||
println!("{:#04x?}", out);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let test_deser: SliceSerTest = from_bytes(ser_vec.as_slice()).unwrap();
|
|
||||||
println!("{:?}", test_deser);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
use std::any::Any;
|
|
||||||
|
|
||||||
/// This trait encapsulates being able to cast a trait object to its original concrete type
|
|
||||||
/// TODO: Add example code and maybe write derive macro because this code is always the same
|
|
||||||
pub trait AsAny {
|
|
||||||
fn as_any(&self) -> &dyn Any;
|
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any;
|
|
||||||
}
|
|
@ -35,7 +35,7 @@ impl FsrcErrorRaw {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy, Default)]
|
||||||
pub struct SimpleStdErrorHandler {}
|
pub struct SimpleStdErrorHandler {}
|
||||||
|
|
||||||
#[cfg(feature = "use_std")]
|
#[cfg(feature = "use_std")]
|
||||||
|
@ -5,7 +5,7 @@ pub type GroupId = u16;
|
|||||||
pub type UniqueId = u16;
|
pub type UniqueId = u16;
|
||||||
pub type EventRaw = u32;
|
pub type EventRaw = u32;
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Severity {
|
pub enum Severity {
|
||||||
INFO = 1,
|
INFO = 1,
|
||||||
LOW = 2,
|
LOW = 2,
|
||||||
@ -27,7 +27,7 @@ impl TryFrom<u8> for Severity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct Event {
|
pub struct Event {
|
||||||
severity: Severity,
|
severity: Severity,
|
||||||
group_id: GroupId,
|
group_id: GroupId,
|
||||||
|
@ -6,7 +6,7 @@ use std::thread;
|
|||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum OpResult {
|
pub enum OpResult {
|
||||||
Ok,
|
Ok,
|
||||||
TerminationRequested,
|
TerminationRequested,
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
|
use crate::hal::host::udp_server::ReceiveResult::{IoError, ReceiverError};
|
||||||
use crate::tmtc::ReceivesTc;
|
use crate::tmtc::ReceivesTc;
|
||||||
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
||||||
use std::vec::Vec;
|
use std::vec::Vec;
|
||||||
|
|
||||||
pub struct UdpTmtcServer {
|
pub struct UdpTmtcServer<E> {
|
||||||
socket: UdpSocket,
|
socket: UdpSocket,
|
||||||
recv_buf: Vec<u8>,
|
recv_buf: Vec<u8>,
|
||||||
tc_receiver: Box<dyn ReceivesTc>,
|
tc_receiver: Box<dyn ReceivesTc<Error = E>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UdpTmtcServer {
|
pub enum ReceiveResult<E> {
|
||||||
pub fn new<A: ToSocketAddrs, E>(
|
IoError(std::io::Error),
|
||||||
|
ReceiverError(E),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E> UdpTmtcServer<E> {
|
||||||
|
pub fn new<A: ToSocketAddrs>(
|
||||||
addr: A,
|
addr: A,
|
||||||
max_recv_size: usize,
|
max_recv_size: usize,
|
||||||
tc_receiver: Box<dyn ReceivesTc>,
|
tc_receiver: Box<dyn ReceivesTc<Error = E>>,
|
||||||
) -> Result<Self, std::io::Error> {
|
) -> Result<Self, std::io::Error> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
socket: UdpSocket::bind(addr)?,
|
socket: UdpSocket::bind(addr)?,
|
||||||
@ -21,9 +27,14 @@ impl UdpTmtcServer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recv_tc(&mut self) -> Result<(usize, SocketAddr), std::io::Error> {
|
pub fn recv_tc(&mut self) -> Result<(usize, SocketAddr), ReceiveResult<E>> {
|
||||||
let res = self.socket.recv_from(&mut self.recv_buf)?;
|
let res = self
|
||||||
self.tc_receiver.pass_tc(&self.recv_buf[0..res.0]);
|
.socket
|
||||||
|
.recv_from(&mut self.recv_buf)
|
||||||
|
.map_err(|e| IoError(e))?;
|
||||||
|
self.tc_receiver
|
||||||
|
.pass_tc(&self.recv_buf[0..res.0])
|
||||||
|
.map_err(|e| ReceiverError(e))?;
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
//! # Core components of the Flight Software Rust Crate (FSRC) collection
|
//! # Core components of the Flight Software Rust Crate (FSRC) collection
|
||||||
pub mod any;
|
//!
|
||||||
|
//! This is a collection of Rust crates which can be used to build On-Board Software for remote
|
||||||
|
//! systems like satellites of rovers. It has special support for space tailored protocols
|
||||||
|
//! like the ones provided by CCSDS and ECSS.
|
||||||
|
//!
|
||||||
|
//! The crates can generally be used in a `no_std` environment, but some crates expect that the
|
||||||
|
//! [alloc](https://doc.rust-lang.org/alloc) crate is available to allow boxed trait objects.
|
||||||
|
//! These are used to supply user code to the crates.
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod event_man;
|
pub mod event_man;
|
||||||
pub mod events;
|
pub mod events;
|
||||||
@ -8,3 +15,5 @@ pub mod hal;
|
|||||||
pub mod objects;
|
pub mod objects;
|
||||||
pub mod pool;
|
pub mod pool;
|
||||||
pub mod tmtc;
|
pub mod tmtc;
|
||||||
|
|
||||||
|
extern crate downcast_rs;
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
//!
|
//!
|
||||||
//! # Examples
|
//! # Examples
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```rust
|
||||||
//! use std::any::Any;
|
//! use std::any::Any;
|
||||||
//! use std::error::Error;
|
//! use std::error::Error;
|
||||||
//! use fsrc_core::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
|
//! use fsrc_core::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
|
||||||
@ -29,9 +29,6 @@
|
|||||||
//! }
|
//! }
|
||||||
//!
|
//!
|
||||||
//! impl SystemObject for ExampleSysObj {
|
//! impl SystemObject for ExampleSysObj {
|
||||||
//! fn as_any(&self) -> &dyn Any {
|
|
||||||
//! self
|
|
||||||
//! }
|
|
||||||
//!
|
//!
|
||||||
//! fn get_object_id(&self) -> &ObjectId {
|
//! fn get_object_id(&self) -> &ObjectId {
|
||||||
//! &self.id
|
//! &self.id
|
||||||
@ -45,18 +42,17 @@
|
|||||||
//!
|
//!
|
||||||
//! impl ManagedSystemObject for ExampleSysObj {}
|
//! impl ManagedSystemObject for ExampleSysObj {}
|
||||||
//!
|
//!
|
||||||
//!
|
|
||||||
//! let mut obj_manager = ObjectManager::default();
|
//! let mut obj_manager = ObjectManager::default();
|
||||||
//! let obj_id = ObjectId { id: 0, name: "Example 0"};
|
//! let obj_id = ObjectId { id: 0, name: "Example 0"};
|
||||||
//! let example_obj = ExampleSysObj::new(obj_id, 42);
|
//! let example_obj = ExampleSysObj::new(obj_id, 42);
|
||||||
//! obj_manager.insert(Box::new(example_obj));
|
//! obj_manager.insert(Box::new(example_obj));
|
||||||
//! let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get(&obj_id);
|
//! let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get_ref(&obj_id);
|
||||||
//! let example_obj = obj_back_casted.unwrap();
|
//! let example_obj = obj_back_casted.unwrap();
|
||||||
//! assert_eq!(example_obj.id, obj_id);
|
//! assert_eq!(example_obj.id, obj_id);
|
||||||
//! assert_eq!(example_obj.dummy, 42);
|
//! assert_eq!(example_obj.dummy, 42);
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use std::any::Any;
|
use downcast_rs::Downcast;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
@ -68,13 +64,14 @@ pub struct ObjectId {
|
|||||||
|
|
||||||
/// Each object which is stored inside the [object manager][ObjectManager] needs to implemented
|
/// Each object which is stored inside the [object manager][ObjectManager] needs to implemented
|
||||||
/// this trait
|
/// this trait
|
||||||
pub trait SystemObject {
|
pub trait SystemObject: Downcast {
|
||||||
fn as_any(&self) -> &dyn Any;
|
|
||||||
fn get_object_id(&self) -> &ObjectId;
|
fn get_object_id(&self) -> &ObjectId;
|
||||||
fn initialize(&mut self) -> Result<(), Box<dyn Error>>;
|
fn initialize(&mut self) -> Result<(), Box<dyn Error>>;
|
||||||
}
|
}
|
||||||
|
downcast_rs::impl_downcast!(SystemObject);
|
||||||
|
|
||||||
pub trait ManagedSystemObject: SystemObject + Any + Send {}
|
pub trait ManagedSystemObject: SystemObject + Send {}
|
||||||
|
downcast_rs::impl_downcast!(ManagedSystemObject);
|
||||||
|
|
||||||
/// Helper module to manage multiple [ManagedSystemObjects][ManagedSystemObject] by mapping them
|
/// Helper module to manage multiple [ManagedSystemObjects][ManagedSystemObject] by mapping them
|
||||||
/// using an [object ID][ObjectId]
|
/// using an [object ID][ObjectId]
|
||||||
@ -114,19 +111,26 @@ impl ObjectManager {
|
|||||||
Ok(init_success)
|
Ok(init_success)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve an object stored inside the manager. The type to retrieve needs to be explicitly
|
/// Retrieve a reference to an object stored inside the manager. The type to retrieve needs to
|
||||||
/// passed as a generic parameter
|
/// be explicitly passed as a generic parameter or specified on the left hand side of the
|
||||||
pub fn get<T: Any>(&self, key: &ObjectId) -> Option<&T> {
|
/// expression.
|
||||||
|
pub fn get_ref<T: ManagedSystemObject>(&self, key: &ObjectId) -> Option<&T> {
|
||||||
|
self.obj_map.get(key).and_then(|o| o.downcast_ref::<T>())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve a mutable reference to an object stored inside the manager. The type to retrieve
|
||||||
|
/// needs to be explicitly passed as a generic parameter or specified on the left hand side
|
||||||
|
/// of the expression.
|
||||||
|
pub fn get_mut<T: ManagedSystemObject>(&mut self, key: &ObjectId) -> Option<&mut T> {
|
||||||
self.obj_map
|
self.obj_map
|
||||||
.get(key)
|
.get_mut(key)
|
||||||
.and_then(|o| o.as_ref().as_any().downcast_ref::<T>())
|
.and_then(|o| o.downcast_mut::<T>())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
|
use crate::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
|
||||||
use std::any::Any;
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
@ -148,10 +152,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SystemObject for ExampleSysObj {
|
impl SystemObject for ExampleSysObj {
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_object_id(&self) -> &ObjectId {
|
fn get_object_id(&self) -> &ObjectId {
|
||||||
&self.id
|
&self.id
|
||||||
}
|
}
|
||||||
@ -171,10 +171,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SystemObject for OtherExampleObject {
|
impl SystemObject for OtherExampleObject {
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_object_id(&self) -> &ObjectId {
|
fn get_object_id(&self) -> &ObjectId {
|
||||||
&self.id
|
&self.id
|
||||||
}
|
}
|
||||||
@ -199,7 +195,7 @@ mod tests {
|
|||||||
let res = obj_manager.initialize();
|
let res = obj_manager.initialize();
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
assert_eq!(res.unwrap(), 1);
|
assert_eq!(res.unwrap(), 1);
|
||||||
let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get(&expl_obj_id);
|
let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get_ref(&expl_obj_id);
|
||||||
assert!(obj_back_casted.is_some());
|
assert!(obj_back_casted.is_some());
|
||||||
let expl_obj_back_casted = obj_back_casted.unwrap();
|
let expl_obj_back_casted = obj_back_casted.unwrap();
|
||||||
assert_eq!(expl_obj_back_casted.dummy, 42);
|
assert_eq!(expl_obj_back_casted.dummy, 42);
|
||||||
@ -219,7 +215,7 @@ mod tests {
|
|||||||
let res = obj_manager.initialize();
|
let res = obj_manager.initialize();
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
assert_eq!(res.unwrap(), 2);
|
assert_eq!(res.unwrap(), 2);
|
||||||
let obj_back_casted: Option<&OtherExampleObject> = obj_manager.get(&second_obj_id);
|
let obj_back_casted: Option<&OtherExampleObject> = obj_manager.get_ref(&second_obj_id);
|
||||||
assert!(obj_back_casted.is_some());
|
assert!(obj_back_casted.is_some());
|
||||||
let expl_obj_back_casted = obj_back_casted.unwrap();
|
let expl_obj_back_casted = obj_back_casted.unwrap();
|
||||||
assert_eq!(expl_obj_back_casted.string, String::from("Hello Test"));
|
assert_eq!(expl_obj_back_casted.string, String::from("Hello Test"));
|
||||||
@ -266,7 +262,7 @@ mod tests {
|
|||||||
let obj_man_0 = obj_manager.clone();
|
let obj_man_0 = obj_manager.clone();
|
||||||
let jh0 = thread::spawn(move || {
|
let jh0 = thread::spawn(move || {
|
||||||
let locked_man = obj_man_0.lock().expect("Mutex lock failed");
|
let locked_man = obj_man_0.lock().expect("Mutex lock failed");
|
||||||
let obj_back_casted: Option<&ExampleSysObj> = locked_man.get(&expl_obj_id);
|
let obj_back_casted: Option<&ExampleSysObj> = locked_man.get_ref(&expl_obj_id);
|
||||||
assert!(obj_back_casted.is_some());
|
assert!(obj_back_casted.is_some());
|
||||||
let expl_obj_back_casted = obj_back_casted.unwrap();
|
let expl_obj_back_casted = obj_back_casted.unwrap();
|
||||||
assert_eq!(expl_obj_back_casted.dummy, 42);
|
assert_eq!(expl_obj_back_casted.dummy, 42);
|
||||||
@ -276,7 +272,7 @@ mod tests {
|
|||||||
|
|
||||||
let jh1 = thread::spawn(move || {
|
let jh1 = thread::spawn(move || {
|
||||||
let locked_man = obj_manager.lock().expect("Mutex lock failed");
|
let locked_man = obj_manager.lock().expect("Mutex lock failed");
|
||||||
let obj_back_casted: Option<&OtherExampleObject> = locked_man.get(&second_obj_id);
|
let obj_back_casted: Option<&OtherExampleObject> = locked_man.get_ref(&second_obj_id);
|
||||||
assert!(obj_back_casted.is_some());
|
assert!(obj_back_casted.is_some());
|
||||||
let expl_obj_back_casted = obj_back_casted.unwrap();
|
let expl_obj_back_casted = obj_back_casted.unwrap();
|
||||||
assert_eq!(expl_obj_back_casted.string, String::from("Hello Test"));
|
assert_eq!(expl_obj_back_casted.string, String::from("Hello Test"));
|
||||||
|
@ -110,7 +110,7 @@ pub struct LocalPool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Simple address type used for transactions with the local pool.
|
/// Simple address type used for transactions with the local pool.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub struct StoreAddr {
|
pub struct StoreAddr {
|
||||||
pool_idx: u16,
|
pool_idx: u16,
|
||||||
packet_idx: NumBlocks,
|
packet_idx: NumBlocks,
|
||||||
@ -124,13 +124,13 @@ impl StoreAddr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum StoreIdError {
|
pub enum StoreIdError {
|
||||||
InvalidSubpool(u16),
|
InvalidSubpool(u16),
|
||||||
InvalidPacketIdx(u16),
|
InvalidPacketIdx(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum StoreError {
|
pub enum StoreError {
|
||||||
/// Requested data block is too large
|
/// Requested data block is too large
|
||||||
DataTooLarge(usize),
|
DataTooLarge(usize),
|
||||||
|
@ -1,76 +1,201 @@
|
|||||||
use crate::any::AsAny;
|
//! CCSDS packet routing components.
|
||||||
use crate::error::FsrcErrorHandler;
|
//!
|
||||||
use crate::tmtc::{ReceivesTc, FROM_BYTES_SLICE_TOO_SMALL_ERROR, FROM_BYTES_ZEROCOPY_ERROR};
|
//! The routing components consist of two core components:
|
||||||
|
//! 1. [CcsdsDistributor] component which dispatches received packets to a user-provided handler
|
||||||
|
//! 2. [ApidPacketHandler] trait which should be implemented by the user-provided packet handler.
|
||||||
|
//!
|
||||||
|
//! The [CcsdsDistributor] implements the [ReceivesCcsdsTc] and [ReceivesTc] trait which allows to
|
||||||
|
//! pass raw or CCSDS packets to it. Upon receiving a packet, it performs the following steps:
|
||||||
|
//!
|
||||||
|
//! 1. It tries to identify the target Application Process Identifier (APID) based on the
|
||||||
|
//! respective CCSDS space packet header field. If that process fails, a [PacketError] is
|
||||||
|
//! returned to the user
|
||||||
|
//! 2. If a valid APID is found and matches one of the APIDs provided by
|
||||||
|
//! [ApidPacketHandler::valid_apids], it will pass the packet to the user provided
|
||||||
|
//! [ApidPacketHandler::handle_known_apid] function. If no valid APID is found, the packet
|
||||||
|
//! will be passed to the [ApidPacketHandler::handle_unknown_apid] function.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use fsrc_core::tmtc::ccsds_distrib::{ApidPacketHandler, CcsdsDistributor};
|
||||||
|
//! use fsrc_core::tmtc::ReceivesTc;
|
||||||
|
//! use spacepackets::{CcsdsPacket, SpHeader};
|
||||||
|
//! use spacepackets::tc::PusTc;
|
||||||
|
//!
|
||||||
|
//! #[derive (Default)]
|
||||||
|
//! struct ConcreteApidHandler {
|
||||||
|
//! known_call_count: u32,
|
||||||
|
//! unknown_call_count: u32
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl ConcreteApidHandler {
|
||||||
|
//! fn mutable_foo(&mut self) {}
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! impl ApidPacketHandler for ConcreteApidHandler {
|
||||||
|
//! type Error = ();
|
||||||
|
//! fn valid_apids(&self) -> &'static [u16] { &[0x002] }
|
||||||
|
//! fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
//! assert_eq!(sp_header.apid(), 0x002);
|
||||||
|
//! assert_eq!(tc_raw.len(), 13);
|
||||||
|
//! self.known_call_count += 1;
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! fn handle_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
//! assert_eq!(sp_header.apid(), 0x003);
|
||||||
|
//! assert_eq!(tc_raw.len(), 13);
|
||||||
|
//! self.unknown_call_count += 1;
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! let apid_handler = ConcreteApidHandler::default();
|
||||||
|
//! let mut ccsds_distributor = CcsdsDistributor::new(Box::new(apid_handler));
|
||||||
|
//!
|
||||||
|
//! // Create and pass PUS telecommand with a valid APID
|
||||||
|
//! let mut space_packet_header = SpHeader::tc(0x002, 0x34, 0).unwrap();
|
||||||
|
//! let mut pus_tc = PusTc::new_simple(&mut space_packet_header, 17, 1, None, true);
|
||||||
|
//! let mut test_buf: [u8; 32] = [0; 32];
|
||||||
|
//! let mut size = pus_tc
|
||||||
|
//! .write_to(test_buf.as_mut_slice())
|
||||||
|
//! .expect("Error writing TC to buffer");
|
||||||
|
//! let tc_slice = &test_buf[0..size];
|
||||||
|
//! ccsds_distributor.pass_tc(&tc_slice).expect("Passing TC slice failed");
|
||||||
|
//!
|
||||||
|
//! // Now pass a packet with an unknown APID to the distributor
|
||||||
|
//! pus_tc.set_apid(0x003);
|
||||||
|
//! size = pus_tc
|
||||||
|
//! .write_to(test_buf.as_mut_slice())
|
||||||
|
//! .expect("Error writing TC to buffer");
|
||||||
|
//! let tc_slice = &test_buf[0..size];
|
||||||
|
//! ccsds_distributor.pass_tc(&tc_slice).expect("Passing TC slice failed");
|
||||||
|
//!
|
||||||
|
//! // User helper function to retrieve concrete class
|
||||||
|
//! let concrete_handler_ref: &ConcreteApidHandler = ccsds_distributor
|
||||||
|
//! .apid_handler_ref()
|
||||||
|
//! .expect("Casting back to concrete type failed");
|
||||||
|
//! assert_eq!(concrete_handler_ref.known_call_count, 1);
|
||||||
|
//! assert_eq!(concrete_handler_ref.unknown_call_count, 1);
|
||||||
|
//!
|
||||||
|
//! // It's also possible to retrieve a mutable reference
|
||||||
|
//! let mutable_ref: &mut ConcreteApidHandler = ccsds_distributor
|
||||||
|
//! .apid_handler_mut()
|
||||||
|
//! .expect("Casting back to concrete type failed");
|
||||||
|
//! mutable_ref.mutable_foo();
|
||||||
|
//! ```
|
||||||
|
use crate::tmtc::{ReceivesCcsdsTc, ReceivesTc};
|
||||||
|
use downcast_rs::Downcast;
|
||||||
use spacepackets::{CcsdsPacket, PacketError, SpHeader};
|
use spacepackets::{CcsdsPacket, PacketError, SpHeader};
|
||||||
|
|
||||||
pub trait ApidPacketHandler: AsAny {
|
/// Generic trait for a handler or dispatcher object handling CCSDS packets.
|
||||||
|
///
|
||||||
|
/// Users should implement this trait on their custom CCSDS packet handler and then pass a boxed
|
||||||
|
/// instance of this handler to the [CcsdsDistributor]. The distributor will use the trait
|
||||||
|
/// interface to dispatch received packets to the user based on the Application Process Identifier
|
||||||
|
/// (APID) field of the CCSDS packet.
|
||||||
|
///
|
||||||
|
/// This trait automatically implements the [downcast_rs::Downcast] to allow a more convenient API
|
||||||
|
/// to cast trait objects back to their concrete type after the handler was passed to the
|
||||||
|
/// distributor.
|
||||||
|
pub trait ApidPacketHandler: Downcast {
|
||||||
|
type Error;
|
||||||
|
|
||||||
fn valid_apids(&self) -> &'static [u16];
|
fn valid_apids(&self) -> &'static [u16];
|
||||||
fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]);
|
fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8])
|
||||||
fn handle_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]);
|
-> Result<(), Self::Error>;
|
||||||
|
fn handle_unknown_apid(
|
||||||
|
&mut self,
|
||||||
|
sp_header: &SpHeader,
|
||||||
|
tc_raw: &[u8],
|
||||||
|
) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CcsdsDistributor {
|
downcast_rs::impl_downcast!(ApidPacketHandler assoc Error);
|
||||||
pub apid_handler: Box<dyn ApidPacketHandler>,
|
|
||||||
error_handler: Box<dyn FsrcErrorHandler>,
|
/// The CCSDS distributor dispatches received CCSDS packets to a user provided packet handler.
|
||||||
|
pub struct CcsdsDistributor<E> {
|
||||||
|
/// User provided APID handler stored as a generic trait object.
|
||||||
|
/// It can be cast back to the original concrete type using the [Self::apid_handler_ref] or
|
||||||
|
/// the [Self::apid_handler_mut] method.
|
||||||
|
pub apid_handler: Box<dyn ApidPacketHandler<Error = E>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CcsdsDistributor {
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
pub fn new(
|
pub enum CcsdsError<E> {
|
||||||
apid_handler: Box<dyn ApidPacketHandler>,
|
CustomError(E),
|
||||||
error_handler: Box<dyn FsrcErrorHandler>,
|
PacketError(PacketError),
|
||||||
) -> Self {
|
}
|
||||||
CcsdsDistributor {
|
|
||||||
apid_handler,
|
impl<E: 'static> ReceivesCcsdsTc for CcsdsDistributor<E> {
|
||||||
error_handler,
|
type Error = CcsdsError<E>;
|
||||||
}
|
|
||||||
|
fn pass_ccsds(&mut self, header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
|
self.dispatch_ccsds(header, tc_raw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReceivesTc for CcsdsDistributor {
|
impl<E: 'static> ReceivesTc for CcsdsDistributor<E> {
|
||||||
fn pass_tc(&mut self, tm_raw: &[u8]) {
|
type Error = CcsdsError<E>;
|
||||||
let sp_header = match SpHeader::from_raw_slice(tm_raw) {
|
|
||||||
Ok(header) => header,
|
fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
Err(e) => {
|
let sp_header = SpHeader::from_raw_slice(tc_raw).map_err(|e| CcsdsError::PacketError(e))?;
|
||||||
match e {
|
self.dispatch_ccsds(&sp_header, tc_raw)
|
||||||
PacketError::FromBytesSliceTooSmall(missmatch) => {
|
|
||||||
self.error_handler.error_with_two_params(
|
|
||||||
FROM_BYTES_SLICE_TOO_SMALL_ERROR,
|
|
||||||
missmatch.found as u32,
|
|
||||||
missmatch.expected as u32,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
PacketError::FromBytesZeroCopyError => {
|
}
|
||||||
self.error_handler.error(FROM_BYTES_ZEROCOPY_ERROR);
|
|
||||||
|
impl<E: 'static> CcsdsDistributor<E> {
|
||||||
|
pub fn new(apid_handler: Box<dyn ApidPacketHandler<Error = E>>) -> Self {
|
||||||
|
CcsdsDistributor { apid_handler }
|
||||||
}
|
}
|
||||||
_ => {
|
|
||||||
// TODO: Unexpected error
|
/// This function can be used to retrieve a reference to the concrete instance of the APID
|
||||||
|
/// handler after it was passed to the distributor. See the
|
||||||
|
/// [module documentation][crate::tmtc::ccsds_distrib] for an fsrc-example.
|
||||||
|
pub fn apid_handler_ref<T: ApidPacketHandler<Error = E>>(&self) -> Option<&T> {
|
||||||
|
self.apid_handler.downcast_ref::<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This function can be used to retrieve a mutable reference to the concrete instance of the
|
||||||
|
/// APID handler after it was passed to the distributor.
|
||||||
|
pub fn apid_handler_mut<T: ApidPacketHandler<Error = E>>(&mut self) -> Option<&mut T> {
|
||||||
|
self.apid_handler.downcast_mut::<T>()
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
fn dispatch_ccsds(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) -> Result<(), CcsdsError<E>> {
|
||||||
};
|
|
||||||
let apid = sp_header.apid();
|
let apid = sp_header.apid();
|
||||||
let valid_apids = self.apid_handler.valid_apids();
|
let valid_apids = self.apid_handler.valid_apids();
|
||||||
for &valid_apid in valid_apids {
|
for &valid_apid in valid_apids {
|
||||||
if valid_apid == apid {
|
if valid_apid == apid {
|
||||||
return self.apid_handler.handle_known_apid(&sp_header, tm_raw);
|
return self
|
||||||
|
.apid_handler
|
||||||
|
.handle_known_apid(sp_header, tc_raw)
|
||||||
|
.map_err(|e| CcsdsError::CustomError(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.apid_handler.handle_unknown_apid(&sp_header, tm_raw);
|
self.apid_handler
|
||||||
|
.handle_unknown_apid(sp_header, tc_raw)
|
||||||
|
.map_err(|e| CcsdsError::CustomError(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::error::SimpleStdErrorHandler;
|
|
||||||
use crate::tmtc::ccsds_distrib::{ApidPacketHandler, CcsdsDistributor};
|
use crate::tmtc::ccsds_distrib::{ApidPacketHandler, CcsdsDistributor};
|
||||||
use spacepackets::tc::PusTc;
|
use spacepackets::tc::PusTc;
|
||||||
use spacepackets::CcsdsPacket;
|
use spacepackets::CcsdsPacket;
|
||||||
use std::any::Any;
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
pub fn generate_ping_tc(buf: &mut [u8]) -> &[u8] {
|
||||||
|
let mut sph = SpHeader::tc(0x002, 0x34, 0).unwrap();
|
||||||
|
let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true);
|
||||||
|
let size = pus_tc.write_to(buf).expect("Error writing TC to buffer");
|
||||||
|
assert_eq!(size, 13);
|
||||||
|
&buf[0..size]
|
||||||
|
}
|
||||||
|
|
||||||
pub struct BasicApidHandlerSharedQueue {
|
pub struct BasicApidHandlerSharedQueue {
|
||||||
pub known_packet_queue: Arc<Mutex<VecDeque<(u16, Vec<u8>)>>>,
|
pub known_packet_queue: Arc<Mutex<VecDeque<(u16, Vec<u8>)>>>,
|
||||||
pub unknown_packet_queue: Arc<Mutex<VecDeque<(u16, Vec<u8>)>>>,
|
pub unknown_packet_queue: Arc<Mutex<VecDeque<(u16, Vec<u8>)>>>,
|
||||||
@ -82,65 +207,66 @@ pub(crate) mod tests {
|
|||||||
pub unknown_packet_queue: VecDeque<(u16, Vec<u8>)>,
|
pub unknown_packet_queue: VecDeque<(u16, Vec<u8>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsAny for BasicApidHandlerSharedQueue {
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApidPacketHandler for BasicApidHandlerSharedQueue {
|
impl ApidPacketHandler for BasicApidHandlerSharedQueue {
|
||||||
|
type Error = ();
|
||||||
fn valid_apids(&self) -> &'static [u16] {
|
fn valid_apids(&self) -> &'static [u16] {
|
||||||
&[0x000, 0x002]
|
&[0x000, 0x002]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) {
|
fn handle_known_apid(
|
||||||
|
&mut self,
|
||||||
|
sp_header: &SpHeader,
|
||||||
|
tc_raw: &[u8],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
vec.extend_from_slice(tc_raw);
|
vec.extend_from_slice(tc_raw);
|
||||||
self.known_packet_queue
|
Ok(self
|
||||||
|
.known_packet_queue
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.push_back((sp_header.apid(), vec));
|
.push_back((sp_header.apid(), vec)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) {
|
fn handle_unknown_apid(
|
||||||
|
&mut self,
|
||||||
|
sp_header: &SpHeader,
|
||||||
|
tc_raw: &[u8],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
vec.extend_from_slice(tc_raw);
|
vec.extend_from_slice(tc_raw);
|
||||||
self.unknown_packet_queue
|
Ok(self
|
||||||
|
.unknown_packet_queue
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.push_back((sp_header.apid(), vec));
|
.push_back((sp_header.apid(), vec)))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsAny for BasicApidHandlerOwnedQueue {
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApidPacketHandler for BasicApidHandlerOwnedQueue {
|
impl ApidPacketHandler for BasicApidHandlerOwnedQueue {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
fn valid_apids(&self) -> &'static [u16] {
|
fn valid_apids(&self) -> &'static [u16] {
|
||||||
&[0x000, 0x002]
|
&[0x000, 0x002]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) {
|
fn handle_known_apid(
|
||||||
|
&mut self,
|
||||||
|
sp_header: &SpHeader,
|
||||||
|
tc_raw: &[u8],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
vec.extend_from_slice(tc_raw);
|
vec.extend_from_slice(tc_raw);
|
||||||
self.known_packet_queue.push_back((sp_header.apid(), vec));
|
Ok(self.known_packet_queue.push_back((sp_header.apid(), vec)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) {
|
fn handle_unknown_apid(
|
||||||
|
&mut self,
|
||||||
|
sp_header: &SpHeader,
|
||||||
|
tc_raw: &[u8],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
vec.extend_from_slice(tc_raw);
|
vec.extend_from_slice(tc_raw);
|
||||||
self.unknown_packet_queue.push_back((sp_header.apid(), vec));
|
Ok(self.unknown_packet_queue.push_back((sp_header.apid(), vec)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,22 +278,17 @@ pub(crate) mod tests {
|
|||||||
known_packet_queue: known_packet_queue.clone(),
|
known_packet_queue: known_packet_queue.clone(),
|
||||||
unknown_packet_queue: unknown_packet_queue.clone(),
|
unknown_packet_queue: unknown_packet_queue.clone(),
|
||||||
};
|
};
|
||||||
let error_handler = SimpleStdErrorHandler {};
|
let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler));
|
||||||
let mut ccsds_distrib =
|
|
||||||
CcsdsDistributor::new(Box::new(apid_handler), Box::new(error_handler));
|
|
||||||
let mut sph = SpHeader::tc(0x002, 0x34, 0).unwrap();
|
|
||||||
let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true);
|
|
||||||
let mut test_buf: [u8; 32] = [0; 32];
|
let mut test_buf: [u8; 32] = [0; 32];
|
||||||
pus_tc
|
let tc_slice = generate_ping_tc(test_buf.as_mut_slice());
|
||||||
.write_to(test_buf.as_mut_slice())
|
|
||||||
.expect("Error writing TC to buffer");
|
ccsds_distrib.pass_tc(tc_slice).expect("Passing TC failed");
|
||||||
ccsds_distrib.pass_tc(&test_buf);
|
|
||||||
let recvd = known_packet_queue.lock().unwrap().pop_front();
|
let recvd = known_packet_queue.lock().unwrap().pop_front();
|
||||||
assert!(unknown_packet_queue.lock().unwrap().is_empty());
|
assert!(unknown_packet_queue.lock().unwrap().is_empty());
|
||||||
assert!(recvd.is_some());
|
assert!(recvd.is_some());
|
||||||
let (apid, packet) = recvd.unwrap();
|
let (apid, packet) = recvd.unwrap();
|
||||||
assert_eq!(apid, 0x002);
|
assert_eq!(apid, 0x002);
|
||||||
assert_eq!(packet.as_slice(), test_buf);
|
assert_eq!(packet, tc_slice);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -178,16 +299,14 @@ pub(crate) mod tests {
|
|||||||
known_packet_queue: known_packet_queue.clone(),
|
known_packet_queue: known_packet_queue.clone(),
|
||||||
unknown_packet_queue: unknown_packet_queue.clone(),
|
unknown_packet_queue: unknown_packet_queue.clone(),
|
||||||
};
|
};
|
||||||
let error_handler = SimpleStdErrorHandler {};
|
let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler));
|
||||||
let mut ccsds_distrib =
|
|
||||||
CcsdsDistributor::new(Box::new(apid_handler), Box::new(error_handler));
|
|
||||||
let mut sph = SpHeader::tc(0x004, 0x34, 0).unwrap();
|
let mut sph = SpHeader::tc(0x004, 0x34, 0).unwrap();
|
||||||
let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true);
|
let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true);
|
||||||
let mut test_buf: [u8; 32] = [0; 32];
|
let mut test_buf: [u8; 32] = [0; 32];
|
||||||
pus_tc
|
pus_tc
|
||||||
.write_to(test_buf.as_mut_slice())
|
.write_to(test_buf.as_mut_slice())
|
||||||
.expect("Error writing TC to buffer");
|
.expect("Error writing TC to buffer");
|
||||||
ccsds_distrib.pass_tc(&test_buf);
|
ccsds_distrib.pass_tc(&test_buf).expect("Passing TC failed");
|
||||||
let recvd = unknown_packet_queue.lock().unwrap().pop_front();
|
let recvd = unknown_packet_queue.lock().unwrap().pop_front();
|
||||||
assert!(known_packet_queue.lock().unwrap().is_empty());
|
assert!(known_packet_queue.lock().unwrap().is_empty());
|
||||||
assert!(recvd.is_some());
|
assert!(recvd.is_some());
|
||||||
|
@ -1,32 +1,62 @@
|
|||||||
|
//! Telemetry and Telecommanding (TMTC) module. Contains packet routing components with special
|
||||||
|
//! support for CCSDS and ECSS packets.
|
||||||
|
//!
|
||||||
|
//! The distributor modules provided by this module use trait objects provided by the user to
|
||||||
|
//! directly dispatch received packets to packet listeners based on packet fields like the CCSDS
|
||||||
|
//! 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 crate::error::{FsrcErrorRaw, FsrcGroupIds};
|
||||||
use spacepackets::{PacketError, SpHeader};
|
use spacepackets::tc::PusTc;
|
||||||
|
use spacepackets::SpHeader;
|
||||||
|
|
||||||
pub mod ccsds_distrib;
|
pub mod ccsds_distrib;
|
||||||
pub mod pus_distrib;
|
pub mod pus_distrib;
|
||||||
|
|
||||||
const RAW_PACKET_ERROR: &str = "raw-tmtc";
|
const _RAW_PACKET_ERROR: &str = "raw-tmtc";
|
||||||
const _CCSDS_ERROR: &str = "ccsds-tmtc";
|
const _CCSDS_ERROR: &str = "ccsds-tmtc";
|
||||||
const _PUS_ERROR: &str = "pus-tmtc";
|
const _PUS_ERROR: &str = "pus-tmtc";
|
||||||
|
|
||||||
// TODO: A macro for general and unknown errors would be nice
|
// TODO: A macro for general and unknown errors would be nice
|
||||||
const FROM_BYTES_SLICE_TOO_SMALL_ERROR: FsrcErrorRaw = FsrcErrorRaw::new(
|
const _FROM_BYTES_SLICE_TOO_SMALL_ERROR: FsrcErrorRaw = FsrcErrorRaw::new(
|
||||||
FsrcGroupIds::Tmtc as u8,
|
FsrcGroupIds::Tmtc as u8,
|
||||||
0,
|
0,
|
||||||
RAW_PACKET_ERROR,
|
_RAW_PACKET_ERROR,
|
||||||
"FROM_BYTES_SLICE_TOO_SMALL_ERROR",
|
"FROM_BYTES_SLICE_TOO_SMALL_ERROR",
|
||||||
);
|
);
|
||||||
|
|
||||||
const FROM_BYTES_ZEROCOPY_ERROR: FsrcErrorRaw = FsrcErrorRaw::new(
|
const _FROM_BYTES_ZEROCOPY_ERROR: FsrcErrorRaw = FsrcErrorRaw::new(
|
||||||
FsrcGroupIds::Tmtc as u8,
|
FsrcGroupIds::Tmtc as u8,
|
||||||
1,
|
1,
|
||||||
RAW_PACKET_ERROR,
|
_RAW_PACKET_ERROR,
|
||||||
"FROM_BYTES_ZEROCOPY_ERROR",
|
"FROM_BYTES_ZEROCOPY_ERROR",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// Generic trait for object which can receive any telecommands in form of a raw bytestream, with
|
||||||
|
/// no assumptions about the received protocol.
|
||||||
|
///
|
||||||
|
/// 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 {
|
||||||
fn pass_tc(&mut self, tc_raw: &[u8]);
|
type Error;
|
||||||
|
fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generic trait for object which can receive CCSDS space packets, for fsrc-example ECSS PUS packets
|
||||||
|
/// for CCSDS File Delivery Protocol (CFDP) packets.
|
||||||
|
///
|
||||||
|
/// 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 or in CCSDS format into them.
|
||||||
pub trait ReceivesCcsdsTc {
|
pub trait ReceivesCcsdsTc {
|
||||||
fn pass_ccsds(&mut self, header: &SpHeader, tc_raw: &[u8]) -> Result<(), PacketError>;
|
type Error;
|
||||||
|
fn pass_ccsds(&mut self, header: &SpHeader, tc_raw: &[u8]) -> Result<(), Self::Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic trait for objects which can receive ECSS PUS telecommands. This trait is
|
||||||
|
/// implemented by the [crate::tmtc::pus_distrib::PusDistributor] objects to allow passing PUS TC
|
||||||
|
/// packets into it.
|
||||||
|
pub trait ReceivesEcssPusTc {
|
||||||
|
type Error;
|
||||||
|
fn pass_pus_tc(&mut self, header: &SpHeader, pus_tc: &PusTc) -> Result<(), Self::Error>;
|
||||||
}
|
}
|
||||||
|
@ -1,67 +1,146 @@
|
|||||||
use crate::any::AsAny;
|
//! ECSS PUS packet routing components.
|
||||||
use crate::error::FsrcErrorHandler;
|
//!
|
||||||
use crate::tmtc::{ReceivesCcsdsTc, ReceivesTc};
|
//! The routing components consist of two core components:
|
||||||
|
//! 1. [PusDistributor] component which dispatches received packets to a user-provided handler.
|
||||||
|
//! 2. [PusServiceProvider] trait which should be implemented by the user-provided PUS packet
|
||||||
|
//! handler.
|
||||||
|
//!
|
||||||
|
//! The [PusDistributor] implements the [ReceivesEcssPusTc], [ReceivesCcsdsTc] and the [ReceivesTc]
|
||||||
|
//! trait which allows to pass raw packets, CCSDS packets and PUS TC packets into it.
|
||||||
|
//! Upon receiving a packet, it performs the following steps:
|
||||||
|
//!
|
||||||
|
//! 1. It tries to extract the [SpHeader] and [PusTc] objects from the raw bytestream. If this
|
||||||
|
//! process fails, a [PusDistribError::PusError] is returned to the user.
|
||||||
|
//! 2. If it was possible to extract both components, the packet will be passed to the
|
||||||
|
//! [PusServiceProvider::handle_pus_tc_packet] method provided by the user.
|
||||||
|
//!
|
||||||
|
//! # Example
|
||||||
|
//!
|
||||||
|
//! ```rust
|
||||||
|
//! use fsrc_core::tmtc::pus_distrib::{PusDistributor, PusServiceProvider};
|
||||||
|
//! use fsrc_core::tmtc::ReceivesTc;
|
||||||
|
//! use spacepackets::SpHeader;
|
||||||
|
//! use spacepackets::tc::PusTc;
|
||||||
|
//! struct ConcretePusHandler {
|
||||||
|
//! handler_call_count: u32
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! // This is a very simple possible service provider. It increments an internal call count field,
|
||||||
|
//! // which is used to verify the handler was called
|
||||||
|
//! impl PusServiceProvider for ConcretePusHandler {
|
||||||
|
//! type Error = ();
|
||||||
|
//! fn handle_pus_tc_packet(&mut self, service: u8, header: &SpHeader, pus_tc: &PusTc) -> Result<(), Self::Error> {
|
||||||
|
//! assert_eq!(service, 17);
|
||||||
|
//! assert_eq!(pus_tc.len_packed(), 13);
|
||||||
|
//! self.handler_call_count += 1;
|
||||||
|
//! Ok(())
|
||||||
|
//! }
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! let service_handler = ConcretePusHandler {
|
||||||
|
//! handler_call_count: 0
|
||||||
|
//! };
|
||||||
|
//! let mut pus_distributor = PusDistributor::new(Box::new(service_handler));
|
||||||
|
//!
|
||||||
|
//! // Create and pass PUS ping telecommand with a valid APID
|
||||||
|
//! let mut space_packet_header = SpHeader::tc(0x002, 0x34, 0).unwrap();
|
||||||
|
//! let mut pus_tc = PusTc::new_simple(&mut space_packet_header, 17, 1, None, true);
|
||||||
|
//! let mut test_buf: [u8; 32] = [0; 32];
|
||||||
|
//! let mut size = pus_tc
|
||||||
|
//! .write_to(test_buf.as_mut_slice())
|
||||||
|
//! .expect("Error writing TC to buffer");
|
||||||
|
//! let tc_slice = &test_buf[0..size];
|
||||||
|
//!
|
||||||
|
//! pus_distributor.pass_tc(tc_slice).expect("Passing PUS telecommand failed");
|
||||||
|
//!
|
||||||
|
//! // User helper function to retrieve concrete class. We check the call count here to verify
|
||||||
|
//! // that the PUS ping telecommand was routed successfully.
|
||||||
|
//! let concrete_handler_ref: &ConcretePusHandler = pus_distributor
|
||||||
|
//! .service_provider_ref()
|
||||||
|
//! .expect("Casting back to concrete type failed");
|
||||||
|
//! assert_eq!(concrete_handler_ref.handler_call_count, 1);
|
||||||
|
//! ```
|
||||||
|
use crate::tmtc::{ReceivesCcsdsTc, ReceivesEcssPusTc, ReceivesTc};
|
||||||
|
use downcast_rs::Downcast;
|
||||||
use spacepackets::ecss::{PusError, PusPacket};
|
use spacepackets::ecss::{PusError, PusPacket};
|
||||||
use spacepackets::tc::PusTc;
|
use spacepackets::tc::PusTc;
|
||||||
use spacepackets::{CcsdsPacket, PacketError, SpHeader};
|
use spacepackets::SpHeader;
|
||||||
|
|
||||||
pub trait PusServiceProvider: AsAny {
|
pub trait PusServiceProvider: Downcast {
|
||||||
fn handle_pus_tc_packet(&mut self, service: u8, apid: u16, pus_tc: &PusTc);
|
type Error;
|
||||||
|
fn handle_pus_tc_packet(
|
||||||
|
&mut self,
|
||||||
|
service: u8,
|
||||||
|
header: &SpHeader,
|
||||||
|
pus_tc: &PusTc,
|
||||||
|
) -> Result<(), Self::Error>;
|
||||||
|
}
|
||||||
|
downcast_rs::impl_downcast!(PusServiceProvider assoc Error);
|
||||||
|
|
||||||
|
pub struct PusDistributor<E> {
|
||||||
|
pub service_provider: Box<dyn PusServiceProvider<Error = E>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PusDistributor {
|
impl<E> PusDistributor<E> {
|
||||||
pub service_provider: Box<dyn PusServiceProvider>,
|
pub fn new(service_provider: Box<dyn PusServiceProvider<Error = E>>) -> Self {
|
||||||
error_handler: Box<dyn FsrcErrorHandler>,
|
PusDistributor { service_provider }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReceivesTc for PusDistributor {
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
fn pass_tc(&mut self, tm_raw: &[u8]) {
|
pub enum PusDistribError<E> {
|
||||||
|
CustomError(E),
|
||||||
|
PusError(PusError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: 'static> ReceivesTc for PusDistributor<E> {
|
||||||
|
type Error = PusDistribError<E>;
|
||||||
|
fn pass_tc(&mut self, tm_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
// Convert to ccsds and call pass_ccsds
|
// Convert to ccsds and call pass_ccsds
|
||||||
match SpHeader::from_raw_slice(tm_raw) {
|
let sp_header = SpHeader::from_raw_slice(tm_raw)
|
||||||
Ok(sp_header) => {
|
.map_err(|e| PusDistribError::PusError(PusError::PacketError(e)))?;
|
||||||
self.pass_ccsds(&sp_header, tm_raw).unwrap();
|
self.pass_ccsds(&sp_header, tm_raw)
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
// TODO: Error handling
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ReceivesCcsdsTc for PusDistributor {
|
impl<E: 'static> ReceivesCcsdsTc for PusDistributor<E> {
|
||||||
fn pass_ccsds(&mut self, _header: &SpHeader, tm_raw: &[u8]) -> Result<(), PacketError> {
|
type Error = PusDistribError<E>;
|
||||||
// TODO: Better error handling
|
fn pass_ccsds(&mut self, header: &SpHeader, tm_raw: &[u8]) -> Result<(), Self::Error> {
|
||||||
let (tc, _) = match PusTc::new_from_raw_slice(tm_raw) {
|
let (tc, _) =
|
||||||
Ok(tuple) => tuple,
|
PusTc::new_from_raw_slice(tm_raw).map_err(|e| PusDistribError::PusError(e))?;
|
||||||
Err(e) => {
|
self.pass_pus_tc(header, &tc)
|
||||||
match e {
|
|
||||||
PusError::VersionNotSupported(_) => {}
|
|
||||||
PusError::IncorrectCrc(_) => {}
|
|
||||||
PusError::RawDataTooShort(_) => {}
|
|
||||||
PusError::NoRawData => {}
|
|
||||||
PusError::CrcCalculationMissing => {}
|
|
||||||
PusError::PacketError(_) => {}
|
|
||||||
}
|
}
|
||||||
return Ok(());
|
}
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
impl<E: 'static> ReceivesEcssPusTc for PusDistributor<E> {
|
||||||
|
type Error = PusDistribError<E>;
|
||||||
|
fn pass_pus_tc(&mut self, header: &SpHeader, pus_tc: &PusTc) -> Result<(), Self::Error> {
|
||||||
self.service_provider
|
self.service_provider
|
||||||
.handle_pus_tc_packet(tc.service(), tc.apid(), &tc);
|
.handle_pus_tc_packet(pus_tc.service(), header, pus_tc)
|
||||||
Ok(())
|
.map_err(|e| PusDistribError::CustomError(e))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: 'static> PusDistributor<E> {
|
||||||
|
pub fn service_provider_ref<T: PusServiceProvider<Error = E>>(&self) -> Option<&T> {
|
||||||
|
self.service_provider.downcast_ref::<T>()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn service_provider_mut<T: PusServiceProvider<Error = E>>(&mut self) -> Option<&mut T> {
|
||||||
|
self.service_provider.downcast_mut::<T>()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::error::SimpleStdErrorHandler;
|
|
||||||
use crate::tmtc::ccsds_distrib::tests::{
|
use crate::tmtc::ccsds_distrib::tests::{
|
||||||
BasicApidHandlerOwnedQueue, BasicApidHandlerSharedQueue,
|
generate_ping_tc, BasicApidHandlerOwnedQueue, BasicApidHandlerSharedQueue,
|
||||||
};
|
};
|
||||||
use crate::tmtc::ccsds_distrib::{ApidPacketHandler, CcsdsDistributor};
|
use crate::tmtc::ccsds_distrib::{ApidPacketHandler, CcsdsDistributor};
|
||||||
|
use spacepackets::ecss::PusError;
|
||||||
use spacepackets::tc::PusTc;
|
use spacepackets::tc::PusTc;
|
||||||
use std::any::Any;
|
use spacepackets::CcsdsPacket;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
@ -74,103 +153,93 @@ mod tests {
|
|||||||
pub pus_queue: VecDeque<(u8, u16, Vec<u8>)>,
|
pub pus_queue: VecDeque<(u8, u16, Vec<u8>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsAny for PusHandlerSharedQueue {
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PusServiceProvider for PusHandlerSharedQueue {
|
impl PusServiceProvider for PusHandlerSharedQueue {
|
||||||
fn handle_pus_tc_packet(&mut self, service: u8, apid: u16, pus_tc: &PusTc) {
|
type Error = PusError;
|
||||||
|
fn handle_pus_tc_packet(
|
||||||
|
&mut self,
|
||||||
|
service: u8,
|
||||||
|
sp_header: &SpHeader,
|
||||||
|
pus_tc: &PusTc,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
let mut vec: Vec<u8> = Vec::new();
|
let mut vec: Vec<u8> = Vec::new();
|
||||||
pus_tc
|
pus_tc.append_to_vec(&mut vec)?;
|
||||||
.append_to_vec(&mut vec)
|
Ok(self
|
||||||
.expect("Appending raw PUS TC to vector failed");
|
.pus_queue
|
||||||
self.pus_queue
|
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.expect("Mutex lock failed")
|
||||||
.push_back((service, apid, vec));
|
.push_back((service, sp_header.apid(), vec)))
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsAny for PusHandlerOwnedQueue {
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PusServiceProvider for PusHandlerOwnedQueue {
|
impl PusServiceProvider for PusHandlerOwnedQueue {
|
||||||
fn handle_pus_tc_packet(&mut self, service: u8, apid: u16, pus_tc: &PusTc) {
|
type Error = PusError;
|
||||||
|
fn handle_pus_tc_packet(
|
||||||
|
&mut self,
|
||||||
|
service: u8,
|
||||||
|
sp_header: &SpHeader,
|
||||||
|
pus_tc: &PusTc,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
let mut vec: Vec<u8> = Vec::new();
|
let mut vec: Vec<u8> = Vec::new();
|
||||||
pus_tc
|
pus_tc.append_to_vec(&mut vec)?;
|
||||||
.append_to_vec(&mut vec)
|
Ok(self.pus_queue.push_back((service, sp_header.apid(), vec)))
|
||||||
.expect("Appending raw PUS TC to vector failed");
|
|
||||||
self.pus_queue.push_back((service, apid, vec));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ApidHandlerShared {
|
struct ApidHandlerShared {
|
||||||
pub pus_distrib: PusDistributor,
|
pub pus_distrib: PusDistributor<PusError>,
|
||||||
handler_base: BasicApidHandlerSharedQueue,
|
pub handler_base: BasicApidHandlerSharedQueue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsAny for ApidHandlerShared {
|
struct ApidHandlerOwned {
|
||||||
fn as_any(&self) -> &dyn Any {
|
pub pus_distrib: PusDistributor<PusError>,
|
||||||
self
|
handler_base: BasicApidHandlerOwnedQueue,
|
||||||
}
|
|
||||||
|
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! apid_handler_impl {
|
macro_rules! apid_handler_impl {
|
||||||
() => {
|
() => {
|
||||||
|
type Error = PusError;
|
||||||
|
|
||||||
fn valid_apids(&self) -> &'static [u16] {
|
fn valid_apids(&self) -> &'static [u16] {
|
||||||
&[0x000, 0x002]
|
&[0x000, 0x002]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) {
|
fn handle_known_apid(
|
||||||
self.handler_base.handle_known_apid(&sp_header, tc_raw);
|
&mut self,
|
||||||
self.pus_distrib
|
sp_header: &SpHeader,
|
||||||
.pass_ccsds(&sp_header, tc_raw)
|
tc_raw: &[u8],
|
||||||
.expect("Passing PUS packet failed");
|
) -> Result<(), Self::Error> {
|
||||||
|
self.handler_base
|
||||||
|
.handle_known_apid(&sp_header, tc_raw)
|
||||||
|
.ok()
|
||||||
|
.expect("Unexpected error");
|
||||||
|
match self.pus_distrib.pass_ccsds(&sp_header, tc_raw) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => match e {
|
||||||
|
PusDistribError::CustomError(_) => Ok(()),
|
||||||
|
PusDistribError::PusError(e) => Err(e),
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) {
|
fn handle_unknown_apid(
|
||||||
self.handler_base.handle_unknown_apid(&sp_header, tc_raw);
|
&mut self,
|
||||||
|
sp_header: &SpHeader,
|
||||||
|
tc_raw: &[u8],
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
self.handler_base
|
||||||
|
.handle_unknown_apid(&sp_header, tc_raw)
|
||||||
|
.ok()
|
||||||
|
.expect("Unexpected error");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ApidPacketHandler for ApidHandlerShared {
|
impl ApidPacketHandler for ApidHandlerOwned {
|
||||||
apid_handler_impl!();
|
apid_handler_impl!();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ApidHandlerOwned {
|
impl ApidPacketHandler for ApidHandlerShared {
|
||||||
pub pus_distrib: PusDistributor,
|
|
||||||
handler_base: BasicApidHandlerOwnedQueue,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsAny for ApidHandlerOwned {
|
|
||||||
fn as_any(&self) -> &dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ApidPacketHandler for ApidHandlerOwned {
|
|
||||||
apid_handler_impl!();
|
apid_handler_impl!();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,27 +256,22 @@ mod tests {
|
|||||||
unknown_packet_queue: unknown_packet_queue.clone(),
|
unknown_packet_queue: unknown_packet_queue.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let error_handler = SimpleStdErrorHandler {};
|
|
||||||
let pus_distrib = PusDistributor {
|
let pus_distrib = PusDistributor {
|
||||||
service_provider: Box::new(pus_handler),
|
service_provider: Box::new(pus_handler),
|
||||||
error_handler: Box::new(error_handler),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let apid_handler = ApidHandlerShared {
|
let apid_handler = ApidHandlerShared {
|
||||||
pus_distrib,
|
pus_distrib,
|
||||||
handler_base,
|
handler_base,
|
||||||
};
|
};
|
||||||
|
let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler));
|
||||||
let mut ccsds_distrib =
|
|
||||||
CcsdsDistributor::new(Box::new(apid_handler), Box::new(error_handler));
|
|
||||||
let mut sph = SpHeader::tc(0x002, 0x34, 0).unwrap();
|
|
||||||
let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true);
|
|
||||||
let mut test_buf: [u8; 32] = [0; 32];
|
let mut test_buf: [u8; 32] = [0; 32];
|
||||||
let size = pus_tc
|
let tc_slice = generate_ping_tc(test_buf.as_mut_slice());
|
||||||
.write_to(test_buf.as_mut_slice())
|
|
||||||
.expect("Error writing TC to buffer");
|
// Pass packet to distributor
|
||||||
let tc_slice = &test_buf[0..size];
|
ccsds_distrib
|
||||||
ccsds_distrib.pass_tc(tc_slice);
|
.pass_tc(tc_slice)
|
||||||
|
.expect("Passing TC slice failed");
|
||||||
let recvd_ccsds = known_packet_queue.lock().unwrap().pop_front();
|
let recvd_ccsds = known_packet_queue.lock().unwrap().pop_front();
|
||||||
assert!(unknown_packet_queue.lock().unwrap().is_empty());
|
assert!(unknown_packet_queue.lock().unwrap().is_empty());
|
||||||
assert!(recvd_ccsds.is_some());
|
assert!(recvd_ccsds.is_some());
|
||||||
@ -226,32 +290,25 @@ mod tests {
|
|||||||
fn test_as_any_cast() {
|
fn test_as_any_cast() {
|
||||||
let pus_handler = PusHandlerOwnedQueue::default();
|
let pus_handler = PusHandlerOwnedQueue::default();
|
||||||
let handler_base = BasicApidHandlerOwnedQueue::default();
|
let handler_base = BasicApidHandlerOwnedQueue::default();
|
||||||
let error_handler = SimpleStdErrorHandler {};
|
|
||||||
let pus_distrib = PusDistributor {
|
let pus_distrib = PusDistributor {
|
||||||
service_provider: Box::new(pus_handler),
|
service_provider: Box::new(pus_handler),
|
||||||
error_handler: Box::new(error_handler),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let apid_handler = ApidHandlerOwned {
|
let apid_handler = ApidHandlerOwned {
|
||||||
pus_distrib,
|
pus_distrib,
|
||||||
handler_base,
|
handler_base,
|
||||||
};
|
};
|
||||||
let mut ccsds_distrib =
|
let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler));
|
||||||
CcsdsDistributor::new(Box::new(apid_handler), Box::new(error_handler));
|
|
||||||
|
|
||||||
let mut sph = SpHeader::tc(0x002, 0x34, 0).unwrap();
|
|
||||||
let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true);
|
|
||||||
let mut test_buf: [u8; 32] = [0; 32];
|
let mut test_buf: [u8; 32] = [0; 32];
|
||||||
let size = pus_tc
|
let tc_slice = generate_ping_tc(test_buf.as_mut_slice());
|
||||||
.write_to(test_buf.as_mut_slice())
|
|
||||||
.expect("Error writing TC to buffer");
|
ccsds_distrib
|
||||||
let tc_slice = &test_buf[0..size];
|
.pass_tc(tc_slice)
|
||||||
ccsds_distrib.pass_tc(tc_slice);
|
.expect("Passing TC slice failed");
|
||||||
|
|
||||||
let apid_handler_casted_back: &mut ApidHandlerOwned = ccsds_distrib
|
let apid_handler_casted_back: &mut ApidHandlerOwned = ccsds_distrib
|
||||||
.apid_handler
|
.apid_handler_mut()
|
||||||
.as_mut_any()
|
|
||||||
.downcast_mut::<ApidHandlerOwned>()
|
|
||||||
.expect("Cast to concrete type ApidHandler failed");
|
.expect("Cast to concrete type ApidHandler failed");
|
||||||
assert!(!apid_handler_casted_back
|
assert!(!apid_handler_casted_back
|
||||||
.handler_base
|
.handler_base
|
||||||
@ -259,10 +316,8 @@ mod tests {
|
|||||||
.is_empty());
|
.is_empty());
|
||||||
let handler_casted_back: &mut PusHandlerOwnedQueue = apid_handler_casted_back
|
let handler_casted_back: &mut PusHandlerOwnedQueue = apid_handler_casted_back
|
||||||
.pus_distrib
|
.pus_distrib
|
||||||
.service_provider
|
.service_provider_mut()
|
||||||
.as_mut_any()
|
.expect("Cast to concrete type PusHandlerOwnedQueue failed");
|
||||||
.downcast_mut::<PusHandlerOwnedQueue>()
|
|
||||||
.expect("Cast to concrete type PusHandler failed");
|
|
||||||
assert!(!handler_casted_back.pus_queue.is_empty());
|
assert!(!handler_casted_back.pus_queue.is_empty());
|
||||||
let (service, apid, packet_raw) = handler_casted_back.pus_queue.pop_front().unwrap();
|
let (service, apid, packet_raw) = handler_casted_back.pus_queue.pop_front().unwrap();
|
||||||
assert_eq!(service, 17);
|
assert_eq!(service, 17);
|
||||||
|
8
fsrc-example/Cargo.toml
Normal file
8
fsrc-example/Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "fsrc-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
|
||||||
|
[dependencies.spacepackets]
|
||||||
|
path = "../spacepackets"
|
16
fsrc-example/src/bin/client.rs
Normal file
16
fsrc-example/src/bin/client.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT};
|
||||||
|
use spacepackets::tc::PusTc;
|
||||||
|
use spacepackets::SpHeader;
|
||||||
|
use std::net::{IpAddr, SocketAddr, UdpSocket};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut buf = [0; 32];
|
||||||
|
let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
||||||
|
let mut sph = SpHeader::tc(0x02, 0, 0).unwrap();
|
||||||
|
let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true);
|
||||||
|
let client = UdpSocket::bind("127.0.0.1:7300").expect("Connecting to UDP server failed");
|
||||||
|
let size = pus_tc.write_to(&mut buf).expect("Creating PUS TC failed");
|
||||||
|
client
|
||||||
|
.send_to(&buf[0..size], &addr)
|
||||||
|
.expect(&*format!("Sending to {:?} failed", addr));
|
||||||
|
}
|
15
fsrc-example/src/bin/obsw.rs
Normal file
15
fsrc-example/src/bin/obsw.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
use fsrc_example::{OBSW_SERVER_ADDR, SERVER_PORT};
|
||||||
|
use std::net::{IpAddr, SocketAddr, UdpSocket};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut recv_buf = [0; 1024];
|
||||||
|
let addr = SocketAddr::new(IpAddr::V4(OBSW_SERVER_ADDR), SERVER_PORT);
|
||||||
|
let socket = UdpSocket::bind(&addr).expect("Error opening UDP socket");
|
||||||
|
loop {
|
||||||
|
let (num_bytes, src) = socket.recv_from(&mut recv_buf).expect("UDP Receive error");
|
||||||
|
println!(
|
||||||
|
"Received TM with len {num_bytes} from {src}: {:x?}",
|
||||||
|
&recv_buf[0..num_bytes]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
4
fsrc-example/src/lib.rs
Normal file
4
fsrc-example/src/lib.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
pub const OBSW_SERVER_ADDR: Ipv4Addr = Ipv4Addr::new(127, 0, 0, 1);
|
||||||
|
pub const SERVER_PORT: u16 = 7301;
|
@ -1 +1 @@
|
|||||||
Subproject commit fde3fe9cba62b816e004a174821b2d4760003d23
|
Subproject commit 35073a45a536051e3852696c501d7afa1b36a808
|
Loading…
Reference in New Issue
Block a user