From 7aec5819c6c5e9ef2f604e2f675e62d127917875 Mon Sep 17 00:00:00 2001 From: Ulrich Mohr Date: Wed, 6 Dec 2023 16:39:55 +0100 Subject: [PATCH] rust modules --- mission_rust/CMakeLists.txt | 2 + mission_rust/src/fsrc/mod.rs | 4 + mission_rust/src/fsrc/osal/mod.rs | 25 +++ mission_rust/src/fsrc/queues/mod.rs | 88 +++++++++ mission_rust/src/fsrc/sif.rs | 35 ++++ mission_rust/src/fsrc/tasks/mod.rs | 102 ++++++++++ mission_rust/src/lib.rs | 295 +++------------------------- 7 files changed, 282 insertions(+), 269 deletions(-) create mode 100644 mission_rust/src/fsrc/mod.rs create mode 100644 mission_rust/src/fsrc/osal/mod.rs create mode 100644 mission_rust/src/fsrc/queues/mod.rs create mode 100644 mission_rust/src/fsrc/sif.rs create mode 100644 mission_rust/src/fsrc/tasks/mod.rs diff --git a/mission_rust/CMakeLists.txt b/mission_rust/CMakeLists.txt index 7093c55..217aba8 100644 --- a/mission_rust/CMakeLists.txt +++ b/mission_rust/CMakeLists.txt @@ -1,3 +1,5 @@ +#TODO can we get CMake to configure cmake --build --clean to run cargo clean? + add_custom_target( mission_rust_internal COMMAND cargo build $<$:--release> diff --git a/mission_rust/src/fsrc/mod.rs b/mission_rust/src/fsrc/mod.rs new file mode 100644 index 0000000..1ba25a8 --- /dev/null +++ b/mission_rust/src/fsrc/mod.rs @@ -0,0 +1,4 @@ +pub mod sif; +pub mod queues; +pub mod osal; +pub mod tasks; \ No newline at end of file diff --git a/mission_rust/src/fsrc/osal/mod.rs b/mission_rust/src/fsrc/osal/mod.rs new file mode 100644 index 0000000..e80b241 --- /dev/null +++ b/mission_rust/src/fsrc/osal/mod.rs @@ -0,0 +1,25 @@ +type TaskFunction = unsafe extern "C" fn(*mut cty::c_void); + +extern "C" { + pub fn outbyte(c: cty::c_char); + //void *create_task(TaskFunction_t taskFunction, void *parameter, size_t stack_size) + pub fn create_task( + taskFunction: TaskFunction, + parameter: *const cty::c_void, + stack_size: cty::size_t, + ) -> *const cty::c_void; + + pub fn get_task_name() -> *const core::ffi::c_uchar; + + pub fn stop_it(); + + pub fn delete_task(handle: *const cty::c_void); + + pub fn task_delay(milliseconds: cty::uint32_t); + + //void *create_queue(size_t length, size_t element_size) + pub fn create_queue(length: cty::size_t, element_size: cty::size_t) -> *const cty::c_void; + + pub fn queue_receive(queue: *const cty::c_void, message: *const cty::c_void) -> cty::uint8_t; + pub fn queue_send(queue: *const cty::c_void, message: *const cty::c_void) -> cty::uint8_t; +} \ No newline at end of file diff --git a/mission_rust/src/fsrc/queues/mod.rs b/mission_rust/src/fsrc/queues/mod.rs new file mode 100644 index 0000000..cd78616 --- /dev/null +++ b/mission_rust/src/fsrc/queues/mod.rs @@ -0,0 +1,88 @@ +pub struct MessageQueue { + queue_id: *const cty::c_void, + _unused: Option, //need to constrain the queue to one message type for safety, but compiler needs that to be used +} + +pub struct MessageQueueSender { + queue_id: Option<*const cty::c_void>, + _unused: Option, //need to constrain the sender to one message type for safety, but compiler needs that to be used +} + +impl MessageQueue { + pub fn new(depth: usize) -> Self { + let mut instance: Self; + unsafe { + instance = Self { + queue_id: 0 as *const cty::c_void, + _unused: None, + }; + //TODO check cast of depth + instance.queue_id = crate::fsrc::osal::create_queue(depth, core::mem::size_of::()); + + if instance.queue_id == 0 as *mut cty::c_void { + panic!("could not create Queue"); + } + instance + } + } + + pub fn get_sender(&self) -> MessageQueueSender { + let instance: MessageQueueSender = MessageQueueSender:: { + queue_id: Some(self.queue_id), + _unused: None, + }; + instance + } + + pub fn receive(&self) -> Result { + let mut message: T = T::default(); + let res: cty::uint8_t; + unsafe { + //message = core::mem::MaybeUninit::zeroed().assume_init(); // We only return it if the queue received something + let message_pointer: *mut cty::c_void = &mut message as *mut _ as *mut cty::c_void; + res = crate::fsrc::osal::queue_receive(self.queue_id, message_pointer); + } + if res == 1 { + Ok(message) + } else { + Err(()) + } + } +} + +impl MessageQueueSender { + pub fn new() -> Self { + Self { + queue_id: None, + _unused: None, + } + } + + pub fn send(&self, message: T) -> Result<(), ()> { + let queue_id = self.queue_id.expect("unitialized Message Queue"); + let res: cty::uint8_t; + unsafe { + let message_pointer: *const cty::c_void = &message as *const _ as *const cty::c_void; + res = crate::fsrc::osal::queue_send(queue_id, message_pointer); + } + if res == 1 { + Ok(()) + } else { + Err(()) + } + } +} + +#[derive(Clone, Copy)] +pub struct GenericMessageData { + pub p1: u32, + pub p2: u32, +} + +#[derive(Copy, Clone, Default)] +pub enum Message { + OK, + #[default] + FAILED, + DATA(GenericMessageData), +} \ No newline at end of file diff --git a/mission_rust/src/fsrc/sif.rs b/mission_rust/src/fsrc/sif.rs new file mode 100644 index 0000000..a1801a3 --- /dev/null +++ b/mission_rust/src/fsrc/sif.rs @@ -0,0 +1,35 @@ +pub struct Outbytes {} + +use core::fmt::{Error, Write}; + +impl Write for Outbytes { + fn write_str(&mut self, s: &str) -> Result<(), Error> { + for c in s.as_bytes() { + unsafe { + crate::fsrc::osal::outbyte(*c); + } + } + Ok(()) + } +} + +#[macro_export] +macro_rules! sifln { + ($(,)?) => ( + let mut stdout = Outbytes {}; + writeln!(stdout); + ); + ($($arg:tt)*) => ( + let mut stdout = crate::fsrc::sif::Outbytes {}; + let _alwaysok = writeln!(stdout, $($arg)*); + ); +} + +#[macro_export] +macro_rules! sif { + ($($arg:tt)*) => ( + let mut stdout = crate::fsrc::sif::Outbytes {}; + let _alwaysok = write!(stdout, $($arg)*); + ); +} + diff --git a/mission_rust/src/fsrc/tasks/mod.rs b/mission_rust/src/fsrc/tasks/mod.rs new file mode 100644 index 0000000..878540c --- /dev/null +++ b/mission_rust/src/fsrc/tasks/mod.rs @@ -0,0 +1,102 @@ +#[no_mangle] +extern "C" fn task_entry(task_object: *mut cty::c_void) { + let task: &mut dyn TaskIF; + unsafe { + let pointer = task_object as *mut PeriodicTask; + task = &mut *pointer; + } + task.run(); +} + +pub trait ExecutableObjectIF { + fn perform(&mut self); +} + +pub trait TaskIF { + fn run(&mut self); + fn get_stack_size(&self) -> cty::size_t; + fn set_handle(&mut self, task_handle: *const cty::c_void); + fn get_handle(&self) -> *const cty::c_void; +} + +pub struct PeriodicTask<'a> { + pub stack_size: cty::size_t, //TODO generic type and safety + pub task_handle: *const cty::c_void, + pub period: usize, + pub task_objects: &'a mut [&'a mut dyn ExecutableObjectIF], +} + +impl<'a> PeriodicTask<'a> { + pub fn new( + objects: &'a mut [&'a mut dyn ExecutableObjectIF], + stack_size: usize, + period: usize, + ) -> PeriodicTask<'a> { + let instance: PeriodicTask<'a> = Self { + stack_size: stack_size, + task_handle: 0 as *const cty::c_void, + period: period, + task_objects: objects, + }; + instance + } +} + +impl<'a> TaskIF for PeriodicTask<'a> { + fn run(&mut self) { + loop { + for object in self.task_objects.iter_mut() { + object.perform(); + } + //TODO make this exact + unsafe { + crate::fsrc::osal::task_delay(self.period as cty::uint32_t); //TODO type of delay should be generic but safe (cap to max in C) + } + } + } + fn get_stack_size(&self) -> cty::size_t { + self.stack_size + } + fn set_handle(&mut self, task_handle: *const cty::c_void) { + self.task_handle = task_handle; + } + fn get_handle(&self) -> *const cty::c_void { + self.task_handle + } +} + +pub struct TaskExecutor<'a> { + pub tasks: &'a mut [&'a mut dyn TaskIF], +} + +impl<'a> TaskExecutor<'a> { + pub fn run_tasks(&mut self) { + for task in self.tasks.iter_mut() { + // we give away a raw pointer, to be called by an OS task + // while this is generally very broken, we use a reference tied + // to our own lifetime and destroy the task when we get dropped + // this way, the reference is guaranteed to be valid over our + // lifetime while the task is deleted at the end of our lifetime + let task_pointer: *const cty::c_void = *task as *mut _ as *const cty::c_void; //TODO this does work without the "*" in front of the task -> Why?? + let handle; + unsafe { + handle = crate::fsrc::osal::create_task(task_entry, task_pointer, task.get_stack_size()); + } + if handle == 0 as *mut cty::c_void { + panic!("could not create Task"); + } else { + task.set_handle(handle); + } + } + } +} + +impl<'a> Drop for TaskExecutor<'a> { + fn drop(&mut self) { + for task in self.tasks.iter_mut() { + unsafe { + crate::fsrc::osal::delete_task(task.get_handle()); + } + } + } +} \ No newline at end of file diff --git a/mission_rust/src/lib.rs b/mission_rust/src/lib.rs index 2103732..68bd7d8 100644 --- a/mission_rust/src/lib.rs +++ b/mission_rust/src/lib.rs @@ -4,38 +4,23 @@ //TODO os errors in API calls //TODO look into a pattern for late initialized stuff, currently using Option (can we make it compile time safe?) -#[macro_export] -macro_rules! sifln { - ($(,)?) => ( - let mut stdout = Outbytes {}; - writeln!(stdout); - ); - ($($arg:tt)*) => ( - let mut stdout = Outbytes {}; - let _alwaysok = writeln!(stdout, $($arg)*); - ); -} - -#[macro_export] -macro_rules! sif { - ($($arg:tt)*) => ( - let mut stdout = Outbytes {}; - let _alwaysok = write!(stdout, $($arg)*); - ); -} +mod fsrc; use core::mem::size_of; use core::panic::PanicInfo; +use core::fmt::Write; + +use fsrc::*; #[panic_handler] fn panic(panic: &PanicInfo<'_>) -> ! { unsafe { - stop_it(); + osal::stop_it(); } // TODO: Make this unicode-safe sif!("In Task \""); unsafe { - let task_name = get_task_name(); + let task_name = osal::get_task_name(); let mut offset = 0; while *task_name.offset(offset) != 0 { sif!("{}", *task_name.offset(offset) as char); @@ -48,31 +33,7 @@ fn panic(panic: &PanicInfo<'_>) -> ! { loop {} } -type TaskFunction = unsafe extern "C" fn(*mut cty::c_void); -extern "C" { - fn outbyte(c: cty::c_char); - //void *create_task(TaskFunction_t taskFunction, void *parameter, size_t stack_size) - fn create_task( - taskFunction: TaskFunction, - parameter: *const cty::c_void, - stack_size: cty::size_t, - ) -> *const cty::c_void; - - fn get_task_name() -> *const core::ffi::c_uchar; - - fn stop_it(); - - fn delete_task(handle: *const cty::c_void); - - fn task_delay(milliseconds: cty::uint32_t); - - //void *create_queue(size_t length, size_t element_size) - fn create_queue(length: cty::size_t, element_size: cty::size_t) -> *const cty::c_void; - - fn queue_receive(queue: *const cty::c_void, message: *const cty::c_void) -> cty::uint8_t; - fn queue_send(queue: *const cty::c_void, message: *const cty::c_void) -> cty::uint8_t; -} #[no_mangle] extern "C" fn rust_main() { @@ -81,137 +42,35 @@ extern "C" fn rust_main() { sifln!("Mission done"); } -#[no_mangle] -extern "C" fn task_entry(task_object: *mut cty::c_void) { - let task: &mut dyn TaskIF; - unsafe { - let pointer = task_object as *mut PeriodicTask; - task = &mut *pointer; - } - task.run(); -} - -trait ExecutableObjectIF { - fn perform(&mut self); -} - -trait TaskIF { - fn run(&mut self); - fn get_stack_size(&self) -> cty::size_t; - fn set_handle(&mut self, task_handle: *const cty::c_void); - fn get_handle(&self) -> *const cty::c_void; -} - -struct PeriodicTask<'a> { - stack_size: cty::size_t, //TODO generic type and safety - task_handle: *const cty::c_void, - period: usize, - task_objects: &'a mut [&'a mut dyn ExecutableObjectIF], -} - -impl<'a> PeriodicTask<'a> { - fn new( - objects: &'a mut [&'a mut dyn ExecutableObjectIF], - stack_size: usize, - period: usize, - ) -> PeriodicTask<'a> { - let instance: PeriodicTask<'a> = Self { - stack_size: stack_size, - task_handle: 0 as *const cty::c_void, - period: period, - task_objects: objects, - }; - instance - } -} - -impl<'a> TaskIF for PeriodicTask<'a> { - fn run(&mut self) { - loop { - for object in self.task_objects.iter_mut() { - object.perform(); - } - //TODO make this exact - unsafe { - task_delay(self.period as cty::uint32_t); //TODO type of delay should be generic but safe (cap to max in C) - } - } - } - fn get_stack_size(&self) -> cty::size_t { - self.stack_size - } - fn set_handle(&mut self, task_handle: *const cty::c_void) { - self.task_handle = task_handle; - } - fn get_handle(&self) -> *const cty::c_void { - self.task_handle - } -} - -struct TaskExecutor<'a> { - tasks: &'a mut [&'a mut dyn TaskIF], -} - -impl<'a> TaskExecutor<'a> { - fn run_tasks(&mut self) { - for task in self.tasks.iter_mut() { - // we give away a raw pointer, to be called by an OS task - // while this is generally very broken, we use a reference tied - // to our own lifetime and destroy the task when we get dropped - // this way, the reference is guaranteed to be valid over our - // lifetime while the task is deleted at the end of our lifetime - let task_pointer: *const cty::c_void = *task as *mut _ as *const cty::c_void; //TODO this does work without the "*" in front of the task -> Why?? - let handle; - unsafe { - handle = create_task(task_entry, task_pointer, task.get_stack_size()); - } - if handle == 0 as *mut cty::c_void { - panic!("could not create Task"); - } else { - task.set_handle(handle); - } - } - } -} - -impl<'a> Drop for TaskExecutor<'a> { - fn drop(&mut self) { - for task in self.tasks.iter_mut() { - unsafe { - delete_task(task.get_handle()); - } - } - } -} struct Handler { id: u32, - command_queue: MessageQueue, + command_queue: queues::MessageQueue, } struct HandlerSender { id: u32, cycle: u8, - other_handler: MessageQueueSender, + other_handler: queues::MessageQueueSender, } impl Handler { - fn handle_message(&self, message: Message) { + fn handle_message(&self, message: queues::Message) { match message { - Message::OK => { + queues::Message::OK => { sifln!("OK"); } - Message::FAILED => { + queues::Message::FAILED => { sifln!("FAILED"); } - Message::DATA(data) => { + queues::Message::DATA(data) => { sifln!("p1: {}, p2 {}", data.p1, data.p2); } } } } -impl ExecutableObjectIF for Handler { +impl tasks::ExecutableObjectIF for Handler { fn perform(&mut self) { sifln!("Handler {} performs", self.id); let result = self.command_queue.receive(); @@ -224,20 +83,20 @@ impl ExecutableObjectIF for Handler { } } -impl ExecutableObjectIF for HandlerSender { +impl tasks::ExecutableObjectIF for HandlerSender { fn perform(&mut self) { sifln!("HandlerSender {} performs step {}", self.id, self.cycle); match self.cycle { 0 => { - let _ = self.other_handler.send(Message::OK); + let _ = self.other_handler.send(queues::Message::OK); } 1 => { - let _ = self.other_handler.send(Message::FAILED); + let _ = self.other_handler.send(queues::Message::FAILED); } 2 => { let _ = self .other_handler - .send(Message::DATA(GenericMessageData { p1: 13, p2: 2 })); + .send(queues::Message::DATA(queues::GenericMessageData { p1: 13, p2: 2 })); } _ => (), } @@ -245,129 +104,27 @@ impl ExecutableObjectIF for HandlerSender { } } -struct MessageQueue { - queue_id: *const cty::c_void, - _unused: Option, //need to constrain the queue to one message type for safety, but compiler needs that to be used -} - -struct MessageQueueSender { - queue_id: Option<*const cty::c_void>, - _unused: Option, //need to constrain the sender to one message type for safety, but compiler needs that to be used -} - -impl MessageQueue { - fn new(depth: usize) -> Self { - let mut instance: Self; - unsafe { - instance = Self { - queue_id: 0 as *const cty::c_void, - _unused: None, - }; - //TODO check cast of depth - instance.queue_id = create_queue(depth, core::mem::size_of::()); - - if instance.queue_id == 0 as *mut cty::c_void { - panic!("could not create Queue"); - } - instance - } - } - - fn get_sender(&self) -> MessageQueueSender { - let instance: MessageQueueSender = MessageQueueSender:: { - queue_id: Some(self.queue_id), - _unused: None, - }; - instance - } - - fn receive(&self) -> Result { - let mut message: T = T::default(); - let res: cty::uint8_t; - unsafe { - //message = core::mem::MaybeUninit::zeroed().assume_init(); // We only return it if the queue received something - let message_pointer: *mut cty::c_void = &mut message as *mut _ as *mut cty::c_void; - res = queue_receive(self.queue_id, message_pointer); - } - if res == 1 { - Ok(message) - } else { - Err(()) - } - } -} - -impl MessageQueueSender { - fn new() -> Self { - Self { - queue_id: None, - _unused: None, - } - } - - fn send(&self, message: T) -> Result<(), ()> { - let queue_id = self.queue_id.expect("unitialized Message Queue"); - let res: cty::uint8_t; - unsafe { - let message_pointer: *const cty::c_void = &message as *const _ as *const cty::c_void; - res = queue_send(queue_id, message_pointer); - } - if res == 1 { - Ok(()) - } else { - Err(()) - } - } -} - -#[derive(Clone, Copy)] -struct GenericMessageData { - p1: u32, - p2: u32, -} - -#[derive(Copy, Clone, Default)] -enum Message { - OK, - #[default] - FAILED, - DATA(GenericMessageData), -} - -struct Outbytes {} - -use core::fmt::{Error, Write}; -impl Write for Outbytes { - fn write_str(&mut self, s: &str) -> Result<(), Error> { - for c in s.as_bytes() { - unsafe { - outbyte(*c); - } - } - Ok(()) - } -} fn mission() { sifln!("Mission enter"); let mut h1 = Handler { id: 1, - command_queue: MessageQueue::new(5), + command_queue: queues::MessageQueue::new(5), }; let mut h2 = HandlerSender { id: 2, cycle: 0, - other_handler: MessageQueueSender::::new(), + other_handler: queues::MessageQueueSender::::new(), }; h2.other_handler = h1.command_queue.get_sender(); - let array: &mut [&mut dyn ExecutableObjectIF] = &mut [&mut h1]; + let array: &mut [&mut dyn tasks::ExecutableObjectIF] = &mut [&mut h1]; - let mut t1 = PeriodicTask::new(array, 512, 200); + let mut t1 = tasks::PeriodicTask::new(array, 512, 200); - let mut t2: PeriodicTask = PeriodicTask { + let mut t2: tasks::PeriodicTask = tasks::PeriodicTask { task_objects: &mut [&mut h2], stack_size: 512, period: 400, @@ -376,9 +133,9 @@ fn mission() { let _i = 1; - sifln!("sizeof {}, pointer struct {:p}, pointer element {:p}, next element{:p}", size_of::(), &t2, t2.task_objects, &_i); + sifln!("sizeof {}, pointer struct {:p}, pointer element {:p}, next element{:p}", size_of::(), &t2, t2.task_objects, &_i); - let mut task_executor = TaskExecutor { + let mut task_executor = tasks::TaskExecutor { tasks: &mut [&mut t1, &mut t2], }; @@ -388,12 +145,12 @@ fn mission() { sifln!("Mission delay"); unsafe { - task_delay(2000); + osal::task_delay(2000); } sifln!("executor dropped"); drop(task_executor); unsafe { - task_delay(2000); + osal::task_delay(2000); } sifln!("Mission delay done"); }