[ETロボコン] 次のコンテストに向けて技術的備忘録その１(準備~μITRON編)

# はじめに
ETロボコン2020年に恵庭戦隊トシキングで参加してた人です．
技術的に振り返りたいと思ったので記事を書いてます．また，来年度にむけて設計意図を忘れないよう備忘録としても活用します．
間違っている部分も多いと思いますが，見る方は暖かい目で見ていただけると幸いです．
ここでは，準備からμITRONの使い方を自分なりに説明しています．他はでき次第随時上げていきます・・・
[アーキテクチャ編](https://qiita.com/Haaamaaaaa/items/99eccd83f3c4b1acdd49)
コード全体は[こちら](https://github.com/EniwaSentaiToshiking/2020-base)

## 開発環境
- WSL1 (Windows 10)
- C/C++ (std=c++03)

## 開発準備の攻略
今年は[公式](https://www.etrobo.jp/)(1)から[シミュレータ](https://github.com/ETrobocon/etrobo/wiki)(2)が配布されていたので，手順通りに構築してます．
去年と違ってほぼ自動だったのでとても楽ですね．

**参考文献**
(1) [ETロボコン2020](https://www.etrobo.jp/)
(2) [etrobo all-in-one installer/builder/launcher environment](https://github.com/ETrobocon/etrobo)

## μITRON4.0の攻略
[μITRON4.0の仕様書](https://www.tron.org/ja/specifications#d)(1)と[講演会資料](https://www.tron.org/ja/seminar/workshops-text-cc-by-sa/)(2)を参考にしながら攻略していきます．[^ITRONnamed]

[^ITRONnamed]: ちなみにμITRONはMicro Industrial TRONです．

リアルタイムオペレーティングシステム(リアルタイムOS)と聞きなれない用語が出てきますが，肝心な部分は```タスク・優先度・周期ハンドラ```あたりを理解できれば使えるかと思います．
次に，この3つを軽く説明していきます．
### μITRON4.0におけるタスク・優先度・周期ハンドラ
タスクの状態は[(1)](https://www.tron.org/ja/specifications#d)の3章2節1項内に記載されている図を抜粋します．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/182036/9f852edc-5ce4-75d1-1dda-c3393ff564dd.jpeg" width=50%>
まず，重要なのはタスクを生成（登録）して休止状態にすることです．基本的にETロボコンでは，起動時にすべてのタスクを休止状態にしておくと生成・削除でリソースを気にする必要はなくなります．どうしてもリソース(主にメモリ)が足りないときは未登録状態を活用すればよいかと．
個人的に理想なタスクの状態遷移は```休止->実行可能->実行->休止```のサイクルです．複数タスクを定義するとどうしてもこの理想なサイクルにはならなくなりますが，この問題をタスクの優先度を使って解決していきます．

優先度は，実行可能状態から実行状態に移すとき，タスクに持たせることができる変数です．この変数があると，優先度の一番高いタスクは```休止->実行可能->実行->休止```のサイクルを保ちやすくなります．[^Priority]

[^Priority]: 実際にはタスクの処理時間なども関係してくるので，絶対保てるとは言えません．特に周期ハンドラが絡んだ実行となるとなおわかりずらいです．

周期ハンドラでは，各タスクを一定周期で起動しています[^HandlerCycle]．この機能により，タスク内では，いわゆるループ文(for, while)を書くことが少なくなります．
なので，不用意に時間のかかるループ文を書いてしまったり，FILEのI/Oを周期内に書いてしまうと予期せぬエラーや動作が不安定に・・・
周期ハンドラは慣れないとどこがループしているのか気づきずらくなるので[公式のサンプル](https://github.com/ETrobocon/etrobo/tree/master/dist/sample_c4)(3)を改造してみるのがいいです．
例示で[恵庭戦隊トシキングのコード](https://github.com/EniwaSentaiToshiking/2020-base)(4)を載せておくと．

*app.cfg*

```c++
INCLUDE("app_common.cfg");

#include "app.h"

// 休止状態のタスクを定義
DOMAIN(TDOM_APP) {
CRE_TSK( SETUP_GAME_TASK,
  { TA_NULL,  0, setup_game_task,   SETUP_GAME_PRIORITY,   STACK_SIZE, NULL });
CRE_TSK( GAME_STATE_MANAGER_TASK,
  { TA_NULL,  0, game_state_manager_task, GAME_STATE_MANAGER_PRIORITY, STACK_SIZE*8, NULL });

// 周期ハンドラを定義
CRE_CYC( SETUP_GAME_CYC,
  { TA_STA, { TNFY_ACTTSK, SETUP_GAME_TASK}, 100*1000, 1*1000});
CRE_CYC( GAME_STATE_MANAGER_CYC, 
  { TA_NULL, { TNFY_ACTTSK, GAME_STATE_MANAGER_TASK}, 20*1000, 1*1000});

ATT_MOD("app.o");

```

*app.h*

```c++
#ifdef __cplusplus
extern "C" {
#endif

#include "ev3api.h"

#define SETUP_GAME_PRIORITY (TMIN_APP_TPRI + 1)
#define GAME_STATE_MANAGER_PRIORITY (TMIN_APP_TPRI + 2)

#ifndef STACK_SIZE
#define STACK_SIZE      (512)
#endif /* STACK_SIZE */

#ifndef TOPPERS_MACRO_ONLY

    extern void setup_game_task(intptr_t exinf);
    extern void setup_game_cyc(intptr_t exinf);
    extern void game_state_manager_task(intptr_t exinf);
    extern void game_state_manager_cyc(intptr_t exinf);

#endif /* TOPPERS_MACRO_ONLY */

#ifdef __cplusplus
}
#endif
```

*app.cpp*

```c++
// 200 msec loop
void setup_game_task(intptr_t exinf)
{
  d.init("Ready");
  if (setupGame.isStarted())
  {
    gameStateManager.init();
    sta_cyc(GAME_STATE_MANAGER_CYC);
  }

  if (ev3_button_is_pressed(LEFT_BUTTON))
  {
    snprintf(syslogBuf, sizeof(syslogBuf), "push left button:stop ev3");
    syslog(LOG_NOTICE, syslogBuf);
    stp_cyc(SETUP_GAME_CYC);
    stp_cyc(GAME_STATE_MANAGER_CYC);
    gameStateManager.terminate();
  }
  ext_tsk();
}

// 20 msec loop
void game_state_manager_task(intptr_t exinf)
{
  gameStateManager.manageGameState();
  ext_tsk();
}
```

[^HandlerCycle]: この起動するとは、μITRONでは休止状態から実行可能状態に移行することです．

まとめると

- リソースに余裕があるなら，最初にタスクは定義して休止状態にしてあげる
- 理想は```休止->実行可能->実行->休止```のサイクル
- 実行可能から実行の間には優先度でタスクの順位付けをしてあげる[^DispatchPreemption]
- 周期ハンドラは実質ループ(for, while)文とみなす
- タスク内の処理は(for, while)文を少なくして計算量を多くしない

[^DispatchPreemption]: ディスパッチ(dispatch )：タスクを処理の実行，プリエンプト(preemption)：実行状態を一旦止める


その他タスクの定義方法や使い方についてはTOPPERSの[FAQ](https://dev.toppers.jp/trac_user/ev3pf/wiki/FAQ#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E9%96%8B%E7%99%BA%E6%96%B9%E6%B3%95%E3%81%AB%E9%96%A2%E3%81%99%E3%82%8B%E8%B3%AA%E5%95%8F)や[(1)](https://www.tron.org/ja/specifications#d)を見てみると理解が進むかもしれません．
**参考文献**
(1) [ITRON仕様書　ホワイトペーパー](https://www.tron.org/ja/specifications#d)
(2) [講演会テキスト](https://www.tron.org/ja/seminar/workshops-text-cc-by-sa/)
(3) [sample_c4](https://github.com/ETrobocon/etrobo/tree/master/dist/sample_c4)
(4) [恵庭戦隊トシキングのコード](https://github.com/EniwaSentaiToshiking/2020-base)

## さいごに
次は，アーキテクチャの設計意図についてまとめる予定です．
app.cfgの引数についてより知りたい方が多ければスピンオフで作るかもしれないです．