有限状態マシンプログラミングFSI
7143 ワード
改善された状態マシンプログラミングアプリケーション
iTRON系OSを使った組み込みシステムでは、ドライバ以外のほとんどのモジュール、つまりミドルウェアとアプリケーションは、タスク(TASK)として設計されています.iTRON系OSはC言語で実現することが多いので、機能モジュールを状態機で実現することが主な設計方法となっています.対象に向けては、少し厳密な組み込みシステムであれば、すべての可能性をカバーするプログラムを設計する必要があります.プログラムは緊急時に異常を投げてデバッグを待つことはできません.また、ハードウェアや他のアプリケーションモジュールにはしばしば深刻な結合性がありますので、コードの再使用と拡張は、任意のものではありません.もちろん、言葉による実行速度なども考えられます.この場合、C言語は多くの現代語に取って代わって主役になるでしょう.
iTRON系OSのタスク間通信は、一般的に2つの方法でイベント(EVENT)またはメッセージ(MESSAGE)が行われます.イベントの処理はすばやいですが、パラメータは一切付けられず、重ね合わせはできません.メッセージの転送は少し遅いですが、メモリなどに一定数のパラメータを付けることができます.また、同じ複数のメッセージは、メッセージスタックに蓄積されて順次処理されてもよい.もしイメージが比喩されているならば、イベントは一連のビットコードであり、特定の0または1の状態でイベントが発生したかどうかを判断し、タスクは自分の優先レベルで各種のイベントを処理する.メッセージはバッファです.OSはFIFOでメッセージを古いものから新しいものへ順次配信して対応処理します.
ここで強調したいのですが、本稿で議論するポイントは、ステートマシンを通じてメッセージを処理するモデルです.事件の対応については、今後別途検討する可能性があります.
OSが管轄する組み込みシステムのアプリケーションモジュールは、プログラムの角度にはmain関数がないため、電源を切らない限り終了されません.GUIプログラムをやったことがある限り、この点は理解にかたくないです.プログラムが実行された瞬間、OS呼び出しプログラムの初期化部(Initialization)は、一定の時間スライスプログラムの実行部(Execute)毎に、プログラムが閉じられたときに、休止部分(Exit)を呼び出します.組み込みシステムとデスクトップGUIシステムの違いは、システムの電源がロードされ、OSが初期化動作を完了すると、電源管理モジュールが起動しますが、このモジュールはすべてのアプリケーションモジュールの初期化部分を呼び出します.一方、OSや電源管理モジュールは、電源が切れるところを監視すると、すべてのアプリケーションモジュールの休止部分を呼び出します.システムが正常に動作している場合、OSは、各タスクの優先度に応じて、その実行部分を順次呼び出し、それぞれの所属メッセージを配信する.
アプリケーションモジュールは、メッセージを受信したらすぐに処理するわけではない.よく設計されたアプリケーションモジュールはしばしば内部が複数の状態に分かれています.簡単に言えば、睡眠状態、初期状態、アイドル状態、多忙状態、故障状態の4つから5つの状態があります.もちろんです.デザインによって、相応の修正と拡充ができます.アプリケーションモジュール内部は、異なる状態および受信可能なすべてのメッセージに基づいて、状態対応テーブルを作成する.テーブルに適切な関数ポインタを記入して、異なる状態に応答して各種メッセージを処理する.これは組み込みシステムにおいて一般的な状態機械モードです.
状態マシンの実現の簡単な例を挙げます.私たちは仮象の小さいロボットを作ります.このロボットは座ってもいいし、喧嘩もできます.壊れたら自分で修理できます.これらの機能の枠組みをステートマシンのモードで実現するつもりです.
//*ロボットが受け入れられるイベント*/enum{EVENT_POWERON=0、EVENT_uPOWEROFF、EVENT_WALK、EVENT_FIGHT、EVENT_UREST、EVENT_PAIR EVENT_USIZE}
//*ロボットの状態*/enum{STATUS骢SLEEP、STATUS輐INITIAL、STATUSėNORMAL、STATUS FIGHTING、STATU SuBRIOKEN、STATU SyuMAX};ステータスマシン関数ポインタのプロトタイプ*/typedef void(*MATRIXuFP)(const void*)
//*状態機械関数表*/const MATRIX_FP_Robot_Matrix[EVENTT SIZE][STATUUS SIZE]==={/EVENTUPOWERON*/{Robot PowerOn,Robot NoProcess,Robot NoProcess,Robot_NoProcess,Robot NoProcess,Robot NoProcess,RobotttuNoProcess,RobobobottttuNoProcess,RoboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboPowerOff、/*EVENTUWALK*/{Robot廆NoProcess、Robot柚NoProcess、Robot_Walk、Robot_Walk、Robot柚NoProcess}プロプロプログレス、Robot NoProcess、Robot NoProcess、Robot BattleMode、Robot Fight、Robot NoProcess、/*EVENT REST*/Robot NoProcess、RobotuNoProcess、RobotuNoProcess、RobotubotuNoProcess、RobotuNoProcess、RobotubotuNoProcess、RobotubotbotbotubobobotubotbobobobobobobobotNoProcess、RobotNoProcess、Robot、RobotubotMode、RobotNoProcess bot NoProcess,RobotWorlapair,Robot廆NoProcess,Robot彎Repair;;
//*ロボットイベント受付関数*/void Aki_Robot_Execute(void*pMsgData){byEvent=AiuGetRobotEvent(pMsgData);byStuts=AiuGetRobotStStatsts();if(NULL!=suRobotuMatrix[byEvent][byStStatx](Event)(Event))[Event[Event[Event]Event[Event[Event](Event[Event]Event[Ent[Ent]Event[Event]Ent[Event[Event]Ent[Event NoProcess(pMsgData);return;
以上がステートマシンの原形です.ロボットが応答できる各種イベントとロボット内部の各種状態を説明しました.同時にそれらを使って、状態/イベント応答関数ポインタ二次元配列、つまり、現在の状態マシンの行列を編み上げました.最後に外来事象を説明し、内部状態を読み取り、最終的に呼び出しモーメントを決定しました.陣中の具体的などの関数のイベント配布関数ですか?
次に簡単に紹介します.マトリックスに記入されたすべてのメンバー関数はどのような機能を実現しますか?
机能のない空関数*/void RobotmouNoProcess/*電源ローディング、初期化操作*/void RobotmuPower On(void*pMsgData)/*電源オフ、休止操作*/void RobotbobotwowerOff(voddddddddmmboddddddmmmboboboboddddddtttwowodddddddddddmmmmmmmmmmmmmmmmmmmmmmmmbobobobobobobobobobobobobobobobobobobobobobobodon))、/ウォーウォー動作動作動作動作動作動作動作/RoddddddddmmmttleMode(void*pMsgData);/*戦闘動作*/ヴォイロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイド(void*pMsgData)、/*一般モードに切り替えました.
ロボットは起動時と停止時に初期化処理と休止処理を行います.初期化処理が完了したら、デフォルトは一般状態です.通常の状態では走行、休憩、戦闘ダメージの修復ができます.通常の状態で攻撃コマンドを実行すると攻撃状態に切り替わります.攻撃状態では走行と攻撃ができます.休憩コマンドで通常状態に切り替えることができます.ロボットの相手が強すぎて、自分がボロボロにされた時、破損状態に強制的に切り替わります.破損した状態では修復動作しかできません.具体的には言わないです.各関数の実現はどうなるかを想像する興味があれば、きっと面白いと思います.
他にまだ二つの関数があります.
//*メッセージ/イベント変換関数*/int App GetRobotEvent(void*pMsgData)/*内部状態取得関数*int/App GetRobotStatus(void);
メッセージ・イベント変換関数は、外部のメッセージを状態マシン・マトリクスで識別できるイベントコードに変換します.内部状態取得関数は、その名前のように、ステータスマシン・マトリクスの関数ポインタを呼び出すために内部状態に戻るだけ使用します.
一つのステータスマシンシステムの設計過程では、私自身の経験から、以下のいくつかの点を厳守しなければならないと思います.メッセージをイベントに変換するたびに、読んで内部の状態を一度だけ読んでください.そして、どのメンバー関数を呼び出すかを決めます.メンバー関数では、再度状態を読み取り、分枝条件として異なる処理を行うことは絶対にできません.一つの外部モジュールです.このモジュールの公開APIを通じて、このモジュールの内部状態を直接修正してはいけません.状態マシンマトリックスのメンバー関数はむしろ数が多すぎるものと類似していても、統一を求めてはいけません.盲目的にコードを簡略化してはいけません.必要な時には、一つの状態/イベントに応答して、一つの関数よりも内部の無数の不一致判定条件によって異なる処理を行って、維持しやすいです..
状態マシンの内容については多くの前人の研究成果があります.リアルタイムのオペレーティングシステムに組み込まれた環境の中で、些細な経験を得ただけです.
// :http://blog.csdn.net/jiankangshiye/article/details/8725017
// :
// (FSM)
// 2479
#include
// xxxx2479, 4 2479, 0~9 ,
#include
#include
//
typedef void (*(*lock_func)( char ))( char ); // typedef
//typedef void (*lock_func_temp)(char c);
//typedef lock_func_temp (*lock_func)(char c);
lock_func state;
//
// ,
// ,
lock_func init_state(char ch);
lock_func state1(char ch);
lock_func state2(char ch);
lock_func state3(char ch);
lock_func state4(char ch);
//
lock_func init_state(char ch)
{
if ((ch < '0') || (ch > '9'))
{
printf( "invaild number!
" );
return (lock_func)init_state;
}
else
return (lock_func)state1(ch); // ,
}
// 1
lock_func state1(char ch)
{
if (ch == '2')
{
return (lock_func)state2;
} else
{
printf( "error!
" );
return (lock_func)init_state;
}
}
// 2
lock_func state2(char ch)
{
if (ch == '4')
{
return (lock_func)state3;
} else
{
printf( "error!
" );
return (lock_func)init_state;
}
}
// 3
lock_func state3(char ch)
{
if (ch == '7')
{
return (lock_func)state4;
} else
{
printf( "error!
" );
return (lock_func)init_state;
}
}
// 4
lock_func state4(char ch)
{
if (ch == '9')
{
printf("Correct, lock is open!
");
return NULL;
} else
{
printf( "error!
" );
return (lock_func)init_state;
}
}
// NULL
// return NULL; .
//
void lock_handle (void)
{
char ch;
state = (lock_func)init_state;
while (state)
{
ch = getchar();
state = (lock_func)(*state)(ch);
}
}
int main()
{
lock_handle();
system( "pause" );
}
組み込みシステムの状態マシンの設計の心得iTRON系OSを使った組み込みシステムでは、ドライバ以外のほとんどのモジュール、つまりミドルウェアとアプリケーションは、タスク(TASK)として設計されています.iTRON系OSはC言語で実現することが多いので、機能モジュールを状態機で実現することが主な設計方法となっています.対象に向けては、少し厳密な組み込みシステムであれば、すべての可能性をカバーするプログラムを設計する必要があります.プログラムは緊急時に異常を投げてデバッグを待つことはできません.また、ハードウェアや他のアプリケーションモジュールにはしばしば深刻な結合性がありますので、コードの再使用と拡張は、任意のものではありません.もちろん、言葉による実行速度なども考えられます.この場合、C言語は多くの現代語に取って代わって主役になるでしょう.
iTRON系OSのタスク間通信は、一般的に2つの方法でイベント(EVENT)またはメッセージ(MESSAGE)が行われます.イベントの処理はすばやいですが、パラメータは一切付けられず、重ね合わせはできません.メッセージの転送は少し遅いですが、メモリなどに一定数のパラメータを付けることができます.また、同じ複数のメッセージは、メッセージスタックに蓄積されて順次処理されてもよい.もしイメージが比喩されているならば、イベントは一連のビットコードであり、特定の0または1の状態でイベントが発生したかどうかを判断し、タスクは自分の優先レベルで各種のイベントを処理する.メッセージはバッファです.OSはFIFOでメッセージを古いものから新しいものへ順次配信して対応処理します.
ここで強調したいのですが、本稿で議論するポイントは、ステートマシンを通じてメッセージを処理するモデルです.事件の対応については、今後別途検討する可能性があります.
OSが管轄する組み込みシステムのアプリケーションモジュールは、プログラムの角度にはmain関数がないため、電源を切らない限り終了されません.GUIプログラムをやったことがある限り、この点は理解にかたくないです.プログラムが実行された瞬間、OS呼び出しプログラムの初期化部(Initialization)は、一定の時間スライスプログラムの実行部(Execute)毎に、プログラムが閉じられたときに、休止部分(Exit)を呼び出します.組み込みシステムとデスクトップGUIシステムの違いは、システムの電源がロードされ、OSが初期化動作を完了すると、電源管理モジュールが起動しますが、このモジュールはすべてのアプリケーションモジュールの初期化部分を呼び出します.一方、OSや電源管理モジュールは、電源が切れるところを監視すると、すべてのアプリケーションモジュールの休止部分を呼び出します.システムが正常に動作している場合、OSは、各タスクの優先度に応じて、その実行部分を順次呼び出し、それぞれの所属メッセージを配信する.
アプリケーションモジュールは、メッセージを受信したらすぐに処理するわけではない.よく設計されたアプリケーションモジュールはしばしば内部が複数の状態に分かれています.簡単に言えば、睡眠状態、初期状態、アイドル状態、多忙状態、故障状態の4つから5つの状態があります.もちろんです.デザインによって、相応の修正と拡充ができます.アプリケーションモジュール内部は、異なる状態および受信可能なすべてのメッセージに基づいて、状態対応テーブルを作成する.テーブルに適切な関数ポインタを記入して、異なる状態に応答して各種メッセージを処理する.これは組み込みシステムにおいて一般的な状態機械モードです.
状態マシンの実現の簡単な例を挙げます.私たちは仮象の小さいロボットを作ります.このロボットは座ってもいいし、喧嘩もできます.壊れたら自分で修理できます.これらの機能の枠組みをステートマシンのモードで実現するつもりです.
//*ロボットが受け入れられるイベント*/enum{EVENT_POWERON=0、EVENT_uPOWEROFF、EVENT_WALK、EVENT_FIGHT、EVENT_UREST、EVENT_PAIR EVENT_USIZE}
//*ロボットの状態*/enum{STATUS骢SLEEP、STATUS輐INITIAL、STATUSėNORMAL、STATUS FIGHTING、STATU SuBRIOKEN、STATU SyuMAX};ステータスマシン関数ポインタのプロトタイプ*/typedef void(*MATRIXuFP)(const void*)
//*状態機械関数表*/const MATRIX_FP_Robot_Matrix[EVENTT SIZE][STATUUS SIZE]==={/EVENTUPOWERON*/{Robot PowerOn,Robot NoProcess,Robot NoProcess,Robot_NoProcess,Robot NoProcess,Robot NoProcess,RobotttuNoProcess,RobobobottttuNoProcess,RoboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboboPowerOff、/*EVENTUWALK*/{Robot廆NoProcess、Robot柚NoProcess、Robot_Walk、Robot_Walk、Robot柚NoProcess}プロプロプログレス、Robot NoProcess、Robot NoProcess、Robot BattleMode、Robot Fight、Robot NoProcess、/*EVENT REST*/Robot NoProcess、RobotuNoProcess、RobotuNoProcess、RobotubotuNoProcess、RobotuNoProcess、RobotubotuNoProcess、RobotubotbotbotubobobotubotbobobobobobobobotNoProcess、RobotNoProcess、Robot、RobotubotMode、RobotNoProcess bot NoProcess,RobotWorlapair,Robot廆NoProcess,Robot彎Repair;;
//*ロボットイベント受付関数*/void Aki_Robot_Execute(void*pMsgData){byEvent=AiuGetRobotEvent(pMsgData);byStuts=AiuGetRobotStStatsts();if(NULL!=suRobotuMatrix[byEvent][byStStatx](Event)(Event))[Event[Event[Event]Event[Event[Event](Event[Event]Event[Ent[Ent]Event[Event]Ent[Event[Event]Ent[Event NoProcess(pMsgData);return;
以上がステートマシンの原形です.ロボットが応答できる各種イベントとロボット内部の各種状態を説明しました.同時にそれらを使って、状態/イベント応答関数ポインタ二次元配列、つまり、現在の状態マシンの行列を編み上げました.最後に外来事象を説明し、内部状態を読み取り、最終的に呼び出しモーメントを決定しました.陣中の具体的などの関数のイベント配布関数ですか?
次に簡単に紹介します.マトリックスに記入されたすべてのメンバー関数はどのような機能を実現しますか?
机能のない空関数*/void RobotmouNoProcess/*電源ローディング、初期化操作*/void RobotmuPower On(void*pMsgData)/*電源オフ、休止操作*/void RobotbobotwowerOff(voddddddddmmboddddddmmmboboboboddddddtttwowodddddddddddmmmmmmmmmmmmmmmmmmmmmmmmbobobobobobobobobobobobobobobobobobobobobobobodon))、/ウォーウォー動作動作動作動作動作動作動作/RoddddddddmmmttleMode(void*pMsgData);/*戦闘動作*/ヴォイロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイドロイド(void*pMsgData)、/*一般モードに切り替えました.
ロボットは起動時と停止時に初期化処理と休止処理を行います.初期化処理が完了したら、デフォルトは一般状態です.通常の状態では走行、休憩、戦闘ダメージの修復ができます.通常の状態で攻撃コマンドを実行すると攻撃状態に切り替わります.攻撃状態では走行と攻撃ができます.休憩コマンドで通常状態に切り替えることができます.ロボットの相手が強すぎて、自分がボロボロにされた時、破損状態に強制的に切り替わります.破損した状態では修復動作しかできません.具体的には言わないです.各関数の実現はどうなるかを想像する興味があれば、きっと面白いと思います.
他にまだ二つの関数があります.
//*メッセージ/イベント変換関数*/int App GetRobotEvent(void*pMsgData)/*内部状態取得関数*int/App GetRobotStatus(void);
メッセージ・イベント変換関数は、外部のメッセージを状態マシン・マトリクスで識別できるイベントコードに変換します.内部状態取得関数は、その名前のように、ステータスマシン・マトリクスの関数ポインタを呼び出すために内部状態に戻るだけ使用します.
一つのステータスマシンシステムの設計過程では、私自身の経験から、以下のいくつかの点を厳守しなければならないと思います.メッセージをイベントに変換するたびに、読んで内部の状態を一度だけ読んでください.そして、どのメンバー関数を呼び出すかを決めます.メンバー関数では、再度状態を読み取り、分枝条件として異なる処理を行うことは絶対にできません.一つの外部モジュールです.このモジュールの公開APIを通じて、このモジュールの内部状態を直接修正してはいけません.状態マシンマトリックスのメンバー関数はむしろ数が多すぎるものと類似していても、統一を求めてはいけません.盲目的にコードを簡略化してはいけません.必要な時には、一つの状態/イベントに応答して、一つの関数よりも内部の無数の不一致判定条件によって異なる処理を行って、維持しやすいです..
状態マシンの内容については多くの前人の研究成果があります.リアルタイムのオペレーティングシステムに組み込まれた環境の中で、些細な経験を得ただけです.