From 462c2fe25547bcaea7135be0d8f3c527e326197b Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Mon, 18 Jan 2021 21:58:54 +0100 Subject: [PATCH 01/38] timers wip --- finny/src/decl/state.rs | 14 +++++++- finny/src/fsm/mod.rs | 4 ++- finny/src/fsm/timers.rs | 60 +++++++++++++++++++++++++++++++++ finny/src/lib.rs | 1 + finny_tests/tests/fsm_timers.rs | 56 ++++++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 2 deletions(-) create mode 100644 finny/src/fsm/timers.rs create mode 100644 finny_tests/tests/fsm_timers.rs diff --git a/finny/src/decl/state.rs b/finny/src/decl/state.rs index b700852..06104e9 100644 --- a/finny/src/decl/state.rs +++ b/finny/src/decl/state.rs @@ -1,4 +1,4 @@ -use crate::lib::*; +use crate::{TimerSettings, lib::*}; use crate::{EventContext, FsmBackend}; use super::{FsmQueueMock, event::FsmEventBuilderState}; @@ -29,4 +29,16 @@ impl FsmStateBuilder _event: PhantomData::default() } } + + /// Start a new timer when entering this state. The timer should be unit struct with a implemented + /// Default trait. The timer is setup within a closure and the trigger is another closure + /// that returns an event to be enqueued in the FSM. + pub fn on_entry_start_timer(&self, _setup: FSetup, _trigger: FTrigger) -> &Self + where + TTimer: Default, + FSetup: FnOnce(&TContext, &mut TimerSettings), + FTrigger: Fn(&TContext, &TState) -> Option< ::Events > + { + self + } } diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 0a6ecc3..da0ae7a 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -10,6 +10,7 @@ mod transitions; mod tests_fsm; mod inspect; mod dispatch; +mod timers; pub use self::events::*; pub use self::fsm_factory::*; @@ -19,8 +20,9 @@ pub use self::states::*; pub use self::transitions::*; pub use self::inspect::*; pub use self::dispatch::*; +pub use self::timers::*; -use crate::{bundled, lib::*}; +use crate::lib::*; pub type FsmResult = Result; diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs new file mode 100644 index 0000000..7235220 --- /dev/null +++ b/finny/src/fsm/timers.rs @@ -0,0 +1,60 @@ +use crate::lib::*; +use crate::{FsmBackend, FsmResult}; + + +pub trait FsmTimer + where F: FsmBackend +{ + fn setup(ctx: &::Context, settings: &mut TimerSettings); + fn trigger(ctx: &::Context, state: &S) -> Option< ::Events >; +} + +#[derive(Debug, Clone)] +pub struct TimerFsmSettings { + pub timeout: Duration, + pub renew: bool, + pub cancel_on_state_exit: bool +} + +impl TimerFsmSettings { + pub fn to_timer_settings(&self) -> TimerSettings { + TimerSettings { + timeout: self.timeout, + renew: self.renew + } + } +} + +impl Default for TimerFsmSettings { + fn default() -> Self { + Self { + timeout: Duration::from_secs(1), + renew: false, + cancel_on_state_exit: true + } + } +} + + +#[derive(Debug, Clone)] +pub struct TimerSettings +{ + pub timeout: Duration, + pub renew: bool +} + +pub type TimerId = usize; + +pub trait FsmTimers { + fn create(&mut self, id: TimerId, settings: TimerSettings) -> FsmResult<()>; + fn cancel(&mut self, id: TimerId) -> FsmResult<()>; + + /// Return the latest timer that was triggered. Poll this until it returns None. + fn get_triggered_timer(&mut self) -> Option; +} + + +#[derive(Debug, Copy, Clone)] +pub struct FsmTimersTriggerEventsResult { + pub triggered_events: usize +} \ No newline at end of file diff --git a/finny/src/lib.rs b/finny/src/lib.rs index 8eb3bfd..0efc146 100644 --- a/finny/src/lib.rs +++ b/finny/src/lib.rs @@ -102,6 +102,7 @@ mod lib { pub use self::core::fmt; pub use self::core::any::type_name; pub use self::core::slice::SliceIndex; + pub use self::core::time::Duration; #[cfg(feature="std")] pub use std::collections::VecDeque; diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs new file mode 100644 index 0000000..cd16e19 --- /dev/null +++ b/finny_tests/tests/fsm_timers.rs @@ -0,0 +1,56 @@ +extern crate finny; + +use finny::{FsmCurrentState, FsmError, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::{self, InspectSlog}}; +use slog::{Drain, Logger, info, o}; + +#[derive(Debug)] +pub struct TimersMachineContext { +} + +#[derive(Default)] +pub struct StateA { + +} +#[derive(Default)] +pub struct StateB { + +} +#[derive(Default)] +pub struct StateC; +#[derive(Clone, Debug)] +pub struct EventClick { time: usize } +#[derive(Clone, Debug)] +pub struct EventEnter { shift: bool } + +#[finny_fsm] +fn build_fsm(mut fsm: FsmBuilder) -> BuiltFsm { + fsm.events_debug(); + fsm.initial_state::(); + + fsm.state::(); + + fsm.state::() + .on_event::() + .transition_to::(); + + fsm.state::(); + + fsm.build() +} + + +#[test] +fn test_timers_fsm() -> FsmResult<()> { + let plain = slog_term::PlainSyncDecorator::new(std::io::stdout()); + let logger = Logger::root( + slog_term::FullFormat::new(plain) + .build().fuse(), o!() + ); + + let ctx = TimersMachineContext { }; + + let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)))?; + + fsm.start()?; + Ok(()) +} \ No newline at end of file From 402e32fb4cf5037cf6f38baa1a70d195bb64a896 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 19 Jan 2021 23:35:46 +0100 Subject: [PATCH 02/38] wip --- finny/src/decl/state.rs | 5 ++--- finny/src/fsm/timers.rs | 4 ++++ finny_derive/src/parse.rs | 11 +++++++++- finny_derive/src/parse_fsm.rs | 38 ++++++++++++++++++++++++++++----- finny_tests/tests/fsm_timers.rs | 31 +++++++++++++++++++++++++-- 5 files changed, 78 insertions(+), 11 deletions(-) diff --git a/finny/src/decl/state.rs b/finny/src/decl/state.rs index 06104e9..328e512 100644 --- a/finny/src/decl/state.rs +++ b/finny/src/decl/state.rs @@ -33,10 +33,9 @@ impl FsmStateBuilder /// Start a new timer when entering this state. The timer should be unit struct with a implemented /// Default trait. The timer is setup within a closure and the trigger is another closure /// that returns an event to be enqueued in the FSM. - pub fn on_entry_start_timer(&self, _setup: FSetup, _trigger: FTrigger) -> &Self + pub fn on_entry_start_timer(&self, _setup: FSetup, _trigger: FTrigger) -> &Self where - TTimer: Default, - FSetup: FnOnce(&TContext, &mut TimerSettings), + FSetup: Fn(&TContext, &mut TimerSettings), FTrigger: Fn(&TContext, &TState) -> Option< ::Events > { self diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index 7235220..5d632bd 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -11,6 +11,7 @@ pub trait FsmTimer #[derive(Debug, Clone)] pub struct TimerFsmSettings { + pub enabled: bool, pub timeout: Duration, pub renew: bool, pub cancel_on_state_exit: bool @@ -19,6 +20,7 @@ pub struct TimerFsmSettings { impl TimerFsmSettings { pub fn to_timer_settings(&self) -> TimerSettings { TimerSettings { + enabled: self.enabled, timeout: self.timeout, renew: self.renew } @@ -28,6 +30,7 @@ impl TimerFsmSettings { impl Default for TimerFsmSettings { fn default() -> Self { Self { + enabled: true, timeout: Duration::from_secs(1), renew: false, cancel_on_state_exit: true @@ -39,6 +42,7 @@ impl Default for TimerFsmSettings { #[derive(Debug, Clone)] pub struct TimerSettings { + pub enabled: bool, pub timeout: Duration, pub renew: bool } diff --git a/finny_derive/src/parse.rs b/finny_derive/src/parse.rs index a775484..1255428 100644 --- a/finny_derive/src/parse.rs +++ b/finny_derive/src/parse.rs @@ -240,8 +240,17 @@ pub struct FsmState { pub kind: FsmStateKind, pub state_storage_field: syn::Ident, pub on_entry_closure: Option, - pub on_exit_closure: Option + pub on_exit_closure: Option, + pub timers: Vec } + +#[derive(Debug, Clone)] +pub struct FsmTimer { + pub id: usize, + pub setup: syn::ExprClosure, + pub trigger: syn::ExprClosure +} + #[derive(Debug, Clone)] pub struct FsmEvent { pub ty: syn::Type, diff --git a/finny_derive/src/parse_fsm.rs b/finny_derive/src/parse_fsm.rs index e162895..00f6c72 100644 --- a/finny_derive/src/parse_fsm.rs +++ b/finny_derive/src/parse_fsm.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use proc_macro2::Span; use syn::{ExprMethodCall, ItemFn, Type, spanned::Spanned}; -use crate::{parse::{EventGuardAction, FsmDeclarations, FsmEvent, FsmEventTransition, FsmFnBase, FsmState, FsmStateAction, FsmStateKind, FsmStateTransition, FsmSubMachineOptions, FsmTransition, FsmTransitionEvent, FsmTransitionState, FsmTransitionType, ValidatedFsm}, parse_blocks::{FsmBlock, get_generics}, utils::{assert_no_generics, to_field_name, get_closure}, validation::create_regions}; +use crate::{parse::{EventGuardAction, FsmDeclarations, FsmEvent, FsmEventTransition, FsmFnBase, FsmState, FsmStateAction, FsmStateKind, FsmStateTransition, FsmSubMachineOptions, FsmTimer, FsmTransition, FsmTransitionEvent, FsmTransitionState, FsmTransitionType, ValidatedFsm}, parse_blocks::{FsmBlock, get_generics}, utils::{assert_no_generics, to_field_name, get_closure}, validation::create_regions}; #[derive(Copy, Clone, Debug)] pub struct FsmCodegenOptions { @@ -23,7 +23,8 @@ pub struct FsmParser { states: HashMap, events: HashMap, options: FsmCodegenOptions, - base: FsmFnBase + base: FsmFnBase, + timer_id: usize } impl FsmParser { @@ -33,7 +34,8 @@ impl FsmParser { states: HashMap::new(), events: HashMap::new(), options: FsmCodegenOptions::new(), - base + base, + timer_id: 1 } } @@ -88,7 +90,8 @@ impl FsmParser { state_storage_field: field_name, on_entry_closure: None, on_exit_closure: None, - kind: FsmStateKind::SubMachine(FsmSubMachineOptions::default()) + kind: FsmStateKind::SubMachine(FsmSubMachineOptions::default()), + timers: vec![] }); let mut sub_options = match state.kind { FsmStateKind::SubMachine(ref sub) => sub.clone(), @@ -283,7 +286,8 @@ impl FsmParser { on_entry_closure: None, on_exit_closure: None, state_storage_field: field_name, - kind: FsmStateKind::Normal + kind: FsmStateKind::Normal, + timers: vec![] }); for (i, method) in st.iter().enumerate() { @@ -317,6 +321,30 @@ impl FsmParser { break; }, + MethodOverviewRef { name: "on_entry_start_timer", generics: [], .. } => { + + let call_args: Vec<_> = method.call.args.iter().collect(); + match call_args.as_slice() { + [syn::Expr::Closure(ref setup), syn::Expr::Closure(ref trigger)] => { + + let timer = FsmTimer { + setup: setup.clone(), + trigger: trigger.clone(), + id: self.timer_id + }; + + state.timers.push(timer); + + self.timer_id += 1; + + }, + _ => { + return Err(syn::Error::new(method.call.span(), "Unexpected arguments to the timer setup method.")); + } + } + + }, + _ => { return Err(syn::Error::new(method.call.span(), format!("Unsupported method '{}'!", method.name))); } } } diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index cd16e19..a1127a5 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -1,5 +1,7 @@ extern crate finny; +use std::time::Duration; + use finny::{FsmCurrentState, FsmError, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::{self, InspectSlog}}; use slog::{Drain, Logger, info, o}; @@ -9,7 +11,7 @@ pub struct TimersMachineContext { #[derive(Default)] pub struct StateA { - + timers: usize } #[derive(Default)] pub struct StateB { @@ -19,6 +21,9 @@ pub struct StateB { pub struct StateC; #[derive(Clone, Debug)] pub struct EventClick { time: usize } +#[derive(Clone, Debug)] +pub struct EventTimer; + #[derive(Clone, Debug)] pub struct EventEnter { shift: bool } @@ -30,8 +35,30 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF fsm.state::(); fsm.state::() + .on_exit(|state, ctx| { + state.timers = 0; + }) .on_event::() - .transition_to::(); + .transition_to::() + .guard(|ev, ctx, states| { + let state: &StateA = states.as_ref(); + state.timers > 5 + }); + + fsm.state::() + .on_event::() + .internal_transition() + .action(|ev, ctx, state| { + state.timers += 1; + }); + + fsm.state::() + .on_entry_start_timer(|_ctx, timer| { + timer.timeout = Duration::from_millis(100); + timer.renew = true; + }, |ctx, state| { + Some( EventTimer.into() ) + }); fsm.state::(); From 2053ba7d9d6d5fd50e86ae9f83540f87c3faab4a Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 19 Jan 2021 23:51:05 +0100 Subject: [PATCH 03/38] create the basic structs --- finny/src/fsm/fsm_factory.rs | 14 ++++++++------ finny/src/fsm/fsm_impl.rs | 17 +++++++++-------- finny/src/fsm/mod.rs | 3 ++- finny/src/fsm/timers.rs | 22 ++++++++++++++++++++-- finny_derive/src/codegen.rs | 23 +++++++++++++++++++++++ finny_tests/tests/fsm_fn.rs | 4 ++-- finny_tests/tests/fsm_sub.rs | 4 ++-- finny_tests/tests/fsm_sub_generic.rs | 4 ++-- finny_tests/tests/fsm_timers.rs | 6 ++++-- 9 files changed, 72 insertions(+), 25 deletions(-) diff --git a/finny/src/fsm/fsm_factory.rs b/finny/src/fsm/fsm_factory.rs index 5f97591..4ad6472 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, Inspect, InspectNull}; +use crate::{FsmBackend, FsmBackendImpl, FsmEventQueue, FsmFrontend, FsmResult, FsmTimers, FsmTimersNull, Inspect, InspectNull}; #[cfg(feature="std")] use crate::FsmEventQueueVec; @@ -11,13 +11,14 @@ pub trait FsmFactory { fn new_submachine_backend(backend: FsmBackendImpl) -> FsmResult where Self: Sized; /// Build a new frontend for the FSM with all the environmental services provided by the caller. - fn new_with(context: ::Context, queue: Q, inspect: I) -> FsmResult> - where Q: FsmEventQueue, I: Inspect + fn new_with(context: ::Context, queue: Q, inspect: I, timers: T) -> FsmResult> + where Q: FsmEventQueue, I: Inspect, T: FsmTimers { let frontend = FsmFrontend { queue, inspect, - backend: FsmBackendImpl::new(context)? + backend: FsmBackendImpl::new(context)?, + timers }; Ok(frontend) @@ -25,11 +26,12 @@ pub trait FsmFactory { /// Build a new frontend for the FSM with a `FsmEventQueueVec` queue and no logging. #[cfg(feature="std")] - fn new(context: ::Context) -> FsmResult, InspectNull>> { + fn new(context: ::Context) -> FsmResult, InspectNull, FsmTimersNull>> { let frontend = FsmFrontend { queue: FsmEventQueueVec::new(), backend: FsmBackendImpl::new(context)?, - inspect: InspectNull::new() + inspect: InspectNull::new(), + timers: FsmTimersNull::default() }; Ok(frontend) diff --git a/finny/src/fsm/fsm_impl.rs b/finny/src/fsm/fsm_impl.rs index 17fe784..99bfd76 100644 --- a/finny/src/fsm/fsm_impl.rs +++ b/finny/src/fsm/fsm_impl.rs @@ -1,4 +1,4 @@ -use crate::{DispatchContext, Inspect, lib::*}; +use crate::{DispatchContext, FsmTimers, Inspect, lib::*}; use crate::{FsmBackend, FsmEvent, FsmEventQueue, FsmResult, FsmStates}; use super::FsmStateFactory; @@ -51,16 +51,17 @@ impl Deref for FsmBackendImpl { /// The frontend of a state machine which also includes environmental services like queues /// and inspection. The usual way to use the FSM. -pub struct FsmFrontend - where F: FsmBackend, Q: FsmEventQueue, I: Inspect +pub struct FsmFrontend + where F: FsmBackend, Q: FsmEventQueue, I: Inspect, T: FsmTimers { pub backend: FsmBackendImpl, pub queue: Q, - pub inspect: I + pub inspect: I, + pub timers: T } -impl FsmFrontend - where F: FsmBackend, Q: FsmEventQueue, I: Inspect +impl FsmFrontend + where F: FsmBackend, Q: FsmEventQueue, I: Inspect, T: FsmTimers { /// Start the FSM, initiates the transition to the initial state. pub fn start(&mut self) -> FsmResult<()> { @@ -95,8 +96,8 @@ impl FsmFrontend } } -impl Deref for FsmFrontend - where F: FsmBackend, Q: FsmEventQueue, I: Inspect +impl Deref for FsmFrontend + where F: FsmBackend, Q: FsmEventQueue, I: Inspect, T: FsmTimers { type Target = FsmBackendImpl; diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index da0ae7a..222d7cb 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -30,7 +30,8 @@ pub type FsmResult = Result; #[derive(Debug, PartialEq)] pub enum FsmError { NoTransition, - QueueOverCapacity + QueueOverCapacity, + NotSupported } pub type FsmDispatchResult = FsmResult<()>; diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index 5d632bd..b2ff0c0 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -1,4 +1,4 @@ -use crate::lib::*; +use crate::{FsmError, lib::*}; use crate::{FsmBackend, FsmResult}; @@ -53,7 +53,8 @@ pub trait FsmTimers { fn create(&mut self, id: TimerId, settings: TimerSettings) -> FsmResult<()>; fn cancel(&mut self, id: TimerId) -> FsmResult<()>; - /// Return the latest timer that was triggered. Poll this until it returns None. + /// Return the timer that was triggered. Poll this until it returns None. The events + /// should be dequeued in a FIFO manner. fn get_triggered_timer(&mut self) -> Option; } @@ -61,4 +62,21 @@ pub trait FsmTimers { #[derive(Debug, Copy, Clone)] pub struct FsmTimersTriggerEventsResult { pub triggered_events: usize +} + +#[derive(Debug, Default, Copy, Clone)] +pub struct FsmTimersNull; + +impl FsmTimers for FsmTimersNull { + fn create(&mut self, id: TimerId, settings: TimerSettings) -> FsmResult<()> { + Err(FsmError::NotSupported) + } + + fn cancel(&mut self, id: TimerId) -> FsmResult<()> { + Err(FsmError::NotSupported) + } + + fn get_triggered_timer(&mut self) -> Option { + None + } } \ No newline at end of file diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 328639f..47bd9ee 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -1,5 +1,6 @@ use proc_macro2::{TokenStream}; use quote::{TokenStreamExt, quote}; +use syn::punctuated::Punctuated; use crate::{fsm::FsmTypes, parse::{FsmState, FsmStateKind}, utils::{remap_closure_inputs}}; use crate::{parse::{FsmFnInput, FsmStateTransition, FsmTransitionState, FsmTransitionType}, utils::ty_append}; @@ -626,6 +627,26 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream } }; + let timers = { + + let mut code = TokenStream::new(); + + let timers = fsm.fsm.states.iter().map(|s| &s.1.timers).flatten(); + for timer in timers { + let timer_ty = ty_append(&fsm.base.fsm_ty, &format!("Timer{}", timer.id)); + + code.append_all(quote! { + + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] + pub struct #timer_ty; + + }); + + } + + code + }; + let mut q = quote! { #states_store @@ -638,6 +659,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream #dispatch #builder + + #timers }; // this goes in front of our definition function diff --git a/finny_tests/tests/fsm_fn.rs b/finny_tests/tests/fsm_fn.rs index c4ee183..46903db 100644 --- a/finny_tests/tests/fsm_fn.rs +++ b/finny_tests/tests/fsm_fn.rs @@ -1,6 +1,6 @@ extern crate finny; -use finny::{FsmCurrentState, FsmError, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::{self, InspectSlog}}; +use finny::{FsmCurrentState, FsmError, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::{self, InspectSlog}}; use slog::{Drain, Logger, info, o}; #[derive(Debug)] @@ -76,7 +76,7 @@ fn test_fsm() -> FsmResult<()> { let ctx = StateMachineContext { count: 0, total_time: 0 }; - let mut fsm = StateMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)))?; + let mut fsm = StateMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), FsmTimersNull)?; let current_state = fsm.get_current_states()[0]; let state: &StateA = fsm.get_state(); diff --git a/finny_tests/tests/fsm_sub.rs b/finny_tests/tests/fsm_sub.rs index 570e145..0151a0b 100644 --- a/finny_tests/tests/fsm_sub.rs +++ b/finny_tests/tests/fsm_sub.rs @@ -1,6 +1,6 @@ extern crate finny; -use finny::{FsmCurrentState, FsmError, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::InspectSlog}; +use finny::{FsmCurrentState, FsmError, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::InspectSlog}; use slog::{Drain, o}; #[derive(Default)] @@ -132,7 +132,7 @@ fn test_sub() -> FsmResult<()> { let logger = slog::Logger::root(drain, o!()); - let mut fsm = StateMachine::new_with(MainContext::default(), FsmEventQueueVec::new(), InspectSlog::new(Some(logger)))?; + let mut fsm = StateMachine::new_with(MainContext::default(), FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), FsmTimersNull)?; fsm.start()?; assert_eq!(FsmCurrentState::State(StateMachineCurrentState::StateA), fsm.get_current_states()[0]); diff --git a/finny_tests/tests/fsm_sub_generic.rs b/finny_tests/tests/fsm_sub_generic.rs index 5cc19ad..96ca605 100644 --- a/finny_tests/tests/fsm_sub_generic.rs +++ b/finny_tests/tests/fsm_sub_generic.rs @@ -2,7 +2,7 @@ extern crate finny; use std::ops::{Add, AddAssign}; -use finny::{FsmCurrentState, FsmError, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::InspectSlog}; +use finny::{FsmCurrentState, FsmError, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::InspectSlog}; use slog::{Drain, o}; #[derive(Debug)] @@ -110,7 +110,7 @@ fn test_sub_generics() -> FsmResult<()> { field: 0usize, some_string: "Hello".into() }; - let mut fsm = StateMachine::::new_with(main_ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)))?; + let mut fsm = StateMachine::::new_with(main_ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), FsmTimersNull)?; fsm.start()?; assert_eq!(FsmCurrentState::State(StateMachineCurrentState::StateA), fsm.get_current_states()[0]); diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index a1127a5..e1d2f6a 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -2,7 +2,7 @@ extern crate finny; use std::time::Duration; -use finny::{FsmCurrentState, FsmError, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::{self, InspectSlog}}; +use finny::{FsmCurrentState, FsmError, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::{self, InspectSlog}}; use slog::{Drain, Logger, info, o}; #[derive(Debug)] @@ -76,7 +76,9 @@ fn test_timers_fsm() -> FsmResult<()> { let ctx = TimersMachineContext { }; - let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)))?; + let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), FsmTimersNull)?; + + let foo = TimersMachineTimer1::default(); fsm.start()?; Ok(()) From ff700adefe7474896d7075166fbd4b5f71c5baba Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Thu, 21 Jan 2021 00:09:42 +0100 Subject: [PATCH 04/38] wip --- finny/src/decl/state.rs | 4 +- finny/src/fsm/dispatch.rs | 23 ++++---- finny/src/fsm/fsm_impl.rs | 3 +- finny/src/fsm/mod.rs | 4 +- finny/src/fsm/tests_fsm.rs | 4 +- finny/src/fsm/timers.rs | 8 +-- finny/src/fsm/transitions.rs | 39 ++++++++------ finny_derive/src/codegen.rs | 94 +++++++++++++++++++++++++++++---- finny_derive/src/parse.rs | 12 ++++- finny_derive/src/parse_fsm.rs | 4 +- finny_derive/src/utils.rs | 4 +- finny_tests/tests/fsm_timers.rs | 2 +- 12 files changed, 147 insertions(+), 54 deletions(-) diff --git a/finny/src/decl/state.rs b/finny/src/decl/state.rs index 328e512..2e3d406 100644 --- a/finny/src/decl/state.rs +++ b/finny/src/decl/state.rs @@ -1,4 +1,4 @@ -use crate::{TimerSettings, lib::*}; +use crate::{TimerFsmSettings, lib::*}; use crate::{EventContext, FsmBackend}; use super::{FsmQueueMock, event::FsmEventBuilderState}; @@ -35,7 +35,7 @@ impl FsmStateBuilder /// that returns an event to be enqueued in the FSM. pub fn on_entry_start_timer(&self, _setup: FSetup, _trigger: FTrigger) -> &Self where - FSetup: Fn(&TContext, &mut TimerSettings), + FSetup: Fn(&TContext, &mut TimerFsmSettings), FTrigger: Fn(&TContext, &TState) -> Option< ::Events > { self diff --git a/finny/src/fsm/dispatch.rs b/finny/src/fsm/dispatch.rs index fb6bfb0..38da685 100644 --- a/finny/src/fsm/dispatch.rs +++ b/finny/src/fsm/dispatch.rs @@ -1,20 +1,23 @@ -use crate::lib::*; +use crate::{FsmTimers, lib::*}; use crate::{EventContext, FsmBackend, FsmBackendImpl, FsmEvent, FsmEventQueue, FsmEventQueueSub, FsmRegionId, FsmResult, Inspect}; -pub struct DispatchContext<'a, 'b, 'c, F, Q, I> +pub struct DispatchContext<'a, 'b, 'c, F, Q, I, T> where F: FsmBackend, Q: FsmEventQueue, - I: Inspect + I: Inspect, + T: FsmTimers { pub queue: &'a mut Q, pub inspect: &'b mut I, - pub backend: &'c mut FsmBackendImpl + pub backend: &'c mut FsmBackendImpl, + pub timers: &'a mut T } -impl<'a, 'b, 'c, F, Q, I> DispatchContext<'a, 'b, 'c, F, Q, I> +impl<'a, 'b, 'c, F, Q, I, T> DispatchContext<'a, 'b, 'c, F, Q, I, T> where F: FsmBackend, Q: FsmEventQueue, - I: Inspect + I: Inspect, + T: FsmTimers { pub fn to_event_context(&'a mut self, region: FsmRegionId) -> EventContext<'a, F, Q> @@ -28,7 +31,7 @@ where F: FsmBackend, } /// Used to funnel the event down to the sub-machine. -pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, Q, I>(ctx: &mut DispatchContext<'a, 'b, 'c, TFsm, Q, I>, ev: &TEvent, inspect_event_ctx: &mut I) +pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, Q, I, T>(ctx: &mut DispatchContext<'a, 'b, 'c, TFsm, Q, I, T>, ev: &TEvent, inspect_event_ctx: &mut I) -> FsmResult<()> where TFsm: FsmBackend, @@ -37,7 +40,8 @@ pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, Q, I>(ctx: Q: FsmEventQueue, I: Inspect, ::Events: From<::Events>, - TEvent: Clone + TEvent: Clone, + T: FsmTimers { let sub_fsm: &mut TSubMachine = ctx.backend.states.as_mut(); @@ -52,7 +56,8 @@ pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, Q, I>(ctx: let sub_dispatch_ctx = DispatchContext { backend: sub_fsm, inspect: &mut inspect, - queue: &mut queue_adapter + queue: &mut queue_adapter, + timers: ctx.timers }; ::dispatch_event(sub_dispatch_ctx, FsmEvent::Event(ev.clone())) diff --git a/finny/src/fsm/fsm_impl.rs b/finny/src/fsm/fsm_impl.rs index 99bfd76..f71fb8d 100644 --- a/finny/src/fsm/fsm_impl.rs +++ b/finny/src/fsm/fsm_impl.rs @@ -89,7 +89,8 @@ impl FsmFrontend let dispatch_ctx = DispatchContext { backend: &mut self.backend, inspect: &mut self.inspect, - queue: &mut self.queue + queue: &mut self.queue, + timers: &mut self.timers }; F::dispatch_event(dispatch_ctx, event) diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 222d7cb..7cb12ab 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -47,6 +47,6 @@ pub trait FsmBackend where Self: Sized { /// the dispatch into sub-machines and into multiple regions. type Events: AsRef + Clone; - fn dispatch_event(ctx: DispatchContext, event: FsmEvent) -> FsmDispatchResult - where Q: FsmEventQueue, I: Inspect; + fn dispatch_event(ctx: DispatchContext, event: FsmEvent) -> FsmDispatchResult + where Q: FsmEventQueue, I: Inspect, T: FsmTimers; } \ No newline at end of file diff --git a/finny/src/fsm/tests_fsm.rs b/finny/src/fsm/tests_fsm.rs index 96c2d7b..6cb7da4 100644 --- a/finny/src/fsm/tests_fsm.rs +++ b/finny/src/fsm/tests_fsm.rs @@ -42,9 +42,9 @@ impl FsmBackend for TestFsm { type States = States; type Events = Events; - fn dispatch_event(_ctx: crate::DispatchContext, _event: crate::FsmEvent) -> crate::FsmDispatchResult + fn dispatch_event(_ctx: crate::DispatchContext, _event: crate::FsmEvent) -> crate::FsmDispatchResult where Q: crate::FsmEventQueue, - I: crate::Inspect + I: crate::Inspect, T: crate::FsmTimers { todo!() } diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index b2ff0c0..ad473cd 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -3,9 +3,9 @@ use crate::{FsmBackend, FsmResult}; pub trait FsmTimer - where F: FsmBackend + where F: FsmBackend, Self: Default { - fn setup(ctx: &::Context, settings: &mut TimerSettings); + fn setup(ctx: &::Context, settings: &mut TimerFsmSettings); fn trigger(ctx: &::Context, state: &S) -> Option< ::Events >; } @@ -50,7 +50,7 @@ pub struct TimerSettings pub type TimerId = usize; pub trait FsmTimers { - fn create(&mut self, id: TimerId, settings: TimerSettings) -> FsmResult<()>; + fn create(&mut self, settings: TimerSettings) -> FsmResult; fn cancel(&mut self, id: TimerId) -> FsmResult<()>; /// Return the timer that was triggered. Poll this until it returns None. The events @@ -68,7 +68,7 @@ pub struct FsmTimersTriggerEventsResult { pub struct FsmTimersNull; impl FsmTimers for FsmTimersNull { - fn create(&mut self, id: TimerId, settings: TimerSettings) -> FsmResult<()> { + fn create(&mut self, settings: TimerSettings) -> FsmResult { Err(FsmError::NotSupported) } diff --git a/finny/src/fsm/transitions.rs b/finny/src/fsm/transitions.rs index f00b61c..a64016e 100644 --- a/finny/src/fsm/transitions.rs +++ b/finny/src/fsm/transitions.rs @@ -1,6 +1,6 @@ //! All of these traits will be implemented by the procedural code generator. -use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, lib::*}; +use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, FsmTimers, FsmTimersNull, lib::*}; use crate::{DispatchContext, EventContext, FsmBackend, FsmCurrentState, FsmEvent, FsmEventQueue, FsmFrontend, FsmRegionId, FsmStateTransitionAsMut, FsmStates, Inspect}; @@ -11,8 +11,8 @@ pub trait FsmState { /// Action that is executed whenever this state is being exited. fn on_exit<'a, Q: FsmEventQueue>(&mut self, context: &mut EventContext<'a, F, Q>); - fn execute_on_entry<'a, 'b, 'c, 'd, Q, I>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I>, region: FsmRegionId) - where Q: FsmEventQueue, I: Inspect, ::States: AsMut + fn execute_on_entry<'a, 'b, 'c, 'd, Q, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, region: FsmRegionId) + where Q: FsmEventQueue, I: Inspect, ::States: AsMut, T: FsmTimers { let mut event_context = EventContext { context: &mut context.backend.context, @@ -24,8 +24,8 @@ pub trait FsmState { state.on_entry(&mut event_context); } - fn execute_on_exit<'a, 'b, 'c, 'd, Q, I>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I>, region: FsmRegionId) - where Q: FsmEventQueue, I: Inspect, ::States: AsMut + fn execute_on_exit<'a, 'b, 'c, 'd, Q, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, region: FsmRegionId) + where Q: FsmEventQueue, I: Inspect, ::States: AsMut, T: FsmTimers { let mut event_context = EventContext { context: &mut context.backend.context, @@ -45,8 +45,8 @@ pub trait FsmTransitionGuard { /// Return a boolean value whether this transition is usable at the moment. The check shouln't mutate any structures. fn guard<'a, Q: FsmEventQueue>(event: &E, context: &EventContext<'a, F, Q>, states: &'a ::States) -> bool; - fn execute_guard<'a, 'b, 'c, 'd, Q: FsmEventQueue, I>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I>, event: &E, region: FsmRegionId, inspect_event_ctx: &mut I) -> bool - where I: Inspect, Self: Sized + fn execute_guard<'a, 'b, 'c, 'd, Q: FsmEventQueue, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, event: &E, region: FsmRegionId, inspect_event_ctx: &mut I) -> bool + where I: Inspect, Self: Sized, T: FsmTimers { let event_context = EventContext { context: &mut context.backend.context, @@ -65,7 +65,7 @@ pub trait FsmTransitionGuard { /// The transition that starts the machine, triggered using the `start()` method. pub trait FsmTransitionFsmStart { - fn execute_transition<'a, 'b, 'c, 'd, Q: FsmEventQueue, I >(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I>, + fn execute_transition<'a, 'b, 'c, 'd, Q: FsmEventQueue, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, _fsm_event: &FsmEvent<::Events>, region: FsmRegionId, inspect_event_ctx: &mut I) @@ -75,6 +75,7 @@ pub trait FsmTransitionFsmStart { ::States: AsMut, ::States: AsRef, Self: Sized, + T: FsmTimers { let ctx = inspect_event_ctx.for_transition::(); ctx.on_state_enter::(); @@ -91,14 +92,15 @@ pub trait FsmTransitionAction { /// This action is executed after the first state's exit event, and just before the second event's entry action. It can mutate both states. fn action<'a, Q: FsmEventQueue>(event: &E, context: &mut EventContext<'a, F, Q>, from: &mut TStateFrom, to: &mut TStateTo); - fn execute_transition<'a, 'b, 'c, 'd, Q: FsmEventQueue, I>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I>, event: &E, region: FsmRegionId, inspect_event_ctx: &mut I) + fn execute_transition<'a, 'b, 'c, 'd, Q: FsmEventQueue, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, event: &E, region: FsmRegionId, inspect_event_ctx: &mut I) where I: Inspect, ::States: FsmStateTransitionAsMut, ::States: AsMut, ::States: AsMut, TStateFrom: FsmState, - TStateTo: FsmState, Self: Sized + TStateTo: FsmState, Self: Sized, + T: FsmTimers { let inspect_ctx = inspect_event_ctx.for_transition::(); @@ -126,7 +128,7 @@ pub trait FsmTransitionAction { /// Executed after the transition on the parent FSM (F) and triggers the first `start()` call if necessary. Subsequent /// dispatches are handled using the main dispatch table. - fn execute_on_sub_entry<'a, 'b, 'c, 'd, Q, I>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I>, _region: FsmRegionId, inspect_event_ctx: &mut I) + fn execute_on_sub_entry<'a, 'b, 'c, 'd, Q, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, _region: FsmRegionId, inspect_event_ctx: &mut I) -> FsmDispatchResult where TStateTo: FsmBackend, @@ -134,7 +136,8 @@ pub trait FsmTransitionAction { I: Inspect, ::Events: From<::Events>, ::States: AsMut, - TStateTo: DerefMut> + TStateTo: DerefMut>, + T: FsmTimers { let sub_backend: &mut TStateTo = context.backend.states.as_mut(); let states = sub_backend.get_current_states(); @@ -150,7 +153,8 @@ pub trait FsmTransitionAction { let sub_dispatch_context = DispatchContext { backend: sub_backend, inspect: &mut inspect, - queue: &mut queue_adapter + queue: &mut queue_adapter, + timers: context.timers }; return TStateTo::dispatch_event(sub_dispatch_context, FsmEvent::Start); @@ -167,8 +171,8 @@ pub trait FsmAction { /// Is this a self transition which should trigger the state's exit and entry actions? fn should_trigger_state_actions() -> bool; - fn execute_action<'a, 'b, 'c, 'd, Q: FsmEventQueue, I >(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I>, event: &E, region: FsmRegionId) - where ::States: AsMut, I: Inspect + fn execute_action<'a, 'b, 'c, 'd, Q: FsmEventQueue, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, event: &E, region: FsmRegionId) + where ::States: AsMut, I: Inspect, T: FsmTimers { let mut event_context = EventContext { context: &mut context.backend.context, @@ -181,10 +185,11 @@ pub trait FsmAction { Self::action(event, &mut event_context, state); } - fn execute_transition<'a, 'b, 'c, 'd, Q: FsmEventQueue, I>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I>, event: &E, region: FsmRegionId, inspect_event_ctx: &mut I) + fn execute_transition<'a, 'b, 'c, 'd, Q: FsmEventQueue, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, event: &E, region: FsmRegionId, inspect_event_ctx: &mut I) where I: Inspect, State: FsmState, - ::States: AsMut, Self: Sized + ::States: AsMut, Self: Sized, + T: FsmTimers { let ctx = inspect_event_ctx.for_transition::(); diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 47bd9ee..3b2ecb9 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -1,7 +1,7 @@ use proc_macro2::{TokenStream}; use quote::{TokenStreamExt, quote}; use syn::punctuated::Punctuated; -use crate::{fsm::FsmTypes, parse::{FsmState, FsmStateKind}, utils::{remap_closure_inputs}}; +use crate::{fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs}}; use crate::{parse::{FsmFnInput, FsmStateTransition, FsmTransitionState, FsmTransitionType}, utils::ty_append}; @@ -32,6 +32,14 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let ty = state_ty.get_fsm_ty(); let ty_name = state_ty.get_fsm_no_generics_ty(); + for timer in &state.timers { + let timer_ty = timer.get_ty(&fsm.base); + let timer_field = timer.get_field(&fsm.base); + + code_fields.append_all(quote! { #timer_field: #timer_ty, }); + new_state_fields.append_all(quote! { #timer_field: #timer_ty::default(), }); + } + code_fields.append_all(quote! { #name: #ty, }); state_variants.append_all(quote!{ #ty_name, }); @@ -448,6 +456,41 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream _ => TokenStream::new() }; + let timers_enter = { + let mut timers_enter = TokenStream::new(); + + let state = match &transition.ty { + FsmTransitionType::SelfTransition(FsmStateAction { state: FsmTransitionState::State(st @ FsmState { kind: FsmStateKind::Normal, .. }), .. }) => { + Some(st) + }, + FsmTransitionType::StateTransition(FsmStateTransition { state_to: FsmTransitionState::State(st @ FsmState { kind: FsmStateKind::Normal, .. }), .. }) => { + Some(st) + }, + _ => None + }; + + if let Some(state) = state { + for timer in &state.timers { + let timer_ty = timer.get_ty(&fsm.base); + let timer_field = timer.get_field(&fsm.base); + + timers_enter.append_all(quote! { + + { + use finny::FsmTimer; + + let mut settings = finny::TimerFsmSettings::default(); + < #timer_ty > :: setup ( &ctx.backend.context, &mut settings); + //ctx. + } + + }); + } + } + + timers_enter + }; + let m = quote! { ( #match_state , #match_event ) #guard => { @@ -455,6 +498,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream #fsm_sub_entry + #timers_enter }, }; @@ -482,7 +526,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let sub = quote! { ( finny::FsmCurrentState::State(#states_enum_ty :: #kind_variant), finny::FsmEvent::Event(#event_enum_ty::#kind_variant(ev)) ) => { - return finny::dispatch_to_submachine::<_, #kind, _, _, _>(&mut ctx, ev, &mut inspect_event_ctx); + return finny::dispatch_to_submachine::<_, #kind, _, _, _, _>(&mut ctx, ev, &mut inspect_event_ctx); }, }; @@ -515,9 +559,9 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream type States = #states_store_ty #fsm_generics_type; type Events = #event_enum_ty; - fn dispatch_event(mut ctx: finny::DispatchContext, event: finny::FsmEvent) -> finny::FsmDispatchResult + fn dispatch_event(mut ctx: finny::DispatchContext, event: finny::FsmEvent) -> finny::FsmDispatchResult where Q: finny::FsmEventQueue, - I: finny::Inspect + I: finny::Inspect, T: finny::FsmTimers { use finny::{FsmTransitionGuard, FsmTransitionAction, FsmAction, FsmState, FsmTransitionFsmStart}; @@ -631,17 +675,45 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let mut code = TokenStream::new(); - let timers = fsm.fsm.states.iter().map(|s| &s.1.timers).flatten(); - for timer in timers { - let timer_ty = ty_append(&fsm.base.fsm_ty, &format!("Timer{}", timer.id)); + let states = fsm.fsm.states.iter().map(|s| s.1); + for state in states { - code.append_all(quote! { + for timer in &state.timers { + let state_ty = &state.ty; + let timer_ty = timer.get_ty(&fsm.base); - #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Default)] - pub struct #timer_ty; + let setup = remap_closure_inputs(&timer.setup.inputs, &[quote! { ctx }, quote! { settings }])?; + let setup_body = &timer.setup.body; - }); + let trigger = remap_closure_inputs(&timer.trigger.inputs, &[quote! { ctx }, quote! { state }])?; + let trigger_body = &timer.trigger.body; + code.append_all(quote! { + + #[derive(Debug, Clone, Default)] + pub struct #timer_ty { + instance: Option<(finny::TimerId, finny::TimerFsmSettings)> + } + + impl #fsm_generics_impl finny::FsmTimer< #fsm_ty #fsm_generics_type , #state_ty > for #timer_ty #fsm_generics_where { + fn setup(ctx: & #ctx_ty, settings: &mut finny::TimerFsmSettings) { + #setup + { + #setup_body + } + } + + fn trigger(ctx: & #ctx_ty, state: & #state_ty ) -> Option< #event_enum_ty > { + #trigger + let ret = { + #trigger_body + }; + ret + } + } + + }); + } } code diff --git a/finny_derive/src/parse.rs b/finny_derive/src/parse.rs index 1255428..a57378b 100644 --- a/finny_derive/src/parse.rs +++ b/finny_derive/src/parse.rs @@ -3,7 +3,7 @@ use std::collections::{HashMap, HashSet}; use proc_macro2::{Span, TokenStream}; use syn::{Error, Expr, ExprMethodCall, GenericArgument, ItemFn, parse::{self, Parse, ParseStream}, spanned::Spanned}; -use crate::{parse_blocks::{FsmBlock, decode_blocks, get_generics, get_method_receiver_ident}, parse_fsm::{FsmCodegenOptions, FsmParser}, utils::{assert_no_generics, get_closure, to_field_name}}; +use crate::{parse_blocks::{FsmBlock, decode_blocks, get_generics, get_method_receiver_ident}, parse_fsm::{FsmCodegenOptions, FsmParser}, utils::{assert_no_generics, get_closure, to_field_name, ty_append}}; pub struct FsmFnInput { @@ -251,6 +251,16 @@ pub struct FsmTimer { pub trigger: syn::ExprClosure } +impl FsmTimer { + pub fn get_ty(&self, fsm: &FsmFnBase) -> syn::Type { + ty_append(&fsm.fsm_ty, &format!("Timer{}", self.id)) + } + + pub fn get_field(&self, fsm: &FsmFnBase) -> syn::Ident { + to_field_name(&self.get_ty(fsm)) + } +} + #[derive(Debug, Clone)] pub struct FsmEvent { pub ty: syn::Type, diff --git a/finny_derive/src/parse_fsm.rs b/finny_derive/src/parse_fsm.rs index 00f6c72..3e8cf00 100644 --- a/finny_derive/src/parse_fsm.rs +++ b/finny_derive/src/parse_fsm.rs @@ -81,7 +81,7 @@ impl FsmParser { [MethodOverviewRef { name: "sub_machine", generics: [ty_sub_fsm], ..}, st @ .. ] => { //assert_no_generics(ty_sub_fsm)?; - let field_name = to_field_name(&ty_sub_fsm)?; + let field_name = to_field_name(&ty_sub_fsm); let state = self.states .entry(ty_sub_fsm.clone()) @@ -278,7 +278,7 @@ impl FsmParser { fn state_builder_parser(&mut self, ty_state: &syn::Type, st: &[MethodOverviewRef], is_sub_fsm: bool) -> syn::Result<()> { if !is_sub_fsm { assert_no_generics(ty_state)?; } - let field_name = to_field_name(&ty_state)?; + let field_name = to_field_name(&ty_state); let state = self.states .entry(ty_state.clone()) .or_insert(FsmState { diff --git a/finny_derive/src/utils.rs b/finny_derive/src/utils.rs index 9485697..51d72ad 100644 --- a/finny_derive/src/utils.rs +++ b/finny_derive/src/utils.rs @@ -51,12 +51,12 @@ pub fn strip_generics(mut ty: syn::Type) -> syn::Type { ty } -pub fn to_field_name(ty: &syn::Type) -> syn::Result { +pub fn to_field_name(ty: &syn::Type) -> syn::Ident { let ty = strip_generics(ty.clone()); let s = tokens_to_string(&ty); let snake = to_snake_case(&s); - Ok(syn::Ident::new(&snake, ty.span())) + syn::Ident::new(&snake, ty.span()) } pub fn tokens_to_string(t: &T) -> String { diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index e1d2f6a..4260333 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -78,7 +78,7 @@ fn test_timers_fsm() -> FsmResult<()> { let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), FsmTimersNull)?; - let foo = TimersMachineTimer1::default(); + //let foo = TimersMachineTimer1::default(); fsm.start()?; Ok(()) From 6c553bdee5060f873fc149bb622b4c104e084b51 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Thu, 21 Jan 2021 19:05:02 +0100 Subject: [PATCH 05/38] basic timers --- finny/src/fsm/inspect.rs | 9 ++++++++- finny/src/fsm/timers.rs | 4 ++-- finny/src/inspect_slog.rs | 7 ++++++- finny_derive/src/codegen.rs | 11 ++++++++++- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/finny/src/fsm/inspect.rs b/finny/src/fsm/inspect.rs index da6b9d8..0697065 100644 --- a/finny/src/fsm/inspect.rs +++ b/finny/src/fsm/inspect.rs @@ -1,4 +1,5 @@ -use crate::{FsmBackend, FsmEvent}; +use crate::lib::*; +use crate::{FsmBackend, FsmError, FsmEvent}; pub trait Inspect { fn new_event(&self, event: &FsmEvent<::Events>) -> Self; @@ -11,6 +12,8 @@ pub trait Inspect { 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; } #[derive(Default)] @@ -54,4 +57,8 @@ impl Inspect for InspectNull { fn event_done(self) { } + + fn on_error(&self, msg: &str, error: &E) where E: Debug { + + } } \ No newline at end of file diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index ad473cd..c645b50 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -50,7 +50,7 @@ pub struct TimerSettings pub type TimerId = usize; pub trait FsmTimers { - fn create(&mut self, settings: TimerSettings) -> FsmResult; + fn create(&mut self, id: TimerId, settings: &TimerSettings) -> FsmResult<()>; fn cancel(&mut self, id: TimerId) -> FsmResult<()>; /// Return the timer that was triggered. Poll this until it returns None. The events @@ -68,7 +68,7 @@ pub struct FsmTimersTriggerEventsResult { pub struct FsmTimersNull; impl FsmTimers for FsmTimersNull { - fn create(&mut self, settings: TimerSettings) -> FsmResult { + fn create(&mut self, id: TimerId, settings: &TimerSettings) -> FsmResult<()> { Err(FsmError::NotSupported) } diff --git a/finny/src/inspect_slog.rs b/finny/src/inspect_slog.rs index d88802a..b88fedd 100644 --- a/finny/src/inspect_slog.rs +++ b/finny/src/inspect_slog.rs @@ -1,4 +1,4 @@ -use slog::{info, o}; +use slog::{info, o, error}; use crate::{FsmBackend, FsmEvent, Inspect}; use super::lib::*; use AsRef; @@ -66,4 +66,9 @@ impl Inspect for InspectSlog { fn event_done(self) { info!(self.logger, "Dispatch done"); } + + fn on_error(&self, msg: &str, error: &E) where E: Debug { + let kv = o!("error" => format!("{:?}", error)); + error!(self.logger, "{}", msg; kv); + } } \ No newline at end of file diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 3b2ecb9..67dc060 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -473,15 +473,24 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream for timer in &state.timers { let timer_ty = timer.get_ty(&fsm.base); let timer_field = timer.get_field(&fsm.base); + let timer_id = timer.id; timers_enter.append_all(quote! { { use finny::FsmTimer; + let timer_id = #timer_id ; let mut settings = finny::TimerFsmSettings::default(); < #timer_ty > :: setup ( &ctx.backend.context, &mut settings); - //ctx. + match ctx.timers.create(timer_id, &settings.to_timer_settings()) { + Ok(_) => { + ctx.backend.states. #timer_field .instance = Some( (timer_id, settings) ); + }, + Err(ref e) => { + inspect_event_ctx.on_error("Failed to create a timer", e); + } + } } }); From 516a09b109504227ef4033bdfa51c38b6bcf42a3 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Thu, 21 Jan 2021 19:31:09 +0100 Subject: [PATCH 06/38] timer basics --- finny/src/fsm/events.rs | 5 ++- finny/src/fsm/mod.rs | 3 +- finny_derive/src/codegen.rs | 60 +++++++++++++++++++++++++++++++++ finny_derive/src/parse.rs | 3 +- finny_derive/src/validation.rs | 7 ++-- finny_tests/tests/fsm_timers.rs | 6 ++-- 6 files changed, 75 insertions(+), 9 deletions(-) diff --git a/finny/src/fsm/events.rs b/finny/src/fsm/events.rs index b29120c..dde2b29 100644 --- a/finny/src/fsm/events.rs +++ b/finny/src/fsm/events.rs @@ -1,9 +1,10 @@ -use crate::{FsmBackend, FsmEventQueue, FsmEventQueueSender, lib::*}; +use crate::{FsmBackend, FsmEventQueue, FsmEventQueueSender, TimerId, lib::*}; /// The internal event type that also allows stopping or starting the machine. pub enum FsmEvent { Start, Stop, + Timer(TimerId), Event(E) } @@ -18,6 +19,7 @@ impl Debug for FsmEvent where E: Debug { match self { FsmEvent::Start => f.write_str("Fsm::Start"), FsmEvent::Stop => f.write_str("Fsm::Stop"), + FsmEvent::Timer(t) => f.write_fmt(format_args!("Fsm::Timer({})", t)), FsmEvent::Event(ev) => ev.fmt(f) } } @@ -28,6 +30,7 @@ impl AsRef for FsmEvent where E: AsRef { match self { FsmEvent::Start => "Fsm::Start", FsmEvent::Stop => "Fsm::Stop", + FsmEvent::Timer(_) => "Fsm::Timer", FsmEvent::Event(e) => e.as_ref() } } diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 7cb12ab..06fa972 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -31,7 +31,8 @@ pub type FsmResult = Result; pub enum FsmError { NoTransition, QueueOverCapacity, - NotSupported + NotSupported, + TimerNotStarted(TimerId) } pub type FsmDispatchResult = FsmResult<()>; diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 67dc060..8b7c5a3 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -545,6 +545,61 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream sub_matches }; + // match and dispatch timer events + let timers = { + let mut timer_dispatch = TokenStream::new(); + + for state in ®ion.states { + let state_ty = &state.ty; + + for timer in &state.timers { + + let timer_id = timer.id; + let timer_ty = timer.get_ty(&fsm.base); + let timer_field = timer.get_field(&fsm.base); + + timer_dispatch.append_all(quote! { + + (_, finny::FsmEvent::Timer( timer_id @ #timer_id )) => { + + { + use crate::finny::FsmTimer; + + let timer = &ctx.backend.states. #timer_field; + match &timer.instance { + Some((_, timer_settings)) => { + + let state: & #state_ty = ctx.backend.states.as_ref(); + match <#timer_ty> :: trigger( &ctx.backend.context, state ) { + Some(ev) => { + match ctx.queue.enqueue(ev) { + Ok(_) => (), + Err(e) => { + inspect_event_ctx.on_error("The event triggered by the timer couldn't be enqueued.", &e); + } + } + }, + _ => () + } + + }, + None => { + let error = finny::FsmError::TimerNotStarted(*timer_id); + inspect_event_ctx.on_error("Timer hasn't been started.", &error); + } + } + } + + }, + + }); + + } + } + + timer_dispatch + }; + regions.append_all(quote! { match (ctx.backend.current_states[#region_id], &event) { @@ -552,6 +607,11 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream #region_transitions + // do not dispatch timers if the machine is stopped + (finny::FsmCurrentState::Stopped, finny::FsmEvent::Timer(_)) => (), + + #timers + _ => { transition_misses += 1; } diff --git a/finny_derive/src/parse.rs b/finny_derive/src/parse.rs index a57378b..b9739b4 100644 --- a/finny_derive/src/parse.rs +++ b/finny_derive/src/parse.rs @@ -144,7 +144,8 @@ pub struct ValidatedFsm { pub struct FsmRegion { pub region_id: usize, pub initial_state: syn::Type, - pub transitions: Vec + pub transitions: Vec, + pub states: Vec } #[derive(Debug, Clone)] diff --git a/finny_derive/src/validation.rs b/finny_derive/src/validation.rs index f43444b..978049e 100644 --- a/finny_derive/src/validation.rs +++ b/finny_derive/src/validation.rs @@ -66,7 +66,7 @@ pub fn create_regions(decl: FsmDeclarations, options: FsmCodegenOptions) -> syn: // build the regions let mut regions = vec![]; for (region_id, initial_state) in decl.initial_states.iter().enumerate() { - let transitions = { + let (transitions, states) = { let region_states: HashSet<_> = graph.raw_nodes().iter() .filter(|n| n.weight.region == Some(region_id)) .map(|n| n.weight.state.clone()) @@ -88,13 +88,14 @@ pub fn create_regions(decl: FsmDeclarations, options: FsmCodegenOptions) -> syn: } } - transitions + (transitions, region_states) }; regions.push(FsmRegion { initial_state: initial_state.clone(), region_id, - transitions + transitions, + states: states.into_iter().map(|ty| decl.states.get(&ty).unwrap()).cloned().collect() }); } diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index 4260333..ff5afac 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -77,9 +77,9 @@ fn test_timers_fsm() -> FsmResult<()> { let ctx = TimersMachineContext { }; let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), FsmTimersNull)?; - - //let foo = TimersMachineTimer1::default(); - fsm.start()?; + fsm.start()?; + fsm.dispatch_single_event(FsmEvent::Timer(1))?; + Ok(()) } \ No newline at end of file From 28912dbf1f6ead984d275f4b4d03299e225d3e97 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Fri, 22 Jan 2021 00:01:43 +0100 Subject: [PATCH 07/38] a very basic std timer --- finny/Cargo.toml | 6 ++- finny/src/fsm/fsm_impl.rs | 19 +++++++ finny/src/inspect/mod.rs | 2 + .../src/{inspect_slog.rs => inspect/slog.rs} | 2 +- finny/src/lib.rs | 4 +- finny/src/timers/mod.rs | 2 + finny/src/timers/std.rs | 50 +++++++++++++++++++ finny_tests/tests/fsm_fn.rs | 2 +- finny_tests/tests/fsm_sub.rs | 2 +- finny_tests/tests/fsm_sub_generic.rs | 2 +- finny_tests/tests/fsm_timers.rs | 17 +++++-- 11 files changed, 95 insertions(+), 13 deletions(-) create mode 100644 finny/src/inspect/mod.rs rename finny/src/{inspect_slog.rs => inspect/slog.rs} (99%) create mode 100644 finny/src/timers/mod.rs create mode 100644 finny/src/timers/std.rs diff --git a/finny/Cargo.toml b/finny/Cargo.toml index 62e4f7a..0d37303 100644 --- a/finny/Cargo.toml +++ b/finny/Cargo.toml @@ -16,7 +16,9 @@ finny_derive = { path = "../finny_derive", version = "0.2.0" } arraydeque = { version = "0.4", default-features = false } slog = { version = "2.7.0", optional = true } + [features] -default = ["std", "inspect_slog"] +default = ["std", "inspect_slog", "timers_std"] std = ["arraydeque/std"] -inspect_slog = ["slog"] \ No newline at end of file +inspect_slog = ["slog"] +timers_std = [] \ No newline at end of file diff --git a/finny/src/fsm/fsm_impl.rs b/finny/src/fsm/fsm_impl.rs index f71fb8d..a8f51ec 100644 --- a/finny/src/fsm/fsm_impl.rs +++ b/finny/src/fsm/fsm_impl.rs @@ -68,6 +68,25 @@ impl FsmFrontend Self::dispatch_single_event(self, FsmEvent::Start) } + /// Dispatch any pending timer events into the queue, then run all the + /// events from the queue until completition. + pub fn dispatch_timer_events(&mut self) -> FsmResult<()> { + loop { + if let Some(timer_id) = self.timers.get_triggered_timer() { + self.dispatch_single_event(FsmEvent::Timer(timer_id))?; + } else { + break; + } + } + + while let Some(ev) = self.queue.dequeue() { + let ev: ::Events = ev.into(); + Self::dispatch(self, ev)?; + } + + Ok(()) + } + /// Dispatch this event and run it to completition. pub fn dispatch(&mut self, event: E) -> FsmResult<()> where E: Into<::Events> diff --git a/finny/src/inspect/mod.rs b/finny/src/inspect/mod.rs new file mode 100644 index 0000000..f1e5914 --- /dev/null +++ b/finny/src/inspect/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature="inspect_slog")] +pub mod slog; \ No newline at end of file diff --git a/finny/src/inspect_slog.rs b/finny/src/inspect/slog.rs similarity index 99% rename from finny/src/inspect_slog.rs rename to finny/src/inspect/slog.rs index b88fedd..90d2eb1 100644 --- a/finny/src/inspect_slog.rs +++ b/finny/src/inspect/slog.rs @@ -1,6 +1,6 @@ use slog::{info, o, error}; use crate::{FsmBackend, FsmEvent, Inspect}; -use super::lib::*; +use crate::lib::*; use AsRef; pub struct InspectSlog { diff --git a/finny/src/lib.rs b/finny/src/lib.rs index 0efc146..769bf65 100644 --- a/finny/src/lib.rs +++ b/finny/src/lib.rs @@ -68,8 +68,8 @@ pub mod decl; mod fsm; -#[cfg(feature="inspect_slog")] -pub mod inspect_slog; +pub mod inspect; +pub mod timers; pub use fsm::*; diff --git a/finny/src/timers/mod.rs b/finny/src/timers/mod.rs new file mode 100644 index 0000000..7b5d294 --- /dev/null +++ b/finny/src/timers/mod.rs @@ -0,0 +1,2 @@ +#[cfg(feature="timers_std")] +pub mod std; \ No newline at end of file diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs new file mode 100644 index 0000000..b35b369 --- /dev/null +++ b/finny/src/timers/std.rs @@ -0,0 +1,50 @@ +use std::{collections::{HashMap, hash_map::RandomState}, time::{Duration, Instant}}; +use crate::{FsmTimers, TimerId}; + +pub struct TimersStd { + timers: Vec<(TimerId, StdTimer)> +} + +enum StdTimer { + Oneshot { started_at: Instant, duration: Duration } +} + +impl TimersStd { + pub fn new() -> Self { + Self { + timers: vec![] + } + } +} + +impl FsmTimers for TimersStd { + fn create(&mut self, id: crate::TimerId, settings: &crate::TimerSettings) -> crate::FsmResult<()> { + self.timers.push((id, StdTimer::Oneshot { started_at: Instant::now(), duration: settings.timeout })); + Ok(()) + } + + fn cancel(&mut self, id: crate::TimerId) -> crate::FsmResult<()> { + todo!() + } + + fn get_triggered_timer(&mut self) -> Option { + let mut timed_out_idx = None; + let now = Instant::now(); + for (idx, (_, timer)) in self.timers.iter().enumerate() { + match timer { + StdTimer::Oneshot { started_at, duration } if now.duration_since(*started_at) >= *duration => { + timed_out_idx = Some(idx); + break; + }, + _ => () + } + } + + if let Some(idx) = timed_out_idx { + let (id, _) = self.timers.remove(idx); + return Some(id); + } + + None + } +} \ No newline at end of file diff --git a/finny_tests/tests/fsm_fn.rs b/finny_tests/tests/fsm_fn.rs index 46903db..84e16be 100644 --- a/finny_tests/tests/fsm_fn.rs +++ b/finny_tests/tests/fsm_fn.rs @@ -1,6 +1,6 @@ extern crate finny; -use finny::{FsmCurrentState, FsmError, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::{self, InspectSlog}}; +use finny::{FsmCurrentState, FsmError, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect::slog::InspectSlog}; use slog::{Drain, Logger, info, o}; #[derive(Debug)] diff --git a/finny_tests/tests/fsm_sub.rs b/finny_tests/tests/fsm_sub.rs index 0151a0b..73c5fd8 100644 --- a/finny_tests/tests/fsm_sub.rs +++ b/finny_tests/tests/fsm_sub.rs @@ -1,6 +1,6 @@ extern crate finny; -use finny::{FsmCurrentState, FsmError, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::InspectSlog}; +use finny::{FsmCurrentState, FsmError, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect::slog::InspectSlog}; use slog::{Drain, o}; #[derive(Default)] diff --git a/finny_tests/tests/fsm_sub_generic.rs b/finny_tests/tests/fsm_sub_generic.rs index 96ca605..bd810bb 100644 --- a/finny_tests/tests/fsm_sub_generic.rs +++ b/finny_tests/tests/fsm_sub_generic.rs @@ -2,7 +2,7 @@ extern crate finny; use std::ops::{Add, AddAssign}; -use finny::{FsmCurrentState, FsmError, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::InspectSlog}; +use finny::{FsmCurrentState, FsmError, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect::slog::InspectSlog}; use slog::{Drain, o}; #[derive(Debug)] diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index ff5afac..56d9e7a 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -1,8 +1,8 @@ extern crate finny; -use std::time::Duration; +use std::{thread::{sleep, sleep_ms}, time::Duration}; -use finny::{FsmCurrentState, FsmError, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, FsmTimersNull, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect_slog::{self, InspectSlog}}; +use finny::{FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect::slog::InspectSlog, timers::std::{TimersStd}}; use slog::{Drain, Logger, info, o}; #[derive(Debug)] @@ -55,7 +55,7 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF fsm.state::() .on_entry_start_timer(|_ctx, timer| { timer.timeout = Duration::from_millis(100); - timer.renew = true; + //timer.renew = true; }, |ctx, state| { Some( EventTimer.into() ) }); @@ -76,10 +76,17 @@ fn test_timers_fsm() -> FsmResult<()> { let ctx = TimersMachineContext { }; - let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), FsmTimersNull)?; + let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), TimersStd::new())?; fsm.start()?; - fsm.dispatch_single_event(FsmEvent::Timer(1))?; + + + sleep(Duration::from_millis(125)); + + fsm.dispatch_timer_events()?; + + let state_a: &StateA = fsm.get_state(); + assert_eq!(1, state_a.timers); Ok(()) } \ No newline at end of file From 4c16ec39b01c4a1cba6da74fbff86c9a4aad5df9 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Fri, 22 Jan 2021 00:36:03 +0100 Subject: [PATCH 08/38] basic algo for intervals --- finny/src/timers/std.rs | 40 +++++++++++++++++++++++++++------ finny_tests/tests/fsm_timers.rs | 9 ++++---- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs index b35b369..d51da4f 100644 --- a/finny/src/timers/std.rs +++ b/finny/src/timers/std.rs @@ -2,40 +2,66 @@ use std::{collections::{HashMap, hash_map::RandomState}, time::{Duration, Instan use crate::{FsmTimers, TimerId}; pub struct TimersStd { - timers: Vec<(TimerId, StdTimer)> + timers: Vec<(TimerId, StdTimer)>, + pending_intervals: Option<(TimerId, usize)> } enum StdTimer { - Oneshot { started_at: Instant, duration: Duration } + Timeout { started_at: Instant, duration: Duration }, + Interval { started_at: Instant, interval: Duration } } impl TimersStd { pub fn new() -> Self { Self { - timers: vec![] + timers: vec![], + pending_intervals: None } } } impl FsmTimers for TimersStd { fn create(&mut self, id: crate::TimerId, settings: &crate::TimerSettings) -> crate::FsmResult<()> { - self.timers.push((id, StdTimer::Oneshot { started_at: Instant::now(), duration: settings.timeout })); + if settings.renew { + self.timers.push((id, StdTimer::Interval { started_at: Instant::now(), interval: settings.timeout })); + } else { + self.timers.push((id, StdTimer::Timeout { started_at: Instant::now(), duration: settings.timeout })); + } Ok(()) } fn cancel(&mut self, id: crate::TimerId) -> crate::FsmResult<()> { - todo!() + todo!("cancel timer") } fn get_triggered_timer(&mut self) -> Option { + if let Some((id, mut times)) = self.pending_intervals.take() { + times -= 1; + if times > 0 { + self.pending_intervals = Some((id, times)); + } + + return Some(id); + } + + let mut timed_out_idx = None; let now = Instant::now(); - for (idx, (_, timer)) in self.timers.iter().enumerate() { + for (idx, (timer_id, timer)) in self.timers.iter_mut().enumerate() { match timer { - StdTimer::Oneshot { started_at, duration } if now.duration_since(*started_at) >= *duration => { + StdTimer::Timeout { started_at, duration } if now.duration_since(*started_at) >= *duration => { timed_out_idx = Some(idx); break; }, + StdTimer::Interval { ref mut started_at, interval } if now.duration_since(*started_at) >= *interval => { + let t = now.duration_since(*started_at); + let times = ((t.as_secs_f32() / interval.as_secs_f32()).floor() as usize) - 1; + if times > 0 { + self.pending_intervals = Some((*timer_id, times)); + } + *started_at = now; + return Some(*timer_id); + }, _ => () } } diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index 56d9e7a..37c3c81 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -54,8 +54,8 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF fsm.state::() .on_entry_start_timer(|_ctx, timer| { - timer.timeout = Duration::from_millis(100); - //timer.renew = true; + timer.timeout = Duration::from_millis(50); + timer.renew = true; }, |ctx, state| { Some( EventTimer.into() ) }); @@ -80,13 +80,12 @@ fn test_timers_fsm() -> FsmResult<()> { fsm.start()?; - - sleep(Duration::from_millis(125)); + sleep(Duration::from_millis(175)); fsm.dispatch_timer_events()?; let state_a: &StateA = fsm.get_state(); - assert_eq!(1, state_a.timers); + assert_eq!(3, state_a.timers); Ok(()) } \ No newline at end of file From 77537add7267ff97c2ced64824359769191044bf Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Fri, 22 Jan 2021 22:46:47 +0100 Subject: [PATCH 09/38] timers --- finny/src/timers/std.rs | 10 ++++++--- finny_tests/tests/fsm_timers.rs | 36 +++++++++++++++++++++++++-------- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs index d51da4f..df6ee8a 100644 --- a/finny/src/timers/std.rs +++ b/finny/src/timers/std.rs @@ -1,4 +1,4 @@ -use std::{collections::{HashMap, hash_map::RandomState}, time::{Duration, Instant}}; +use std::{time::{Duration, Instant}}; use crate::{FsmTimers, TimerId}; pub struct TimersStd { @@ -22,16 +22,21 @@ impl TimersStd { impl FsmTimers for TimersStd { fn create(&mut self, id: crate::TimerId, settings: &crate::TimerSettings) -> crate::FsmResult<()> { + // try to cancel any existing ones + self.cancel(id)?; + if settings.renew { self.timers.push((id, StdTimer::Interval { started_at: Instant::now(), interval: settings.timeout })); } else { self.timers.push((id, StdTimer::Timeout { started_at: Instant::now(), duration: settings.timeout })); } + Ok(()) } fn cancel(&mut self, id: crate::TimerId) -> crate::FsmResult<()> { - todo!("cancel timer") + self.timers.retain(|(timer_id, _)| *timer_id != id); + Ok(()) } fn get_triggered_timer(&mut self) -> Option { @@ -44,7 +49,6 @@ impl FsmTimers for TimersStd { return Some(id); } - let mut timed_out_idx = None; let now = Instant::now(); for (idx, (timer_id, timer)) in self.timers.iter_mut().enumerate() { diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index 37c3c81..876a4b7 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -7,6 +7,7 @@ use slog::{Drain, Logger, info, o}; #[derive(Debug)] pub struct TimersMachineContext { + exit_a: bool } #[derive(Default)] @@ -20,9 +21,9 @@ pub struct StateB { #[derive(Default)] pub struct StateC; #[derive(Clone, Debug)] -pub struct EventClick { time: usize } +pub struct EventClick; #[derive(Clone, Debug)] -pub struct EventTimer; +pub struct EventTimer { n: usize } #[derive(Clone, Debug)] pub struct EventEnter { shift: bool } @@ -36,13 +37,13 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF fsm.state::() .on_exit(|state, ctx| { - state.timers = 0; + ctx.exit_a = true; }) .on_event::() .transition_to::() .guard(|ev, ctx, states| { let state: &StateA = states.as_ref(); - state.timers > 5 + state.timers >= 5 }); fsm.state::() @@ -56,8 +57,18 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF .on_entry_start_timer(|_ctx, timer| { timer.timeout = Duration::from_millis(50); timer.renew = true; + timer.cancel_on_state_exit = true; }, |ctx, state| { - Some( EventTimer.into() ) + Some( EventTimer {n: 0}.into() ) + }); + + fsm.state::() + .on_entry_start_timer(|_ctx, timer| { + timer.timeout = Duration::from_millis(100); + timer.renew = false; + timer.cancel_on_state_exit = true; + }, |ctx, state| { + Some( EventTimer {n: 1}.into() ) }); fsm.state::(); @@ -74,18 +85,27 @@ fn test_timers_fsm() -> FsmResult<()> { .build().fuse(), o!() ); - let ctx = TimersMachineContext { }; + let ctx = TimersMachineContext { exit_a: false }; let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), TimersStd::new())?; fsm.start()?; - sleep(Duration::from_millis(175)); + sleep(Duration::from_millis(225)); + + fsm.dispatch_timer_events()?; + + let state_a: &StateA = fsm.get_state(); + assert_eq!(5, state_a.timers); + + sleep(Duration::from_millis(100)); fsm.dispatch_timer_events()?; + fsm.dispatch(EventClick)?; let state_a: &StateA = fsm.get_state(); - assert_eq!(3, state_a.timers); + assert_eq!(5, state_a.timers); + assert_eq!(true, fsm.exit_a); Ok(()) } \ No newline at end of file From 138c3d4c193811ea7876c505a482532fe5153d7b Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sat, 23 Jan 2021 00:12:15 +0100 Subject: [PATCH 10/38] exit, timer inspection --- finny/src/fsm/inspect.rs | 12 +++++- finny/src/fsm/timers.rs | 2 +- finny/src/inspect/slog.rs | 11 +++++ finny/src/timers/std.rs | 1 + finny_derive/src/codegen.rs | 72 ++++++++++++++++++++++++++++++--- finny_tests/tests/fsm_timers.rs | 19 ++++----- 6 files changed, 100 insertions(+), 17 deletions(-) diff --git a/finny/src/fsm/inspect.rs b/finny/src/fsm/inspect.rs index 0697065..b634a6b 100644 --- a/finny/src/fsm/inspect.rs +++ b/finny/src/fsm/inspect.rs @@ -1,4 +1,4 @@ -use crate::lib::*; +use crate::{TimerId, lib::*}; use crate::{FsmBackend, FsmError, FsmEvent}; pub trait Inspect { @@ -7,6 +7,7 @@ pub trait Inspect { fn for_transition(&self) -> Self; fn for_sub_machine(&self) -> Self; + fn for_timer(&self, timer_id: TimerId) -> Self; fn on_guard(&self, guard_result: bool); fn on_state_enter(&self); @@ -14,6 +15,7 @@ pub trait Inspect { fn on_action(&self); fn on_error(&self, msg: &str, error: &E) where E: Debug; + fn info(&self, msg: &str); } #[derive(Default)] @@ -38,6 +40,10 @@ impl Inspect for InspectNull { Self::default() } + fn for_timer(&self, timer_id: TimerId) -> Self { + Self::default() + } + fn on_guard(&self, _guard_result: bool) { } @@ -61,4 +67,8 @@ impl Inspect for InspectNull { fn on_error(&self, msg: &str, error: &E) where E: Debug { } + + fn info(&self, msg: &str) { + + } } \ No newline at end of file diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index c645b50..dc12da3 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -9,7 +9,7 @@ pub trait FsmTimer fn trigger(ctx: &::Context, state: &S) -> Option< ::Events >; } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub struct TimerFsmSettings { pub enabled: bool, pub timeout: Duration, diff --git a/finny/src/inspect/slog.rs b/finny/src/inspect/slog.rs index 90d2eb1..ad654ac 100644 --- a/finny/src/inspect/slog.rs +++ b/finny/src/inspect/slog.rs @@ -43,6 +43,13 @@ impl Inspect for InspectSlog { } } + fn for_timer(&self, timer_id: crate::TimerId) -> Self { + let kv = o!("timer_id" => timer_id); + InspectSlog { + logger: self.logger.new(kv) + } + } + fn on_guard(&self, guard_result: bool) { let guard = type_name::(); info!(self.logger, "Guard {guard} evaluated to {guard_result}", guard = guard, guard_result = guard_result); @@ -71,4 +78,8 @@ impl Inspect for InspectSlog { let kv = o!("error" => format!("{:?}", error)); error!(self.logger, "{}", msg; kv); } + + fn info(&self, msg: &str) { + info!(self.logger, "{}", msg); + } } \ No newline at end of file diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs index df6ee8a..09f2a36 100644 --- a/finny/src/timers/std.rs +++ b/finny/src/timers/std.rs @@ -6,6 +6,7 @@ pub struct TimersStd { pending_intervals: Option<(TimerId, usize)> } +#[derive(Debug)] enum StdTimer { Timeout { started_at: Instant, duration: Duration }, Interval { started_at: Instant, interval: Duration } diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 8b7c5a3..526498f 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -481,15 +481,21 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream use finny::FsmTimer; let timer_id = #timer_id ; + let log = inspect_event_ctx.for_timer(timer_id); let mut settings = finny::TimerFsmSettings::default(); < #timer_ty > :: setup ( &ctx.backend.context, &mut settings); - match ctx.timers.create(timer_id, &settings.to_timer_settings()) { - Ok(_) => { - ctx.backend.states. #timer_field .instance = Some( (timer_id, settings) ); - }, - Err(ref e) => { - inspect_event_ctx.on_error("Failed to create a timer", e); + if settings.enabled { + match ctx.timers.create(timer_id, &settings.to_timer_settings()) { + Ok(_) => { + ctx.backend.states. #timer_field .instance = Some( (timer_id, settings) ); + log.info("Started the timer."); + }, + Err(ref e) => { + log.on_error("Failed to create a timer", e); + } } + } else { + log.info("The timer wasn't enabled."); } } @@ -500,9 +506,63 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream timers_enter }; + let timers_exit = { + let mut timers_exit = TokenStream::new(); + + let state = match &transition.ty { + FsmTransitionType::SelfTransition(FsmStateAction { state: FsmTransitionState::State(st @ FsmState { kind: FsmStateKind::Normal, .. }), .. }) => { + Some(st) + }, + FsmTransitionType::StateTransition(FsmStateTransition { state_from: FsmTransitionState::State(st @ FsmState { kind: FsmStateKind::Normal, .. }), .. }) => { + Some(st) + }, + _ => None + }; + + if let Some(state) = state { + for timer in &state.timers { + let timer_field = timer.get_field(&fsm.base); + let timer_id = timer.id; + + timers_exit.append_all(quote! { + + { + use finny::FsmTimer; + + let timer_id = #timer_id ; + let log = inspect_event_ctx.for_timer(timer_id); + let mut timer = &mut ctx.backend.states. #timer_field; + match timer.instance { + Some((instance_timer_id, settings)) => { + if timer_id == instance_timer_id && settings.cancel_on_state_exit { + match ctx.timers.cancel(timer_id) { + Ok(_) => { + timer.instance = None; + log.info("Cancelled the timer."); + }, + Err(ref e) => { + log.on_error("Failed to cancel the timer", e); + } + } + } + }, + _ => () + } + + } + + }); + } + } + + timers_exit + }; + let m = quote! { ( #match_state , #match_event ) #guard => { + #timers_exit + <#transition_ty>::execute_transition(&mut ctx, &ev, #region_id, &mut inspect_event_ctx); #fsm_sub_entry diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index 876a4b7..d09b36a 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -2,7 +2,7 @@ extern crate finny; use std::{thread::{sleep, sleep_ms}, time::Duration}; -use finny::{FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect::slog::InspectSlog, timers::std::{TimersStd}}; +use finny::{FsmCurrentState, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect::slog::InspectSlog, timers::std::{TimersStd}}; use slog::{Drain, Logger, info, o}; #[derive(Debug)] @@ -79,12 +79,11 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF #[test] fn test_timers_fsm() -> FsmResult<()> { - let plain = slog_term::PlainSyncDecorator::new(std::io::stdout()); - let logger = Logger::root( - slog_term::FullFormat::new(plain) - .build().fuse(), o!() - ); - + let decorator = slog_term::TermDecorator::new().build(); + let drain = slog_term::CompactFormat::new(decorator).build().fuse(); + let drain = std::sync::Mutex::new(drain).fuse(); + let logger = slog::Logger::root(drain, o!()); + let ctx = TimersMachineContext { exit_a: false }; let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), TimersStd::new())?; @@ -97,11 +96,13 @@ fn test_timers_fsm() -> FsmResult<()> { let state_a: &StateA = fsm.get_state(); assert_eq!(5, state_a.timers); + fsm.dispatch(EventClick)?; sleep(Duration::from_millis(100)); - fsm.dispatch_timer_events()?; - fsm.dispatch(EventClick)?; + fsm.dispatch_timer_events()?; + + assert_eq!(FsmCurrentState::State(TimersMachineCurrentState::StateB), fsm.get_current_states()[0]); let state_a: &StateA = fsm.get_state(); assert_eq!(5, state_a.timers); From 20e5ef240e98c3fe4c88c3e76bb55146a204764f Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sat, 23 Jan 2021 00:16:59 +0100 Subject: [PATCH 11/38] stopgap --- finny/src/fsm/timers.rs | 8 ++++++++ finny_derive/src/codegen.rs | 18 +++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index dc12da3..22754c2 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -1,12 +1,20 @@ use crate::{FsmError, lib::*}; use crate::{FsmBackend, FsmResult}; +#[derive(Debug, Clone, Copy)] +pub struct TimerInstance { + pub id: TimerId, + pub settings: TimerFsmSettings +} pub trait FsmTimer where F: FsmBackend, Self: Default { fn setup(ctx: &::Context, settings: &mut TimerFsmSettings); fn trigger(ctx: &::Context, state: &S) -> Option< ::Events >; + + fn get_instance(&self) -> &Option; + fn get_instance_mut(&mut self) -> &mut Option; } #[derive(Debug, Clone, Copy)] diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 526498f..2235ae3 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -487,7 +487,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream if settings.enabled { match ctx.timers.create(timer_id, &settings.to_timer_settings()) { Ok(_) => { - ctx.backend.states. #timer_field .instance = Some( (timer_id, settings) ); + ctx.backend.states. #timer_field .instance = Some( finny::TimerInstance { id: timer_id, settings } ); log.info("Started the timer."); }, Err(ref e) => { @@ -533,8 +533,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let log = inspect_event_ctx.for_timer(timer_id); let mut timer = &mut ctx.backend.states. #timer_field; match timer.instance { - Some((instance_timer_id, settings)) => { - if timer_id == instance_timer_id && settings.cancel_on_state_exit { + Some(instance) => { + if timer_id == instance.id && instance.settings.cancel_on_state_exit { match ctx.timers.cancel(timer_id) { Ok(_) => { timer.instance = None; @@ -627,7 +627,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let timer = &ctx.backend.states. #timer_field; match &timer.instance { - Some((_, timer_settings)) => { + Some(_) => { let state: & #state_ty = ctx.backend.states.as_ref(); match <#timer_ty> :: trigger( &ctx.backend.context, state ) { @@ -821,7 +821,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream #[derive(Debug, Clone, Default)] pub struct #timer_ty { - instance: Option<(finny::TimerId, finny::TimerFsmSettings)> + instance: Option } impl #fsm_generics_impl finny::FsmTimer< #fsm_ty #fsm_generics_type , #state_ty > for #timer_ty #fsm_generics_where { @@ -839,6 +839,14 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream }; ret } + + fn get_instance(&self) -> &Option { + &self.instance + } + + fn get_instance_mut(&mut self) -> &mut Option { + &mut self.instance + } } }); From 55e31b8556bbc05fc364eecf691bc2d6b304a46f Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sat, 23 Jan 2021 00:43:57 +0100 Subject: [PATCH 12/38] stopgap --- finny/src/fsm/timers.rs | 72 ++++++++++++++++++++++++++++++++- finny_derive/src/codegen.rs | 79 ++----------------------------------- 2 files changed, 74 insertions(+), 77 deletions(-) diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index 22754c2..8627108 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -1,4 +1,4 @@ -use crate::{FsmError, lib::*}; +use crate::{DispatchContext, FsmError, FsmEventQueue, Inspect, lib::*}; use crate::{FsmBackend, FsmResult}; #[derive(Debug, Clone, Copy)] @@ -12,9 +12,77 @@ pub trait FsmTimer { fn setup(ctx: &::Context, settings: &mut TimerFsmSettings); fn trigger(ctx: &::Context, state: &S) -> Option< ::Events >; - + fn get_instance(&self) -> &Option; fn get_instance_mut(&mut self) -> &mut Option; + + fn execute_on_enter(&mut self, id: TimerId, ctx: &::Context, inspect: &mut I, timers: &mut T) { + let log = inspect.for_timer(id); + let mut settings = TimerFsmSettings::default(); + Self::setup(ctx, &mut settings); + if settings.enabled { + match timers.create(id, &settings.to_timer_settings()) { + Ok(_) => { + let instance = self.get_instance_mut(); + *instance = Some( TimerInstance { id, settings } ); + log.info("Started the timer."); + }, + Err(ref e) => { + log.on_error("Failed to create a timer", e); + } + } + } else { + log.info("The timer wasn't enabled."); + } + } + + fn execute_on_exit(&mut self, id: TimerId, inspect: &mut I, timers: &mut T) { + let log = inspect.for_timer(id); + match self.get_instance_mut() { + Some(instance) => { + if id == instance.id && instance.settings.cancel_on_state_exit { + match timers.cancel(id) { + Ok(_) => { + *self.get_instance_mut() = None; + log.info("Cancelled the timer."); + }, + Err(ref e) => { + log.on_error("Failed to cancel the timer", e); + } + } + } + }, + _ => () + } + } + + fn execute_trigger<'a, 'b, 'c, 'd, Q, I, T>(id: TimerId, context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, inspect: &mut I) + where Q: FsmEventQueue, I: Inspect, ::States: AsRef, T: FsmTimers + { + let inspect = inspect.for_timer(id); + //match self.get_instance() { + // Some(_) => { + match Self::trigger(&context.backend.context, context.backend.states.as_ref()) { + Some(ev) => { + match context.queue.enqueue(ev) { + Ok(_) => { + inspect.info("The event triggered by the timer was enqueued."); + }, + Err(e) => { + inspect.on_error("The event triggered by the timer couldn't be enqueued.", &e); + } + } + }, + _ => () + } + + //}, + //None => { + // let error = FsmError::TimerNotStarted(id); + // inspect.on_error("Timer hasn't been started.", &error); + //} + //} + } } #[derive(Debug, Clone, Copy)] diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 2235ae3..951c1da 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -476,29 +476,10 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let timer_id = timer.id; timers_enter.append_all(quote! { - { use finny::FsmTimer; - - let timer_id = #timer_id ; - let log = inspect_event_ctx.for_timer(timer_id); - let mut settings = finny::TimerFsmSettings::default(); - < #timer_ty > :: setup ( &ctx.backend.context, &mut settings); - if settings.enabled { - match ctx.timers.create(timer_id, &settings.to_timer_settings()) { - Ok(_) => { - ctx.backend.states. #timer_field .instance = Some( finny::TimerInstance { id: timer_id, settings } ); - log.info("Started the timer."); - }, - Err(ref e) => { - log.on_error("Failed to create a timer", e); - } - } - } else { - log.info("The timer wasn't enabled."); - } + ctx.backend.states. #timer_field . execute_on_enter( #timer_id, &ctx.backend.context, &mut inspect_event_ctx, ctx.timers ); } - }); } } @@ -525,32 +506,10 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let timer_id = timer.id; timers_exit.append_all(quote! { - { use finny::FsmTimer; - - let timer_id = #timer_id ; - let log = inspect_event_ctx.for_timer(timer_id); - let mut timer = &mut ctx.backend.states. #timer_field; - match timer.instance { - Some(instance) => { - if timer_id == instance.id && instance.settings.cancel_on_state_exit { - match ctx.timers.cancel(timer_id) { - Ok(_) => { - timer.instance = None; - log.info("Cancelled the timer."); - }, - Err(ref e) => { - log.on_error("Failed to cancel the timer", e); - } - } - } - }, - _ => () - } - + ctx.backend.states. #timer_field . execute_on_exit( #timer_id, &mut inspect_event_ctx, ctx.timers ); } - }); } } @@ -610,50 +569,20 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let mut timer_dispatch = TokenStream::new(); for state in ®ion.states { - let state_ty = &state.ty; - for timer in &state.timers { let timer_id = timer.id; - let timer_ty = timer.get_ty(&fsm.base); let timer_field = timer.get_field(&fsm.base); + let timer_ty = timer.get_ty(&fsm.base); timer_dispatch.append_all(quote! { - (_, finny::FsmEvent::Timer( timer_id @ #timer_id )) => { - { use crate::finny::FsmTimer; - - let timer = &ctx.backend.states. #timer_field; - match &timer.instance { - Some(_) => { - - let state: & #state_ty = ctx.backend.states.as_ref(); - match <#timer_ty> :: trigger( &ctx.backend.context, state ) { - Some(ev) => { - match ctx.queue.enqueue(ev) { - Ok(_) => (), - Err(e) => { - inspect_event_ctx.on_error("The event triggered by the timer couldn't be enqueued.", &e); - } - } - }, - _ => () - } - - }, - None => { - let error = finny::FsmError::TimerNotStarted(*timer_id); - inspect_event_ctx.on_error("Timer hasn't been started.", &error); - } - } + < #timer_ty > :: execute_trigger(#timer_id, &mut ctx, &mut inspect_event_ctx); } - }, - }); - } } From 61d71eefe0cb97edd1c607fb94bb0f19f4b2bebc Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sat, 23 Jan 2021 00:47:06 +0100 Subject: [PATCH 13/38] working timer trigger with external code --- finny/src/fsm/timers.rs | 26 ++++++++++++++++---------- finny_derive/src/codegen.rs | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index 8627108..ab138bb 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -1,4 +1,4 @@ -use crate::{DispatchContext, FsmError, FsmEventQueue, Inspect, lib::*}; +use crate::{DispatchContext, FsmBackendImpl, FsmError, FsmEventQueue, Inspect, lib::*}; use crate::{FsmBackend, FsmResult}; #[derive(Debug, Clone, Copy)] @@ -57,11 +57,17 @@ pub trait FsmTimer } fn execute_trigger<'a, 'b, 'c, 'd, Q, I, T>(id: TimerId, context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, inspect: &mut I) - where Q: FsmEventQueue, I: Inspect, ::States: AsRef, T: FsmTimers + where + Q: FsmEventQueue, + I: Inspect, + ::States: AsRef, + ::States: AsRef, + T: FsmTimers { let inspect = inspect.for_timer(id); - //match self.get_instance() { - // Some(_) => { + let timer: &Self = context.backend.states.as_ref(); + match timer.get_instance() { + Some(_) => { match Self::trigger(&context.backend.context, context.backend.states.as_ref()) { Some(ev) => { match context.queue.enqueue(ev) { @@ -76,12 +82,12 @@ pub trait FsmTimer _ => () } - //}, - //None => { - // let error = FsmError::TimerNotStarted(id); - // inspect.on_error("Timer hasn't been started.", &error); - //} - //} + }, + None => { + let error = FsmError::TimerNotStarted(id); + inspect.on_error("Timer hasn't been started.", &error); + } + } } } diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 951c1da..6e0fad2 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -38,6 +38,20 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream code_fields.append_all(quote! { #timer_field: #timer_ty, }); new_state_fields.append_all(quote! { #timer_field: #timer_ty::default(), }); + + state_accessors.append_all(quote! { + impl #fsm_generics_impl core::convert::AsRef<#timer_ty> for #states_store_ty #fsm_generics_type #fsm_generics_where { + fn as_ref(&self) -> & #timer_ty { + &self. #timer_field + } + } + + impl #fsm_generics_impl core::convert::AsMut<#timer_ty> for #states_store_ty #fsm_generics_type #fsm_generics_where { + fn as_mut(&mut self) -> &mut #timer_ty { + &mut self. #timer_field + } + } + }); } code_fields.append_all(quote! { #name: #ty, }); From e579d26602abf3c0441dc66d61efbc47be48cb26 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sat, 23 Jan 2021 15:55:25 +0100 Subject: [PATCH 14/38] cleanup --- finny/src/fsm/timers.rs | 6 +++--- finny/src/fsm/transitions.rs | 5 ++--- finny_derive/src/codegen.rs | 2 -- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index ab138bb..13b9642 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -1,4 +1,4 @@ -use crate::{DispatchContext, FsmBackendImpl, FsmError, FsmEventQueue, Inspect, lib::*}; +use crate::{DispatchContext, FsmError, FsmEventQueue, Inspect, lib::*}; use crate::{FsmBackend, FsmResult}; #[derive(Debug, Clone, Copy)] @@ -150,11 +150,11 @@ pub struct FsmTimersTriggerEventsResult { pub struct FsmTimersNull; impl FsmTimers for FsmTimersNull { - fn create(&mut self, id: TimerId, settings: &TimerSettings) -> FsmResult<()> { + fn create(&mut self, _id: TimerId, _settings: &TimerSettings) -> FsmResult<()> { Err(FsmError::NotSupported) } - fn cancel(&mut self, id: TimerId) -> FsmResult<()> { + fn cancel(&mut self, _id: TimerId) -> FsmResult<()> { Err(FsmError::NotSupported) } diff --git a/finny/src/fsm/transitions.rs b/finny/src/fsm/transitions.rs index a64016e..1abef7c 100644 --- a/finny/src/fsm/transitions.rs +++ b/finny/src/fsm/transitions.rs @@ -1,8 +1,7 @@ //! All of these traits will be implemented by the procedural code generator. -use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, FsmTimers, FsmTimersNull, lib::*}; - -use crate::{DispatchContext, EventContext, FsmBackend, FsmCurrentState, FsmEvent, FsmEventQueue, FsmFrontend, FsmRegionId, FsmStateTransitionAsMut, FsmStates, Inspect}; +use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, FsmTimers, lib::*}; +use crate::{DispatchContext, EventContext, FsmBackend, FsmCurrentState, FsmEvent, FsmEventQueue, FsmRegionId, FsmStateTransitionAsMut, FsmStates, Inspect}; /// A state's entry and exit actions. pub trait FsmState { diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 6e0fad2..a8d0f6b 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -1,6 +1,5 @@ use proc_macro2::{TokenStream}; use quote::{TokenStreamExt, quote}; -use syn::punctuated::Punctuated; use crate::{fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs}}; use crate::{parse::{FsmFnInput, FsmStateTransition, FsmTransitionState, FsmTransitionType}, utils::ty_append}; @@ -586,7 +585,6 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream for timer in &state.timers { let timer_id = timer.id; - let timer_field = timer.get_field(&fsm.base); let timer_ty = timer.get_ty(&fsm.base); timer_dispatch.append_all(quote! { From aa8cf47cc656db39f3f23a524072f3e2870bc30c Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sun, 24 Jan 2021 21:23:49 +0100 Subject: [PATCH 15/38] wip --- finny/src/fsm/dispatch.rs | 24 ++++++++-- finny/src/fsm/events.rs | 1 + finny/src/fsm/fsm_impl.rs | 3 +- finny/src/fsm/mod.rs | 3 ++ finny/src/fsm/tests_fsm.rs | 8 ++++ finny/src/fsm/transitions.rs | 41 +++++++++++++++- finny_derive/src/codegen.rs | 85 +++++++++++++++++++++++++++++++-- finny_tests/tests/fsm_timers.rs | 38 ++++++++++++++- 8 files changed, 192 insertions(+), 11 deletions(-) diff --git a/finny/src/fsm/dispatch.rs b/finny/src/fsm/dispatch.rs index 38da685..c386faa 100644 --- a/finny/src/fsm/dispatch.rs +++ b/finny/src/fsm/dispatch.rs @@ -10,7 +10,8 @@ pub struct DispatchContext<'a, 'b, 'c, F, Q, I, T> pub queue: &'a mut Q, pub inspect: &'b mut I, pub backend: &'c mut FsmBackendImpl, - pub timers: &'a mut T + pub timers: &'a mut T, + pub timers_offset: usize } impl<'a, 'b, 'c, F, Q, I, T> DispatchContext<'a, 'b, 'c, F, Q, I, T> @@ -28,10 +29,22 @@ where F: FsmBackend, region } } + + pub fn with_timers_offset(&'a mut self, timers_offset: usize) -> Self + where 'a: 'b + 'c + { + DispatchContext { + queue: &mut self.queue, + inspect: &mut self.inspect, + backend: &mut self.backend, + timers: &mut self.timers, + timers_offset + } + } } /// Used to funnel the event down to the sub-machine. -pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, Q, I, T>(ctx: &mut DispatchContext<'a, 'b, 'c, TFsm, Q, I, T>, ev: &TEvent, inspect_event_ctx: &mut I) +pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, Q, I, T>(ctx: &mut DispatchContext<'a, 'b, 'c, TFsm, Q, I, T>, ev: &FsmEvent, inspect_event_ctx: &mut I) -> FsmResult<()> where TFsm: FsmBackend, @@ -57,8 +70,11 @@ pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, Q, I, T>(ct backend: sub_fsm, inspect: &mut inspect, queue: &mut queue_adapter, - timers: ctx.timers + timers: ctx.timers, + timers_offset: ctx.timers_offset + TFsm::timer_count_self() }; - ::dispatch_event(sub_dispatch_ctx, FsmEvent::Event(ev.clone())) + let ev = ev.clone(); + + ::dispatch_event(sub_dispatch_ctx, ev) } \ No newline at end of file diff --git a/finny/src/fsm/events.rs b/finny/src/fsm/events.rs index dde2b29..ed319cf 100644 --- a/finny/src/fsm/events.rs +++ b/finny/src/fsm/events.rs @@ -1,6 +1,7 @@ use crate::{FsmBackend, FsmEventQueue, FsmEventQueueSender, TimerId, lib::*}; /// The internal event type that also allows stopping or starting the machine. +#[derive(Clone)] pub enum FsmEvent { Start, Stop, diff --git a/finny/src/fsm/fsm_impl.rs b/finny/src/fsm/fsm_impl.rs index a8f51ec..e73cebb 100644 --- a/finny/src/fsm/fsm_impl.rs +++ b/finny/src/fsm/fsm_impl.rs @@ -109,7 +109,8 @@ impl FsmFrontend backend: &mut self.backend, inspect: &mut self.inspect, queue: &mut self.queue, - timers: &mut self.timers + timers: &mut self.timers, + timers_offset: 0 }; F::dispatch_event(dispatch_ctx, event) diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 06fa972..6fc0bb9 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -50,4 +50,7 @@ pub trait FsmBackend where Self: Sized { fn dispatch_event(ctx: DispatchContext, event: FsmEvent) -> FsmDispatchResult where Q: FsmEventQueue, I: Inspect, T: FsmTimers; + + fn timer_count_self() -> usize; + fn timer_count_submachines() -> usize; } \ No newline at end of file diff --git a/finny/src/fsm/tests_fsm.rs b/finny/src/fsm/tests_fsm.rs index 6cb7da4..2fdc331 100644 --- a/finny/src/fsm/tests_fsm.rs +++ b/finny/src/fsm/tests_fsm.rs @@ -48,4 +48,12 @@ impl FsmBackend for TestFsm { { todo!() } + + fn timer_count_self() -> usize { + 0 + } + + fn timer_count_submachines() -> usize { + 0 + } } \ No newline at end of file diff --git a/finny/src/fsm/transitions.rs b/finny/src/fsm/transitions.rs index 1abef7c..91014e4 100644 --- a/finny/src/fsm/transitions.rs +++ b/finny/src/fsm/transitions.rs @@ -84,6 +84,44 @@ pub trait FsmTransitionFsmStart { let cs = context.backend.current_states.as_mut(); cs[region] = FsmCurrentState::State(::fsm_state()); } + + /// Executed after the transition on the parent FSM (F) and triggers the first `start()` call if necessary. Subsequent + /// dispatches are handled using the main dispatch table. + fn execute_on_sub_entry<'a, 'b, 'c, 'd, Q, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, _region: FsmRegionId, inspect_event_ctx: &mut I) + -> FsmDispatchResult + where + TInitialState: FsmBackend, + Q: FsmEventQueue, + I: Inspect, + ::Events: From<::Events>, + ::States: AsMut, + TInitialState: DerefMut>, + T: FsmTimers + { + let sub_backend: &mut TInitialState = context.backend.states.as_mut(); + let states = sub_backend.get_current_states(); + if FsmCurrentState::all_stopped(states.as_ref()) { + let mut queue_adapter = FsmEventQueueSub { + parent: context.queue, + _parent_fsm: PhantomData::::default(), + _sub_fsm: PhantomData::::default() + }; + + let mut inspect = inspect_event_ctx.for_sub_machine::(); + + let sub_dispatch_context = DispatchContext { + backend: sub_backend, + inspect: &mut inspect, + queue: &mut queue_adapter, + timers: context.timers, + timers_offset: F::timer_count_self() + }; + + return TInitialState::dispatch_event(sub_dispatch_context, FsmEvent::Start); + } + + Ok(()) + } } /// A transition's action that operates on both the exit and entry states. @@ -153,7 +191,8 @@ pub trait FsmTransitionAction { backend: sub_backend, inspect: &mut inspect, queue: &mut queue_adapter, - timers: context.timers + timers: context.timers, + timers_offset: 0 }; return TStateTo::dispatch_event(sub_dispatch_context, FsmEvent::Start); diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index a8d0f6b..b942c4e 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -1,4 +1,4 @@ -use proc_macro2::{TokenStream}; +use proc_macro2::{Span, TokenStream}; use quote::{TokenStreamExt, quote}; use crate::{fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs}}; @@ -17,6 +17,45 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let region_count = fsm.fsm.regions.len(); let (fsm_generics_impl, fsm_generics_type, fsm_generics_where) = fsm.base.fsm_generics.split_for_impl(); + + let timers_count: usize = fsm.fsm.states.iter().map(|s| s.1.timers.len()).sum(); + let sub_machines: Vec<_> = fsm.fsm.states.iter() + .filter_map(|(_, state)| { + match state.kind { + FsmStateKind::Normal => None, + FsmStateKind::SubMachine(_) => { + Some(&state.ty) + } + } + }).collect(); + + let sub_machine_states: Vec<_> = fsm.fsm.states.iter() + .filter(|(_, state)| { + if let FsmStateKind::SubMachine(_) = state.kind { true } else { false } + }).collect(); + + let timer_ranges = { + let mut q = TokenStream::new(); + + q.append_all(quote! { + let self_timer_range = (ctx.timers_offset + 1)..(ctx.timers_offset + 1 + #timers_count ); + }); + + let mut prev = syn::Ident::new(&"self_timer_range", Span::call_site()); + + for (ty, state) in sub_machine_states { + let time_range_field = &state.state_storage_field; + + q.append_all(quote! { + let #time_range_field = (#prev . end) .. (#prev . end) + (<#ty>::timer_count_self() + <#ty>::timer_count_submachines()); + }); + + prev = time_range_field.clone(); + } + + q + }; + let states_store = { @@ -567,7 +606,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let sub = quote! { ( finny::FsmCurrentState::State(#states_enum_ty :: #kind_variant), finny::FsmEvent::Event(#event_enum_ty::#kind_variant(ev)) ) => { - return finny::dispatch_to_submachine::<_, #kind, _, _, _, _>(&mut ctx, ev, &mut inspect_event_ctx); + return finny::dispatch_to_submachine::<_, #kind, _, _, _, _>(&mut ctx, &finny::FsmEvent::Event(ev.clone()), &mut inspect_event_ctx); }, }; @@ -581,6 +620,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let timers = { let mut timer_dispatch = TokenStream::new(); + // our timers for state in ®ion.states { for timer in &state.timers { @@ -588,16 +628,33 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let timer_ty = timer.get_ty(&fsm.base); timer_dispatch.append_all(quote! { - (_, finny::FsmEvent::Timer( timer_id @ #timer_id )) => { + (_, finny::FsmEvent::Timer( timer_id )) if self_timer_range.contains(timer_id) => { { use crate::finny::FsmTimer; - < #timer_ty > :: execute_trigger(#timer_id, &mut ctx, &mut inspect_event_ctx); + < #timer_ty > :: execute_trigger(*timer_id, &mut ctx, &mut inspect_event_ctx); } }, }); } } + // sub machines + for state in region.states.iter().filter(|s| if let FsmStateKind::SubMachine(_) = s.kind { true } else { false }) + { + let timer_region_field = &state.state_storage_field; + let sub = &state.ty; + + timer_dispatch.append_all(quote! { + (_, finny::FsmEvent::Timer( timer_id )) if #timer_region_field.contains(timer_id) => { + { + let ev = finny::FsmEvent::Timer(*timer_id); + let mut ctx = ctx.with_timers_offset(#timer_region_field.start); + return finny::dispatch_to_submachine::<_, #sub, _, _, _, _>(&mut ctx, &ev, &mut inspect_event_ctx); + } + } + }); + } + timer_dispatch }; @@ -620,6 +677,16 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream }); } + let fn_timer_count_submachines = { + let mut q = quote! { 0 }; + if sub_machines.len() > 0 { + q.append_all(quote! { + }); + q.append_separated(sub_machines.iter().map(|s| quote! { ( <#s>::timer_count_self() + <#s>::timer_count_submachines() ) }), quote! { + }); + } + + q + }; + quote! { impl #fsm_generics_impl finny::FsmBackend for #fsm_ty #fsm_generics_type @@ -638,6 +705,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let mut transition_misses = 0; let mut inspect_event_ctx = ctx.inspect.new_event::(&event); + + #timer_ranges #regions @@ -651,6 +720,14 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream result } + + fn timer_count_self() -> usize { + #timers_count + } + + fn timer_count_submachines() -> usize { + #fn_timer_count_submachines + } } } }; diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index d09b36a..fc38fd8 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -31,7 +31,9 @@ pub struct EventEnter { shift: bool } #[finny_fsm] fn build_fsm(mut fsm: FsmBuilder) -> BuiltFsm { fsm.events_debug(); - fsm.initial_state::(); + fsm.initial_states::<(StateA, BlinkerMachine)>(); + + fsm.sub_machine::(); fsm.state::(); @@ -76,6 +78,40 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF fsm.build() } +#[derive(Default, Debug)] +pub struct LightOn; +#[derive(Default, Debug)] +pub struct LightOff; +#[derive(Default, Debug)] +pub struct BlinkingOn; +#[derive(Default, Clone, Debug)] +pub struct BlinkToggle; + +#[finny_fsm] +fn build_blinker_fsm(mut fsm: FsmBuilder) -> BuiltFsm { + fsm.events_debug(); + fsm.initial_states::<(LightOff, BlinkingOn)>(); + + fsm.state::() + .on_event::() + .transition_to::(); + + fsm.state::() + .on_event::() + .transition_to::(); + + fsm.state::() + .on_entry_start_timer(|ctx, settings| { + settings.timeout = Duration::from_millis(50); + settings.renew = true; + }, |ctx, state| { + Some( BlinkToggle.into() ) + }); + + fsm.build() +} + + #[test] fn test_timers_fsm() -> FsmResult<()> { From 464d85cf5d95ebbee1b80645858a4c60394ec709 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Mon, 25 Jan 2021 20:13:53 +0100 Subject: [PATCH 16/38] silly offsets --- finny/src/fsm/dispatch.rs | 4 +++- finny/src/fsm/fsm_impl.rs | 2 +- finny/src/fsm/transitions.rs | 4 ++-- finny/src/inspect/slog.rs | 12 ++++++++---- finny_derive/src/codegen.rs | 31 +++++++++++++++++++++++-------- finny_tests/tests/fsm_timers.rs | 2 ++ 6 files changed, 39 insertions(+), 16 deletions(-) diff --git a/finny/src/fsm/dispatch.rs b/finny/src/fsm/dispatch.rs index c386faa..2dfc715 100644 --- a/finny/src/fsm/dispatch.rs +++ b/finny/src/fsm/dispatch.rs @@ -30,6 +30,7 @@ where F: FsmBackend, } } + /* pub fn with_timers_offset(&'a mut self, timers_offset: usize) -> Self where 'a: 'b + 'c { @@ -40,7 +41,8 @@ where F: FsmBackend, timers: &mut self.timers, timers_offset } - } + + */ } /// Used to funnel the event down to the sub-machine. diff --git a/finny/src/fsm/fsm_impl.rs b/finny/src/fsm/fsm_impl.rs index e73cebb..7dddc2e 100644 --- a/finny/src/fsm/fsm_impl.rs +++ b/finny/src/fsm/fsm_impl.rs @@ -110,7 +110,7 @@ impl FsmFrontend inspect: &mut self.inspect, queue: &mut self.queue, timers: &mut self.timers, - timers_offset: 0 + timers_offset: 1 }; F::dispatch_event(dispatch_ctx, event) diff --git a/finny/src/fsm/transitions.rs b/finny/src/fsm/transitions.rs index 91014e4..662dcda 100644 --- a/finny/src/fsm/transitions.rs +++ b/finny/src/fsm/transitions.rs @@ -114,7 +114,7 @@ pub trait FsmTransitionFsmStart { inspect: &mut inspect, queue: &mut queue_adapter, timers: context.timers, - timers_offset: F::timer_count_self() + timers_offset: context.timers_offset }; return TInitialState::dispatch_event(sub_dispatch_context, FsmEvent::Start); @@ -192,7 +192,7 @@ pub trait FsmTransitionAction { inspect: &mut inspect, queue: &mut queue_adapter, timers: context.timers, - timers_offset: 0 + timers_offset: context.timers_offset }; return TStateTo::dispatch_event(sub_dispatch_context, FsmEvent::Start); diff --git a/finny/src/inspect/slog.rs b/finny/src/inspect/slog.rs index ad654ac..fa83cb7 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, FsmEvent, Inspect}; +use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect}; use crate::lib::*; use AsRef; @@ -15,10 +15,14 @@ impl InspectSlog { } } -impl Inspect for InspectSlog { +impl Inspect for InspectSlog +{ fn new_event(&self, event: &FsmEvent<::Events>) -> Self { - let event = event.as_ref().to_string(); - let kv = o!("event" => event); + let event_display = match event { + FsmEvent::Timer(t) => format!("Fsm::Timer({})", *t), + _ => event.as_ref().to_string() + }; + let kv = o!("event" => event_display); info!(self.logger, "Dispatching"; &kv); InspectSlog { logger: self.logger.new(kv) diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index b942c4e..03a49e2 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -38,7 +38,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let mut q = TokenStream::new(); q.append_all(quote! { - let self_timer_range = (ctx.timers_offset + 1)..(ctx.timers_offset + 1 + #timers_count ); + let self_timer_range = (ctx.timers_offset)..(ctx.timers_offset + #timers_count); }); let mut prev = syn::Ident::new(&"self_timer_range", Span::call_site()); @@ -499,10 +499,21 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream } }; - let fsm_sub_entry = match transition.ty { - FsmTransitionType::StateTransition(FsmStateTransition { state_to: FsmTransitionState::State(FsmState { kind: FsmStateKind::SubMachine(_), .. }), .. }) => { + let fsm_sub_entry = match &transition.ty { + FsmTransitionType::StateTransition(FsmStateTransition {state_to: FsmTransitionState::State(s @ FsmState { kind: FsmStateKind::SubMachine(_), .. }), .. }) => { + let timers_field = &s.state_storage_field; quote! { - <#transition_ty>::execute_on_sub_entry(&mut ctx, #region_id, &mut inspect_event_ctx); + { + let mut ctx = finny::DispatchContext { + queue: ctx.queue, + backend: ctx.backend, + inspect: ctx.inspect, + timers: ctx.timers, + timers_offset: #timers_field.start + }; + + <#transition_ty>::execute_on_sub_entry(&mut ctx, #region_id, &mut inspect_event_ctx); + } } }, _ => TokenStream::new() @@ -530,7 +541,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream timers_enter.append_all(quote! { { use finny::FsmTimer; - ctx.backend.states. #timer_field . execute_on_enter( #timer_id, &ctx.backend.context, &mut inspect_event_ctx, ctx.timers ); + ctx.backend.states. #timer_field . execute_on_enter( ctx.timers_offset + #timer_id - 1, &ctx.backend.context, &mut inspect_event_ctx, ctx.timers ); } }); } @@ -560,7 +571,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream timers_exit.append_all(quote! { { use finny::FsmTimer; - ctx.backend.states. #timer_field . execute_on_exit( #timer_id, &mut inspect_event_ctx, ctx.timers ); + ctx.backend.states. #timer_field . execute_on_exit( ctx.timers_offset + #timer_id - 1, &mut inspect_event_ctx, ctx.timers ); } }); } @@ -603,9 +614,11 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let kind = &submachine.ty; let fsm_sub = FsmTypes::new(&submachine.ty, &fsm.base.fsm_generics); let kind_variant = fsm_sub.get_fsm_no_generics_ty(); + let timer_region_field = &submachine.state_storage_field; let sub = quote! { ( finny::FsmCurrentState::State(#states_enum_ty :: #kind_variant), finny::FsmEvent::Event(#event_enum_ty::#kind_variant(ev)) ) => { + //let mut ctx = ctx.with_timers_offset(#timer_region_field.start); return finny::dispatch_to_submachine::<_, #kind, _, _, _, _>(&mut ctx, &finny::FsmEvent::Event(ev.clone()), &mut inspect_event_ctx); }, }; @@ -628,7 +641,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let timer_ty = timer.get_ty(&fsm.base); timer_dispatch.append_all(quote! { - (_, finny::FsmEvent::Timer( timer_id )) if self_timer_range.contains(timer_id) => { + (_, finny::FsmEvent::Timer( timer_id )) if self_timer_range.contains(timer_id) && (*timer_id - (self_timer_range.start) + 1) == #timer_id => { { use crate::finny::FsmTimer; < #timer_ty > :: execute_trigger(*timer_id, &mut ctx, &mut inspect_event_ctx); @@ -648,7 +661,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream (_, finny::FsmEvent::Timer( timer_id )) if #timer_region_field.contains(timer_id) => { { let ev = finny::FsmEvent::Timer(*timer_id); - let mut ctx = ctx.with_timers_offset(#timer_region_field.start); + //let mut ctx = ctx.with_timers_offset(#timer_region_field.start); return finny::dispatch_to_submachine::<_, #sub, _, _, _, _>(&mut ctx, &ev, &mut inspect_event_ctx); } } @@ -707,6 +720,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let mut inspect_event_ctx = ctx.inspect.new_event::(&event); #timer_ranges + + inspect_event_ctx.info(&format!("self timers range: {:?}", &self_timer_range)); #regions diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index fc38fd8..d018a9c 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -105,7 +105,9 @@ fn build_blinker_fsm(mut fsm: FsmBuilder) -> BuiltFsm { settings.timeout = Duration::from_millis(50); settings.renew = true; }, |ctx, state| { + // todo: this breaks it! Some( BlinkToggle.into() ) + //None }); fsm.build() From 4c9d93090445505545401dc687470b923f8df16e Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Mon, 25 Jan 2021 23:25:44 +0100 Subject: [PATCH 17/38] timers to enums --- finny/src/fsm/dispatch.rs | 40 +++++------ finny/src/fsm/events.rs | 14 ++-- finny/src/fsm/fsm_factory.rs | 2 +- finny/src/fsm/fsm_impl.rs | 11 ++- finny/src/fsm/inspect.rs | 10 +-- finny/src/fsm/mod.rs | 8 ++- finny/src/fsm/tests_fsm.rs | 9 ++- finny/src/fsm/timers.rs | 118 +++++++++++++++++++++++++------- finny/src/fsm/transitions.rs | 42 ++++++++---- finny/src/inspect/slog.rs | 8 +-- finny/src/timers/std.rs | 32 +++++---- finny_derive/src/codegen.rs | 28 +++++--- finny_tests/tests/fsm_timers.rs | 8 ++- 13 files changed, 215 insertions(+), 115 deletions(-) diff --git a/finny/src/fsm/dispatch.rs b/finny/src/fsm/dispatch.rs index 2dfc715..a7f6770 100644 --- a/finny/src/fsm/dispatch.rs +++ b/finny/src/fsm/dispatch.rs @@ -1,24 +1,23 @@ -use crate::{FsmTimers, lib::*}; +use crate::{FsmTimers, FsmTimersSub, lib::*}; use crate::{EventContext, FsmBackend, FsmBackendImpl, FsmEvent, FsmEventQueue, FsmEventQueueSub, FsmRegionId, FsmResult, Inspect}; pub struct DispatchContext<'a, 'b, 'c, F, Q, I, T> where F: FsmBackend, Q: FsmEventQueue, I: Inspect, - T: FsmTimers + T: FsmTimers { pub queue: &'a mut Q, pub inspect: &'b mut I, pub backend: &'c mut FsmBackendImpl, - pub timers: &'a mut T, - pub timers_offset: usize + pub timers: &'a mut T } impl<'a, 'b, 'c, F, Q, I, T> DispatchContext<'a, 'b, 'c, F, Q, I, T> where F: FsmBackend, Q: FsmEventQueue, I: Inspect, - T: FsmTimers + T: FsmTimers { pub fn to_event_context(&'a mut self, region: FsmRegionId) -> EventContext<'a, F, Q> @@ -28,25 +27,12 @@ where F: FsmBackend, queue: self.queue, region } - } - - /* - pub fn with_timers_offset(&'a mut self, timers_offset: usize) -> Self - where 'a: 'b + 'c - { - DispatchContext { - queue: &mut self.queue, - inspect: &mut self.inspect, - backend: &mut self.backend, - timers: &mut self.timers, - timers_offset - } - - */ + } } /// Used to funnel the event down to the sub-machine. -pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, Q, I, T>(ctx: &mut DispatchContext<'a, 'b, 'c, TFsm, Q, I, T>, ev: &FsmEvent, inspect_event_ctx: &mut I) +pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, TTimer, Q, I, T>(ctx: &mut DispatchContext<'a, 'b, 'c, TFsm, Q, I, T>, + ev: &FsmEvent::Timers>, inspect_event_ctx: &mut I) -> FsmResult<()> where TFsm: FsmBackend, @@ -55,8 +41,9 @@ pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, Q, I, T>(ct Q: FsmEventQueue, I: Inspect, ::Events: From<::Events>, + ::Timers: From<::Timers>, TEvent: Clone, - T: FsmTimers + T: FsmTimers { let sub_fsm: &mut TSubMachine = ctx.backend.states.as_mut(); @@ -66,14 +53,19 @@ pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, Q, I, T>(ct _sub_fsm: core::marker::PhantomData::::default() }; + let mut timers_adapter = FsmTimersSub { + parent: ctx.timers, + _parent_fsm: core::marker::PhantomData::::default(), + _sub_fsm: core::marker::PhantomData::::default() + }; + let mut inspect = inspect_event_ctx.for_sub_machine::(); let sub_dispatch_ctx = DispatchContext { backend: sub_fsm, inspect: &mut inspect, queue: &mut queue_adapter, - timers: ctx.timers, - timers_offset: ctx.timers_offset + TFsm::timer_count_self() + timers: &mut timers_adapter }; let ev = ev.clone(); diff --git a/finny/src/fsm/events.rs b/finny/src/fsm/events.rs index ed319cf..415e6aa 100644 --- a/finny/src/fsm/events.rs +++ b/finny/src/fsm/events.rs @@ -1,32 +1,32 @@ -use crate::{FsmBackend, FsmEventQueue, FsmEventQueueSender, TimerId, lib::*}; +use crate::{FsmBackend, FsmEventQueue, FsmEventQueueSender, lib::*}; /// The internal event type that also allows stopping or starting the machine. #[derive(Clone)] -pub enum FsmEvent { +pub enum FsmEvent { Start, Stop, - Timer(TimerId), + Timer(T), Event(E) } -impl From for FsmEvent { +impl From for FsmEvent { fn from(event: E) -> Self { FsmEvent::Event(event) } } -impl Debug for FsmEvent where E: Debug { +impl Debug for FsmEvent where E: Debug, T: Debug { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { FsmEvent::Start => f.write_str("Fsm::Start"), FsmEvent::Stop => f.write_str("Fsm::Stop"), - FsmEvent::Timer(t) => f.write_fmt(format_args!("Fsm::Timer({})", t)), + FsmEvent::Timer(t) => f.write_fmt(format_args!("Fsm::Timer({:?})", t)), FsmEvent::Event(ev) => ev.fmt(f) } } } -impl AsRef for FsmEvent where E: AsRef { +impl AsRef for FsmEvent where E: AsRef { fn as_ref(&self) -> &str { match self { FsmEvent::Start => "Fsm::Start", diff --git a/finny/src/fsm/fsm_factory.rs b/finny/src/fsm/fsm_factory.rs index 4ad6472..0e862f0 100644 --- a/finny/src/fsm/fsm_factory.rs +++ b/finny/src/fsm/fsm_factory.rs @@ -12,7 +12,7 @@ pub trait FsmFactory { /// Build a new frontend for the FSM with all the environmental services provided by the caller. fn new_with(context: ::Context, queue: Q, inspect: I, timers: T) -> FsmResult> - where Q: FsmEventQueue, I: Inspect, T: FsmTimers + where Q: FsmEventQueue, I: Inspect, T: FsmTimers { let frontend = FsmFrontend { queue, diff --git a/finny/src/fsm/fsm_impl.rs b/finny/src/fsm/fsm_impl.rs index 7dddc2e..5448318 100644 --- a/finny/src/fsm/fsm_impl.rs +++ b/finny/src/fsm/fsm_impl.rs @@ -52,7 +52,7 @@ impl Deref for FsmBackendImpl { /// The frontend of a state machine which also includes environmental services like queues /// and inspection. The usual way to use the FSM. pub struct FsmFrontend - where F: FsmBackend, Q: FsmEventQueue, I: Inspect, T: FsmTimers + where F: FsmBackend, Q: FsmEventQueue, I: Inspect, T: FsmTimers { pub backend: FsmBackendImpl, pub queue: Q, @@ -61,7 +61,7 @@ pub struct FsmFrontend } impl FsmFrontend - where F: FsmBackend, Q: FsmEventQueue, I: Inspect, T: FsmTimers + where F: FsmBackend, Q: FsmEventQueue, I: Inspect, T: FsmTimers { /// Start the FSM, initiates the transition to the initial state. pub fn start(&mut self) -> FsmResult<()> { @@ -104,13 +104,12 @@ impl FsmFrontend } /// Dispatch only this event, do not run it to completition. - pub fn dispatch_single_event(&mut self, event: FsmEvent<::Events>) -> FsmResult<()> { + pub fn dispatch_single_event(&mut self, event: FsmEvent<::Events, ::Timers>) -> FsmResult<()> { let dispatch_ctx = DispatchContext { backend: &mut self.backend, inspect: &mut self.inspect, queue: &mut self.queue, - timers: &mut self.timers, - timers_offset: 1 + timers: &mut self.timers }; F::dispatch_event(dispatch_ctx, event) @@ -118,7 +117,7 @@ impl FsmFrontend } impl Deref for FsmFrontend - where F: FsmBackend, Q: FsmEventQueue, I: Inspect, T: FsmTimers + where F: FsmBackend, Q: FsmEventQueue, I: Inspect, T: FsmTimers { type Target = FsmBackendImpl; diff --git a/finny/src/fsm/inspect.rs b/finny/src/fsm/inspect.rs index b634a6b..53688a0 100644 --- a/finny/src/fsm/inspect.rs +++ b/finny/src/fsm/inspect.rs @@ -1,13 +1,13 @@ -use crate::{TimerId, lib::*}; +use crate::lib::*; use crate::{FsmBackend, FsmError, FsmEvent}; pub trait Inspect { - fn new_event(&self, event: &FsmEvent<::Events>) -> Self; + fn new_event(&self, event: &FsmEvent<::Events, ::Timers>) -> Self; fn event_done(self); fn for_transition(&self) -> Self; fn for_sub_machine(&self) -> Self; - fn for_timer(&self, timer_id: TimerId) -> Self; + fn for_timer(&self, timer_id: ::Timers) -> Self where F: FsmBackend; fn on_guard(&self, guard_result: bool); fn on_state_enter(&self); @@ -28,7 +28,7 @@ impl InspectNull { } impl Inspect for InspectNull { - fn new_event(&self, _event: &FsmEvent<::Events>) -> Self { + fn new_event(&self, _event: &FsmEvent<::Events, ::Timers>) -> Self { Self::default() } @@ -40,7 +40,7 @@ impl Inspect for InspectNull { Self::default() } - fn for_timer(&self, timer_id: TimerId) -> Self { + fn for_timer(&self, _timer_id: ::Timers) -> Self where F: FsmBackend { Self::default() } diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 6fc0bb9..3f5eae5 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -32,7 +32,7 @@ pub enum FsmError { NoTransition, QueueOverCapacity, NotSupported, - TimerNotStarted(TimerId) + TimerNotStarted } pub type FsmDispatchResult = FsmResult<()>; @@ -47,9 +47,11 @@ pub trait FsmBackend where Self: Sized { /// A tagged union type with all the supported events. This type has to support cloning to facilitate /// the dispatch into sub-machines and into multiple regions. type Events: AsRef + Clone; + /// An enum with variants for all the possible timer instances, with support for submachines. + type Timers: Debug + Clone + PartialEq; - fn dispatch_event(ctx: DispatchContext, event: FsmEvent) -> FsmDispatchResult - where Q: FsmEventQueue, I: Inspect, T: FsmTimers; + fn dispatch_event(ctx: DispatchContext, event: FsmEvent) -> FsmDispatchResult + where Q: FsmEventQueue, I: Inspect, T: FsmTimers; fn timer_count_self() -> usize; fn timer_count_submachines() -> usize; diff --git a/finny/src/fsm/tests_fsm.rs b/finny/src/fsm/tests_fsm.rs index 2fdc331..9019238 100644 --- a/finny/src/fsm/tests_fsm.rs +++ b/finny/src/fsm/tests_fsm.rs @@ -35,16 +35,21 @@ impl AsRef for Events { todo!() } } +#[derive(Debug, Clone, PartialEq)] +pub enum FsmBackendTimers { + +} impl FsmBackend for TestFsm { type Context = (); type States = States; type Events = Events; + type Timers = FsmBackendTimers; - fn dispatch_event(_ctx: crate::DispatchContext, _event: crate::FsmEvent) -> crate::FsmDispatchResult + fn dispatch_event(_ctx: crate::DispatchContext, _event: crate::FsmEvent) -> crate::FsmDispatchResult where Q: crate::FsmEventQueue, - I: crate::Inspect, T: crate::FsmTimers + I: crate::Inspect, T: crate::FsmTimers { todo!() } diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index 13b9642..edd761d 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -1,9 +1,11 @@ use crate::{DispatchContext, FsmError, FsmEventQueue, Inspect, lib::*}; use crate::{FsmBackend, FsmResult}; -#[derive(Debug, Clone, Copy)] -pub struct TimerInstance { - pub id: TimerId, +#[derive(Debug, Clone)] +pub struct TimerInstance + where F: FsmBackend +{ + pub id: ::Timers, pub settings: TimerFsmSettings } @@ -13,15 +15,15 @@ pub trait FsmTimer fn setup(ctx: &::Context, settings: &mut TimerFsmSettings); fn trigger(ctx: &::Context, state: &S) -> Option< ::Events >; - fn get_instance(&self) -> &Option; - fn get_instance_mut(&mut self) -> &mut Option; + fn get_instance(&self) -> &Option>; + fn get_instance_mut(&mut self) -> &mut Option>; - fn execute_on_enter(&mut self, id: TimerId, ctx: &::Context, inspect: &mut I, timers: &mut T) { - let log = inspect.for_timer(id); + fn execute_on_enter>(&mut self, id: F::Timers, ctx: &::Context, inspect: &mut I, timers: &mut T) { + let log = inspect.for_timer::(id.clone()); let mut settings = TimerFsmSettings::default(); Self::setup(ctx, &mut settings); if settings.enabled { - match timers.create(id, &settings.to_timer_settings()) { + match timers.create(id.clone(), &settings.to_timer_settings()) { Ok(_) => { let instance = self.get_instance_mut(); *instance = Some( TimerInstance { id, settings } ); @@ -36,8 +38,8 @@ pub trait FsmTimer } } - fn execute_on_exit(&mut self, id: TimerId, inspect: &mut I, timers: &mut T) { - let log = inspect.for_timer(id); + fn execute_on_exit>(&mut self, id: F::Timers, inspect: &mut I, timers: &mut T) { + let log = inspect.for_timer::(id.clone()); match self.get_instance_mut() { Some(instance) => { if id == instance.id && instance.settings.cancel_on_state_exit { @@ -56,15 +58,15 @@ pub trait FsmTimer } } - fn execute_trigger<'a, 'b, 'c, 'd, Q, I, T>(id: TimerId, context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, inspect: &mut I) + fn execute_trigger<'a, 'b, 'c, 'd, Q, I, T>(id: F::Timers, context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, inspect: &mut I) where Q: FsmEventQueue, I: Inspect, ::States: AsRef, ::States: AsRef, - T: FsmTimers + T: FsmTimers { - let inspect = inspect.for_timer(id); + let inspect = inspect.for_timer::(id); let timer: &Self = context.backend.states.as_ref(); match timer.get_instance() { Some(_) => { @@ -84,7 +86,7 @@ pub trait FsmTimer }, None => { - let error = FsmError::TimerNotStarted(id); + let error = FsmError::TimerNotStarted; inspect.on_error("Timer hasn't been started.", &error); } } @@ -129,15 +131,17 @@ pub struct TimerSettings pub renew: bool } -pub type TimerId = usize; +//pub type TimerId = usize; -pub trait FsmTimers { - fn create(&mut self, id: TimerId, settings: &TimerSettings) -> FsmResult<()>; - fn cancel(&mut self, id: TimerId) -> FsmResult<()>; +pub trait FsmTimers + where F: FsmBackend +{ + fn create(&mut self, id: ::Timers, settings: &TimerSettings) -> FsmResult<()>; + fn cancel(&mut self, id: ::Timers) -> FsmResult<()>; /// Return the timer that was triggered. Poll this until it returns None. The events /// should be dequeued in a FIFO manner. - fn get_triggered_timer(&mut self) -> Option; + fn get_triggered_timer(&mut self) -> Option<::Timers>; } @@ -149,16 +153,82 @@ pub struct FsmTimersTriggerEventsResult { #[derive(Debug, Default, Copy, Clone)] pub struct FsmTimersNull; -impl FsmTimers for FsmTimersNull { - fn create(&mut self, _id: TimerId, _settings: &TimerSettings) -> FsmResult<()> { +impl FsmTimers for FsmTimersNull + where F: FsmBackend +{ + fn create(&mut self, _id: ::Timers, _settings: &TimerSettings) -> FsmResult<()> { Err(FsmError::NotSupported) } - fn cancel(&mut self, _id: TimerId) -> FsmResult<()> { + fn cancel(&mut self, _id: ::Timers) -> FsmResult<()> { Err(FsmError::NotSupported) } - fn get_triggered_timer(&mut self) -> Option { + fn get_triggered_timer(&mut self) -> Option<::Timers> { None } -} \ No newline at end of file +} + + +pub struct FsmTimersSub<'a, T, F, FSub> + where + F: FsmBackend, + T: FsmTimers +{ + pub parent: &'a mut T, + pub _parent_fsm: PhantomData, + pub _sub_fsm: PhantomData +} + +impl<'a, T, F, FSub> FsmTimers for FsmTimersSub<'a, T, F, FSub> + where + F: FsmBackend, + T: FsmTimers, + FSub: FsmBackend, + ::Timers: From<::Timers> +{ + fn create(&mut self, id: ::Timers, settings: &TimerSettings) -> FsmResult<()> { + self.parent.create(id.into(), settings) + } + + fn cancel(&mut self, id: ::Timers) -> FsmResult<()> { + self.parent.cancel(id.into()) + } + + fn get_triggered_timer(&mut self) -> Option<::Timers> { + // todo: not needed, split the trait + None + } +} + + + + +/* + +pub struct FsmEventQueueSub<'a, Q, F, FSub> + where + F: FsmBackend, + Q: FsmEventQueueSender +{ + pub parent: &'a mut Q, + pub _parent_fsm: PhantomData, + pub _sub_fsm: PhantomData +} + +impl<'a, Q, F, FSub> FsmEventQueue for FsmEventQueueSub<'a, Q, F, FSub> + where + F: FsmBackend, + Q: FsmEventQueueSender, + FSub: FsmBackend, + ::Events: From<::Events> +{ + fn dequeue(&mut self) -> Option<::Events> { + None + } + + fn len(&self) -> usize { + 0 + } +} +*/ \ No newline at end of file diff --git a/finny/src/fsm/transitions.rs b/finny/src/fsm/transitions.rs index 662dcda..c8df0e1 100644 --- a/finny/src/fsm/transitions.rs +++ b/finny/src/fsm/transitions.rs @@ -1,6 +1,6 @@ //! All of these traits will be implemented by the procedural code generator. -use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, FsmTimers, lib::*}; +use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, FsmTimers, FsmTimersSub, lib::*}; use crate::{DispatchContext, EventContext, FsmBackend, FsmCurrentState, FsmEvent, FsmEventQueue, FsmRegionId, FsmStateTransitionAsMut, FsmStates, Inspect}; /// A state's entry and exit actions. @@ -11,7 +11,7 @@ pub trait FsmState { fn on_exit<'a, Q: FsmEventQueue>(&mut self, context: &mut EventContext<'a, F, Q>); fn execute_on_entry<'a, 'b, 'c, 'd, Q, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, region: FsmRegionId) - where Q: FsmEventQueue, I: Inspect, ::States: AsMut, T: FsmTimers + where Q: FsmEventQueue, I: Inspect, ::States: AsMut, T: FsmTimers { let mut event_context = EventContext { context: &mut context.backend.context, @@ -24,7 +24,7 @@ pub trait FsmState { } fn execute_on_exit<'a, 'b, 'c, 'd, Q, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, region: FsmRegionId) - where Q: FsmEventQueue, I: Inspect, ::States: AsMut, T: FsmTimers + where Q: FsmEventQueue, I: Inspect, ::States: AsMut, T: FsmTimers { let mut event_context = EventContext { context: &mut context.backend.context, @@ -45,7 +45,7 @@ pub trait FsmTransitionGuard { fn guard<'a, Q: FsmEventQueue>(event: &E, context: &EventContext<'a, F, Q>, states: &'a ::States) -> bool; fn execute_guard<'a, 'b, 'c, 'd, Q: FsmEventQueue, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, event: &E, region: FsmRegionId, inspect_event_ctx: &mut I) -> bool - where I: Inspect, Self: Sized, T: FsmTimers + where I: Inspect, Self: Sized, T: FsmTimers { let event_context = EventContext { context: &mut context.backend.context, @@ -65,7 +65,7 @@ pub trait FsmTransitionGuard { /// The transition that starts the machine, triggered using the `start()` method. pub trait FsmTransitionFsmStart { fn execute_transition<'a, 'b, 'c, 'd, Q: FsmEventQueue, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, - _fsm_event: &FsmEvent<::Events>, + _fsm_event: &FsmEvent<::Events, ::Timers>, region: FsmRegionId, inspect_event_ctx: &mut I) where @@ -74,7 +74,7 @@ pub trait FsmTransitionFsmStart { ::States: AsMut, ::States: AsRef, Self: Sized, - T: FsmTimers + T: FsmTimers { let ctx = inspect_event_ctx.for_transition::(); ctx.on_state_enter::(); @@ -96,7 +96,8 @@ pub trait FsmTransitionFsmStart { ::Events: From<::Events>, ::States: AsMut, TInitialState: DerefMut>, - T: FsmTimers + T: FsmTimers, + ::Timers: From<::Timers> { let sub_backend: &mut TInitialState = context.backend.states.as_mut(); let states = sub_backend.get_current_states(); @@ -107,14 +108,19 @@ pub trait FsmTransitionFsmStart { _sub_fsm: PhantomData::::default() }; + let mut timers_adapter = FsmTimersSub { + parent: context.timers, + _parent_fsm: core::marker::PhantomData::::default(), + _sub_fsm: core::marker::PhantomData::::default() + }; + let mut inspect = inspect_event_ctx.for_sub_machine::(); let sub_dispatch_context = DispatchContext { backend: sub_backend, inspect: &mut inspect, queue: &mut queue_adapter, - timers: context.timers, - timers_offset: context.timers_offset + timers: &mut timers_adapter }; return TInitialState::dispatch_event(sub_dispatch_context, FsmEvent::Start); @@ -137,7 +143,7 @@ pub trait FsmTransitionAction { ::States: AsMut, TStateFrom: FsmState, TStateTo: FsmState, Self: Sized, - T: FsmTimers + T: FsmTimers { let inspect_ctx = inspect_event_ctx.for_transition::(); @@ -174,7 +180,8 @@ pub trait FsmTransitionAction { ::Events: From<::Events>, ::States: AsMut, TStateTo: DerefMut>, - T: FsmTimers + T: FsmTimers, + ::Timers: From<::Timers> { let sub_backend: &mut TStateTo = context.backend.states.as_mut(); let states = sub_backend.get_current_states(); @@ -185,14 +192,19 @@ pub trait FsmTransitionAction { _sub_fsm: PhantomData::::default() }; + let mut timers_adapter = FsmTimersSub { + parent: context.timers, + _parent_fsm: core::marker::PhantomData::::default(), + _sub_fsm: core::marker::PhantomData::::default() + }; + let mut inspect = inspect_event_ctx.for_sub_machine::(); let sub_dispatch_context = DispatchContext { backend: sub_backend, inspect: &mut inspect, queue: &mut queue_adapter, - timers: context.timers, - timers_offset: context.timers_offset + timers: &mut timers_adapter }; return TStateTo::dispatch_event(sub_dispatch_context, FsmEvent::Start); @@ -210,7 +222,7 @@ pub trait FsmAction { fn should_trigger_state_actions() -> bool; fn execute_action<'a, 'b, 'c, 'd, Q: FsmEventQueue, I, T>(context: &'d mut DispatchContext<'a, 'b, 'c, F, Q, I, T>, event: &E, region: FsmRegionId) - where ::States: AsMut, I: Inspect, T: FsmTimers + where ::States: AsMut, I: Inspect, T: FsmTimers { let mut event_context = EventContext { context: &mut context.backend.context, @@ -227,7 +239,7 @@ pub trait FsmAction { where I: Inspect, State: FsmState, ::States: AsMut, Self: Sized, - T: FsmTimers + T: FsmTimers { let ctx = inspect_event_ctx.for_transition::(); diff --git a/finny/src/inspect/slog.rs b/finny/src/inspect/slog.rs index fa83cb7..3593996 100644 --- a/finny/src/inspect/slog.rs +++ b/finny/src/inspect/slog.rs @@ -17,9 +17,9 @@ impl InspectSlog { impl Inspect for InspectSlog { - fn new_event(&self, event: &FsmEvent<::Events>) -> Self { + fn new_event(&self, event: &FsmEvent<::Events, ::Timers>) -> Self { let event_display = match event { - FsmEvent::Timer(t) => format!("Fsm::Timer({})", *t), + FsmEvent::Timer(t) => format!("Fsm::Timer({:?})", t), _ => event.as_ref().to_string() }; let kv = o!("event" => event_display); @@ -47,8 +47,8 @@ impl Inspect for InspectSlog } } - fn for_timer(&self, timer_id: crate::TimerId) -> Self { - let kv = o!("timer_id" => timer_id); + fn for_timer(&self, timer_id: ::Timers) -> Self where F: FsmBackend { + let kv = o!("timer_id" => format!("{:?}", timer_id)); InspectSlog { logger: self.logger.new(kv) } diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs index 09f2a36..e753429 100644 --- a/finny/src/timers/std.rs +++ b/finny/src/timers/std.rs @@ -1,9 +1,11 @@ use std::{time::{Duration, Instant}}; -use crate::{FsmTimers, TimerId}; +use crate::{FsmBackend, FsmTimers}; -pub struct TimersStd { - timers: Vec<(TimerId, StdTimer)>, - pending_intervals: Option<(TimerId, usize)> +pub struct TimersStd + where F: FsmBackend +{ + timers: Vec<(::Timers, StdTimer)>, + pending_intervals: Option<(::Timers, usize)> } #[derive(Debug)] @@ -12,7 +14,9 @@ enum StdTimer { Interval { started_at: Instant, interval: Duration } } -impl TimersStd { +impl TimersStd + where F: FsmBackend +{ pub fn new() -> Self { Self { timers: vec![], @@ -21,10 +25,12 @@ impl TimersStd { } } -impl FsmTimers for TimersStd { - fn create(&mut self, id: crate::TimerId, settings: &crate::TimerSettings) -> crate::FsmResult<()> { +impl FsmTimers for TimersStd + where F: FsmBackend +{ + fn create(&mut self, id: ::Timers, settings: &crate::TimerSettings) -> crate::FsmResult<()> { // try to cancel any existing ones - self.cancel(id)?; + self.cancel(id.clone())?; if settings.renew { self.timers.push((id, StdTimer::Interval { started_at: Instant::now(), interval: settings.timeout })); @@ -35,16 +41,16 @@ impl FsmTimers for TimersStd { Ok(()) } - fn cancel(&mut self, id: crate::TimerId) -> crate::FsmResult<()> { + fn cancel(&mut self, id: ::Timers) -> crate::FsmResult<()> { self.timers.retain(|(timer_id, _)| *timer_id != id); Ok(()) } - fn get_triggered_timer(&mut self) -> Option { + fn get_triggered_timer(&mut self) -> Option<::Timers> { if let Some((id, mut times)) = self.pending_intervals.take() { times -= 1; if times > 0 { - self.pending_intervals = Some((id, times)); + self.pending_intervals = Some((id.clone(), times)); } return Some(id); @@ -62,10 +68,10 @@ impl FsmTimers for TimersStd { let t = now.duration_since(*started_at); let times = ((t.as_secs_f32() / interval.as_secs_f32()).floor() as usize) - 1; if times > 0 { - self.pending_intervals = Some((*timer_id, times)); + self.pending_intervals = Some((timer_id.clone(), times)); } *started_at = now; - return Some(*timer_id); + return Some(timer_id.clone()); }, _ => () } diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 03a49e2..2faed31 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -12,6 +12,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let states_store_ty = ty_append(&fsm.base.fsm_ty, "States"); let states_enum_ty = ty_append(&fsm.base.fsm_ty, "CurrentState"); + let timers_enum_ty = ty_append(&fsm.base.fsm_ty, "Timers"); let event_enum_ty = fsm_types.get_fsm_events_ty(); let region_count = fsm.fsm.regions.len(); @@ -504,14 +505,6 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let timers_field = &s.state_storage_field; quote! { { - let mut ctx = finny::DispatchContext { - queue: ctx.queue, - backend: ctx.backend, - inspect: ctx.inspect, - timers: ctx.timers, - timers_offset: #timers_field.start - }; - <#transition_ty>::execute_on_sub_entry(&mut ctx, #region_id, &mut inspect_event_ctx); } } @@ -708,6 +701,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream type Context = #ctx_ty; type States = #states_store_ty #fsm_generics_type; type Events = #event_enum_ty; + type Timers = #timers_enum_ty; fn dispatch_event(mut ctx: finny::DispatchContext, event: finny::FsmEvent) -> finny::FsmDispatchResult where Q: finny::FsmEventQueue, @@ -837,6 +831,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let mut code = TokenStream::new(); + let mut enum_variants = vec![]; + let states = fsm.fsm.states.iter().map(|s| s.1); for state in states { @@ -844,6 +840,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let state_ty = &state.ty; let timer_ty = timer.get_ty(&fsm.base); + enum_variants.push(quote! { #timer_ty }); + let setup = remap_closure_inputs(&timer.setup.inputs, &[quote! { ctx }, quote! { settings }])?; let setup_body = &timer.setup.body; @@ -886,6 +884,20 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream } } + + let variants = { + let mut t = TokenStream::new(); + t.append_separated(enum_variants, quote! { , }); + t + }; + + code.append_all(quote! { + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] + pub enum #timers_enum_ty { + #variants + } + }); + code }; diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index d018a9c..0762e09 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -31,9 +31,9 @@ pub struct EventEnter { shift: bool } #[finny_fsm] fn build_fsm(mut fsm: FsmBuilder) -> BuiltFsm { fsm.events_debug(); - fsm.initial_states::<(StateA, BlinkerMachine)>(); - - fsm.sub_machine::(); + fsm.initial_state::(); + //fsm.initial_states::<(StateA, BlinkerMachine)>(); + //fsm.sub_machine::(); fsm.state::(); @@ -78,6 +78,7 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF fsm.build() } +/* #[derive(Default, Debug)] pub struct LightOn; #[derive(Default, Debug)] @@ -112,6 +113,7 @@ fn build_blinker_fsm(mut fsm: FsmBuilder) -> BuiltFsm { fsm.build() } +*/ From 568df0135939fa04cfbc5a9bf8632d3409aaecfd Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Mon, 25 Jan 2021 23:38:57 +0100 Subject: [PATCH 18/38] stopgap --- finny_derive/src/codegen.rs | 34 +++------------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 2faed31..16b7b08 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -33,30 +33,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let sub_machine_states: Vec<_> = fsm.fsm.states.iter() .filter(|(_, state)| { if let FsmStateKind::SubMachine(_) = state.kind { true } else { false } - }).collect(); - - let timer_ranges = { - let mut q = TokenStream::new(); - - q.append_all(quote! { - let self_timer_range = (ctx.timers_offset)..(ctx.timers_offset + #timers_count); - }); - - let mut prev = syn::Ident::new(&"self_timer_range", Span::call_site()); - - for (ty, state) in sub_machine_states { - let time_range_field = &state.state_storage_field; - - q.append_all(quote! { - let #time_range_field = (#prev . end) .. (#prev . end) + (<#ty>::timer_count_self() + <#ty>::timer_count_submachines()); - }); - - prev = time_range_field.clone(); - } - - q - }; - + }).collect(); let states_store = { @@ -654,7 +631,6 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream (_, finny::FsmEvent::Timer( timer_id )) if #timer_region_field.contains(timer_id) => { { let ev = finny::FsmEvent::Timer(*timer_id); - //let mut ctx = ctx.with_timers_offset(#timer_region_field.start); return finny::dispatch_to_submachine::<_, #sub, _, _, _, _>(&mut ctx, &ev, &mut inspect_event_ctx); } } @@ -703,9 +679,9 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream type Events = #event_enum_ty; type Timers = #timers_enum_ty; - fn dispatch_event(mut ctx: finny::DispatchContext, event: finny::FsmEvent) -> finny::FsmDispatchResult + fn dispatch_event(mut ctx: finny::DispatchContext, event: finny::FsmEvent) -> finny::FsmDispatchResult where Q: finny::FsmEventQueue, - I: finny::Inspect, T: finny::FsmTimers + I: finny::Inspect, T: finny::FsmTimers { use finny::{FsmTransitionGuard, FsmTransitionAction, FsmAction, FsmState, FsmTransitionFsmStart}; @@ -713,10 +689,6 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let mut inspect_event_ctx = ctx.inspect.new_event::(&event); - #timer_ranges - - inspect_event_ctx.info(&format!("self timers range: {:?}", &self_timer_range)); - #regions let result = if transition_misses == #region_count { From b1b009023e26b9c0aa1143fac6cc3e7021e0326b Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 26 Jan 2021 22:38:05 +0100 Subject: [PATCH 19/38] enum timer dispatch --- finny/src/fsm/dispatch.rs | 20 ++++++++++---- finny/src/fsm/events.rs | 20 ++++++++++++++ finny/src/fsm/timers.rs | 1 - finny_derive/src/codegen.rs | 55 +++++++++++++++++++++++-------------- finny_derive/src/fsm.rs | 4 +++ 5 files changed, 73 insertions(+), 27 deletions(-) diff --git a/finny/src/fsm/dispatch.rs b/finny/src/fsm/dispatch.rs index a7f6770..30b234c 100644 --- a/finny/src/fsm/dispatch.rs +++ b/finny/src/fsm/dispatch.rs @@ -31,19 +31,27 @@ where F: FsmBackend, } /// Used to funnel the event down to the sub-machine. -pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, TTimer, Q, I, T>(ctx: &mut DispatchContext<'a, 'b, 'c, TFsm, Q, I, T>, - ev: &FsmEvent::Timers>, inspect_event_ctx: &mut I) +pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, Q, I, T>(ctx: &mut DispatchContext<'a, 'b, 'c, TFsm, Q, I, T>, + //ev: &FsmEvent<::Events, ::Timers>, + //ev: TEvent, + ev: FsmEvent<::Events, ::Timers>, + inspect_event_ctx: &mut I) -> FsmResult<()> where TFsm: FsmBackend, ::States: AsMut, - TSubMachine: FsmBackend + DerefMut> + FsmBackend, + //TSubMachine: FsmBackend + DerefMut> + FsmBackend, + TSubMachine: FsmBackend + DerefMut>, Q: FsmEventQueue, I: Inspect, ::Events: From<::Events>, ::Timers: From<::Timers>, - TEvent: Clone, - T: FsmTimers + + T: FsmTimers, + //::Events: From + + //::Timers: From<::Timers>, + //::Timers: From<::Events> { let sub_fsm: &mut TSubMachine = ctx.backend.states.as_mut(); @@ -68,7 +76,7 @@ pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, TEvent, TTimer, Q, timers: &mut timers_adapter }; - let ev = ev.clone(); + // todo: convert the event to sub ::dispatch_event(sub_dispatch_ctx, ev) } \ No newline at end of file diff --git a/finny/src/fsm/events.rs b/finny/src/fsm/events.rs index 415e6aa..5567007 100644 --- a/finny/src/fsm/events.rs +++ b/finny/src/fsm/events.rs @@ -37,6 +37,26 @@ impl AsRef for FsmEvent where E: AsRef { } } +impl FsmEvent { + pub fn to_sub_fsm(self) -> FsmEvent<::Events, ::Timers> + where FSub: FsmBackend, + ::Timers: From, + ::Timers: From + { + match self { + FsmEvent::Start => FsmEvent::Start, + FsmEvent::Stop => FsmEvent::Stop, + FsmEvent::Timer(t) => { + FsmEvent::Timer(t.into()) + } + FsmEvent::Event(ev) => { + FsmEvent::Timer(ev.into()) + } + } + } +} + + pub type FsmRegionId = usize; /// The context that is given to all of the guards and actions. diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index edd761d..b29a58c 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -1,7 +1,6 @@ use crate::{DispatchContext, FsmError, FsmEventQueue, Inspect, lib::*}; use crate::{FsmBackend, FsmResult}; -#[derive(Debug, Clone)] pub struct TimerInstance where F: FsmBackend { diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 16b7b08..2ab024d 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -479,7 +479,6 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let fsm_sub_entry = match &transition.ty { FsmTransitionType::StateTransition(FsmStateTransition {state_to: FsmTransitionState::State(s @ FsmState { kind: FsmStateKind::SubMachine(_), .. }), .. }) => { - let timers_field = &s.state_storage_field; quote! { { <#transition_ty>::execute_on_sub_entry(&mut ctx, #region_id, &mut inspect_event_ctx); @@ -504,14 +503,13 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream if let Some(state) = state { for timer in &state.timers { - let timer_ty = timer.get_ty(&fsm.base); let timer_field = timer.get_field(&fsm.base); - let timer_id = timer.id; + let timer_ty = timer.get_ty(&fsm.base); timers_enter.append_all(quote! { { use finny::FsmTimer; - ctx.backend.states. #timer_field . execute_on_enter( ctx.timers_offset + #timer_id - 1, &ctx.backend.context, &mut inspect_event_ctx, ctx.timers ); + ctx.backend.states. #timer_field . execute_on_enter( #timers_enum_ty :: #timer_ty , &ctx.backend.context, &mut inspect_event_ctx, ctx.timers ); } }); } @@ -536,12 +534,12 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream if let Some(state) = state { for timer in &state.timers { let timer_field = timer.get_field(&fsm.base); - let timer_id = timer.id; + let timer_ty = timer.get_ty(&fsm.base); timers_exit.append_all(quote! { { use finny::FsmTimer; - ctx.backend.states. #timer_field . execute_on_exit( ctx.timers_offset + #timer_id - 1, &mut inspect_event_ctx, ctx.timers ); + ctx.backend.states. #timer_field . execute_on_exit( #timers_enum_ty :: #timer_ty , &mut inspect_event_ctx, ctx.timers ); } }); } @@ -584,12 +582,10 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let kind = &submachine.ty; let fsm_sub = FsmTypes::new(&submachine.ty, &fsm.base.fsm_generics); let kind_variant = fsm_sub.get_fsm_no_generics_ty(); - let timer_region_field = &submachine.state_storage_field; let sub = quote! { ( finny::FsmCurrentState::State(#states_enum_ty :: #kind_variant), finny::FsmEvent::Event(#event_enum_ty::#kind_variant(ev)) ) => { - //let mut ctx = ctx.with_timers_offset(#timer_region_field.start); - return finny::dispatch_to_submachine::<_, #kind, _, _, _, _>(&mut ctx, &finny::FsmEvent::Event(ev.clone()), &mut inspect_event_ctx); + return finny::dispatch_to_submachine::<_, #kind, _, _, _>(&mut ctx, finny::FsmEvent::Event(ev.clone()), &mut inspect_event_ctx); }, }; @@ -606,12 +602,10 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream // our timers for state in ®ion.states { for timer in &state.timers { - - let timer_id = timer.id; let timer_ty = timer.get_ty(&fsm.base); timer_dispatch.append_all(quote! { - (_, finny::FsmEvent::Timer( timer_id )) if self_timer_range.contains(timer_id) && (*timer_id - (self_timer_range.start) + 1) == #timer_id => { + (_, finny::FsmEvent::Timer( timer_id @ #timers_enum_ty :: #timer_ty )) => { { use crate::finny::FsmTimer; < #timer_ty > :: execute_trigger(*timer_id, &mut ctx, &mut inspect_event_ctx); @@ -624,17 +618,21 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream // sub machines for state in region.states.iter().filter(|s| if let FsmStateKind::SubMachine(_) = s.kind { true } else { false }) { - let timer_region_field = &state.state_storage_field; let sub = &state.ty; + let sub_ty = FsmTypes::new(sub, &fsm.base.fsm_generics); + let sub_variant = sub_ty.get_fsm_no_generics_ty(); + /* timer_dispatch.append_all(quote! { - (_, finny::FsmEvent::Timer( timer_id )) if #timer_region_field.contains(timer_id) => { + (_, finny::FsmEvent::Timer( #sub_variant (timer_id))) { { - let ev = finny::FsmEvent::Timer(*timer_id); - return finny::dispatch_to_submachine::<_, #sub, _, _, _, _>(&mut ctx, &ev, &mut inspect_event_ctx); + //let ev = finny::FsmEvent::Timer(*timer_id); + //return finny::dispatch_to_submachine::<_, #sub, _, _, _, _, _>(&mut ctx, &ev, &mut inspect_event_ctx); + todo!(" panic sub dispatch "); } } }); + */ } timer_dispatch @@ -808,6 +806,23 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let states = fsm.fsm.states.iter().map(|s| s.1); for state in states { + if let FsmStateKind::SubMachine(_) = &state.kind { + let sub_fsm_ty = FsmTypes::new(&state.ty, &fsm.base.fsm_generics); + let n = sub_fsm_ty.get_fsm_no_generics_ty(); + let t = sub_fsm_ty.get_fsm_timers_ty(); + enum_variants.push(quote! { #n ( #t ) }); + + code.append_all(quote! { + + impl From<#t> for #timers_enum_ty { + fn from(t: #t) -> Self { + #timers_enum_ty :: #n ( t ) + } + } + + }); + } + for timer in &state.timers { let state_ty = &state.ty; let timer_ty = timer.get_ty(&fsm.base); @@ -822,9 +837,9 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream code.append_all(quote! { - #[derive(Debug, Clone, Default)] + #[derive(Default)] pub struct #timer_ty { - instance: Option + instance: Option > } impl #fsm_generics_impl finny::FsmTimer< #fsm_ty #fsm_generics_type , #state_ty > for #timer_ty #fsm_generics_where { @@ -843,11 +858,11 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream ret } - fn get_instance(&self) -> &Option { + fn get_instance(&self) -> &Option > { &self.instance } - fn get_instance_mut(&mut self) -> &mut Option { + fn get_instance_mut(&mut self) -> &mut Option > { &mut self.instance } } diff --git a/finny_derive/src/fsm.rs b/finny_derive/src/fsm.rs index 9fd1341..7c07b31 100644 --- a/finny_derive/src/fsm.rs +++ b/finny_derive/src/fsm.rs @@ -29,4 +29,8 @@ impl FsmTypes { pub fn get_fsm_events_ty(&self) -> syn::Type { ty_append(&self.fsm_no_generics, "Events") } + + pub fn get_fsm_timers_ty(&self) -> syn::Type { + ty_append(&self.fsm_no_generics, "Timers") + } } \ No newline at end of file From f2d24e611e52130ad6d08e2d7ab855b671abcd19 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 26 Jan 2021 22:52:20 +0100 Subject: [PATCH 20/38] timers with routing to submachines --- finny/src/fsm/dispatch.rs | 21 +++++-------------- finny/src/fsm/inspect.rs | 2 +- finny/src/fsm/states.rs | 2 +- finny/src/fsm/timers.rs | 36 +-------------------------------- finny_derive/src/codegen.rs | 16 ++++----------- finny_tests/tests/fsm_timers.rs | 28 ++++++++++++++++--------- 6 files changed, 30 insertions(+), 75 deletions(-) diff --git a/finny/src/fsm/dispatch.rs b/finny/src/fsm/dispatch.rs index 30b234c..a12a659 100644 --- a/finny/src/fsm/dispatch.rs +++ b/finny/src/fsm/dispatch.rs @@ -32,26 +32,17 @@ where F: FsmBackend, /// Used to funnel the event down to the sub-machine. pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, Q, I, T>(ctx: &mut DispatchContext<'a, 'b, 'c, TFsm, Q, I, T>, - //ev: &FsmEvent<::Events, ::Timers>, - //ev: TEvent, - ev: FsmEvent<::Events, ::Timers>, - inspect_event_ctx: &mut I) + ev: FsmEvent<::Events, ::Timers>, inspect_event_ctx: &mut I) -> FsmResult<()> where TFsm: FsmBackend, - ::States: AsMut, - //TSubMachine: FsmBackend + DerefMut> + FsmBackend, + ::States: AsMut, + ::Events: From<::Events>, + ::Timers: From<::Timers>, TSubMachine: FsmBackend + DerefMut>, Q: FsmEventQueue, I: Inspect, - ::Events: From<::Events>, - ::Timers: From<::Timers>, - T: FsmTimers, - //::Events: From - - //::Timers: From<::Timers>, - //::Timers: From<::Events> { let sub_fsm: &mut TSubMachine = ctx.backend.states.as_mut(); @@ -75,8 +66,6 @@ pub fn dispatch_to_submachine<'a, 'b, 'c, TFsm, TSubMachine, Q, I, T>(ctx: &mut queue: &mut queue_adapter, timers: &mut timers_adapter }; - - // todo: convert the event to sub - + ::dispatch_event(sub_dispatch_ctx, ev) } \ No newline at end of file diff --git a/finny/src/fsm/inspect.rs b/finny/src/fsm/inspect.rs index 53688a0..8f3ac52 100644 --- a/finny/src/fsm/inspect.rs +++ b/finny/src/fsm/inspect.rs @@ -1,5 +1,5 @@ use crate::lib::*; -use crate::{FsmBackend, FsmError, FsmEvent}; +use crate::{FsmBackend, FsmEvent}; pub trait Inspect { fn new_event(&self, event: &FsmEvent<::Events, ::Timers>) -> Self; diff --git a/finny/src/fsm/states.rs b/finny/src/fsm/states.rs index 0d53cb9..c2f8ea0 100644 --- a/finny/src/fsm/states.rs +++ b/finny/src/fsm/states.rs @@ -1,4 +1,4 @@ -use crate::{FsmBackend, FsmBackendImpl, lib::*}; +use crate::{FsmBackend, lib::*}; use crate::FsmResult; diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index b29a58c..6d18a6c 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -130,8 +130,6 @@ pub struct TimerSettings pub renew: bool } -//pub type TimerId = usize; - pub trait FsmTimers where F: FsmBackend { @@ -198,36 +196,4 @@ impl<'a, T, F, FSub> FsmTimers for FsmTimersSub<'a, T, F, FSub> // todo: not needed, split the trait None } -} - - - - -/* - -pub struct FsmEventQueueSub<'a, Q, F, FSub> - where - F: FsmBackend, - Q: FsmEventQueueSender -{ - pub parent: &'a mut Q, - pub _parent_fsm: PhantomData, - pub _sub_fsm: PhantomData -} - -impl<'a, Q, F, FSub> FsmEventQueue for FsmEventQueueSub<'a, Q, F, FSub> - where - F: FsmBackend, - Q: FsmEventQueueSender, - FSub: FsmBackend, - ::Events: From<::Events> -{ - fn dequeue(&mut self) -> Option<::Events> { - None - } - - fn len(&self) -> usize { - 0 - } -} -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 2ab024d..e52f0f2 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -30,11 +30,6 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream } }).collect(); - let sub_machine_states: Vec<_> = fsm.fsm.states.iter() - .filter(|(_, state)| { - if let FsmStateKind::SubMachine(_) = state.kind { true } else { false } - }).collect(); - let states_store = { let mut code_fields = TokenStream::new(); @@ -622,17 +617,14 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let sub_ty = FsmTypes::new(sub, &fsm.base.fsm_generics); let sub_variant = sub_ty.get_fsm_no_generics_ty(); - /* timer_dispatch.append_all(quote! { - (_, finny::FsmEvent::Timer( #sub_variant (timer_id))) { + (_, finny::FsmEvent::Timer( #timers_enum_ty :: #sub_variant (timer_id))) => { { - //let ev = finny::FsmEvent::Timer(*timer_id); - //return finny::dispatch_to_submachine::<_, #sub, _, _, _, _, _>(&mut ctx, &ev, &mut inspect_event_ctx); - todo!(" panic sub dispatch "); + let ev = finny::FsmEvent::Timer(*timer_id); + return finny::dispatch_to_submachine::<_, #sub, _, _, _>(&mut ctx, ev, &mut inspect_event_ctx); } - } + }, }); - */ } timer_dispatch diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index 0762e09..301c80b 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -31,9 +31,8 @@ pub struct EventEnter { shift: bool } #[finny_fsm] fn build_fsm(mut fsm: FsmBuilder) -> BuiltFsm { fsm.events_debug(); - fsm.initial_state::(); - //fsm.initial_states::<(StateA, BlinkerMachine)>(); - //fsm.sub_machine::(); + fsm.initial_states::<(StateA, BlinkerMachine)>(); + fsm.sub_machine::(); fsm.state::(); @@ -78,7 +77,6 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF fsm.build() } -/* #[derive(Default, Debug)] pub struct LightOn; #[derive(Default, Debug)] @@ -87,33 +85,40 @@ pub struct LightOff; pub struct BlinkingOn; #[derive(Default, Clone, Debug)] pub struct BlinkToggle; +#[derive(Default)] +pub struct BlinkerContext { + toggles: usize +} #[finny_fsm] -fn build_blinker_fsm(mut fsm: FsmBuilder) -> BuiltFsm { +fn build_blinker_fsm(mut fsm: FsmBuilder) -> BuiltFsm { fsm.events_debug(); fsm.initial_states::<(LightOff, BlinkingOn)>(); fsm.state::() .on_event::() - .transition_to::(); + .transition_to::() + .action(|_, ctx, _, _| { + ctx.toggles += 1; + }); fsm.state::() .on_event::() - .transition_to::(); + .transition_to::() + .action(|_, ctx, _, _| { + ctx.toggles += 1; + }); fsm.state::() .on_entry_start_timer(|ctx, settings| { settings.timeout = Duration::from_millis(50); settings.renew = true; }, |ctx, state| { - // todo: this breaks it! Some( BlinkToggle.into() ) - //None }); fsm.build() } -*/ @@ -148,5 +153,8 @@ fn test_timers_fsm() -> FsmResult<()> { assert_eq!(5, state_a.timers); assert_eq!(true, fsm.exit_a); + let sub_machine: &BlinkerMachine = fsm.get_state(); + assert_eq!(7, sub_machine.toggles); + Ok(()) } \ No newline at end of file From c72a066af2804e1259a8e5b1d718ad1ff6485725 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 26 Jan 2021 23:28:27 +0100 Subject: [PATCH 21/38] increased the durations for more stable CI builds --- finny_tests/tests/fsm_timers.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index 301c80b..5f41ed0 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -56,7 +56,7 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF fsm.state::() .on_entry_start_timer(|_ctx, timer| { - timer.timeout = Duration::from_millis(50); + timer.timeout = Duration::from_millis(100); timer.renew = true; timer.cancel_on_state_exit = true; }, |ctx, state| { @@ -65,7 +65,7 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF fsm.state::() .on_entry_start_timer(|_ctx, timer| { - timer.timeout = Duration::from_millis(100); + timer.timeout = Duration::from_millis(200); timer.renew = false; timer.cancel_on_state_exit = true; }, |ctx, state| { @@ -111,7 +111,7 @@ fn build_blinker_fsm(mut fsm: FsmBuilder) -> Bui fsm.state::() .on_entry_start_timer(|ctx, settings| { - settings.timeout = Duration::from_millis(50); + settings.timeout = Duration::from_millis(100); settings.renew = true; }, |ctx, state| { Some( BlinkToggle.into() ) @@ -135,7 +135,7 @@ fn test_timers_fsm() -> FsmResult<()> { fsm.start()?; - sleep(Duration::from_millis(225)); + sleep(Duration::from_millis(450)); fsm.dispatch_timer_events()?; @@ -143,7 +143,7 @@ fn test_timers_fsm() -> FsmResult<()> { assert_eq!(5, state_a.timers); fsm.dispatch(EventClick)?; - sleep(Duration::from_millis(100)); + sleep(Duration::from_millis(200)); fsm.dispatch_timer_events()?; @@ -154,7 +154,7 @@ fn test_timers_fsm() -> FsmResult<()> { assert_eq!(true, fsm.exit_a); let sub_machine: &BlinkerMachine = fsm.get_state(); - assert_eq!(7, sub_machine.toggles); + assert_eq!(6, sub_machine.toggles); Ok(()) } \ No newline at end of file From 6274dd7f94475c9fc9d46578477d7a8af195ffd2 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 26 Jan 2021 23:36:10 +0100 Subject: [PATCH 22/38] no_std fix --- finny_nostd_tests/src/main.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/finny_nostd_tests/src/main.rs b/finny_nostd_tests/src/main.rs index 86ee5a4..04591ec 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}; +use finny::{finny_fsm, FsmFactory, FsmEventQueueArray, InspectNull, FsmTimersNull}; use finny::decl::{FsmBuilder, BuiltFsm}; use heapless::consts::*; @@ -17,7 +17,8 @@ pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize { let ctx = StateMachineContext::default(); let queue = FsmEventQueueArray::<_, [_; 16]>::new(); let inspect = InspectNull::new(); - let mut fsm = StateMachine::new_with(ctx, queue, inspect).unwrap(); + let timers = FsmTimersNull; + let mut fsm = StateMachine::new_with(ctx, queue, inspect, timers).unwrap(); fsm.start().unwrap(); } From 6dcda0224b220f64974dc22878f53cf03404c55d Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Fri, 29 Jan 2021 21:25:53 +0100 Subject: [PATCH 23/38] include the FSM's generics in the timer structs. make the context mutable on setup for timers --- finny/src/decl/state.rs | 2 +- finny/src/fsm/timers.rs | 4 ++-- finny_derive/src/codegen.rs | 31 +++++++++++++++++++------------ 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/finny/src/decl/state.rs b/finny/src/decl/state.rs index 2e3d406..212dc39 100644 --- a/finny/src/decl/state.rs +++ b/finny/src/decl/state.rs @@ -35,7 +35,7 @@ impl FsmStateBuilder /// that returns an event to be enqueued in the FSM. pub fn on_entry_start_timer(&self, _setup: FSetup, _trigger: FTrigger) -> &Self where - FSetup: Fn(&TContext, &mut TimerFsmSettings), + FSetup: Fn(&mut TContext, &mut TimerFsmSettings), FTrigger: Fn(&TContext, &TState) -> Option< ::Events > { self diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index 6d18a6c..e84abd6 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -11,13 +11,13 @@ pub struct TimerInstance pub trait FsmTimer where F: FsmBackend, Self: Default { - fn setup(ctx: &::Context, settings: &mut TimerFsmSettings); + fn setup(ctx: &mut ::Context, settings: &mut TimerFsmSettings); fn trigger(ctx: &::Context, state: &S) -> Option< ::Events >; fn get_instance(&self) -> &Option>; fn get_instance_mut(&mut self) -> &mut Option>; - fn execute_on_enter>(&mut self, id: F::Timers, ctx: &::Context, inspect: &mut I, timers: &mut T) { + fn execute_on_enter>(&mut self, id: F::Timers, ctx: &mut ::Context, inspect: &mut I, timers: &mut T) { let log = inspect.for_timer::(id.clone()); let mut settings = TimerFsmSettings::default(); Self::setup(ctx, &mut settings); diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index e52f0f2..7ffc951 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -47,18 +47,18 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let timer_ty = timer.get_ty(&fsm.base); let timer_field = timer.get_field(&fsm.base); - code_fields.append_all(quote! { #timer_field: #timer_ty, }); + code_fields.append_all(quote! { #timer_field: #timer_ty #fsm_generics_type, }); new_state_fields.append_all(quote! { #timer_field: #timer_ty::default(), }); state_accessors.append_all(quote! { - impl #fsm_generics_impl core::convert::AsRef<#timer_ty> for #states_store_ty #fsm_generics_type #fsm_generics_where { - fn as_ref(&self) -> & #timer_ty { + impl #fsm_generics_impl core::convert::AsRef<#timer_ty #fsm_generics_type> for #states_store_ty #fsm_generics_type #fsm_generics_where { + fn as_ref(&self) -> & #timer_ty #fsm_generics_type { &self. #timer_field } } - impl #fsm_generics_impl core::convert::AsMut<#timer_ty> for #states_store_ty #fsm_generics_type #fsm_generics_where { - fn as_mut(&mut self) -> &mut #timer_ty { + impl #fsm_generics_impl core::convert::AsMut<#timer_ty #fsm_generics_type> for #states_store_ty #fsm_generics_type #fsm_generics_where { + fn as_mut(&mut self) -> &mut #timer_ty #fsm_generics_type { &mut self. #timer_field } } @@ -504,7 +504,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream timers_enter.append_all(quote! { { use finny::FsmTimer; - ctx.backend.states. #timer_field . execute_on_enter( #timers_enum_ty :: #timer_ty , &ctx.backend.context, &mut inspect_event_ctx, ctx.timers ); + ctx.backend.states. #timer_field . execute_on_enter( #timers_enum_ty :: #timer_ty , &mut ctx.backend.context, &mut inspect_event_ctx, ctx.timers ); } }); } @@ -602,8 +602,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream timer_dispatch.append_all(quote! { (_, finny::FsmEvent::Timer( timer_id @ #timers_enum_ty :: #timer_ty )) => { { - use crate::finny::FsmTimer; - < #timer_ty > :: execute_trigger(*timer_id, &mut ctx, &mut inspect_event_ctx); + use finny::FsmTimer; + < #timer_ty #fsm_generics_type > :: execute_trigger(*timer_id, &mut ctx, &mut inspect_event_ctx); } }, }); @@ -829,13 +829,20 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream code.append_all(quote! { - #[derive(Default)] - pub struct #timer_ty { + pub struct #timer_ty #fsm_generics_type #fsm_generics_where { instance: Option > } - impl #fsm_generics_impl finny::FsmTimer< #fsm_ty #fsm_generics_type , #state_ty > for #timer_ty #fsm_generics_where { - fn setup(ctx: & #ctx_ty, settings: &mut finny::TimerFsmSettings) { + impl #fsm_generics_impl core::default::Default for #timer_ty #fsm_generics_type #fsm_generics_where { + fn default() -> Self { + Self { + instance: None + } + } + } + + impl #fsm_generics_impl finny::FsmTimer< #fsm_ty #fsm_generics_type , #state_ty > for #timer_ty #fsm_generics_type #fsm_generics_where { + fn setup(ctx: &mut #ctx_ty, settings: &mut finny::TimerFsmSettings) { #setup { #setup_body From c55b8b6ea1e9592e5da2d1de2638702cb4800a5a Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sun, 31 Jan 2021 14:22:12 +0100 Subject: [PATCH 24/38] quality of life fixes --- finny/src/fsm/fsm_impl.rs | 40 ++++++++++++++++++++++++++----------- finny/src/fsm/inspect.rs | 10 +++++----- finny/src/fsm/timers.rs | 3 ++- finny/src/inspect/slog.rs | 14 ++++++++----- finny_derive/src/codegen.rs | 12 +++++++++-- 5 files changed, 54 insertions(+), 25 deletions(-) diff --git a/finny/src/fsm/fsm_impl.rs b/finny/src/fsm/fsm_impl.rs index 5448318..272190b 100644 --- a/finny/src/fsm/fsm_impl.rs +++ b/finny/src/fsm/fsm_impl.rs @@ -49,6 +49,13 @@ impl Deref for FsmBackendImpl { } } +impl DerefMut for FsmBackendImpl { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.context + } +} + + /// The frontend of a state machine which also includes environmental services like queues /// and inspection. The usual way to use the FSM. pub struct FsmFrontend @@ -79,12 +86,7 @@ impl FsmFrontend } } - while let Some(ev) = self.queue.dequeue() { - let ev: ::Events = ev.into(); - Self::dispatch(self, ev)?; - } - - Ok(()) + self.dispatch_queue() } /// Dispatch this event and run it to completition. @@ -95,12 +97,7 @@ impl FsmFrontend let ev = FsmEvent::Event(ev); Self::dispatch_single_event(self, ev)?; - while let Some(ev) = self.queue.dequeue() { - let ev: ::Events = ev.into(); - Self::dispatch(self, ev)?; - } - - Ok(()) + self.dispatch_queue() } /// Dispatch only this event, do not run it to completition. @@ -114,6 +111,17 @@ impl FsmFrontend F::dispatch_event(dispatch_ctx, event) } + + /// Dispatch the entire event queue and run it to completition. + pub fn dispatch_queue(&mut self) -> FsmResult<()> { + while let Some(ev) = self.queue.dequeue() { + let ev: ::Events = ev.into(); + // todo: log? + Self::dispatch(self, ev); + } + + Ok(()) + } } impl Deref for FsmFrontend @@ -124,4 +132,12 @@ impl Deref for FsmFrontend fn deref(&self) -> &Self::Target { &self.backend } +} + +impl DerefMut for FsmFrontend + where F: FsmBackend, Q: FsmEventQueue, I: Inspect, T: FsmTimers +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.backend + } } \ No newline at end of file diff --git a/finny/src/fsm/inspect.rs b/finny/src/fsm/inspect.rs index 8f3ac52..37ac64c 100644 --- a/finny/src/fsm/inspect.rs +++ b/finny/src/fsm/inspect.rs @@ -1,9 +1,9 @@ -use crate::lib::*; +use crate::{FsmBackendImpl, lib::*}; use crate::{FsmBackend, FsmEvent}; pub trait Inspect { - fn new_event(&self, event: &FsmEvent<::Events, ::Timers>) -> Self; - fn event_done(self); + 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; @@ -28,7 +28,7 @@ impl InspectNull { } impl Inspect for InspectNull { - fn new_event(&self, _event: &FsmEvent<::Events, ::Timers>) -> Self { + fn new_event(&self, _event: &FsmEvent<::Events, ::Timers>, _fsm: &FsmBackendImpl) -> Self { Self::default() } @@ -60,7 +60,7 @@ impl Inspect for InspectNull { } - fn event_done(self) { + fn event_done(self, fsm: &FsmBackendImpl) { } diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index e84abd6..fa41339 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -1,4 +1,4 @@ -use crate::{DispatchContext, FsmError, FsmEventQueue, Inspect, lib::*}; +use crate::{DispatchContext, FsmError, FsmEvent, FsmEventQueue, Inspect, lib::*}; use crate::{FsmBackend, FsmResult}; pub struct TimerInstance @@ -71,6 +71,7 @@ pub trait FsmTimer Some(_) => { match Self::trigger(&context.backend.context, context.backend.states.as_ref()) { Some(ev) => { + let inspect = inspect.new_event::(&FsmEvent::Event(ev.clone()), &context.backend); match context.queue.enqueue(ev) { Ok(_) => { inspect.info("The event triggered by the timer was enqueued."); diff --git a/finny/src/inspect/slog.rs b/finny/src/inspect/slog.rs index 3593996..beae764 100644 --- a/finny/src/inspect/slog.rs +++ b/finny/src/inspect/slog.rs @@ -4,7 +4,7 @@ use crate::lib::*; use AsRef; pub struct InspectSlog { - logger: slog::Logger + pub logger: slog::Logger } impl InspectSlog { @@ -17,12 +17,15 @@ impl InspectSlog { impl Inspect for InspectSlog { - fn new_event(&self, event: &FsmEvent<::Events, ::Timers>) -> Self { + fn new_event(&self, event: &FsmEvent<::Events, ::Timers>, fsm: &FsmBackendImpl) -> Self { let event_display = match event { FsmEvent::Timer(t) => format!("Fsm::Timer({:?})", t), _ => event.as_ref().to_string() }; - let kv = o!("event" => event_display); + + let current_state = format!("{:?}", fsm.get_current_states()); + + let kv = o!("event" => event_display, "start_state" => current_state); info!(self.logger, "Dispatching"; &kv); InspectSlog { logger: self.logger.new(kv) @@ -74,8 +77,9 @@ impl Inspect for InspectSlog info!(self.logger, "Executing {action}", action = action); } - fn event_done(self) { - info!(self.logger, "Dispatch done"); + fn event_done(self, fsm: &FsmBackendImpl) { + let states = format!("{:?}", fsm.get_current_states()); + info!(self.logger, "Dispatch done"; "stop_state" => states); } fn on_error(&self, msg: &str, error: &E) where E: Debug { diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 7ffc951..30ace71 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use proc_macro2::{Span, TokenStream}; use quote::{TokenStreamExt, quote}; use crate::{fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs}}; @@ -127,6 +129,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let mut transition_states = TokenStream::new(); + + let mut transitions_seen = HashSet::new(); for region in &fsm.fsm.regions { for transition in ®ion.transitions { match transition.ty { @@ -140,6 +144,10 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let state_from_field = &state_from.state_storage_field; let state_to_field = &state_to.state_storage_field; + let key = (state_from_ty.clone(), state_to_ty.clone()); + if transitions_seen.contains(&key) { continue; } + transitions_seen.insert(key); + transition_states.append_all(quote! { impl #fsm_generics_impl finny::FsmStateTransitionAsMut<#state_from_ty, #state_to_ty> for #states_store_ty #fsm_generics_type #fsm_generics_where { fn as_state_transition_mut(&mut self) -> (&mut #state_from_ty, &mut #state_to_ty) { @@ -677,7 +685,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let mut transition_misses = 0; - let mut inspect_event_ctx = ctx.inspect.new_event::(&event); + let mut inspect_event_ctx = ctx.inspect.new_event::(&event, &ctx.backend); #regions @@ -687,7 +695,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream Ok(()) }; - inspect_event_ctx.event_done(); + inspect_event_ctx.event_done(&ctx.backend); result } From aec8dec6fbcc036a15018ed307cd693c4675aef2 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sun, 31 Jan 2021 21:31:50 +0100 Subject: [PATCH 25/38] support for naming timers. suppressing the output of the input function, as otherwise we would need to add the generics to each of these generated and user-supplied types. --- finny/src/decl/state.rs | 19 +++++++++++++++++-- finny_derive/src/codegen.rs | 2 ++ finny_derive/src/parse.rs | 9 +++++++-- finny_derive/src/parse_fsm.rs | 30 +++++++++++++++++++++++------- 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/finny/src/decl/state.rs b/finny/src/decl/state.rs index 212dc39..2c3aa4e 100644 --- a/finny/src/decl/state.rs +++ b/finny/src/decl/state.rs @@ -33,11 +33,26 @@ impl FsmStateBuilder /// Start a new timer when entering this state. The timer should be unit struct with a implemented /// Default trait. The timer is setup within a closure and the trigger is another closure /// that returns an event to be enqueued in the FSM. - pub fn on_entry_start_timer(&self, _setup: FSetup, _trigger: FTrigger) -> &Self + pub fn on_entry_start_timer(&self, _setup: FSetup, _trigger: FTrigger) -> FsmStateTimerBuilder where FSetup: Fn(&mut TContext, &mut TimerFsmSettings), FTrigger: Fn(&TContext, &TState) -> Option< ::Events > { - self + FsmStateTimerBuilder { + _state: self + } } } + +pub struct FsmStateTimerBuilder<'a, TFsm, TContext, TState> { + _state: &'a FsmStateBuilder +} + +impl<'a, TFsm, TContext, TState> FsmStateTimerBuilder<'a, TFsm, TContext, TState> + where TFsm: FsmBackend +{ + /// Assign this type to the timer. The struct for it will be auto-generated. + pub fn with_timer_ty(self) { + + } +} \ No newline at end of file diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 30ace71..2778086 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -911,6 +911,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream #timers }; + /* // this goes in front of our definition function q.append_all(quote! { #[allow(dead_code)] @@ -918,6 +919,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream q.append_all(attr); q.append_all(input); + */ Ok(q.into()) } \ No newline at end of file diff --git a/finny_derive/src/parse.rs b/finny_derive/src/parse.rs index b9739b4..6ad5ace 100644 --- a/finny_derive/src/parse.rs +++ b/finny_derive/src/parse.rs @@ -249,12 +249,17 @@ pub struct FsmState { pub struct FsmTimer { pub id: usize, pub setup: syn::ExprClosure, - pub trigger: syn::ExprClosure + pub trigger: syn::ExprClosure, + pub type_hint: Option } impl FsmTimer { pub fn get_ty(&self, fsm: &FsmFnBase) -> syn::Type { - ty_append(&fsm.fsm_ty, &format!("Timer{}", self.id)) + if let Some(ref type_hint) = self.type_hint { + type_hint.clone() + } else { + ty_append(&fsm.fsm_ty, &format!("Timer{}", self.id)) + } } pub fn get_field(&self, fsm: &FsmFnBase) -> syn::Ident { diff --git a/finny_derive/src/parse_fsm.rs b/finny_derive/src/parse_fsm.rs index 3e8cf00..d16d5b2 100644 --- a/finny_derive/src/parse_fsm.rs +++ b/finny_derive/src/parse_fsm.rs @@ -290,8 +290,10 @@ impl FsmParser { timers: vec![] }); - for (i, method) in st.iter().enumerate() { + + let mut timer = None; + for (i, method) in st.iter().enumerate() { match method { MethodOverviewRef { name: "on_entry", .. } => { let closure = get_closure(&method.call)?; @@ -327,14 +329,15 @@ impl FsmParser { match call_args.as_slice() { [syn::Expr::Closure(ref setup), syn::Expr::Closure(ref trigger)] => { - let timer = FsmTimer { + if timer.is_some() { panic!("double timer bug!"); } + + timer = Some(FsmTimer { setup: setup.clone(), trigger: trigger.clone(), - id: self.timer_id - }; - - state.timers.push(timer); - + id: self.timer_id, + type_hint: None + }); + self.timer_id += 1; }, @@ -342,6 +345,15 @@ impl FsmParser { return Err(syn::Error::new(method.call.span(), "Unexpected arguments to the timer setup method.")); } } + }, + + MethodOverviewRef { name: "with_timer_ty", generics: [timer_ty], .. } => { + + if let Some(ref mut timer) = timer { + timer.type_hint = Some(timer_ty.clone()); + } else { + return Err(syn::Error::new(method.call.span(), "Missing the timer to apply the type to.")); + } }, @@ -349,6 +361,10 @@ impl FsmParser { } } + if let Some(timer) = timer { + state.timers.push(timer); + } + Ok(()) } } From e8e8ad33f6399b418670d0a36bf776571b02415c Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sun, 31 Jan 2021 23:10:46 +0100 Subject: [PATCH 26/38] support for naming transition types --- finny/src/decl/event.rs | 10 ++++++++++ finny_derive/src/parse.rs | 3 ++- finny_derive/src/parse_fsm.rs | 31 ++++++++++++++++++++++--------- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/finny/src/decl/event.rs b/finny/src/decl/event.rs index d635f4e..b5f317e 100644 --- a/finny/src/decl/event.rs +++ b/finny/src/decl/event.rs @@ -49,6 +49,11 @@ impl<'a, TFsm, TContext, TEvent, TState> FsmEventBuilderTransition<'a, TFsm, TCo pub fn guard>, &::States) -> bool>(&mut self, _guard: TGuard) -> &mut Self { self } + + /// A type for this transition. The struct for the transition will be generated. + pub fn with_transition_ty(&mut self) -> &mut Self { + self + } } @@ -69,4 +74,9 @@ impl<'a, TFsm, TContext, TEvent, TStateFrom, TStateTo> FsmEventBuilderTransition pub fn guard>, &::States) -> bool>(&mut self, _guard: TGuard) -> &mut Self { self } + + /// A type for this transition. The struct for the transition will be generated. + pub fn with_transition_ty(&mut self) -> &mut Self { + self + } } \ No newline at end of file diff --git a/finny_derive/src/parse.rs b/finny_derive/src/parse.rs index 6ad5ace..2b8b4bd 100644 --- a/finny_derive/src/parse.rs +++ b/finny_derive/src/parse.rs @@ -286,7 +286,8 @@ pub enum FsmEventTransition { #[derive(Default, Debug, Clone)] pub struct EventGuardAction{ pub guard: Option, - pub action: Option + pub action: Option, + pub type_hint: Option } impl FsmDeclarations { diff --git a/finny_derive/src/parse_fsm.rs b/finny_derive/src/parse_fsm.rs index d16d5b2..6e3ac03 100644 --- a/finny_derive/src/parse_fsm.rs +++ b/finny_derive/src/parse_fsm.rs @@ -139,7 +139,7 @@ impl FsmParser { } fn parse_event_guard_action(event_method_calls: &[MethodOverviewRef]) -> syn::Result { - let mut guard_action = EventGuardAction { guard: None, action: None}; + let mut guard_action = EventGuardAction { guard: None, action: None, type_hint: None }; for method in event_method_calls { match method { @@ -160,7 +160,16 @@ impl FsmParser { } guard_action.action = Some(closure.clone()); - } + }, + MethodOverviewRef { name: "with_transition_ty", generics: [transition_ty], ..} => { + + if guard_action.type_hint.is_some() { + return Err(syn::Error::new(method.call.span(), "Duplicate 'with_transition_ty'!")); + } + + guard_action.type_hint = Some(transition_ty.clone()); + + }, _ => { return Err(syn::Error::new(method.call.span(), "Unsupported method.")); } } } @@ -197,9 +206,13 @@ impl FsmParser { { let mut i = 0; - fn generate_transition_ty(base: &FsmFnBase, i: &mut usize) -> syn::Type { - *i = *i + 1; - crate::utils::ty_append(&base.fsm_ty, &format!("Transition{}", i)) + fn generate_transition_ty(base: &FsmFnBase, i: &mut usize, ty_hint: &Option) -> syn::Type { + if let Some(type_hint) = ty_hint { + type_hint.clone() + } else { + *i = *i + 1; + crate::utils::ty_append(&base.fsm_ty, &format!("Transition{}", i)) + } } // start transition @@ -207,7 +220,7 @@ impl FsmParser { let fsm_initial_state = self.states.get(&initial_state).ok_or(syn::Error::new(initial_state.span(), "The initial state is not refered in the builder. Use the 'state' method on the builder."))?; transitions.push(FsmTransition { - transition_ty: generate_transition_ty(&self.base, &mut i), + transition_ty: generate_transition_ty(&self.base, &mut i, &None), ty: FsmTransitionType::StateTransition(FsmStateTransition { action: EventGuardAction::default(), event: FsmTransitionEvent::Start, @@ -226,7 +239,7 @@ impl FsmParser { let to = self.states.get(to).ok_or(syn::Error::new(to.span(), "State not found."))?; transitions.push(FsmTransition { - transition_ty: generate_transition_ty(&self.base, &mut i), + transition_ty: generate_transition_ty(&self.base, &mut i, &action.type_hint), ty: FsmTransitionType::StateTransition(FsmStateTransition { action: action.clone(), state_from: FsmTransitionState::State(from.clone()), @@ -239,7 +252,7 @@ impl FsmParser { // todo: code duplication! let state = self.states.get(state).ok_or(syn::Error::new(state.span(), "State not found."))?; transitions.push(FsmTransition { - transition_ty: generate_transition_ty(&self.base, &mut i), + transition_ty: generate_transition_ty(&self.base, &mut i, &action.type_hint), ty: FsmTransitionType::InternalTransition(FsmStateAction { state: FsmTransitionState::State(state.clone()), action: action.clone(), @@ -251,7 +264,7 @@ impl FsmParser { // todo: code duplication! let state = self.states.get(state).ok_or(syn::Error::new(state.span(), "State not found."))?; transitions.push(FsmTransition { - transition_ty: generate_transition_ty(&self.base, &mut i), + transition_ty: generate_transition_ty(&self.base, &mut i, &action.type_hint), ty: FsmTransitionType::SelfTransition(FsmStateAction { state: FsmTransitionState::State(state.clone()), action: action.clone(), From 709e1914987e2629581bd482fb560c20abb91a0a Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Tue, 2 Feb 2021 23:48:03 +0100 Subject: [PATCH 27/38] basic info generation --- finny/src/fsm/mod.rs | 4 + finny_derive/Cargo.toml | 4 +- finny_derive/src/codegen.rs | 6 +- finny_derive/src/codegen_info.rs | 157 +++++++++++++++++++++++++++++++ finny_derive/src/info/mod.rs | 75 +++++++++++++++ finny_derive/src/lib.rs | 3 + finny_derive/src/parse.rs | 4 +- finny_tests/Cargo.toml | 3 +- finny_tests/tests/fsm_timers.rs | 2 +- 9 files changed, 253 insertions(+), 5 deletions(-) create mode 100644 finny_derive/src/codegen_info.rs create mode 100644 finny_derive/src/info/mod.rs diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 3f5eae5..b5c26ea 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -55,4 +55,8 @@ pub trait FsmBackend where Self: Sized { fn timer_count_self() -> usize; fn timer_count_submachines() -> usize; +} + +pub trait FsmInfoJson { + fn get_json_info() -> &'static str; } \ No newline at end of file diff --git a/finny_derive/Cargo.toml b/finny_derive/Cargo.toml index 6dfe724..a03189e 100644 --- a/finny_derive/Cargo.toml +++ b/finny_derive/Cargo.toml @@ -15,4 +15,6 @@ proc-macro = true quote = "1.0" syn = { version = "1.0", features = ["full", "extra-traits", "visit"] } proc-macro2 = "1.0" -petgraph = "0.5.1" \ No newline at end of file +petgraph = "0.5.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" \ No newline at end of file diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 2778086..96870f4 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::{fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs}}; +use crate::{codegen_info::generate_fsm_info, fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs}}; use crate::{parse::{FsmFnInput, FsmStateTransition, FsmTransitionState, FsmTransitionType}, utils::ty_append}; @@ -895,6 +895,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream code }; + let fsm_info = generate_fsm_info(&fsm); + let mut q = quote! { #states_store @@ -909,6 +911,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream #builder #timers + + #fsm_info }; /* diff --git a/finny_derive/src/codegen_info.rs b/finny_derive/src/codegen_info.rs new file mode 100644 index 0000000..5370b53 --- /dev/null +++ b/finny_derive/src/codegen_info.rs @@ -0,0 +1,157 @@ +use std::collections::HashMap; + +use proc_macro2::TokenStream; + +use crate::{ + info::{ + FinnyEvent, FinnyFsm, FinnyRegion, FinnyState, FinnyStateKind, FinnyTimer, FinnyTransition, + FinnyTransitionKind, FinnyTransitionNormal, + }, + parse::{FsmFnInput, FsmTransitionState}, + utils::tokens_to_string, +}; +use quote::quote; + +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), + timers: s + .timers + .iter() + .map(|t| FinnyTimer { + timer_id: tokens_to_string(&t.get_ty(&fsm.base)), + }) + .collect(), + }), + } +} + +fn to_info(fsm: &FsmFnInput) -> FinnyFsm { + let stopped_state = FinnyStateKind::Stopped; + + let finny_fsm = FinnyFsm { + fsm_id: tokens_to_string(&fsm.base.fsm_ty), + context_id: tokens_to_string(&fsm.base.context_ty), + regions: fsm + .fsm + .regions + .iter() + .enumerate() + .map(|(region_id, region)| { + ( + region_id, + FinnyRegion { + region_id, + states: region + .states + .iter() + .map(|state| { + let s = + to_info_state(&FsmTransitionState::State(state.clone()), fsm); + (s.get_state_id(), s) + }) + .chain(vec![(stopped_state.get_state_id(), stopped_state.clone())]) + .collect(), + transitions: region + .transitions + .iter() + .map(|transition| { + let transition_id = tokens_to_string(&transition.transition_ty); + + let (event, transition_ty) = match transition.ty { + crate::parse::FsmTransitionType::InternalTransition( + ref internal, + ) => ( + internal.event.clone(), + FinnyTransitionKind::InternalTransition, + ), + crate::parse::FsmTransitionType::SelfTransition( + ref self_transition, + ) => ( + self_transition.event.clone(), + FinnyTransitionKind::SelfTransition, + ), + crate::parse::FsmTransitionType::StateTransition(ref st) => ( + st.event.clone(), + FinnyTransitionKind::NormalTransition( + FinnyTransitionNormal { + from_state: to_info_state(&st.state_from, fsm) + .get_state_id(), + to_state: to_info_state(&st.state_to, fsm) + .get_state_id(), + }, + ), + ), + }; + + let event = match event { + crate::parse::FsmTransitionEvent::Stop => FinnyEvent::Stop, + crate::parse::FsmTransitionEvent::Start => FinnyEvent::Start, + crate::parse::FsmTransitionEvent::Event(ev) => { + FinnyEvent::Event(tokens_to_string(&ev.ty)) + } + }; + + ( + transition_id.clone(), + FinnyTransition { + transition_id, + event, + transition: transition_ty, + }, + ) + }) + .collect(), + }, + ) + }) + .collect(), + }; + + finny_fsm +} + +pub fn generate_fsm_info(fsm: &FsmFnInput) -> TokenStream { + let info = to_info(fsm); + + 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_info_ty = &fsm.base.fsm_info_ty; + let fsm_ty_name_str = 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 test_fn_name = + crate::utils::to_field_name(&crate::utils::ty_append(&fsm_ty, "html_docs_build")); + + let test_build = quote! { + #[test] + #[cfg(test)] + fn #test_fn_name () { + use std::io::prelude::*; + use std::fs; + use finny::FsmInfoJson; + + let json = <#fsm_info_ty>::get_json_info(); + + let mut f = fs::File::create(&format!("{}.json", #fsm_ty_name_str )).unwrap(); + f.write_all(json.as_bytes()).unwrap(); + } + }; + + quote! { + + #[derive(Default)] + pub struct #fsm_info_ty; + + impl finny::FsmInfoJson for #fsm_info_ty { + fn get_json_info() -> &'static str { + #json + } + } + + } +} diff --git a/finny_derive/src/info/mod.rs b/finny_derive/src/info/mod.rs new file mode 100644 index 0000000..dd6abb9 --- /dev/null +++ b/finny_derive/src/info/mod.rs @@ -0,0 +1,75 @@ +//! Structures that describe the FSM. Used by inspection frontends and documentation. + +use std::collections::HashMap; +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct FinnyFsm { + pub fsm_id: String, + pub context_id: String, + pub regions: HashMap +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct FinnyRegion { + pub region_id: usize, + pub states: HashMap, + pub transitions: HashMap +} + + + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum FinnyStateKind { + Stopped, + State(FinnyState), + //SubMachine(String) +} + +impl FinnyStateKind { + pub fn get_state_id(&self) -> String { + match self { + FinnyStateKind::Stopped => "Stopped".into(), + FinnyStateKind::State(s) => s.state_id.clone() + } + } +} + + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct FinnyState { + pub state_id: String, + pub timers: Vec +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct FinnyTransition { + pub transition_id: String, + pub event: FinnyEvent, + pub transition: FinnyTransitionKind +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum FinnyEvent { + Start, + Stop, + Event(String) +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub enum FinnyTransitionKind { + SelfTransition, + InternalTransition, + NormalTransition(FinnyTransitionNormal) +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct FinnyTransitionNormal { + pub from_state: String, + pub to_state: String +} + +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct FinnyTimer { + pub timer_id: String +} \ No newline at end of file diff --git a/finny_derive/src/lib.rs b/finny_derive/src/lib.rs index 57e05db..02ebc71 100644 --- a/finny_derive/src/lib.rs +++ b/finny_derive/src/lib.rs @@ -9,6 +9,7 @@ use parse::FsmFnInput; use proc_macro::TokenStream; mod codegen; +mod codegen_info; mod parse; mod parse_blocks; mod parse_fsm; @@ -16,6 +17,8 @@ mod utils; mod validation; mod fsm; +mod info; + #[proc_macro_attribute] pub fn finny_fsm(attr: TokenStream, item: TokenStream) -> TokenStream { let attr2: proc_macro2::TokenStream = attr.into(); diff --git a/finny_derive/src/parse.rs b/finny_derive/src/parse.rs index 2b8b4bd..f3b2a69 100644 --- a/finny_derive/src/parse.rs +++ b/finny_derive/src/parse.rs @@ -15,6 +15,7 @@ pub struct FsmFnInput { pub struct FsmFnBase { pub context_ty: syn::Type, pub fsm_ty: syn::Type, + pub fsm_info_ty: syn::Type, pub builder_ident: proc_macro2::Ident, pub fsm_generics: syn::Generics } @@ -109,7 +110,8 @@ impl FsmFnInput { let base = FsmFnBase { builder_ident, context_ty, - fsm_ty, + fsm_info_ty: crate::utils::ty_append(&fsm_ty, "Info"), + fsm_ty, fsm_generics: input_fn.sig.generics.clone() }; diff --git a/finny_tests/Cargo.toml b/finny_tests/Cargo.toml index dfe184c..8a994ef 100644 --- a/finny_tests/Cargo.toml +++ b/finny_tests/Cargo.toml @@ -8,4 +8,5 @@ edition = "2018" [dependencies] finny = { path = "../finny/" } slog = "2.7.0" -slog-term = "2.6.0" \ No newline at end of file +slog-term = "2.6.0" +slog-async = "2.6.0" \ No newline at end of file diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index 5f41ed0..5f85260 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -126,7 +126,7 @@ fn build_blinker_fsm(mut fsm: FsmBuilder) -> Bui fn test_timers_fsm() -> FsmResult<()> { let decorator = slog_term::TermDecorator::new().build(); let drain = slog_term::CompactFormat::new(decorator).build().fuse(); - let drain = std::sync::Mutex::new(drain).fuse(); + let drain = slog_async::Async::new(drain).build().fuse(); let logger = slog::Logger::root(drain, o!()); let ctx = TimersMachineContext { exit_a: false }; From 01ae6104dec1d880dbfd680b7cfe2a07be8bbc60 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Wed, 3 Feb 2021 23:38:01 +0100 Subject: [PATCH 28/38] plantuml experiment --- finny/Cargo.toml | 3 +- finny/src/fsm/mod.rs | 4 -- finny_derive/Cargo.toml | 4 ++ finny_derive/src/codegen.rs | 6 +- .../src/{codegen_info.rs => codegen_meta.rs} | 63 ++++++++++++------- finny_derive/src/lib.rs | 4 +- finny_derive/src/{info => meta}/mod.rs | 6 +- finny_derive/src/meta/plantuml.rs | 56 +++++++++++++++++ 8 files changed, 111 insertions(+), 35 deletions(-) rename finny_derive/src/{codegen_info.rs => codegen_meta.rs} (79%) rename finny_derive/src/{info => meta}/mod.rs (94%) create mode 100644 finny_derive/src/meta/plantuml.rs diff --git a/finny/Cargo.toml b/finny/Cargo.toml index 0d37303..c098aa1 100644 --- a/finny/Cargo.toml +++ b/finny/Cargo.toml @@ -21,4 +21,5 @@ slog = { version = "2.7.0", optional = true } default = ["std", "inspect_slog", "timers_std"] std = ["arraydeque/std"] inspect_slog = ["slog"] -timers_std = [] \ No newline at end of file +timers_std = [] +generate_plantuml = ["finny_derive/generate_plantuml"] \ No newline at end of file diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index b5c26ea..3f5eae5 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -55,8 +55,4 @@ pub trait FsmBackend where Self: Sized { fn timer_count_self() -> usize; fn timer_count_submachines() -> usize; -} - -pub trait FsmInfoJson { - fn get_json_info() -> &'static str; } \ No newline at end of file diff --git a/finny_derive/Cargo.toml b/finny_derive/Cargo.toml index a03189e..81e8929 100644 --- a/finny_derive/Cargo.toml +++ b/finny_derive/Cargo.toml @@ -11,6 +11,10 @@ keywords = ["fsm", "state"] [lib] proc-macro = true +[features] +default = [] +generate_plantuml = [] + [dependencies] quote = "1.0" syn = { version = "1.0", features = ["full", "extra-traits", "visit"] } diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 96870f4..ec838dd 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_info::generate_fsm_info, fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs}}; +use crate::{codegen_meta::generate_fsm_meta, fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs}}; use crate::{parse::{FsmFnInput, FsmStateTransition, FsmTransitionState, FsmTransitionType}, utils::ty_append}; @@ -895,7 +895,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream code }; - let fsm_info = generate_fsm_info(&fsm); + let fsm_meta = generate_fsm_meta(&fsm); let mut q = quote! { #states_store @@ -912,7 +912,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream #timers - #fsm_info + #fsm_meta }; /* diff --git a/finny_derive/src/codegen_info.rs b/finny_derive/src/codegen_meta.rs similarity index 79% rename from finny_derive/src/codegen_info.rs rename to finny_derive/src/codegen_meta.rs index 5370b53..1106e65 100644 --- a/finny_derive/src/codegen_info.rs +++ b/finny_derive/src/codegen_meta.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use proc_macro2::TokenStream; use crate::{ - info::{ + meta::{ FinnyEvent, FinnyFsm, FinnyRegion, FinnyState, FinnyStateKind, FinnyTimer, FinnyTransition, FinnyTransitionKind, FinnyTransitionNormal, }, @@ -65,13 +65,13 @@ fn to_info(fsm: &FsmFnInput) -> FinnyFsm { ref internal, ) => ( internal.event.clone(), - FinnyTransitionKind::InternalTransition, + FinnyTransitionKind::InternalTransition { state_id: to_info_state(&internal.state, fsm).get_state_id() }, ), crate::parse::FsmTransitionType::SelfTransition( ref self_transition, ) => ( self_transition.event.clone(), - FinnyTransitionKind::SelfTransition, + FinnyTransitionKind::SelfTransition { state_id: to_info_state(&self_transition.state, fsm).get_state_id() }, ), crate::parse::FsmTransitionType::StateTransition(ref st) => ( st.event.clone(), @@ -113,10 +113,10 @@ fn to_info(fsm: &FsmFnInput) -> FinnyFsm { finny_fsm } -pub fn generate_fsm_info(fsm: &FsmFnInput) -> TokenStream { +pub fn generate_fsm_meta(fsm: &FsmFnInput) -> TokenStream { let info = to_info(fsm); - let json = serde_json::to_string_pretty(&info).expect("Failed to serialize the FSM info JSON!"); + //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_info_ty = &fsm.base.fsm_info_ty; @@ -124,34 +124,51 @@ pub fn generate_fsm_info(fsm: &FsmFnInput) -> TokenStream { let (fsm_generics_impl, fsm_generics_type, fsm_generics_where) = fsm.base.fsm_generics.split_for_impl(); - let test_fn_name = - crate::utils::to_field_name(&crate::utils::ty_append(&fsm_ty, "html_docs_build")); - - let test_build = quote! { - #[test] - #[cfg(test)] - fn #test_fn_name () { - use std::io::prelude::*; - use std::fs; - use finny::FsmInfoJson; + 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 json = <#fsm_info_ty>::get_json_info(); - let mut f = fs::File::create(&format!("{}.json", #fsm_ty_name_str )).unwrap(); - f.write_all(json.as_bytes()).unwrap(); - } - }; + /* quote! { #[derive(Default)] - pub struct #fsm_info_ty; + pub struct #fsm_meta_ty; - impl finny::FsmInfoJson for #fsm_info_ty { - fn get_json_info() -> &'static str { + impl finny::FsmMetaJson for #fsm_info_ty { + fn get_json_meta() -> &'static str { #json } + + fn get_plantuml_meta() -> &'static str { + + } } } + */ + + quote! { + #plant_uml_test_build + } } diff --git a/finny_derive/src/lib.rs b/finny_derive/src/lib.rs index 02ebc71..ea28883 100644 --- a/finny_derive/src/lib.rs +++ b/finny_derive/src/lib.rs @@ -9,7 +9,8 @@ use parse::FsmFnInput; use proc_macro::TokenStream; mod codegen; -mod codegen_info; +mod codegen_meta; +mod meta; mod parse; mod parse_blocks; mod parse_fsm; @@ -17,7 +18,6 @@ mod utils; mod validation; mod fsm; -mod info; #[proc_macro_attribute] pub fn finny_fsm(attr: TokenStream, item: TokenStream) -> TokenStream { diff --git a/finny_derive/src/info/mod.rs b/finny_derive/src/meta/mod.rs similarity index 94% rename from finny_derive/src/info/mod.rs rename to finny_derive/src/meta/mod.rs index dd6abb9..cc552b5 100644 --- a/finny_derive/src/info/mod.rs +++ b/finny_derive/src/meta/mod.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; use serde::{Serialize, Deserialize}; +pub mod plantuml; + #[derive(Serialize, Deserialize, Clone, Debug)] pub struct FinnyFsm { pub fsm_id: String, @@ -58,8 +60,8 @@ pub enum FinnyEvent { #[derive(Serialize, Deserialize, Clone, Debug)] pub enum FinnyTransitionKind { - SelfTransition, - InternalTransition, + SelfTransition { state_id: String }, + InternalTransition { state_id: String }, NormalTransition(FinnyTransitionNormal) } diff --git a/finny_derive/src/meta/plantuml.rs b/finny_derive/src/meta/plantuml.rs new file mode 100644 index 0000000..63a0c83 --- /dev/null +++ b/finny_derive/src/meta/plantuml.rs @@ -0,0 +1,56 @@ +use super::FinnyFsm; +use std::fmt::Write; + + +pub fn to_plant_uml(fsm: &FinnyFsm) -> String { + let mut output = String::new(); + + output.push_str("@startuml\n"); + + for region in fsm.regions.values() { + for state in region.states.values() { + match state { + super::FinnyStateKind::Stopped => { + + } + super::FinnyStateKind::State(state) => { + + writeln!(&mut output, "state {} {{", state.state_id); + + + writeln!(&mut output, "}}"); + } + } + } + + for transition in region.transitions.values() { + + let event = match transition.event { + super::FinnyEvent::Start => "Start".to_string(), + super::FinnyEvent::Stop => "Stop".to_string(), + super::FinnyEvent::Event(ref ev) => ev.clone() + }; + + match &transition.transition { + super::FinnyTransitionKind::SelfTransition { state_id } => { + 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); + } + super::FinnyTransitionKind::NormalTransition(t) => { + let state_from = match t.from_state.as_str() { + "Stopped" => "[*]", + _ => &t.from_state + }; + + 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 +} \ No newline at end of file From fe6d7d54f989d7f4b494a4bc4eb2e3bf6dc321f6 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sun, 7 Feb 2021 00:34:34 +0100 Subject: [PATCH 29/38] cleanup --- finny/src/fsm/mod.rs | 3 --- finny/src/fsm/tests_fsm.rs | 8 -------- finny_derive/src/codegen.rs | 31 +------------------------------ 3 files changed, 1 insertion(+), 41 deletions(-) diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 3f5eae5..4cdd6b7 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -52,7 +52,4 @@ pub trait FsmBackend where Self: Sized { fn dispatch_event(ctx: DispatchContext, event: FsmEvent) -> FsmDispatchResult where Q: FsmEventQueue, I: Inspect, T: FsmTimers; - - fn timer_count_self() -> usize; - fn timer_count_submachines() -> usize; } \ No newline at end of file diff --git a/finny/src/fsm/tests_fsm.rs b/finny/src/fsm/tests_fsm.rs index 9019238..14226ab 100644 --- a/finny/src/fsm/tests_fsm.rs +++ b/finny/src/fsm/tests_fsm.rs @@ -53,12 +53,4 @@ impl FsmBackend for TestFsm { { todo!() } - - fn timer_count_self() -> usize { - 0 - } - - fn timer_count_submachines() -> usize { - 0 - } } \ No newline at end of file diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index ec838dd..5b0f3ef 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -6,7 +6,7 @@ use crate::{codegen_meta::generate_fsm_meta, fsm::FsmTypes, parse::{FsmState, Fs use crate::{parse::{FsmFnInput, FsmStateTransition, FsmTransitionState, FsmTransitionType}, utils::ty_append}; -pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream) -> syn::Result { +pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStream) -> syn::Result { let fsm_ty = &fsm.base.fsm_ty; let fsm_types = FsmTypes::new(&fsm.base.fsm_ty, &fsm.base.fsm_generics); //let fsm_mod = to_field_name(&ty_append(fsm_ty, "Finny"))?; @@ -21,17 +21,6 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream let (fsm_generics_impl, fsm_generics_type, fsm_generics_where) = fsm.base.fsm_generics.split_for_impl(); - let timers_count: usize = fsm.fsm.states.iter().map(|s| s.1.timers.len()).sum(); - let sub_machines: Vec<_> = fsm.fsm.states.iter() - .filter_map(|(_, state)| { - match state.kind { - FsmStateKind::Normal => None, - FsmStateKind::SubMachine(_) => { - Some(&state.ty) - } - } - }).collect(); - let states_store = { let mut code_fields = TokenStream::new(); @@ -657,16 +646,6 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream }); } - let fn_timer_count_submachines = { - let mut q = quote! { 0 }; - if sub_machines.len() > 0 { - q.append_all(quote! { + }); - q.append_separated(sub_machines.iter().map(|s| quote! { ( <#s>::timer_count_self() + <#s>::timer_count_submachines() ) }), quote! { + }); - } - - q - }; - quote! { impl #fsm_generics_impl finny::FsmBackend for #fsm_ty #fsm_generics_type @@ -699,14 +678,6 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, attr: TokenStream, input: TokenStream result } - - fn timer_count_self() -> usize { - #timers_count - } - - fn timer_count_submachines() -> usize { - #fn_timer_count_submachines - } } } }; From c3501ab0090ff8768470be9bff5f442849523870 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sun, 7 Feb 2021 16:40:57 +0100 Subject: [PATCH 30/38] iterators for variants --- finny/src/fsm/mod.rs | 10 +++- finny/src/fsm/tests_fsm.rs | 10 +++- finny/src/fsm/timers.rs | 19 +++++++ finny/src/timers/std.rs | 52 +++++++++++------- finny_derive/src/codegen.rs | 94 +++++++++++++++++++++++++++++++-- finny_derive/src/fsm.rs | 14 +++-- finny_tests/tests/fsm_timers.rs | 20 +++++-- 7 files changed, 185 insertions(+), 34 deletions(-) diff --git a/finny/src/fsm/mod.rs b/finny/src/fsm/mod.rs index 4cdd6b7..0583137 100644 --- a/finny/src/fsm/mod.rs +++ b/finny/src/fsm/mod.rs @@ -48,8 +48,16 @@ pub trait FsmBackend where Self: Sized { /// the dispatch into sub-machines and into multiple regions. type Events: AsRef + Clone; /// An enum with variants for all the possible timer instances, with support for submachines. - type Timers: Debug + Clone + PartialEq; + type Timers: Debug + Clone + PartialEq + AllVariants; fn dispatch_event(ctx: DispatchContext, event: FsmEvent) -> FsmDispatchResult where Q: FsmEventQueue, I: Inspect, T: FsmTimers; +} + +/// Enumerates all the possible variants of a simple enum. +pub trait AllVariants where Self: Sized +{ + type Iter: Iterator; + + fn iter() -> Self::Iter; } \ No newline at end of file diff --git a/finny/src/fsm/tests_fsm.rs b/finny/src/fsm/tests_fsm.rs index 14226ab..2f7200c 100644 --- a/finny/src/fsm/tests_fsm.rs +++ b/finny/src/fsm/tests_fsm.rs @@ -1,6 +1,6 @@ //! A minimal, internal FSM for unit tests, manually written. -use crate::{FsmBackend, FsmCurrentState, FsmStates}; +use crate::{AllVariants, FsmBackend, FsmCurrentState, FsmStates}; use derive_more::From; #[derive(Default)] @@ -40,6 +40,14 @@ pub enum FsmBackendTimers { } +impl AllVariants for FsmBackendTimers { + type Iter = std::iter::Once; + + fn iter() -> Self::Iter { + todo!() + } +} + impl FsmBackend for TestFsm { type Context = (); diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index fa41339..b470fc5 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -1,6 +1,25 @@ use crate::{DispatchContext, FsmError, FsmEvent, FsmEventQueue, Inspect, lib::*}; use crate::{FsmBackend, FsmResult}; +/// Associate some data with a specific timer ID. +pub trait TimersStorage<'a, F, T> : Default + where F: FsmBackend, T: 'a +{ + type IterMut: TimersStorageIter<'a, F, T>; + + fn get_timer_storage_mut(&mut self, id: &::Timers) -> &mut Option; + fn iter_mut(&mut self) -> Self::IterMut; +} + +pub trait TimersStorageIter<'a, F, T> :Iterator::Timers, &'a mut Option)> + where F: FsmBackend, T: 'a +{ + +} + + + + pub struct TimerInstance where F: FsmBackend { diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs index e753429..3e83502 100644 --- a/finny/src/timers/std.rs +++ b/finny/src/timers/std.rs @@ -1,48 +1,56 @@ -use std::{time::{Duration, Instant}}; -use crate::{FsmBackend, FsmTimers}; +//! A naive timers implementation based on standard libraries' `Instant` time provider. -pub struct TimersStd +use std::{marker::PhantomData, time::{Duration, Instant}}; +use crate::{FsmBackend, FsmTimers, TimersStorage}; + +pub struct TimersStd where F: FsmBackend { - timers: Vec<(::Timers, StdTimer)>, + //timers: Vec<(::Timers, StdTimer)>, + timers: S, pending_intervals: Option<(::Timers, usize)> } #[derive(Debug)] -enum StdTimer { +pub enum StdTimer { Timeout { started_at: Instant, duration: Duration }, Interval { started_at: Instant, interval: Duration } } -impl TimersStd - where F: FsmBackend +impl<'a, F, S> TimersStd + where F: FsmBackend, + S: TimersStorage<'a, F, StdTimer> { pub fn new() -> Self { Self { - timers: vec![], + timers: S::default(), pending_intervals: None } } } -impl FsmTimers for TimersStd - where F: FsmBackend +impl<'a, F, S> FsmTimers for TimersStd + where F: FsmBackend, + S: TimersStorage<'a, F, StdTimer> { fn create(&mut self, id: ::Timers, settings: &crate::TimerSettings) -> crate::FsmResult<()> { // try to cancel any existing ones self.cancel(id.clone())?; + let t = self.timers.get_timer_storage_mut(&id); + if settings.renew { - self.timers.push((id, StdTimer::Interval { started_at: Instant::now(), interval: settings.timeout })); + *t = Some(StdTimer::Interval { started_at: Instant::now(), interval: settings.timeout }); } else { - self.timers.push((id, StdTimer::Timeout { started_at: Instant::now(), duration: settings.timeout })); + *t = Some(StdTimer::Timeout { started_at: Instant::now(), duration: settings.timeout }); } Ok(()) } fn cancel(&mut self, id: ::Timers) -> crate::FsmResult<()> { - self.timers.retain(|(timer_id, _)| *timer_id != id); + let t = self.timers.get_timer_storage_mut(&id); + *t = None; Ok(()) } @@ -56,15 +64,18 @@ impl FsmTimers for TimersStd return Some(id); } - let mut timed_out_idx = None; + let mut timed_out_id = None; let now = Instant::now(); - for (idx, (timer_id, timer)) in self.timers.iter_mut().enumerate() { + + //let iter = self.timers.iter(); + + for (timer_id, timer) in self.timers.iter_mut() { match timer { - StdTimer::Timeout { started_at, duration } if now.duration_since(*started_at) >= *duration => { - timed_out_idx = Some(idx); + Some(StdTimer::Timeout { started_at, duration }) if now.duration_since(*started_at) >= *duration => { + timed_out_id = Some(timer_id); break; }, - StdTimer::Interval { ref mut started_at, interval } if now.duration_since(*started_at) >= *interval => { + Some(StdTimer::Interval { ref mut started_at, interval }) if now.duration_since(*started_at) >= *interval => { let t = now.duration_since(*started_at); let times = ((t.as_secs_f32() / interval.as_secs_f32()).floor() as usize) - 1; if times > 0 { @@ -77,8 +88,9 @@ impl FsmTimers for TimersStd } } - if let Some(idx) = timed_out_idx { - let (id, _) = self.timers.remove(idx); + if let Some(id) = timed_out_id { + let timer = self.timers.get_timer_storage_mut(&id); + *timer = None; return Some(id); } diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 5b0f3ef..6f73e3d 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}}; +use crate::{codegen_meta::generate_fsm_meta, fsm::FsmTypes, parse::{FsmState, FsmStateAction, FsmStateKind}, utils::{remap_closure_inputs, to_field_name}}; use crate::{parse::{FsmFnInput, FsmStateTransition, FsmTransitionState, FsmTransitionType}, utils::ty_append}; @@ -14,7 +14,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let states_store_ty = ty_append(&fsm.base.fsm_ty, "States"); let states_enum_ty = ty_append(&fsm.base.fsm_ty, "CurrentState"); - let timers_enum_ty = ty_append(&fsm.base.fsm_ty, "Timers"); + let timers_enum_ty = fsm_types.get_fsm_timers_ty(); + let timers_enum_iter_ty = fsm_types.get_fsm_timers_iter_ty(); let event_enum_ty = fsm_types.get_fsm_events_ty(); let region_count = fsm.fsm.regions.len(); @@ -169,7 +170,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre Ok(s) } } - + #[derive(Copy, Clone, Debug, PartialEq)] pub enum #states_enum_ty { #state_variants @@ -773,6 +774,8 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let mut code = TokenStream::new(); let mut enum_variants = vec![]; + let mut submachines = vec![]; + let mut timers = vec![]; let states = fsm.fsm.states.iter().map(|s| s.1); for state in states { @@ -782,6 +785,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let n = sub_fsm_ty.get_fsm_no_generics_ty(); let t = sub_fsm_ty.get_fsm_timers_ty(); enum_variants.push(quote! { #n ( #t ) }); + submachines.push(sub_fsm_ty.clone()); code.append_all(quote! { @@ -799,6 +803,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let timer_ty = timer.get_ty(&fsm.base); enum_variants.push(quote! { #timer_ty }); + timers.push(timer_ty.clone()); let setup = remap_closure_inputs(&timer.setup.inputs, &[quote! { ctx }, quote! { settings }])?; let setup_body = &timer.setup.body; @@ -852,7 +857,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let variants = { let mut t = TokenStream::new(); - t.append_separated(enum_variants, quote! { , }); + t.append_separated(&enum_variants, quote! { , }); t }; @@ -863,6 +868,87 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre } }); + let submachine_iters: Vec<_> = submachines.iter().map(|s| { + let ty = s.get_fsm_timers_iter_ty(); + let field = to_field_name(&ty); + (ty, field) + }).collect(); + + let enum_iter_matches_variants = timers.iter().enumerate().map(|(i, variant)| quote! { + #i => { self.position += 1; Some(#timers_enum_ty :: #variant) } + }); + let mut enum_iter_matches = TokenStream::new(); + enum_iter_matches.append_separated(enum_iter_matches_variants, quote! { , }); + enum_iter_matches.append_separated(submachine_iters.iter().enumerate().map(|(i, (ty, field))| { + let i = timers.len() + i; + quote! { + #i if self.#field.is_some() => { + if let Some(ref mut iter) = self.#field { + let r = iter.next(); + if let Some(r) = r { + let r = r.into(); + return Some(r); + } else { + self.#field = None; + self.position += 1; + return self.next(); + } + } else { None } + } + } + }), quote! { , }); + + let mut submachine_iter_struct = TokenStream::new(); + submachine_iter_struct.append_separated(submachine_iters.iter().map(|(ty, field)| quote! { + #field : Option< #ty > + }), quote! { , }); + + let mut submachine_iter_new = TokenStream::new(); + submachine_iter_new.append_separated(submachine_iters.iter().map(|(ty, field)| quote! { + #field : Some ( <#ty> :: new() ) + }), quote! { , }); + + // timers iterator + code.append_all(quote! { + impl finny::AllVariants for #timers_enum_ty { + type Iter = #timers_enum_iter_ty; + + fn iter() -> #timers_enum_iter_ty { + #timers_enum_iter_ty::new() + } + } + + pub struct #timers_enum_iter_ty { + position: usize, + #submachine_iter_struct + } + + impl #timers_enum_iter_ty { + pub fn new() -> Self { + Self { + position: 0, + #submachine_iter_new + } + } + } + + impl core::iter::Iterator for #timers_enum_iter_ty { + type Item = #timers_enum_ty; + + fn next(&mut self) -> Option { + match self.position { + #enum_iter_matches + _ => None + } + } + } + }); + + // timers storage + + + + code }; diff --git a/finny_derive/src/fsm.rs b/finny_derive/src/fsm.rs index 7c07b31..93d7bfe 100644 --- a/finny_derive/src/fsm.rs +++ b/finny_derive/src/fsm.rs @@ -1,8 +1,6 @@ -use syn::{WhereClause, spanned::Spanned}; - -use crate::utils::{get_ty_ident, strip_generics, ty_append}; - +use crate::utils::{strip_generics, ty_append}; +#[derive(Clone)] pub struct FsmTypes { fsm: syn::Type, fsm_no_generics: syn::Type, @@ -33,4 +31,12 @@ impl FsmTypes { pub fn get_fsm_timers_ty(&self) -> syn::Type { ty_append(&self.fsm_no_generics, "Timers") } + + pub fn get_fsm_timers_iter_ty(&self) -> syn::Type { + ty_append(&self.fsm_no_generics, "TimersIter") + } + + pub fn get_fsm_timers_storage_ty(&self) -> syn::Type { + ty_append(&self.fsm_no_generics, "TimersStorage") + } } \ No newline at end of file diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index 5f85260..f962cb0 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -2,7 +2,7 @@ extern crate finny; use std::{thread::{sleep, sleep_ms}, time::Duration}; -use finny::{FsmCurrentState, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect::slog::InspectSlog, timers::std::{TimersStd}}; +use finny::{FsmCurrentState, FsmEvent, FsmEventQueueVec, FsmFactory, FsmResult, decl::{BuiltFsm, FsmBuilder}, finny_fsm, inspect::slog::InspectSlog, timers::std::{TimersStd}, AllVariants}; use slog::{Drain, Logger, info, o}; #[derive(Debug)] @@ -61,7 +61,8 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF timer.cancel_on_state_exit = true; }, |ctx, state| { Some( EventTimer {n: 0}.into() ) - }); + }) + .with_timer_ty::(); fsm.state::() .on_entry_start_timer(|_ctx, timer| { @@ -70,7 +71,8 @@ fn build_fsm(mut fsm: FsmBuilder) -> BuiltF timer.cancel_on_state_exit = true; }, |ctx, state| { Some( EventTimer {n: 1}.into() ) - }); + }) + .with_timer_ty::(); fsm.state::(); @@ -115,7 +117,8 @@ fn build_blinker_fsm(mut fsm: FsmBuilder) -> Bui settings.renew = true; }, |ctx, state| { Some( BlinkToggle.into() ) - }); + }) + .with_timer_ty::(); fsm.build() } @@ -131,6 +134,14 @@ fn test_timers_fsm() -> FsmResult<()> { let ctx = TimersMachineContext { exit_a: false }; + + let sub_timers_variants: Vec<_> = BlinkerMachineTimers::iter().collect(); + assert_eq!(&[BlinkerMachineTimers::BlinkingTimer], sub_timers_variants.as_slice()); + + let timers_variants: Vec<_> = TimersMachineTimers::iter().collect(); + assert_eq!(&[TimersMachineTimers::Timer1, TimersMachineTimers::Timer2, TimersMachineTimers::BlinkerMachine(BlinkerMachineTimers::BlinkingTimer)], timers_variants.as_slice()); + + /* let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), TimersStd::new())?; fsm.start()?; @@ -155,6 +166,7 @@ fn test_timers_fsm() -> FsmResult<()> { let sub_machine: &BlinkerMachine = fsm.get_state(); assert_eq!(6, sub_machine.toggles); + */ Ok(()) } \ No newline at end of file From 070dced57c67591669e7c3688aea1656306c3818 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sun, 7 Feb 2021 21:35:06 +0100 Subject: [PATCH 31/38] problems with impl generics for fsm + custom --- finny/src/fsm/timers.rs | 42 +++++++++++++++++++++++--- finny/src/timers/mod.rs | 4 ++- finny/src/timers/std.rs | 3 ++ finny_derive/src/codegen.rs | 60 +++++++++++++++++++++++++++++++++---- 4 files changed, 99 insertions(+), 10 deletions(-) diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index b470fc5..54068b9 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -1,21 +1,55 @@ -use crate::{DispatchContext, FsmError, FsmEvent, FsmEventQueue, Inspect, lib::*}; +use crate::{AllVariants, DispatchContext, FsmError, FsmEvent, FsmEventQueue, Inspect, lib::*}; use crate::{FsmBackend, FsmResult}; /// Associate some data with a specific timer ID. pub trait TimersStorage<'a, F, T> : Default where F: FsmBackend, T: 'a { - type IterMut: TimersStorageIter<'a, F, T>; + //type IterMut: TimersStorageIter<'a, F, T>; - fn get_timer_storage_mut(&mut self, id: &::Timers) -> &mut Option; - fn iter_mut(&mut self) -> Self::IterMut; + fn get_timer_storage_mut(&mut self, id: &::Timers) -> &'a mut Option; + + fn iter_mut(&'a mut self) -> TimersStorageIterMut<'a, Self, F, T> { + TimersStorageIterMut { + storage: self, + iter: ::Timers::iter(), + _fsm: PhantomData::default(), + _ty: PhantomData::default() + } + } +} + +pub struct TimersStorageIterMut<'a, TS, F, T> + where F: FsmBackend +{ + storage: &'a mut TS, + iter: <::Timers as AllVariants>::Iter, + _fsm: PhantomData, + _ty: PhantomData +} + +impl<'a, TS, F, T: 'a> Iterator for TimersStorageIterMut<'a, TS, F, T> + where F: FsmBackend, TS: TimersStorage<'a, F, T> +{ + type Item = (::Timers, &'a mut Option); + + fn next(&mut self) -> Option { + if let Some(id) = self.iter.next() { + let val = self.storage.get_timer_storage_mut(&id); + return Some((id, val)); + } + + None + } } +/* pub trait TimersStorageIter<'a, F, T> :Iterator::Timers, &'a mut Option)> where F: FsmBackend, T: 'a { } +*/ diff --git a/finny/src/timers/mod.rs b/finny/src/timers/mod.rs index 7b5d294..0d4b6f5 100644 --- a/finny/src/timers/mod.rs +++ b/finny/src/timers/mod.rs @@ -1,2 +1,4 @@ #[cfg(feature="timers_std")] -pub mod std; \ No newline at end of file +pub mod std; + +//pub mod core; \ No newline at end of file diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs index 3e83502..7aacbc5 100644 --- a/finny/src/timers/std.rs +++ b/finny/src/timers/std.rs @@ -64,11 +64,13 @@ impl<'a, F, S> FsmTimers for TimersStd return Some(id); } + /* let mut timed_out_id = None; let now = Instant::now(); //let iter = self.timers.iter(); + for (timer_id, timer) in self.timers.iter_mut() { match timer { Some(StdTimer::Timeout { started_at, duration }) if now.duration_since(*started_at) >= *duration => { @@ -93,6 +95,7 @@ impl<'a, F, S> FsmTimers for TimersStd *timer = None; return Some(id); } + */ None } diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 6f73e3d..cec2e13 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -16,6 +16,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let states_enum_ty = ty_append(&fsm.base.fsm_ty, "CurrentState"); let timers_enum_ty = fsm_types.get_fsm_timers_ty(); let timers_enum_iter_ty = fsm_types.get_fsm_timers_iter_ty(); + let timers_storage_ty = fsm_types.get_fsm_timers_storage_ty(); let event_enum_ty = fsm_types.get_fsm_events_ty(); let region_count = fsm.fsm.regions.len(); @@ -775,7 +776,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let mut enum_variants = vec![]; let mut submachines = vec![]; - let mut timers = vec![]; + let mut our_timers = vec![]; let states = fsm.fsm.states.iter().map(|s| s.1); for state in states { @@ -803,7 +804,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let timer_ty = timer.get_ty(&fsm.base); enum_variants.push(quote! { #timer_ty }); - timers.push(timer_ty.clone()); + our_timers.push(timer_ty.clone()); let setup = remap_closure_inputs(&timer.setup.inputs, &[quote! { ctx }, quote! { settings }])?; let setup_body = &timer.setup.body; @@ -868,19 +869,19 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre } }); - let submachine_iters: Vec<_> = submachines.iter().map(|s| { + let submachine_iters: Vec<_> = submachines.iter().map(|s| { let ty = s.get_fsm_timers_iter_ty(); let field = to_field_name(&ty); (ty, field) }).collect(); - let enum_iter_matches_variants = timers.iter().enumerate().map(|(i, variant)| quote! { + let enum_iter_matches_variants = our_timers.iter().enumerate().map(|(i, variant)| quote! { #i => { self.position += 1; Some(#timers_enum_ty :: #variant) } }); let mut enum_iter_matches = TokenStream::new(); enum_iter_matches.append_separated(enum_iter_matches_variants, quote! { , }); enum_iter_matches.append_separated(submachine_iters.iter().enumerate().map(|(i, (ty, field))| { - let i = timers.len() + i; + let i = our_timers.len() + i; quote! { #i if self.#field.is_some() => { if let Some(ref mut iter) = self.#field { @@ -945,7 +946,56 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre }); // timers storage + let our_timers_storage: Vec<_> = our_timers.iter().map(|t| { + let field = to_field_name(t); + (field, t.clone()) + }).collect(); + + let mut timers_storage_struct_fields = Vec::new(); + timers_storage_struct_fields.extend(our_timers_storage.iter().map(|(field, ty)| { + quote! { + #field: Option < #ty > + } + })); + timers_storage_struct_fields.extend(submachines.iter().map(|s| { + let ty = s.get_fsm_timers_storage_ty(); + let field = to_field_name(&ty); + quote! { + #field: #ty + } + })); + let mut fields = TokenStream::new(); + fields.append_separated(timers_storage_struct_fields, quote! { , }); + + + /* + code.append_all(quote! { + + #[derive(Default)] + pub struct #timers_storage_ty #fsm_generics_type #fsm_generics_where { + _fsm: core::marker::PhantomData< #fsm_ty #fsm_generics_type >, + #fields + } + }); + */ + + code.append_all(quote! { + + #[derive(Default)] + pub struct #timers_storage_ty { + #fields + } + + impl<'timer_storage, TTimerStorage: 'timer_storage #fsm_generics_impl > finny::TimersStorage<'timer_storage, #fsm_ty #fsm_generics_type, TTimerStorage> for #timers_storage_ty + #fsm_generics_where + { + fn get_timer_storage_mut(&mut self, id: & #timers_enum_ty ) -> &'timer_storage mut Option { + todo!() + } + } + + }); From 3e7bdc49d38bfbb9f7f65cd0f0901bbf61f3af8c Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Sun, 7 Feb 2021 21:43:06 +0100 Subject: [PATCH 32/38] lifetime... --- finny/src/fsm/timers.rs | 24 +++++++++++------------- finny/src/timers/std.rs | 8 ++++---- finny_derive/src/codegen.rs | 3 +-- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index 54068b9..7cd4667 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -2,36 +2,34 @@ use crate::{AllVariants, DispatchContext, FsmError, FsmEvent, FsmEventQueue, Ins use crate::{FsmBackend, FsmResult}; /// Associate some data with a specific timer ID. -pub trait TimersStorage<'a, F, T> : Default - where F: FsmBackend, T: 'a +pub trait TimersStorage<'a, FT, T> : Default + where FT: AllVariants, T: 'a { //type IterMut: TimersStorageIter<'a, F, T>; - fn get_timer_storage_mut(&mut self, id: &::Timers) -> &'a mut Option; + fn get_timer_storage_mut(&mut self, id: &FT) -> &'a mut Option; - fn iter_mut(&'a mut self) -> TimersStorageIterMut<'a, Self, F, T> { + fn iter_mut(&'a mut self) -> TimersStorageIterMut<'a, Self, FT, T> { TimersStorageIterMut { storage: self, - iter: ::Timers::iter(), - _fsm: PhantomData::default(), + iter: FT::iter(), _ty: PhantomData::default() } } } -pub struct TimersStorageIterMut<'a, TS, F, T> - where F: FsmBackend +pub struct TimersStorageIterMut<'a, TS, FT, T> + where FT: AllVariants { storage: &'a mut TS, - iter: <::Timers as AllVariants>::Iter, - _fsm: PhantomData, + iter: ::Iter, _ty: PhantomData } -impl<'a, TS, F, T: 'a> Iterator for TimersStorageIterMut<'a, TS, F, T> - where F: FsmBackend, TS: TimersStorage<'a, F, T> +impl<'a, TS, FT, T: 'a> Iterator for TimersStorageIterMut<'a, TS, FT, T> + where FT: AllVariants, TS: TimersStorage<'a, FT, T> { - type Item = (::Timers, &'a mut Option); + type Item = (FT, &'a mut Option); fn next(&mut self) -> Option { if let Some(id) = self.iter.next() { diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs index 7aacbc5..026f6a8 100644 --- a/finny/src/timers/std.rs +++ b/finny/src/timers/std.rs @@ -19,7 +19,7 @@ pub enum StdTimer { impl<'a, F, S> TimersStd where F: FsmBackend, - S: TimersStorage<'a, F, StdTimer> + S: TimersStorage<'a, ::Timers, StdTimer> { pub fn new() -> Self { Self { @@ -31,7 +31,7 @@ impl<'a, F, S> TimersStd impl<'a, F, S> FsmTimers for TimersStd where F: FsmBackend, - S: TimersStorage<'a, F, StdTimer> + S: TimersStorage<'a, ::Timers, StdTimer> { fn create(&mut self, id: ::Timers, settings: &crate::TimerSettings) -> crate::FsmResult<()> { // try to cancel any existing ones @@ -64,7 +64,7 @@ impl<'a, F, S> FsmTimers for TimersStd return Some(id); } - /* + let mut timed_out_id = None; let now = Instant::now(); @@ -95,7 +95,7 @@ impl<'a, F, S> FsmTimers for TimersStd *timer = None; return Some(id); } - */ + None } diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index cec2e13..7fb0bc0 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -987,8 +987,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre #fields } - impl<'timer_storage, TTimerStorage: 'timer_storage #fsm_generics_impl > finny::TimersStorage<'timer_storage, #fsm_ty #fsm_generics_type, TTimerStorage> for #timers_storage_ty - #fsm_generics_where + impl<'timer_storage, TTimerStorage: 'timer_storage > finny::TimersStorage<'timer_storage, #timers_enum_ty , TTimerStorage> for #timers_storage_ty { fn get_timer_storage_mut(&mut self, id: & #timers_enum_ty ) -> &'timer_storage mut Option { todo!() From 8ea62328d9b95cf8a9b3d7be61a96c3d1e12b6f6 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Mon, 8 Feb 2021 22:06:46 +0100 Subject: [PATCH 33/38] blah blah --- finny/src/fsm/timers.rs | 45 +------------------------- finny/src/timers/std.rs | 12 +++---- finny_derive/src/codegen.rs | 56 ++++++++++++++++++++++++--------- finny_tests/tests/fsm_timers.rs | 4 +-- 4 files changed, 50 insertions(+), 67 deletions(-) diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index 7cd4667..7c77d9b 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -3,53 +3,10 @@ use crate::{FsmBackend, FsmResult}; /// Associate some data with a specific timer ID. pub trait TimersStorage<'a, FT, T> : Default - where FT: AllVariants, T: 'a -{ - //type IterMut: TimersStorageIter<'a, F, T>; - - fn get_timer_storage_mut(&mut self, id: &FT) -> &'a mut Option; - - fn iter_mut(&'a mut self) -> TimersStorageIterMut<'a, Self, FT, T> { - TimersStorageIterMut { - storage: self, - iter: FT::iter(), - _ty: PhantomData::default() - } - } -} - -pub struct TimersStorageIterMut<'a, TS, FT, T> where FT: AllVariants { - storage: &'a mut TS, - iter: ::Iter, - _ty: PhantomData -} - -impl<'a, TS, FT, T: 'a> Iterator for TimersStorageIterMut<'a, TS, FT, T> - where FT: AllVariants, TS: TimersStorage<'a, FT, T> -{ - type Item = (FT, &'a mut Option); - - fn next(&mut self) -> Option { - if let Some(id) = self.iter.next() { - let val = self.storage.get_timer_storage_mut(&id); - return Some((id, val)); - } - - None - } -} - -/* -pub trait TimersStorageIter<'a, F, T> :Iterator::Timers, &'a mut Option)> - where F: FsmBackend, T: 'a -{ - + fn get_timer_storage_mut(&mut self, id: &FT) -> &'a mut Option; } -*/ - - pub struct TimerInstance diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs index 026f6a8..eaa06f2 100644 --- a/finny/src/timers/std.rs +++ b/finny/src/timers/std.rs @@ -1,7 +1,7 @@ //! A naive timers implementation based on standard libraries' `Instant` time provider. use std::{marker::PhantomData, time::{Duration, Instant}}; -use crate::{FsmBackend, FsmTimers, TimersStorage}; +use crate::{FsmBackend, FsmTimers, TimersStorage, AllVariants}; pub struct TimersStd where F: FsmBackend @@ -21,9 +21,9 @@ impl<'a, F, S> TimersStd where F: FsmBackend, S: TimersStorage<'a, ::Timers, StdTimer> { - pub fn new() -> Self { + pub fn new(timers: S) -> Self { Self { - timers: S::default(), + timers, pending_intervals: None } } @@ -64,14 +64,15 @@ impl<'a, F, S> FsmTimers for TimersStd return Some(id); } - let mut timed_out_id = None; let now = Instant::now(); //let iter = self.timers.iter(); - for (timer_id, timer) in self.timers.iter_mut() { + //for (timer_id, timer) in self.timers.iter_mut() { + for timer_id in ::Timers::iter() { + let timer = self.timers.get_timer_storage_mut(&timer_id); match timer { Some(StdTimer::Timeout { started_at, duration }) if now.duration_since(*started_at) >= *duration => { timed_out_id = Some(timer_id); @@ -96,7 +97,6 @@ impl<'a, F, S> FsmTimers for TimersStd return Some(id); } - None } } \ No newline at end of file diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 7fb0bc0..8dc727b 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -954,43 +954,71 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let mut timers_storage_struct_fields = Vec::new(); timers_storage_struct_fields.extend(our_timers_storage.iter().map(|(field, ty)| { quote! { - #field: Option < #ty > + #field: Option < TTimerStorage > } })); timers_storage_struct_fields.extend(submachines.iter().map(|s| { let ty = s.get_fsm_timers_storage_ty(); let field = to_field_name(&ty); quote! { - #field: #ty + #field: #ty < TTimerStorage > } })); let mut fields = TokenStream::new(); fields.append_separated(timers_storage_struct_fields, quote! { , }); - /* - code.append_all(quote! { + + let mut timers_storage_matches = vec![]; + timers_storage_matches.extend(our_timers_storage.iter().map(|(field, ty)| { + quote! { + #timers_enum_ty :: #ty => &mut self. #field + } + })); + timers_storage_matches.extend(submachines.iter().map(|s| { + let ty = s.get_fsm_timers_storage_ty(); + //let t = s.get_fsm_timers_ty(); + let t = s.get_fsm_ty(); + let field = to_field_name(&ty); + quote! { + #timers_enum_ty :: #t (ref sub) => { + self. #field .get_timer_storage_mut(sub) + } + } + })); - #[derive(Default)] - pub struct #timers_storage_ty #fsm_generics_type #fsm_generics_where { - _fsm: core::marker::PhantomData< #fsm_ty #fsm_generics_type >, - #fields + let matches = if timers_storage_matches.len() == 0 { + quote! { + panic!("Not supported in this FSM."); } + } else { + let mut m = TokenStream::new(); + m.append_separated(timers_storage_matches, quote! { , }); - }); - */ + quote! { + match *id { + #m + } + } + }; code.append_all(quote! { - #[derive(Default)] - pub struct #timers_storage_ty { + pub struct #timers_storage_ty { + _storage: core::marker::PhantomData, #fields } - impl<'timer_storage, TTimerStorage: 'timer_storage > finny::TimersStorage<'timer_storage, #timers_enum_ty , TTimerStorage> for #timers_storage_ty + impl core::default::Default for #timers_storage_ty { + fn default() -> Self { + todo!() + } + } + + impl<'timer_storage, TTimerStorage: 'timer_storage > finny::TimersStorage<'timer_storage, #timers_enum_ty , TTimerStorage> for #timers_storage_ty { fn get_timer_storage_mut(&mut self, id: & #timers_enum_ty ) -> &'timer_storage mut Option { - todo!() + #matches } } diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index f962cb0..3a0ec44 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -141,8 +141,7 @@ fn test_timers_fsm() -> FsmResult<()> { let timers_variants: Vec<_> = TimersMachineTimers::iter().collect(); assert_eq!(&[TimersMachineTimers::Timer1, TimersMachineTimers::Timer2, TimersMachineTimers::BlinkerMachine(BlinkerMachineTimers::BlinkingTimer)], timers_variants.as_slice()); - /* - let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), TimersStd::new())?; + let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), TimersStd::new(TimersMachineTimersStorage::default()))?; fsm.start()?; @@ -166,7 +165,6 @@ fn test_timers_fsm() -> FsmResult<()> { let sub_machine: &BlinkerMachine = fsm.get_state(); assert_eq!(6, sub_machine.toggles); - */ Ok(()) } \ No newline at end of file From 9733978739a2492e79970f8b9a43fb476ec913ac Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Mon, 8 Feb 2021 22:25:01 +0100 Subject: [PATCH 34/38] allocation free timers! --- finny/src/fsm/timers.rs | 4 ++-- finny/src/timers/std.rs | 15 +++++---------- finny_derive/src/codegen.rs | 24 ++++++++++++++++++++---- 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/finny/src/fsm/timers.rs b/finny/src/fsm/timers.rs index 7c77d9b..9bf78cf 100644 --- a/finny/src/fsm/timers.rs +++ b/finny/src/fsm/timers.rs @@ -2,10 +2,10 @@ use crate::{AllVariants, DispatchContext, FsmError, FsmEvent, FsmEventQueue, Ins use crate::{FsmBackend, FsmResult}; /// Associate some data with a specific timer ID. -pub trait TimersStorage<'a, FT, T> : Default +pub trait TimersStorage : Default where FT: AllVariants { - fn get_timer_storage_mut(&mut self, id: &FT) -> &'a mut Option; + fn get_timer_storage_mut(&mut self, id: &FT) -> &mut Option; } diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs index eaa06f2..691e03b 100644 --- a/finny/src/timers/std.rs +++ b/finny/src/timers/std.rs @@ -1,12 +1,11 @@ //! A naive timers implementation based on standard libraries' `Instant` time provider. -use std::{marker::PhantomData, time::{Duration, Instant}}; +use std::{time::{Duration, Instant}}; use crate::{FsmBackend, FsmTimers, TimersStorage, AllVariants}; pub struct TimersStd where F: FsmBackend { - //timers: Vec<(::Timers, StdTimer)>, timers: S, pending_intervals: Option<(::Timers, usize)> } @@ -17,9 +16,9 @@ pub enum StdTimer { Interval { started_at: Instant, interval: Duration } } -impl<'a, F, S> TimersStd +impl TimersStd where F: FsmBackend, - S: TimersStorage<'a, ::Timers, StdTimer> + S: TimersStorage<::Timers, StdTimer>, { pub fn new(timers: S) -> Self { Self { @@ -29,9 +28,9 @@ impl<'a, F, S> TimersStd } } -impl<'a, F, S> FsmTimers for TimersStd +impl FsmTimers for TimersStd where F: FsmBackend, - S: TimersStorage<'a, ::Timers, StdTimer> + S: TimersStorage<::Timers, StdTimer> { fn create(&mut self, id: ::Timers, settings: &crate::TimerSettings) -> crate::FsmResult<()> { // try to cancel any existing ones @@ -67,10 +66,6 @@ impl<'a, F, S> FsmTimers for TimersStd let mut timed_out_id = None; let now = Instant::now(); - //let iter = self.timers.iter(); - - - //for (timer_id, timer) in self.timers.iter_mut() { for timer_id in ::Timers::iter() { let timer = self.timers.get_timer_storage_mut(&timer_id); match timer { diff --git a/finny_derive/src/codegen.rs b/finny_derive/src/codegen.rs index 8dc727b..360b8fa 100644 --- a/finny_derive/src/codegen.rs +++ b/finny_derive/src/codegen.rs @@ -967,6 +967,19 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre let mut fields = TokenStream::new(); fields.append_separated(timers_storage_struct_fields, quote! { , }); + let mut new_fields_vec = vec![]; + new_fields_vec.extend(our_timers_storage.iter().map(|(field, ty)| quote! { + #field: None + })); + new_fields_vec.extend(submachines.iter().map(|s| { + let ty = s.get_fsm_timers_storage_ty(); + let field = to_field_name(&ty); + quote! { + #field: #ty :: default() + } + })); + let mut new_fields = TokenStream::new(); + new_fields.append_separated(new_fields_vec, quote! { , }); let mut timers_storage_matches = vec![]; @@ -978,7 +991,7 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre timers_storage_matches.extend(submachines.iter().map(|s| { let ty = s.get_fsm_timers_storage_ty(); //let t = s.get_fsm_timers_ty(); - let t = s.get_fsm_ty(); + let t = s.get_fsm_no_generics_ty(); let field = to_field_name(&ty); quote! { #timers_enum_ty :: #t (ref sub) => { @@ -1011,13 +1024,16 @@ pub fn generate_fsm_code(fsm: &FsmFnInput, _attr: TokenStream, _input: TokenStre impl core::default::Default for #timers_storage_ty { fn default() -> Self { - todo!() + Self { + _storage: core::marker::PhantomData::default(), + #new_fields + } } } - impl<'timer_storage, TTimerStorage: 'timer_storage > finny::TimersStorage<'timer_storage, #timers_enum_ty , TTimerStorage> for #timers_storage_ty + impl finny::TimersStorage<#timers_enum_ty , TTimerStorage> for #timers_storage_ty { - fn get_timer_storage_mut(&mut self, id: & #timers_enum_ty ) -> &'timer_storage mut Option { + fn get_timer_storage_mut(&mut self, id: & #timers_enum_ty ) -> &mut Option { #matches } } From 4ffcc2f4180113f0d6dd61de46165b1f1f32b793 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Mon, 8 Feb 2021 22:39:42 +0100 Subject: [PATCH 35/38] standard --- finny/src/fsm/fsm_factory.rs | 8 +-- finny/src/timers/core.rs | 56 +++++++++++++++++++ finny/src/timers/mod.rs | 3 + finny/src/timers/std.rs | 52 ++++++++--------- finny/src/timers/std_noalloc.rs | 98 +++++++++++++++++++++++++++++++++ finny_tests/tests/fsm_timers.rs | 2 +- 6 files changed, 184 insertions(+), 35 deletions(-) create mode 100644 finny/src/timers/core.rs create mode 100644 finny/src/timers/std_noalloc.rs diff --git a/finny/src/fsm/fsm_factory.rs b/finny/src/fsm/fsm_factory.rs index 0e862f0..fbd2506 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, InspectNull, timers::std::TimersStd}; #[cfg(feature="std")] use crate::FsmEventQueueVec; @@ -24,14 +24,14 @@ pub trait FsmFactory { Ok(frontend) } - /// Build a new frontend for the FSM with a `FsmEventQueueVec` queue and no logging. + /// 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, FsmTimersNull>> { + fn new(context: ::Context) -> FsmResult, InspectNull, TimersStd>> { let frontend = FsmFrontend { queue: FsmEventQueueVec::new(), backend: FsmBackendImpl::new(context)?, inspect: InspectNull::new(), - timers: FsmTimersNull::default() + timers: TimersStd::new(), }; Ok(frontend) diff --git a/finny/src/timers/core.rs b/finny/src/timers/core.rs new file mode 100644 index 0000000..d2ec06d --- /dev/null +++ b/finny/src/timers/core.rs @@ -0,0 +1,56 @@ +//! An implementation of timers that relies just on the `Duration`. Has to be called with +//! a reasonable period rate to trigger the timers. + +use crate::{FsmBackend, FsmTimers}; +use crate::lib::*; +use Duration; +use arraydeque::{Array, ArrayDeque}; + +pub struct TimersCore + where F: FsmBackend, + Q: Array::Timers> +{ + timers: Vec<(::Timers, CoreTimer)>, + pending_events: ArrayDeque + //pending_intervals: Option<(::Timers, usize)> +} + +#[derive(Debug)] +enum CoreTimer { + Timeout { time_remaining: Duration }, +} + +impl TimersCore + where F: FsmBackend, + Q: Array::Timers> +{ + pub fn new() -> Self { + Self { + timers: vec![], + pending_events: ArrayDeque::new() + //pending_intervals: None + } + } + + pub fn tick(&mut self, elapsed_since_last_tick: Duration) { + + } +} + +impl FsmTimers for TimersCore + where F: FsmBackend, + Q: Array::Timers> +{ + fn create(&mut self, id: ::Timers, settings: &crate::TimerSettings) -> crate::FsmResult<()> { + todo!() + } + + fn cancel(&mut self, id: ::Timers) -> crate::FsmResult<()> { + self.timers.retain(|(timer_id, _)| *timer_id != id); + Ok(()) + } + + fn get_triggered_timer(&mut self) -> Option<::Timers> { + todo!() + } +} \ No newline at end of file diff --git a/finny/src/timers/mod.rs b/finny/src/timers/mod.rs index 0d4b6f5..b58b828 100644 --- a/finny/src/timers/mod.rs +++ b/finny/src/timers/mod.rs @@ -1,3 +1,6 @@ +#[cfg(feature="timers_std")] +pub mod std_noalloc; + #[cfg(feature="timers_std")] pub mod std; diff --git a/finny/src/timers/std.rs b/finny/src/timers/std.rs index 691e03b..85b8888 100644 --- a/finny/src/timers/std.rs +++ b/finny/src/timers/std.rs @@ -1,55 +1,50 @@ -//! A naive timers implementation based on standard libraries' `Instant` time provider. +//! Standard library timers with dynamic allocation of the timer's storage. use std::{time::{Duration, Instant}}; -use crate::{FsmBackend, FsmTimers, TimersStorage, AllVariants}; +use crate::{FsmBackend, FsmTimers}; -pub struct TimersStd +pub struct TimersStd where F: FsmBackend { - timers: S, + timers: Vec<(::Timers, StdTimer)>, pending_intervals: Option<(::Timers, usize)> } #[derive(Debug)] -pub enum StdTimer { +enum StdTimer { Timeout { started_at: Instant, duration: Duration }, Interval { started_at: Instant, interval: Duration } } -impl TimersStd - where F: FsmBackend, - S: TimersStorage<::Timers, StdTimer>, +impl TimersStd + where F: FsmBackend { - pub fn new(timers: S) -> Self { + pub fn new() -> Self { Self { - timers, + timers: vec![], pending_intervals: None } } } -impl FsmTimers for TimersStd - where F: FsmBackend, - S: TimersStorage<::Timers, StdTimer> +impl FsmTimers for TimersStd + where F: FsmBackend { fn create(&mut self, id: ::Timers, settings: &crate::TimerSettings) -> crate::FsmResult<()> { // try to cancel any existing ones self.cancel(id.clone())?; - let t = self.timers.get_timer_storage_mut(&id); - if settings.renew { - *t = Some(StdTimer::Interval { started_at: Instant::now(), interval: settings.timeout }); + self.timers.push((id, StdTimer::Interval { started_at: Instant::now(), interval: settings.timeout })); } else { - *t = Some(StdTimer::Timeout { started_at: Instant::now(), duration: settings.timeout }); + self.timers.push((id, StdTimer::Timeout { started_at: Instant::now(), duration: settings.timeout })); } Ok(()) } fn cancel(&mut self, id: ::Timers) -> crate::FsmResult<()> { - let t = self.timers.get_timer_storage_mut(&id); - *t = None; + self.timers.retain(|(timer_id, _)| *timer_id != id); Ok(()) } @@ -63,17 +58,15 @@ impl FsmTimers for TimersStd return Some(id); } - let mut timed_out_id = None; + let mut timed_out_idx = None; let now = Instant::now(); - - for timer_id in ::Timers::iter() { - let timer = self.timers.get_timer_storage_mut(&timer_id); + for (idx, (timer_id, timer)) in self.timers.iter_mut().enumerate() { match timer { - Some(StdTimer::Timeout { started_at, duration }) if now.duration_since(*started_at) >= *duration => { - timed_out_id = Some(timer_id); + StdTimer::Timeout { started_at, duration } if now.duration_since(*started_at) >= *duration => { + timed_out_idx = Some(idx); break; }, - Some(StdTimer::Interval { ref mut started_at, interval }) if now.duration_since(*started_at) >= *interval => { + StdTimer::Interval { ref mut started_at, interval } if now.duration_since(*started_at) >= *interval => { let t = now.duration_since(*started_at); let times = ((t.as_secs_f32() / interval.as_secs_f32()).floor() as usize) - 1; if times > 0 { @@ -86,12 +79,11 @@ impl FsmTimers for TimersStd } } - if let Some(id) = timed_out_id { - let timer = self.timers.get_timer_storage_mut(&id); - *timer = None; + if let Some(idx) = timed_out_idx { + let (id, _) = self.timers.remove(idx); return Some(id); } - + None } } \ No newline at end of file diff --git a/finny/src/timers/std_noalloc.rs b/finny/src/timers/std_noalloc.rs new file mode 100644 index 0000000..3125cdd --- /dev/null +++ b/finny/src/timers/std_noalloc.rs @@ -0,0 +1,98 @@ +//! A naive timers implementation based on standard libraries' `Instant` time provider and no runtime allocations. +//! Type system has to be setup manually. + +use std::{time::{Duration, Instant}}; +use crate::{FsmBackend, FsmTimers, TimersStorage, AllVariants}; + +pub struct TimersStdNoAlloc + where F: FsmBackend +{ + timers: S, + pending_intervals: Option<(::Timers, usize)> +} + +#[derive(Debug)] +pub enum StdTimer { + Timeout { started_at: Instant, duration: Duration }, + Interval { started_at: Instant, interval: Duration } +} + +impl TimersStdNoAlloc + where F: FsmBackend, + S: TimersStorage<::Timers, StdTimer>, +{ + pub fn new(timers: S) -> Self { + Self { + timers, + pending_intervals: None + } + } +} + +impl FsmTimers for TimersStdNoAlloc + where F: FsmBackend, + S: TimersStorage<::Timers, StdTimer> +{ + fn create(&mut self, id: ::Timers, settings: &crate::TimerSettings) -> crate::FsmResult<()> { + // try to cancel any existing ones + self.cancel(id.clone())?; + + let t = self.timers.get_timer_storage_mut(&id); + + if settings.renew { + *t = Some(StdTimer::Interval { started_at: Instant::now(), interval: settings.timeout }); + } else { + *t = Some(StdTimer::Timeout { started_at: Instant::now(), duration: settings.timeout }); + } + + Ok(()) + } + + fn cancel(&mut self, id: ::Timers) -> crate::FsmResult<()> { + let t = self.timers.get_timer_storage_mut(&id); + *t = None; + Ok(()) + } + + fn get_triggered_timer(&mut self) -> Option<::Timers> { + if let Some((id, mut times)) = self.pending_intervals.take() { + times -= 1; + if times > 0 { + self.pending_intervals = Some((id.clone(), times)); + } + + return Some(id); + } + + let mut timed_out_id = None; + let now = Instant::now(); + + for timer_id in ::Timers::iter() { + let timer = self.timers.get_timer_storage_mut(&timer_id); + match timer { + Some(StdTimer::Timeout { started_at, duration }) if now.duration_since(*started_at) >= *duration => { + timed_out_id = Some(timer_id); + break; + }, + Some(StdTimer::Interval { ref mut started_at, interval }) if now.duration_since(*started_at) >= *interval => { + let t = now.duration_since(*started_at); + let times = ((t.as_secs_f32() / interval.as_secs_f32()).floor() as usize) - 1; + if times > 0 { + self.pending_intervals = Some((timer_id.clone(), times)); + } + *started_at = now; + return Some(timer_id.clone()); + }, + _ => () + } + } + + if let Some(id) = timed_out_id { + let timer = self.timers.get_timer_storage_mut(&id); + *timer = None; + return Some(id); + } + + None + } +} \ No newline at end of file diff --git a/finny_tests/tests/fsm_timers.rs b/finny_tests/tests/fsm_timers.rs index 3a0ec44..c932cdd 100644 --- a/finny_tests/tests/fsm_timers.rs +++ b/finny_tests/tests/fsm_timers.rs @@ -141,7 +141,7 @@ fn test_timers_fsm() -> FsmResult<()> { let timers_variants: Vec<_> = TimersMachineTimers::iter().collect(); assert_eq!(&[TimersMachineTimers::Timer1, TimersMachineTimers::Timer2, TimersMachineTimers::BlinkerMachine(BlinkerMachineTimers::BlinkingTimer)], timers_variants.as_slice()); - let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), TimersStd::new(TimersMachineTimersStorage::default()))?; + let mut fsm = TimersMachine::new_with(ctx, FsmEventQueueVec::new(), InspectSlog::new(Some(logger)), TimersStd::new())?; fsm.start()?; From 3668d981cefc37956a6e215afce238c75baf5cd6 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Mon, 8 Feb 2021 22:54:46 +0100 Subject: [PATCH 36/38] untested core, alloc free timers --- finny/src/timers/core.rs | 76 ++++++++++++++++++++++++++++++---------- finny/src/timers/mod.rs | 2 +- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/finny/src/timers/core.rs b/finny/src/timers/core.rs index d2ec06d..ade75ab 100644 --- a/finny/src/timers/core.rs +++ b/finny/src/timers/core.rs @@ -1,56 +1,96 @@ //! An implementation of timers that relies just on the `Duration`. Has to be called with //! a reasonable period rate to trigger the timers. -use crate::{FsmBackend, FsmTimers}; +use crate::{FsmBackend, FsmTimers, TimersStorage, AllVariants}; use crate::lib::*; use Duration; use arraydeque::{Array, ArrayDeque}; -pub struct TimersCore +pub struct TimersCore where F: FsmBackend, - Q: Array::Timers> + Q: Array::Timers>, + S: TimersStorage<::Timers, CoreTimer> { - timers: Vec<(::Timers, CoreTimer)>, - pending_events: ArrayDeque - //pending_intervals: Option<(::Timers, usize)> + timers: S, + pending_events: ArrayDeque, + _fsm: PhantomData } #[derive(Debug)] -enum CoreTimer { +pub enum CoreTimer { Timeout { time_remaining: Duration }, + Interval { time_remaining: Duration, interval: Duration } } -impl TimersCore +impl TimersCore where F: FsmBackend, - Q: Array::Timers> + Q: Array::Timers>, + S: TimersStorage<::Timers, CoreTimer> { - pub fn new() -> Self { + pub fn new(timers: S) -> Self { Self { - timers: vec![], - pending_events: ArrayDeque::new() - //pending_intervals: None + timers, + pending_events: ArrayDeque::new(), + _fsm: PhantomData::default() } } pub fn tick(&mut self, elapsed_since_last_tick: Duration) { + let iter = ::Timers::iter(); + for id in iter { + let mut timer = self.timers.get_timer_storage_mut(&id); + // todo: account for the difference between time remaining and elapsed time, currently we just reset it + match timer { + Some(CoreTimer::Timeout { time_remaining}) => { + if *time_remaining <= elapsed_since_last_tick { + self.pending_events.push_front(id); + *timer = None + } else { + *time_remaining -= elapsed_since_last_tick; + } + }, + Some(CoreTimer::Interval { time_remaining, interval }) => { + if *time_remaining <= elapsed_since_last_tick { + self.pending_events.push_front(id); + *time_remaining = *interval; + } else { + *time_remaining -= elapsed_since_last_tick; + } + } + None => {} + } + } } } -impl FsmTimers for TimersCore +impl FsmTimers for TimersCore where F: FsmBackend, - Q: Array::Timers> + Q: Array::Timers>, + S: TimersStorage<::Timers, CoreTimer> { fn create(&mut self, id: ::Timers, settings: &crate::TimerSettings) -> crate::FsmResult<()> { - todo!() + self.cancel(id.clone()); + + if settings.enabled { + let mut timer = self.timers.get_timer_storage_mut(&id); + if settings.renew { + *timer = Some(CoreTimer::Interval { interval: settings.timeout, time_remaining: settings.timeout }); + } else { + *timer = Some(CoreTimer::Timeout { time_remaining: settings.timeout }); + } + } + + Ok(()) } fn cancel(&mut self, id: ::Timers) -> crate::FsmResult<()> { - self.timers.retain(|(timer_id, _)| *timer_id != id); + let timer = self.timers.get_timer_storage_mut(&id); + *timer = None; Ok(()) } fn get_triggered_timer(&mut self) -> Option<::Timers> { - todo!() + self.pending_events.pop_back() } } \ No newline at end of file diff --git a/finny/src/timers/mod.rs b/finny/src/timers/mod.rs index b58b828..3be48f0 100644 --- a/finny/src/timers/mod.rs +++ b/finny/src/timers/mod.rs @@ -4,4 +4,4 @@ pub mod std_noalloc; #[cfg(feature="timers_std")] pub mod std; -//pub mod core; \ No newline at end of file +pub mod core; \ No newline at end of file From 893cbdfc66ee52dd3cc9acb6171310c5af745185 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Thu, 16 Sep 2021 18:33:22 +0200 Subject: [PATCH 37/38] ping --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fcae1ff..6c41ef1 100644 --- a/README.md +++ b/README.md @@ -61,4 +61,4 @@ fn main() -> FsmResult<()> { } ``` -License: MIT OR Apache-2.0 +License: MIT OR Apache-2.0 \ No newline at end of file From 8fef1d7bd5221b866c541ee1af22d80c7680eee1 Mon Sep 17 00:00:00 2001 From: Rudi Benkovic Date: Thu, 16 Sep 2021 18:41:10 +0200 Subject: [PATCH 38/38] no_std fix --- finny/src/fsm/fsm_factory.rs | 4 ++-- finny/src/fsm/tests_fsm.rs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/finny/src/fsm/fsm_factory.rs b/finny/src/fsm/fsm_factory.rs index fbd2506..df889bb 100644 --- a/finny/src/fsm/fsm_factory.rs +++ b/finny/src/fsm/fsm_factory.rs @@ -1,7 +1,7 @@ -use crate::{FsmBackend, FsmBackendImpl, FsmEventQueue, FsmFrontend, FsmResult, FsmTimers, FsmTimersNull, Inspect, InspectNull, timers::std::TimersStd}; +use crate::{FsmBackend, FsmBackendImpl, FsmEventQueue, FsmFrontend, FsmResult, FsmTimers, FsmTimersNull, Inspect, InspectNull}; #[cfg(feature="std")] -use crate::FsmEventQueueVec; +use crate::{FsmEventQueueVec, timers::std::TimersStd}; /// Builds a frontend for running your FSM. pub trait FsmFactory { diff --git a/finny/src/fsm/tests_fsm.rs b/finny/src/fsm/tests_fsm.rs index 2f7200c..9150eb4 100644 --- a/finny/src/fsm/tests_fsm.rs +++ b/finny/src/fsm/tests_fsm.rs @@ -41,14 +41,13 @@ pub enum FsmBackendTimers { } impl AllVariants for FsmBackendTimers { - type Iter = std::iter::Once; + type Iter = core::iter::Once; fn iter() -> Self::Iter { todo!() } } - impl FsmBackend for TestFsm { type Context = (); type States = States;