Skip to content

Commit

Permalink
Initial support for sub-machines (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
rudib authored Jan 17, 2021
1 parent 88261df commit 9c7c82f
Show file tree
Hide file tree
Showing 29 changed files with 1,140 additions and 313 deletions.
4 changes: 2 additions & 2 deletions finny/src/decl/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl<'a, TFsm, TContext, TEvent, TState> FsmEventBuilderTransition<'a, TFsm, TCo
}

/// A guard for executing this action.
pub fn guard<TGuard: Fn(&TEvent, &EventContext<'a, TFsm, FsmQueueMock<TFsm>>) -> bool>(&mut self, _guard: TGuard) -> &mut Self {
pub fn guard<TGuard: Fn(&TEvent, &EventContext<'a, TFsm, FsmQueueMock<TFsm>>, &<TFsm as FsmBackend>::States) -> bool>(&mut self, _guard: TGuard) -> &mut Self {
self
}
}
Expand All @@ -66,7 +66,7 @@ impl<'a, TFsm, TContext, TEvent, TStateFrom, TStateTo> FsmEventBuilderTransition
}

/// A guard for starting this transition from one state to another, including executing the action.
pub fn guard<TGuard: Fn(&TEvent, &EventContext<'a, TFsm, FsmQueueMock<TFsm>>) -> bool>(&mut self, _guard: TGuard) -> &mut Self {
pub fn guard<TGuard: Fn(&TEvent, &EventContext<'a, TFsm, FsmQueueMock<TFsm>>, &<TFsm as FsmBackend>::States) -> bool>(&mut self, _guard: TGuard) -> &mut Self {
self
}
}
22 changes: 19 additions & 3 deletions finny/src/decl/fsm.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::lib::*;
use crate::{FsmBackendImpl, lib::*};

use crate::FsmBackend;
use super::FsmStateBuilder;
use super::{FsmStateBuilder, FsmSubMachineBuilder};

/// The main builder-API for defining your Finny state machine.
#[derive(Default)]
Expand All @@ -14,7 +14,7 @@ pub struct FsmBuilder<TFsm, TContext> {
pub struct BuiltFsm;

impl<TFsm, TContext> FsmBuilder<TFsm, TContext>
where TFsm: FsmBackend
where TFsm: FsmBackend<Context = TContext>
{
/// Sets the initial state of the state machine. Required!
pub fn initial_state<TSTate>(&mut self) {
Expand Down Expand Up @@ -43,6 +43,22 @@ impl<TFsm, TContext> FsmBuilder<TFsm, TContext>
}
}

/// Adds a sub machine
pub fn sub_machine<TSubFsm>(&mut self) -> FsmSubMachineBuilder<TFsm, TContext, TSubFsm>
where TSubFsm: FsmBackend
{
FsmSubMachineBuilder {
_fsm: PhantomData::default(),
_ctx: PhantomData::default(),
_sub: PhantomData::default(),
_state_builder: FsmStateBuilder {
_context: PhantomData::default(),
_fsm: PhantomData::default(),
_state: PhantomData::default()
}
}
}

/// Builds the final machine. Has to be returned from the definition function.
pub fn build(self) -> BuiltFsm {
BuiltFsm
Expand Down
2 changes: 2 additions & 0 deletions finny/src/decl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
mod fsm;
mod state;
mod event;
mod sub;

pub use self::fsm::*;
pub use self::state::*;
pub use self::event::*;
pub use self::sub::*;

#[cfg(feature = "std")]
pub type FsmQueueMock<F> = crate::FsmEventQueueVec<F>;
Expand Down
39 changes: 39 additions & 0 deletions finny/src/decl/sub.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use crate::{EventContext, FsmBackend, lib::*};

use super::{FsmEventBuilderState, FsmQueueMock, FsmStateBuilder};


pub struct FsmSubMachineBuilder<TFsm, TContext, TSubMachine> {
pub (crate) _fsm: PhantomData<TFsm>,
pub (crate) _ctx: PhantomData<TContext>,
pub (crate) _sub: PhantomData<TSubMachine>,
pub (crate) _state_builder: FsmStateBuilder<TFsm, TContext, TSubMachine>
}

impl<TFsm, TContext, TSubMachine> FsmSubMachineBuilder<TFsm, TContext, TSubMachine>
where TFsm: FsmBackend<Context = TContext>, TSubMachine: FsmBackend
{
/// Adds a context adapter. A referenced context of the parent machine is provided, and a new
/// instance of the submachine's context has to be instantiated.
pub fn with_context<TCtxFactory: Fn(&TContext) -> <TSubMachine as FsmBackend>::Context>(&mut self, _sub_context_factory: TCtxFactory) -> &Self {
self
}

/// Execute this action when entering the sub-machine state.
pub fn on_entry<'a, TAction: Fn(&mut TSubMachine, &mut EventContext<'a, TFsm, FsmQueueMock<TFsm>>)>(&self, _action: TAction) -> &Self {
self
}

/// Execute this action when exiting the sub-machine state.
pub fn on_exit<'a, TAction: Fn(&mut TSubMachine, &mut EventContext<'a, TFsm, FsmQueueMock<TFsm>>)>(&self, _action: TAction) -> &Self {
self
}

/// What happens if we receive this event and we are in this submachine's state right now?
pub fn on_event<TEvent>(&self) -> FsmEventBuilderState<TFsm, TContext, TEvent, TSubMachine> {
FsmEventBuilderState {
_state_builder: &self._state_builder,
_event: PhantomData::default()
}
}
}
59 changes: 59 additions & 0 deletions finny/src/fsm/dispatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use crate::lib::*;
use crate::{EventContext, FsmBackend, FsmBackendImpl, FsmEvent, FsmEventQueue, FsmEventQueueSub, FsmRegionId, FsmResult, Inspect};

pub struct DispatchContext<'a, 'b, 'c, F, Q, I>
where F: FsmBackend,
Q: FsmEventQueue<F>,
I: Inspect
{
pub queue: &'a mut Q,
pub inspect: &'b mut I,
pub backend: &'c mut FsmBackendImpl<F>
}

impl<'a, 'b, 'c, F, Q, I> DispatchContext<'a, 'b, 'c, F, Q, I>
where F: FsmBackend,
Q: FsmEventQueue<F>,
I: Inspect
{

pub fn to_event_context(&'a mut self, region: FsmRegionId) -> EventContext<'a, F, Q>
{
EventContext {
context: &mut self.backend.context,
queue: self.queue,
region
}
}
}

/// 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)
-> FsmResult<()>
where
TFsm: FsmBackend,
<TFsm as FsmBackend>::States: AsMut<TSubMachine>,
TSubMachine: FsmBackend + DerefMut<Target = FsmBackendImpl<TSubMachine>> + FsmBackend<Events = TEvent>,
Q: FsmEventQueue<TFsm>,
I: Inspect,
<TFsm as FsmBackend>::Events: From<<TSubMachine as FsmBackend>::Events>,
TEvent: Clone
{
let sub_fsm: &mut TSubMachine = ctx.backend.states.as_mut();

let mut queue_adapter = FsmEventQueueSub {
parent: ctx.queue,
_parent_fsm: core::marker::PhantomData::<TFsm>::default(),
_sub_fsm: core::marker::PhantomData::<TSubMachine>::default()
};

let mut inspect = inspect_event_ctx.for_sub_machine::<TSubMachine>();

let sub_dispatch_ctx = DispatchContext {
backend: sub_fsm,
inspect: &mut inspect,
queue: &mut queue_adapter
};

<TSubMachine>::dispatch_event(sub_dispatch_ctx, FsmEvent::Event(ev.clone()))
}
20 changes: 14 additions & 6 deletions finny/src/fsm/events.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use crate::lib::*;

use crate::{FsmBackend, FsmEventQueue};
use crate::{FsmBackend, FsmEventQueue, FsmEventQueueSender, lib::*};

/// The internal event type that also allows stopping or starting the machine.
pub enum FsmEvent<E> {
Expand All @@ -25,16 +23,26 @@ impl<E> Debug for FsmEvent<E> where E: Debug {
}
}

impl<E> AsRef<str> for FsmEvent<E> where E: AsRef<str> {
fn as_ref(&self) -> &str {
match self {
FsmEvent::Start => "Fsm::Start",
FsmEvent::Stop => "Fsm::Stop",
FsmEvent::Event(e) => e.as_ref()
}
}
}

pub type FsmRegionId = usize;

/// The context that is given to all of the guards and actions.
pub struct EventContext<'a, TFsm, Q> where TFsm: FsmBackend, Q: FsmEventQueue<TFsm> {
pub struct EventContext<'a, TFsm, Q> where TFsm: FsmBackend, Q: FsmEventQueueSender<TFsm> {
pub context: &'a mut TFsm::Context,
pub queue: &'a mut Q,
pub region: FsmRegionId
}

impl<'a, TFsm, Q> Deref for EventContext<'a, TFsm, Q> where TFsm: FsmBackend, Q: FsmEventQueue<TFsm>
impl<'a, TFsm, Q> Deref for EventContext<'a, TFsm, Q> where TFsm: FsmBackend, Q: FsmEventQueueSender<TFsm>
{
type Target = <TFsm as FsmBackend>::Context;

Expand All @@ -43,7 +51,7 @@ impl<'a, TFsm, Q> Deref for EventContext<'a, TFsm, Q> where TFsm: FsmBackend, Q:
}
}

impl<'a, TFsm: FsmBackend, Q> DerefMut for EventContext<'a, TFsm, Q> where TFsm: FsmBackend, Q: FsmEventQueue<TFsm> {
impl<'a, TFsm: FsmBackend, Q> DerefMut for EventContext<'a, TFsm, Q> where TFsm: FsmBackend, Q: FsmEventQueueSender<TFsm> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.context
}
Expand Down
7 changes: 5 additions & 2 deletions finny/src/fsm/fsm_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ use crate::FsmEventQueueVec;
pub trait FsmFactory {
type Fsm: FsmBackend;

/// For submachines, for use with codegen.
fn new_submachine_backend(backend: FsmBackendImpl<Self::Fsm>) -> FsmResult<Self> where Self: Sized;

/// Build a new frontend for the FSM with all the environmental services provided by the caller.
fn new_with<Q, I>(context: <Self::Fsm as FsmBackend>::Context, queue: Q, inspect: I) -> FsmResult<FsmFrontend<Self::Fsm, Q, I>>
where Q: FsmEventQueue<Self::Fsm>, I: Inspect<Self::Fsm>
where Q: FsmEventQueue<Self::Fsm>, I: Inspect
{
let frontend = FsmFrontend {
queue,
Expand All @@ -22,7 +25,7 @@ pub trait FsmFactory {

/// Build a new frontend for the FSM with a `FsmEventQueueVec` queue and no logging.
#[cfg(feature="std")]
fn new(context: <Self::Fsm as FsmBackend>::Context) -> FsmResult<FsmFrontend<Self::Fsm, FsmEventQueueVec<Self::Fsm>, InspectNull<Self::Fsm>>> {
fn new(context: <Self::Fsm as FsmBackend>::Context) -> FsmResult<FsmFrontend<Self::Fsm, FsmEventQueueVec<Self::Fsm>, InspectNull>> {
let frontend = FsmFrontend {
queue: FsmEventQueueVec::new(),
backend: FsmBackendImpl::new(context)?,
Expand Down
28 changes: 17 additions & 11 deletions finny/src/fsm/fsm_impl.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{Inspect, lib::*};
use crate::{DispatchContext, Inspect, lib::*};
use crate::{FsmBackend, FsmEvent, FsmEventQueue, FsmResult, FsmStates};

use super::FsmStateFactory;
Expand All @@ -8,14 +8,14 @@ use super::FsmStateFactory;
pub struct FsmBackendImpl<F: FsmBackend> {
pub context: <F as FsmBackend>::Context,
pub states: <F as FsmBackend>::States,
pub current_states: <<F as FsmBackend>::States as FsmStates>::CurrentState
pub current_states: <<F as FsmBackend>::States as FsmStates<F>>::CurrentState
}

impl<F: FsmBackend> FsmBackendImpl<F> {
pub fn new(context: <F as FsmBackend>::Context) -> FsmResult<Self> {

let states = <<F as FsmBackend>::States>::new_state(&context)?;
let current_states = <<<F as FsmBackend>::States as FsmStates>::CurrentState>::default();
let current_states = <<<F as FsmBackend>::States as FsmStates<F>>::CurrentState>::default();

let backend = FsmBackendImpl::<F> {
context,
Expand All @@ -30,7 +30,7 @@ impl<F: FsmBackend> FsmBackendImpl<F> {
&self.context
}

pub fn get_current_states(&self) -> <<F as FsmBackend>::States as FsmStates>::CurrentState {
pub fn get_current_states(&self) -> <<F as FsmBackend>::States as FsmStates<F>>::CurrentState {
self.current_states
}

Expand All @@ -52,19 +52,19 @@ impl<F: FsmBackend> Deref for FsmBackendImpl<F> {
/// 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<F, Q, I>
where F: FsmBackend, Q: FsmEventQueue<F>, I: Inspect<F>
where F: FsmBackend, Q: FsmEventQueue<F>, I: Inspect
{
pub backend: FsmBackendImpl<F>,
pub queue: Q,
pub inspect: I
}

impl<F, Q, I> FsmFrontend<F, Q, I>
where F: FsmBackend, Q: FsmEventQueue<F>, I: Inspect<F>
where F: FsmBackend, Q: FsmEventQueue<F>, I: Inspect
{
/// Start the FSM, initiates the transition to the initial state.
pub fn start(&mut self) -> FsmResult<()> {
Self::dispatch_single_event(self, &FsmEvent::Start)
Self::dispatch_single_event(self, FsmEvent::Start)
}

/// Dispatch this event and run it to completition.
Expand All @@ -73,7 +73,7 @@ impl<F, Q, I> FsmFrontend<F, Q, I>
{
let ev = event.into();
let ev = FsmEvent::Event(ev);
Self::dispatch_single_event(self, &ev)?;
Self::dispatch_single_event(self, ev)?;

while let Some(ev) = self.queue.dequeue() {
let ev: <F as FsmBackend>::Events = ev.into();
Expand All @@ -84,13 +84,19 @@ impl<F, Q, I> FsmFrontend<F, Q, I>
}

/// Dispatch only this event, do not run it to completition.
pub fn dispatch_single_event(&mut self, event: &FsmEvent<<F as FsmBackend>::Events>) -> FsmResult<()> {
F::dispatch_event(self, event)
pub fn dispatch_single_event(&mut self, event: FsmEvent<<F as FsmBackend>::Events>) -> FsmResult<()> {
let dispatch_ctx = DispatchContext {
backend: &mut self.backend,
inspect: &mut self.inspect,
queue: &mut self.queue
};

F::dispatch_event(dispatch_ctx, event)
}
}

impl<F, Q, I> Deref for FsmFrontend<F, Q, I>
where F: FsmBackend, Q: FsmEventQueue<F>, I: Inspect<F>
where F: FsmBackend, Q: FsmEventQueue<F>, I: Inspect
{
type Target = FsmBackendImpl<F>;

Expand Down
Loading

0 comments on commit 9c7c82f

Please sign in to comment.