salut! Je m'appelle Alexander, je travaille en tant qu'ingénieur logiciel microcontrôleur.
J'écris en C / C ++, et je préfère les plus, car je crois en leur inévitabilité évolutive en embarqué.
Le monde des logiciels embarqués, le langage C ++ se développe de manière dynamique, il est donc important pour les développeurs de se tenir au courant et de maintenir leurs compétences et développements à jour.
J'essaie de suivre ce message évident, puisque les célestes, les principaux programmeurs et consultants C ++ partagent généreusement leur expérience et leurs idées sur différentes plateformes (par exemple, ici , ou ici ).
Il y a quelque temps, j'ai regardé un discours puissant de Sergei Fedorov sur la construction d'une machine à états finis avec une table de transition sur des modèles.
Si tout à coup: "qu'est-ce qu'une machine d'état?"
L'une des idées du rapport est de définir des états, des événements et des actions via des types personnalisés, et d'implémenter la table de transition via un paramètre de modèle, je suis très
impressionné
// Transition table definition
using transitions =
transition_table<
/* State Event Next */
tr< initial, start, running >,
tr< running, stop, terminated >>;
};
// State machine object
using minimal = state_machine<transitions>;
minimal fsm;
//...and then call
fsm.process_event(start{});
fsm.process_event(stop{});
Et si l'on ajoute à cela le transfert d'une partie de la fonctionnalité du code au moment de la compilation, la sécurité des threads déclarée par l'auteur, l'expressivité, la lisibilité du code et la vitesse de construction améliorées par rapport à Boost :: MSM, le modèle d'en-tête seul de la bibliothèque, alors j'ai décidé de la prendre .
Voici juste une tentative pour construire et exécuter même l' exemple le plus simple sur STM-ke terminé avec le langage grossier du compilateur: "impossible d'utiliser 'typeid' avec" -fno-rtti "et" gestion des exceptions désactivée ".
, . , RTTI , -fno-cxa-atexit, -fno-threadsafe-static. --specs=nano.specs ( ++ newlib-nano), --specs=nosys.specs ( ).
, , - . , typeid exceptions, - too much.
, , RTTI, throw .
. gcc-arm-none-eabi-9-2020-q2-update -O3, 200.
, - " ".
, STM, 1, , , , , .
, , . , - " " - extra light embedded FSM .
:
, .
header only
, , - .
- , . , .
:
/State
struct StateBase{};
template <base_t N, typename Action = void>
struct State : StateBase{
static constexpr base_t idx = N;
using action_t = Action;
};
base_t - , . unsigned int.
- , , - , action_t.
idx .
/Event
struct EventBase{};
template <base_t N>
struct Event : EventBase{
static constexpr base_t idx = N;
};
, .
:
Action
struct action{
void operator()(void){
// do something
};
, operator() , .
:
Guard
enum class Guard : base_t{
OFF,
CONDITION_1,
CONDITION_2,
//etc.
};
- , /transition-a. , . . , , , . Up to you.
, . .
:
Transition
struct TrBase{};
template <typename Source,
typename Event,
typename Target,
typename Action,
Guard G,
class =
std::enable_if_t<std::is_base_of_v<StateBase, Source>&&
std::is_base_of_v<EventBase, Event> &&
std::is_base_of_v<StateBase, Target>>
>
struct Tr : TrBase{
using source_t = Source;
using event_t = Event;
using target_t = Target;
using action_t = Action;
static constexpr Guard guard = G;
};
Tr . - Source, Event, Target, Guard.
. .
:
Transition table
struct TransitionTableBase{};
template<typename... T>
struct TransitionTable : TransitionTableBase{
using test_t = typename NoDuplicates<Collection<T...>>::Result;
static_assert(std::is_same_v<test_t, Collection<T...>>,
"Repeated transitions");
using transition_p = type_pack<T...>;
using state_collection = typename NoDuplicates
<Collection<typename T::source_t... ,typename T::target_t...>
>::Result;
using event_collection = typename NoDuplicates
<Collection<typename T::event_t...>
>::Result;
using state_v = decltype(get_var(state_collection{}));
using event_v = decltype(get_var(event_collection{}));
using transition_v = std::variant<T...>;
};
, , . , .
TransitionTable /transition-, .
, . NoDuplicates Loki. test_t static_assert-e .
, static_assert , type_pack transition_p. type_pack, typelist.h. .
transition_p StateMachine.
, , . alias- state_collection event_collection .
?
- , , , .
std::variant ( ).
std::variant ( transition_v); state_v event_v .
. transition_v std::variant variadic pack (T...) TransitionTable.
state_v event_v
constexpr
template<typename... Types>
constexpr auto get_var (th::Collection<Types...>){
return std::variant<Types...>{};
}
StateMachine , .
- .
StateMachine , , .
transitions
template<typename Table>
class StateMachine{
//other stuff
private:
using map_type =
std::unordered_map < Key, transition_v, KeyHash, KeyEqual>;
Key key;
map_type transitions;
};
, . Unordered - . , , , .
Key :
Key
struct Key{
base_t state_idx = 0;
base_t event_idx = 0;
};
idx . , . typeid _cxa_demangle , , RTTI.
events
template<typename Table>
class StateMachine{
//other stuff
private:
using queue_type =
RingBufferPO2 <EVENT_STACK_SIZE, event_v, Atomic>;
queue_type events;
};
events - , . , . RingBufferPO2, ( !).
, StateMachine /state /guard:
state and guard
template<typename Table>
class StateMachine{
//other stuff
private:
state_v current_state;
Guard guard = Guard::OFF;
};
.
template<typename Table>
class StateMachine{
public:
using transition_pack = typename Table::transition_p;
StateMachine(){
set(transition_pack{});
}
// other stuff
};
set , , , transitions, :
set
template <class... Ts>
void set (type_pack<Ts...>){
(set_impl(just_type<Ts>{}), ...);
};
template <typename T>
void set_impl (just_type<T> t){
using transition = typename decltype(t)::type;
using state_t = typename transition::source_t;
using event_t = typename transition::event_t;
Guard g = transition::guard;
Key k;
k.state_idx = state_t::idx;
k.event_idx = event_t::idx;
transitions.insert( {k, transition{}} );
if (0 == key.state_idx) {
key.state_idx = k.state_idx;
guard = g;
current_state = state_t{};
}
}
, StateMachine , - .
:
: /state, /event, /action, /guard
/transition, source state, event, target state, guard.
. /transition-, .
TransitionTable, std::variant - , , StateMachine , .
(): , (idx), Key, transitions , , , , , /().
API , .
: fsm.on_event(event{}) ( fsm.on_event<Event>() ), fsm.push_event(event{}), , , fsm.process(). , - , fsm.state_action().
,
state action
template <typename... Args>
void state_action (const Args&... args){
state_v temp_v{current_state};
auto l = [&](const auto& arg){
using state_t = std::decay_t<decltype(arg)>;
using functor_t = typename state_t::action_t;
if constexpr (!std::is_same_v<functor_t, void>){
functor_t{}(args...);
}
};
std::visit(l, temp_v);
}
std::variant<State...> temp_v . , std::visit.
"" variant, , , (, void) , , .
, , , . , , . callable object.
on_event
template <typename Event,
class = std::enable_if_t<std::is_base_of_v<EventBase, Event>>>
void on_event(const Event& e){
Key k;
k.event_idx = e.idx;
k.state_idx = key.state_idx;
on_event_impl(k);
}
void on_event_impl (Key& k){
transition_v tr_var = transitions[k];
Key &ref_k = key;
Guard &ref_g = guard;
state_v &ref_state = current_state;
auto l = [&](const auto& arg){
using tr_t = std::decay_t<decltype(arg)>;
using functor_t = typename tr_t::action_t;
if ( GuardEqual{}(ref_g, tr_t::guard) ){
using target_t = typename tr_t::target_t;
ref_k.state_idx = target_t::idx;
ref_state = target_t{};
functor_t{}();
}
};
std::visit(l, tr_var);
}
, , , Key , on_event_impl(Key& k).
transitions std::variant<Tr...> tr_var. - , . std::visit c tr_var l, Tr , (target_t), (tr_t::guard) (functor_t) .
, c , functor_t, target_t (current_state), . .
push_event
template <unsigned int N>
void push_event (const Event<N>& e){
events.push_back(e);
}
.
set_guard
void set_guard (const Guard& g){
guard = g;
}
, .
process
void process (void){
state_action();
auto it = transitions.begin();
Key k;
k.state_idx = key.state_idx;
for (uint32_t i = 0; i != events.size(); ++i){
auto v = events.front();
auto l = [&](const auto& arg){
using event_t = std::decay_t<decltype(arg)>;
k.event_idx = event_t::idx;
it = transitions.find(k);
}
std::visit(l, v);
if ( it != transitions.end() ){
events.pop_front();
on_event_impl(k);
return;
} else {
events.push_back(v);
events.pop_front();
}
}
}
( void), , state_action().
, fsm.on_event(event{}).
, , . Event
template <base_t N, base_t Priority>
struct Event : EventBase{
static constexpr base_t idx = N;
static constexpr base_t pri = Priority;
};
, , , std::array<queue_t, PRIRITY_NUM>, . , , , , , .
, , , .
FSM , .
, ?
()
struct green_a {/*toogle green led every 50ms*/}
struct yellow_a {/*toogle yellow led every 50ms*/}
struct red_a {/*toogle red led every 50ms*/}
struct green_f {/*toogle green led every 150ms*/}
struct yellow_f {/*toogle yellow led every 150ms*/}
struct red_f {/*toogle red led every 150ms*/}
using STATE_A(green_s, green_f);
using STATE_A(yellow_s, yellow_f);
using STATE_A(red_s, red_f);
using EVENT(green_e);
using EVENT(yellow_e);
using EVENT(red_e);
using fsm_table = TransitionTable
<
Tr<green_s, yellow_e, yellow_s, yellow_a, Guard::NO_GUARD>,
Tr<yellow_s, red_e, red_s, red_a, Guard::NO_GUARD>,
Tr<red_s, green_e, green_s, green_a, Guard::NO_GUARD>
>;
int main(void){
//some other stuff
StateMachine<fsm_table> fsm;
fsm.push_event(red_e{});
fsm.push_event(yellow_e{});
fsm.push_event(green_e{});
while (1){
fsm.process();
}
}
color_a(ction) - ; color_f(unctor) - , , .
, , , . StateMachine<fsm_table> fsm. , while .
, . :
using even_t = Event<1, 15>;
using state_t = State<1, state_functor>;
, . - .
, constexpr , , . .
-
#define STATE_A(str, act) str = State<name(#str), act>
#define EVENT(str) str = Event<name(#str)>
constexpr base_t name (const char* n){
base_t res = 0;
for (base_t i = 0; n[i] != '\0'; i++){
char data = n[i];
for (base_t j = sizeof (char) * 8; j > 0; j--){
res = ((res ^ data) & 1) ? (res >> 1) ^ 0x8C : (res >> 1);
data >>= 1;
}
}
return res;
};
-O3 ( FSM) 6,8, HAL- - 14,4.
, , . , .
Ce sera formidable si la communauté signale des faux pas imminents et montre la voie à des améliorations. J'ose aussi espérer que quelqu'un choisira quelque chose d'utile dans le matériel.
Merci de votre attention!