[Glue!] 一般的なエージェント


一般にエージェントのプログラムは以下のようになる:
#include <stdio.h>
#include <string.h>
#include "GlueLogicAPI.h"

void main( argc, argv )
     int	argc;
     char	**argv;
{
    char	line[1024], *p, *q;

    GlueLogicParseArgs( argc, argv );
    GlueLogicConnect( "GlueC#", NULL, NULL );
    printf( "Agent:  [%s]\nServer: [%s]\nAnchor: [%s]\n\n",
	   GlueLogicAgent, GlueLogicServer, GlueLogicAnchor );

    sprintf( line, "!AddInformTo %s.a %s", GlueLogicAnchor, GlueLogicAgent );
    GlueLogicAccess(line, 1024, line, NULL);
    sprintf( line, "!AddInformTo %s.bb %s", GlueLogicAnchor, GlueLogicAgent );
    GlueLogicAccess(line, 1024, line, NULL);
    sprintf( line, "!AddInformTo %s.zzz %s", GlueLogicAnchor, GlueLogicAgent );
    GlueLogicAccess(line, 1024, line, NULL);

    while (1) {
	GlueLogicEnqueueMessage();
	if ( ! GlueLogicMessageLines() ) {
	    GlueLogicWaitForMessage(); GlueLogicEnqueueMessage();   }
	if ( GlueLogicEndOfMessage() ) { break; }
	if ( GlueLogicGetMessage(line,1024) == NULL ) { continue; }

	if ( strncmp( line, "Changed ", 8 ) ) { continue; }
	if ( q = strchr( (p = line+8), ' ' ) ) ) { *q = '\0'; }
	if ( strncmp( p, GlueLogicAnchor, strlen(GlueLogicAnchor) ) ) { continue; }
	p += strlen( GlueLogicAnchor );

	if      ( ! strcmp( p, ".a" ) )   { ProcessEventA(); }
	else if ( ! strcmp( p, ".bb" ) )  { ProcessEventBB(); }
	else if ( ! strcmp( p, ".zzz" ) ) { break; }
    }

    sprintf( line, "!DelInformTo %s.a %s", GlueLogicAnchor, GlueLogicAgent );
    GlueLogicAccess(line, 1024, line, NULL);
    sprintf( line, "!DelInformTo %s.bb %s", GlueLogicAnchor, GlueLogicAgent );
    GlueLogicAccess(line, 1024, line, NULL);
    sprintf( line, "!DelInformTo %s.zzz %s", GlueLogicAnchor, GlueLogicAgent );
    GlueLogicAccess(line, 1024, line, NULL);
    GlueLogicDisconnect();
    exit(0);
}
このプログラムは三つの部分から構成されており、 それぞれ初期化部, イベントループ, 終結部とでも呼ぶべき機能を果たしている。

初期化部

ファイルの先頭で GlueLogic.API を include する 宣言をしておき、 実行文の最初で GlueLogicParseArgs 関数によって コマンドライン引数の値を取り込む。

その後に GlueLogicConnect 関数によって サーバと通信路を開設するが、この時に与えられる引数は 通信路開設時にはもっとも優先度の低い情報源として利用される。
すなわち、これらの引数よりは環境変数の指定の方が優先され、 さらにそれらよりは GlueLogicAgent, GlueLogicAnchor, GlueLogicServer の 各文字配列の値の方が優先される。 このため、 GlueLogicParseArgs 関数が既に実行されておれば、 コマンドライン引数として指定された値が最優先で使われることになる。

初期化の最後としてこのエージェントが監視しようとする名前について、 変更通知メッセージの要求登録を行なう。


イベントループ

イベントループは、サーバからの通知メッセージを待ち合わせ、 メッセージが届けばそれに対応したメッセージ処理関数を呼び出す。 上記の例では、 アンカーで指定された名前で示される構造の二つの要素に対して、 値の変化があった時に対応する動作を行ない、 他の一つの要素の値が変化した時にはエージェントの処理を停止する。

エンドレス・ループの先頭では、 GlueLogicEnqueueMessage 関数を用いて、 サーバからの変更通知、もしくは標準入力からの入力行が到着しておれば、 それらを対応する待ち行列の最後尾に入れる。 その後、 GlueLogicMessageLines 関数を用いて変更通知メッセージの待ち行列の長さを確認し、 もしも一つもメッセージが入っていない場合には GlueLogicWaitForMessage 関数を呼び出す。 この関数は変更通知メッセージまたは標準入力からの入力行のどちらか一方が確認されるまで待ち合わせる。

もし万一、サーバが異常終了していた場合には、 GlueLogicEndOfMessage は真の論理値を返す。 この場合には必ずイベントループから抜け出すようにしておかなければならない。 なお、サーバの異常終了によって通信路が閉鎖されてしまった後では、 どの API も何もしないようになるので、 以降の処理で特に場合分けを行なう必要は無い。

次に、 GlueLogicGetMessage 関数を使って 変更通知メッセージの待ち行列の先頭の要素を読み出す。 GlueLogicWaitForMessage 関数から戻って来る原因となったイベントが 標準入力からの入力であった場合など、 待ち行列が空になっている場合には、 GlueLogicGetMessage 関数の戻り値は NULL になる。 この場合には以下の処理は無駄であるので、次にイベントの発生を待つ。

これで、新しい変更通知メッセージが確認され、その事象の解析を行なえる状態になった。 この例ではさらに、そのメッセージの第一ワードが "Changed" であることと、 第二ワードに示される名前の先頭が GlueLogicAnchor の内容に一致していることを確認し、 もしもこの前提が成り立っていない場合には次のイベントの処理に入る。

以降の連続する if 文によって行なわれる処理が、 値の変更があった事が通知された名前毎に、 対応する処理関数を呼び出す部分である。 これらの関数の中では、必要に応じて、新しい値の取得、 対応する内部処理、 GlueLogic に対するデータの書き込みが行なわれる。

この例では、 イベントとしてサーバからの通知メッセージの到着のみを対象としているが、 GlueLogicEnqueueMessage 関数は 原則として標準入力からの入力行の到着も通知メッセージの到着と同等に扱う。 このため、どちらか一方だけを問題にしたい場合には、 この例のように他方のイベントの発生を適切に無視するように注意しなければならない。


終結部

イベントループでの処理が完了すると、 それまで要求していた変更通知メッセージの登録を抹消し、 GlueLogicDisconnect 関数によってサーバとの通信路を閉鎖する。

自分が変更要求を出していた名前の全てに対して、 忘れずに必ず要求登録を抹消しなければならない。


 [M.T. HomePage]  [written & copyrighted by Masayuki Takata]