初めてのSEATML
eSEATでは、SEATMLファイルで様々な機能を実現することができます。このページでは、SEATMLの基本的な部分について説明していきます。
何もしないSEATML
もっとも簡単なSEATMLファイルは、<general>タグと1つの<state>タグで構成された下のようなファイルです。このSEATMLファイルは、文法的には正しいのですが、何の機能もありません。eSEATコマンドで実行させると空のRTCとして動作します。
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="sample">
</general>
<state name="main">
</state>
</seatml>
この例では、<general>タグにname属性が設定されていますが、このname属性の値が、RTコンポーネント名またはROSノード名になります。
このファイルをsample.seatmlというファイル名で保存し、コマンドラインで
# eSEAT sample.seatml
として実行します。すると何もしないRTCが実行されますので、RTSystemEditor等で確認しください。
sample.seatmlのファイルの作成には、/usr/local/eSEAT/bin にあるgen_seatmlコマンドを実行すると便利です。
# source /usr/local/eSEAT/setup.bash
# gen_seatml sample
で上にあるsample.seatmlがカレントディレクトリに生成されます。
起動時にPythonスクリプトを実行
次に、起動時にPythonスクリプトを実行するSEATMLにします。SEATMLでは、<script>タグでPythonスクリプトを挿入することができます。ここではサンプルとして、eSEATの起動時に "Hello"という文字をコンソールに出力します。
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="sample">
<script>
print("Hello")
</script>
</general>
<state name="main">
</state>
</seatml>
このファイルを先ほどと同じsample.seatmlというファイル名で保存し、
# eSEAT sample.seatml
と実行すると、起動時にコンソールに "Hello"と表示されると思います。デフォルトでは、RTCのメッセージも表示されますので
# eSEAT_Node sample.seatml
とすれば、余計な文字列は表示されませんので確認しやすいと思います。
<script>タグの中にPythonスクリプトを書く代わりに、Pythonの外部ファイルを用意する方法もあります。例えば、下記の内容のhello.pyというファイルを作成します。
print("Hello world in hello.py")
このファイルを<script>タグの内容として実行させる場合には、
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="sample">
<script execfile="hello.py"></script>
</general>
<state name="main">
</state>
</seatml>
と記述します。execfile属性で指定されたファイルは、eSEATの内部では、execfile関数を使って実行されています。
周期実行タスクを定義する
次に、周期実行タスクを定義します。SEATMLでは、周期実行タスクは<onexec>タグで行います。<onexec>タグは、<general>タグと<state>タグ内に記述することができますが、<onexec>タグを記述する場所によってeSEATの振る舞いが若干異なります。
<general>タグ内で定義した場合には、eSEATの内部状態によらず必ず実行されます。<state>タグ内で定義した場合には、その状態にあるときに限って実行されます。
<general>タグと<state>タグの両方に記載した場合には、<state>タグで定義したタスクの後に<general>タグで定義したタスクが実行されます。(現在は、この順番は変更できません)
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="sample">
<script>
print("Hello sample")
</script>
<onexec>
<script>
print("==> onexec in general")
</script>
</onexec>
</general>
<state name="main">
<onexec>
<script>
print("==> onexec in state:main")
</script>
</onexec>
</state>
</seatml>
この例では、<onexec>の中には<script>タグのタスクしか記載していませんが、<shell>, <message>, <log>, <statetransition>タグを使った処理(Task)を記載することができます。
周期タスクの実行周期を指定する
eSEATでは、周期タスクの実行周期を指定することができます。通常、eSEATは、RTコンポーネントとして動作しますので、rtc.confの中の実行周期で動作します。これは、他のRTCとの互換性を保つための仕様です。
しかし、OpenRTM-aistがインストールされていない場合には、このままでは少し都合が悪いので周期タスクを設定する属性を<general>タグに持たせています。 rateという属性を設定してください。単位はHzになっていますのでその点だけ注意が必要です。
上の例で、周期タスクを0.2Hz(5秒に1回)とするには、
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="sample" rate="0.2">
<script>
print("Hello sample")
</script>
<onexec>
<script>
print("==> onexec in general")
</script>
</onexec>
</general>
<state name="main">
<onexec>
<script>
print("==> onexec in state:main")
</script>
</onexec>
</state>
</seatml>
となります。RTコンポーネントとして動作させない場合には、eSEAT_Nodeコマンドを用いて
# eSEAT_Node sample.seatml
と実行してみてください。メッセージが5秒に1回の周期で表示されると思います。
タイムアウトを設定する
eSEATでは、''一定時間adaptorからの入力がない''か''GUIによるイベント呼出しがない''か''状態遷移が起きない''場合に実行する処理(Task)を定義することができます。
このようなタスクの設定には、<ontimeout>タグを使います。<ontimeout>には、timeout属性で時間(秒)を指定して使います。
<ontimeout>タグは、<general>タグと<state>タグの中に1つのみ記載することができます。<general>と<state>の両方に設定した場合には、<state>タグで設定したものが利用されますので注意してください。
また、タイムアウトの処理は、周期実行タスク内で処理されますので、上記の周期実行の周期より短い時間でのタイムアウト設定はできませんのでご注意ください。
例えば、上のSEATMLにある "Hello sample" を2秒間のタイムアウトで出力するようにすると下のようになります。
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="sample" rate="1">
<ontimeout timeout="2">
<script>
print("Hello sample")
</script>
</ontimeout>
<onexec>
<script>
print("==> onexec in general")
</script>
</onexec>
</general>
<state name="main">
<onexec>
<script>
print("==> onexec in state:main")
</script>
</onexec>
</state>
</seatml>
このSEATMLを実行すると
==>onexec in state:main
==>onexec in general
==>onexec in state:main
==>onexec in general
Hello sample
==>onexec in state:main
==>onexec in general
==>onexec in state:main
==>onexec in general
Hello sample
...
...
という感じで、周期タスクの2回行うことに "Hello sample"が出力されると思います。
内部状態を作る
次に、内部状態を複数作りたいと思います。SEATMLでは、内部状態は<state>タグで作成します。ここでちょっと注意しなければいけないのは、"start"という名前で内部状態を作成すると、必ずこの状態が初期状態になります。(ハードコーディングされています)
したがって、特に初期状態にしたいというのでなければ、"start"という名前は付けない方が良いでしょう。
ここでは、main2という内部状態を作成して、ontimeoutで2秒ごとに内部状態を遷移するようにしてみます。
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general name="sample" rate="1">
<onexec>
<script>
print("==> onexec in general")
</script>
</onexec>
</general>
<state name="main">
<onexec>
<script>
print("==> onexec in state:main")
</script>
</onexec>
<ontimeout timeout="2">
<statetransition>main2</statetransition>
</ontimeout>
</state>
<state name="main2">
<onexec>
<script>
print("==> onexec in state:main2")
</script>
</onexec>
<ontimeout timeout="2">
<statetransition>main</statetransition>
</ontimeout>
</state>
</seatml>
このSEATMLを実行すると2回(初回のみ3回) <onexec>の内容を実行した後に状態遷移が起こると思います。
<state>タグ内には、<onentry>タグとと<onexit>タグで、その内部状態に入ったときとその状態から出て行ったときに実行する処理(Task)を設定することもできます。ここでは、この2つのタグについては省略していますが、書式は<onexec>タグと同じです。