From f80eb49899365e7599bac3afb3631549e8fae88a Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sat, 18 Sep 2021 12:10:21 +0200 Subject: [PATCH 01/13] fix for empty fsms --- finny_derive/src/codegen.rs | 23 ++++++++++++++++++++--- finny_tests/tests/fsm_minimal.rs | 13 +++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 finny_tests/tests/fsm_minimal.rs diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 360b8fa..9cea4b6 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -202,13 +202,16 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let mut variants = TokenStream::new(); let mut as_ref_str = TokenStream::new(); + let mut i = 0; for (ty, _ev) in fsm.fsm.events.iter() { let ty_str = crate::utils::tokens_to_string(ty); variants.append_all(quote! { #ty ( #ty ), }); as_ref_str.append_all(quote! { #event_enum_ty:: #ty(_) => #ty_str, }); + i += 1; } + for (_sub, state) in submachines { let sub_fsm = FsmTypes::new(&state.ty, &fsm.base.fsm_generics); let sub_fsm_event_ty = sub_fsm.get_fsm_events_ty(); @@ -222,6 +225,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre as_ref_str.append_all(quote! { #event_enum_ty :: #sub_fsm_ty(_) => #sub_fsm_event_ty_str , }); + i += 1; } let mut derives = TokenStream::new(); @@ -230,6 +234,21 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre #[derive(Debug)] }); } + + let as_ref_str = match i { + 0 => { + quote! { + stringify!(#event_enum_ty) + } + }, + _ => { + quote! { + match self { + #as_ref_str + } + } + } + }; let evs = quote! { #[derive(finny::bundled::derive_more::From)] @@ -241,9 +260,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre impl core::convert::AsRef for #event_enum_ty { fn as_ref(&self) -> &'static str { - match self { - #as_ref_str - } + #as_ref_str } } }; diff --git a/finny_tests/tests/fsm_minimal.rs b/finny_tests/tests/fsm_minimal.rs new file mode 100644 index 0000000..517774d --- /dev/null +++ b/finny_tests/tests/fsm_minimal.rs @@ -0,0 +1,13 @@ +use finny::{FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm}; + +#[derive(Default, Debug)] +struct State { + +} + +#[finny_fsm] +fn build_fsm(mut fsm: FsmBuilder) -> BuiltFsm { + fsm.initial_state::(); + fsm.state::(); + fsm.build() +} From 4a48ba120ceabbfa112a1e87654fad834524e77f Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sat, 18 Sep 2021 13:42:28 +0200 Subject: [PATCH 02/13] sharable queue, for std builds --- finny/src/fsm/queue.rs | 75 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/finny/src/fsm/queue.rs b/finny/src/fsm/queue.rs index 145593a..32f39f0 100644 --- a/finny/src/fsm/queue.rs +++ b/finny/src/fsm/queue.rs @@ -53,6 +53,74 @@ mod queue_vec { #[cfg(feature = "std")] pub use self::queue_vec::*; +#[cfg(feature = "std")] +mod queue_vec_shared { + use std::sync::{Arc, Mutex}; + + use crate::FsmError; + + use super::*; + + /// An unbound event queue that uses `VecDeque`. + pub struct FsmEventQueueVecShared { + inner: Inner + } + + impl Clone for FsmEventQueueVecShared where F: FsmBackend { + fn clone(&self) -> Self { + Self { inner: Inner { queue: self.inner.queue.clone() } } + } + } + + struct Inner { + queue: Arc::Events>>> + } + + impl FsmEventQueueVecShared { + pub fn new() -> Self { + let q = VecDeque::new(); + let inner = Inner { + queue: Arc::new(Mutex::new(q)) + }; + FsmEventQueueVecShared { + inner + } + } + } + + impl FsmEventQueue for FsmEventQueueVecShared { + fn dequeue(&mut self) -> Option<::Events> { + if let Ok(mut q) = self.inner.queue.lock() { + q.pop_front() + } else { + None + } + } + + fn len(&self) -> usize { + if let Ok(q) = self.inner.queue.lock() { + q.len() + } else { + 0 + } + } + } + + impl FsmEventQueueSender for FsmEventQueueVecShared { + fn enqueue::Events>>(&mut self, event: E) -> FsmResult<()> { + if let Ok(mut q) = self.inner.queue.lock() { + q.push_back(event.into()); + Ok(()) + } else { + Err(FsmError::QueueOverCapacity) + } + } + } +} + +#[cfg(feature = "std")] +pub use self::queue_vec_shared::*; + mod queue_array { use arraydeque::{Array, ArrayDeque}; @@ -169,6 +237,7 @@ impl<'a, Q, F, FSub> FsmEventQueueSender for FsmEventQueueSub<'a, Q, F, FS } + #[cfg(test)] use super::tests_fsm::TestFsm; @@ -185,6 +254,12 @@ fn test_array() { test_queue(queue); } +#[test] +fn test_dequeue_vec_shared() { + let queue = FsmEventQueueVecShared::::new(); + test_queue(queue); +} + #[cfg(test)] fn test_queue>(mut queue: Q) { use super::tests_fsm::{Events, EventA}; From d33e7f0566d3920ecc1012b85e0be11a745608f5 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Mon, 20 Sep 2021 00:29:58 +0200 Subject: [PATCH 03/13] reset the current state of a sub machine before re-entering. will have to be made more configurable. --- finny/src/fsm/fsm_impl.rs | 4 +++ finny/src/fsm/states.rs | 1 + finny_derive/src/codegen.rs | 52 ++++++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/finny/src/fsm/fsm_impl.rs b/finny/src/fsm/fsm_impl.rs index 272190b..c5350c4 100644 --- a/finny/src/fsm/fsm_impl.rs +++ b/finny/src/fsm/fsm_impl.rs @@ -55,6 +55,10 @@ impl DerefMut for FsmBackendImpl { } } +pub trait FsmBackendResetSubmachine { + fn reset(backend: &mut FsmBackendImpl); +} + /// The frontend of a state machine which also includes environmental services like queues /// and inspection. The usual way to use the FSM. diff --git a/finny/src/fsm/states.rs b/finny/src/fsm/states.rs index c2f8ea0..e39ea4e 100644 --- a/finny/src/fsm/states.rs +++ b/finny/src/fsm/states.rs @@ -49,6 +49,7 @@ pub trait FsmStateFactory where Self: Sized, TFsm: FsmBackend { fn new_state(context: &::Context) -> FsmResult; } +/// The implementation of a simple state factory, where the state supports Default. impl FsmStateFactory for TState where TState: Default, TFsm: FsmBackend { fn new_state(_context: &::Context) -> FsmResult { Ok(Default::default()) diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 9cea4b6..a986bd0 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -30,9 +30,10 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let mut state_variants = TokenStream::new(); let mut state_accessors = TokenStream::new(); + for (i, (_, state)) in fsm.fsm.states.iter().enumerate() { let name = &state.state_storage_field; - let state_ty = FsmTypes::new(&state.ty,&fsm.base.fsm_generics); + let state_ty = FsmTypes::new(&state.ty, &fsm.base.fsm_generics); let ty = state_ty.get_fsm_ty(); let ty_name = state_ty.get_fsm_no_generics_ty(); @@ -490,7 +491,16 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let fsm_sub_entry = match &transition.ty { FsmTransitionType::StateTransition(FsmStateTransition {state_to: FsmTransitionState::State(s @ FsmState { kind: FsmStateKind::SubMachine(_), .. }), .. }) => { + + let sub_ty = &s.ty; + quote! { + + // reset + { + use finny::FsmBackendResetSubmachine; + >::reset(ctx.backend); + } { <#transition_ty>::execute_on_sub_entry(&mut ctx, #region_id, &mut inspect_event_ctx); } @@ -1062,6 +1072,44 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre code }; + // submachine restart + + let sub_restart = { + + let subs: Vec<_> = fsm.fsm.states.iter().filter_map(|(ty, state)| match state.kind { + FsmStateKind::SubMachine(ref sub) => Some((ty, state, sub.clone())), + _ => None + }).collect(); + + + if subs.len() == 0 { + TokenStream::new() + } else { + + let mut q = TokenStream::new(); + + for (sub_ty, state, sub) in subs { + + q.append_all(quote! { + + impl #fsm_generics_impl finny::FsmBackendResetSubmachine< #fsm_ty #fsm_generics_type , #sub_ty > for #fsm_ty #fsm_generics_type + #fsm_generics_where + { + + fn reset(backend: &mut finny::FsmBackendImpl< #fsm_ty #fsm_generics_type >) { + let sub_fsm: &mut #sub_ty = backend.states.as_mut(); + sub_fsm.backend.current_states = Default::default(); + } + } + + }); + + } + + q + } + }; + let fsm_meta = generate_fsm_meta(&fsm); let mut q = quote! { @@ -1079,6 +1127,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre #timers + #sub_restart + #fsm_meta }; From 9f35f25fe02277c5c14d0332aeac5bc2024ebfdd Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Thu, 23 Sep 2021 23:07:40 +0200 Subject: [PATCH 04/13] basic submachine plantuml --- finny_derive/src/codegen_meta.rs | 98 +++++++++++++++++++++---------- finny_derive/src/meta/mod.rs | 6 +- finny_derive/src/meta/plantuml.rs | 35 +++++++---- 3 files changed, 95 insertions(+), 44 deletions(-) diff --git a/finny_derive/src/codegen_meta.rs b/finny_derive/src/codegen_meta.rs index 1106e65..bd05ca8 100644 --- a/finny_derive/src/codegen_meta.rs +++ b/finny_derive/src/codegen_meta.rs @@ -2,21 +2,23 @@ use std::collections::HashMap; use proc_macro2::TokenStream; -use crate::{ - meta::{ +use crate::{meta::{ FinnyEvent, FinnyFsm, FinnyRegion, FinnyState, FinnyStateKind, FinnyTimer, FinnyTransition, FinnyTransitionKind, FinnyTransitionNormal, - }, - parse::{FsmFnInput, FsmTransitionState}, - utils::tokens_to_string, -}; + }, parse::{FsmFnInput, FsmState, FsmStateKind, FsmTransitionState}, utils::{strip_generics, tokens_to_string}}; use quote::quote; +fn ty_to_string(ty: &syn::Type) -> String { + let ty = ty.clone(); + let ty = strip_generics(ty); + tokens_to_string(&ty) +} + fn to_info_state(s: &FsmTransitionState, fsm: &FsmFnInput) -> FinnyStateKind { match s { FsmTransitionState::None => FinnyStateKind::Stopped, - FsmTransitionState::State(s) => FinnyStateKind::State(FinnyState { - state_id: tokens_to_string(&s.ty), + FsmTransitionState::State(s @ FsmState { kind: FsmStateKind::Normal, .. }) => FinnyStateKind::State(FinnyState { + state_id: ty_to_string(&s.ty), timers: s .timers .iter() @@ -25,6 +27,7 @@ fn to_info_state(s: &FsmTransitionState, fsm: &FsmFnInput) -> FinnyStateKind { }) .collect(), }), + FsmTransitionState::State(s @ FsmState { kind: FsmStateKind::SubMachine(_), .. }) => FinnyStateKind::SubMachine(ty_to_string(&s.ty)) } } @@ -119,33 +122,68 @@ pub fn generate_fsm_meta(fsm: &FsmFnInput) -> TokenStream { //let json = serde_json::to_string_pretty(&info).expect("Failed to serialize the FSM info JSON!"); let fsm_ty = &fsm.base.fsm_ty; + let fsm_ty_name = tokens_to_string(&strip_generics(fsm_ty.clone())); let fsm_info_ty = &fsm.base.fsm_info_ty; - let fsm_ty_name_str = crate::utils::to_snake_case(&tokens_to_string(&fsm_ty)); + let fsm_ty_name_snake = crate::utils::to_snake_case(&tokens_to_string(&fsm_ty)); let (fsm_generics_impl, fsm_generics_type, fsm_generics_where) = fsm.base.fsm_generics.split_for_impl(); - let mut plant_uml_test_build = TokenStream::new(); - - #[cfg(feature="generate_plantuml")] - { - let plant_uml = crate::meta::plantuml::to_plant_uml(&info); - - let test_fn_name = crate::utils::to_field_name(&crate::utils::ty_append(&fsm_ty, "PlantUML")); - - plant_uml_test_build = quote! { - #[test] - #[cfg(test)] - fn #test_fn_name () { - use std::io::prelude::*; - use std::fs; - - let contents = #plant_uml; - - let mut f = fs::File::create(&format!("{}.plantuml", #fsm_ty_name_str )).unwrap(); - f.write_all(contents.as_bytes()).unwrap(); + let plant_uml_test_build = { + #[cfg(not(feature="generate_plantuml"))] + { TokenStream::new() } + #[cfg(feature="generate_plantuml")] + { + let (plant_uml_str, additional) = crate::meta::plantuml::to_plant_uml(&info).expect("PlantUML syntax generation error!"); + + let test_fn_name = crate::utils::to_field_name(&crate::utils::ty_append(&fsm_ty, "_plantuml")); + + quote! { + #[test] + #[cfg(test)] + fn #test_fn_name () { + use std::io::prelude::*; + use std::fs; + + let contents = < #fsm_info_ty > :: plantuml(); + + let mut f = fs::File::create(&format!("{}.plantuml", #fsm_ty_name_snake )).unwrap(); + f.write_all(contents.as_bytes()).unwrap(); + } + + #[derive(Default)] + pub struct #fsm_info_ty; + + impl #fsm_info_ty { + pub fn plantuml_inner() -> String { + use std::fmt::Write; + + let mut output = ( #plant_uml_str ).to_string(); + + #additional + + output + } + + pub fn plantuml() -> String { + use std::fmt::Write; + + let mut output = String::new(); + + writeln!(&mut output, "@startuml {}", #fsm_ty_name ); + + writeln!(&mut output, "{}", Self::plantuml_inner()); + + writeln!(&mut output, "@enduml"); + + output + } + } } - }; - } + } + }; + + + diff --git a/finny_derive/src/meta/mod.rs b/finny_derive/src/meta/mod.rs index cc552b5..b04f75c 100644 --- a/finny_derive/src/meta/mod.rs +++ b/finny_derive/src/meta/mod.rs @@ -25,19 +25,19 @@ pub struct FinnyRegion { pub enum FinnyStateKind { Stopped, State(FinnyState), - //SubMachine(String) + SubMachine(String) } impl FinnyStateKind { pub fn get_state_id(&self) -> String { match self { FinnyStateKind::Stopped => "Stopped".into(), - FinnyStateKind::State(s) => s.state_id.clone() + FinnyStateKind::State(s) => s.state_id.clone(), + FinnyStateKind::SubMachine(id) => id.clone() } } } - #[derive(Serialize, Deserialize, Clone, Debug)] pub struct FinnyState { pub state_id: String, diff --git a/finny_derive/src/meta/plantuml.rs b/finny_derive/src/meta/plantuml.rs index 63a0c83..2493313 100644 --- a/finny_derive/src/meta/plantuml.rs +++ b/finny_derive/src/meta/plantuml.rs @@ -1,11 +1,14 @@ +use proc_macro2:: TokenStream; +use quote::{quote, TokenStreamExt}; +use syn::{PathSegment, TypePath, parse::Parse}; + use super::FinnyFsm; use std::fmt::Write; -pub fn to_plant_uml(fsm: &FinnyFsm) -> String { +pub fn to_plant_uml(fsm: &FinnyFsm) -> Result<(String, TokenStream), std::fmt::Error> { let mut output = String::new(); - - output.push_str("@startuml\n"); + let mut subs = TokenStream::new(); for region in fsm.regions.values() { for state in region.states.values() { @@ -15,10 +18,22 @@ pub fn to_plant_uml(fsm: &FinnyFsm) -> String { } super::FinnyStateKind::State(state) => { - writeln!(&mut output, "state {} {{", state.state_id); + writeln!(&mut output, "state {} {{", state.state_id)?; - writeln!(&mut output, "}}"); + writeln!(&mut output, "}}")?; + }, + super::FinnyStateKind::SubMachine(sub_id) => { + + let p = syn::parse_str::(&format!("{}Info", sub_id)).unwrap(); + + + subs.append_all(quote! { + writeln!(&mut output, "state {} {{", #sub_id); + writeln!(&mut output, "{}", < #p > :: plantuml_inner() ); + writeln!(&mut output, "}}"); + }); + } } } @@ -33,10 +48,10 @@ pub fn to_plant_uml(fsm: &FinnyFsm) -> String { match &transition.transition { super::FinnyTransitionKind::SelfTransition { state_id } => { - writeln!(&mut output, "{state} --> {state} : {event} (Self)", state = state_id, event = event); + writeln!(&mut output, "{state} --> {state} : {event} (Self)", state = state_id, event = event)?; } super::FinnyTransitionKind::InternalTransition { state_id } => { - writeln!(&mut output, "{state} --> {state} : {event} (Internal)", state = state_id, event = event); + writeln!(&mut output, "{state} --> {state} : {event} (Internal)", state = state_id, event = event)?; } super::FinnyTransitionKind::NormalTransition(t) => { let state_from = match t.from_state.as_str() { @@ -44,13 +59,11 @@ pub fn to_plant_uml(fsm: &FinnyFsm) -> String { _ => &t.from_state }; - writeln!(&mut output, "{state_from} --> {state_to} : {event}", state_from = state_from, state_to = t.to_state, event = event); + writeln!(&mut output, "{state_from} --> {state_to} : {event}", state_from = state_from, state_to = t.to_state, event = event)?; } } } } - output.push_str("@enduml\n"); - - output + Ok((output, subs)) } \ No newline at end of file From aa0a86d62771c0726b3052df2824e01f0bac842f Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Fri, 24 Sep 2021 00:17:37 +0200 Subject: [PATCH 05/13] timers and transition names --- finny_derive/src/meta/plantuml.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/finny_derive/src/meta/plantuml.rs b/finny_derive/src/meta/plantuml.rs index 2493313..aed2f66 100644 --- a/finny_derive/src/meta/plantuml.rs +++ b/finny_derive/src/meta/plantuml.rs @@ -19,9 +19,11 @@ pub fn to_plant_uml(fsm: &FinnyFsm) -> Result<(String, TokenStream), std::fmt::E super::FinnyStateKind::State(state) => { writeln!(&mut output, "state {} {{", state.state_id)?; - - writeln!(&mut output, "}}")?; + + for timer in &state.timers { + writeln!(&mut output, "state {} : Timer {}", state.state_id, timer.timer_id)?; + } }, super::FinnyStateKind::SubMachine(sub_id) => { @@ -49,9 +51,11 @@ pub fn to_plant_uml(fsm: &FinnyFsm) -> Result<(String, TokenStream), std::fmt::E match &transition.transition { super::FinnyTransitionKind::SelfTransition { state_id } => { writeln!(&mut output, "{state} --> {state} : {event} (Self)", state = state_id, event = event)?; + writeln!(&mut output, "note on link: {}", transition.transition_id)?; } super::FinnyTransitionKind::InternalTransition { state_id } => { writeln!(&mut output, "{state} --> {state} : {event} (Internal)", state = state_id, event = event)?; + writeln!(&mut output, "note on link: {}", transition.transition_id)?; } super::FinnyTransitionKind::NormalTransition(t) => { let state_from = match t.from_state.as_str() { @@ -60,6 +64,7 @@ pub fn to_plant_uml(fsm: &FinnyFsm) -> Result<(String, TokenStream), std::fmt::E }; writeln!(&mut output, "{state_from} --> {state_to} : {event}", state_from = state_from, state_to = t.to_state, event = event)?; + writeln!(&mut output, "note on link: {}", transition.transition_id)?; } } } From c4e3f830c4f22506cc1a9266f2d599b641e699d4 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Fri, 24 Sep 2021 23:27:48 +0200 Subject: [PATCH 06/13] some documentation --- finny_derive/src/codegen.rs | 52 +++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index a986bd0..4604fb7 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use proc_macro2::{Span, TokenStream}; use quote::{TokenStreamExt, quote}; -use crate::{codegen_meta::generate_fsm_meta, fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs, to_field_name}}; +use crate::{codegen_meta::generate_fsm_meta, fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs, to_field_name, tokens_to_string}}; use crate::{parse::{FsmFnInput, FsmStateTransition, FsmTransitionState, FsmTransitionType}, utils::ty_append}; @@ -158,6 +158,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre } quote! { + /// States storage struct for the state machine. pub struct #states_store_ty #fsm_generics_type #fsm_generics_where { #code_fields _fsm: core::marker::PhantomData< #fsm_ty #fsm_generics_type > @@ -276,9 +277,10 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre for transition in ®ion.transitions { let ty = &transition.transition_ty; - let mut q = quote! { - pub struct #ty; - }; + + let mut transition_doc = String::new(); + + let mut q = TokenStream::new(); match &transition.ty { // internal or self transtion (only the current state) @@ -289,6 +291,12 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let is_self_transition = if let FsmTransitionType::SelfTransition(_) = &transition.ty { true } else { false }; + transition_doc.push_str(&format!(" {} transition within state [{}], responds to the event [{}].", + if is_self_transition { "A self" } else {"An internal"}, + tokens_to_string(&state.ty), + tokens_to_string(event_ty) + )); + if let Some(ref guard) = s.action.guard { let remap = remap_closure_inputs(&guard.inputs, vec![ quote! { event }, quote! { context }, quote! { states } @@ -296,6 +304,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let body = &guard.body; + transition_doc.push_str(" Guarded."); + let g = quote! { impl #fsm_generics_impl finny::FsmTransitionGuard<#fsm_ty #fsm_generics_type, #event_ty> for #ty #fsm_generics_where { fn guard<'fsm_event, Q>(event: & #event_ty, context: &finny::EventContext<'fsm_event, #fsm_ty #fsm_generics_type, Q>, states: & #states_store_ty #fsm_generics_type ) -> bool @@ -316,6 +326,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre quote! { event }, quote! { context }, quote! { state } ].as_slice())?; + transition_doc.push_str(" Executes an action."); + let body = &action.body; quote! { @@ -346,6 +358,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre FsmTransitionType::StateTransition(s @ FsmStateTransition { state_from: FsmTransitionState::None, .. }) => { let initial_state_ty = &s.state_to.get_fsm_state()?.ty; + transition_doc.push_str(" Start transition."); + q.append_all(quote! { impl #fsm_generics_impl finny::FsmTransitionFsmStart<#fsm_ty #fsm_generics_type, #initial_state_ty > for #ty #fsm_generics_where { @@ -357,9 +371,21 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre // normal state transition FsmTransitionType::StateTransition(s) => { + let event_ty = &s.event.get_event()?.ty; + let state_from = s.state_from.get_fsm_state()?; + let state_to = s.state_to.get_fsm_state()?; + + transition_doc.push_str(&format!(" Transition, from state [{}] to state [{}] upon the event [{}].", + tokens_to_string(&state_from.ty), + tokens_to_string(&state_to.ty), + tokens_to_string(&event_ty) + )); + if let Some(ref guard) = s.action.guard { let event_ty = &s.event.get_event()?.ty; + transition_doc.push_str(" Guarded."); + let remap = remap_closure_inputs(&guard.inputs, vec![ quote! { event }, quote! { context }, quote! { states } ].as_slice())?; @@ -382,6 +408,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre } let action_body = if let Some(ref action) = s.action.action { + transition_doc.push_str(" Executes an action."); + let remap = remap_closure_inputs(&action.inputs, vec![ quote! { event }, quote! { context }, quote! { from }, quote! { to } ].as_slice())?; @@ -395,10 +423,6 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre } else { TokenStream::new() }; - - let event_ty = &s.event.get_event()?.ty; - let state_from = s.state_from.get_fsm_state()?; - let state_to = s.state_to.get_fsm_state()?; let state_from_ty = &state_from.ty; let state_to_ty = &state_to.ty; @@ -416,6 +440,13 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre q.append_all(a); } } + + transition_doc.push_str(&format!(" Part of [{}].", tokens_to_string(fsm_ty))); + + q.append_all(quote! { + #[doc = #transition_doc ] + pub struct #ty; + }); t.append_all(q); } @@ -766,6 +797,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre quote! { + /// A Finny Finite State Machine. pub struct #fsm_ty #fsm_generics_type #fsm_generics_where { backend: finny::FsmBackendImpl<#fsm_ty #fsm_generics_type > } @@ -839,8 +871,12 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let trigger = remap_closure_inputs(&timer.trigger.inputs, &[quote! { ctx }, quote! { state }])?; let trigger_body = &timer.trigger.body; + let timer_doc = format!("A timer in the state [{}] of FSM [{}].", tokens_to_string(state_ty), tokens_to_string(fsm_ty)); + code.append_all(quote! { + #[doc = #timer_doc ] + pub struct #timer_ty #fsm_generics_type #fsm_generics_where { instance: Option > } From 116c8a7515c9f5059af83f17cb7c6bc8d7475dab Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 5 Oct 2021 00:20:38 +0200 Subject: [PATCH 07/13] log the reset --- finny/src/fsm/fsm_impl.rs | 2 +- finny_derive/src/codegen.rs | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/finny/src/fsm/fsm_impl.rs b/finny/src/fsm/fsm_impl.rs index c5350c4..e89830f 100644 --- a/finny/src/fsm/fsm_impl.rs +++ b/finny/src/fsm/fsm_impl.rs @@ -56,7 +56,7 @@ impl DerefMut for FsmBackendImpl { } pub trait FsmBackendResetSubmachine { - fn reset(backend: &mut FsmBackendImpl); + fn reset(backend: &mut FsmBackendImpl, inspect_event_ctx: &mut I) where I: Inspect; } diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 4604fb7..1b00f7b 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -530,7 +530,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre // reset { use finny::FsmBackendResetSubmachine; - >::reset(ctx.backend); + >::reset(ctx.backend, &mut inspect_event_ctx); } { <#transition_ty>::execute_on_sub_entry(&mut ctx, #region_id, &mut inspect_event_ctx); @@ -1132,9 +1132,12 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre #fsm_generics_where { - fn reset(backend: &mut finny::FsmBackendImpl< #fsm_ty #fsm_generics_type >) { + fn reset(backend: &mut finny::FsmBackendImpl< #fsm_ty #fsm_generics_type >, inspect_event_ctx: &mut I) + where I: finny::Inspect + { let sub_fsm: &mut #sub_ty = backend.states.as_mut(); sub_fsm.backend.current_states = Default::default(); + inspect_event_ctx.info("Setting the state of the submachine to Start."); } } From 97185ee35694dd08baaf867fc1040f777c527103 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 5 Oct 2021 00:20:52 +0200 Subject: [PATCH 08/13] inspect chain --- finny/src/fsm/inspect.rs | 85 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/finny/src/fsm/inspect.rs b/finny/src/fsm/inspect.rs index 37ac64c..b56daf0 100644 --- a/finny/src/fsm/inspect.rs +++ b/finny/src/fsm/inspect.rs @@ -71,4 +71,89 @@ impl Inspect for InspectNull { fn info(&self, msg: &str) { } +} + +pub struct InspectChain + where A: Inspect, B: Inspect +{ + pub a: A, + pub b: B +} + +impl InspectChain + where A: Inspect, B: Inspect +{ + pub fn new_pair(inspect_a: A, inspect_b: B) -> Self { + InspectChain { + a: inspect_a, + b: inspect_b + } + } +} + +impl Inspect for InspectChain + where A: Inspect, B: Inspect +{ + fn new_event(&self, event: &FsmEvent<::Events, ::Timers>, fsm: &FsmBackendImpl) -> Self { + Self { + a: self.a.new_event(event, fsm), + b: self.b.new_event(event, fsm) + } + } + + fn event_done(self, fsm: &FsmBackendImpl) { + self.a.event_done(fsm); + self.b.event_done(fsm); + } + + fn for_transition(&self) -> Self { + Self { + a: self.a.for_transition::(), + b: self.b.for_transition::() + } + } + + fn for_sub_machine(&self) -> Self { + Self { + a: self.a.for_sub_machine::(), + b: self.b.for_sub_machine::() + } + } + + fn for_timer(&self, timer_id: ::Timers) -> Self where F: FsmBackend { + Self { + a: self.a.for_timer::(timer_id.clone()), + b: self.b.for_timer::(timer_id) + } + } + + fn on_guard(&self, guard_result: bool) { + self.a.on_guard::(guard_result); + self.b.on_guard::(guard_result); + } + + fn on_state_enter(&self) { + self.a.on_state_enter::(); + self.b.on_state_enter::(); + } + + fn on_state_exit(&self) { + self.a.on_state_exit::(); + self.b.on_state_exit::(); + } + + fn on_action(&self) { + self.a.on_action::(); + self.b.on_action::(); + } + + fn on_error(&self, msg: &str, error: &E) where E: Debug { + self.a.on_error(msg, error); + self.b.on_error(msg, error); + } + + fn info(&self, msg: &str) { + self.a.info(msg); + self.b.info(msg); + } } \ No newline at end of file From 6371b38c097ccb39690a6a92697aaa80fdc86efc Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 5 Oct 2021 20:51:54 +0200 Subject: [PATCH 09/13] basic inspection events --- finny/src/fsm/inspect.rs | 48 +++++++++++++++++++++++++++++++++++- finny/src/fsm/mod.rs | 2 +- finny/src/fsm/tests_fsm.rs | 1 + finny/src/fsm/transitions.rs | 22 +++++++++++++++-- finny/src/inspect/slog.rs | 4 +++ finny_derive/src/codegen.rs | 8 ++++++ 6 files changed, 81 insertions(+), 4 deletions(-) diff --git a/finny/src/fsm/inspect.rs b/finny/src/fsm/inspect.rs index b56daf0..ec18971 100644 --- a/finny/src/fsm/inspect.rs +++ b/finny/src/fsm/inspect.rs @@ -1,5 +1,21 @@ -use crate::{FsmBackendImpl, lib::*}; +use crate::{FsmBackendImpl, FsmStates, lib::*}; use crate::{FsmBackend, FsmEvent}; + +#[derive(Debug)] +pub enum InspectFsmEvent where F: FsmBackend { + StateEnter(<::States as FsmStates>::StateKind), + StateExit(<::States as FsmStates>::StateKind) +} + +impl Clone for InspectFsmEvent where F: FsmBackend { + fn clone(&self) -> Self { + match self { + Self::StateEnter(arg0) => Self::StateEnter(arg0.clone()), + Self::StateExit(arg0) => Self::StateExit(arg0.clone()), + } + } +} + pub trait Inspect { fn new_event(&self, event: &FsmEvent<::Events, ::Timers>, fsm: &FsmBackendImpl) -> Self; @@ -16,6 +32,8 @@ pub trait Inspect { fn on_error(&self, msg: &str, error: &E) where E: Debug; fn info(&self, msg: &str); + + fn on_event(&self, event: InspectFsmEvent); } #[derive(Default)] @@ -71,6 +89,10 @@ impl Inspect for InspectNull { fn info(&self, msg: &str) { } + + fn on_event(&self, event: InspectFsmEvent) { + + } } pub struct InspectChain @@ -89,8 +111,27 @@ impl InspectChain b: inspect_b } } + + pub fn add_inspect(self, inspect: C) -> InspectChain, C> { + InspectChain { + a: self, + b: inspect + } + } +} + +impl InspectChain + where A: Inspect +{ + pub fn new_chain(inspect: A) -> Self { + InspectChain { + a: inspect, + b: InspectNull::new() + } + } } + impl Inspect for InspectChain where A: Inspect, B: Inspect { @@ -156,4 +197,9 @@ impl Inspect for InspectChain self.a.info(msg); self.b.info(msg); } + + fn on_event(&self, event: InspectFsmEvent) { + self.a.on_event(event.clone()); + self.b.on_event(event); + } } \ No newline at end of file diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 0583137..57f0098 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -39,7 +39,7 @@ pub type FsmDispatchResult = FsmResult<()>; /// Finite State Machine backend. Handles the dispatching, the types are /// defined by the code generator. -pub trait FsmBackend where Self: Sized { +pub trait FsmBackend where Self: Sized + Debug { /// The machine's context that is shared between its constructors and actions. type Context; /// The type that holds the states of the machine. diff --git a/finny/src/fsm/tests_fsm.rs b/finny/src/fsm/tests_fsm.rs index 9150eb4..70b166e 100644 --- a/finny/src/fsm/tests_fsm.rs +++ b/finny/src/fsm/tests_fsm.rs @@ -8,6 +8,7 @@ pub struct StateA; #[derive(Copy, Clone, Debug, PartialEq)] pub struct EventA { pub n: usize } +#[derive(Debug)] pub struct TestFsm; #[derive(Default)] diff --git a/finny/src/fsm/transitions.rs b/finny/src/fsm/transitions.rs index c8df0e1..9475d12 100644 --- a/finny/src/fsm/transitions.rs +++ b/finny/src/fsm/transitions.rs @@ -1,10 +1,10 @@ //! All of these traits will be implemented by the procedural code generator. -use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, FsmTimers, FsmTimersSub, lib::*}; +use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, FsmTimers, FsmTimersSub, InspectFsmEvent, lib::*}; use crate::{DispatchContext, EventContext, FsmBackend, FsmCurrentState, FsmEvent, FsmEventQueue, FsmRegionId, FsmStateTransitionAsMut, FsmStates, Inspect}; /// A state's entry and exit actions. -pub trait FsmState { +pub trait FsmState where Self: Sized { /// Action that is executed whenever this state is being entered. fn on_entry<'a, Q: FsmEventQueue>(&mut self, context: &mut EventContext<'a, F, Q>); /// Action that is executed whenever this state is being exited. @@ -19,6 +19,15 @@ pub trait FsmState { queue: context.queue }; + // inspection + { + context.inspect.on_state_enter::(); + + let kind = ::fsm_state(); + let ev = InspectFsmEvent::::StateEnter(kind); + context.inspect.on_event(ev); + } + let state: &mut Self = context.backend.states.as_mut(); state.on_entry(&mut event_context); } @@ -34,6 +43,15 @@ pub trait FsmState { let state: &mut Self = context.backend.states.as_mut(); state.on_exit(&mut event_context); + + // inspection + { + context.inspect.on_state_exit::(); + + let kind = ::fsm_state(); + let ev = InspectFsmEvent::::StateExit(kind); + context.inspect.on_event(ev); + } } fn fsm_state() -> <::States as FsmStates>::StateKind; diff --git a/finny/src/inspect/slog.rs b/finny/src/inspect/slog.rs index beae764..e904cd1 100644 --- a/finny/src/inspect/slog.rs +++ b/finny/src/inspect/slog.rs @@ -90,4 +90,8 @@ impl Inspect for InspectSlog fn info(&self, msg: &str) { info!(self.logger, "{}", msg); } + + fn on_event(&self, event: crate::InspectFsmEvent) { + info!(self.logger, "Inspection event {:?}", event); + } } \ No newline at end of file diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 1b00f7b..f943cc0 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -739,6 +739,14 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre result } } + + impl #fsm_generics_impl core::fmt::Debug for #fsm_ty #fsm_generics_type + #fsm_generics_where + { + fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error > { + Ok(()) + } + } } }; From e96d62ff02e426734ca070fa4982caef6e302f16 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 5 Oct 2021 21:50:07 +0200 Subject: [PATCH 10/13] inspect split, events helper --- finny/src/fsm/fsm_factory.rs | 6 +- finny/src/fsm/inspect.rs | 205 -------------------------------- finny/src/fsm/inspect/chain.rs | 115 ++++++++++++++++++ finny/src/fsm/inspect/events.rs | 74 ++++++++++++ finny/src/fsm/inspect/mod.rs | 42 +++++++ finny/src/fsm/inspect/null.rs | 63 ++++++++++ finny/src/inspect/slog.rs | 5 +- 7 files changed, 302 insertions(+), 208 deletions(-) delete mode 100644 finny/src/fsm/inspect.rs create mode 100644 finny/src/fsm/inspect/chain.rs create mode 100644 finny/src/fsm/inspect/events.rs create mode 100644 finny/src/fsm/inspect/mod.rs create mode 100644 finny/src/fsm/inspect/null.rs diff --git a/finny/src/fsm/fsm_factory.rs b/finny/src/fsm/fsm_factory.rs index df889bb..2459c42 100644 --- a/finny/src/fsm/fsm_factory.rs +++ b/finny/src/fsm/fsm_factory.rs @@ -1,4 +1,4 @@ -use crate::{FsmBackend, FsmBackendImpl, FsmEventQueue, FsmFrontend, FsmResult, FsmTimers, FsmTimersNull, Inspect, InspectNull}; +use crate::{FsmBackend, FsmBackendImpl, FsmEventQueue, FsmFrontend, FsmResult, FsmTimers, FsmTimersNull, Inspect}; #[cfg(feature="std")] use crate::{FsmEventQueueVec, timers::std::TimersStd}; @@ -26,7 +26,9 @@ pub trait FsmFactory { /// Build a new frontend for the FSM with a `FsmEventQueueVec` queue, `TimersStd` for timers and no logging. #[cfg(feature="std")] - fn new(context: ::Context) -> FsmResult, InspectNull, TimersStd>> { + fn new(context: ::Context) -> FsmResult, crate::null::InspectNull, TimersStd>> { + use crate::null::InspectNull; + let frontend = FsmFrontend { queue: FsmEventQueueVec::new(), backend: FsmBackendImpl::new(context)?, diff --git a/finny/src/fsm/inspect.rs b/finny/src/fsm/inspect.rs deleted file mode 100644 index ec18971..0000000 --- a/finny/src/fsm/inspect.rs +++ /dev/null @@ -1,205 +0,0 @@ -use crate::{FsmBackendImpl, FsmStates, lib::*}; -use crate::{FsmBackend, FsmEvent}; - -#[derive(Debug)] -pub enum InspectFsmEvent where F: FsmBackend { - StateEnter(<::States as FsmStates>::StateKind), - StateExit(<::States as FsmStates>::StateKind) -} - -impl Clone for InspectFsmEvent where F: FsmBackend { - fn clone(&self) -> Self { - match self { - Self::StateEnter(arg0) => Self::StateEnter(arg0.clone()), - Self::StateExit(arg0) => Self::StateExit(arg0.clone()), - } - } -} - -pub trait Inspect { - - fn new_event(&self, event: &FsmEvent<::Events, ::Timers>, fsm: &FsmBackendImpl) -> Self; - fn event_done(self, fsm: &FsmBackendImpl); - - fn for_transition(&self) -> Self; - fn for_sub_machine(&self) -> Self; - fn for_timer(&self, timer_id: ::Timers) -> Self where F: FsmBackend; - - fn on_guard(&self, guard_result: bool); - fn on_state_enter(&self); - fn on_state_exit(&self); - fn on_action(&self); - - fn on_error(&self, msg: &str, error: &E) where E: Debug; - fn info(&self, msg: &str); - - fn on_event(&self, event: InspectFsmEvent); -} - -#[derive(Default)] -pub struct InspectNull; - -impl InspectNull { - pub fn new() -> Self { - InspectNull { } - } -} - -impl Inspect for InspectNull { - fn new_event(&self, _event: &FsmEvent<::Events, ::Timers>, _fsm: &FsmBackendImpl) -> Self { - Self::default() - } - - fn for_transition(&self) -> Self { - Self::default() - } - - fn for_sub_machine(&self) -> Self { - Self::default() - } - - fn for_timer(&self, _timer_id: ::Timers) -> Self where F: FsmBackend { - Self::default() - } - - fn on_guard(&self, _guard_result: bool) { - - } - - fn on_state_enter(&self) { - - } - - fn on_state_exit(&self) { - - } - - fn on_action(&self) { - - } - - fn event_done(self, fsm: &FsmBackendImpl) { - - } - - fn on_error(&self, msg: &str, error: &E) where E: Debug { - - } - - fn info(&self, msg: &str) { - - } - - fn on_event(&self, event: InspectFsmEvent) { - - } -} - -pub struct InspectChain - where A: Inspect, B: Inspect -{ - pub a: A, - pub b: B -} - -impl InspectChain - where A: Inspect, B: Inspect -{ - pub fn new_pair(inspect_a: A, inspect_b: B) -> Self { - InspectChain { - a: inspect_a, - b: inspect_b - } - } - - pub fn add_inspect(self, inspect: C) -> InspectChain, C> { - InspectChain { - a: self, - b: inspect - } - } -} - -impl InspectChain - where A: Inspect -{ - pub fn new_chain(inspect: A) -> Self { - InspectChain { - a: inspect, - b: InspectNull::new() - } - } -} - - -impl Inspect for InspectChain - where A: Inspect, B: Inspect -{ - fn new_event(&self, event: &FsmEvent<::Events, ::Timers>, fsm: &FsmBackendImpl) -> Self { - Self { - a: self.a.new_event(event, fsm), - b: self.b.new_event(event, fsm) - } - } - - fn event_done(self, fsm: &FsmBackendImpl) { - self.a.event_done(fsm); - self.b.event_done(fsm); - } - - fn for_transition(&self) -> Self { - Self { - a: self.a.for_transition::(), - b: self.b.for_transition::() - } - } - - fn for_sub_machine(&self) -> Self { - Self { - a: self.a.for_sub_machine::(), - b: self.b.for_sub_machine::() - } - } - - fn for_timer(&self, timer_id: ::Timers) -> Self where F: FsmBackend { - Self { - a: self.a.for_timer::(timer_id.clone()), - b: self.b.for_timer::(timer_id) - } - } - - fn on_guard(&self, guard_result: bool) { - self.a.on_guard::(guard_result); - self.b.on_guard::(guard_result); - } - - fn on_state_enter(&self) { - self.a.on_state_enter::(); - self.b.on_state_enter::(); - } - - fn on_state_exit(&self) { - self.a.on_state_exit::(); - self.b.on_state_exit::(); - } - - fn on_action(&self) { - self.a.on_action::(); - self.b.on_action::(); - } - - fn on_error(&self, msg: &str, error: &E) where E: Debug { - self.a.on_error(msg, error); - self.b.on_error(msg, error); - } - - fn info(&self, msg: &str) { - self.a.info(msg); - self.b.info(msg); - } - - fn on_event(&self, event: InspectFsmEvent) { - self.a.on_event(event.clone()); - self.b.on_event(event); - } -} \ No newline at end of file diff --git a/finny/src/fsm/inspect/chain.rs b/finny/src/fsm/inspect/chain.rs new file mode 100644 index 0000000..eeddc32 --- /dev/null +++ b/finny/src/fsm/inspect/chain.rs @@ -0,0 +1,115 @@ +use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent, null::InspectNull}; + + +pub struct InspectChain +where A: Inspect, B: Inspect +{ +pub a: A, +pub b: B +} + +impl InspectChain +where A: Inspect, B: Inspect +{ +pub fn new_pair(inspect_a: A, inspect_b: B) -> Self { + InspectChain { + a: inspect_a, + b: inspect_b + } +} + +pub fn add_inspect(self, inspect: C) -> InspectChain, C> { + InspectChain { + a: self, + b: inspect + } +} +} + +impl InspectChain +where A: Inspect +{ +pub fn new_chain(inspect: A) -> Self { + InspectChain { + a: inspect, + b: InspectNull::new() + } +} +} + + +impl Inspect for InspectChain + where A: Inspect, B: Inspect +{ + fn new_event(&self, event: &FsmEvent<::Events, ::Timers>, fsm: &FsmBackendImpl) -> Self { + Self { + a: self.a.new_event(event, fsm), + b: self.b.new_event(event, fsm) + } + } + + fn event_done(self, fsm: &FsmBackendImpl) { + self.a.event_done(fsm); + self.b.event_done(fsm); + } + + fn for_transition(&self) -> Self { + Self { + a: self.a.for_transition::(), + b: self.b.for_transition::() + } + } + + fn for_sub_machine(&self) -> Self { + Self { + a: self.a.for_sub_machine::(), + b: self.b.for_sub_machine::() + } + } + + fn for_timer(&self, timer_id: ::Timers) -> Self where F: FsmBackend { + Self { + a: self.a.for_timer::(timer_id.clone()), + b: self.b.for_timer::(timer_id) + } + } + + fn on_guard(&self, guard_result: bool) { + self.a.on_guard::(guard_result); + self.b.on_guard::(guard_result); + } + + fn on_state_enter(&self) { + self.a.on_state_enter::(); + self.b.on_state_enter::(); + } + + fn on_state_exit(&self) { + self.a.on_state_exit::(); + self.b.on_state_exit::(); + } + + fn on_action(&self) { + self.a.on_action::(); + self.b.on_action::(); + } + + fn on_error(&self, msg: &str, error: &E) where E: core::fmt::Debug { + self.a.on_error(msg, error); + self.b.on_error(msg, error); + } + + fn info(&self, msg: &str) { + self.a.info(msg); + self.b.info(msg); + } +} + +impl InspectEvent for InspectChain + where A: Inspect, B: Inspect +{ + fn on_event(&self, event: InspectFsmEvent) { + self.a.on_event(event.clone()); + self.b.on_event(event); + } +} \ No newline at end of file diff --git a/finny/src/fsm/inspect/events.rs b/finny/src/fsm/inspect/events.rs new file mode 100644 index 0000000..53d3a96 --- /dev/null +++ b/finny/src/fsm/inspect/events.rs @@ -0,0 +1,74 @@ +use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent}; + +#[derive(Clone)] +pub struct EventInspector + where T: InspectEvent + Clone +{ + event_handler: T +} + +impl EventInspector + where T: InspectEvent + Clone +{ + pub fn new(inspect: T) -> Self { + Self { + event_handler: inspect + } + } +} + +impl Inspect for EventInspector + where TI: InspectEvent + Clone +{ + fn new_event(&self, _event: &FsmEvent<::Events, ::Timers>, _fsm: &FsmBackendImpl) -> Self { + self.clone() + } + + fn for_transition(&self) -> Self { + self.clone() + } + + fn for_sub_machine(&self) -> Self { + self.clone() + } + + fn for_timer(&self, _timer_id: ::Timers) -> Self where F: FsmBackend { + self.clone() + } + + fn on_guard(&self, _guard_result: bool) { + + } + + fn on_state_enter(&self) { + + } + + fn on_state_exit(&self) { + + } + + fn on_action(&self) { + + } + + fn event_done(self, fsm: &FsmBackendImpl) { + + } + + fn on_error(&self, msg: &str, error: &E) where E: core::fmt::Debug { + + } + + fn info(&self, msg: &str) { + + } +} + +impl InspectEvent for EventInspector + where TI: InspectEvent + Clone +{ + fn on_event(&self, event: crate::InspectFsmEvent) { + self.event_handler.on_event(event) + } +} \ No newline at end of file diff --git a/finny/src/fsm/inspect/mod.rs b/finny/src/fsm/inspect/mod.rs new file mode 100644 index 0000000..46ea9fa --- /dev/null +++ b/finny/src/fsm/inspect/mod.rs @@ -0,0 +1,42 @@ +pub mod null; +pub mod chain; +pub mod events; + +use crate::{FsmBackend, FsmBackendImpl, FsmEvent, FsmStates}; + +#[derive(Debug)] +pub enum InspectFsmEvent where F: FsmBackend { + StateEnter(<::States as FsmStates>::StateKind), + StateExit(<::States as FsmStates>::StateKind) +} + +impl Clone for InspectFsmEvent where F: FsmBackend { + fn clone(&self) -> Self { + match self { + Self::StateEnter(arg0) => Self::StateEnter(arg0.clone()), + Self::StateExit(arg0) => Self::StateExit(arg0.clone()), + } + } +} + +pub trait Inspect: InspectEvent { + + fn new_event(&self, event: &FsmEvent<::Events, ::Timers>, fsm: &FsmBackendImpl) -> Self; + fn event_done(self, fsm: &FsmBackendImpl); + + fn for_transition(&self) -> Self; + fn for_sub_machine(&self) -> Self; + fn for_timer(&self, timer_id: ::Timers) -> Self where F: FsmBackend; + + fn on_guard(&self, guard_result: bool); + fn on_state_enter(&self); + fn on_state_exit(&self); + fn on_action(&self); + + fn on_error(&self, msg: &str, error: &E) where E: core::fmt::Debug; + fn info(&self, msg: &str); +} + +pub trait InspectEvent { + fn on_event(&self, event: InspectFsmEvent); +} \ No newline at end of file diff --git a/finny/src/fsm/inspect/null.rs b/finny/src/fsm/inspect/null.rs new file mode 100644 index 0000000..f0a9af4 --- /dev/null +++ b/finny/src/fsm/inspect/null.rs @@ -0,0 +1,63 @@ +use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent}; + + +#[derive(Default)] +pub struct InspectNull; + +impl InspectNull { + pub fn new() -> Self { + InspectNull { } + } +} + +impl Inspect for InspectNull { + fn new_event(&self, _event: &FsmEvent<::Events, ::Timers>, _fsm: &FsmBackendImpl) -> Self { + Self::default() + } + + fn for_transition(&self) -> Self { + Self::default() + } + + fn for_sub_machine(&self) -> Self { + Self::default() + } + + fn for_timer(&self, _timer_id: ::Timers) -> Self where F: FsmBackend { + Self::default() + } + + fn on_guard(&self, _guard_result: bool) { + + } + + fn on_state_enter(&self) { + + } + + fn on_state_exit(&self) { + + } + + fn on_action(&self) { + + } + + fn event_done(self, fsm: &FsmBackendImpl) { + + } + + fn on_error(&self, msg: &str, error: &E) where E: core::fmt::Debug { + + } + + fn info(&self, msg: &str) { + + } +} + +impl InspectEvent for InspectNull { + fn on_event(&self, event: InspectFsmEvent) { + + } +} \ No newline at end of file diff --git a/finny/src/inspect/slog.rs b/finny/src/inspect/slog.rs index e904cd1..ca6d014 100644 --- a/finny/src/inspect/slog.rs +++ b/finny/src/inspect/slog.rs @@ -1,5 +1,5 @@ use slog::{info, o, error}; -use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect}; +use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent}; use crate::lib::*; use AsRef; @@ -90,7 +90,10 @@ impl Inspect for InspectSlog fn info(&self, msg: &str) { info!(self.logger, "{}", msg); } +} +impl InspectEvent for InspectSlog +{ fn on_event(&self, event: crate::InspectFsmEvent) { info!(self.logger, "Inspection event {:?}", event); } From 11955763bee6502c779b419db43b8043f0aa3870 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Wed, 6 Oct 2021 00:02:55 +0200 Subject: [PATCH 11/13] inspect moved, added any cast for inspection events --- finny/src/fsm/fsm_factory.rs | 4 ++-- finny/src/fsm/inspect/mod.rs | 6 ++---- finny/src/fsm/mod.rs | 4 ++-- finny/src/fsm/transitions.rs | 4 +++- finny/src/{fsm => }/inspect/chain.rs | 6 ++++-- finny/src/{fsm => }/inspect/events.rs | 3 ++- finny/src/inspect/mod.rs | 5 +++++ finny/src/{fsm => }/inspect/null.rs | 0 finny/src/inspect/slog.rs | 4 ++-- 9 files changed, 22 insertions(+), 14 deletions(-) rename finny/src/{fsm => }/inspect/chain.rs (94%) rename finny/src/{fsm => }/inspect/events.rs (93%) rename finny/src/{fsm => }/inspect/null.rs (100%) diff --git a/finny/src/fsm/fsm_factory.rs b/finny/src/fsm/fsm_factory.rs index 2459c42..4382926 100644 --- a/finny/src/fsm/fsm_factory.rs +++ b/finny/src/fsm/fsm_factory.rs @@ -26,8 +26,8 @@ pub trait FsmFactory { /// Build a new frontend for the FSM with a `FsmEventQueueVec` queue, `TimersStd` for timers and no logging. #[cfg(feature="std")] - fn new(context: ::Context) -> FsmResult, crate::null::InspectNull, TimersStd>> { - use crate::null::InspectNull; + fn new(context: ::Context) -> FsmResult, crate::inspect::null::InspectNull, TimersStd>> { + use crate::inspect::null::InspectNull; let frontend = FsmFrontend { queue: FsmEventQueueVec::new(), diff --git a/finny/src/fsm/inspect/mod.rs b/finny/src/fsm/inspect/mod.rs index 46ea9fa..4fa65a3 100644 --- a/finny/src/fsm/inspect/mod.rs +++ b/finny/src/fsm/inspect/mod.rs @@ -1,6 +1,4 @@ -pub mod null; -pub mod chain; -pub mod events; +use core::any::Any; use crate::{FsmBackend, FsmBackendImpl, FsmEvent, FsmStates}; @@ -38,5 +36,5 @@ pub trait Inspect: InspectEvent { } pub trait InspectEvent { - fn on_event(&self, event: InspectFsmEvent); + fn on_event(&self, event: InspectFsmEvent); } \ No newline at end of file diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 57f0098..6878b64 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -8,9 +8,9 @@ mod queue; mod states; mod transitions; mod tests_fsm; -mod inspect; mod dispatch; mod timers; +mod inspect; pub use self::events::*; pub use self::fsm_factory::*; @@ -39,7 +39,7 @@ pub type FsmDispatchResult = FsmResult<()>; /// Finite State Machine backend. Handles the dispatching, the types are /// defined by the code generator. -pub trait FsmBackend where Self: Sized + Debug { +pub trait FsmBackend where Self: Sized + Debug + 'static { /// The machine's context that is shared between its constructors and actions. type Context; /// The type that holds the states of the machine. diff --git a/finny/src/fsm/transitions.rs b/finny/src/fsm/transitions.rs index 9475d12..309152f 100644 --- a/finny/src/fsm/transitions.rs +++ b/finny/src/fsm/transitions.rs @@ -1,8 +1,10 @@ //! All of these traits will be implemented by the procedural code generator. -use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, FsmTimers, FsmTimersSub, InspectFsmEvent, lib::*}; +use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, FsmTimers, FsmTimersSub, lib::*}; use crate::{DispatchContext, EventContext, FsmBackend, FsmCurrentState, FsmEvent, FsmEventQueue, FsmRegionId, FsmStateTransitionAsMut, FsmStates, Inspect}; +use super::inspect::InspectFsmEvent; + /// A state's entry and exit actions. pub trait FsmState where Self: Sized { /// Action that is executed whenever this state is being entered. diff --git a/finny/src/fsm/inspect/chain.rs b/finny/src/inspect/chain.rs similarity index 94% rename from finny/src/fsm/inspect/chain.rs rename to finny/src/inspect/chain.rs index eeddc32..c868e63 100644 --- a/finny/src/fsm/inspect/chain.rs +++ b/finny/src/inspect/chain.rs @@ -1,4 +1,6 @@ -use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent, null::InspectNull}; +use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent}; +use core::any::Any; +use super::{null::InspectNull}; pub struct InspectChain @@ -108,7 +110,7 @@ impl Inspect for InspectChain impl InspectEvent for InspectChain where A: Inspect, B: Inspect { - fn on_event(&self, event: InspectFsmEvent) { + fn on_event(&self, event: InspectFsmEvent) { self.a.on_event(event.clone()); self.b.on_event(event); } diff --git a/finny/src/fsm/inspect/events.rs b/finny/src/inspect/events.rs similarity index 93% rename from finny/src/fsm/inspect/events.rs rename to finny/src/inspect/events.rs index 53d3a96..2573ae3 100644 --- a/finny/src/fsm/inspect/events.rs +++ b/finny/src/inspect/events.rs @@ -1,4 +1,5 @@ use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent}; +use core::any::Any; #[derive(Clone)] pub struct EventInspector @@ -68,7 +69,7 @@ impl Inspect for EventInspector impl InspectEvent for EventInspector where TI: InspectEvent + Clone { - fn on_event(&self, event: crate::InspectFsmEvent) { + fn on_event(&self, event: crate::InspectFsmEvent) { self.event_handler.on_event(event) } } \ No newline at end of file diff --git a/finny/src/inspect/mod.rs b/finny/src/inspect/mod.rs index f1e5914..d7893aa 100644 --- a/finny/src/inspect/mod.rs +++ b/finny/src/inspect/mod.rs @@ -1,2 +1,7 @@ +pub mod null; +pub mod chain; +pub mod events; + + #[cfg(feature="inspect_slog")] pub mod slog; \ No newline at end of file diff --git a/finny/src/fsm/inspect/null.rs b/finny/src/inspect/null.rs similarity index 100% rename from finny/src/fsm/inspect/null.rs rename to finny/src/inspect/null.rs diff --git a/finny/src/inspect/slog.rs b/finny/src/inspect/slog.rs index ca6d014..a6f3ed8 100644 --- a/finny/src/inspect/slog.rs +++ b/finny/src/inspect/slog.rs @@ -1,5 +1,5 @@ use slog::{info, o, error}; -use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent}; +use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent}; use crate::lib::*; use AsRef; @@ -94,7 +94,7 @@ impl Inspect for InspectSlog impl InspectEvent for InspectSlog { - fn on_event(&self, event: crate::InspectFsmEvent) { + fn on_event(&self, event: InspectFsmEvent) { info!(self.logger, "Inspection event {:?}", event); } } \ No newline at end of file From b32358cf8fdd5bdf661969db560cc48bcd57602c Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sat, 9 Oct 2021 14:35:06 +0200 Subject: [PATCH 12/13] use the specific state type instead of the entire FSM --- finny/src/fsm/inspect/mod.rs | 20 ++++++-------------- finny/src/fsm/mod.rs | 2 +- finny/src/fsm/states.rs | 4 ++-- finny/src/fsm/transitions.rs | 8 ++++---- finny/src/inspect/chain.rs | 5 +++-- finny/src/inspect/events.rs | 5 +++-- finny/src/inspect/null.rs | 5 +++-- finny/src/inspect/slog.rs | 4 +++- 8 files changed, 25 insertions(+), 28 deletions(-) diff --git a/finny/src/fsm/inspect/mod.rs b/finny/src/fsm/inspect/mod.rs index 4fa65a3..308b2b7 100644 --- a/finny/src/fsm/inspect/mod.rs +++ b/finny/src/fsm/inspect/mod.rs @@ -1,20 +1,12 @@ +use core::fmt::Debug; use core::any::Any; use crate::{FsmBackend, FsmBackendImpl, FsmEvent, FsmStates}; -#[derive(Debug)] -pub enum InspectFsmEvent where F: FsmBackend { - StateEnter(<::States as FsmStates>::StateKind), - StateExit(<::States as FsmStates>::StateKind) -} - -impl Clone for InspectFsmEvent where F: FsmBackend { - fn clone(&self) -> Self { - match self { - Self::StateEnter(arg0) => Self::StateEnter(arg0.clone()), - Self::StateExit(arg0) => Self::StateExit(arg0.clone()), - } - } +#[derive(Debug, Clone)] +pub enum InspectFsmEvent where S: Debug + Clone { + StateEnter(S), + StateExit(S) } pub trait Inspect: InspectEvent { @@ -36,5 +28,5 @@ pub trait Inspect: InspectEvent { } pub trait InspectEvent { - fn on_event(&self, event: InspectFsmEvent); + fn on_event(&self, event: &InspectFsmEvent); } \ No newline at end of file diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 6878b64..25468df 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -39,7 +39,7 @@ pub type FsmDispatchResult = FsmResult<()>; /// Finite State Machine backend. Handles the dispatching, the types are /// defined by the code generator. -pub trait FsmBackend where Self: Sized + Debug + 'static { +pub trait FsmBackend where Self: Sized + Debug { /// The machine's context that is shared between its constructors and actions. type Context; /// The type that holds the states of the machine. diff --git a/finny/src/fsm/states.rs b/finny/src/fsm/states.rs index e39ea4e..79121cf 100644 --- a/finny/src/fsm/states.rs +++ b/finny/src/fsm/states.rs @@ -5,9 +5,9 @@ use crate::FsmResult; /// The implementation should hold all of the FSM's states as fields. pub trait FsmStates: FsmStateFactory where TFsm: FsmBackend { /// The enum type for all states that's used as the "current state" field in the FSM's backend. - type StateKind: Clone + Copy + Debug + PartialEq; + type StateKind: Clone + Copy + Debug + PartialEq + 'static; /// An array of current states for the machine, one for each region. - type CurrentState: Clone + Copy + Debug + Default + AsRef<[FsmCurrentState]> + AsMut<[FsmCurrentState]>; + type CurrentState: Clone + Copy + Debug + Default + AsRef<[FsmCurrentState]> + AsMut<[FsmCurrentState]> + 'static; } /// The current state of the FSM. diff --git a/finny/src/fsm/transitions.rs b/finny/src/fsm/transitions.rs index 309152f..4ab6a43 100644 --- a/finny/src/fsm/transitions.rs +++ b/finny/src/fsm/transitions.rs @@ -26,8 +26,8 @@ pub trait FsmState where Self: Sized { context.inspect.on_state_enter::(); let kind = ::fsm_state(); - let ev = InspectFsmEvent::::StateEnter(kind); - context.inspect.on_event(ev); + let ev = InspectFsmEvent::StateEnter(kind); + context.inspect.on_event(&ev); } let state: &mut Self = context.backend.states.as_mut(); @@ -51,8 +51,8 @@ pub trait FsmState where Self: Sized { context.inspect.on_state_exit::(); let kind = ::fsm_state(); - let ev = InspectFsmEvent::::StateExit(kind); - context.inspect.on_event(ev); + let ev = InspectFsmEvent::StateExit(kind); + context.inspect.on_event(&ev); } } diff --git a/finny/src/inspect/chain.rs b/finny/src/inspect/chain.rs index c868e63..42bb446 100644 --- a/finny/src/inspect/chain.rs +++ b/finny/src/inspect/chain.rs @@ -1,5 +1,6 @@ use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent}; use core::any::Any; +use core::fmt::Debug; use super::{null::InspectNull}; @@ -110,8 +111,8 @@ impl Inspect for InspectChain impl InspectEvent for InspectChain where A: Inspect, B: Inspect { - fn on_event(&self, event: InspectFsmEvent) { - self.a.on_event(event.clone()); + fn on_event(&self, event: &InspectFsmEvent) { + self.a.on_event(event); self.b.on_event(event); } } \ No newline at end of file diff --git a/finny/src/inspect/events.rs b/finny/src/inspect/events.rs index 2573ae3..0b8c783 100644 --- a/finny/src/inspect/events.rs +++ b/finny/src/inspect/events.rs @@ -1,5 +1,6 @@ -use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent}; +use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent}; use core::any::Any; +use core::fmt::Debug; #[derive(Clone)] pub struct EventInspector @@ -69,7 +70,7 @@ impl Inspect for EventInspector impl InspectEvent for EventInspector where TI: InspectEvent + Clone { - fn on_event(&self, event: crate::InspectFsmEvent) { + fn on_event(&self, event: &InspectFsmEvent) { self.event_handler.on_event(event) } } \ No newline at end of file diff --git a/finny/src/inspect/null.rs b/finny/src/inspect/null.rs index f0a9af4..04c3b3b 100644 --- a/finny/src/inspect/null.rs +++ b/finny/src/inspect/null.rs @@ -1,5 +1,6 @@ use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent}; - +use core::fmt::Debug; +use core::any::Any; #[derive(Default)] pub struct InspectNull; @@ -57,7 +58,7 @@ impl Inspect for InspectNull { } impl InspectEvent for InspectNull { - fn on_event(&self, event: InspectFsmEvent) { + fn on_event(&self, event: &InspectFsmEvent) { } } \ No newline at end of file diff --git a/finny/src/inspect/slog.rs b/finny/src/inspect/slog.rs index a6f3ed8..cc04cba 100644 --- a/finny/src/inspect/slog.rs +++ b/finny/src/inspect/slog.rs @@ -2,6 +2,8 @@ use slog::{info, o, error}; use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent}; use crate::lib::*; use AsRef; +use core::fmt::Debug; +use core::any::Any; pub struct InspectSlog { pub logger: slog::Logger @@ -94,7 +96,7 @@ impl Inspect for InspectSlog impl InspectEvent for InspectSlog { - fn on_event(&self, event: InspectFsmEvent) { + fn on_event(&self, event: &InspectFsmEvent) { info!(self.logger, "Inspection event {:?}", event); } } \ No newline at end of file From ea8a1d2ac757c813d8e3e45d6812998ed8d8c9ac Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sat, 9 Oct 2021 14:39:00 +0200 Subject: [PATCH 13/13] import fix --- finny_nostd_tests/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/finny_nostd_tests/src/main.rs b/finny_nostd_tests/src/main.rs index 04591ec..10f5b72 100644 --- a/finny_nostd_tests/src/main.rs +++ b/finny_nostd_tests/src/main.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use finny::{finny_fsm, FsmFactory, FsmEventQueueArray, InspectNull, FsmTimersNull}; +use finny::{finny_fsm, FsmFactory, FsmEventQueueArray, inspect::null::InspectNull, FsmTimersNull}; use finny::decl::{FsmBuilder, BuiltFsm}; use heapless::consts::*;