SEATMLファイルの書き方
SEATMLは、eSEATのコンポーネントの入出力アダプタやGUIを設計、実装するためのXMLファイルです。
以前のドキュメントはこちらです。
以前は、XSDを必須としていましたが、様々な機能拡張に伴いXSDを必要としないようにしています。ただし現在でも、XSDを指定も可能です。
ここでは、SEATMLの基本とRTコンポーネント、ROSノード、RTC-ROS連携コンポーネントの作成の例を通して、SEATMLの書き方等を書いていきたいと思います。
0. SETMLの基本構成
0.1 初めてのSEATML
0.2 adaptorを作る
0.3 ruleで内部処理を定義する
0.4 GUIパネルを作成
1. RTコンポーネントの作成
1.1 SimpleIO
1.2 SeqIO
1.3 SimpleService
2. ROSノードの作成
4. Web連携機能の作成
4.1 SimpleIO
4.2 Virtual Joystick
4.3 スマートフォンの内部センサの利用
5. その他のサンプル
5.1 GUIパネルの利用例
5.2 状態遷移マシンの利用例
5.3 LeapMotionの利用例
5.4 Choreonoidの利用例
SEATMLの基本構成
SEATMLは、独自のスキーマで定義されたXMLファイルです。そのため、下記のような文書構造になっています。
<?xml version="1.0" encoding="UTF-8" ?>
<seatml>
<general>
- Adaptorの定義<adaptor>
- 周期実行スクリプト<onexec>
- 起動時に実行されるスクリプト<script>
- 大域変数の定義<var>
- 一つの状態で一定時間入出力がない場合に実行される共通スクリプト(各状態でontimeoutがない場合有効になる)<ontimeout>
- Active化の時に実行される共通スクリプト(RTCのみ)<onactivated>
- Deactive化の時に実行される共通スクリプト(RTCのみ)<ondeactivated>
</general>
<state name="st1">
- 状態遷移時に実行するスクリプトなど<onentry>(state内に1つのみ定義可能)
- 状態を抜けるときに実行するスクリプトなど<onexit>(state内に1つのみ定義可能)
- 周期実行スクリプト<onexec>(state内に1つのみ定義可能)
- 一定時間入出力がなかった時に実行するスクリプトなど<ontimeout>(state内に1つのみ定義可能)
- ruleの定義<rule>
- GUI部品の定義 <label, button, input, text, combobox, listbox, radiobutton, scale, frame, labelframe, brk, space>
</state>
<state name="st2">
...
...
</state>
....
....
</seatml>
上の例からもわかるとは思いますが、SEATMLのファイルは、''XMLの宣言''と''1つの<seatml>タグによる定義''で構成されています。また、<seatml>タグには、
- <general>タグによるeSEATの内部状態によらない機能の定義(アダプタや周期タスクなど)
- <state>タグによる内部状態の定義
の2つの部分から構成されています。
eSEATの内部状態に依存しない機能の定義は <general>タグで行い、1つのSEATMLファイルでは1つのみ有効になっています。
<state>タグには name属性は必須であり、1つのSEATML内では、1つ以上の<state>タグによる内部状態の定義が必要になります。
<general>
''<general>''タグの中では、eSEATの内部状態によらない機能を定義します。''<general>''タグは、1つのSEATMLファイルには、1つのみ定義することができます。ここでは、通常、外部のコンポーネントとの通信を行うための''adaptor''の定義を行いますが、adaptor定義を行うときに必要な関数やクラスを読込ために''<script>''タグを使って、モジュールのインポートを行うことができます。
さらに、eSEATをRTCとして動作させる場合に、RTCの有効化(''<onactivated>'')/無効化(''<ondeactivated>'')や1つの状態におけるタイムアウト時(''<ontimeout>'')の処理、すべての内部状態で常に動作する処理(''<onexec>'')を定義することができます。
onexec, onactivated, ondeactivated, ontimeoutの各タグには、''<message>'',''<script>'', ''<shell>'', ''<statetransition>'', ''<log>''の5つのタグを使って、eSEATが実行すべき処理(Task)を定義することができます。eSEATの内部では、これらの処理はTaskクラスに変換されています。
<adaptor>
現在のeSEATでは、外部のコンポーネントとの通信はすべてadaptorを介して行われます。adaptorでサポートされている通信は、
- Raw Scoket
- HTTPD (Cometによる通信)
- OpenRTM-aistのデータポートおよびサービスポート
- ROSまたはROS2によるPublisher, Subscriber, RosService(Server, Client)
の4つです。
元々、eSEATはRTコンポーネントとして開発されてきましたが、adaptorを定義することで、ROSやHTTPとの連携も可能です。
adaptorを定義する場合には、''name''属性と''type''属性は、必ず必要となります。その他の属性(dir, document_root, port, host, datatype, size, callback, file, service, service_type, impl, if_class, interface)は、生成するadaptorのタイプに応じて必要なものが決まります。
''name''属性は、adaptorの識別子として使用しますので、1つのSEATML内で重複してしようすることができません。また、''type''属性で指定可能な値は、''socket'', ''web'', ''rtc_in'', ''rtc_out'', ''consumer'', ''provider'', ''ros_pub'', ''ros_sub'', ''ros_server'', ''ros_client''です。
ROSのアダプタに関しては、使用されている環境に応じてROS1かROS2のいづれかが使用可能です。(同時利用は不可能です)
各adaptorのタイプ別に、使用する属性は下記の通りです。
type | 設定可能な属性 | 説明 |
---|---|---|
socket | port | 接続先のポート番号 |
host | 接続先のホスト名またはIPアドレス | |
web | port | HTTPサーバーのポート番号 |
document_rootまたはdir | HTMLのドキュメントルート(オプション)(省略時は ''html'') | |
host | 接続可能なホスト名のリスト(オプション)(省略時はすべてのホストから接続可能) | |
rtc_in, rtc_out | datatype | データポートのデータ型 |
provider | interface | サービスポートのインターフェースタイプ(''interface_type|interface_name''の書式とする) |
if_class | 実装クラス名 | |
impl_file | 実装クラスが定義されたファイル名 | |
consumer | interface | サービスポートのインターフェースタイプ(''interface_type|interface_name''の書式とする) |
if_class | インターフェース名 | |
ros_pub | datatype | Publisherのメッセージ型 |
size | ROS1の時、queue_sizeのサイズ(デフォルトは1) ROS2の場合は無視される | |
ros_sub | datatype | Subscriberのメッセージ型 |
callback | Subscriberのコールバック関数(デフォルトは、seat.onData) | |
file | Subscriberを生成する前に読み込むPythonスクリプト(オプション) | |
ros_server | service | ROSサービス名 |
service_type | ROSサービスの型 | |
impl | 実装関数名 | |
file | ROS Serverを生成する前に読み込むPythonスクリプト(オプション) | |
ros_client | service | ROSサービス名 |
service_type | ROSサービスの型 |
<state>
<state>タグは、eSEATの内部状態とその振る舞いを定義するためのものです。1つのSEATMLファイルには、1つ以上の<state>(状態)が必要です。
SEATML内で最初に定義された<state>が初期状態となります。他の状態遷移を行うには、<statetransition>タグを使うかstateTransferメソッドを利用します。
stateTransferメソッドを使うと状態遷移の履歴保持ができませんので、<statetransition>タグで設定することを推奨しています。
<state>タグの中には、
- 状態遷移時に実行する処理(状態に入るとき(''<onentry>'')と出るとき(''<onexit>''))
- 状態に滞在中に実行する周期タスク(''<onexec>'')
- 状態に滞在中のタイムアウトに対する処理(''<ontimeout>'')
- 状態に滞在中に非活性化が行われた時の処理(''<ondeactivated>'')
- adaptorから受けっとったデータまたはGUIの操作に対する処理 (''<rule>'')
- GUIパネルの要素(''label, brk, space, frame, labelframe, button, input, text, combobox, checkbutton, listbox, radiobutton, scale'')
で定義した子要素を記述することができます。
<state>タグで定義する内部状態は、状態遷移マシンの"状態”そのものと同値です。また、状態遷移マシンのイベントは、<rule>または<ontimeout>であると考えると、このSEATMLの表現は、SCXMLで表現された状態遷移マシンに置換可能です。
<onentry><onexit><onexec><ondeactivated>
これらのタグには、eSEATで行う''処理(Task)''を定義することができます。これらは<general>タグで定義したものと同じです。eSEATの処理(Task)の詳細については、後述します。
<ontimeout>
このタグで記述できるのは、上記の4つのタグと同じようにeSEATで行う処理です。上記のタグと異なるのは、<ontimeout>には、timout属性を取ります。この属性で設定された数字の単位は''秒''であり、この状態に滞在中に、adaptorからの入力およびGUIパネルへの操作がtimeoutで属性で指定された秒数経過すれば、ここで定義された処理(Task)が実行されます。
これによって、一定時間に何もなければ、内部状態を初期状態に戻すなどの記述が可能です。
<rule>
このタグは、eSEATにおける振舞の大部分を占めるものです。''<key>タグ''かつ''source属性''で定義された条件で、実行される処理(Task)を定義します。
実行される処理に''<cond>タグ''で設定された条件があった場合には、その条件を満たす場合にのみ処理が実行されます。
<key>タグに関しては、単純な文字列ですが、adaptorからの入力およびGUI部品のラベルとのマッチングを取ります。マッチングには、Pythonのmatch関数が用いられています。
実際にどのような振る舞いをするかは、サンプル等を見ていただけるとわかりやすいと思います。
GUI部品
GUI部品は、eSEATの内部状態ごとのUIを設定することができます。現在利用可能な部品は、Tkinterにおける''label, frame, labelframe, button, input, text, combobox, checkbutton, listbox, radiobutton, scale''です。パネルのレイアウトは、GridLayoutを使っており、多段やスキップをおこなうために''brk''や''space''等のタグを使います。
具体的な例は、examplesにあるサンプルを参照してください。
eSEATの処理(Task)について
eSEATにおける処理(Task)とは、''<message>'',''<script>'', ''<shell>'', ''<statetransition>'', ''<log>''の5つのタグを使って、定義します。eSEATの内部では、これらの処理はTaskGroupクラスに変換され、各タグは、Taskクラスに変換、保持されています。
Taskクラス、TaskGroupクラスには、<cond>タグで定義された conditionを設定することができます。
また、各タグで定義される処理は下のようになっています。
- <message>
- sendto属性で指定されたadaptorに対し、設定された文字列または処理結果(rtc_result)を出力します
- <script>
- 子要素として記載されたPythonスクリプトを実行し、その結果が''__result__''に格納されます。sendto属性があり、''__result__''が真であれば、rtc_resultに格納されたデータをsendto属性で指定されたadaptorに出力する。また、このタグに記載されたPythonスクリプトでは、''rtc_in_data'',''web_in_data''がadaptorからの受信したデータとして参照可能です。
- <shell>
- 子要素として記載されたシェルコマンドを実行します。sendto属性があれば、実行結果を指定されたadaptorに出力します。
- <log>
- 子要素の文字列をloggerに出力します
- <statetransition>
- eSEATの内部状態を子要素に指定された状態に遷移します
SEATMLで記述された処理がどのようになるかは、サンプル等を参照して頂ければよりわかりやすいと思います。