C 언어로 상태 머신 다이어그램 패턴화

상태 머신 다이어그램을 C 언어로 매핑



공용 언어인 UML로부터 스테이트 머신 다이어그램에 대하여, 비 오브젝트 지향인 C 언어에 매핑하기 위한 템플릿 코드를 소개한다.

모델의 템플릿 코드에 의해 조직이나 팀내에서 공유할 수 있는 것이 목표. (C++의 디자인 패턴 같은 녀석을 목표로 해봤어?)

예시적인 상태 머신 다이어그램



스테이트 머신 다이어그램 자체의 설명은 아래 또는 google에서 검색하여 조사하고 싶다.
위키피디아 상태 천이도

샘플 상태 머신 다이어그램





샘플 상태 머신의 C 소스 코드



모델 다이어그램에 대해 소스 코드는 일정 횟수 상태 천이를 반복적으로 종료하도록 구현됩니다. 또, 각 상태내의 액션을 printf문으로 표시하도록 하고 있다.

소스 파일 전단 정의

main.c

#include "stdio.h"

/** アクション定数 */
#define  FSM_ENTRY     ((unsigned char)0x01)
#define  FSM_DO        ((unsigned char)0x02)
#define  FSM_EXIT      ((unsigned char)0x03)

/** 状態定数 */
#define STATE_XXXXX    ((unsigned char)0x00)
#define STATE_YYYYY    ((unsigned char)0x01)
#define STATE_ZZZZZ    ((unsigned char)0x02)
#define STATE_NUM      ((unsigned char)0x03)  


static unsigned char   temp_cnt;

/**
* @brief 状態変数型定義
*/
typedef struct
{
    unsigned char  crreStat;   /* 現在の状態 */
    unsigned char  newStat;    /* 遷移先の状態 */
}ST_FMS_STATE_INFO;


상태 머신 이벤트 및 액션 함수 및 테이블 정의

main.c

/**
* @brief イベント関数
*/
static unsigned char    XXXXX_Fsm_Event(void);
static unsigned char    YYYYY_Fsm_Event(void);
static unsigned char    ZZZZZ_Fsm_Event(void);

/**
* @brief 状態関数(アクション)
*/
static void  XXXXX_Fsm_State(unsigned char cmd);
static void  YYYYY_Fsm_State(unsigned char cmd);
static void  ZZZZZ_Fsm_State(unsigned char cmd);

/**
* @brief 状態制御関数のテーブル型定義
*/
typedef struct
{
    unsigned char(*fmc_Event)(void);
             void(*fmc_state)(unsigned char cmd);
}ST_FMS_TYPE;



/**
* @brief ステートマシン関数テーブル
*/
const static ST_FMS_TYPE sts_fms_stateInst[STATE_NUM] =
{
    { &XXXXX_Fsm_Event, &XXXXX_Fsm_State},
    { &YYYYY_Fsm_Event, &YYYYY_Fsm_State},
    { &ZZZZZ_Fsm_Event, &ZZZZZ_Fsm_State}
};

main.c

/**
* @brief 状態XXXXXから呼ばれるイベント処理を実施
* @param NONE
* @return  遷移先の状態
*/
static unsigned char  XXXXX_Fsm_Event(void)
{
    unsigned char  ret = STATE_XXXXX;

    if (temp_cnt >= 1)
    {
        ret = STATE_YYYYY;
    }

    return ret;
}

/**
* @brief 状態YYYYYから呼ばれるイベント処理を実施
* @param NONE
* @return  遷移先の状態
*/
static unsigned char  YYYYY_Fsm_Event(void)
{
    unsigned char  ret = STATE_YYYYY;

    if (temp_cnt >= 1)
    {
        ret = STATE_ZZZZZ;
    }

    return ret;
}

/**
* @brief 状態ZZZZZから呼ばれるイベント処理を実施
* @param NONE
* @return  遷移先の状態
*/
static unsigned char  ZZZZZ_Fsm_Event(void)
{
    unsigned char ret = STATE_ZZZZZ;

    if (temp_cnt >= 1)
    {
        ret = STATE_XXXXX;
    }

    return ret;
}

main.c

/**
* @brief 状態Xのアクション処理を実施
* @param cmd=>アクション定義(ENTRY,DO,EXIT)
* @return  遷移先の状態
*/
static void  XXXXX_Fsm_State(unsigned char cmd)
{
    switch (cmd)
    {
    case FSM_ENTRY:
        printf("XXXXXのENTRY処理\n");
        temp_cnt = 0;
        break;
    case FSM_DO:
        printf(">>XXXXXのDO処理\n");
        temp_cnt++;
        break;
    case FSM_EXIT:
        printf(">>>>XXXXXのEXIT処理\n\n");
        break;
    default:
        /* 処理なし */
        break;
    }
}


/**
* @brief 状態YYYYYのアクション処理を実施
* @param cmd=>アクション定義(ENTRY,DO,EXIT)
* @return  遷移先の状態
*/
static void  YYYYY_Fsm_State(unsigned char cmd)
{
    switch (cmd)
    {
    case FSM_ENTRY:
        printf("YYYYYのENTRY処理\n");
        temp_cnt = 0;
        break;
    case FSM_DO:
        printf(">>YYYYYのDO処理\n");
        temp_cnt++;
        break;
    case FSM_EXIT:
        printf(">>>>YYYYYのEXIT処理\n\n");
        break;
    default:
        /* 処理なし */
        break;
    }
}


/**
* @brief 状態ZZZZZのアクション処理を実施
* @param cmd=>アクション定義(ENTRY,DO,EXIT)
* @return  遷移先の状態
*/
static void  ZZZZZ_Fsm_State(unsigned char cmd)
{
    switch (cmd)
    {
    case FSM_ENTRY:
        printf("ZZZZZのENTRY処理\n");
        temp_cnt = 0;
        break;
    case FSM_DO:
        printf(">>ZZZZZのDO処理\n");
        temp_cnt++;
        break;
    case FSM_EXIT:
        printf(">>>>ZZZZZのEXIT処理\n\n");
        break;
    default:
        /* 処理なし */
        break;

    }
}

마지막으로 상기 상태 머신 코드를 제어하는 ​​메인 처리

main.c

/**
* @brief main処理
* @param NONE
* @return  NONE
* @details メインの処理(ステートマシンコントローラー)
*/
void main(void)
{
    ST_FMS_STATE_INFO sts_state;
    unsigned char cnt;

    /* イニシャル処理 */
    sts_state.crreStat = STATE_XXXXX;
    sts_state.newStat = STATE_XXXXX;

    cnt = 13;
    temp_cnt = 0;

    /* 状態遷移処理 */
    while (cnt--)
    {
        printf("%d周期目\n", (13 - cnt));
        /* イベント判定 */
        sts_state.newStat = sts_fms_stateInst[sts_state.crreStat].fmc_Event();

        if (sts_state.crreStat != sts_state.newStat)
        {
            /* exit処理 */
            sts_fms_stateInst[sts_state.crreStat].fmc_state(FSM_EXIT);
            /* entry処理 */
            sts_fms_stateInst[sts_state.newStat].fmc_state(FSM_ENTRY);
        }
        else
        {
            /*do処理 */
            sts_fms_stateInst[sts_state.newStat].fmc_state(FSM_DO);
        }

        sts_state.crreStat = sts_state.newStat;
    }
}


상기 소프트웨어의 실행 결과 ↓



템플릿 코드에서는 종료하는 상태의 Exit 처리와 동일한 주기로 Entry 처리를 실행하도록 하고 있다.
여기에 관해서는 각자의 사정에 맞게 설계하면 좋다고 생각한다.
1周期目
>>XXXXXのDO処理
2周期目
>>>>XXXXXのEXIT処理

YYYYYのENTRY処理
3周期目
>>YYYYYのDO処理
4周期目
>>>>YYYYYのEXIT処理

ZZZZZのENTRY処理
5周期目
>>ZZZZZのDO処理
6周期目
>>>>ZZZZZのEXIT処理

XXXXXのENTRY処理
7周期目
>>XXXXXのDO処理
8周期目
>>>>XXXXXのEXIT処理

YYYYYのENTRY処理
9周期目
>>YYYYYのDO処理
10周期目
>>>>YYYYYのEXIT処理

ZZZZZのENTRY処理
11周期目
>>ZZZZZのDO処理
12周期目
>>>>ZZZZZのEXIT処理

XXXXXのENTRY処理
13周期目
>>XXXXXのDO処理

좋은 웹페이지 즐겨찾기