Halo! Nama saya Alexander, saya bekerja sebagai insinyur perangkat lunak mikrokontroler.
Saya menulis dalam C / C ++, dan saya lebih suka plus, karena saya percaya pada keniscayaan evolusioner mereka yang tertanam.
Dunia perangkat lunak yang disematkan, bahasa C ++ berkembang secara dinamis, jadi penting bagi pengembang untuk mengikuti dan terus memperbarui keterampilan dan perkembangan mereka.
Saya mencoba untuk mengikuti pesan yang jelas ini, karena Celestials, programmer dan konsultan C ++ terkemuka dengan murah hati membagikan pengalaman dan ide mereka pada platform yang berbeda (misalnya, di sini , atau di sini ).
Beberapa waktu yang lalu saya menyaksikan ceramah yang kuat oleh Sergei Fedorov tentang membangun mesin keadaan terbatas dengan tabel transisi pada templat.
Jika tiba-tiba: "apa itu mesin negara?"
Mesin keadaan terbatas, atau FSM (mesin keadaan terbatas), adalah salah satu teknik yang paling banyak diminta dan populer dalam pemrograman MC. Pada saat yang singkat dan panduan praktis untuk memasak FSM saya pergi ke meninggalkan , tanah .
Salah satu gagasan dari laporan ini adalah untuk menentukan keadaan, peristiwa dan tindakan melalui jenis khusus, dan menerapkan tabel transisi melalui parameter kerangka, saya sangat
terkesan
// 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{});
Dan jika kita menambahkan transfer bagian dari fungsionalitas kode ke waktu kompilasi, keamanan utas yang dinyatakan oleh penulis, ekspresi, keterbacaan kode, dan kecepatan pembuatan meningkat dibandingkan dengan Boost :: MSM, model hanya header dari perpustakaan, lalu saya memutuskan untuk mengambilnya .
Berikut ini hanya upaya untuk membangun dan menjalankan bahkan contoh paling sederhana di STM-ke yang diakhiri dengan bahasa kotor kompiler: "tidak dapat menggunakan 'typeid' dengan" -fno-rtti "dan" penanganan pengecualian dinonaktifkan ".
, . , 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.
, , . , .
Akan lebih bagus jika komunitas menunjukkan fakups yang akan datang dan menunjukkan jalan untuk perbaikan. Saya juga berani berharap bahwa seseorang akan memilih sesuatu yang berguna untuk diri mereka sendiri dari materi tersebut.
Terima kasih atas perhatian Anda!