use Downcast for Object Manager

This commit is contained in:
Robin Müller 2022-08-14 19:37:34 +02:00
parent 44d1c6cf97
commit 287f67a477
No known key found for this signature in database
GPG Key ID: 71B58F8A3CDFA9AC
3 changed files with 49 additions and 38 deletions

View File

@ -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
@ -39,24 +36,23 @@
//!
//! fn initialize(&mut self) -> Result<(), Box<dyn Error>> {
//! self.was_initialized = true;
//! Ok(())
//! }
//! Ok(())
//! }
//! }
//!
//! 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"));

View File

@ -1,4 +1,5 @@
//! TMTC module. Contains packet routing components with special support for CCSDS and ECSS packets.
//! Telemetry and Telecommanding (TMTC) module. Contains packet routing components with special
//! support for CCSDS and ECSS packets.
use crate::error::{FsrcErrorRaw, FsrcGroupIds};
use spacepackets::tc::PusTc;
use spacepackets::SpHeader;
@ -25,18 +26,31 @@ const FROM_BYTES_ZEROCOPY_ERROR: FsrcErrorRaw = FsrcErrorRaw::new(
"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 {
type Error;
// TODO: Maybe it makes sense to return Result<(), Self::Error> here with Error being an associated
// type..
fn pass_tc(&mut self, tc_raw: &[u8]) -> Result<(), Self::Error>;
}
/// Generic trait for object which can receive CCSDS space packets, for 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 {
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>;

View File

@ -25,6 +25,8 @@
//! 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> {
@ -40,7 +42,7 @@
//! };
//! let mut pus_distributor = PusDistributor::new(Box::new(service_handler));
//!
//! // Create and pass PUS telecommand with a valid APID
//! // 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];
@ -51,7 +53,8 @@
//!
//! pus_distributor.pass_tc(tc_slice).expect("Passing PUS telecommand failed");
//!
//! // User helper function to retrieve concrete class
//! // 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");
@ -80,9 +83,7 @@ pub struct PusDistributor<E> {
impl<E> PusDistributor<E> {
pub fn new(service_provider: Box<dyn PusServiceProvider<Error = E>>) -> Self {
PusDistributor {
service_provider
}
PusDistributor { service_provider }
}
}