Robin Mueller
de4e6183b3
All checks were successful
Rust/sat-rs/pipeline/pr-main This commit looks good
- Add new shared subcrate satrs-shared to split off some shared components not expected to change very often. - Renmame `satrs-core` to `satrs`. It is expected that sat-rs will remain the primary crate, so the core information is superfluous, and core also implies stability, which will not be the case for some time.
308 lines
10 KiB
Rust
308 lines
10 KiB
Rust
//! # Module providing addressable object support and a manager for them
|
|
//!
|
|
//! Each addressable object can be identified using an [object ID][ObjectId].
|
|
//! The [system object][ManagedSystemObject] trait also allows storing these objects into the
|
|
//! [object manager][ObjectManager]. They can then be retrieved and casted back to a known type
|
|
//! using the object ID.
|
|
//!
|
|
//! # Examples
|
|
//!
|
|
//! ```rust
|
|
//! use std::any::Any;
|
|
//! use std::error::Error;
|
|
//! use satrs::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
|
|
//!
|
|
//! struct ExampleSysObj {
|
|
//! id: ObjectId,
|
|
//! dummy: u32,
|
|
//! was_initialized: bool,
|
|
//! }
|
|
//!
|
|
//! impl ExampleSysObj {
|
|
//! fn new(id: ObjectId, dummy: u32) -> ExampleSysObj {
|
|
//! ExampleSysObj {
|
|
//! id,
|
|
//! dummy,
|
|
//! was_initialized: false,
|
|
//! }
|
|
//! }
|
|
//! }
|
|
//!
|
|
//! impl SystemObject for ExampleSysObj {
|
|
//! type Error = ();
|
|
//! fn get_object_id(&self) -> &ObjectId {
|
|
//! &self.id
|
|
//! }
|
|
//!
|
|
//! fn initialize(&mut self) -> Result<(), Self::Error> {
|
|
//! self.was_initialized = true;
|
|
//! 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_ref(&obj_id);
|
|
//! let example_obj = obj_back_casted.unwrap();
|
|
//! assert_eq!(example_obj.id, obj_id);
|
|
//! assert_eq!(example_obj.dummy, 42);
|
|
//! ```
|
|
use crate::tmtc::TargetId;
|
|
#[cfg(feature = "alloc")]
|
|
use alloc::boxed::Box;
|
|
#[cfg(feature = "alloc")]
|
|
pub use alloc_mod::*;
|
|
#[cfg(feature = "alloc")]
|
|
use downcast_rs::Downcast;
|
|
#[cfg(feature = "alloc")]
|
|
use hashbrown::HashMap;
|
|
#[cfg(feature = "std")]
|
|
use std::error::Error;
|
|
|
|
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
|
pub struct ObjectId {
|
|
pub id: TargetId,
|
|
pub name: &'static str,
|
|
}
|
|
|
|
#[cfg(feature = "alloc")]
|
|
pub mod alloc_mod {
|
|
use super::*;
|
|
|
|
/// Each object which is stored inside the [object manager][ObjectManager] needs to implemented
|
|
/// this trait
|
|
pub trait SystemObject: Downcast {
|
|
type Error;
|
|
fn get_object_id(&self) -> &ObjectId;
|
|
fn initialize(&mut self) -> Result<(), Self::Error>;
|
|
}
|
|
downcast_rs::impl_downcast!(SystemObject assoc Error);
|
|
|
|
pub trait ManagedSystemObject: SystemObject + Send {}
|
|
downcast_rs::impl_downcast!(ManagedSystemObject assoc Error);
|
|
|
|
/// Helper module to manage multiple [ManagedSystemObjects][ManagedSystemObject] by mapping them
|
|
/// using an [object ID][ObjectId]
|
|
#[cfg(feature = "alloc")]
|
|
pub struct ObjectManager<E> {
|
|
obj_map: HashMap<ObjectId, Box<dyn ManagedSystemObject<Error = E>>>,
|
|
}
|
|
|
|
#[cfg(feature = "alloc")]
|
|
impl<E: 'static> Default for ObjectManager<E> {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "alloc")]
|
|
impl<E: 'static> ObjectManager<E> {
|
|
pub fn new() -> Self {
|
|
ObjectManager {
|
|
obj_map: HashMap::new(),
|
|
}
|
|
}
|
|
pub fn insert(&mut self, sys_obj: Box<dyn ManagedSystemObject<Error = E>>) -> bool {
|
|
let obj_id = sys_obj.get_object_id();
|
|
if self.obj_map.contains_key(obj_id) {
|
|
return false;
|
|
}
|
|
self.obj_map.insert(*obj_id, sys_obj).is_none()
|
|
}
|
|
|
|
/// Initializes all System Objects in the hash map and returns the number of successful
|
|
/// initializations
|
|
pub fn initialize(&mut self) -> Result<u32, Box<dyn Error>> {
|
|
let mut init_success = 0;
|
|
for val in self.obj_map.values_mut() {
|
|
if val.initialize().is_ok() {
|
|
init_success += 1
|
|
}
|
|
}
|
|
Ok(init_success)
|
|
}
|
|
|
|
/// 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<Error = E>>(&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<Error = E>>(
|
|
&mut self,
|
|
key: &ObjectId,
|
|
) -> Option<&mut T> {
|
|
self.obj_map
|
|
.get_mut(key)
|
|
.and_then(|o| o.downcast_mut::<T>())
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
|
|
use std::boxed::Box;
|
|
use std::string::String;
|
|
use std::sync::{Arc, Mutex};
|
|
use std::thread;
|
|
|
|
struct ExampleSysObj {
|
|
id: ObjectId,
|
|
dummy: u32,
|
|
was_initialized: bool,
|
|
}
|
|
|
|
impl ExampleSysObj {
|
|
fn new(id: ObjectId, dummy: u32) -> ExampleSysObj {
|
|
ExampleSysObj {
|
|
id,
|
|
dummy,
|
|
was_initialized: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl SystemObject for ExampleSysObj {
|
|
type Error = ();
|
|
fn get_object_id(&self) -> &ObjectId {
|
|
&self.id
|
|
}
|
|
|
|
fn initialize(&mut self) -> Result<(), Self::Error> {
|
|
self.was_initialized = true;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl ManagedSystemObject for ExampleSysObj {}
|
|
|
|
struct OtherExampleObject {
|
|
id: ObjectId,
|
|
string: String,
|
|
was_initialized: bool,
|
|
}
|
|
|
|
impl SystemObject for OtherExampleObject {
|
|
type Error = ();
|
|
fn get_object_id(&self) -> &ObjectId {
|
|
&self.id
|
|
}
|
|
|
|
fn initialize(&mut self) -> Result<(), Self::Error> {
|
|
self.was_initialized = true;
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl ManagedSystemObject for OtherExampleObject {}
|
|
|
|
#[test]
|
|
fn test_obj_manager_simple() {
|
|
let mut obj_manager = ObjectManager::default();
|
|
let expl_obj_id = ObjectId {
|
|
id: 0,
|
|
name: "Example 0",
|
|
};
|
|
let example_obj = ExampleSysObj::new(expl_obj_id, 42);
|
|
assert!(obj_manager.insert(Box::new(example_obj)));
|
|
let res = obj_manager.initialize();
|
|
assert!(res.is_ok());
|
|
assert_eq!(res.unwrap(), 1);
|
|
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);
|
|
assert!(expl_obj_back_casted.was_initialized);
|
|
|
|
let second_obj_id = ObjectId {
|
|
id: 12,
|
|
name: "Example 1",
|
|
};
|
|
let second_example_obj = OtherExampleObject {
|
|
id: second_obj_id,
|
|
string: String::from("Hello Test"),
|
|
was_initialized: false,
|
|
};
|
|
|
|
assert!(obj_manager.insert(Box::new(second_example_obj)));
|
|
let res = obj_manager.initialize();
|
|
assert!(res.is_ok());
|
|
assert_eq!(res.unwrap(), 2);
|
|
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"));
|
|
assert!(expl_obj_back_casted.was_initialized);
|
|
|
|
let existing_obj_id = ObjectId {
|
|
id: 12,
|
|
name: "Example 1",
|
|
};
|
|
let invalid_obj = OtherExampleObject {
|
|
id: existing_obj_id,
|
|
string: String::from("Hello Test"),
|
|
was_initialized: false,
|
|
};
|
|
|
|
assert!(!obj_manager.insert(Box::new(invalid_obj)));
|
|
}
|
|
|
|
#[test]
|
|
fn object_man_threaded() {
|
|
let obj_manager = Arc::new(Mutex::new(ObjectManager::new()));
|
|
let expl_obj_id = ObjectId {
|
|
id: 0,
|
|
name: "Example 0",
|
|
};
|
|
let example_obj = ExampleSysObj::new(expl_obj_id, 42);
|
|
let second_obj_id = ObjectId {
|
|
id: 12,
|
|
name: "Example 1",
|
|
};
|
|
let second_example_obj = OtherExampleObject {
|
|
id: second_obj_id,
|
|
string: String::from("Hello Test"),
|
|
was_initialized: false,
|
|
};
|
|
|
|
let mut obj_man_handle = obj_manager.lock().expect("Mutex lock failed");
|
|
assert!(obj_man_handle.insert(Box::new(example_obj)));
|
|
assert!(obj_man_handle.insert(Box::new(second_example_obj)));
|
|
let res = obj_man_handle.initialize();
|
|
std::mem::drop(obj_man_handle);
|
|
assert!(res.is_ok());
|
|
assert_eq!(res.unwrap(), 2);
|
|
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_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);
|
|
assert!(expl_obj_back_casted.was_initialized);
|
|
std::mem::drop(locked_man)
|
|
});
|
|
|
|
let jh1 = thread::spawn(move || {
|
|
let locked_man = obj_manager.lock().expect("Mutex lock failed");
|
|
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"));
|
|
assert!(expl_obj_back_casted.was_initialized);
|
|
std::mem::drop(locked_man)
|
|
});
|
|
jh0.join().expect("Joining thread 0 failed");
|
|
jh1.join().expect("Joining thread 1 failed");
|
|
}
|
|
}
|