Skip to content

Commit

Permalink
Merge pull request #15 from hashmismatch/inspect_shared_queue
Browse files Browse the repository at this point in the history
Improved inspection, docs, thread-sharable queue
  • Loading branch information
rudib authored Oct 10, 2021
2 parents 0c592d3 + ea8a1d2 commit 2f90268
Show file tree
Hide file tree
Showing 19 changed files with 601 additions and 85 deletions.
6 changes: 4 additions & 2 deletions finny/src/fsm/fsm_factory.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::{FsmBackend, FsmBackendImpl, FsmEventQueue, FsmFrontend, FsmResult, FsmTimers, FsmTimersNull, Inspect, InspectNull};
use crate::{FsmBackend, FsmBackendImpl, FsmEventQueue, FsmFrontend, FsmResult, FsmTimers, FsmTimersNull, Inspect};

#[cfg(feature="std")]
use crate::{FsmEventQueueVec, timers::std::TimersStd};
Expand Down Expand Up @@ -26,7 +26,9 @@ pub trait FsmFactory {

/// Build a new frontend for the FSM with a `FsmEventQueueVec` queue, `TimersStd` for timers and no logging.
#[cfg(feature="std")]
fn new(context: <Self::Fsm as FsmBackend>::Context) -> FsmResult<FsmFrontend<Self::Fsm, FsmEventQueueVec<Self::Fsm>, InspectNull, TimersStd<Self::Fsm>>> {
fn new(context: <Self::Fsm as FsmBackend>::Context) -> FsmResult<FsmFrontend<Self::Fsm, FsmEventQueueVec<Self::Fsm>, crate::inspect::null::InspectNull, TimersStd<Self::Fsm>>> {
use crate::inspect::null::InspectNull;

let frontend = FsmFrontend {
queue: FsmEventQueueVec::new(),
backend: FsmBackendImpl::new(context)?,
Expand Down
4 changes: 4 additions & 0 deletions finny/src/fsm/fsm_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ impl<F: FsmBackend> DerefMut for FsmBackendImpl<F> {
}
}

pub trait FsmBackendResetSubmachine<F: FsmBackend, FSub> {
fn reset<I>(backend: &mut FsmBackendImpl<F>, inspect_event_ctx: &mut I) where I: Inspect;
}


/// The frontend of a state machine which also includes environmental services like queues
/// and inspection. The usual way to use the FSM.
Expand Down
32 changes: 32 additions & 0 deletions finny/src/fsm/inspect/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use core::fmt::Debug;
use core::any::Any;

use crate::{FsmBackend, FsmBackendImpl, FsmEvent, FsmStates};

#[derive(Debug, Clone)]
pub enum InspectFsmEvent<S> where S: Debug + Clone {
StateEnter(S),
StateExit(S)
}

pub trait Inspect: InspectEvent {

fn new_event<F: FsmBackend>(&self, event: &FsmEvent<<F as FsmBackend>::Events, <F as FsmBackend>::Timers>, fsm: &FsmBackendImpl<F>) -> Self;
fn event_done<F: FsmBackend>(self, fsm: &FsmBackendImpl<F>);

fn for_transition<T>(&self) -> Self;
fn for_sub_machine<FSub: FsmBackend>(&self) -> Self;
fn for_timer<F>(&self, timer_id: <F as FsmBackend>::Timers) -> Self where F: FsmBackend;

fn on_guard<T>(&self, guard_result: bool);
fn on_state_enter<S>(&self);
fn on_state_exit<S>(&self);
fn on_action<S>(&self);

fn on_error<E>(&self, msg: &str, error: &E) where E: core::fmt::Debug;
fn info(&self, msg: &str);
}

pub trait InspectEvent {
fn on_event<S: Any + Debug + Clone>(&self, event: &InspectFsmEvent<S>);
}
4 changes: 2 additions & 2 deletions finny/src/fsm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ mod queue;
mod states;
mod transitions;
mod tests_fsm;
mod inspect;
mod dispatch;
mod timers;
mod inspect;

pub use self::events::*;
pub use self::fsm_factory::*;
Expand Down Expand Up @@ -39,7 +39,7 @@ pub type FsmDispatchResult = FsmResult<()>;

/// Finite State Machine backend. Handles the dispatching, the types are
/// defined by the code generator.
pub trait FsmBackend where Self: Sized {
pub trait FsmBackend where Self: Sized + Debug {
/// The machine's context that is shared between its constructors and actions.
type Context;
/// The type that holds the states of the machine.
Expand Down
75 changes: 75 additions & 0 deletions finny/src/fsm/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,74 @@ mod queue_vec {
#[cfg(feature = "std")]
pub use self::queue_vec::*;

#[cfg(feature = "std")]
mod queue_vec_shared {
use std::sync::{Arc, Mutex};

use crate::FsmError;

use super::*;

/// An unbound event queue that uses `VecDeque`.
pub struct FsmEventQueueVecShared<F: FsmBackend> {
inner: Inner<F>
}

impl<F> Clone for FsmEventQueueVecShared<F> where F: FsmBackend {
fn clone(&self) -> Self {
Self { inner: Inner { queue: self.inner.queue.clone() } }
}
}

struct Inner<F: FsmBackend> {
queue: Arc<Mutex<VecDeque<<F as FsmBackend>::Events>>>
}

impl<F: FsmBackend> FsmEventQueueVecShared<F> {
pub fn new() -> Self {
let q = VecDeque::new();
let inner = Inner {
queue: Arc::new(Mutex::new(q))
};
FsmEventQueueVecShared {
inner
}
}
}

impl<F: FsmBackend> FsmEventQueue<F> for FsmEventQueueVecShared<F> {
fn dequeue(&mut self) -> Option<<F as FsmBackend>::Events> {
if let Ok(mut q) = self.inner.queue.lock() {
q.pop_front()
} else {
None
}
}

fn len(&self) -> usize {
if let Ok(q) = self.inner.queue.lock() {
q.len()
} else {
0
}
}
}

impl<F: FsmBackend> FsmEventQueueSender<F> for FsmEventQueueVecShared<F> {
fn enqueue<E: Into<<F as FsmBackend>::Events>>(&mut self, event: E) -> FsmResult<()> {
if let Ok(mut q) = self.inner.queue.lock() {
q.push_back(event.into());
Ok(())
} else {
Err(FsmError::QueueOverCapacity)
}
}
}
}

#[cfg(feature = "std")]
pub use self::queue_vec_shared::*;

mod queue_array {
use arraydeque::{Array, ArrayDeque};

Expand Down Expand Up @@ -169,6 +237,7 @@ impl<'a, Q, F, FSub> FsmEventQueueSender<FSub> for FsmEventQueueSub<'a, Q, F, FS
}



#[cfg(test)]
use super::tests_fsm::TestFsm;

Expand All @@ -185,6 +254,12 @@ fn test_array() {
test_queue(queue);
}

#[test]
fn test_dequeue_vec_shared() {
let queue = FsmEventQueueVecShared::<TestFsm>::new();
test_queue(queue);
}

#[cfg(test)]
fn test_queue<Q: FsmEventQueue<TestFsm>>(mut queue: Q) {
use super::tests_fsm::{Events, EventA};
Expand Down
5 changes: 3 additions & 2 deletions finny/src/fsm/states.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ use crate::FsmResult;
/// The implementation should hold all of the FSM's states as fields.
pub trait FsmStates<TFsm>: FsmStateFactory<TFsm> where TFsm: FsmBackend {
/// The enum type for all states that's used as the "current state" field in the FSM's backend.
type StateKind: Clone + Copy + Debug + PartialEq;
type StateKind: Clone + Copy + Debug + PartialEq + 'static;
/// An array of current states for the machine, one for each region.
type CurrentState: Clone + Copy + Debug + Default + AsRef<[FsmCurrentState<Self::StateKind>]> + AsMut<[FsmCurrentState<Self::StateKind>]>;
type CurrentState: Clone + Copy + Debug + Default + AsRef<[FsmCurrentState<Self::StateKind>]> + AsMut<[FsmCurrentState<Self::StateKind>]> + 'static;
}

/// The current state of the FSM.
Expand Down Expand Up @@ -49,6 +49,7 @@ pub trait FsmStateFactory<TFsm> where Self: Sized, TFsm: FsmBackend {
fn new_state(context: &<TFsm as FsmBackend>::Context) -> FsmResult<Self>;
}

/// The implementation of a simple state factory, where the state supports Default.
impl<TState, TFsm> FsmStateFactory<TFsm> for TState where TState: Default, TFsm: FsmBackend {
fn new_state(_context: &<TFsm as FsmBackend>::Context) -> FsmResult<Self> {
Ok(Default::default())
Expand Down
1 change: 1 addition & 0 deletions finny/src/fsm/tests_fsm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub struct StateA;
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct EventA { pub n: usize }

#[derive(Debug)]
pub struct TestFsm;

#[derive(Default)]
Expand Down
22 changes: 21 additions & 1 deletion finny/src/fsm/transitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
use crate::{FsmBackendImpl, FsmDispatchResult, FsmEventQueueSub, FsmTimers, FsmTimersSub, lib::*};
use crate::{DispatchContext, EventContext, FsmBackend, FsmCurrentState, FsmEvent, FsmEventQueue, FsmRegionId, FsmStateTransitionAsMut, FsmStates, Inspect};

use super::inspect::InspectFsmEvent;

/// A state's entry and exit actions.
pub trait FsmState<F: FsmBackend> {
pub trait FsmState<F: FsmBackend> where Self: Sized {
/// Action that is executed whenever this state is being entered.
fn on_entry<'a, Q: FsmEventQueue<F>>(&mut self, context: &mut EventContext<'a, F, Q>);
/// Action that is executed whenever this state is being exited.
Expand All @@ -19,6 +21,15 @@ pub trait FsmState<F: FsmBackend> {
queue: context.queue
};

// inspection
{
context.inspect.on_state_enter::<Self>();

let kind = <Self>::fsm_state();
let ev = InspectFsmEvent::StateEnter(kind);
context.inspect.on_event(&ev);
}

let state: &mut Self = context.backend.states.as_mut();
state.on_entry(&mut event_context);
}
Expand All @@ -34,6 +45,15 @@ pub trait FsmState<F: FsmBackend> {

let state: &mut Self = context.backend.states.as_mut();
state.on_exit(&mut event_context);

// inspection
{
context.inspect.on_state_exit::<Self>();

let kind = <Self>::fsm_state();
let ev = InspectFsmEvent::StateExit(kind);
context.inspect.on_event(&ev);
}
}

fn fsm_state() -> <<F as FsmBackend>::States as FsmStates<F>>::StateKind;
Expand Down
118 changes: 118 additions & 0 deletions finny/src/inspect/chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent};
use core::any::Any;
use core::fmt::Debug;
use super::{null::InspectNull};


pub struct InspectChain<A, B>
where A: Inspect, B: Inspect
{
pub a: A,
pub b: B
}

impl<A, B> InspectChain<A, B>
where A: Inspect, B: Inspect
{
pub fn new_pair(inspect_a: A, inspect_b: B) -> Self {
InspectChain {
a: inspect_a,
b: inspect_b
}
}

pub fn add_inspect<C: Inspect>(self, inspect: C) -> InspectChain<InspectChain<A, B>, C> {
InspectChain {
a: self,
b: inspect
}
}
}

impl<A> InspectChain<A, InspectNull>
where A: Inspect
{
pub fn new_chain(inspect: A) -> Self {
InspectChain {
a: inspect,
b: InspectNull::new()
}
}
}


impl<A, B> Inspect for InspectChain<A, B>
where A: Inspect, B: Inspect
{
fn new_event<F: FsmBackend>(&self, event: &FsmEvent<<F as FsmBackend>::Events, <F as FsmBackend>::Timers>, fsm: &FsmBackendImpl<F>) -> Self {
Self {
a: self.a.new_event(event, fsm),
b: self.b.new_event(event, fsm)
}
}

fn event_done<F: FsmBackend>(self, fsm: &FsmBackendImpl<F>) {
self.a.event_done(fsm);
self.b.event_done(fsm);
}

fn for_transition<T>(&self) -> Self {
Self {
a: self.a.for_transition::<T>(),
b: self.b.for_transition::<T>()
}
}

fn for_sub_machine<FSub: FsmBackend>(&self) -> Self {
Self {
a: self.a.for_sub_machine::<FSub>(),
b: self.b.for_sub_machine::<FSub>()
}
}

fn for_timer<F>(&self, timer_id: <F as FsmBackend>::Timers) -> Self where F: FsmBackend {
Self {
a: self.a.for_timer::<F>(timer_id.clone()),
b: self.b.for_timer::<F>(timer_id)
}
}

fn on_guard<T>(&self, guard_result: bool) {
self.a.on_guard::<T>(guard_result);
self.b.on_guard::<T>(guard_result);
}

fn on_state_enter<S>(&self) {
self.a.on_state_enter::<S>();
self.b.on_state_enter::<S>();
}

fn on_state_exit<S>(&self) {
self.a.on_state_exit::<S>();
self.b.on_state_exit::<S>();
}

fn on_action<S>(&self) {
self.a.on_action::<S>();
self.b.on_action::<S>();
}

fn on_error<E>(&self, msg: &str, error: &E) where E: core::fmt::Debug {
self.a.on_error(msg, error);
self.b.on_error(msg, error);
}

fn info(&self, msg: &str) {
self.a.info(msg);
self.b.info(msg);
}
}

impl<A, B> InspectEvent for InspectChain<A, B>
where A: Inspect, B: Inspect
{
fn on_event<S: Any + Debug + Clone>(&self, event: &InspectFsmEvent<S>) {
self.a.on_event(event);
self.b.on_event(event);
}
}
Loading

0 comments on commit 2f90268

Please sign in to comment.