athrillでTECSを動かす

#1. ETロボコンシミュレーションの環境構築
まず、ETロボコンシミュレーションの環境の構築を行います。
[こちら](https://toppers.github.io/hakoniwa/prototypes/single-robot/)を参考にしてください。

#2. mryby-on-ev3rt+tecsプラットフォーム
以下のリンクからダウンロードできます。
[mruby-on-ev3rt+tecs](https://www.toppers.jp/tecs.html#mruby_ev3rt)
#3 TECS CDL記述
次はathrill側での操作の説明です。
まず新しくsdk2というディレクトリを作成し、sdkの中身をコピーしておきます。
###3.1 tMotor.cdl,tColorSensor.cdlのコピー
ロボットを動かすために用いるcdlファイルです。
これらを**mryby-on-ev3rt+tecs**から**athrill**側に持ってきます。
詳しく説明すると
`mruby-on-ev3rt+tecs2.2.0\hr-tecs\tecs_lib`
にあるtMotor.cdlおよびtColorSensor.cdlを
`ev3rt-athrill-v850e2m/sdk2/common/ev3api/src`
にコピーします。


###3.2 tMotor.cdl
コピーしてきたtMotor.cdlを書き加えます。

```TECSCDL:tMotor.cdl
import_C("ev3api_motor.h");
signature sMotor{
	ER setPower([in]int power);
	void initializePort([in]int32_t type);
};
signature sControlMotors
{
	ER steer([in]int power, [in]int turn_ratio);
};
celltype tMotor
{
	entry sMotor eMotor;
	attr{
		int32_t port;
	};
};
[singleton]
celltype tControlMotors
{
	call sMotor cLeftMotor;
	call sMotor cRightMotor;
	entry sControlMotors eControlMotors;
};
```

まず、Left・RightMotorそれぞれのパワーを決める関数setPowerとポートのタイプを決める関数initializePortのみ有効にします。
####ControlMotorsのシグニチャ記述

```
signature sControlMotors{
	ER steer([in]int power, [in]int turn_ratio);
　　void initializePort([in]int32_t type);
};
```

ロボットのステアリングを制御する関数steerを定義し、引数をpowerとturn_ratioの2つだけにします。
####セルタイプtControlMotorsのセルタイプ記述

```
[singleton]
celltype tControlMotors
{
	call sMotor cLeftMotor;
	call sMotor cRightMotor;
	entry sControlMotors eControlMotors;
};
```

今までと同様に受け口と呼び口を定義します。
[singleton]でなくても構いません。
###3.3 athrill_common.cdl
TECS CDL記述をsdk2/common/athrill_common.cdl
に追加します。
今回は以下のコンポーネント図でロボットを動かしていきます。


```TECSCDL:athrill_common.cdl

                                                          +--------------+
                                                          | tColorSensor |
                                 +----------------------->|  ColorSensor |
                                 |                        +--------------+
                                 |
                                 |                        +-----------+
+----------+    +--------------+ |  +----------------+    |  tMotor   |
|  tTask   |    | tControlMain |-   | tControlMotors |--->| LeftMoter |
|          |--->|              |--->|                |    +-----------+
|   Task   |    |  ControlMain |    |  ControlMotors |--->+-----------+
+----------+    +--------------+    +----------------+    |   tMotor  |
                                                          | RightMotor|
                                                          +-----------+ 

```

追加するコードがこちらです。

```TECSCDL:athrill_common.cdl                                                         
cell tTask Task
{
	cTaskBody = ControlMain.eBody;
	stackSize = 81920;
	priority  = 10;
    attribute =  C_EXP("TA_ACT");
};
celltype tControlMain
{
	entry sTaskBody eBody;
	call sControlMotors cControlMotors;
	call sColorSensor cColorSensor;
};

cell tControlMain ControlMain
{
	cControlMotors = ControlMotors.eControlMotors;
	cColorSensor = ColorSensor.eColorSensor;
};
cell tControlMotors ControlMotors
{
    cLeftMotor  = LeftMotor.eMotor;
    cRightMotor = RightMotor.eMotor;
};
cell tMotor LeftMotor
{
    port = C_EXP("EV3_PORT_A");
};
cell tMotor RightMotor
{	
    port = C_EXP("EV3_PORT_B");
};
cell tColorSensor ColorSensor
{
    port = C_EXP("EV3_PORT_1");
};

```
この時、tMotor.cdl, tColorSensor.cdlへのパスも加えます。

```
import("sdk2/common/ev3api/src/tMotor.cdl");
import("sdk2/common/ev3api/src/tColorSensor.cdl");
```

####セルTaskの組み上げ記述
```
cell tTask Task
{
	cTaskBody = ControlMain.eBody;
	stackSize = 81920;
	priority = 10;
    attribute = C_EXP("TA_ACT");
};
```
組み上げ記述は**セル同士の結合関係を定義**し、**アプリケーションを構築**するために用います。
今回だと、セルTaskの呼び口cTaskBodyがセルControlMainの受け口Bodyと結合しているという意味です。
**attribute = C_EXP("TA_ACT")**はOS起動時にタスクを起動状態にしています。
セルTaskは以下にあります。

```
import(<kernel.cdl>)
```

###セルタイプtControlMainのセルタイプ記述
```

celltype tControlMain
{
	entry sTaskBody eBody;
	call sControlMotors cControlMotors;
	call sColorSensor cColorSensor;
};
```

セルタイプ記述は呼び口、受け口、属性、変数を用いてセルタイプを定義します。
今回は、シグニチャ**sTaskBody**の受け口を**eBody**、
シグニチャ**sControlMotors**, **sColorSensor**
の呼び口をそれぞれ**cControlMotors**、**cColorSensor**と定義している。
####セルControlMainの組み上げ記述

```
cell tControlMain ControlMain
{
	cControlMotors = ControlMotors.eControlMotors;
	cColorSensor = ColorSensor.eColorSensor;
};
```
セル**ControlMain**の呼び口**cControlMotors**がセル**ControlMotors**の受け口**eControlMotors**と結合している。呼び口cColorSensorも同様です。
####セルControlMotorsの組み上げ記述####

```
cell tControlMotors ControlMotors
{
    cLeftMotor  = LeftMotor.eMotor;
    cRightMotor = RightMotor.eMotor;
};
```

上記と同様に呼び口と受け口の結合を表しています。
####セルLeftMotor,RightMotor,ColorSensorの組み上げ記述
```
cell tMotor LeftMotor
{
	port = C_EXP("EV3_PORT_A");
};
cell tMotor RightMotor
{
	port = C_EXP("EV3_PORT_B");
};
cell tColorSensor ColorSensor
{
    port = C_EXP("EV3_PORT_1");
}

```
ここで、**C_EXP("~")**はTECSジェネレータ上では~がただの文字列として扱われるように命令している。
今回の場合では、athrill側のCファイルでEV3_PORT_A,B,1を定義する記述に出力させ、ジェネレータでは処理せずC言語のコンパイラに任せている。(コンパイルの際にはしっかり有効になる)
また、C_EXPを用いる理由としては、コードの見栄えや定義を再度理解させるのが面倒だからである。
C_EXPRESSIONの略です。

#4 Makefile.app
common/Makefile.appの34, 42, 56, 61行目のappをtControlMainに書き換えます。
#5 app.cfg
workspace/single-robot/line_trace/app.cfgの

```
INCLUDE("app_common.cfg");
INCLUDE("tecsgen.cfg");
```
この2つ以外のコードを削除します。(削除したコードはC++用なので)
削除後、ファイル名をtControlMain,cfgとしてathrillsampleにコピー。
#6 Makefile.img
common/Makefile.imgの236~241行目を#で無効にする。

```makefile:Makefile.img
APPL_ASMOBJS :=
ifdef USE_CXX
　　APPL_CXXOBJS := @(APPLOBJS) 
　　APPL_COBJS +=
else
　　APPL_COBJS += @(APPLOBJS) 
endif
```
この操作はControMain.cの2重定義を解消しています。
#7 Makefile
workspace/Makefileの12行目に

```
-include ../OBJ/gen/Makefile.tecsgen
```
を加えます。
#8 TECSジェネレータ
TECSジェネレータを実行してテンプレートコードを生成します。
[TECSジェネレータ](https://dev.toppers.jp/trac_user/tecs/wiki/tecsgen)の詳しい説明はこちらから。
一旦、[こちら](https://toppers.github.io/hakoniwa/single-robot-usage/01_usage_v850/)を参考にETロボコン制御プログラムをビルドしてください。
このビルド過程の中でTECSジェネレータが実行されるので、
`aev3rt-athrill-v850e2m/sdk2/common/OBJ/gen`
にテンプレートコードが生成されます。
![tecsgenの印.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638172/28b33da1-23a7-5e4e-7017-927a5cec1118.png)
また、今回のビルドでは以下のエラーが出てしまいますが、この後に説明するTECSマージによって解消できます。

![本成功エラー.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/638172/cf67b445-5ceb-7fce-350b-baa4f541c895.png)

#9 TECSマージ
TECSジェネレータの生成したテンプレートコードからセルタイプコードを生成します。
[TECSマージ](https://dev.toppers.jp/trac_user/tecs/wiki/tecsmerge)の詳しい説明はこちらから。
テンプレートコードの**tControlMain_templ.c**を
`ev3rt-athrill-v850e2m/sdk2/workspace/アプリケーションフォルダ名`に
**tControlMotors_templ.c**,**tMotor_templ.c**および**tColorSensor_templ.c**を
`ev3rt-athrill-v850e2m/sdk2/common/ev3api/src`
にセルタイプコードとして生成させます。

```
$ruby tecsmerge.rb 対象のテンプレートファイル　セルタイプコード生成場所
```

#10 テンプレートコード

生成されたセルタイプコードにコンポーネントの振る舞いを記述します。
###tMotor.c
`src/ev3api_motor.c`から**ev3rt_motor_set_power**および**ev3_motor_config**をコピー。
戻り値として追加し、引数を属性に合わせ変更します。

```TECSCDL:tMotor.c
ER
eMotor_setPower(CELLIDX idx, int power)
{
	ER		ercd = E_OK;
	CELLCB	*p_cellcb;
	if (VALID_IDX(idx)) {
		p_cellcb = GET_CELLCB(idx);
	}
	return ev3_motor_set_power(ATTR_port, power);
}
void
eMotor_initializePort(CELLIDX idx, int32_t type)
{
	CELLCB	*p_cellcb;
	if (VALID_IDX(idx)) {
		p_cellcb = GET_CELLCB(idx);
	}
	else {
		
	} 
	return ev3_motor_config(ATTR_port,type);
}
```

###tControlMotors.c
`src/ev3api_motor.c`から関数ev3_motor_steerの中身をコピー。

```
ER
eControlMotors_steer(int power, int turn_ratio)
{
	cLeftMotor_initializePort( LARGE_MOTOR );
	cRightMotor_initializePort( LARGE_MOTOR );

	int left_power;
	int right_power;
	int abs_turn_ratio = (turn_ratio < 0) ? -turn_ratio : turn_ratio;
	int abs_power = (power < 0) ? -power : power;

	if (abs_turn_ratio > 100) {
		abs_turn_ratio = 100;
	}
	if (abs_power > 100) {
		abs_power = 100;
	}
	left_power = abs_power;
	right_power = abs_power;

	if (turn_ratio > 0) {
		right_power = (abs_power * (100 - abs_turn_ratio))  / 100 ;
	}
	else {
		left_power = (abs_power * (100 - abs_turn_ratio))  / 100 ;
	}
	if (power < 0) {
		left_power = left_power * (-1);
		right_power = right_power * (-1);
	}

	cLeftMotor_setPower(left_power);
	cRightMotor_setPower(right_power);
	return E_OK;
}

```

`cLeftMotor_initializePort( LARGE_MOTOR );
 cRightMotor_initializePort( LARGE_MOTOR );`
を追加し、モータのタイプをLARGE_MOTORに。
引数がpowerおよびturn_ratioのみになったので、`CHECK_`から始まる関数は削除。
`ev3_Motor_set_power`名をTECS CDL記述に合わせ、引数をpowerだけに変更。
関数の名前ですが、**呼び口cLeftMotorがセルLeftMotorのsetPowerを利用するようなイメージ。**
###tColorSensor.c

```
colorid_t
eColorSensor_getColor(CELLIDX idx)
{
	CELLCB	*p_cellcb;
	if (VALID_IDX(idx)) {
		p_cellcb = GET_CELLCB(idx);
	}
	return ev3_color_sensor_get_color(ATTR_port);

}
uint8_t
eColorSensor_getReflect(CELLIDX idx)
{
	CELLCB	*p_cellcb;
	if (VALID_IDX(idx)) {
		p_cellcb = GET_CELLCB(idx);
	}
	return ev3_color_sensor_get_reflect(ATTR_port);

}
uint8_t
eColorSensor_getAmbient(CELLIDX idx)
{
	CELLCB	*p_cellcb;
	if (VALID_IDX(idx)) {
		p_cellcb = GET_CELLCB(idx);
	}
	return ev3_color_sensor_get_ambient(ATTR_port);

}
void
eColorSensor_initializePort(CELLIDX idx)
{
	CELLCB	*p_cellcb;
	if (VALID_IDX(idx)) {
		p_cellcb = GET_CELLCB(idx);
	}
	ev3_sensor_config(ATTR_port, COLOR_SENSOR);

}
void
eColorSensor_getRGBRaw(CELLIDX idx, uint16_t* r, uint16_t* g, uint16_t* b)
{
	CELLCB	*p_cellcb;
	if (VALID_IDX(idx)) {
		p_cellcb = GET_CELLCB(idx);
	}
	rgb_raw_t val;
	ev3_color_sensor_get_rgb_raw(ATTR_port, &val);

	(*r) = val.r; (*g) = val.g; (*b) = val.b;
}
```

`src/ev3api_sensor.c`からtColorSensor.cにある関数に対応する関数をコピーし、引数を属性に合わせて変更。
###tControlMain.c

```
void
eBody_main(CELLIDX idx)
{
	CELLCB	*p_cellcb;
	if (VALID_IDX(idx)) {
		p_cellcb = GET_CELLCB(idx);
	}
	else {
		
	} 
cColorSensor_initializePort( );
while(1) {

#define LIGHT_BRIGHT
#ifdef LIGHT_DARK

#define white 78
#define black 20
#else

#define white 100
#define black 50
#endif
        static float lasterror = 0, integral = 0;
        static float midpoint = (white - black) / 2 + black;
        {
            float error = midpoint - cColorSensor_getReflect();
#ifdef LIGHT_BRIGHT
            integral = error + integral * 0.01;
            float steer = 0.9 * error + 0.1 * integral + 1 * (error - lasterror);
#else
            integral = error + integral * 0.05;
            float steer = 0.7 * error + 0.1 * integral + 1 * (error - lasterror);
#endif
            cControlMotors_steer(10, steer);
            lasterror = error;
        }
        tslp_tsk(100000); /* 100msec */
}
```

`workspace/athrillsample/app.c`から
main_taskの中身をコピーして、main_task内の1行~syslogは必要ないので削除します。
`cColorSensor_initializePort( )`を代わりに置きます。
次に、`ev3_color_sensor_get_reflect`をTECS CDL記述に合わせて書き直し、引数を無くします。
さらに、`ev3_motor_steer`もTECS CDL記述に合わせて書き直し、引数をpowerとturn_ratioのみにします。



#11 Unityのシミュレーションの開始
**10**まで終わればもう一度`$make img=<アプリケーションフォルダ名>`して終了です。
シミュレーションは[こちら](https://qiita.com/kanetugu2018/items/0e521f4779cd680dab18#unity%E3%81%AE%E3%82%B7%E3%83%9F%E3%83%A5%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E9%96%8B%E5%A7%8B%E3%81%99%E3%82%8B)を参考にしてください。
参考にしているWebサイトのUnityパッケージではなく[こちらのバージョン](https://github.com/toppers/hakoniwa-Unity-HackEV/releases/tag/v1.0)を使用してください。(参考サイトで説明されているScriptが見つからないため)

<blockquote class="twitter-tweet"><p lang="und" dir="ltr"><a href="https://t.co/Q7J5bQmi73">pic.twitter.com/Q7J5bQmi73</a></p>&mdash; 下村遼太 (@mlBIlXK8fUiDlGv) <a href="https://twitter.com/mlBIlXK8fUiDlGv/status/1287385742046408710?ref_src=twsrc%5Etfw">July 26, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>