diff --git a/.idea/runConfigurations/Docs.xml b/.idea/runConfigurations/Docs.xml
new file mode 100644
index 0000000..c4efeaa
--- /dev/null
+++ b/.idea/runConfigurations/Docs.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/core/executable.rs b/src/core/executable.rs
index fad7d83..5b8193a 100644
--- a/src/core/executable.rs
+++ b/src/core/executable.rs
@@ -5,6 +5,7 @@ use std::thread;
use std::thread::JoinHandle;
use std::time::Duration;
+#[derive(Debug, PartialEq)]
pub enum OpResult {
Ok,
TerminationRequested,
@@ -24,23 +25,80 @@ pub trait Executable: Send {
fn periodic_op(&mut self, op_code: i32) -> Result;
}
-pub fn executable_scheduler<
+/// This function allows executing one task which implements the [Executable][Executable] trait
+/// # Arguments
+///
+/// * `executable`: Executable task
+/// * `task_freq`: Optional frequency of task. Required for periodic and fixed cycle tasks
+/// * `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_single<
+ T: Executable + Send + 'static + ?Sized,
+ E: Error + Send + 'static,
+>(
+ mut executable: Box,
+ task_freq: Option,
+ op_code: i32,
+ mut termination: Option>,
+) -> JoinHandle> {
+ let mut cycle_count = 0;
+ thread::spawn(move || loop {
+ if let Some(ref mut terminator) = termination {
+ match terminator.try_recv() {
+ Ok(_) | Err(TryRecvError::Disconnected) => {
+ return Ok(OpResult::Ok);
+ }
+ Err(TryRecvError::Empty) => (),
+ }
+ }
+ match executable.exec_type() {
+ ExecutionType::OneShot => {
+ executable.periodic_op(op_code)?;
+ return Ok(OpResult::Ok);
+ }
+ ExecutionType::Infinite => {
+ executable.periodic_op(op_code)?;
+ }
+ ExecutionType::Cycles(cycles) => {
+ executable.periodic_op(op_code)?;
+ cycle_count += 1;
+ if cycle_count == cycles {
+ return Ok(OpResult::Ok);
+ }
+ }
+ }
+ let freq = task_freq.unwrap_or_else(|| panic!("No task frequency specified"));
+ thread::sleep(freq);
+ })
+}
+
+/// This function allows executing multiple tasks as long as the tasks implement the
+/// [Executable][Executable] trait
+/// # Arguments
+///
+/// * `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
+/// * `termination`: Optional termination handler which can cancel threads with a broadcast
+pub fn exec_sched_multi<
T: Executable + Send + 'static + ?Sized,
E: Error + Send + 'static,
>(
mut executable_vec: Vec>,
task_freq: Option,
op_code: i32,
- mut termination: BusReader<()>,
+ mut termination: Option>,
) -> JoinHandle> {
let mut cycle_counts = vec![0; executable_vec.len()];
let mut removal_flags = vec![false; executable_vec.len()];
thread::spawn(move || loop {
- match termination.try_recv() {
- Ok(_) | Err(TryRecvError::Disconnected) => {
- removal_flags.iter_mut().for_each(|x| *x = true);
+ if let Some(ref mut terminator) = termination {
+ match terminator.try_recv() {
+ Ok(_) | Err(TryRecvError::Disconnected) => {
+ removal_flags.iter_mut().for_each(|x| *x = true);
+ }
+ Err(TryRecvError::Empty) => (),
}
- Err(TryRecvError::Empty) => (),
}
for (idx, executable) in executable_vec.iter_mut().enumerate() {
match executable.exec_type() {
@@ -72,3 +130,226 @@ pub fn executable_scheduler<
thread::sleep(freq);
})
}
+
+#[cfg(test)]
+mod tests {
+ use super::{exec_sched_multi, exec_sched_single, Executable, ExecutionType, OpResult};
+ use std::error::Error;
+ use std::fmt;
+ use std::sync::{Arc, Mutex};
+ use std::time::Duration;
+
+ struct TestInfo {
+ exec_num: u32,
+ op_code: i32,
+ }
+ struct OneShotTask {
+ exec_num: Arc>,
+ }
+ struct FixedCyclesTask {
+ cycles: u32,
+ exec_num: Arc>,
+ }
+ struct PeriodicTask {
+ exec_num: Arc>,
+ }
+
+ #[derive(Debug, PartialEq)]
+ struct ExampleError {
+ details: String,
+ code: i32,
+ }
+
+ impl ExampleError {
+ fn new(msg: &str, code: i32) -> ExampleError {
+ ExampleError {
+ details: msg.to_string(),
+ code,
+ }
+ }
+ }
+
+ impl fmt::Display for ExampleError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.details)
+ }
+ }
+
+ impl Error for ExampleError {
+ fn description(&self) -> &str {
+ &self.details
+ }
+ }
+
+ const ONE_SHOT_TASK_NAME: &'static str = "One Shot Task";
+
+ impl Executable for OneShotTask {
+ type Error = ExampleError;
+
+ fn exec_type(&self) -> ExecutionType {
+ ExecutionType::OneShot
+ }
+
+ fn task_name(&self) -> &'static str {
+ return ONE_SHOT_TASK_NAME;
+ }
+
+ fn periodic_op(&mut self, op_code: i32) -> Result {
+ let mut data = self.exec_num.lock().expect("Locking Mutex failed");
+ data.exec_num += 1;
+ data.op_code = op_code;
+ std::mem::drop(data);
+ if op_code >= 0 {
+ Ok(OpResult::Ok)
+ } else {
+ Err(ExampleError::new("One Shot Task Failure", op_code))
+ }
+ }
+ }
+
+ const CYCLE_TASK_NAME: &'static str = "Fixed Cycles Task";
+
+ impl Executable for FixedCyclesTask {
+ type Error = ExampleError;
+
+ fn exec_type(&self) -> ExecutionType {
+ ExecutionType::Cycles(self.cycles)
+ }
+
+ fn task_name(&self) -> &'static str {
+ return CYCLE_TASK_NAME
+ }
+
+ fn periodic_op(&mut self, op_code: i32) -> Result {
+ let mut data = self.exec_num.lock().expect("Locking Mutex failed");
+ data.exec_num += 1;
+ data.op_code = op_code;
+ std::mem::drop(data);
+ if op_code >= 0 {
+ Ok(OpResult::Ok)
+ } else {
+ Err(ExampleError::new("Fixed Cycle Task Failure", op_code))
+ }
+ }
+ }
+
+ impl Executable for PeriodicTask {
+ type Error = ExampleError;
+
+ fn exec_type(&self) -> ExecutionType {
+ ExecutionType::Infinite
+ }
+
+ fn task_name(&self) -> &'static str {
+ "Periodic Task"
+ }
+
+ fn periodic_op(&mut self, op_code: i32) -> Result {
+ let mut data = self.exec_num.lock().expect("Locking Mutex failed");
+ data.exec_num += 1;
+ data.op_code = op_code;
+ std::mem::drop(data);
+ if op_code >= 0 {
+ Ok(OpResult::Ok)
+ } else {
+ Err(ExampleError::new("Example Task Failure", op_code))
+ }
+ }
+ }
+
+ #[test]
+ fn test_simple_one_shot() {
+ let expected_op_code = 42;
+ let shared = Arc::new(Mutex::new(TestInfo {
+ exec_num: 0,
+ op_code: 0,
+ }));
+ let exec_task = OneShotTask {
+ exec_num: shared.clone(),
+ };
+ let task = Box::new(exec_task);
+ let jhandle = exec_sched_single(
+ task,
+ Some(Duration::from_millis(100)),
+ expected_op_code,
+ None,
+ );
+ let thread_res = jhandle.join().expect("One Shot Task failed");
+ assert!(thread_res.is_ok());
+ assert_eq!(thread_res.unwrap(), OpResult::Ok);
+ let data = shared.lock().expect("Locking Mutex failed");
+ assert_eq!(data.exec_num, 1);
+ assert_eq!(data.op_code, expected_op_code);
+ }
+
+ #[test]
+ fn test_simple_multi_one_shot() {
+ let expected_op_code = 43;
+ let shared = Arc::new(Mutex::new(TestInfo {
+ exec_num: 0,
+ op_code: 0,
+ }));
+ let exec_task_0 = OneShotTask {
+ exec_num: shared.clone(),
+ };
+ let exec_task_1 = OneShotTask {
+ exec_num: shared.clone(),
+ };
+ let task_vec = vec![Box::new(exec_task_0), Box::new(exec_task_1)];
+ for task in task_vec.iter() {
+ assert_eq!(task.task_name(), ONE_SHOT_TASK_NAME);
+ }
+ let jhandle = exec_sched_multi(
+ task_vec,
+ Some(Duration::from_millis(100)),
+ expected_op_code,
+ None,
+ );
+ let thread_res = jhandle.join().expect("One Shot Task failed");
+ assert!(thread_res.is_ok());
+ assert_eq!(thread_res.unwrap(), OpResult::Ok);
+ let data = shared.lock().expect("Locking Mutex failed");
+ assert_eq!(data.exec_num, 2);
+ assert_eq!(data.op_code, expected_op_code);
+ }
+
+ #[test]
+ fn test_cycles() {
+ let expected_op_code = 44;
+ let shared = Arc::new(Mutex::new(TestInfo {
+ exec_num: 0,
+ op_code: 0,
+ }));
+ let cycled_task = Box::new(FixedCyclesTask {
+ exec_num: shared.clone(),
+ cycles: 1,
+ });
+ assert_eq!(cycled_task.task_name(), CYCLE_TASK_NAME);
+ let jh = exec_sched_single(cycled_task, Some(Duration::from_millis(100)), expected_op_code, None);
+ let thread_res = jh.join().expect("Cycles Task failed");
+ assert!(thread_res.is_ok());
+ let data = shared.lock().expect("Locking Mutex failed");
+ assert_eq!(thread_res.unwrap(), OpResult::Ok);
+ assert_eq!(data.exec_num, 1);
+ assert_eq!(data.op_code, expected_op_code);
+ }
+
+ #[test]
+ #[ignore]
+ fn test_periodic() {
+ let expected_op_code = 45;
+ let shared = Arc::new(Mutex::new(TestInfo {
+ exec_num: 0,
+ op_code: 0,
+ }));
+ let periodic_task = Box::new(PeriodicTask {
+ exec_num: shared.clone()
+ });
+ let jh = exec_sched_single(periodic_task, Some(Duration::from_millis(50)), expected_op_code, None);
+ let thread_res = jh.join().expect("Periodic Task failed");
+ assert!(thread_res.is_ok());
+ let data = shared.lock().expect("Locking Mutex failed");
+ assert_eq!(thread_res.unwrap(), OpResult::Ok);
+ assert_eq!(data.op_code, expected_op_code);
+ }
+}
diff --git a/src/core/objects.rs b/src/core/objects.rs
index dd51ef2..e339358 100644
--- a/src/core/objects.rs
+++ b/src/core/objects.rs
@@ -124,7 +124,7 @@ mod tests {
#[test]
fn test_obj_manager_simple() {
- let mut obj_manager = ObjectManager::new();
+ let mut obj_manager = ObjectManager::default();
let expl_obj_id = ObjectId {
id: 0,
name: "Example 0",
@@ -159,5 +159,17 @@ mod tests {
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_eq!(obj_manager.insert(Box::new(invalid_obj)), false);
}
}
diff --git a/src/main.rs b/src/main.rs
index f77a51d..7527576 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,162 +1,3 @@
-use bus::{Bus};
-
-use launchpad::core::executable::{executable_scheduler, Executable, ExecutionType, OpResult};
-use std::error::Error;
-use std::fmt;
-
-use std::thread;
-use std::time::Duration;
-
-struct OneShotTask {}
-struct FixedCyclesTask {}
-struct PeriodicTask {}
-
-#[derive(Debug)]
-struct ExampleError {
- details: String,
-}
-
-impl ExampleError {
- fn new(msg: &str) -> ExampleError {
- ExampleError {
- details: msg.to_string(),
- }
- }
-}
-
-impl fmt::Display for ExampleError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.details)
- }
-}
-
-impl Error for ExampleError {
- fn description(&self) -> &str {
- &self.details
- }
-}
-
-impl Executable for OneShotTask {
- type Error = ExampleError;
-
- fn exec_type(&self) -> ExecutionType {
- ExecutionType::OneShot
- }
-
- fn task_name(&self) -> &'static str {
- "One Shot Task"
- }
-
- fn periodic_op(&mut self, op_code: i32) -> Result {
- if op_code >= 0 {
- println!("One-shot operation with operation code {op_code} OK!");
- Ok(OpResult::Ok)
- } else {
- println!("One-shot operation failure by passing op code {op_code}!");
- Err(ExampleError::new("Example Task Failure"))
- }
- }
-}
-
-impl Executable for FixedCyclesTask {
- type Error = ExampleError;
-
- fn exec_type(&self) -> ExecutionType {
- ExecutionType::Cycles(3)
- }
-
- fn task_name(&self) -> &'static str {
- "Fixed Cycles Task"
- }
-
- fn periodic_op(&mut self, op_code: i32) -> Result {
- if op_code >= 0 {
- println!("Fixed-cycle operation with operation code {op_code} OK!");
- Ok(OpResult::Ok)
- } else {
- println!("Fixed-cycle operation failure by passing op code {op_code}!");
- Err(ExampleError::new("Example Task Failure"))
- }
- }
-}
-
-impl Executable for PeriodicTask {
- type Error = ExampleError;
-
- fn exec_type(&self) -> ExecutionType {
- ExecutionType::Infinite
- }
-
- fn task_name(&self) -> &'static str {
- "Periodic Task"
- }
-
- fn periodic_op(&mut self, op_code: i32) -> Result {
- if op_code >= 0 {
- println!("Periodic operation with operation code {op_code} OK!");
- Ok(OpResult::Ok)
- } else {
- println!("Periodic operation failure by passing op code {op_code}!");
- Err(ExampleError::new("Example Task Failure"))
- }
- }
-}
-
-fn test0(term_bus: &mut Bus<()>) {
- let exec_task = OneShotTask {};
- let task_vec = vec![Box::new(exec_task)];
- let jhandle = executable_scheduler(
- task_vec,
- Some(Duration::from_millis(100)),
- 0,
- term_bus.add_rx(),
- );
- let exec_task2 = FixedCyclesTask {};
- let task_vec2: Vec + Send>> =
- vec![Box::new(exec_task2)];
- let jhandle2 = executable_scheduler(
- task_vec2,
- Some(Duration::from_millis(100)),
- 1,
- term_bus.add_rx(),
- );
-
- jhandle
- .join()
- .expect("Joining thread failed")
- .expect("Task failed");
- jhandle2
- .join()
- .expect("Joining thread 2 failed")
- .expect("Task 2 failed");
-}
-
-fn test1(term_bus: &mut Bus<()>) {
- let one_shot_in_vec = OneShotTask {};
- let cycles_in_vec = FixedCyclesTask {};
- let periodic_in_vec = PeriodicTask {};
- let test_vec: Vec>> = vec![
- Box::new(one_shot_in_vec),
- Box::new(cycles_in_vec),
- Box::new(periodic_in_vec),
- ];
- let jhandle3 = executable_scheduler(
- test_vec,
- Some(Duration::from_millis(100)),
- 3,
- term_bus.add_rx(),
- );
- thread::sleep(Duration::from_millis(5000));
- println!("Broadcasting cancel");
- term_bus.broadcast(());
- jhandle3
- .join()
- .expect("Joining thread 3 failed")
- .expect("Task 3 failed");
-}
-
fn main() {
- let mut tx = Bus::new(5);
- test0(&mut tx);
- test1(&mut tx);
+ println!("hello");
}