Tipik bir durum makinesi uygulama modeli var mı?


118

C'de basit bir durum makinesi uygulamalıyız .
Standart bir anahtar ifadesi gitmenin en iyi yolu mu?
Mevcut bir durumumuz (durumumuz) ve geçiş için bir tetikleyicimiz var.


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

Basit durum makineleri için daha iyi bir yol var mı

DÜZENLEME: C ++ için, Boost Statechart kitaplığının gitmenin yolu olabileceğini düşünüyorum . Ancak, yok değil C. C kullanım durumuna konsantre sağlar yardımcı.


Yanıtlar:


134

Çoğu durum makinesi için tabloya dayalı bir yaklaşım kullanmayı tercih ederim:

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

Bu, elbette birden çok durum makinesini vb. Desteklemek için genişletilebilir. Geçiş eylemleri de barındırılabilir:

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

Tablo güdümlü yaklaşımın bakımı ve genişletilmesi daha kolaydır ve durum diyagramlarıyla eşlemek daha kolaydır.


Başlamak için çok güzel bir yol, en azından benim için başlangıç ​​noktası. Bir not, run_state () işlevinin ilk satırında yaramaz bir "" var. orada olmamalı.
Atilla Filiz

2
Bu yanıt diğer iki yaklaşım hakkında da en azından 2 kelime söylese daha iyi olurdu: büyük bir anahtar durumu olan "küresel" bir yöntem ve Durum Tasarım Modeli ile durumları ayırmak ve her bir durumun kendi geçişlerini kendisinin yapmasına izin vermek.
erikbwork

Merhaba, bu yazının eski olduğunu biliyorum ama umarım cevabımı alırım :) instance_data_t değişkeninde kesinlikle ne olmalı? Kesintilerdeki durumları nasıl değiştireceğimi merak ediyorum ... bu değişkende işlenmiş kesinti hakkında bilgi depolamanın iyi bir yolu mu? Örneğin, düğmeye basıldığı bilgisini kaydedin, böylece durum değiştirilmelidir.
grongor

@GRoNGoR Olay güdümlü bir durum makinesiyle uğraşıyormuşsunuz gibi geliyor bana. Olay verilerini gerçekten depolamak için kullanabileceğinizi düşünüyorum.
Zimano

3
NUM_STATES'in nasıl tanımlandığı gerçekten hoş bir dokunuş.
Albin Stigo

25

FSM'den bahsettiğim başka bir C sorusuna verdiğim yanıtı görmüş olabilirsiniz! İşte bunu nasıl yapıyorum:

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

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

Aşağıdaki makrolar tanımlanmış olarak

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

Bu, özel duruma uyacak şekilde değiştirilebilir. Örneğin, FSMFILEFSM'nizi çalıştırmak istediğiniz bir dosyanız olabilir , böylece sonraki karakteri okuma eylemini makronun kendisine dahil edebilirsiniz:

#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

şimdi iki tür geçişiniz var: biri bir duruma gider ve yeni bir karakter okur, diğeri herhangi bir girdi tüketmeden bir duruma geçer.

Ayrıca EOF'nin işlenmesini aşağıdaki gibi bir şeyle otomatikleştirebilirsiniz:

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

#define ENDFSM    sx_endfsm:

Bu yaklaşımın iyi tarafı, çizdiğiniz bir durum diyagramını doğrudan çalışma koduna çevirebilmeniz ve tersine koddan kolayca bir durum diyagramı çizebilmenizdir.

FSM'yi uygulamaya yönelik diğer tekniklerde, geçişlerin yapısı kontrol yapılarına gömülüdür (while, if, switch ...) ve değişkenler değeri tarafından kontrol edilir (tipik olarak a state değişken) ve güzel diyagramı bir kıvrımlı kod.

Bu tekniği, ne yazık ki artık yayınlanmayan büyük "Bilgisayar Dili" dergisinde çıkan bir makaleden öğrendim.


1
Temel olarak, iyi bir FSM'nin tamamı okunabilirlikle ilgilidir. Bu iyi bir arayüz sağlar ve uygulama olabildiğince iyidir. Dilde yerli bir FSM yapısının olmaması çok yazık. Şimdi C1X'e geç bir ekleme olarak görebiliyorum!
Kelden Cowan

3
Gömülü uygulamalar için bu yaklaşımı seviyorum. Bu yaklaşımı olay güdümlü bir durum makinesiyle kullanmanın bir yolu var mı?
ARF

13

Masa yaklaşımını da kullandım. Ancak, genel giderler var. Neden ikinci bir işaretçi listesi depolayasınız? () Olmadan C'deki bir işlev bir sabit göstericidir. Böylece şunları yapabilirsiniz:

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

Elbette korku faktörünüze bağlı olarak (yani güvenlik ve hız) geçerli işaretçileri kontrol etmek isteyebilirsiniz. Üç veya daha fazla durumdan daha büyük durum makineleri için, yukarıdaki yaklaşım, eşdeğer bir anahtar veya masa yaklaşımından daha az talimat olmalıdır. Hatta şu şekilde makro hale getirebilirsiniz:

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

Ayrıca, OP'nin örneğinden, bir durum makinesi hakkında düşünürken / tasarlarken yapılması gereken bir basitleştirme olduğunu hissediyorum. Geçiş durumunun mantık için kullanılması gerektiğini düşünmüyorum. Her durum işlevi, geçmiş durum (lar) ın açık bilgisi olmadan verilen rolünü yerine getirebilmelidir. Temelde, içinde bulunduğunuz durumdan başka bir duruma nasıl geçeceğinizi tasarlarsınız.

Son olarak, "işlevsel" sınırlara dayalı bir durum makinesinin tasarımına başlamayın, bunun için alt işlevleri kullanın. Bunun yerine, devam etmeden önce bir şeyin olmasını beklemeniz gereken zamana göre durumları bölün. Bu, bir sonuç almadan önce durum makinesini kaç kez çalıştırmanız gerektiğini en aza indirmeye yardımcı olacaktır. Bu, G / Ç işlevlerini yazarken veya işleyicileri keserken önemli olabilir.

Ayrıca, klasik anahtar ifadesinin birkaç artı ve eksisi:

Artıları:

  • dilde, bu yüzden belgelenmiş ve açık
  • devletler arandıkları yerde tanımlanır
  • bir işlev çağrısında birden çok durumu çalıştırabilir
  • tüm durumlar için ortak olan kod, switch ifadesinden önce ve sonra çalıştırılabilir

Eksileri:

  • bir işlev çağrısında birden çok durumu çalıştırabilir
  • tüm durumlar için ortak olan kod, switch ifadesinden önce ve sonra çalıştırılabilir
  • anahtar uygulaması yavaş olabilir

Hem pro hem de aleyhte olan iki özelliğe dikkat edin. Sanırım geçiş, devletler arasında çok fazla paylaşım fırsatı sunuyor ve devletler arasındaki karşılıklı bağımlılık yönetilemez hale gelebilir. Bununla birlikte, az sayıda eyalet için, en okunabilir ve bakımı yapılabilir olabilir.


10

Basit bir durum makinesi için, durumunuz için bir switch deyimi ve bir enum türü kullanın. Girişinize göre geçişlerinizi switch deyimi içinde yapın. Gerçek bir programda, geçiş noktalarınızı kontrol etmek için açıkça "if (input)" seçeneğini değiştirirsiniz. Bu yardımcı olur umarım.

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
İşlevin içine "durum" koymaya ve onu statik yapmaya değer olabilir.
Steve Melnikoff

2
@Steve Melnikoff: Yalnızca bir durum makineniz varsa. Bunu işlevin dışında tutun ve kendi durumlarına sahip bir dizi durum makinesine sahip olabilirsiniz.
Vicky

@Vicky: Bir işlev, istenirse, başka bir yerde kullanılmazlarsa işlevin içinde (statik değişkenler olarak) yaşayabilen bir dizi durum değişkeniyle istediğiniz kadar durum makinesi içerebilir.
Steve Melnikoff

10

In Martin Fowler UML Damıtılmış , o (hayır cinas tasarlamak) Bölüm 10 Durum Makinesi Diyagramları (vurgu benim) buyurmuştur:

Bir durum diyagramı üç ana yolla uygulanabilir: iç içe geçmiş anahtar , Durum modeli ve durum tabloları .

Bir cep telefonunun ekranının durumlarının basitleştirilmiş bir örneğini kullanalım:

görüntü açıklamasını buraya girin

İç içe anahtar

Fowler bir C # kodu örneği verdi, ancak bunu örneğime uyarladım.

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

Durum modeli

GoF State kalıbı ile örneğimin bir uygulaması:

görüntü açıklamasını buraya girin

Durum Tabloları

Fowler'dan ilham alarak, işte örneğim için bir tablo:

Kaynak Durum Hedef Durum Olay Koruma Eylemi
-------------------------------------------------- ------------------------------------
Ekran Kapalı Ekran Kapalı düğmesine basın Buton gücü Düşük ekran Düşük Güç Mesaj  
ScreenOff ScreenOn pressButton! PowerLow
ScreenOn ScreenOff basınButton
ScreenOff ScreenCharging fişi Güç
Ekran Üzerinde Ekran Şarj fişi Güç
ScreenCharging ScreenOff unplugPower

karşılaştırma

İç içe geçmiş anahtar, tüm mantığı tek bir noktada tutar, ancak çok sayıda durum ve geçiş olduğunda kodun okunması zor olabilir. Muhtemelen diğer yaklaşımlardan daha güvenli ve daha kolaydır (polimorfizm veya yorumlama yok).

Durum modeli uygulaması, mantığı potansiyel olarak birkaç ayrı sınıfa yayar ve bu da onu bir bütün olarak anlamayı bir sorun haline getirebilir. Öte yandan, küçük sınıfları ayrı ayrı anlamak kolaydır. Geçişler ekleyerek veya kaldırarak davranışı değiştirirseniz tasarım özellikle kırılgandır, çünkü bunlar hiyerarşideki yöntemlerdir ve kodda birçok değişiklik olabilir. Küçük arayüzlerin tasarım prensibine göre yaşıyorsanız, bu modelin pek de iyi olmadığını göreceksiniz. Ancak, durum makinesi kararlıysa, bu tür değişikliklere gerek olmayacaktır.

Durum tabloları yaklaşımı, içerik için bir tür yorumlayıcı yazmayı gerektirir (kullandığınız dilde düşünceleriniz varsa bu daha kolay olabilir) ve bu, önceden yapılması gereken çok iş olabilir. Fowler'ın işaret ettiği gibi, tablonuz kodunuzdan ayrı ise, yazılımınızın davranışını yeniden derlemeden değiştirebilirsiniz. Bununla birlikte, bunun bazı güvenlik etkileri vardır; yazılım, harici bir dosyanın içeriğine göre davranıyor.

Düzenleme (gerçekten C dili için değil)

Muhtemelen birinci sınıf işlevlere sahip diller tarafından kolaylaştırılan akıcı bir arayüz (diğer adıyla dahili Etki Alanına Özel Dil) yaklaşımı da vardır . Vatansız kütüphanesi var ve bu blog gösterileri kodu ile basit bir örnek. Bir Java uygulaması (Java8 öncesi) tartışılmaktadır. GitHub'da da bir Python örneği gösterildim .


Resimleri oluşturmak için hangi yazılımı kullandınız?
sjas



4

Basit durumlar için, anahtar stili yönteminizi yapabilirsiniz. Geçmişte işe yaradığını bulduğum şey, geçişlerle de başa çıkmaktır:

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
}

Yükseltme kitaplığı hakkında hiçbir şey bilmiyorum, ancak bu tür bir yaklaşım son derece basittir, herhangi bir dış bağımlılık gerektirmez ve uygulaması kolaydır.


4

switch (), C'deki durum makinelerini uygulamanın güçlü ve standart bir yoludur, ancak çok sayıda durumunuz varsa, sürekliliği azaltabilir. Diğer bir yaygın yöntem, sonraki durumu depolamak için işlev işaretçileri kullanmaktır. Bu basit örnek, bir set / reset flip-flop'u uygular:

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

Jonathan Valvano ve Ramesh Yerraballi tarafından yazılan edx.org Embedded Systems - Shape the World UTAustinX - UT.6.02x, bölüm 10'da Moore FSM'nin gerçekten kaygan bir C uygulamasını buldum ....

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 oluşturucu yazılımına bakmak isteyebilirsiniz . Bir durum tanımlama dilinden ve / veya bir (Windows) durum diyagramı düzenleyicisinden C, C ++, java ve diğerleri için kod üretebilirsiniz ... artı güzel belgeler ve diyagramlar. İMatix'ten kaynak ve ikili dosyalar



2

En sevdiğim kalıplardan biri durum tasarım kalıbıdır. Verilen aynı girdi kümesine farklı tepki verin veya farklı davranın.
Durum makineleri için anahtar / durum ifadelerini kullanmanın sorunlarından biri, siz daha fazla durum oluşturdukça, anahtar / vakaların okuması / bakımı zorlaşır / hantal hale gelir, organize olmayan spagetti kodunu destekler ve bir şeyi bozmadan değiştirmenin giderek zorlaşmasıdır. Tasarım kalıplarını kullanmanın verilerimi daha iyi düzenlememe yardımcı olduğunu görüyorum, bu da soyutlamanın tüm noktası. Durum kodunuzu geldiğiniz eyalete göre tasarlamak yerine, kodunuzu yeni bir duruma girdiğinizde durumu kaydedecek şekilde yapılandırın. Bu şekilde, önceki durumunuzun kaydını etkili bir şekilde alırsınız. @ JoshPetit'in yanıtını beğendim ve doğrudan GoF kitabından alınan çözümünü bir adım öteye taşıdım:

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

Çoğu Durum Makinesi için, özellikle. Sonlu durum makineleri, her durum bir sonraki durumunun ne olması gerektiğini ve bir sonraki duruma geçiş kriterlerini bilecektir. Gevşek durum tasarımları için durum böyle olmayabilir, dolayısıyla durum geçişleri için API'yi açığa çıkarma seçeneği olabilir. Daha fazla soyutlama istiyorsanız, her durum işleyicisi, GoF kitabındaki somut durum işleyicilerine eşdeğer olan kendi dosyasına ayrılabilir. Tasarımınız yalnızca birkaç durumla basitse, o zaman hem stateCtxt.c hem de statehandlers.c basitlik için tek bir dosyada birleştirilebilir.


State3 ve State2, void olarak bildirilmesine rağmen dönüş değerlerine sahiptir.
Karınca

1

Tecrübelerime göre, birden çok olası durumu ele almanın standart yolu, 'anahtar' ifadesini kullanmaktır. Durum başına işleme bir geçiş değeri geçirdiğinize şaşırmış olsam da. Bir durum makinesinin tüm amacının, her bir durumun tek bir eylem gerçekleştirmesi olduğunu sanıyordum. Sonra bir sonraki eylem / girdi, hangi yeni duruma geçileceğini belirler. Bu nedenle, her durum işleme işlevinin duruma girmek için sabit olanı hemen gerçekleştirmesini ve daha sonra başka bir duruma geçişin gerekip gerekmediğine karar vermesini beklerdim.


2
Farklı temel modeller vardır: Mealy makineleri ve Moore makineleri. Mealy'nin eylemleri geçişe bağlıdır, Moore'un eylemleri duruma bağlıdır.
xmjx

1

C / C ++ 'da Practical Statecharts adlı bir kitap var . Ancak, öyle bir yol ihtiyacımız olan şey için çok ağır.


2
Kitaba tamamen aynı tepkiyi verdim. Oldukça sezgisel ve anlaşılır olduğunu düşündüğüm bir şeyi tanımlamak ve uygulamak için 700'den fazla sayfaya nasıl ihtiyaç duyulabilir?!?!?
Dan

1

Destekleyen derleyici için __COUNTER__, bunları basit (ama büyük) durum mayınları için kullanabilirsiniz.

  #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__Sabit kodlanmış sayılar yerine kullanmanın avantajı, her seferinde her şeyi yeniden numaralandırmadan diğer durumların ortasına durum ekleyebilmenizdir. Derleyici __COUNTER__, sınırlı bir şekilde desteklemiyorsa , önlemle kullanılması mümkündür.__LINE__


Cevabınızı biraz daha açıklar mısınız?
abarisone

Normal bir "anahtar" durum mashinde, örneğin durum 0, durum 1, durum 2, ... durum 100'e sahip olursunuz. Şimdi 5 ile 6 arasında 3 durum eklemek istiyorsanız, geri kalanını 100 olarak yeniden numaralandırmanız gerekir; şimdi 103 olacaktır __COUNTER__. Ön derleyici numaralandırmayı derleme sırasında yaptığı için yeniden numaralandırma ihtiyacını ortadan kaldırır.
Seb

1

Minimalist UML durum makinesi çerçevesini c. https://github.com/kiishor/UML-State-Machine-in-C

Hem sonlu hem de hiyerarşik durum makinesini destekler. Yalnızca 3 API, 2 yapı ve 1 numaralandırması vardır.

Durum makinesi state_machine_tyapı ile temsil edilir . Bir durum makinesi oluşturmak için miras alınabilen soyut bir yapıdır.

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

Durum, state_tçerçevedeki yapıya işaretçi ile temsil edilir .

Çerçeve, sonlu durum makinesi için yapılandırılmışsa, şunları state_tiçerir:

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;

Çerçeve dispatch_event, olayı durum makinesine göndermek için bir API ve durum geçişi için iki API sağlar.

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*);

Hiyerarşik durum makinesinin nasıl uygulanacağıyla ilgili daha fazla ayrıntı için GitHub deposuna bakın.

kod örnekleri
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 / damla / ana / sunum / simple_state_machine_enhanced / readme.md


soruya uyan bir kod örneği de ekleyebilir misiniz?
Giulio Caccin

1
Depodaki demo klasörünün bir örneği vardır. github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/… . Şu anda anahtar, led ve zamanlayıcıları içeren bir başka gömülü sistem örneği üzerinde çalışıyorum, yine de tamamlanmadı. Hazır olduğunda size haber verecek.
Nandkishor Biradar


0

Sorunuz "tipik bir Veri Tabanı uygulama modeli var mı" na benzer? Cevap, neyi başarmak istediğinize bağlıdır? Daha büyük bir deterministik durum makinesi uygulamak istiyorsanız, bir model ve bir durum makinesi üreteci kullanabilirsiniz. Örnekler www.StateSoft.org - SM Gallery adresinde görülebilir. Janusz Dobrowolski


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.