SM_SIMPLE ステートマシン
SM_SIMPLE
は、サンプルコード中の状態遷移、タイムアウト待ち、送信完了などの処理待ちを行うために用意しています。SM_SIMPLE
の基本的なコードを示します。
##include <SM_SIMPLE>
enum class STATE : uint8_t {
INIT = 0,
SENSOR,
TX,
TX_WAIT_COMP,
GO_SLEEP
};
SM_SIMPLE<STATE> step;
begin() {
...
step.init(); //初期化
}
loop() {
do {
switch(step.state()) {
case STATE::INIT:
...
step.next(STATE::SENSOR);
break;
case STATE::SENSOR:
...
step.next(STATE::TX);
break;
case STATE::TX:
if (/*送信要求成功*/) {
step.set_timeout(100); // タイムアウトの設定
step.clear_flag(); //処理待ち
step.next(STATE::TX_WAIT_COMP);
}
break;
case STATE::TX_WAIT_COMP:
if (step.is_timeout()) the_twelite.reset_system(); // タイムアウト
if (step.is_flag_ready()) sleepNow(); // flagがセットされた
break;
...
}
} while(step.b_more_loop());
}
void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
step.set_flag(ev.bStatus);
}
void sleepNow() {
step.on_sleep(false); // reset state machine.
the_twelite.sleep(10000); // 10sec
}
解説
SM_SIMPLE
を利用するには状態一覧としてのenum class
定義が必要です。上記ではSTATE
として定義しています。このステージをパラメータとしてSM_SIMPLE<STATE> step;
のようにクラスオブエクトを生成します。生成したクラスオブジェクトは.setup()
により初期化しておきます。
enum class STATE : uint8_t {
INIT = 0,
SENSOR,
TX,
TX_WAIT_COMP,
GO_SLEEP
};
SM_SIMPLE<STATE> step;
void setup() {
step.init();
}
SM_SIMPLE
の初期状態は値が0で、上記の例ではSTATE::INIT
が対応します。現在の状態を取得するには.state()
を用、上記例のように_do while_文中の_switch_節の判定式に用います。
loop() {
do {
switch(step.state()) {
case STATE::INIT: // 値0の状態
...
状態の遷移には.next()
を呼び出します。状態が変更された場合、b_more_loop()
がtrue
になり_do while_節のループがもう一度実行されます。例ではSTATE::SENSOR
状態から.next(STATE::TX)
を呼び出すことで、ループがもう一度実行されcase STATE::TX:
節も実行されることになります。状態を変更しない場合は_do while_ループを脱出しloop()
を一旦終了します。次のloop()
の呼び出しまで一旦待ちます。
do {
switch(step.state()) {
...
case STATE::SENSOR:
...
step.next(STATE::TX); // (1)状態遷移
break;
case STATE::TX: // (3) 2回めのループで呼び出される
if (/*送信要求成功*/) {
...
}
} while (b_more_loop()); // (2) ループ継続判定 true
送信完了などの処理待ちをしたい場合は.clear_flag()
を呼び出し、別のコールバック関数などで.set_flag(uint32_t)
により処理完了を知らせます。ここで指定したuint32_t
型のパラメータをは.get_flag_value()
から読み出せます。
またタイムアウトの処理を行いたい場合は.set_timeout(uint32_t)
を呼び出した時刻を記録し、.is_timeout()
によりタイムアウト時間が経過したかを調べることができます。
case STATE::TX:
if (/*送信要求成功*/) {
step.set_timeout(100); // タイムアウトの設定
step.clear_flag(); //処理待ち
step.next(STATE::TX_WAIT_COMP);
}
break;
case STATE::TX_WAIT_COMP:
if (step.is_timeout()) ...; // タイムアウト
if (step.is_flag_ready()) ...; // flagがセットされた
break;
...
// 送信完了イベント
void on_tx_comp(mwx::packet_ev_tx& ev, bool_t &b_handled) {
step.set_flag(ev.bStatus); // flag を設定する
}
スリープからの復帰で再びSM_SIMPLE
を利用することになりますが、スリープ前に必ず.on_sleep(bool)
を呼び出すようにします。パラメータにfalse
を入れると復帰後に0
状態から開始し、true
を入れるとスリープ直前の状態から再開します。
void sleepNow() {
step.on_sleep(false); // reset state machine.
the_twelite.sleep(10000); // 10sec
}
ソースコード
以下にSM_SIMPLE
のソースコードを示します。
// very simple class to control state used in loop().
template <typename STATE>
class SM_SIMPLE {
uint32_t _u32_flag_value; // optional data when flag is set.
uint32_t _ms_start; // system time when start waiting.
uint32_t _ms_timeout; // timeout duration
STATE _step; // current state
STATE _step_prev; // previous state
bool_t _b_flag; // flag control.
public:
// init
void setup() { memset(this, 0, sizeof(SM_SIMPLE)); }
// call befoer sleeping (save state machine status)
void on_sleep(bool b_save_state = false) {
STATE save = _step;
setup();
if(b_save_state) _step = _step_prev = save;
}
// state control
void next(STATE next) { _step = next; } // set next state
STATE state() { return _step; } // state number
bool b_more_loop() { // if state is changed during the loop, set true
if (_step != _step_prev) { _step_prev = _step; return true; }
else return false;
}
// timeout control
void set_timeout(uint32_t timeout) {
_ms_start = millis();
_ms_timeout = timeout;
}
bool is_timeout() { return (millis() - _ms_start) >= _ms_timeout; }
// flag control
void clear_flag() { _b_flag = false; _u32_flag_value = 0; }
void set_flag(uint32_t u32_flag_value = 0) {
_b_flag = true;
_u32_flag_value = u32_flag_value; }
uint32_t get_flag_value() { return _u32_flag_value; }
bool is_flag_ready() { return _b_flag; }
};
- バージョンによって内容が変化する場合があり。
- 本体は MWX ライブラリソースフォルダの
SM_SIMPLE.hpp
に格納されます。