典型的なステートマシン実装パターンはありますか?


118

Cで単純な状態マシンを実装する必要があります。
標準のswitchステートメントは最善の方法ですか?
現在の状態(state)と遷移のトリガーがあります。


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;
}

単純な状態機械のためのより良い方法はありますか

編集: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()の最初の行にはいたずらな "。"があります。それがあってはいけません。
Atilla Filiz

2
この回答が他の2つのアプローチについて少なくとも2つの言葉を言うとよいでしょう。大きなスイッチケースを使用する「グローバル」メソッドと、状態デザインパターンで状態を分離し、各状態にその遷移自体を処理させます。
erikbwork 2013

こんにちは、私はこの投稿が古いことを知っていますが、私の答えが得られることを願っています:)確かにinstance_data_t変数で何をすべきですか?割り込みの状態を変更するにはどうすればいいですか...この変数に処理された割り込みに関する情報を格納するのに良い方法ですか?たとえば、ボタンが押されたので状態を変更する必要があるという情報を保存します。
グロゴン

@GRoNGoRイベントドリブンステートマシンを扱っているように聞こえます。実際にイベントデータを格納するために使用できると思います。
Zimano 2015年

3
NUM_STATESがどのように定義されているかを本当に感じてください。
Albin Stigo、2015

25

私がFSMについて言及した別のCの質問への私の回答を見たことがあるかもしれません!ここに私がそれをする方法があります:

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

これで、2つのタイプの遷移があります。1つは状態に移行して新しい文字を読み取る方法、もう1つは入力を消費せずに状態に移行する方法です。

次のような方法でEOFの処理を自動化することもできます。

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

#define ENDFSM    sx_endfsm:

このアプローチの良い点は、描画した状態図を作業コードに直接変換でき、逆に、コードから状態図を簡単に描画できることです。

FSMを実装する他の手法では、遷移の構造は制御構造(if、if、switch ...)に埋め込まれ、変数の値(通常はstate変数)によって制御されます。複雑なコード。

このテクニックは、残念ながら出版されなくなった「Computer Language」誌に掲載された記事から知りました。


1
基本的に、優れたFSMはすべて読みやすさです。これは優れたインターフェースを提供し、実装はそれと同じくらい優れています。言語にネイティブFSM構造がないことは残念です。C1Xへの追加として今それを見ることができます!
Kelden Cowan、2010

3
組み込みアプリケーションに対するこのアプローチが大好きです。イベントドリブンステートマシンでこのアプローチを使用する方法はありますか?
ARF、2016

13

テーブルアプローチも使用しました。ただし、オーバーヘッドがあります。なぜポインタの2番目のリストを保存するのですか?()なしの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
    }
}

もちろん、恐怖の要因(つまり、安全性と速度)に応じて、有効なポインタを確認することをお勧めします。3つ程度の状態より大きいステートマシンの場合、上記のアプローチは、同等のスイッチまたはテーブルアプローチよりも命令が少ないはずです。次のようにマクロ化することもできます。

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

また、私はOPの例から、ステートマシンを検討/設計するときに行うべき単純化があると感じています。遷移状態をロジックに使用する必要はありません。各状態関数は、過去の状態を明示的に認識しなくても、特定の役割を実行できる必要があります。基本的には、現在の状態から別の状態に遷移する方法を設計します。

最後に、「機能的な」境界に基づいて状態マシンの設計を開始しないでください。そのためにサブ機能を使用します。代わりに、続行する前に何かが起こるのを待つ必要がある時期に基づいて状態を分割します。これにより、結果を得るまでにステートマシンを実行する必要がある回数を最小限に抑えることができます。これは、I / O関数や割り込みハンドラーを作成するときに重要になります。

また、古典的なswitchステートメントのいくつかの長所と短所:

長所:

  • それは言語で書かれているので、文書化され、明確です
  • 状態は、それらが呼び出される場所で定義されます
  • 1回の関数呼び出しで複数の状態を実行できます
  • すべての状態に共通のコードは、switchステートメントの前後に実行できます

短所:

  • 1回の関数呼び出しで複数の状態を実行できます
  • すべての状態に共通のコードは、switchステートメントの前後に実行できます
  • スイッチの実装が遅くなる可能性があります

賛否両論の2つの属性に注意してください。この切り替えにより、国家間の過剰な共有の機会が生まれ、国家間の相互依存関係が管理不能になる可能性があると思います。ただし、状態の数が少ない場合は、最も読みやすく、保守しやすい可能性があります。


10

単純なステートマシンの場合は、スイッチステートメントと状態の列挙型を使用するだけです。入力に基づいて、switchステートメント内で遷移を行います。実際のプログラムでは、「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
関数内に「状態」を入れ、静的にする価値があるかもしれません。
スティーブメルニコフ2009年

2
@Steve Melnikoff:状態マシンが1つしかない場合のみ。それを関数の外に置いてください。そうすれば、独自の状態を持つ状態マシンの配列を持つことができます。
Vicky

@Vicky:1つの関数には、必要に応じて任意の数の状態マシンを含めることができ、必要に応じて状態変数の配列を含めることができます。
スティーブメルニコフ2010年

10

Martin Fowler氏のUML蒸留、彼は(しゃれが意図していない)第10章ステートマシン図(強調鉱山)で述べています:

状態図は、ネストされたスイッチ状態パターン状態テーブルの 3つの主な方法で実装できます。

携帯電話のディスプレイの状態の簡単な例を使用してみましょう:

ここに画像の説明を入力してください

ネストされたスイッチ

Fowlerが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 Stateパターンを使用した私の例の実装は次のとおりです。

ここに画像の説明を入力してください

状態テーブル

ファウラーからインスピレーションを得て、ここに私の例の表があります:

ソース状態ターゲット状態イベントガードアクション
-------------------------------------------------- ------------------------------------
ScreenOff ScreenOff pressButton powerLow displayLowPowerMessage  
ScreenOff ScreenOn pressButton!powerLow
ScreenOn ScreenOff pressButton
ScreenOff ScreenCharging plugPower
ScreenOn ScreenCharging plugPower
ScreenCharging ScreenOff unplugPower

比較

ネストされたスイッチはすべてのロジックを1つの場所に保持しますが、状態と遷移が多い場合、コードが読みにくくなる場合があります。他のアプローチよりも安全で検証が簡単です(多態性や解釈はありません)。

Stateパターンの実装は、ロジックをいくつかの個別のクラスに分散させる可能性があり、全体としてそれを理解することが問題になる場合があります。一方、少人数クラスは個別に理解しやすいです。トランジションは階層内のメソッドであり、コードに多くの変更が加えられる可能性があるため、トランジションを追加または削除して動作を変更すると、デザインは特に壊れやすくなります。小さなインターフェースの設計原則に基づいていると、このパターンは実際にはうまく機能しないことがわかります。ただし、状態マシンが安定している場合、そのような変更は必要ありません。

ステートテーブルアプローチでは、コンテンツのなんらかのインタープリターを作成する必要があります(これは、使用している言語に反映している場合に簡単です)。Fowlerが指摘するように、テーブルがコードから分離されている場合、再コンパイルせずにソフトウェアの動作を変更できます。ただし、これにはセキュリティ上の意味があります。ソフトウェアは外部ファイルの内容に基づいて動作しています。

編集(実際にはC言語ではありません)

流暢なインターフェイス(別名、ドメイン固有言語)のアプローチもあります。これは、おそらくファーストクラスの機能を持つ言語によって促進されますステートレスライブラリが存在し、そのブログは、コードで簡単な例を示します。Java実装は、(Java8をPRE)に議論されています。GitHubでもPythonの例が表示されました。


写真を作成するためにどのソフトウェアを使用しましたか?
sjas



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

edx.orgコースで、ムーアFSMの非常に洗練されたC実装を見つけました。組み込みシステム-世界を形作るUTAustinX-UT.6.02x、第10章、Jonathan ValvanoとRamesh Yerraballi ...

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

libero FSMジェネレーターソフトウェアを調べてみてください。状態記述言語や(Windows)状態図エディターから、C、C ++、Javaなどのコードを生成できます。さらに、すばらしいドキュメントと図も作成できます。iMatixのソースとバイナリ



2

私のお気に入りのパターンの1つは、ステートデザインパターンです。与えられた同じ入力セットに対して異なる応答または動作をします。
ステートマシンにswitch / caseステートメントを使用する際の問題の1つは、ステートを作成するにつれて、スイッチ/ケースの読み取り/維持が困難/扱いにくくなり、組織化されていないスパゲッティコードを促進し、何かを壊さずに変更することがますます困難になることです。デザインパターンを使用すると、データをより適切に整理するのに役立ちます。これが抽象化の全体のポイントです。元の状態に基づいて状態コードを設計する代わりに、コードを構造化して、新しい状態に入ったときに状態を記録するようにします。これにより、以前の状態の記録を効果的に取得できます。私は@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の両方を1つのファイルにまとめて簡単にすることができます。


State3とState2には、voidと宣言されていても戻り値があります。
Ant

1

私の経験では、「switch」ステートメントを使用することは、複数の可能な状態を処理する標準的な方法です。状態ごとの処理への移行値を渡していることに驚いていますが。ステートマシンの要点は、各状態が1つのアクションを実行することだと思いました。次に、次のアクション/入力によって、遷移する新しい状態が決まります。したがって、各状態処理関数は、状態に入るために修正されたものをすぐに実行し、その後、別の状態への遷移が必要かどうかを決定することを期待していました。


2
基礎となるモデルには、MealyマシンとMooreマシンがあります。Mealyのアクションは遷移に依存し、Mooreのアクションは状態に依存します。
xmjx

1

という本があります C / C ++の実用的なステートチャートます。しかし、それはある方法私たちが必要なもののためにあまりにもヘビー級。


2
私はその本に対してまったく同じ反応をしました。かなり直感的でわかりやすいと思うものを記述して実装するには、700ページ以上が必要ですか?!?!?
Dan

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__


あなたの答えをもっと説明してもらえますか?
abarisone 2015年

通常の「スイッチ」状態のマシンでは、たとえば、ケース0、ケース1、ケース2、...ケース100があります。5〜6の3つのケースを追加する場合は、残りを100に再番号付けする必要があります。現在は103になります。 __COUNTER__プリコンパイラはコンパイル中に番号付けを行うため、を、番号を付け直す必要なくなります。
Seb

1

cで最小限のUMLステートマシンフレームワークを使用できます。https://github.com/kiishor/UML-State-Machine-in-C

有限状態マシンと階層状態マシンの両方をサポートしています。APIは3つ、構造は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;

フレームワークは、dispatch_eventステートマシンにイベントをディスパッチするAPI と、ステートトラバーサル用の2つの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


質問に合うコード例も追加できますか?
Giulio Caccin

リポジトリのデモフォルダーには1つの例があります。github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/…。私は現在、キー、LED、タイマーを含むもう1つの組み込みシステムの例に取り組んでいますが、まだ完全ではありません。準備ができたらお知らせします。
Nandkishor Biradar


0

あなたの質問は「典型的なデータベース実装パターンはありますか」に似ていますか?答えはあなたが何を達成したいかに依存しますか?より大きな確定的状態機械を実装したい場合は、モデルと状態機械ジェネレータを使用できます。例は、www.StateSoft.org-SM Galleryで閲覧できます。ヤヌス・ドブロウォルスキー


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.