セクションの複数ページをまとめています。 印刷またはPDF形式で保存...

もとのページに戻る

2024-09-06 現在

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に格納されます。