sat-rs/satrs-core/src/objects.rs

307 lines
10 KiB
Rust
Raw Normal View History

2022-06-12 21:17:18 +02:00
//! # 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
//!
2022-08-14 19:37:34 +02:00
//! ```rust
2022-06-12 21:17:18 +02:00
//! use std::any::Any;
//! use std::error::Error;
2022-11-20 19:54:14 +01:00
//! use satrs_core::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
2022-06-12 21:17:18 +02:00
//!
//! 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 {
2022-08-20 23:21:36 +02:00
//! type Error = ();
2022-06-12 21:17:18 +02:00
//! fn get_object_id(&self) -> &ObjectId {
//! &self.id
//! }
//!
2022-08-20 23:21:36 +02:00
//! fn initialize(&mut self) -> Result<(), Self::Error> {
2022-06-12 21:17:18 +02:00
//! self.was_initialized = true;
2022-08-14 19:37:34 +02:00
//! Ok(())
2022-06-12 21:17:18 +02:00
//! }
2022-08-14 19:37:34 +02:00
//! }
2022-06-12 21:17:18 +02:00
//!
//! 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));
2022-08-14 19:37:34 +02:00
//! let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get_ref(&obj_id);
2022-06-12 21:17:18 +02:00
//! let example_obj = obj_back_casted.unwrap();
//! assert_eq!(example_obj.id, obj_id);
//! assert_eq!(example_obj.dummy, 42);
//! ```
2022-12-30 23:09:58 +01:00
#[cfg(feature = "alloc")]
2022-08-20 23:21:36 +02:00
use alloc::boxed::Box;
2023-01-03 01:15:17 +01:00
#[cfg(feature = "alloc")]
pub use alloc_mod::*;
#[cfg(feature = "alloc")]
2022-08-14 19:37:34 +02:00
use downcast_rs::Downcast;
2022-12-30 23:09:58 +01:00
#[cfg(feature = "alloc")]
2022-08-20 23:21:36 +02:00
use hashbrown::HashMap;
#[cfg(feature = "std")]
2022-05-15 19:17:07 +02:00
use std::error::Error;
2022-06-12 21:17:18 +02:00
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
2022-05-15 19:17:07 +02:00
pub struct ObjectId {
2022-06-12 21:17:18 +02:00
pub id: u32,
pub name: &'static str,
2022-05-15 19:17:07 +02:00
}
2023-01-03 01:15:17 +01:00
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use super::*;
2022-05-15 19:17:07 +02:00
2023-01-03 01:15:17 +01:00
/// 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);
2022-05-15 19:17:07 +02:00
2023-01-03 01:15:17 +01:00
pub trait ManagedSystemObject: SystemObject + Send {}
downcast_rs::impl_downcast!(ManagedSystemObject assoc Error);
2022-05-15 19:17:07 +02:00
2023-01-03 01:15:17 +01:00
/// 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>>>,
2022-05-15 19:17:07 +02:00
}
2023-01-03 01:15:17 +01:00
#[cfg(feature = "alloc")]
impl<E: 'static> Default for ObjectManager<E> {
fn default() -> Self {
Self::new()
2022-05-15 19:17:07 +02:00
}
}
2023-01-03 01:15:17 +01:00
#[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()
2022-05-15 19:17:07 +02:00
}
2023-01-03 01:15:17 +01:00
/// 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
}
2022-05-15 19:17:07 +02:00
}
2023-01-03 01:15:17 +01:00
Ok(init_success)
2022-05-15 19:17:07 +02:00
}
2023-01-03 01:15:17 +01:00
/// 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>())
}
2022-08-14 19:37:34 +02:00
2023-01-03 01:15:17 +01:00
/// 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>())
}
2022-05-15 19:17:07 +02:00
}
}
#[cfg(test)]
mod tests {
2022-06-16 15:45:22 +02:00
use crate::objects::{ManagedSystemObject, ObjectId, ObjectManager, SystemObject};
2022-08-20 23:21:36 +02:00
use std::boxed::Box;
use std::string::String;
2022-06-11 14:05:20 +02:00
use std::sync::{Arc, Mutex};
use std::thread;
2022-05-15 19:17:07 +02:00
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 {
2022-08-20 23:21:36 +02:00
type Error = ();
2022-05-15 19:17:07 +02:00
fn get_object_id(&self) -> &ObjectId {
&self.id
}
2022-08-20 23:21:36 +02:00
fn initialize(&mut self) -> Result<(), Self::Error> {
2022-05-15 19:17:07 +02:00
self.was_initialized = true;
Ok(())
}
}
impl ManagedSystemObject for ExampleSysObj {}
struct OtherExampleObject {
id: ObjectId,
string: String,
was_initialized: bool,
}
impl SystemObject for OtherExampleObject {
2022-08-20 23:21:36 +02:00
type Error = ();
2022-05-15 19:17:07 +02:00
fn get_object_id(&self) -> &ObjectId {
&self.id
}
2022-08-20 23:21:36 +02:00
fn initialize(&mut self) -> Result<(), Self::Error> {
2022-05-15 19:17:07 +02:00
self.was_initialized = true;
Ok(())
}
}
impl ManagedSystemObject for OtherExampleObject {}
#[test]
fn test_obj_manager_simple() {
2022-06-08 19:42:32 +02:00
let mut obj_manager = ObjectManager::default();
2022-05-15 19:17:07 +02:00
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);
2022-08-14 19:37:34 +02:00
let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get_ref(&expl_obj_id);
2022-05-15 19:17:07 +02:00
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);
2022-08-14 19:37:34 +02:00
let obj_back_casted: Option<&OtherExampleObject> = obj_manager.get_ref(&second_obj_id);
2022-05-15 19:17:07 +02:00
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);
2022-06-08 19:42:32 +02:00
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,
};
2022-06-08 19:43:32 +02:00
assert!(!obj_manager.insert(Box::new(invalid_obj)));
2022-05-15 19:17:07 +02:00
}
2022-06-11 14:05:20 +02:00
#[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");
2022-08-14 19:37:34 +02:00
let obj_back_casted: Option<&ExampleSysObj> = locked_man.get_ref(&expl_obj_id);
2022-06-11 14:05:20 +02:00
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");
2022-08-14 19:37:34 +02:00
let obj_back_casted: Option<&OtherExampleObject> = locked_man.get_ref(&second_obj_id);
2022-06-11 14:05:20 +02:00
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");
}
2022-05-15 19:17:07 +02:00
}