Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved inspection, docs, thread-sharable queue #15

Merged
merged 13 commits into from
Oct 10, 2021
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
40 changes: 40 additions & 0 deletions finny/src/fsm/inspect/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use core::any::Any;

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

#[derive(Debug)]
pub enum InspectFsmEvent<F> where F: FsmBackend {
StateEnter(<<F as FsmBackend>::States as FsmStates<F>>::StateKind),
StateExit(<<F as FsmBackend>::States as FsmStates<F>>::StateKind)
}

impl<F> Clone for InspectFsmEvent<F> where F: FsmBackend {
fn clone(&self) -> Self {
match self {
Self::StateEnter(arg0) => Self::StateEnter(arg0.clone()),
Self::StateExit(arg0) => Self::StateExit(arg0.clone()),
}
}
}

pub trait Inspect: InspectEvent {

fn new_event<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<F: FsmBackend + Any>(&self, event: InspectFsmEvent<F>);
}
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 + 'static {
rudib marked this conversation as resolved.
Show resolved Hide resolved
/// 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
1 change: 1 addition & 0 deletions finny/src/fsm/states.rs
Original file line number Diff line number Diff line change
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::<F>::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::<F>::StateExit(kind);
context.inspect.on_event(ev);
}
}

fn fsm_state() -> <<F as FsmBackend>::States as FsmStates<F>>::StateKind;
Expand Down
117 changes: 117 additions & 0 deletions finny/src/inspect/chain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use crate::{FsmBackend, FsmBackendImpl, FsmEvent, Inspect, InspectEvent, InspectFsmEvent};
use core::any::Any;
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<F: FsmBackend + Any>(&self, event: InspectFsmEvent<F>) {
self.a.on_event(event.clone());
self.b.on_event(event);
}
}
Loading