Чи існує типова схема впровадження машинної машини?


118

Нам необхідно реалізувати просту державну машину в C .
Чи найкращий спосіб перейти до стандартної заяви комутатора?
У нас є поточний стан (стан) і тригер для переходу.


switch(state)
{
  case STATE_1:
     state = DoState1(transition);
     break;
  case STATE_2:
     state = DoState2(transition);
     break;
}
...
DoState2(int transition)
{
   // Do State Work
   ...
   if(transition == FROM_STATE_2) {
     // New state when doing STATE 2 -> STATE 2
   }
   if(transition == FROM_STATE_1) {
    // New State when moving STATE 1 -> STATE 2
   }
   return new_state;
}

Чи є кращий спосіб для простих державних машин

EDIT: Для C ++ я думаю, що дорога може стати бібліотекою Boost Statechart . Однак це не допомагає C. Дозволяє зосередитися на справі використання C.


Відповіді:


134

Я вважаю за краще використовувати підхід, керований таблицею, для більшості державних машин:

typedef enum { STATE_INITIAL, STATE_FOO, STATE_BAR, NUM_STATES } state_t;
typedef struct instance_data instance_data_t;
typedef state_t state_func_t( instance_data_t *data );

state_t do_state_initial( instance_data_t *data );
state_t do_state_foo( instance_data_t *data );
state_t do_state_bar( instance_data_t *data );

state_func_t* const state_table[ NUM_STATES ] = {
    do_state_initial, do_state_foo, do_state_bar
};

state_t run_state( state_t cur_state, instance_data_t *data ) {
    return state_table[ cur_state ]( data );
};

int main( void ) {
    state_t cur_state = STATE_INITIAL;
    instance_data_t data;

    while ( 1 ) {
        cur_state = run_state( cur_state, &data );

        // do other program logic, run other state machines, etc
    }
}

Звичайно, це може бути розширено для підтримки декількох державних машин тощо. Дії переходу також можуть бути прийняті:

typedef void transition_func_t( instance_data_t *data );

void do_initial_to_foo( instance_data_t *data );
void do_foo_to_bar( instance_data_t *data );
void do_bar_to_initial( instance_data_t *data );
void do_bar_to_foo( instance_data_t *data );
void do_bar_to_bar( instance_data_t *data );

transition_func_t * const transition_table[ NUM_STATES ][ NUM_STATES ] = {
    { NULL,              do_initial_to_foo, NULL },
    { NULL,              NULL,              do_foo_to_bar },
    { do_bar_to_initial, do_bar_to_foo,     do_bar_to_bar }
};

state_t run_state( state_t cur_state, instance_data_t *data ) {
    state_t new_state = state_table[ cur_state ]( data );
    transition_func_t *transition =
               transition_table[ cur_state ][ new_state ];

    if ( transition ) {
        transition( data );
    }

    return new_state;
};

Підхід, керований таблицею, простіше у обслуговуванні та розширенні та простіший зіставлення діаграм станів.


Дуже приємний спосіб почати роботу, принаймні для мене початок. Одне зауваження, перший рядок run_state () має неслухняний ". цього не повинно бути там.
Атілла Філіз

2
Було б краще, якби ця відповідь також принаймні сказала б 2 слова про два інших підходи: "глобальний" метод з великим випадком перемикання, і розділення станів за допомогою моделі проектування стану, і дозволити кожній державі самостійно здійснювати свої переходи.
erikbwork

Привіт, я знаю, що ця публікація стара, але я сподіваюся, що отримаю свою відповідь :) Що, безумовно, має бути змінною inst_data_t? Цікаво, як змінити стани в перериваннях ... це хороший спосіб зберігати інформацію про оброблений переривання в цій змінній? Наприклад, зберігайте інформацію про натискання кнопки, щоб стан було змінено.
grongor

@GRoNGoR Мені здається, що ти маєш справу зі стаціонарною машиною, керованою подіями. Я думаю, ви могли б використовувати його для збереження даних про події.
Зімано

3
Дійсно приємний дотик, як визначено NUM_STATES.
Альбін Стіго

25

Можливо, ви побачили мою відповідь на інше питання C, де я згадав FSM! Ось як я це роблю:

FSM {
  STATE(x) {
    ...
    NEXTSTATE(y);
  }

  STATE(y) {
    ...
    if (x == 0) 
      NEXTSTATE(y);
    else 
      NEXTSTATE(x);
  }
}

Визначаються такі макроси

#define FSM
#define STATE(x)      s_##x :
#define NEXTSTATE(x)  goto s_##x

Це можна змінити відповідно до конкретного випадку. Наприклад, у вас може бути файл, FSMFILEякий ви хочете керувати своїм FSM, так що ви можете включити дію читання наступного символу в сам макрос:

#define FSM
#define STATE(x)         s_##x : FSMCHR = fgetc(FSMFILE); sn_##x :
#define NEXTSTATE(x)     goto s_##x
#define NEXTSTATE_NR(x)  goto sn_##x

тепер у вас є два типи переходів: один переходить у стан і читає новий символ, інший переходить у стан, не витрачаючи жодного вводу.

Ви також можете автоматизувати обробку EOF чимось на зразок:

#define STATE(x)  s_##x  : if ((FSMCHR = fgetc(FSMFILE) == EOF)\
                             goto sx_endfsm;\
                  sn_##x :

#define ENDFSM    sx_endfsm:

Хороша річ у цьому підході в тому, що ви можете безпосередньо перевести схему стану, яку ви малюєте, у робочий код, і, навпаки, ви можете легко намалювати діаграму стану з коду.

В інших методах реалізації FSM структура переходів закопується в керуючі структури (в той час, якщо, якщо, комутатор ...) і контролюється значенням змінних (типово stateзмінна), і це може бути складним завданням пов'язати хорошу діаграму з a перекручений код.

Я дізнався цю техніку із статті, що з’явилася у великому журналі «Комп’ютерна мова», який, на жаль, більше не публікується.


1
Принципово, хороший FSM - це все для читабельності. Це забезпечує хороший інтерфейс, а реалізація настільки ж хороша, як і отримується. Соромно, що в мові не існує рідної структури FSM. Я зараз бачу це як пізнє доповнення до C1X!
Kelden Cowan

3
Мені подобається такий підхід для вбудованих додатків. Чи є спосіб використовувати цей підхід із керованою подіями державною машиною?
ARF

13

Я також застосував табличний підхід. Однак є накладні витрати. Навіщо зберігати другий список покажчиків? Функція в C без () - вказівник const. Тож ви можете зробити:

struct state;
typedef void (*state_func_t)( struct state* );

typedef struct state
{
  state_func_t function;

  // other stateful data

} state_t;

void do_state_initial( state_t* );
void do_state_foo( state_t* );
void do_state_bar( state_t* );

void run_state( state_t* i ) {
    i->function(i);
};

int main( void ) {
    state_t state = { do_state_initial };

    while ( 1 ) {
        run_state( state );

        // do other program logic, run other state machines, etc
    }
}

Звичайно, залежно від коефіцієнта страху (тобто безпеки проти швидкості), ви можете перевірити, чи є дійсні вказівники. Для державних машин, що перевищують три або більше станів, підхід, наведений вище, повинен мати менше інструкцій, ніж еквівалентний підхід комутатора або таблиці. Ви навіть можете макро-ize як:

#define RUN_STATE(state_ptr_) ((state_ptr_)->function(state_ptr_))

Також на прикладі ОП я відчуваю, що є спрощення, яке слід зробити, коли продумувати / проектувати державну машину. Я не маю на увазі, що стан переходу повинен використовуватися для логіки. Кожна функція стану повинна бути спроможна виконувати свою задану роль без явного знання минулих станів. В основному ви розробляєте, як перейти зі стану, в якому перебуваєте, в інший стан.

Нарешті, не починайте проектувати державну машину на основі "функціональних" меж, використовуйте для цього підфункції. Замість цього розділіть штати, виходячи з того, коли вам доведеться чекати, коли щось станеться, перш ніж ви зможете продовжити. Це допоможе мінімізувати кількість разів, коли вам доведеться запустити державну машину, перш ніж отримати результат. Це може бути важливо під час запису функцій вводу / виводу або переривання обробників.

Також кілька плюсів і мінусів класичного твердження про перемикання:

Плюси:

  • це мова, тому це документально і зрозуміло
  • стани визначаються там, де вони називаються
  • може виконувати кілька станів в одному виклику функції
  • загальний для всіх станів код може бути виконаний до і після оператора перемикання

Мінуси:

  • може виконувати кілька станів в одному виклику функції
  • загальний для всіх станів код може бути виконаний до і після оператора перемикання
  • Реалізація перемикачів може бути повільною

Зверніть увагу на два атрибути, які є як про, так і проти. Я думаю, що перемикач дає можливість занадто великого обміну між станами, і взаємозалежність між станами може стати некерованою. Однак для невеликої кількості держав це може бути найбільш читабельним і досяжним.


10

Для простої машини стану просто використовуйте оператор перемикання та тип enum для вашої держави. Виконайте свої переходи всередині оператора перемикача на основі ваших даних. У реальній програмі ви, очевидно, змінили "if (input)", щоб перевірити свої точки переходу. Сподіваюся, це допомагає.

typedef enum
{
    STATE_1 = 0,
    STATE_2,
    STATE_3
} my_state_t;

my_state_t state = STATE_1;

void foo(char input)
{
    ...
    switch(state)
    {
        case STATE_1:
            if(input)
                state = STATE_2;
            break;
        case STATE_2:
            if(input)
                state = STATE_3;
            else
                state = STATE_1;
            break;
        case STATE_3:
            ...
            break;
    }
    ...
}

1
Можливо, варто поставити "стан" всередині функції та зробити її статичною.
Стів Мельников

2
@Steve Melnikoff: тільки якщо у вас є лише одна державна машина. Тримайте це поза функцією, і ви можете мати масив державних машин із власним станом.
Вікі

@Vicky: Одна функція може містити стільки машин стану, скільки вам потрібно, з масивом змінних стану, якщо потрібно, які можуть жити всередині функції (як статичні змінні), якщо вони не використовуються в іншому місці.
Стів Мельников

10

У програмі UML Distilated Мартіна Фоулера він заявляє (каламбур не призначений) у Розділі 10 Діаграми стану машини (моє наголос):

Діаграма стану може бути реалізована трьома основними способами: вкладений комутатор , шаблон стану та таблиці стану .

Давайте скористаємось спрощеним прикладом стану дисплея мобільного телефону:

введіть тут опис зображення

Вкладений перемикач

Фоулер подав приклад коду C #, але я адаптував його до свого прикладу.

public void HandleEvent(PhoneEvent anEvent) {
    switch (CurrentState) {
    case PhoneState.ScreenOff:
        switch (anEvent) {
        case PhoneEvent.PressButton:
            if (powerLow) { // guard condition
                DisplayLowPowerMessage(); // action
                // CurrentState = PhoneState.ScreenOff;
            } else {
                CurrentState = PhoneState.ScreenOn;
            }
            break;
        case PhoneEvent.PlugPower:
            CurrentState = PhoneState.ScreenCharging;
            break;
        }
        break;
    case PhoneState.ScreenOn:
        switch (anEvent) {
        case PhoneEvent.PressButton:
            CurrentState = PhoneState.ScreenOff;
            break;
        case PhoneEvent.PlugPower:
            CurrentState = PhoneState.ScreenCharging;
            break;
        }
        break;
    case PhoneState.ScreenCharging:
        switch (anEvent) {
        case PhoneEvent.UnplugPower:
            CurrentState = PhoneState.ScreenOff;
            break;
        }
        break;
    }
}

Державний зразок

Ось реалізація мого прикладу з урядом держави GoF:

введіть тут опис зображення

Державні таблиці

Отримавши натхнення у Фаулера, ось таблиця для мого прикладу:

Дія джерела Державна цільова держава
-------------------------------------------------- ------------------------------------
ScreenOff ScreenOff натискання кнопки живленняНовий дисплейLowPowerMessage  
ScreenOff ScreenOд натисканням кнопки! PowerLow
ScreenOn ScreenOff натисніть кнопку
ScreenOff ScreenCharging plugPower
ScreenOn ScreenCharging plugPower
ScreenCharging ScreenOff вимкніть живлення

Порівняння

Вкладений перемикач зберігає всю логіку в одному місці, але код може бути важко прочитати, коли існує багато станів і переходів. Це можливо безпечніше і простіше перевірити, ніж інші підходи (без поліморфізму чи інтерпретації).

Реалізація структури держави потенційно поширює логіку на декілька окремих класів, що може спричинити розуміння її в цілому. З іншого боку, невеликі класи легко зрозуміти окремо. Дизайн особливо крихкий, якщо ви змінюєте поведінку, додаючи або видаляючи переходи, оскільки вони є методами в ієрархії, і в коді може бути багато змін. Якщо ви живете за принципом дизайну невеликих інтерфейсів, ви побачите, що ця модель не дуже добре справляється. Однак якщо державна машина стабільна, такі зміни не знадобляться.

Підхід до таблиць держав вимагає написання певного перекладача для вмісту (це може бути простіше, якщо у вас є роздуми на мові, якою ви користуєтесь), що може бути багато роботи, що потрібно зробити заздалегідь. Як зазначає Фоулер, якщо ваша таблиця є окремою від вашого коду, ви можете змінити поведінку свого програмного забезпечення без перекомпіляції. Однак це має певні наслідки для безпеки; програмне забезпечення поводиться на основі вмісту зовнішнього файлу.

Редагувати (не дуже для мови C)

Існує також вільний інтерфейс (він же внутрішній доменний специфічний мова), який, ймовірно, полегшується мовами, які мають першокласні функції . Бібліотека без громадянства існує, і в цьому блозі показаний простий приклад з кодом. Обговорюється реалізація Java (до Java8) . Мені також показали приклад Python на GitHub .


Яке програмне забезпечення ви використовували для створення зображень?
sjas

1
Я підозрюю, що він, можливо, був створений за допомогою PlantUML plantuml.com/state-diagram
Seidleroni


4

Для простих випадків ви можете використовувати метод переключення стилю. Я виявив, що добре працює в минулому, - це також займатися переходами:

static int current_state;    // should always hold current state -- and probably be an enum or something

void state_leave(int new_state) {
    // do processing on what it means to enter the new state
    // which might be dependent on the current state
}

void state_enter(int new_state) {
    // do processing on what is means to leave the current atate
    // might be dependent on the new state

    current_state = new_state;
}

void state_process() {
    // switch statement to handle current state
}

Я нічого не знаю про бібліотеку підсилення, але такий тип підходу мертвий простий, не вимагає зовнішніх залежностей і простий у здійсненні.


4

switch () - це потужний і стандартний спосіб реалізації державних машин в C, але він може зменшити ремонтопридатність вниз, якщо у вас є велика кількість станів. Іншим поширеним методом є використання функціональних покажчиків для зберігання наступного стану. Цей простий приклад реалізує набір / скидання фліп-флопу:

/* Implement each state as a function with the same prototype */
void state_one(int set, int reset);
void state_two(int set, int reset);

/* Store a pointer to the next state */
void (*next_state)(int set, int reset) = state_one;

/* Users should call next_state(set, reset). This could
   also be wrapped by a real function that validated input
   and dealt with output rather than calling the function
   pointer directly. */

/* State one transitions to state one if set is true */
void state_one(int set, int reset) {
    if(set)
        next_state = state_two;
}

/* State two transitions to state one if reset is true */
void state_two(int set, int reset) {
    if(reset)
        next_state = state_one;
}

4

Я знайшов дійсно гладку реалізацію Moore FSM на курсі edx.org Embedded Systems - Shape the World UTAustinX - UT.6.02x, глава 10, Джонатан Вальвано та Рамеш Єррабаллі ....

struct State {
  unsigned long Out;  // 6-bit pattern to output
  unsigned long Time; // delay in 10ms units 
  unsigned long Next[4]; // next state for inputs 0,1,2,3
}; 

typedef const struct State STyp;

//this example has 4 states, defining constants/symbols using #define
#define goN   0
#define waitN 1
#define goE   2
#define waitE 3


//this is the full FSM logic coded into one large array of output values, delays, 
//and next states (indexed by values of the inputs)
STyp FSM[4]={
 {0x21,3000,{goN,waitN,goN,waitN}}, 
 {0x22, 500,{goE,goE,goE,goE}},
 {0x0C,3000,{goE,goE,waitE,waitE}},
 {0x14, 500,{goN,goN,goN,goN}}};
unsigned long currentState;  // index to the current state 

//super simple controller follows
int main(void){ volatile unsigned long delay;
//embedded micro-controller configuration omitteed [...]
  currentState = goN;  
  while(1){
    LIGHTS = FSM[currentState].Out;  // set outputs lines (from FSM table)
    SysTick_Wait10ms(FSM[currentState].Time);
    currentState = FSM[currentState].Next[INPUT_SENSORS];  
  }
}

2

Ви можете заглянути в програмне забезпечення лібера FSM-генератора. З мови опису стану та / або редактора діаграм стану (Windows) ви можете генерувати код для C, C ++, java та багатьох інших ... плюс приємну документацію та діаграми. Джерело та бінарні файли від iMatix



2

Один з моїх улюблених моделей - модель дизайну штату. Відповідайте або поводьтесь інакше на той самий заданий набір входів.
Однією з проблем використання операторів переключення / справи для державних машин є те, що, коли ви створюєте більше станів, перемикач / випадки стає важче / важко читати / підтримувати, сприяє неорганізованому коду спагетті і все складніше змінювати, не змінюючи щось. Я вважаю, що використання моделей дизайну допомагає мені краще впорядкувати свої дані, в чому вся суть абстрагування. Замість того, щоб розробити код свого стану, з якого стану ви прийшли, замість цього структуруйте свій код так, щоб він записував стан, коли ви входите в новий стан. Таким чином, ви ефективно отримуєте запис свого попереднього стану. Мені подобається відповідь @ JoshPetit, і я зробив його рішення ще на крок далі, взятий прямо з книги GoF:

stateCtxt.h:

#define STATE (void *)
typedef enum fsmSignal
{
   eEnter =0,
   eNormal,
   eExit
}FsmSignalT;

typedef struct fsm 
{
   FsmSignalT signal;
   // StateT is an enum that you can define any which way you want
   StateT currentState;
}FsmT;
extern int STATECTXT_Init(void);
/* optionally allow client context to set the target state */
extern STATECTXT_Set(StateT  stateID);
extern void STATECTXT_Handle(void *pvEvent);

stateCtxt.c:

#include "stateCtxt.h"
#include "statehandlers.h"

typedef STATE (*pfnStateT)(FsmSignalT signal, void *pvEvent);

static FsmT      fsm;
static pfnStateT UsbState ;

int STATECTXT_Init(void)
{    
    UsbState = State1;
    fsm.signal = eEnter;
    // use an enum for better maintainability
    fsm.currentState = '1';
    (*UsbState)( &fsm, pvEvent);
    return 0;
}

static void ChangeState( FsmT *pFsm, pfnStateT targetState )
{
    // Check to see if the state has changed
    if (targetState  != NULL)
    {
        // Call current state's exit event
        pFsm->signal = eExit;
        STATE dummyState = (*UsbState)( pFsm, pvEvent);

        // Update the State Machine structure
        UsbState = targetState ;

        // Call the new state's enter event
        pFsm->signal = eEnter;            
        dummyState = (*UsbState)( pFsm, pvEvent);
    }
}

void STATECTXT_Handle(void *pvEvent)
{
    pfnStateT newState;

    if (UsbState != NULL)
    {
         fsm.signal = eNormal;
         newState = (*UsbState)( &fsm, pvEvent );
         ChangeState( &fsm, newState );
    }        
}


void STATECTXT_Set(StateT  stateID)
{
     prevState = UsbState;
     switch (stateID) 
     {
         case '1':               
            ChangeState( State1 );
            break;
          case '2':
            ChangeState( State2);
            break;
          case '3':
            ChangeState( State3);
            break;
     }
}

statehandlers.h:

/* define state handlers */
extern STATE State1(void);
extern STATE State2(void);
extern STATE State3(void);

statehandlers.c:

#include "stateCtxt.h:"

/* Define behaviour to given set of inputs */
STATE State1(FsmT *fsm, void *pvEvent)
{   
    STATE nextState;
    /* do some state specific behaviours 
     * here
     */
    /* fsm->currentState currently contains the previous state
     * just before it gets updated, so you can implement behaviours 
     * which depend on previous state here
     */
    fsm->currentState = '1';
    /* Now, specify the next state
     * to transition to, or return null if you're still waiting for 
     * more stuff to process.  
     */
    switch (fsm->signal)
    {
        case eEnter:
            nextState = State2;
            break;
        case eNormal:
            nextState = null;
            break;
        case eExit:
            nextState = State2;
            break;
    }

    return nextState;
}

STATE  State3(FsmT *fsm, void *pvEvent)
{
    /* do some state specific behaviours 
     * here
     */
    fsm->currentState = '2';
    /* Now, specify the next state
     * to transition to
     */
     return State1;
}

STATE   State2(FsmT *fsm, void *pvEvent)
{   
    /* do some state specific behaviours 
     * here
     */
    fsm->currentState = '3';
    /* Now, specify the next state
     * to transition to
     */
     return State3;
}

Для більшості державних машин, особливо Машини з кінцевим станом, кожна держава буде знати, яким повинен бути її наступний стан, та критерії переходу до наступного стану. Для конструкцій вільних станів це може бути не так, отже, можливість викрити API для перехідних станів. Якщо ви хочете отримати більшу абстракцію, кожен обробник стану може бути відокремлений у свій власний файл, який еквівалентний конкретним обробникам станів у книзі GoF. Якщо ваш дизайн простий лише з кількома станами, то і stateCtxt.c, і statehandlers.c можуть бути об'єднані в один файл для простоти.


State3 та State2 мають значення повернення, навіть якщо вони оголошені недійсними.
Мураш

1

На мій досвід використання оператора 'switch' є стандартним способом обробки декількох можливих станів. Хоча я здивований, що ви передаєте значення переходу на обробку за стан. Я подумав, що вся суть державної машини полягає в тому, що кожна держава виконує одну дію. Тоді наступна дія / вхід визначає, у який новий стан потрібно перейти. Тож я би очікував, що кожна функція обробки стану негайно виконує те, що визначено для введення стану, а потім вирішує, чи потрібен перехід до іншого стану.


2
Існують різні основні моделі: машини Mealy та машини Moore. Дії Мелі залежать від переходу, дії Мура залежать від держави.
xmjx

1

Існує книга під назвою Практичні державні картки в C / C ++ . Тим НЕ менше, це шлях надто важкоатлет за те , що нам потрібно.


2
У мене була така сама реакція на книгу. Як може бути потрібно 700+ сторінок для опису та реалізації чогось, що, на мою думку, є досить інтуїтивним та зрозумілим?!?!?
День

1

Для компілятора, який підтримує __COUNTER__, ви можете використовувати їх для простих (але великих) стан машин.

  #define START 0      
  #define END 1000

  int run = 1;
  state = START;    
  while(run)
  {
    switch (state)
    {
        case __COUNTER__:
            //do something
            state++;
            break;
        case __COUNTER__:
            //do something
            if (input)
               state = END;
            else
               state++;
            break;
            .
            .
            .
        case __COUNTER__:
            //do something
            if (input)
               state = START;
            else
               state++;
            break;
        case __COUNTER__:
            //do something
            state++;
            break;
        case END:
            //do something
            run = 0;
            state = START;
            break;
        default:
            state++;
            break;
     } 
  } 

Перевага використання __COUNTER__замість жорстко закодованих чисел полягає в тому, що ви можете додавати стани посеред інших станів, не переношуючи кожен раз усе. Якщо компілятор не підтримує __COUNTER__, обмеженим чином його можна використовувати з обережністю__LINE__


Не могли б ви пояснити більше свою відповідь?
аборисон

У звичайному режимі "переключення" у вас є, наприклад, випадок 0, випадок 1, випадок 2, ... випадок 100. Якщо тепер ви хочете додати 3 випадки між 5 і 6, вам доведеться перенумерувати решту на 100, що зараз було б 103. Використання __COUNTER__виключає необхідність перенумерації, оскільки прекомпілятор робить нумерацію під час компіляції.
Себ

1

Ви можете використовувати мінімалістичний каркас стану машини UML в c. https://github.com/kiishor/UML-State-Machine-in-C

Він підтримує як кінцевий, так і ієрархічний стан машини. Він має лише 3 API, 2 структури та 1 перелік.

Державна машина представлена state_machine_tструктурою. Це абстрактна структура, яку можна успадкувати для створення державної машини.

//! Abstract state machine structure
struct state_machine_t
{
   uint32_t Event;          //!< Pending Event for state machine
   const state_t* State;    //!< State of state machine.
};

Стан представлений покажчиком на state_t структуру в рамках.

Якщо фреймворк налаштований на машину з кінцевим станом, то він state_tмістить,

typedef struct finite_state_t state_t;

// finite state structure
typedef struct finite_state_t{
  state_handler Handler;        //!< State handler function (function pointer)
  state_handler Entry;          //!< Entry action for state (function pointer)
  state_handler Exit;           //!< Exit action for state (function pointer)
}finite_state_t;

Рамка забезпечує API dispatch_eventдля відправки події на стан машини та два API для обходу стану.

state_machine_result_t dispatch_event(state_machine_t* const pState_Machine[], uint32_t quantity);
state_machine_result_t switch_state(state_machine_t* const, const state_t*);

state_machine_result_t traverse_state(state_machine_t* const, const state_t*);

Детальнішу інформацію про те, як реалізувати ієрархічну машину стану, див. У сховищі GitHub.

приклади коду
https://github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/simple_state_machine/readme.md
https://github.com/kiishor/UML-State-Machine-in -C / blob / master / demo / simple_state_machine_enhanced / readme.md


чи можна також додати приклад коду, який відповідає питанню?
Джуліо Каччін

1
Демонстраційна папка у сховищі має один приклад. github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/… . Зараз я працюю над ще одним вбудованим системним прикладом, який включає в себе ключ, лідер та таймери, але це ще не повно. Повідомте вас, як тільки воно буде готове.
Нандкішор Бірадар


0

Ваше запитання схоже на "чи є типова схема впровадження бази даних"? Відповідь залежить від того, чого ви хочете досягти? Якщо ви хочете реалізувати більшу детерміновану стан-машину, ви можете використовувати модель та генератор стан-машин. Приклади можна переглянути на веб-сайті www.StateSoft.org - SM Gallery. Януш Добровольський


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.