Merge pull request 'Init Event Module' (#4) from events into main

Reviewed-on: rust/launchpad#4
This commit is contained in:
Robin Müller 2022-06-11 12:36:40 +02:00
commit 9119357b7e
11 changed files with 572 additions and 38 deletions

View File

@ -0,0 +1,18 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Clippy Fix" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
<option name="command" value="clippy --fix" />
<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="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>

View File

@ -7,7 +7,6 @@
<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" />

112
Cargo.lock generated
View File

@ -27,7 +27,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1e66e1779f5b1440f1a58220ba3b3ded4427175f0a9fb8d7066521f8b4e8f2b"
dependencies = [
"atomic-option",
"crossbeam-channel 0.4.4",
"crossbeam-channel",
"num_cpus",
"parking_lot_core",
]
@ -38,12 +38,6 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cloudabi"
version = "0.0.3"
@ -59,20 +53,10 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
dependencies = [
"crossbeam-utils 0.7.2",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils 0.8.8",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
@ -80,17 +64,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if 0.1.10",
"lazy_static",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"lazy_static",
]
@ -108,7 +82,7 @@ name = "launchpad"
version = "0.1.0"
dependencies = [
"bus",
"crossbeam-channel 0.5.4",
"num",
"thiserror",
]
@ -130,6 +104,82 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "num"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97fbc387afefefd5e9e39493299f3069e14a140dd34dc19b4c1c1a8fddb6a790"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.1"
@ -146,7 +196,7 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
dependencies = [
"cfg-if 0.1.10",
"cfg-if",
"cloudabi",
"libc",
"redox_syscall",

View File

@ -8,4 +8,4 @@ edition = "2021"
[dependencies]
thiserror = "1.0"
bus = "2.2.3"
crossbeam-channel = "0.5.4"
num = "0.4"

View File

@ -1,3 +1,13 @@
//! # Core components of the Flight Software Rust Crate (FSRC) collection.
//!
//! This includes components to perform the following tasks
//!
//! 1. Object Management with the [objects] module
//! 2. Task schedule with the [executable] module
//! 3. Events with the [events] module and event management with the [event_man] module
//! 4. Pre-Allocated memory pools with the [pool] module
pub mod event_man;
pub mod events;
pub mod executable;
pub mod objects;
pub mod pool;

310
src/core/event_man.rs Normal file
View File

@ -0,0 +1,310 @@
use crate::core::events::{Event, EventRaw, GroupId};
use std::collections::HashMap;
#[derive(PartialEq, Eq, Hash, Copy, Clone)]
enum ListenerType {
Single(EventRaw),
Group(GroupId),
}
pub trait EventListener {
type Error;
fn id(&self) -> u32;
fn send_to(&mut self, event: Event) -> Result<(), Self::Error>;
}
struct Listener<E> {
ltype: ListenerType,
dest: Box<dyn EventListener<Error = E>>,
}
pub trait ReceivesAllEvent {
fn receive(&mut self) -> Option<Event>;
}
pub struct EventManager<E> {
listeners: HashMap<ListenerType, Vec<Listener<E>>>,
event_receiver: Box<dyn ReceivesAllEvent>,
}
pub enum HandlerResult {
Empty,
Handled(u32, Event),
}
impl<E> EventManager<E> {
pub fn new(event_receiver: Box<dyn ReceivesAllEvent>) -> Self {
EventManager {
listeners: HashMap::new(),
event_receiver,
}
}
pub fn subscribe_single(
&mut self,
event: Event,
dest: impl EventListener<Error = E> + 'static,
) {
self.update_listeners(ListenerType::Single(event.raw()), dest);
}
pub fn subscribe_group(
&mut self,
group_id: GroupId,
dest: impl EventListener<Error = E> + 'static,
) {
self.update_listeners(ListenerType::Group(group_id), dest);
}
fn update_listeners(
&mut self,
key: ListenerType,
dest: impl EventListener<Error = E> + 'static,
) {
if let std::collections::hash_map::Entry::Vacant(e) = self.listeners.entry(key) {
e.insert(vec![Listener {
ltype: key,
dest: Box::new(dest),
}]);
} else {
let vec = self.listeners.get_mut(&key).unwrap();
// To prevent double insertions
for entry in vec.iter() {
if entry.ltype == key && entry.dest.id() == dest.id() {
return;
}
}
vec.push(Listener {
ltype: key,
dest: Box::new(dest),
});
}
}
pub fn try_event_handling(&mut self) -> Result<HandlerResult, E> {
let mut err_status = None;
let mut num_recipients = 0;
let mut send_handler = |event, llist: &mut Vec<Listener<E>>| {
for listener in llist.iter_mut() {
if let Err(e) = listener.dest.send_to(event) {
err_status = Some(Err(e));
} else {
num_recipients += 1;
}
}
};
if let Some(event) = self.event_receiver.receive() {
let single_key = ListenerType::Single(event.raw());
if self.listeners.contains_key(&single_key) {
send_handler(event, self.listeners.get_mut(&single_key).unwrap());
}
let group_key = ListenerType::Group(event.group_id());
if self.listeners.contains_key(&group_key) {
send_handler(event, self.listeners.get_mut(&group_key).unwrap());
}
if let Some(err) = err_status {
return err;
}
return Ok(HandlerResult::Handled(num_recipients, event));
}
Ok(HandlerResult::Empty)
}
}
#[cfg(test)]
mod tests {
use super::{EventListener, HandlerResult, ReceivesAllEvent};
use crate::core::event_man::EventManager;
use crate::core::events::{Event, Severity};
use std::sync::mpsc::{channel, Receiver, SendError, Sender};
use std::thread;
use std::time::Duration;
struct EventReceiver {
mpsc_receiver: Receiver<Event>,
}
impl ReceivesAllEvent for EventReceiver {
fn receive(&mut self) -> Option<Event> {
self.mpsc_receiver.try_recv().ok()
}
}
#[derive(Clone)]
struct MpscEventSenderQueue {
id: u32,
mpsc_sender: Sender<Event>,
}
impl EventListener for MpscEventSenderQueue {
type Error = SendError<Event>;
fn id(&self) -> u32 {
self.id
}
fn send_to(&mut self, event: Event) -> Result<(), Self::Error> {
self.mpsc_sender.send(event)
}
}
fn check_next_event(expected: Event, receiver: &Receiver<Event>) {
for _ in 0..5 {
if let Ok(event) = receiver.try_recv() {
assert_eq!(event, expected);
break;
}
thread::sleep(Duration::from_millis(1));
}
}
fn check_handled_event(res: HandlerResult, expected: Event, expected_num_sent: u32) {
assert!(matches!(res, HandlerResult::Handled { .. }));
if let HandlerResult::Handled(num_recipients, event) = res {
assert_eq!(event, expected);
assert_eq!(num_recipients, expected_num_sent);
}
}
#[test]
fn test_basic() {
let (event_sender, manager_queue) = channel();
let event_man_receiver = EventReceiver {
mpsc_receiver: manager_queue,
};
let mut event_man: EventManager<SendError<Event>> =
EventManager::new(Box::new(event_man_receiver));
let event_grp_0 = Event::new(Severity::INFO, 0, 0).unwrap();
let event_grp_1_0 = Event::new(Severity::HIGH, 1, 0).unwrap();
let (single_event_sender, single_event_receiver) = channel();
let single_event_listener = MpscEventSenderQueue {
id: 0,
mpsc_sender: single_event_sender,
};
event_man.subscribe_single(event_grp_0, single_event_listener);
let (group_event_sender_0, group_event_receiver_0) = channel();
let group_event_listener = MpscEventSenderQueue {
id: 1,
mpsc_sender: group_event_sender_0,
};
event_man.subscribe_group(event_grp_1_0.group_id(), group_event_listener);
// Test event with one listener
event_sender
.send(event_grp_0)
.expect("Sending single error failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_grp_0, 1);
check_next_event(event_grp_0, &single_event_receiver);
// Test event which is sent to all group listeners
event_sender
.send(event_grp_1_0)
.expect("Sending group error failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_grp_1_0, 1);
check_next_event(event_grp_1_0, &group_event_receiver_0);
}
/// Test listening for multiple groups
#[test]
fn test_multi_group() {
let (event_sender, manager_queue) = channel();
let event_man_receiver = EventReceiver {
mpsc_receiver: manager_queue,
};
let mut event_man: EventManager<SendError<Event>> =
EventManager::new(Box::new(event_man_receiver));
let res = event_man.try_event_handling();
assert!(res.is_ok());
let hres = res.unwrap();
assert!(matches!(hres, HandlerResult::Empty));
let event_grp_0 = Event::new(Severity::INFO, 0, 0).unwrap();
let event_grp_1_0 = Event::new(Severity::HIGH, 1, 0).unwrap();
let (event_grp_0_sender, event_grp_0_receiver) = channel();
let event_grp_0_and_1_listener = MpscEventSenderQueue {
id: 0,
mpsc_sender: event_grp_0_sender,
};
event_man.subscribe_group(event_grp_0.group_id(), event_grp_0_and_1_listener.clone());
event_man.subscribe_group(event_grp_1_0.group_id(), event_grp_0_and_1_listener);
event_sender
.send(event_grp_0)
.expect("Sending Event Group 0 failed");
event_sender
.send(event_grp_1_0)
.expect("Sendign Event Group 1 failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_grp_0, 1);
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_grp_1_0, 1);
check_next_event(event_grp_0, &event_grp_0_receiver);
check_next_event(event_grp_1_0, &event_grp_0_receiver);
}
/// Test listening to the same event from multiple listeners. Also test listening
/// to both group and single events from one listener
#[test]
fn test_listening_to_same_event_and_multi_type() {
let (event_sender, manager_queue) = channel();
let event_man_receiver = EventReceiver {
mpsc_receiver: manager_queue,
};
let mut event_man: EventManager<SendError<Event>> =
EventManager::new(Box::new(event_man_receiver));
let event_0 = Event::new(Severity::INFO, 0, 5).unwrap();
let event_1 = Event::new(Severity::HIGH, 1, 0).unwrap();
let (event_0_tx_0, event_0_rx_0) = channel();
let (event_0_tx_1, event_0_rx_1) = channel();
let event_listener_0 = MpscEventSenderQueue {
id: 0,
mpsc_sender: event_0_tx_0,
};
let event_listener_1 = MpscEventSenderQueue {
id: 1,
mpsc_sender: event_0_tx_1,
};
event_man.subscribe_single(event_0, event_listener_0.clone());
event_man.subscribe_single(event_0, event_listener_1);
event_sender
.send(event_0)
.expect("Triggering Event 0 failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_0, 2);
check_next_event(event_0, &event_0_rx_0);
check_next_event(event_0, &event_0_rx_1);
event_man.subscribe_group(event_1.group_id(), event_listener_0.clone());
event_sender
.send(event_0)
.expect("Triggering Event 0 failed");
event_sender
.send(event_1)
.expect("Triggering Event 1 failed");
// 3 Events messages will be sent now
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_0, 2);
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_1, 1);
// Both the single event and the group event should arrive now
check_next_event(event_0, &event_0_rx_0);
check_next_event(event_1, &event_0_rx_0);
// Double insertion should be detected, result should remain the same
event_man.subscribe_group(event_1.group_id(), event_listener_0);
event_sender
.send(event_1)
.expect("Triggering Event 1 failed");
let res = event_man.try_event_handling();
assert!(res.is_ok());
check_handled_event(res.unwrap(), event_1, 1);
}
}

View File

@ -1,3 +1,132 @@
pub struct Event {
pub event_id: u32,
use num::pow;
pub type GroupId = u16;
pub type UniqueId = u16;
pub type EventRaw = u32;
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum Severity {
INFO = 1,
LOW = 2,
MEDIUM = 3,
HIGH = 4,
}
impl TryFrom<u8> for Severity {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
x if x == Severity::INFO as u8 => Ok(Severity::INFO),
x if x == Severity::LOW as u8 => Ok(Severity::LOW),
x if x == Severity::MEDIUM as u8 => Ok(Severity::MEDIUM),
x if x == Severity::HIGH as u8 => Ok(Severity::HIGH),
_ => Err(()),
}
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Event {
severity: Severity,
group_id: GroupId,
unique_id: UniqueId,
}
impl Event {
/// Generate an event. The raw representation of an event has 32 bits.
/// If the passed group ID is invalid (too large), None wil be returned
///
/// # Parameter
///
/// `severity`: Each event has a [severity][Severity]. The raw value of the severity will
/// be stored inside the uppermost 3 bits of the raw event ID
/// `group_id`: Related events can be grouped using a group ID. The group ID will occupy the
/// next 13 bits after the severity. Therefore, the size is limited by dec 8191 hex 0x1FFF.
/// `unique_id`: Each event has a unique 16 bit ID occupying the last 16 bits of the
/// raw event ID
pub fn new(severity: Severity, group_id: GroupId, unique_id: UniqueId) -> Option<Event> {
if group_id > (pow::pow(2u8 as u16, 13) - 1) {
return None;
}
Some(Event {
severity,
group_id,
unique_id,
})
}
/// Retrieve the severity of an event. Returns None if that severity bit field of the raw event
/// ID is invalid
pub fn severity(&self) -> Severity {
self.severity
}
pub fn group_id(&self) -> GroupId {
self.group_id
}
pub fn unique_id(&self) -> UniqueId {
self.unique_id
}
pub fn raw(&self) -> EventRaw {
(((self.severity as u32) << 29) as u32
| ((self.group_id as u32) << 16) as u32
| self.unique_id as u32) as EventRaw
}
}
impl TryFrom<EventRaw> for Event {
type Error = ();
fn try_from(raw: u32) -> Result<Self, Self::Error> {
let severity: Option<Severity> = (((raw >> 29) & 0b111) as u8).try_into().ok();
if severity.is_none() {
return Err(());
}
let group_id = ((raw >> 16) & 0x1FFF) as u16;
let unique_id = (raw & 0xFFFF) as u16;
Event::new(severity.unwrap(), group_id, unique_id).ok_or(())
}
}
#[cfg(test)]
mod tests {
use super::Event;
use crate::core::events::Severity;
#[test]
fn test_events() {
let event = Event::new(Severity::INFO, 0, 0).unwrap();
assert_eq!(event.severity(), Severity::INFO);
assert_eq!(event.unique_id(), 0);
assert_eq!(event.group_id(), 0);
let raw_event = event.raw();
assert_eq!(raw_event, 0x20000000);
let conv_from_raw = Event::try_from(raw_event);
assert!(conv_from_raw.is_ok());
let opt_event = conv_from_raw.ok();
assert!(opt_event.is_some());
let event = opt_event.unwrap();
assert_eq!(event.severity(), Severity::INFO);
assert_eq!(event.unique_id(), 0);
assert_eq!(event.group_id(), 0);
let event = Event::new(Severity::HIGH, 0x1FFF, 0xFFFF).unwrap();
assert_eq!(event.severity(), Severity::HIGH);
assert_eq!(event.group_id(), 0x1FFF);
assert_eq!(event.unique_id(), 0xFFFF);
let raw_event = event.raw();
assert_eq!(raw_event, 0x9FFFFFFF);
let conv_from_raw = Event::try_from(raw_event);
assert!(conv_from_raw.is_ok());
let opt_event = conv_from_raw.ok();
assert!(opt_event.is_some());
let event = opt_event.unwrap();
assert_eq!(event.severity(), Severity::HIGH);
assert_eq!(event.group_id(), 0x1FFF);
assert_eq!(event.unique_id(), 0xFFFF);
}
}

View File

@ -80,7 +80,7 @@ pub fn exec_sched_single<
///
/// * `executable_vec`: Vector of executable objects
/// * `task_freq`: Optional frequency of task. Required for periodic and fixed cycle tasks
/// * `op_code`: Operation code which is passed to the executable task periodic_op call
/// * `op_code`: Operation code which is passed to the executable task [operation call][Executable::periodic_op]
/// * `termination`: Optional termination handler which can cancel threads with a broadcast
pub fn exec_sched_multi<
T: Executable<Error = E> + Send + 'static + ?Sized,

View File

@ -8,6 +8,8 @@ pub struct ObjectId {
name: &'static str,
}
/// Each object which is stored inside the [object manager][ObjectManager] needs to implemented
/// this trait
pub trait SystemObject {
fn as_any(&self) -> &dyn Any;
fn get_object_id(&self) -> &ObjectId;
@ -16,7 +18,21 @@ pub trait SystemObject {
pub trait ManagedSystemObject: SystemObject + Any {}
/// Helper module to manage multiple SystemObject by mapping them using an ObjectId
/// Helper module to manage multiple [ManagedSystemObjects][ManagedSystemObject] by mapping them
/// using an [object ID][ObjectId]
///
/// # Example
/// ```rs
/// 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);
/// obj_manager.insert(Box::new(example_obj))
/// let obj_back_casted: Option<&ExampleSysObj> = obj_manager.get(&expl_obj_id);
/// let expl_obj_back_casted = obj_back_casted.unwrap();
/// ```
pub struct ObjectManager {
obj_map: HashMap<ObjectId, Box<dyn ManagedSystemObject>>,
}
@ -53,6 +69,8 @@ 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> {
self.obj_map
.get(key)

0
src/core/pool.rs Normal file
View File

View File

@ -1,3 +1,3 @@
fn main() {
println!("hello");
println!("Hello World")
}