CCSDS and ECSS packet routing #8
@ -1,12 +1,13 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<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="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="true" />
|
||||
<option name="requiredFeatures" value="false" />
|
||||
<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" />
|
||||
|
@ -4,7 +4,7 @@
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="allFeatures" value="true" />
|
||||
<option name="emulateTerminal" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<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="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="allFeatures" value="true" />
|
||||
<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" />
|
||||
|
60
Cargo.lock
generated
60
Cargo.lock
generated
@ -110,7 +110,6 @@ dependencies = [
|
||||
"js-sys",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
"wasm-bindgen",
|
||||
"winapi",
|
||||
]
|
||||
@ -201,6 +200,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-hal"
|
||||
version = "0.2.7"
|
||||
@ -216,14 +221,23 @@ name = "fsrc-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bus",
|
||||
"downcast-rs",
|
||||
"num",
|
||||
"postcard 1.0.1",
|
||||
"once_cell",
|
||||
"postcard",
|
||||
"serde",
|
||||
"spacepackets",
|
||||
"thiserror",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fsrc-example"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"spacepackets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.2.1"
|
||||
@ -411,9 +425,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.13.0"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
@ -429,17 +443,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "postcard"
|
||||
version = "1.0.1"
|
||||
@ -451,12 +454,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postcard-cobs"
|
||||
version = "0.1.5-pre"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c68cb38ed13fd7bc9dd5db8f165b7c8d9c1a315104083a2b10f11354c2af97f"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.39"
|
||||
@ -597,8 +594,8 @@ dependencies = [
|
||||
"chrono",
|
||||
"crc",
|
||||
"delegate",
|
||||
"num",
|
||||
"postcard 0.7.3",
|
||||
"num-traits",
|
||||
"postcard",
|
||||
"serde",
|
||||
"zerocopy",
|
||||
]
|
||||
@ -661,17 +658,6 @@ dependencies = [
|
||||
"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]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.1"
|
||||
@ -705,12 +691,6 @@ dependencies = [
|
||||
"vcell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.82"
|
||||
|
@ -3,4 +3,5 @@
|
||||
members = [
|
||||
"fsrc-core",
|
||||
"spacepackets",
|
||||
"fsrc-example"
|
||||
]
|
||||
|
@ -9,13 +9,20 @@ edition = "2021"
|
||||
thiserror = "1.0"
|
||||
bus = "2.2.3"
|
||||
num = "0.4"
|
||||
spacepackets = { path = "../spacepackets"}
|
||||
|
||||
[dependencies.spacepackets]
|
||||
path = "../spacepackets"
|
||||
|
||||
[dependencies.downcast-rs]
|
||||
version = "1.2.0"
|
||||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
postcard = { version = "1.0.1", features = ["use-std"] }
|
||||
serde = "1.0.143"
|
||||
zerocopy = "0.6.1"
|
||||
once_cell = "1.13.1"
|
||||
|
||||
[features]
|
||||
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 {}
|
||||
|
||||
#[cfg(feature = "use_std")]
|
||||
|
@ -5,7 +5,7 @@ pub type GroupId = u16;
|
||||
pub type UniqueId = u16;
|
||||
pub type EventRaw = u32;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Severity {
|
||||
INFO = 1,
|
||||
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 {
|
||||
severity: Severity,
|
||||
group_id: GroupId,
|
||||
|
@ -6,7 +6,7 @@ use std::thread;
|
||||
use std::thread::JoinHandle;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum OpResult {
|
||||
Ok,
|
||||
TerminationRequested,
|
||||
|
@ -1,18 +1,24 @@
|
||||
use crate::hal::host::udp_server::ReceiveResult::{IoError, ReceiverError};
|
||||
use crate::tmtc::ReceivesTc;
|
||||
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
|
||||
use std::vec::Vec;
|
||||
|
||||
pub struct UdpTmtcServer {
|
||||
pub struct UdpTmtcServer<E> {
|
||||
socket: UdpSocket,
|
||||
recv_buf: Vec<u8>,
|
||||
tc_receiver: Box<dyn ReceivesTc>,
|
||||
tc_receiver: Box<dyn ReceivesTc<Error = E>>,
|
||||
}
|
||||
|
||||
impl UdpTmtcServer {
|
||||
pub fn new<A: ToSocketAddrs, E>(
|
||||
pub enum ReceiveResult<E> {
|
||||
IoError(std::io::Error),
|
||||
ReceiverError(E),
|
||||
}
|
||||
|
||||
impl<E> UdpTmtcServer<E> {
|
||||
pub fn new<A: ToSocketAddrs>(
|
||||
addr: A,
|
||||
max_recv_size: usize,
|
||||
tc_receiver: Box<dyn ReceivesTc>,
|
||||
tc_receiver: Box<dyn ReceivesTc<Error = E>>,
|
||||
) -> Result<Self, std::io::Error> {
|
||||
Ok(Self {
|
||||
socket: UdpSocket::bind(addr)?,
|
||||
@ -21,9 +27,14 @@ impl UdpTmtcServer {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn recv_tc(&mut self) -> Result<(usize, SocketAddr), std::io::Error> {
|
||||
let res = self.socket.recv_from(&mut self.recv_buf)?;
|
||||
self.tc_receiver.pass_tc(&self.recv_buf[0..res.0]);
|
||||
pub fn recv_tc(&mut self) -> Result<(usize, SocketAddr), ReceiveResult<E>> {
|
||||
let res = self
|
||||
.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)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,12 @@
|
||||
//! # 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 event_man;
|
||||
pub mod events;
|
||||
@ -8,3 +15,5 @@ pub mod hal;
|
||||
pub mod objects;
|
||||
pub mod pool;
|
||||
pub mod tmtc;
|
||||
|
||||
extern crate downcast_rs;
|
||||
|
@ -7,7 +7,7 @@
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! ```rust
|
||||
//! use std::any::Any;
|
||||
//! use std::error::Error;
|
||||
//! use fsrc_core::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
|
||||
@ -29,9 +29,6 @@
|
||||
//! }
|
||||
//!
|
||||
//! impl SystemObject for ExampleSysObj {
|
||||
//! fn as_any(&self) -> &dyn Any {
|
||||
//! self
|
||||
//! }
|
||||
//!
|
||||
//! fn get_object_id(&self) -> &ObjectId {
|
||||
//! &self.id
|
||||
@ -45,18 +42,17 @@
|
||||
//!
|
||||
//! impl ManagedSystemObject for ExampleSysObj {}
|
||||
//!
|
||||
//!
|
||||
//! let mut obj_manager = ObjectManager::default();
|
||||
//! let obj_id = ObjectId { id: 0, name: "Example 0"};
|
||||
//! let example_obj = ExampleSysObj::new(obj_id, 42);
|
||||
//! 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();
|
||||
//! assert_eq!(example_obj.id, obj_id);
|
||||
//! assert_eq!(example_obj.dummy, 42);
|
||||
//! ```
|
||||
|
||||
use std::any::Any;
|
||||
use downcast_rs::Downcast;
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
|
||||
@ -68,13 +64,14 @@ pub struct ObjectId {
|
||||
|
||||
/// Each object which is stored inside the [object manager][ObjectManager] needs to implemented
|
||||
/// this trait
|
||||
pub trait SystemObject {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
pub trait SystemObject: Downcast {
|
||||
fn get_object_id(&self) -> &ObjectId;
|
||||
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
|
||||
/// using an [object ID][ObjectId]
|
||||
@ -114,19 +111,26 @@ impl ObjectManager {
|
||||
Ok(init_success)
|
||||
}
|
||||
|
||||
/// Retrieve an object stored inside the manager. The type to retrieve needs to be explicitly
|
||||
/// passed as a generic parameter
|
||||
pub fn get<T: Any>(&self, key: &ObjectId) -> Option<&T> {
|
||||
/// Retrieve a 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_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
|
||||
.get(key)
|
||||
.and_then(|o| o.as_ref().as_any().downcast_ref::<T>())
|
||||
.get_mut(key)
|
||||
.and_then(|o| o.downcast_mut::<T>())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
|
||||
use std::any::Any;
|
||||
use std::error::Error;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread;
|
||||
@ -148,10 +152,6 @@ mod tests {
|
||||
}
|
||||
|
||||
impl SystemObject for ExampleSysObj {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn get_object_id(&self) -> &ObjectId {
|
||||
&self.id
|
||||
}
|
||||
@ -171,10 +171,6 @@ mod tests {
|
||||
}
|
||||
|
||||
impl SystemObject for OtherExampleObject {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn get_object_id(&self) -> &ObjectId {
|
||||
&self.id
|
||||
}
|
||||
@ -199,7 +195,7 @@ mod tests {
|
||||
let res = obj_manager.initialize();
|
||||
assert!(res.is_ok());
|
||||
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());
|
||||
let expl_obj_back_casted = obj_back_casted.unwrap();
|
||||
assert_eq!(expl_obj_back_casted.dummy, 42);
|
||||
@ -219,7 +215,7 @@ mod tests {
|
||||
let res = obj_manager.initialize();
|
||||
assert!(res.is_ok());
|
||||
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());
|
||||
let expl_obj_back_casted = obj_back_casted.unwrap();
|
||||
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 jh0 = thread::spawn(move || {
|
||||
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());
|
||||
let expl_obj_back_casted = obj_back_casted.unwrap();
|
||||
assert_eq!(expl_obj_back_casted.dummy, 42);
|
||||
@ -276,7 +272,7 @@ mod tests {
|
||||
|
||||
let jh1 = thread::spawn(move || {
|
||||
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());
|
||||
let expl_obj_back_casted = obj_back_casted.unwrap();
|
||||
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.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct StoreAddr {
|
||||
pool_idx: u16,
|
||||
packet_idx: NumBlocks,
|
||||
@ -124,13 +124,13 @@ impl StoreAddr {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum StoreIdError {
|
||||
InvalidSubpool(u16),
|
||||
InvalidPacketIdx(u16),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum StoreError {
|
||||
/// Requested data block is too large
|
||||
DataTooLarge(usize),
|
||||
|
@ -1,76 +1,201 @@
|
||||
use crate::any::AsAny;
|
||||
use crate::error::FsrcErrorHandler;
|
||||
use crate::tmtc::{ReceivesTc, FROM_BYTES_SLICE_TOO_SMALL_ERROR, FROM_BYTES_ZEROCOPY_ERROR};
|
||||
//! CCSDS packet routing components.
|
||||
//!
|
||||
//! 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};
|
||||
|
||||
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 handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]);
|
||||
fn handle_unknown_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]);
|
||||
fn handle_known_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 {
|
||||
pub apid_handler: Box<dyn ApidPacketHandler>,
|
||||
error_handler: Box<dyn FsrcErrorHandler>,
|
||||
downcast_rs::impl_downcast!(ApidPacketHandler assoc Error);
|
||||
|
||||
/// 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 {
|
||||
pub fn new(
|
||||
apid_handler: Box<dyn ApidPacketHandler>,
|
||||
error_handler: Box<dyn FsrcErrorHandler>,
|
||||
) -> Self {
|
||||
CcsdsDistributor {
|
||||
apid_handler,
|
||||
error_handler,
|
||||
}
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum CcsdsError<E> {
|
||||
CustomError(E),
|
||||
PacketError(PacketError),
|
||||
}
|
||||
|
||||
impl<E: 'static> ReceivesCcsdsTc for CcsdsDistributor<E> {
|
||||
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 {
|
||||
fn pass_tc(&mut self, tm_raw: &[u8]) {
|
||||
let sp_header = match SpHeader::from_raw_slice(tm_raw) {
|
||||
Ok(header) => header,
|
||||
Err(e) => {
|
||||
match e {
|
||||
PacketError::FromBytesSliceTooSmall(missmatch) => {
|
||||
self.error_handler.error_with_two_params(
|
||||
FROM_BYTES_SLICE_TOO_SMALL_ERROR,
|
||||
missmatch.found as u32,
|
||||
missmatch.expected as u32,
|
||||
);
|
||||
impl<E: 'static> ReceivesTc for CcsdsDistributor<E> {
|
||||
type Error = CcsdsError<E>;
|
||||
|
||||
fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error> {
|
||||
let sp_header = SpHeader::from_raw_slice(tc_raw).map_err(|e| CcsdsError::PacketError(e))?;
|
||||
self.dispatch_ccsds(&sp_header, tc_raw)
|
||||
}
|
||||
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 valid_apids = self.apid_handler.valid_apids();
|
||||
for &valid_apid in valid_apids {
|
||||
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)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use crate::error::SimpleStdErrorHandler;
|
||||
use crate::tmtc::ccsds_distrib::{ApidPacketHandler, CcsdsDistributor};
|
||||
use spacepackets::tc::PusTc;
|
||||
use spacepackets::CcsdsPacket;
|
||||
use std::any::Any;
|
||||
use std::collections::VecDeque;
|
||||
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 known_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>)>,
|
||||
}
|
||||
|
||||
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 {
|
||||
type Error = ();
|
||||
fn valid_apids(&self) -> &'static [u16] {
|
||||
&[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();
|
||||
vec.extend_from_slice(tc_raw);
|
||||
self.known_packet_queue
|
||||
Ok(self
|
||||
.known_packet_queue
|
||||
.lock()
|
||||
.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();
|
||||
vec.extend_from_slice(tc_raw);
|
||||
self.unknown_packet_queue
|
||||
Ok(self
|
||||
.unknown_packet_queue
|
||||
.lock()
|
||||
.unwrap()
|
||||
.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
|
||||
.push_back((sp_header.apid(), vec)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ApidPacketHandler for BasicApidHandlerOwnedQueue {
|
||||
type Error = ();
|
||||
|
||||
fn valid_apids(&self) -> &'static [u16] {
|
||||
&[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();
|
||||
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();
|
||||
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(),
|
||||
unknown_packet_queue: unknown_packet_queue.clone(),
|
||||
};
|
||||
let error_handler = SimpleStdErrorHandler {};
|
||||
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 ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler));
|
||||
let mut test_buf: [u8; 32] = [0; 32];
|
||||
pus_tc
|
||||
.write_to(test_buf.as_mut_slice())
|
||||
.expect("Error writing TC to buffer");
|
||||
ccsds_distrib.pass_tc(&test_buf);
|
||||
let tc_slice = generate_ping_tc(test_buf.as_mut_slice());
|
||||
|
||||
ccsds_distrib.pass_tc(tc_slice).expect("Passing TC failed");
|
||||
let recvd = known_packet_queue.lock().unwrap().pop_front();
|
||||
assert!(unknown_packet_queue.lock().unwrap().is_empty());
|
||||
assert!(recvd.is_some());
|
||||
let (apid, packet) = recvd.unwrap();
|
||||
assert_eq!(apid, 0x002);
|
||||
assert_eq!(packet.as_slice(), test_buf);
|
||||
assert_eq!(packet, tc_slice);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -178,16 +299,14 @@ pub(crate) mod tests {
|
||||
known_packet_queue: known_packet_queue.clone(),
|
||||
unknown_packet_queue: unknown_packet_queue.clone(),
|
||||
};
|
||||
let error_handler = SimpleStdErrorHandler {};
|
||||
let mut ccsds_distrib =
|
||||
CcsdsDistributor::new(Box::new(apid_handler), Box::new(error_handler));
|
||||
let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler));
|
||||
let mut sph = SpHeader::tc(0x004, 0x34, 0).unwrap();
|
||||
let pus_tc = PusTc::new_simple(&mut sph, 17, 1, None, true);
|
||||
let mut test_buf: [u8; 32] = [0; 32];
|
||||
pus_tc
|
||||
.write_to(test_buf.as_mut_slice())
|
||||
.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();
|
||||
assert!(known_packet_queue.lock().unwrap().is_empty());
|
||||
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 spacepackets::{PacketError, SpHeader};
|
||||
use spacepackets::tc::PusTc;
|
||||
use spacepackets::SpHeader;
|
||||
|
||||
pub mod ccsds_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 _PUS_ERROR: &str = "pus-tmtc";
|
||||
|
||||
// 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,
|
||||
0,
|
||||
RAW_PACKET_ERROR,
|
||||
_RAW_PACKET_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,
|
||||
1,
|
||||
RAW_PACKET_ERROR,
|
||||
_RAW_PACKET_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 {
|
||||
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 {
|
||||
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;
|
||||
use crate::error::FsrcErrorHandler;
|
||||
use crate::tmtc::{ReceivesCcsdsTc, ReceivesTc};
|
||||
//! ECSS PUS packet routing components.
|
||||
//!
|
||||
//! 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::tc::PusTc;
|
||||
use spacepackets::{CcsdsPacket, PacketError, SpHeader};
|
||||
use spacepackets::SpHeader;
|
||||
|
||||
pub trait PusServiceProvider: AsAny {
|
||||
fn handle_pus_tc_packet(&mut self, service: u8, apid: u16, pus_tc: &PusTc);
|
||||
pub trait PusServiceProvider: Downcast {
|
||||
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 {
|
||||
pub service_provider: Box<dyn PusServiceProvider>,
|
||||
error_handler: Box<dyn FsrcErrorHandler>,
|
||||
impl<E> PusDistributor<E> {
|
||||
pub fn new(service_provider: Box<dyn PusServiceProvider<Error = E>>) -> Self {
|
||||
PusDistributor { service_provider }
|
||||
}
|
||||
}
|
||||
|
||||
impl ReceivesTc for PusDistributor {
|
||||
fn pass_tc(&mut self, tm_raw: &[u8]) {
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
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
|
||||
match SpHeader::from_raw_slice(tm_raw) {
|
||||
Ok(sp_header) => {
|
||||
self.pass_ccsds(&sp_header, tm_raw).unwrap();
|
||||
}
|
||||
Err(error) => {
|
||||
// TODO: Error handling
|
||||
}
|
||||
}
|
||||
let sp_header = SpHeader::from_raw_slice(tm_raw)
|
||||
.map_err(|e| PusDistribError::PusError(PusError::PacketError(e)))?;
|
||||
self.pass_ccsds(&sp_header, tm_raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReceivesCcsdsTc for PusDistributor {
|
||||
fn pass_ccsds(&mut self, _header: &SpHeader, tm_raw: &[u8]) -> Result<(), PacketError> {
|
||||
// TODO: Better error handling
|
||||
let (tc, _) = match PusTc::new_from_raw_slice(tm_raw) {
|
||||
Ok(tuple) => tuple,
|
||||
Err(e) => {
|
||||
match e {
|
||||
PusError::VersionNotSupported(_) => {}
|
||||
PusError::IncorrectCrc(_) => {}
|
||||
PusError::RawDataTooShort(_) => {}
|
||||
PusError::NoRawData => {}
|
||||
PusError::CrcCalculationMissing => {}
|
||||
PusError::PacketError(_) => {}
|
||||
impl<E: 'static> ReceivesCcsdsTc for PusDistributor<E> {
|
||||
type Error = PusDistribError<E>;
|
||||
fn pass_ccsds(&mut self, header: &SpHeader, tm_raw: &[u8]) -> Result<(), Self::Error> {
|
||||
let (tc, _) =
|
||||
PusTc::new_from_raw_slice(tm_raw).map_err(|e| PusDistribError::PusError(e))?;
|
||||
self.pass_pus_tc(header, &tc)
|
||||
}
|
||||
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
|
||||
.handle_pus_tc_packet(tc.service(), tc.apid(), &tc);
|
||||
Ok(())
|
||||
.handle_pus_tc_packet(pus_tc.service(), header, pus_tc)
|
||||
.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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::error::SimpleStdErrorHandler;
|
||||
use crate::tmtc::ccsds_distrib::tests::{
|
||||
BasicApidHandlerOwnedQueue, BasicApidHandlerSharedQueue,
|
||||
generate_ping_tc, BasicApidHandlerOwnedQueue, BasicApidHandlerSharedQueue,
|
||||
};
|
||||
use crate::tmtc::ccsds_distrib::{ApidPacketHandler, CcsdsDistributor};
|
||||
use spacepackets::ecss::PusError;
|
||||
use spacepackets::tc::PusTc;
|
||||
use std::any::Any;
|
||||
use spacepackets::CcsdsPacket;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@ -74,103 +153,93 @@ mod tests {
|
||||
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 {
|
||||
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();
|
||||
pus_tc
|
||||
.append_to_vec(&mut vec)
|
||||
.expect("Appending raw PUS TC to vector failed");
|
||||
self.pus_queue
|
||||
pus_tc.append_to_vec(&mut vec)?;
|
||||
Ok(self
|
||||
.pus_queue
|
||||
.lock()
|
||||
.unwrap()
|
||||
.push_back((service, apid, vec));
|
||||
}
|
||||
}
|
||||
|
||||
impl AsAny for PusHandlerOwnedQueue {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
.expect("Mutex lock failed")
|
||||
.push_back((service, sp_header.apid(), vec)))
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
pus_tc
|
||||
.append_to_vec(&mut vec)
|
||||
.expect("Appending raw PUS TC to vector failed");
|
||||
self.pus_queue.push_back((service, apid, vec));
|
||||
pus_tc.append_to_vec(&mut vec)?;
|
||||
Ok(self.pus_queue.push_back((service, sp_header.apid(), vec)))
|
||||
}
|
||||
}
|
||||
|
||||
struct ApidHandlerShared {
|
||||
pub pus_distrib: PusDistributor,
|
||||
handler_base: BasicApidHandlerSharedQueue,
|
||||
pub pus_distrib: PusDistributor<PusError>,
|
||||
pub handler_base: BasicApidHandlerSharedQueue,
|
||||
}
|
||||
|
||||
impl AsAny for ApidHandlerShared {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn as_mut_any(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
struct ApidHandlerOwned {
|
||||
pub pus_distrib: PusDistributor<PusError>,
|
||||
handler_base: BasicApidHandlerOwnedQueue,
|
||||
}
|
||||
|
||||
macro_rules! apid_handler_impl {
|
||||
() => {
|
||||
type Error = PusError;
|
||||
|
||||
fn valid_apids(&self) -> &'static [u16] {
|
||||
&[0x000, 0x002]
|
||||
}
|
||||
|
||||
fn handle_known_apid(&mut self, sp_header: &SpHeader, tc_raw: &[u8]) {
|
||||
self.handler_base.handle_known_apid(&sp_header, tc_raw);
|
||||
self.pus_distrib
|
||||
.pass_ccsds(&sp_header, tc_raw)
|
||||
.expect("Passing PUS packet failed");
|
||||
fn handle_known_apid(
|
||||
&mut self,
|
||||
sp_header: &SpHeader,
|
||||
tc_raw: &[u8],
|
||||
) -> 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]) {
|
||||
self.handler_base.handle_unknown_apid(&sp_header, tc_raw);
|
||||
fn handle_unknown_apid(
|
||||
&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!();
|
||||
}
|
||||
|
||||
struct ApidHandlerOwned {
|
||||
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 {
|
||||
impl ApidPacketHandler for ApidHandlerShared {
|
||||
apid_handler_impl!();
|
||||
}
|
||||
|
||||
@ -187,27 +256,22 @@ mod tests {
|
||||
unknown_packet_queue: unknown_packet_queue.clone(),
|
||||
};
|
||||
|
||||
let error_handler = SimpleStdErrorHandler {};
|
||||
let pus_distrib = PusDistributor {
|
||||
service_provider: Box::new(pus_handler),
|
||||
error_handler: Box::new(error_handler),
|
||||
};
|
||||
|
||||
let apid_handler = ApidHandlerShared {
|
||||
pus_distrib,
|
||||
handler_base,
|
||||
};
|
||||
|
||||
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 ccsds_distrib = CcsdsDistributor::new(Box::new(apid_handler));
|
||||
let mut test_buf: [u8; 32] = [0; 32];
|
||||
let size = pus_tc
|
||||
.write_to(test_buf.as_mut_slice())
|
||||
.expect("Error writing TC to buffer");
|
||||
let tc_slice = &test_buf[0..size];
|
||||
ccsds_distrib.pass_tc(tc_slice);
|
||||
let tc_slice = generate_ping_tc(test_buf.as_mut_slice());
|
||||
|
||||
// Pass packet to distributor
|
||||
ccsds_distrib
|
||||
.pass_tc(tc_slice)
|
||||
.expect("Passing TC slice failed");
|
||||
let recvd_ccsds = known_packet_queue.lock().unwrap().pop_front();
|
||||
assert!(unknown_packet_queue.lock().unwrap().is_empty());
|
||||
assert!(recvd_ccsds.is_some());
|
||||
@ -226,32 +290,25 @@ mod tests {
|
||||
fn test_as_any_cast() {
|
||||
let pus_handler = PusHandlerOwnedQueue::default();
|
||||
let handler_base = BasicApidHandlerOwnedQueue::default();
|
||||
let error_handler = SimpleStdErrorHandler {};
|
||||
let pus_distrib = PusDistributor {
|
||||
service_provider: Box::new(pus_handler),
|
||||
error_handler: Box::new(error_handler),
|
||||
};
|
||||
|
||||
let apid_handler = ApidHandlerOwned {
|
||||
pus_distrib,
|
||||
handler_base,
|
||||
};
|
||||
let mut ccsds_distrib =
|
||||
CcsdsDistributor::new(Box::new(apid_handler), Box::new(error_handler));
|
||||
let mut ccsds_distrib = CcsdsDistributor::new(Box::new(apid_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 size = pus_tc
|
||||
.write_to(test_buf.as_mut_slice())
|
||||
.expect("Error writing TC to buffer");
|
||||
let tc_slice = &test_buf[0..size];
|
||||
ccsds_distrib.pass_tc(tc_slice);
|
||||
let tc_slice = generate_ping_tc(test_buf.as_mut_slice());
|
||||
|
||||
ccsds_distrib
|
||||
.pass_tc(tc_slice)
|
||||
.expect("Passing TC slice failed");
|
||||
|
||||
let apid_handler_casted_back: &mut ApidHandlerOwned = ccsds_distrib
|
||||
.apid_handler
|
||||
.as_mut_any()
|
||||
.downcast_mut::<ApidHandlerOwned>()
|
||||
.apid_handler_mut()
|
||||
.expect("Cast to concrete type ApidHandler failed");
|
||||
assert!(!apid_handler_casted_back
|
||||
.handler_base
|
||||
@ -259,10 +316,8 @@ mod tests {
|
||||
.is_empty());
|
||||
let handler_casted_back: &mut PusHandlerOwnedQueue = apid_handler_casted_back
|
||||
.pus_distrib
|
||||
.service_provider
|
||||
.as_mut_any()
|
||||
.downcast_mut::<PusHandlerOwnedQueue>()
|
||||
.expect("Cast to concrete type PusHandler failed");
|
||||
.service_provider_mut()
|
||||
.expect("Cast to concrete type PusHandlerOwnedQueue failed");
|
||||
assert!(!handler_casted_back.pus_queue.is_empty());
|
||||
let (service, apid, packet_raw) = handler_casted_back.pus_queue.pop_front().unwrap();
|
||||
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