سؤال

نحن بحاجة إلى تنفيذ بسيطة آلة الدولة في ج.
هو معيار بيان التبديل أفضل وسيلة للذهاب ؟
لدينا تيار الدولة (الدولة) و الزناد عن الانتقال.


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++, أعتقد دفعة Statechart مكتبة قد يكون وسيلة للذهاب.ومع ذلك ، فإنه لا مساعدة مع C.يتيح التركيز على C في حالة استخدام.

هل كانت مفيدة؟

المحلول

أنا أفضل أن استخدام الجدول الذي يدفعه معظم أجهزة الدولة:

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

الجدول مدفوعة النهج هو أسهل للحفاظ على وتوسيع و أبسط إلى خريطة الدولة الرسوم البيانية.

نصائح أخرى

كنت قد رأيت جوابي إلى آخر ج السؤال حيث ذكرت 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 التي تريد أن محرك الأقراص الخاص بك ولايات ميكرونيزيا الموحدة ، لذا أنت يمكن أن تدرج في العمل من قراءة التالي شار في الكلية نفسها:

#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 بنية التحولات دفن في هياكل المراقبة (في حين, إذا, التبديل ...) والتي تسيطر عليها المتغيرات قيمة (تيبيكالي a state متغير) و قد يكون مهمة معقدة تتصل لطيفة الرسم البياني إلى الملتوية رمز.

تعلمت هذا الأسلوب من مقال ظهر على عظيم "لغة الكمبيوتر" مجلة للأسف لم يعد نشرها.

كما استخدمت الجدول النهج.ومع ذلك ، هناك النفقات العامة.لماذا متجر قائمة ثانية من المؤشرات ؟ وظيفة في ج دون () هو مؤشر 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
    }
}

بالطبع اعتمادا على عامل الخوف (أيسلامة vs السرعة) قد ترغب في التحقق صالحة المؤشرات.على أجهزة الدولة أكبر من ثلاثة أو حتى الدول ، فإن النهج أعلاه يجب أن تكون أقل تعليمات من معادلة التبديل أو الجدول النهج.حتى يمكن الماكرو-إيزي مثل:

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

أيضا أشعر من OP سبيل المثال ، أن هناك تبسيط الذي ينبغي القيام به عند التفكير / تصميم آلة الدولة.لا شيء في الانتقال الدولة ينبغي أن تستخدم المنطق.كل وظيفة الدولة يجب أن تكون قادرة على أداء الدور المعطى دون المعرفة الصريحة الماضية الدولة(الدول).أساسا يمكنك تصميم عن كيفية الانتقال من دولة إلى دولة أخرى.

أخيرا لا تبدأ تصميم آلة الدولة على أساس "وظيفية" حدود استخدام وظائف فرعية على ذلك.بدلا من تقسيم الدول على أساس عندما سيكون لديك إلى الانتظار لشيء أن يحدث قبل أن تتمكن من الاستمرار.وهذا سوف يساعد على تقليل عدد مرات لديك لتشغيل آلة الدولة قبل أن تحصل على نتيجة.هذا يمكن أن تكون هامة عند كتابة وظائف I/O أو معالجات المقاطعة.

أيضا, بعض إيجابيات وسلبيات الكلاسيكية بيان التبديل:

الايجابيات:

  • هو في اللغة ، لذلك فمن موثقة وواضحة
  • يتم تعريف الدول حيث تسمى
  • يمكن تنفيذ العديد من الدول في استدعاء دالة
  • مدونة مشتركة بين جميع الدول يمكن تنفيذها قبل وبعد بيان التبديل

سلبيات:

  • يمكن تنفيذ العديد من الدول في استدعاء دالة
  • مدونة مشتركة بين جميع الدول يمكن تنفيذها قبل وبعد بيان التبديل
  • التبديل التنفيذ يمكن أن تكون بطيئة

لاحظ اثنين من الصفات التي هي على حد سواء المؤيدة والمعارضة.أعتقد التبديل يسمح فرصة تقاسم الكثير من المعلومات بين الدول ، و التكافل بين الدول يمكن أن يصبح لا يمكن السيطرة عليها.إلا أن عددا صغيرا من الدول ، قد يكون الأكثر قابلية للقراءة و الصيانة.

بسيطة آلة الدولة مجرد استخدام رمز التبديل بيانا نوع التعداد لجهاز الدولة.هل التحولات داخل بيان التبديل على أساس المدخلات الخاصة بك.في برنامج حقيقي أنت ومن الواضح أن تغيير "إذا كان(المدخلات)" للتحقق الخاص بك الانتقال نقاط.ويساعد هذا الأمل.

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

هناك أيضا شبكة المنطق والتي هي أكثر للصيانة الدولة آلة يحصل على أكبر

في مارتن فاولر UML المقطر, ، يقول (لا يقصد التورية) في الفصل 10 آلة الدولة الرسوم البيانية (التشديد من الألغام):

مخطط الحالة يمكن تنفيذها في ثلاث طرق رئيسية: متداخلة التبديل, ، الدولة نمط, ، الدولة الجداول.

دعونا نستخدم مثال مبسط من الدول الهاتف المحمول عرض:

enter image description here

متداخلة التبديل

فاولر أعطى مثال من التعليمات البرمجية 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;
    }
}

الدولة نمط

هنا تنفيذ بلدي على سبيل المثال مع صندوق جلوبل القناص الدولة نمط:

enter image description here

الدولة الجداول

أخذ الإلهام من فاولر ، وهنا طاولة بلدي على سبيل المثال:

Source State    Target State    Event         Guard        Action
--------------------------------------------------------------------------------------
ScreenOff       ScreenOff       pressButton   powerLow     displayLowPowerMessage  
ScreenOff       ScreenOn        pressButton   !powerLow
ScreenOn        ScreenOff       pressButton
ScreenOff       ScreenCharging  plugPower
ScreenOn        ScreenCharging  plugPower
ScreenCharging  ScreenOff       unplugPower

مقارنة

متداخلة التبديل يحتفظ كل منطق في بقعة واحدة ، ولكن رمز يمكن أن يكون من الصعب قراءة عندما يكون هناك الكثير من الدول والتحولات.انها ربما أكثر أمنا و أسهل للتحقق من مناهج أخرى (لا تعدد الأشكال أو تفسير).

الدولة نمط تنفيذ يحتمل أن ينتشر المنطق على مدى عدة فصول منفصلة ، مما قد يجعل فهم ككل مشكلة.من ناحية أخرى ، فصول صغيرة من السهل أن نفهم بشكل منفصل.التصميم هو هشة إذا كنت تغيير السلوك عن طريق إضافة أو إزالة التحولات ، كما أنهم الأساليب في التسلسل الهرمي يمكن أن يكون هناك الكثير من التغييرات في التعليمات البرمجية.إذا كنت تعيش في التصميم مبدأ صغيرة واجهات, سوف نرى هذا النمط لا تفعل ذلك بشكل جيد.ومع ذلك ، إذا كانت الدولة آلة مستقرة ، ثم مثل هذه التغييرات لن تكون هناك حاجة.

الدولة الجداول النهج يتطلب الكتابة نوع من مترجم المحتوى (هذا قد يكون أسهل إذا كان لديك للتفكير في اللغة تستخدمه) ، والتي يمكن أن يكون هناك الكثير من العمل للقيام به مقدما.كما فاولر ، إذا كان الجدول الخاص بك هو منفصل من التعليمات البرمجية الخاصة بك ، يمكنك تعديل سلوك البرنامج بدون ترجمة.هذه بعض الآثار الأمنية ، ومع ذلك ؛ البرنامج يتصرف استنادا إلى محتويات ملف خارجي.

تحرير (ليس لغة C)

هناك بطلاقة واجهة (ويعرف أيضا باسم الداخلية المجال لغة معينة) النهج أيضا وربما سهلت لغة ، وظائف من الدرجة الأولى.على عديمي الجنسية المكتبة موجود و هذا بلوق يظهر مثال بسيط مع رمز.A تنفيذ جافا (قبل Java8) يتم مناقشتها.هو بيثون مثال على جيثب وكذلك.

بسيطة الحالات ، يمكن لك تبديل نمط الأسلوب.ما وجدت أنه يعمل بشكل جيد في الماضي للتعامل مع التحولات أيضا:

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
}

أنا لا أعرف أي شيء عن دفعة المكتبة ، ولكن هذا النوع من النهج ميت بسيطة لا تتطلب أي عوامل خارجية ، و هي سهلة التنفيذ.

التبديل() هي قوية و طريقة قياسية من تنفيذ أجهزة الدولة في C ، ولكن يمكن تقليل الصيانة أسفل إذا كان لديك عدد كبير من الدول.أسلوب شائع آخر هو استخدام مؤشرات الدالة لتخزين الدولة القادمة.هذا مثال بسيط تنفذ تعيين/إعادة تعيين flip-flop:

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

وجدت بقعة حقا C تنفيذ مور FSM على edx.org بالطبع جزءا لا يتجزأ من أنظمة شكل العالم UTAustinX - UT.6.02 العاشر, الفصل 10 من قبل جوناثان Valvano و راميش 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];  
  }
}

قد ترغب في النظر في libero ولايات ميكرونيزيا الموحدة مولد البرمجيات.من دولة وصف اللغة و/أو (ويندوز) الدولة محرر الرسم البياني يمكنك توليد رمز C, C++, java وغيرها الكثير ...بالإضافة إلى لطيفة الوثائق و الرسوم البيانية.المصدر الثنائيات من iMatix

هذه المادة هو فكرة جيدة بالنسبة للدولة نمط (على الرغم من أنه هو C++ ، وليس على وجه التحديد ج).

إذا كان يمكنك وضع يديك على الكتاب "أول رئيس أنماط التصميم"،شرح و مثال واضحة جدا.

واحدة من بلدي المفضلة أنماط الدولة في نمط التصميم.الاستجابة أو التصرف بشكل مختلف لنفس مجموعة معينة من المدخلات.
واحدة من المشاكل مع استخدام رمز التبديل/حالة البيانات على أجهزة الدولة أن كما يمكنك إنشاء أكثر الدول التبديل/الحالات يصبح أكثر صعوبة/عملي إلى قراءة/المحافظة يعزز غير المنظم السباغيتي رمز من الصعب على نحو متزايد للتغيير دون كسر شيء.أجد باستخدام أنماط التصميم يساعدني على تنظيم بيانات أفضل مما هو المغزى من التجريد.بدلا من تصميم الخاص بك رمز الدولة حول ما الدولة التي جاءت بدلا بنية التعليمات البرمجية بحيث سجلات الدولة عند دخول ولاية جديدة.بهذه الطريقة يمكنك الحصول على نحو فعال تسجيل حالة سابقة.أود @JoshPetit الجواب ، واتخذت حل له خطوة واحدة أبعد من ذلك ، اتخذت مباشرة من صندوق القناص الكتاب:

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.ج:

#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.ج:

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

معظم أجهزة الدولة ، esp.الدولة المحدودة الآلات ، كل دولة سوف تعرف ما لها الدولة القادمة يجب ان تكون معايير الانتقال إلى الدولة القادمة.فضفاضة الدولة تصاميم, هذا قد لا يكون صحيحا ، ومن ثم خيار لفضح API من أجل الانتقال الدول.إذا كنت ترغب في المزيد من التجريد ، كل دولة معالج يمكن فصل في ملف خاص به ، التي تعادل الخرسانة الدولة معالجات في صندوق جلوبل القناص الكتاب.إذا كان التصميم الخاص بك هو بسيطة مع عدد قليل من الدول ، ثم كلا stateCtxt.ج statehandlers.ج يمكن دمجها في ملف واحد عن البساطة.

في تجربتي باستخدام 'مفتاح' البيان هو المعيار طريقة التعامل مع العديد من الدول ممكن.على الرغم من أنني surpirsed التي تمر في مرحلة انتقالية قيمة لكل دولة المعالجة.اعتقد ان المغزى من آلة الدولة أن كل دولة يتم تنفيذ إجراء واحد.ثم العمل القادم/الإدخال يحدد الدولة الجديدة إلى الانتقال إلى.لذلك كنت أتوقع كل دولة معالجة وظيفة فورا في تنفيذ كل ما هو ثابت على دخول الدولة ثم بعد ذلك تقرر ما إذا كان الانتقال في حاجة إلى دولة أخرى.

هناك كتاب بعنوان عملية Statecharts في C/C++.ومع ذلك ، طريقة الوزن الثقيل جدا على ما نحن بحاجة إلى.

عن المترجم الذي دعم __COUNTER__ يمكنك استخدامها بسيط (لكن كبيرة) الدولة mashines.

  #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__, بشكل محدود لها posible للاستخدام مع الاحتياط __LINE__

في C++ ، والنظر في الدولة نمط.

سؤالك يشبه "هناك نموذجية قاعدة بيانات تنفيذ نمط"?الجواب يعتمد على ماذا تريد أن تحقق ؟ إذا كنت ترغب في تنفيذ أكبر القطعية آلة الدولة يمكنك استخدام نموذج آلة الدولة مولد.أمثلة يمكن أن ينظر إليها في www.StateSoft.org - SM معرض.يانوش Dobrowolski

في ج ، آلات بسيطة شيئا من هذا القبيل عادة ما يكون في نهاية المطاف استخدام.

الحدث يحركها FSM يوصف من قبل الدولة الكائنات (FsmState) المرتبطة إجراء (FsmAction), والتحولات (FsmEdge) المحددة من قبل الدولة الحالية والأحداث.

كما يوفر تخزين وتمرير كل من ولايات ميكرونيزيا الموحدة بيانات المستخدم لفصل ولايات ميكرونيزيا الموحدة-لا بد المستخدم ربط المعلومات والسماح مثيلات متعددة من نفس ميكرونيزيا (أيباستخدام نفس الوصف ولكن يمر مختلفة بيانات المستخدم).

الأحداث التي يمثلها عدد صحيح نوع (FsmEvent).القيم السلبية محفوظة تنفيذ للسماح الخاصة المشتركة الأحداث (إعادة تعيين, لا, أي, فارغة, نهاية).غير الأحداث السلبية هي التي يحددها المستخدم.

عن البساطة, التحولات مدرجة في مجموعة ومطابقة الشروع في صف النظام ، أساسا توفير أولويات الانتقال.لديهم اختياري وظائف الحرس.الدولة القادمة يمكن أن تكون إما المشار إليه مباشرة في الانتقال قائمة أو من خلال وظيفة القفزة, هذه الطريقة توفير المزيد من المرونة التي تمكن ديناميكية FSM السلوك.

في التحولات الأوصاف, NULL الحالي سوف تتطابق مع أي دولة البدل الحدث (أي) سوف تتطابق مع أي حدث.على أي حال ، فإن القيمة الفعلية التي تسببت في وقوع الانتقال سيتم تمريرها إلى القفز وظائف الحرس.

المعقدة FSMs بسيطة حافة مجموعة الحل قد تصبح غير فعالة جدا.في هذه الحالة السليم القفز وظيفة يمكن تنفيذها باستخدام حافة مجموعة الحدث إدخالات تحويلها إلى مصفوفة الانتقال أو دولة الجوار القوائم.

الإجراءات التي تتخذها الدولة تهدف إلى تنفيذها مع عودة الدخول وظيفة يميز بين الدولة الإدخال (Enter) ، حالة خروج (إجازة) في الدولة (الدولة) العمليات.في هذه الطريقة المحلية للدولة المعلومات يمكن أن تكون مغلفة و مع الحفاظ على وظيفة ثابتة المتغيرات.

عادة الدولة الدخول والخروج الإجراءات ستنفذ بشكل عادي و لا عودة الأحداث الجديدة (لا يوجد).إذا لم يكن الحدث الجديد هو المحاصرين وعاد على الفور.هذا سيكون فعالا في منع انتقال في حال حدوثه عند الخروج من الوضع الحالي.

ولايات ميكرونيزيا الموحدة خطوة وظيفة (fsmStep) سوف تؤدي خطوة واحدة من ولايات ميكرونيزيا الموحدة ، باستخدام حدثا جديدا لتحريك انتقالية ، أو أي حال من (لا شيء) إلى تنفيذ في عمل الدولة من الوضع الحالي.الخطوة الدالة بإرجاع الجديدة المنبعثة الحدث التي يمكن التعامل معها أو إعادة تغذية FSM;أو لا شيء ، فارغة و نهاية في حال عدم الحدث ، الانتقالية لم يتم العثور على أو نهاية الدولة الوصول إليها ، على التوالي.

#ifndef FSM_H_
#define FSM_H_

#include <stdbool.h>
#include <stdint.h>

/** FSM enum type */
typedef enum
{
    // Events and return values
    fsm_User = 0, ///< User events start with this id
    fsm_Reset = -1, ///< Reset event
    fsm_None = -2, ///< No event
    fsm_Any = -3, ///< Any event, used as a wildcard
    fsm_Empty = -4, ///< No transition found for event
    fsm_End = -5, ///< Final state event generated when FSM reaches end state, or stop processing when used in transition

    // Action types
    fsm_Enter = 0, ///< Entry action
    fsm_State, ///< In-state action
    fsm_Leave ///< Exit action
} fsm_e;

typedef int FsmEvent; ///< Type for events
typedef struct FsmState FsmState; ///< Type for states
typedef struct FsmEdge FsmEdge; ///< Type for edges (transitions)

/** State action functor
    @param state Pointer to this state
    @param type Action type (Enter/State/Leave)
    @param frto Pointer to from(Enter)/to(Leave) state, NULL otherwise
    @param data User data
    @return Event id in case of a new triggered event, fsm_None otherwise
*/
typedef FsmEvent (*FsmAction)(FsmState *state, fsm_e type, FsmState *frto, void *data);

/** FSM state object */
struct FsmState
{
    FsmAction action; ///< Per-state action
    void *data; ///< Per-state data
};

/** State jump functor
    @param edge Pointer to this edge
    @param state Pointer to the actual current state
    @param event Event id that triggered the transition
    @param data User data
    @return Pointer to the next state and NULL for end state
*/
typedef FsmState *(*FsmJump)(FsmEdge *edge, FsmState *state, FsmEvent event, void *data);

/** Guard function
    @param edge Pointer to this edge
    @param state Pointer to the actual current state
    @param event Event id that triggered the transition
    @param data User data
    @return Guard result
*/
typedef bool (*FsmGuard)(FsmEdge *edge, FsmState *state, FsmEvent event, void *data);

/** FSM edge transition */
struct FsmEdge
{
    FsmState *state; ///< Matching current state pointer, or NULL to match any state
    FsmEvent event; ///< Matching event id or fsm_Any for wildcard
    void *next; ///< Next state pointer (FsmState *) or jump function (FsmJump)
    FsmGuard guard; ///< Transition guard function
    void *data; ///< Per-edge data
};

typedef struct Fsm Fsm;
struct Fsm
{
    FsmState *state; ///< Pointer to the state array
    size_t states; ///< Number of states
    void **stateData; ///< Pointer to user state data array

    FsmEdge *edge; ///< Pointer to the edge transitions array
    size_t edges; ///< Number of edges
    void **edgeData; ///< Pointer to user edge data array

    FsmEvent event; ///< Current/last event
    fsm_e type; ///< Current/last action type

    FsmState *current; ///< Pointer to the current state
    void *data; ///< Per-fsm data
};

#define fsm_INIT { 0 }

static inline FsmEvent
fsmStep(Fsm *f, FsmEvent e)
{
    FsmState *cp = f->current; // Store current state
    FsmEvent ne = fsm_None; // Next event

    // User state data
    void *us = (f->stateData && cp) ? f->stateData[cp - f->state] : NULL;

    if (!cp && e == fsm_None)
        e = fsm_Reset; // Inject reset into uninitialized FSM

    f->event = e;

    switch (e)
    {
    case fsm_Reset:
        {
            // Exit current state
            if (cp && cp->action)
            {
                f->type = fsm_Leave, ne = cp->action(cp, fsm_Leave, f->state, us);

                if (ne != fsm_None)
                    return ne; // Leave action emitted event
            }

            FsmState *ps = cp;
            cp = f->current = f->state; // First state in array is entry state

            if (!cp)
                return fsm_End; // Null state machine

            if (cp->action)
            {
                us = f->stateData ? f->stateData[0] : NULL;
                f->type = fsm_Enter, ne = cp->action(cp, fsm_Enter, ps, us);
            }
        }
        break;

    case fsm_None: // No event, run in-state action
        if (cp->action)
            f->type = fsm_State, ne = cp->action(cp, fsm_State, NULL, us);
        break;

    default: // Process user transition event
        ne = fsm_Empty; // Default return in case no transition is found

        // Search transition in listing order
        for (size_t i = 0; i < f->edges; ++i)
        {
            FsmEdge *ep = &f->edge[i];

            // Check for state match (null edge state matches any state)
            if (ep->state && ep->state != cp)
                continue; // Not a match

            // Check for stop processing filter
            if (ep->event == fsm_End)
                break;

            ne = fsm_None; // Default return for a transition

            // Check for event match
            if (ep->event == e || ep->event == fsm_Any)
            {
                // User edge data
                void *ue = f->edgeData ? f->edgeData[i] : NULL;

                // Check transition guard
                if (!ep->guard || ep->guard(ep, cp, e, ue))
                {
                    // Resolve next state pointer (NULL, new state pointer or jump function)
                    FsmState *np = (!ep->next) ? NULL
                        : ((FsmState *)(ep->next) >= f->state && (FsmState *)(ep->next) < (f->state + f->states)) ? (FsmState *)(ep->next)
                        : ((FsmJump)(ep->next))(ep, cp, e, ue);

                    if (cp->action)
                    {
                        f->type = fsm_Leave, ne = cp->action(cp, fsm_Leave, np, us);

                        if (ne != fsm_None)
                            return ne; // Leave action emitted event
                    }

                    if (!np) // Final state reached if next state is NULL
                        ne = fsm_End;
                    else if (np->action)
                    {
                        us = f->stateData ? f->stateData[np - f->state] : NULL;
                        f->type = fsm_Enter, ne = np->action(np, fsm_Enter, cp, us);
                    }

                    cp = np; // New state value

                    // Transition executed, stop
                    break;
                }
            }
        }
    }

    f->current = cp; // Commit current state

    return ne; // Last event value
}

static inline FsmEvent
fsmReset(Fsm *f)
{
    return fsmStep(f, fsm_Reset);
}

#endif // FSM_H_

أدناه هناك اختبار بسيط جدا للحصول على فكرة عن كيفية تحديد استخدام FSM التنفيذ.لا يوجد معرف من قبل المستخدم الأحداث فقط FSM البيانات (سلاسل) نفس عمل الدولة على كل دولة مشتركة القفز وظيفة:

#include <stdio.h>

#include "fsm.h"

// State action example
static FsmEvent
state_action(FsmState *s, fsm_e t, FsmState *ft, void *us)
{
    FsmEvent e = fsm_None; // State event
    const char *q = "?";

    switch (t)
    {
    case fsm_Enter:
        q = "enter";
        break;

    case fsm_Leave:
        q = "leave";
        break;

    default /* fsm_State */:
        q = "state";
    }

    printf("%s %s\n", (const char *)s->data, q);

    return e;
}

// States
FsmState fs[] =
{
    { state_action, "S0" },
    { state_action, "S1" },
    { state_action, "S2" },
};

// Transition jump example
static FsmState *
state_jump(FsmEdge *t, FsmState *s, FsmEvent e, void *ue)
{
    if (s == &fs[0])
        return &fs[1];

    if (s == &fs[2])
        return NULL; // End state

    return NULL; // Trap
}

// Transition guard example
static bool
count_attempt(FsmEdge *t, FsmState *s, FsmEvent e, void *ue)
{
    static int c = 0;
    ++c;
    bool b = c == 3;
    printf("guard is %s\n", b ? "true" : "false");
    return b;
}

// Transitions
FsmEdge fe[] =
{
    { &fs[0], fsm_Any, state_jump }, // Using jump function, no guard
    { &fs[1], fsm_Any, &fs[2], count_attempt }, // Using direct state and guard
    { &fs[2], fsm_Any, state_jump  }, // Using jump function, no guard
};

int
main(int argc, char **argv)
{
    Fsm f = { fs, 3, NULL, fe, 3, NULL, };

    fsmReset(&f);

    do 
    {
        fsmStep(&f, fsm_None);
    } while (fsmStep(&f, fsm_Any) != fsm_End);

    return 0;
}

دفعة لديه statechart المكتبة. http://www.boost.org/doc/libs/1_36_0/libs/statechart/doc/index.html

لا أستطيع التحدث إلى استخدام ذلك.لا تستخدم نفسي (بعد)

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top