• Stars
    star
    201
  • Rank 194,491 (Top 4 %)
  • Language
    Rust
  • License
    Apache License 2.0
  • Created almost 6 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

A State Machine Language DSL procedual macro for Rust

smlang: A no_std State Machine Language DSL in Rust

Build Status Documentation

A state machine language DSL based on the syntax of Boost-SML.

Aim

The aim of this DSL is to facilitate the use of state machines, as they quite fast can become overly complicated to write and get an overview of.

Transition DSL

The DSL is defined as follows:

statemachine!{
    transitions: {
        *SrcState1 + Event1 [ guard1 ] / action1 = DstState2, // * denotes starting state
        SrcState2 + Event2 [ guard2 ] / action2 = DstState1,
    }
    // ...
}

Where guard and action are optional and can be left out. A guard is a function which returns true if the state transition should happen, and false if the transition should not happen, while action are functions that are run during the transition which are guaranteed to finish before entering the new state.

This implies that any state machine must be written as a list of transitions.

The DSL supports wildcards and pattern matching for input states similar to rust pattern matching:

statemachine!{
    transitions: {
        *State1 | State3 + ToState2 = State2,
        State1 | State2 + ToState3 = State3,
        _ + ToState4 = State4,
        State4 + ToState1 = State1,
    }
    // ...
}

Which is equivalent to:

statemachine!{
    transitions: {
        *State1 + ToState2 = State2,
        State3 + ToState2 = State2,

        State1 + ToState3 = State3,
        State2 + ToState3 = State3,

        State1 + ToState4 = State4,
        State2 + ToState4 = State4,
        State3 + ToState4 = State4,
        State4 + ToState4 = State4,

        State4 + ToState1 = State1,
    }
    // ...
}

See example examples/input_state_pattern_match.rs for a usage example.

State machine context

The state machine needs a context to be defined. The StateMachineContext is generated from the statemachine! proc-macro and is what implements guards and actions, and data that is available in all states within the state machine and persists between state transitions:

statemachine!{
    transitions: {
        State1 + Event1 = State2,
    }
    // ...
}

pub struct Context;

impl StateMachineContext for Context {}

fn main() {
    let mut sm = StateMachine::new(Context);

    // ...
}

See example examples/context.rs for a usage example.

State data

Any state may have some data associated with it:

pub struct MyStateData(pub u32);

statemachine!{
    transitions: {
        State1(MyStateData) + Event1 = State2,
    }
    // ...
}

See example examples/state_with_data.rs for a usage example.

If the starting state contains data, this data must be provided after the context when creating a new machine.

pub struct MyStateData(pub u32);

statemachine!{
    transitions: {
        State2 + Event2 / action = State1(MyStateData),
        *State1(MyStateData) + Event1 = State2,
        // ...
    }
    // ...
}

// ...

let mut sm = StateMachine::new(Context, MyStateData(42));

State data may also have associated lifetimes which the statemachine! macro will pick up and add the States enum and StateMachine structure. This means the following will also work:

pub struct MyStateData<'a>(&'a u32);

statemachine! {
    transitions: {
        *State1 + Event1 / action = State2,
        State2(MyStateData<'a>) + Event2 = State1,
        // ...
    }
    // ...
}

See example examples/state_with_reference_data.rs for a usage example.

Event data

Data may be passed along with an event into the guard and action:

pub struct MyEventData(pub u32);

statemachine!{
    transitions: {
        State1 + Event1(MyEventData) [guard] = State2,
    }
    // ...
}

Event data may also have associated lifetimes which the statemachine! macro will pick up and add the Events enum. This means the following will also work:

pub struct MyEventData<'a>(pub &'a u32);

statemachine!{
    transitions: {
        State1 + Event1(MyEventData<'a>) [guard1] = State2,
        State1 + Event2(&'a [u8]) [guard2] = State3,
    }
    // ...
}

See example examples/event_with_data.rs for a usage example.

Guard and Action syntax

See example examples/guard_action_syntax.rs for a usage-example.

Async Guard and Action

See example examples/async.rs for a usage-example.

State Machine Examples

Here are some examples of state machines converted from UML to the State Machine Language DSL. Runnable versions of each example is available in the examples folder. The .pngs are generated with the graphviz feature.

Linear state machine

alt text

DSL implementation:

statemachine!{
    transitions: {
        *State1 + Event1 = State2,
        State2 + Event2 = State3,
    }
}

This example is available in ex1.rs.

Looping state machine

alt text

DSL implementation:

statemachine!{
    transitions: {
        *State1 + Event1 = State2,
        State2 + Event2 = State3,
        State3 + Event3 = State2,
    }
}

This example is available in ex2.rs.

Using guards and actions

alt text

DSL implementation:

statemachine!{
    transitions: {
        *State1 + Event1 [guard] / action = State2,
    }
}

This example is available in ex3.rs.

Helpers

Auto-derive certain traits for states and events

Setting derive_events and derive_states fields to an array of traits adds a derive expression to Events and States enums respectively. To derive Display, use derive_more::Display.

use core::Debug;
use derive_more::Display;
// ...
statemachine!{
    derive_states: [Debug, Display],
    derive_events: [Debug, Display],
    transitions: {
        *State1 + Event1 = State2,
    }
}

// ...

println!("Current state: {}", sm.state().unwrap());
println!("Expected state: {}", States::State1);
println!("Sending event: {}", Events::Event1);

// ...

Contributors

List of contributors in alphabetical order:


License

Licensed under either of

at your option.

More Repositories

1

crect

A C++, compile-time, reactive RTOS for the Stack Resource Policy based Real-Time For the Masses kernel
C++
187
star
2

fugit

`fugit` provides a comprehensive library of `Duration` and `Instant` for the handling of time in embedded systems, doing all it can at compile time.
Rust
52
star
3

Comparison-of-UKF-CKF-EKF

MATLAB
49
star
4

biquad-rs

A Rust library for digital second order IIR filtrers, also known as biquads
Rust
48
star
5

pico-probe

Rust
35
star
6

trustflight_firmware

A multirotor flight controller firmware you can trust!
Rust
33
star
7

micro-bmp

A small Black Magic Probe based on the pinout of the STLink V2 support firmware
32
star
8

ovio_core

Python
28
star
9

panic-halt

Sets the panicking behavior to halt
Rust
22
star
10

kfly_firmware

A ROS supported multirotor controller software for the KFly boards
C
21
star
11

oxidize2020-rtic

Rust
15
star
12

rusty-tracker

Rust
14
star
13

RTFMplusplus

A proof-of-concept C++ implementation of the Stack Resource Policy (SRP) based Real-Time For the Masses (RTFM) kernel
C++
12
star
14

KFly_STM32F4

C
9
star
15

rust-embedded-example-project

Rust
8
star
16

micro-dap

7
star
17

rp2040-monotonic

RTIC Monotonic implementation based on RP2040's Timer peripheral
Rust
6
star
18

panic-reset

A Rust panic handler for #![no_std] which resets the chip on panic
Rust
6
star
19

rtfm_workshop

Rust
6
star
20

test_rtic_0.6

Rust
4
star
21

lbfgs-rs

Rust
4
star
22

async-nrf52832-hal

Rust
4
star
23

kicad_libs

Python
4
star
24

async-rtic-talk

HTML
3
star
25

ascon-vs-chacha

Rust
3
star
26

fasthosting

Rust
3
star
27

stm32l4x2-flashloader

Rust
3
star
28

ptp-sync

MATLAB
3
star
29

const-register-interrupts-experiments

Rust
3
star
30

crkbd-shockburst

Rust
2
star
31

KFlyConfig_V1.0

The first version of the KFly Config program, not used anymore.
C#
2
star
32

KFly_old

Multirotor controller software
C
2
star
33

msf2_interface

C++
2
star
34

KFlyConfig_V2

Java
2
star
35

ssmarshal

Rust
2
star
36

esl

C++
2
star
37

embedded-dtls

Rust
2
star
38

mqtt-tools

Rust
2
star
39

adkalmanfilter

C++
2
star
40

softdevice-template

Rust
2
star
41

ChibiOS_Debug_Print

C
1
star
42

curve25519-cortex-m4

Rust
1
star
43

ovio_core_firmware

Python
1
star
44

trustflight_hardware

1
star
45

simple-bitrange-rs

Rust
1
star
46

history-buffer

Rust
1
star
47

probe-debug-board

1
star
48

crect_dshot_stm32

C++
1
star
49

kicad-6-pkgbuild

Shell
1
star
50

kvio_core

Python
1
star
51

ethernet-testing

Rust
1
star