「MrubyBridgePlugin のマルチ VM 対応」という記事を書き始めたのですが、その前に MrubyBridgePlugin の使い方を紹介する記事を書いていないことに気付きました。それで、改めて簡単なサンプルプログラム HelloMruby を作成して MrubyBridgePlugin の使い方を紹介することとしました。
なお MrubyBridgePlugin は、昨年(2013年) の TOPPERS カンファレンスに合わせて公開した
MrubyBridgePlugin パッケージに含まれています。
それから、記事のタイトルを TECS 版 HelloMruby としましたが、TECS 版以外の HelloMruby があるわけではありません (恐らく英語的には HelloMruby with TECS な感じなのですが、日本語的にしっくりくる感じで選んだだけです)。
■ MrubyBridgePlugin の概要 さて MrubyBridgePlugin が何ものかですが、mruby のスクリプトから TECS のコンポーネントを操作するためのインタフェースコードを自動生成するものになります。このインタフェースコードは、TECS の側からみれば TECS のコンポーネントであり、mruby の側から見れば C 言語によるクラスの実装になります。
図1は、HelloMruby を例題とする MrubyBridgePlugin の効能を示すための概念図です。

(クリックすると拡大します)
図1の右半分は TECS のコンポーネント図になっています。ブリッジセル(BuddyBridge) からターゲットセル(Buddy) へ結合しています。左半分は、何かの記法に従っているわけではありませんが、TECS コンポーネント図風に、mruby のスクリプトとブリッジセルの関係を示しています。mruby 上で、BuddyBridge と結合する mruby オブジェクトを生成します (細かいことを言うと BuddyBridge は、 MRB_TT_DATA な mruby オブジェクトの本体となります)。この mruby オブジェクトを介して、mruby のスクリプトから、シグニチャ sHello の関数 (myMessage, yourMessage) を呼出すことができます。
■ HelloMruby の構成 HelloMruby の例を使って、もう少し詳しく MrubyBridgePlugin の働きを見ていきましょう。
図2 は HelloMruby の TECS コンポーネント図にコメントを書き足したものです。ただし mruby から操作される BuddyBridge と CharPointer セルは、左半分を丸くしています。この丸く記す方法は、本来の TECS コンポーネント図にはない記法ですが、図1との対比を分かりやすくするために、そのように記しています。

(クリックすると拡大します)
この図には TECS コンポーネント化された mrubyVM があります。図1との対比では、mruby VM の中でスクリプトが動作することになります。BuddyBridge と Buddy は図1にもありました。
MrubyBridgePlugin が自動生成するセルが三つあります。これらの役割りは、以下の通りです。
* TECS モジュールを登録する
* TECS モジュールの下に TsHello クラスおよびメソッドを登録する
* TECS モジュールの下に CharPointer クラスおよびメソッドを登録する
■ TECS CDL の記述 図2に対応する TECS CDL による記述を示します。
// HelloMruby.cdl
// (1) 必要なものを import
import( <cygwin_kernel.cdl> );
import( "MyMrubyVM.cdl" );
// (2) シグニチャ sHello
signature sHello {
ER myMessage( [in,string]const char_t *buf );
ER yourMessage( [out,string(len)]char_t *buf, [in]int32_t len );
};
// (3) セルタイプ tBuddy
celltype tBuddy {
entry sHello eEnt;
};
// (4) セル Buddy
cell tBuddy Buddy
{
};
// (5) MrubyBridgePlugin の呼出し
generate( MrubyBridgePlugin, sHello, "" );
// (6) ブリッジセルの設置
cell nMruby::tsHello BuddyBridge {
cTECS = Buddy.eEnt;
};
// (7) mrubyVM の設置
cell tPosixMrubyVmInTask MrubyVMTask{
cInit = VM_TECSInitializer.eInitialize;
argc = 2;
argv = { "HelloMruby.exe", "HelloMruby.rb" };
priority = 11;
stackSize = 4096;
taskAttribute = C_EXP( "TA_ACT" );
};
図2で示したものを TECS CDL で表していますので、登場するシグニチャ、セルタイプ、セルは既に見たものになります。しかし TECS コンポーネント図には、シグニチャの持つ関数やセルの持つ属性を表しませんので、それらが CDL の記述では加えられています。
それでは、順を追って、何が書かれているか、見てみましょう。
(1) で cygwin_kernel.cdl と MyMrubyVM.cdl をインポートしています。
cygwin_kernel.cdl は、cygwin または POSIX 環境でテストするときのテスト用のタスク、セマフォを提供します。cygwin_kernel.cdl で定義されているセルタイプは、機能、コード品質ともに高くなく、テスト専用とお考えください。
MyMrubyVM.cdl は、mruby VM のセルタイプ tPosixMrubyVmInTask (複合コンポーネント)を定義しています。ここでは詳細を割愛しますが、cygwin (POSIX) 環境でマルチ VM のテストを容易に行えるように mruby VM を TECS コンポーネント化したものです。
ここで <> と "" の相違ですが、C の #include と同様の使い分けです。TECS ジェネレータは、この相違に基づいて、セルタイプコードのテンプレートを生成するかどうか、判断しています。<> の場合には、既に開発済みと判断して、セルタイプコードのテンプレートを生成しません。不要なテンプレートを生成させないようにして、tecsmerge (テンプレートに変化があったとき、セルタイプコードにマージさせるツール) を使う場合の失敗を予防することができます。
(2) ~ (4) は、シグニチャ、セルタイプとセルの基本的な記述です。ここでは、説明を割愛します。一点だけ補足すると TECS の out 引数は呼び元が記憶域を用意する必要があります。
(5) は MrubyBdridgePlugin を呼出しています。TECS では、いくつかのプラグインの種類があるのですが、MrubyBridgePlugin はシグニチャプラグインというもので、genereate の第一引数はプラグイン名、第二引数はシグニチャ名、第三引数はプラグインのオプションです。プラグインのオプションは、後述するドキュメントにありますので、参照してください。
(6) は、ブリッジセルの設置です。MrubyBdridgePlugin は、ブリッジセルタイプを自動生成します。ブリッジセルは CDL 上に記述して生成する必要があります。このセルタイプ名は、TECS ジェネレータの以下のメッセージで確認できます。
MrubyBridgePlugin: [signature] ::sHello => [celltype] nMruby::tsHello => [class] TECS::TsHello
このメッセージは MrubyBdridgePlugin から出力されるもので、(5) まで記述した状態で TECS ジェネレータを動かす必要があります。
(7) は、VM の設置です。ここで重要なのは、以下の記述です。
cInit = VM_TECSInitializer.eInitialize;
これは、初期化セル VM_TECSInitializer への結合です。VM_TECSInitializer は、MrubyBridgePlugin により自動生成されます。このセル名は、TECS ジェネレータの以下のメッセージで確認できます。
MrubyBridgePlugin: join your VM's cInitialize to VM_TECSInitializer.eInitialize
このメッセージは MrubyBdridgePlugin から出力されるもので、(6) まで記述した状態で TECS ジェネレータを動かす必要があります。
注意点ですが、cInit は、オプショナルな呼び口です。従って、何も結合しなくてもビルド時にエラーになりません。mruby に、思ったようにクラスが登録されていないときは、この箇所を疑ってみる必要があります。
ここで mruby VM のセルタイプ tPosixMrubyVmIntask ですが、cygwin などの POSIX 環境で動作させるように構成したもので、(1) で import した MyMrubyVM.cdl で composite されています。コマンドラインから .rb を引数として起動するもので、試験用に準備したものです。mruby on asp+tecs パッケージで使用されている VM とは、構成の仕方が異なります。
もう一つ CharPointer が何ものかを説明していませんでしたが、TECS の char * 引数を扱うためのものです。文字列引数の扱いとして mruby の文字列オブジェクトからポインタを取り出して使う方法もあったのですが、以下の理由でやめました。
* mruby の内部表現が TECS の要求するものと一致しない可能性がある
本家 Ruby の文字列オブジェクトは、文字コードも理解しており、将来そのような方向に向かう可能性がなくはない
* TECS の out 引数では呼び元がデータの格納領域を準備する必要がある
mruby に文字数を指定して文字列オブジェクトの領域だけを確保する手段が用意されていない。
また mruby の文字列オブジェクトは、文字列本体を共有することができるようになっていて、その共有を解消する関数も用意されていたが static で呼出させなかった.
(これは 2013 年の春頃の状況で、今は変わっているかもしれません)
* int8_t, int16_t などの配列を扱うものと同じコードにできる
TECS は、どちらかといえば制御系組込みに用いることが想定されているため、文字列を扱うことは多くなく、他の型のポインタ(配列) と同様に扱う方が簡単、ということもありました。
■ mruby スクリプト 上記をビルドした後で、実行する際のスクリプトを以下に示します。
# HelloTECS.rb
# Buddy セルを mruby から操作する
# (1) ブリッジセルを buddyBridge に割り付ける
print "[I create] buddyBridge.\n"
buddyBridge = TECS::TsHello.new( "BuddyBridge" )
# (2) CharPointer を生成 (長さ32の配列)
len = 32
buf = TECS::CharPointer.new( len )
# (3) buf にメッセージを設定
buf.from_s "Hello TECS! (from mruby)"
# (4) Buddy の myMessage を呼出す
buddyBridge.myMessage( buf )
# (5) Buddy の yourMessage を呼出す
buddyBridge.yourMessage( buf, len )
print "[I received] " + buf.to_s + "\n"
# buf は out 引数.Buddy で設定されて戻る
# len 引数は、冗長だが、buf の実際の長さより大きいと例外発生
(1) で、ブリッジセルを mruby オブジェクトとして生成します。
クラス名は TsHello になります。これは、ブリッジセルのセルタイプ名 tsHello の先頭を大文字としたものです。TsHello は TECS モジュールの下に作られますので TECS::TsHello.new となります。BuddyBridge はブリッジセルの名前です。若干冗長のようですが、セルタイプ名とセル名の両方を指定して、mruby 側のブリッジオブジェクトを生成します。
(2) は、char * 引数を扱うための mruby オブジェクトを生成します。
char * (文字列) を CharPointer クラスとして扱います。
(3) で、buf にメッセージを設定します。
メッセージを長くすると、32文字で切り捨てられます。
ここで .from_s を忘れて代入してしまうと、CharPointer クラスから String クラスに変わってしまい、(4) の呼出しで mruby 例外が発生します。
(4) で Buddy にメッセージが渡されます。
これは sHello にあった関数と同じ名前、同じ引数並びとなります。
(5) で、Buddy からのメッセージを受け取り表示します。
これも sHello の関数です。
■ HelloMruby のビルド cygwin または Linux 環境でビルドおよび実行できます。HelloMruby のソースコードは、TOPPERS プロジェクトの Contributed Software としてアップしてあります。以下のコマンドで、ダウンロードできます。
svn co http://dev.toppers.jp/svn_user/contrib/HelloMruby/tags/HelloMruby-V1.0.0
HelloMruby-V1.0.0 は
MrubyBridgePlugin パッケージ の mruby-master の下、MrubyBridge の並びに置いてください。あとは、以下の手順でコマンドを実行していくだけです。
> cd tecsgen # MrubyBridgePlugin のパッケージの直下から始めます
> source set_env.sh # PATH などの設定 (必ず set_env.sh のあるディレクトリで行うこと)
> cd ../mruby-master
> make # mruby のビルド (libmruby.a が必要)
> cd HelloMruby-V1.0.0
> make # HelloMruby のビルド
> ./HelloMruby.exe # テスト実行
starting task 'tPosixMrubyVmInTask_MrubyVMTask' 004011AC
[tTaskMain2PosixMain] calling main: HelloMruby.exe HelloTECS.rb
[I create] buddyBridge.
[Buddy received] Hello TECS! (from mruby)
[I received] Hello mruby! (from TECS)
exiting task 'tPosixMrubyVM_MrubyVMTask'
■ その他のファイル 以上の他に、tBuddy などのセルタイプコードと Makefile が必要になります。
tBuddy などのセルタイプコードは、通常の TECS コンポーネントの範囲です。src/tBuddy.c と gen/tBuddy_templ.c の diff を取ってみると、どのような記述を付け加えたか (ハンドコーディング) が分かります。
Makefile は、TECS ジェネレータによって gen ディレクトリの下に作成される Makefile.templ (テンプレート) の名前を Makefile に変更して、そのまま用いています。MrubyBridgePlugin により LDFLAGS に -lruby -lm が追加されているため、POSIX 環境で GNU のツール類を使うのであれば、何も変更する必要がありません。
■ ドキュメント MrubyBridgePlugin の詳しい説明は MrubyBridgePluginPackage/mruby-master/MrubyBridge の下の mrubyBridge.txt にあります。ポインタや構造体に関する説明は、MrubyBridgePluginPackage/tecsgen/tecs/mruby の下に TECSPointer.txt と TECSStruct.txt があります。構造体は、TOPPERS/ASP のカーネル API に出てくるような構造体、すなわち構造体メンバーがスカラー値であるものに限られます。
■ おわりに ところで、本来書くつもりであった「MrubyBridgePlugin のマルチ VM 対応」については、また時間があれば書くつもりです。マルチ VM 対応では、TECS が mruby VM を静的にコンフィグレーションするツールとなり、TECS の効果的な利用方法であることが、より明確になると思います。勘のよい人であれば、本論で用いた CDL の例を見れば、どのようにすればマルチ VM 化できるかがわかるかもしれません。しかし、アクセス制限の方法は、この例からは読み取れない部分がありますので、その点の説明を加えたいと思っています。
テーマ : ソフトウェア開発
ジャンル : コンピュータ