একটি সাধারণ রাষ্ট্র মেশিন বাস্তবায়ন প্যাটার্ন আছে?


118

আমাদের সিতে একটি সরল রাষ্ট্রীয় যন্ত্র প্রয়োগ করতে হবে ।
একটি স্ট্যান্ডার্ড সুইচ স্টেটমেন্ট কি সেরা উপায়?
আমাদের কাছে বর্তমান অবস্থা (রাষ্ট্র) এবং উত্তরণের জন্য একটি ট্রিগার রয়েছে।


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

সরল রাষ্ট্রের মেশিনগুলির জন্য কি আরও ভাল উপায় আছে?

সম্পাদনা: সি ++ এর জন্য আমি মনে করি বুস্ট স্টেটচার্ট লাইব্রেরিটি যাওয়ার উপায় হতে পারে। তবে এটি সি এর সাহায্য করে না সি ব্যবহারের ক্ষেত্রে মনোনিবেশ করতে দেয়।


উত্তর:


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

একাধিক রাজ্য মেশিন, ইত্যাদি সমর্থন করার জন্য অবশ্যই এটি বাড়ানো যেতে পারে 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;
};

টেবিল চালিত পদ্ধতির বজায় রাখা এবং প্রসারিত করা সহজ এবং রাষ্ট্রের চিত্রগুলিতে মানচিত্রের পক্ষে সহজ।


শুরু করার খুব সুন্দর উপায়, আমার পক্ষে কমপক্ষে শুরুর পয়েন্ট। একটি মন্তব্য, রান_স্টেটের প্রথম লাইনে () দুষ্টু আছে "।" এটা সেখানে থাকা উচিত নয়।
আটিলা ফিলিজ

2
এই উত্তরটি যদি অন্য দুটি পদ্ধতির সম্পর্কে কমপক্ষে 2 টি শব্দও বলে থাকে তবে ভাল হবে: একটি "গ্লোবাল" পদ্ধতিটি একটি বড় সুইচ-কেস সহ এবং স্টেট ডিজাইন প্যাটার্নের সাথে রাজ্যগুলিকে পৃথক করে এবং প্রতিটি রাজ্যকে নিজের রূপান্তর নিজেই পরিচালনা করতে দেয়।
এরিকবওয়ার্ক

হাই, আমি জানি এই পোস্টটি পুরানো তবে আমি আশা করি আমি আমার উত্তরটি পেয়ে যাব :) উদাহরণস্বরূপ_ডাটা_টি পরিবর্তনশীল দ্বারা অবশ্যই কী হওয়া উচিত? আমি ভাবছি কীভাবে বাধাগুলিতে রাজ্যগুলি পরিবর্তন করা যায় ... এই ভেরিয়েবলটিতে প্রক্রিয়াজাত বাধা সম্পর্কে তথ্য সংরক্ষণ করার কি ভাল উপায়? উদাহরণস্বরূপ স্টোর সম্পর্কিত তথ্য যা বোতামটি টিপেছিল তাই রাষ্ট্র পরিবর্তন করা উচিত।
গ্রানগার

@ জিআরএনজিওআর আমার কাছে মনে হচ্ছে আপনি কোনও ইভেন্ট-চালিত স্টেট মেশিনের সাথে কাজ করছেন। আমি মনে করি আপনি ইভেন্টের ডেটা সংরক্ষণ করার জন্য এটি ব্যবহার করতে পারেন।
জিমানো

3
কীভাবে NUM_STATES সংজ্ঞায়িত করা হয়েছে তা সত্যিই দুর্দান্ত স্পর্শ।
Albin স্টিগো

25

আপনি অন্য সি প্রশ্নের আমার উত্তরটি দেখে থাকতে পারেন যেখানে আমি এফএসএম উল্লেখ করেছি! এখানে আমি এটি কীভাবে করছি:

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:

এই পদ্ধতির ভাল জিনিসটি হ'ল আপনি কার্যকারী কোডে আঁকেন এমন একটি রাজ্য চিত্রটি সরাসরি অনুবাদ করতে পারেন এবং বিপরীতভাবে আপনি কোড থেকে খুব সহজেই একটি রাষ্ট্র চিত্র আঁকতে পারেন।

এফএসএম বাস্তবায়নের জন্য অন্যান্য কৌশলগুলিতে ট্রানজিশনের কাঠামোটি নিয়ন্ত্রণ কাঠামোয় সমাহিত করা হয় (যখন, যদি স্যুইচ ...) এবং ভেরিয়েবল মান (টিপিকভাবে একটি stateভেরিয়েবল) দ্বারা নিয়ন্ত্রিত হয় এবং সুন্দর চিত্রটি কোনওটির সাথে সংযুক্ত করা জটিল কাজ হতে পারে বিভ্রান্তিকর কোড

আমি এই কৌশলটি শিখেছি একটি নিবন্ধ থেকে দুর্দান্ত "কম্পিউটার ভাষা" ম্যাগাজিনে প্রকাশিত হয়েছিল যা দুর্ভাগ্যক্রমে, আর প্রকাশিত হয় না।


1
মূলত, একটি ভাল এফএসএম হ'ল পাঠযোগ্যতা সম্পর্কে। এটি একটি ভাল ইন্টারফেস সরবরাহ করে এবং প্রয়োগটি যতটা তা পায় তত ভাল। এটি লজ্জার বিষয় যে ভাষাতে কোনও স্থানীয় এফএসএম কাঠামো নেই। আমি এটি এখন সি 1 এক্স এর দেরী সংযোজন হিসাবে দেখতে পাচ্ছি!
ক্যাল্ডেন কোয়ান

3
এম্বেড থাকা অ্যাপ্লিকেশনগুলির জন্য আমি এই পদ্ধতিটি পছন্দ করি। ইভেন্ট-চালিত রাষ্ট্র মেশিনের সাথে এই পদ্ধতির ব্যবহার করার কোনও উপায় আছে কি?
এআরএফ

13

আমি টেবিল পদ্ধতিরও ব্যবহার করেছি। তবে ওভারহেড আছে। পয়েন্টারগুলির একটি দ্বিতীয় তালিকা কেন সঞ্চয় করবেন? () ছাড়া সি-তে একটি ফাংশন হ'ল একটি কনস্ট পয়েন্টার। সুতরাং আপনি এটি করতে পারেন:

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

অবশ্যই আপনার ভয় ফ্যাক্টরের উপর নির্ভর করে (যেমন সুরক্ষা বনাম গতি) আপনি বৈধ পয়েন্টারগুলির জন্য যাচাই করতে চাইতে পারেন। তিন বা ততোধিক রাষ্ট্রের চেয়ে বড় রাষ্ট্রীয় মেশিনগুলির জন্য, উপরের পদ্ধতির সমতুল্য সুইচ বা টেবিলের পদ্ধতির চেয়ে কম নির্দেশ থাকতে হবে। আপনি এমনকি ম্যাক্রো-আইজও করতে পারেন:

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

এছাড়াও, আমি ওপি-র উদাহরণ থেকে অনুভব করি যে, একটি সরকারীকরণ রয়েছে যা কোনও রাষ্ট্রের মেশিনের বিষয়ে / নকশা করার বিষয়ে চিন্তা করার সময় করা উচিত। আমি রূপান্তরকারী রাষ্ট্রটি যুক্তির জন্য ব্যবহার করা উচিত বলে মনে করি না। প্রতিটি রাষ্ট্রের ক্রিয়াকলাপ অতীতের রাজ্য (গুলি) এর সুস্পষ্ট জ্ঞান ছাড়াই তার প্রদত্ত ভূমিকা সম্পাদন করতে সক্ষম হওয়া উচিত। মূলত আপনি যে রাজ্য থেকে অন্য রাজ্যে চলেছেন সেখান থেকে কীভাবে রূপান্তর করা যায় তার জন্য আপনি ডিজাইন করেন।

অবশেষে, "কার্যকরী" সীমানার উপর ভিত্তি করে কোনও রাজ্য মেশিনের নকশা শুরু করবেন না, এর জন্য উপ-ফাংশন ব্যবহার করুন। পরিবর্তে রাজ্যগুলিকে ভাগ করুন যখন আপনি চালিয়ে যাওয়ার আগে কিছু হওয়ার জন্য অপেক্ষা করতে হবে। এটি ফলাফল পাওয়ার আগে আপনাকে রাষ্ট্রীয় মেশিনটি চালানোর সংখ্যা কমাতে সহায়তা করবে। আই / ও ফাংশন লেখার সময় বা হ্যান্ডলারগুলিকে বাধা দেওয়ার ক্ষেত্রে এটি গুরুত্বপূর্ণ হতে পারে।

এছাড়াও, ক্লাসিক স্যুইচ বিবৃতিটির কয়েকটি কার্যকারিতা এবং স্বভাব:

পেশাদাররা:

  • এটি ভাষায় রয়েছে, সুতরাং এটি নথিভুক্ত এবং পরিষ্কার is
  • রাজ্যগুলি যেখানে ডাকা হয় সেখানে সংজ্ঞায়িত হয়
  • এক ফাংশন কলে একাধিক রাজ্য কার্যকর করতে পারে
  • সমস্ত রাজ্যে সাধারণ কোডটি স্যুইচ স্টেটমেন্টের আগে এবং পরে কার্যকর করা যেতে পারে

কনস:

  • এক ফাংশন কলে একাধিক রাজ্য কার্যকর করতে পারে
  • সমস্ত রাজ্যে সাধারণ কোডটি স্যুইচ স্টেটমেন্টের আগে এবং পরে কার্যকর করা যেতে পারে
  • স্যুইচ বাস্তবায়ন ধীর হতে পারে

প্রো এবং কন উভয় হিসাবে দুটি বৈশিষ্ট্য নোট করুন। আমি মনে করি যে স্যুইচ রাজ্যগুলির মধ্যে খুব বেশি ভাগ করে নেওয়ার সুযোগকে মঞ্জুরি দেয় এবং রাজ্যগুলির মধ্যে আন্তঃনির্ভরতা নিয়ন্ত্রণহীন হয়ে উঠতে পারে। তবে অল্প সংখ্যক রাজ্যের ক্ষেত্রে এটি সর্বাধিক পঠনযোগ্য এবং রক্ষণাবেক্ষণযোগ্য হতে পারে।


10

সাধারণ রাষ্ট্রের মেশিনের জন্য আপনার রাজ্যের জন্য একটি সুইচ স্টেটমেন্ট এবং একটি এনাম টাইপ ব্যবহার করুন। আপনার ইনপুটের উপর ভিত্তি করে আপনার স্যুইচ স্টেটমেন্টের অভ্যন্তরে রূপান্তর করুন। একটি বাস্তব প্রোগ্রামে আপনি অবশ্যই আপনার রূপান্তর পয়েন্টগুলি পরীক্ষা করতে "যদি (ইনপুট)" পরিবর্তন করেন। আশাকরি এটা সাহায্য করবে.

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
@ স্টিভ মেলানিকফ: কেবলমাত্র যদি আপনি কেবল একটি রাষ্ট্রীয় মেশিন পেয়ে থাকেন। এটিকে ফাংশনের বাইরে রাখুন এবং তাদের নিজস্ব রাজ্য সহ রাজ্য মেশিনগুলির একটি অ্যারে রাখতে পারেন।
ভিকি

@ ভিকি: একটি ফাংশনটিতে আপনার পছন্দ মতো অনেকগুলি স্টেট মেশিন থাকতে পারে, প্রয়োজনে রাষ্ট্রীয় ভেরিয়েবলগুলির একটি অ্যারে রয়েছে, যা অন্য কোথাও ব্যবহার না করা হলে ফাংশনের অভ্যন্তরে (স্থির ভেরিয়েবল হিসাবে) থাকতে পারে।
স্টিভ মেলানিকফ

10

ইন মার্টিন জালিয়া এর ইউএমএল মদের তিনি যুক্তরাষ্ট্রের (কোন শ্লেষ উদ্দেশ্যে) অধ্যায় 10 রাজ্য মেশিন রেখাচিত্র (জোর খনি) এ:

রাষ্ট্রীয় চিত্রটি তিনটি প্রধান উপায়ে প্রয়োগ করা যেতে পারে: নেস্টেড সুইচ , রাজ্য প্যাটার্ন এবং রাষ্ট্রের সারণী

মোবাইল ফোনের প্রদর্শনের রাজ্যের একটি সরল উদাহরণ ব্যবহার করা যাক:

এখানে চিত্র বর্ণনা লিখুন

নেস্টেড সুইচ

ফোলার সি # কোডের উদাহরণ দিয়েছিল, তবে আমি এটি আমার উদাহরণের সাথে মানিয়ে নিয়েছি।

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

রাজ্য প্যাটার্ন

এখানে জিওএফ স্টেট প্যাটার্ন সহ আমার উদাহরণটির বাস্তবায়ন:

এখানে চিত্র বর্ণনা লিখুন

রাষ্ট্র সারণী

ফোলারের কাছ থেকে অনুপ্রেরণা গ্রহণ করে, এখানে আমার উদাহরণের জন্য একটি টেবিল রয়েছে:

উত্স রাজ্য টার্গেট রাজ্য ইভেন্ট গার্ড অ্যাকশন
-------------------------------------------------- ------------------------------------
স্ক্রিনঅফ স্ক্রিন অফ স্ক্রিনঅফ টিপুন বাটন পাওয়ারলপ ডিসপ্লেলওপাওয়ারমেসেজ  
স্ক্রিন অফ স্ক্রিন অন প্রেসবটন! পাওয়ারলও
স্ক্রিনঅন স্ক্রিনঅফ প্রেসবটন
স্ক্রিনঅফ স্ক্রিনচার্জিং প্লাগ পাওয়ার
স্ক্রীন ও স্ক্রিনচার্জিং প্লাগ পাওয়ার
স্ক্রিনচার্জিং স্ক্রীনঅফ প্লাগ পাওয়ার

তুলনা

নেস্টেড সুইচ সমস্ত যুক্তিগুলিকে এক জায়গায় রাখে, তবে প্রচুর রাজ্য এবং ট্রানজিশন থাকাকালীন কোডটি পড়া খুব কঠিন hard এটি অন্যান্য পদ্ধতির চেয়ে বৈধতা পাওয়ার জন্য সম্ভবত আরও সুরক্ষিত এবং সহজ (কোনও পলিমারফিজম বা ব্যাখ্যা নেই)।

রাজ্য প্যাটার্ন বাস্তবায়ন সম্ভাব্যভাবে বিভিন্ন পৃথক শ্রেণীর উপর যুক্তি ছড়িয়ে দেয়, এটি সামগ্রিকভাবে বুঝতে সমস্যা তৈরি করতে পারে। অন্যদিকে, ছোট শ্রেণিগুলি পৃথকভাবে বোঝা সহজ। আপনি রূপান্তরগুলি যুক্ত বা মুছে ফেলার মাধ্যমে আচরণটি পরিবর্তন করে যদি ডিজাইনটি বিশেষত নাজুক হয় কারণ এগুলি হায়ারার্কির পদ্ধতি এবং কোডে প্রচুর পরিবর্তন হতে পারে। আপনি যদি ছোট ইন্টারফেসগুলির ডিজাইনের নীতি অনুসারে বাস করেন তবে আপনি দেখতে পাবেন যে এই প্যাটার্নটি আসলে এত ভাল করে না। তবে, যদি রাষ্ট্রের মেশিনটি স্থিতিশীল থাকে তবে এই জাতীয় পরিবর্তনের প্রয়োজন হবে না।

রাষ্ট্রের টেবিলগুলির পদ্ধতির জন্য সামগ্রীর জন্য একরকম দোভাষী লিখতে হবে (আপনি যে ভাষাটি ব্যবহার করছেন তাতে প্রতিবিম্ব থাকলে এটি আরও সহজ হতে পারে), যা সামনে কাজ করার জন্য অনেক কাজ হতে পারে। ফওলার যেমন উল্লেখ করেছেন, আপনার টেবিলটি যদি আপনার কোড থেকে পৃথক হয়, আপনি পুনরায় সংশোধন না করেই আপনার সফ্টওয়্যারটির আচরণটি পরিবর্তন করতে পারেন। তবে এর কিছু সুরক্ষা জড়িত রয়েছে; সফ্টওয়্যারটি একটি বাহ্যিক ফাইলের সামগ্রীর উপর ভিত্তি করে আচরণ করছে।

সম্পাদনা করুন (সি ভাষার জন্য সত্যই নয়)

একটি সাবলীল ইন্টারফেস (ওরফে অভ্যন্তরীণ ডোমেন নির্দিষ্ট ভাষা) পদ্ধতিরও রয়েছে, এটি সম্ভবত প্রথম শ্রেণির ফাংশনযুক্ত ভাষাগুলি দ্বারা সহজতর করা হয়েছে । Stateless গ্রন্থাগার বিদ্যমান এবং যে ব্লগ কোড সহ একটি সহজ উদাহরণ দেখায়। একটি জাভা বাস্তবায়ন (পূর্ব জাভা 8) নিয়ে আলোচনা করা হয়েছে। আমাকে গিটহাবেও পাইথনের উদাহরণ দেখানো হয়েছিল ।


ছবি তৈরি করতে আপনি কোন সফটওয়্যার ব্যবহার করেছেন?
sjas

1
আমার সন্দেহ হয় এটি প্লান্টএমএল প্ল্যান্টামল
স্টেট


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

স্যুইচ () হ'ল সিতে রাষ্ট্রীয় মেশিনগুলি প্রয়োগ করার একটি শক্তিশালী এবং মানক উপায়, তবে আপনার যদি প্রচুর পরিমাণে রাজ্য থাকে তবে এটি রক্ষণাবেক্ষণযোগ্যতা হ্রাস করতে পারে। আর একটি সাধারণ পদ্ধতি হ'ল পরবর্তী স্থিতি সঞ্চয় করতে ফাংশন পয়েন্টার ব্যবহার করা। এই সাধারণ উদাহরণটি সেট / রিসেট ফ্লিপ-ফ্লপ প্রয়োগ করে:

/* 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 কোর্সে এম্বেডড সিস্টেমগুলিতে মুর এফএসএম-এর সত্যিকারের স্লট সি বাস্তবায়ন পেয়েছি - জোনাথন ভালভানো এবং রমেশ ইয়েরাবল্লি দ্বারা নির্মিত ওয়ার্ল্ড ইউটিএস্টিনএক্স - ইউটি .0.০২x, দশম অধ্যায়ে ....

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

আপনি লাইবারো এফএসএম জেনারেটর সফ্টওয়্যারটি সন্ধান করতে পারেন । রাজ্যের বর্ণনামূলক ভাষা এবং / অথবা একটি (উইন্ডোজ) স্টেট ডায়াগ্রাম সম্পাদক থেকে আপনি সি, সি ++, জাভা এবং আরও অনেকের জন্য কোড তৈরি করতে পারেন ... পাশাপাশি দুর্দান্ত ডকুমেন্টেশন এবং ডায়াগ্রাম। আইমেটিক্স থেকে উত্স এবং বাইনারি


2

এই নিবন্ধটি রাষ্ট্রীয় প্যাটার্নের জন্য ভাল (যদিও এটি সি ++, বিশেষত সি নয়)।

আপনি যদি " হেড ফার্স্ট ডিজাইন প্যাটার্নস " বইটিতে হাত রাখতে পারেন তবে ব্যাখ্যা এবং উদাহরণটি খুব পরিষ্কার।


2

আমার প্রিয় নিদর্শনগুলির একটি হ'ল রাজ্য ডিজাইনের ধরণ। একই প্রদত্ত ইনপুটগুলির সেটটিতে প্রতিক্রিয়া জানান বা আচরণ করুন।
রাষ্ট্রের মেশিনগুলির জন্য স্যুইচ / কেস স্টেটমেন্টগুলি ব্যবহারে অন্যতম সমস্যা হ'ল আপনি আরও রাজ্য তৈরি করার সাথে সাথে সুইচ / কেসগুলি পড়া / রক্ষণাবেক্ষণ করা শক্ত / অস্বাস্থ্যকর হয়ে ওঠে, অসংগঠিত স্প্যাগেটি কোড প্রচার করে এবং কিছু না ভাঙ্গিয়ে পরিবর্তনের পক্ষে ক্রমবর্ধমান কঠিন। আমি ডিজাইন নিদর্শনগুলি ব্যবহার করে আমার ডেটা আরও ভালভাবে সংগঠিত করতে সহায়তা করি যা বিমূর্তির পুরো বিষয়টি। আপনি কোন রাজ্য থেকে এসেছেন তার চারপাশে আপনার রাষ্ট্র কোডটি ডিজাইনের পরিবর্তে আপনার কোডটি এমনভাবে গঠন করুন যাতে আপনি একটি নতুন রাজ্যে প্রবেশ করার সময় এটি রাষ্ট্রটি রেকর্ড করে। এইভাবে, আপনি কার্যকরভাবে আপনার আগের অবস্থার একটি রেকর্ড পাবেন। আমি @ জোশপেটির উত্তরটি পছন্দ করি এবং তার সমাধানটি আরও একধাপ এগিয়ে নিয়ে এসেছি, জিওএফ বই থেকে সোজা:

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

বেশিরভাগ স্টেট মেশিনের জন্য, এসএসপি। সীমাবদ্ধ রাষ্ট্র মেশিনগুলি, প্রতিটি রাজ্য তার পরবর্তী রাজ্যটি কী হবে এবং তার পরবর্তী রাজ্যে স্থানান্তরিত করার মানদণ্ড জানতে পারবে। Looseিলে designsালা রাষ্ট্রের নকশাগুলির জন্য, এটি এমনটি নাও হতে পারে, তাই রাজ্যগুলিকে রূপান্তরিত করার জন্য API প্রকাশ করার বিকল্প। আপনি যদি আরও বিমূর্ততা চান, তবে প্রতিটি রাষ্ট্রীয় হ্যান্ডলারকে তার নিজস্ব ফাইলে আলাদা করা যেতে পারে, যা জিওএফ বইয়ের কংক্রিটের রাজ্য হ্যান্ডলারের সমতুল্য। যদি আপনার ডিজাইনটি কয়েকটি কয়েকটি রাজ্যের সাথে সহজ হয়, তবে stateCtxt.c এবং statehandlers.c উভয়ই সরলতার জন্য একক ফাইলে একত্রিত হতে পারে।


স্টেট 3 এবং স্টেট 2 এর শূন্য ঘোষিত সত্ত্বেও রিটার্ন মান রয়েছে।
পিঁপড়

1

আমার অভিজ্ঞতার মধ্যে 'সুইচ' বিবৃতিটি ব্যবহার করা একাধিক সম্ভাব্য রাজ্য পরিচালনা করার মানক উপায়। যদিও আমি উদ্বিগ্ন হয়ে পড়েছি যে আপনি প্রতি-রাষ্ট্রের প্রক্রিয়াকরণে একটি রূপান্তর মান দিয়ে যাচ্ছেন। আমি ভেবেছিলাম একটি রাষ্ট্রের মেশিনের পুরো বিষয়টি হ'ল প্রতিটি রাষ্ট্রই একক ক্রিয়া সম্পাদন করে। তারপরে পরবর্তী ক্রিয়া / ইনপুটটি কোন নতুন অবস্থানে রূপান্তরিত হবে তা নির্ধারণ করে। সুতরাং আমি প্রতিটি রাজ্য প্রক্রিয়াজাতকরণের কাজটি রাষ্ট্রের প্রবেশের জন্য যা স্থির করে তা তত্ক্ষণাত্ সম্পাদন করে প্রত্যাশা করতাম এবং তারপরে সিদ্ধান্ত নেব যে অন্য রাজ্যে স্থানান্তরের প্রয়োজন কিনা।


2
বিভিন্ন অন্তর্নিহিত মডেল রয়েছে: মেলি মেশিন এবং মুর মেশিন। মেলার ক্রিয়াগুলি স্থানান্তরের উপর নির্ভর করে, মুরের কর্মগুলি রাজ্যের উপর নির্ভর করে।
xmjx

1

সি / সি ++ এ প্রাকটিক্যাল স্টেটচার্টস নামে একটি বই রয়েছে । যাইহোক, এটা হয় উপায় আমরা যা প্রয়োজন জন্য খুব হেভিওয়েট।


2
বইটিতে আমার ঠিক একই প্রতিক্রিয়া ছিল। আমার মনে হয় যে এটি বেশ স্বজ্ঞাত এবং সোজা??!?!?
ড্যান

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 রয়েছে 100 আপনি যদি এখন 5 থেকে 6 এর মধ্যে 3 কেস যুক্ত করতে চান তবে আপনাকে বাকীটি 100 করে রাখতে হবে, যা এখন 103 হবে ren ​​ব্যবহারটি __COUNTER__পুনর্নবীকরণের প্রয়োজনীয়তা দূর করে, কারণ সংকলনের সময় পূর্ববর্তী সংস্থাপক সংখ্যাটি করেন।
সেব

1

আপনি সিটিতে মিনিমালিস্ট ইউএমএল স্টেট মেশিনের কাঠামো ব্যবহার করতে পারেন। https://github.com/kiishor/UML-State-Machine-in-C

এটি সসীম এবং শ্রেণিবদ্ধ রাষ্ট্র মেশিন উভয় সমর্থন করে। এটিতে কেবল 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ইভেন্টটি রাষ্ট্রের মেশিনে প্রেরণের জন্য একটি এপিআই এবং রাষ্ট্রীয় ট্র্যাভারসালের জন্য দুটি এপিআই সরবরাহ করে।

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

শ্রেণিবদ্ধ স্টেট মেশিন কীভাবে প্রয়োগ করা যায় সে সম্পর্কে আরও তথ্যের জন্য গিটহাব সংগ্রহস্থলটি পড়ুন।

কোড উদাহরণ
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 / ফোঁটা / মাস্টার / ডেমো / simple_state_machine_enhanced / readme.md


আপনি কি এমন একটি উদাহরণ উদাহরণ যুক্ত করতে পারেন যা প্রশ্নের সাথে খাপ খায়?
জিউলিও ক্যাক্সিন

1
সংগ্রহস্থলের ডেমো ফোল্ডারের একটি উদাহরণ রয়েছে। github.com/kiishor/UML-State-Machine-in-C/blob/master/demo/… । আমি বর্তমানে আরও একটি এম্বেড থাকা সিস্টেমের উদাহরণে কাজ করছি যাতে কী, নেতৃত্বাধীন এবং টাইমার জড়িত রয়েছে, এখনও এটি সম্পূর্ণ নয়। এটি প্রস্তুত হয়ে গেলে আপনাকে জানাতে দেবে।
নন্দকিশোর বিড়াদার


0

আপনার প্রশ্নটির অনুরূপ "একটি সাধারণ ডেটা বেস প্রয়োগের ধরণ আছে"? উত্তর আপনি কী অর্জন করতে চান তার উপর নির্ভর করে? আপনি যদি একটি বৃহত্তর ডিস্ট্রিমেন্টিক স্টেট মেশিন বাস্তবায়ন করতে চান তবে আপনি একটি মডেল এবং একটি রাষ্ট্রীয় যন্ত্র জেনারেটর ব্যবহার করতে পারেন। উদাহরণগুলি www.StateSoft.org - এস এম গ্যালারীতে দেখা যায়। জানুস ডব্রোওলস্কি


আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.