5.1.3 C言語カスタムコンポーネント作成
本項では、C言語によるカスタムコンポーネントの作成方法について説明します。
5.1.3.1 前提知識#
本項の内容は以下に記す知識・経験を前提としています。
- C言語を用いた一般的なプログラムの開発経験
- C言語の開発に必要なツール類の準備
- Makefileによるビルドの実行方法
また、本項ではSpeeDBee Synapseが稼働する環境内でCのプログラムをビルドする前提で記載しています。 SpeeDBee Synapseが稼働する環境とは別の環境でプログラムをビルドして移動する場合は、その環境に応じてクロスビルドが必要になります。クロスビルドの方法は稼働する環境のマニュアル等をご確認ください。
5.1.3.2 サンプルカスタムコンポーネント#
C言語によるカスタムコンポーネントの実装方法説明のため、シンプルな仕様のカスタムコンポーネントをサンプルとして提供しています。
もしくは、SpeeDBee Synapseをインストール済みの場合、カスタムコンポーネントサンプルも下記の場所にインストールされています。
$ ls /usr/local/speedbeesynapse/share/sample_component_c/*
/usr/local/speedbeesynapse/share/sample_component_c/cmake_project:
CMakeLists.txt README.md countandrandom.c readandlog.c
/usr/local/speedbeesynapse/share/sample_component_c/makefile_project_for_linux:
Makefile README.md countandrandom.c readandlog.c
/usr/local/speedbeesynapse/share/sample_component_c/makefile_project_for_windows:
Makefile README.md countandrandom.c readandlog.c
サンプルカスタムコンポーネントのディレクトリは、ビルド環境に合わせて下記の3種類用意されています。
-
cmake_project
CMakeを使ってビルドするためのプロジェクトディレクトリです。 Linux/Windowsどちらも同じ手順でビルドできます。
-
makefile_project_for_linux
Makeを使ってビルドするためのLinux環境専用プロジェクトディレクトリです。
-
makefile_project_for_windows
WindowsのNMAKEを使ってビルドするためのプロジェクトディレクトリです。
5.1.3.2.1 サンプルコンポーネントのビルド#
各プロジェクトディレクトリでのビルド方法は、それぞれのREADME.mdファイルを参照してください。 どのプロジェクトディレクトリも、ビルドして生成されるコンポーネントは同一のものになります。
SpeeDBee Synapseを実行する環境がWindowsの場合、cmake_project
もしくはmakefile_project_for_windows
のどちらかを選択してください。
実行する環境がLinuxの場合、cmake_project
もしくはmakefile_project_for_linux
のどちらかを選択してください。
5.1.3.2.2 サンプルの内容#
カスタムコンポーネントのサンプルとして本項では下記のサンプルを説明します。
-
count_and_random
(countandrandom.c)出力ポートに対して以下の2つのカラムを作成し、データを出力し続けるコンポーネントです。
カラム名 内容 count 3秒に1回、1から順にカウントアップした値を登録していきます random 3秒に1回、[0.0, 100.0)の範囲内の値をランダムに登録します -
read_and_log
(readandlog.c)入力ポートからデータを受け取り、それを文字列型カラムとログに出力します。
5.1.3.2.3 実行#
サンプルコンポーネントのビルドに成功すると、Linuxなら.so
ファイルが、Windowsなら.dll
ファイルがコンポーネントごとに生成されます。
生成された.so
or .dll
ファイルをWEBUIから登録することで、SpeeDBee Synapseからコンポーネントとして利用できるようになります。
登録手順は カスタムコンポーネントを使用するを参照してください。
ファイルを登録してコアが再起動されると、SpeeDBee Synapseの画面から、他のコンポーネントと同様に新しいカスタムコンポーネントを使用できるようになっています。
count_and_random
、および、read_and_log
を繋いで実行することで、それぞれの出力したカラムを表示できます。
下記はcount_and_random
の出力ポートをモニタリングしています。
5.1.3.3 カスタムコンポーネント実装詳細#
以降では、サンプルのcount_and_random
、および、read_and_log
のソースコードを例に、カスタムコンポーネントの実装内容を説明していきます。
5.1.3.3.1 インクルードヘッダ#
Cによるカスタムコンポーネントの開発では、ヘッダファイルhiveframework.h
をインクルードする必要があります。
#include <hiveframework.h>
: 以降、カスタムコンポーネントの実装
5.1.3.3.2 カスタムコンポーネント情報定義#
Cによるカスタムコンポーネントのソースコードでは、下記のグローバル変数定義により、そのコンポーネントの名前や識別情報、ライフサイクルコールバック関数の定義が必要になります。
: カスタムコンポーネントの各種関数の定義
const static HIVE_COMPONENT_DEFINITION info = {
.uuid = "2e80ad02-1730-4bdc-b321-2fbc2d1b942e",
.name = "count and random",
.parameter_type = HIVE_COMPONENT_PARAMETER_NONE,
.in_ports = 0,
.out_ports = 1,
.functions = {
.constructor = NULL, // optional
.premain = premain, // optional
.main = mainloop, // required
.postmain = postmain, // optional
.destructor = NULL, // optional
.stop = NULL, // optional
},
};
EXPORT_COMPONENT_DEFINITION(info);
メンバ名 | 定義情報 | 説明 |
---|---|---|
uuid |
UUID | コンポーネントを識別するためのUUIDです。ランダムなIDを生成してここに設定してください。 |
name |
コンポーネント名 | コンポーネントの名前を設定します。画面にはこの名前が表示されますのでなるべく他のコンポーネントと被らない方が良いです。 |
tag |
タグ | コンポーネントへタグを付与します。 "collector","emitter","serializer","action","logic"のいずれかの指定によりコンポーネントベースの配置先を指定できます。省略した場合は、Customへ配置されます。 |
parameter_type |
パラメータ種別 | このコンポーネントに渡すパラメータの形式をNONE, TEXT, JSONから選択できますが、現状はこの項目は使われていません。 |
out_ports |
出力ポート数 | このコンポーネントが持つ出力ポートの数を設定します。他のコンポーネントの入力ポートにデータを渡す場合はこれを1としてください。 |
in_ports |
入力ポート数 | このコンポーネントが持つ入力ポートの数を設定します。他のコンポーネントの出力ポートからデータを受け取る場合は、これを1としてください。 |
functions |
コールバック関数リスト | このコンポーネントの動作を実装した関数のリストを設定します。次節参照。 |
UUIDについては必ずランダムなIDを生成してください。 他のコンポーネントと同じUUIDにしてしまうと、どちらのコンポーネントも使えなくなることがあります。
5.1.3.3.3 ライフサイクルコールバック定義#
前節のfunctions
メンバは、コンポーネントインスタンスのライフサイクルに基づいて
コールされる関数ポインタを設定することができます。
コールバック関数 | 説明 |
---|---|
constructor |
コンポーネントのインスタンスが生成されたときにコールされます |
destructor |
コンポーネントのインスタンスが破棄されるときにコールされます |
premain |
コンポーネントを開始するときにmainの前にコールされます |
main |
コンポーネントのメインの処理を実装する関数です コンポーネントの終了要求を受けるまで、この関数は終了せずにループ処理し続ける必要があります |
postmain |
コンポーネントの終了時、mainの終了直後にコールされます |
stop |
mainの実行中、コンポーネントの停止要求を受けたときに実行されます |
前節のfunctions
定義は、サンプルコンポーネントのcount_and_random
の末尾に定義されています。
このコンポーネントではconstructor
, destructor
は不要なため、省略(NULL指定)しています。premain
, postmain
も不要であれば省略することができます。(このサンプルでも、実際にはログ出力しか実装されていません)
main
は、コンポーネントの処理の本体になりますので、必須です。
実際の関数定義名はmainloop
など、別の名前にしてください。main
だとCの実行形式本体のmain
関数と衝突してしまいます。
以降ではサンプルプログラムのmain
関数の実装内容を説明します。
5.1.3.3.4 出力ポートのカラム作成/データ登録(count_and_random
)#
下記はカスタムコンポーネントcount_and_random
のメインの処理です。
これをもとにデータを出力する流れを説明します。
static bool mainloop(HIVE_COMPONENT comp, const char *param, HIVE_STATUS status) {
HIVE_LOG_INFO("create columns");
HIVE_OUTCOLUMN clm_count = hive_outport_create_column(comp, OUTPORT1, "count", HIVE_DATA_SCALAR(HIVE_TYPE_INT32), HIVE_COLUMN_OPTION_NONE);
HIVE_OUTCOLUMN clm_random = hive_outport_create_column(comp, OUTPORT1, "random", HIVE_DATA_SCALAR(HIVE_TYPE_DOUBLE), HIVE_COLUMN_OPTION_NONE);
int count = 1;
while (hive_component_runnable(comp)) {
HIVE_LOG_INFO("insert count");
if (!hive_outcolumn_insert(clm_count, &count)) {
HIVE_API_ERROR err = hive_get_api_error();
HIVE_LOG_ERROR("insert 'count' failed: errcode=0x%08x", err.code);
}
HIVE_LOG_INFO("insert random");
double r = 100 * (double)rand() / (double)RAND_MAX;
if (!hive_outcolumn_insert(clm_random, &r)) {
HIVE_API_ERROR err = hive_get_api_error();
HIVE_LOG_ERROR("insert 'count' failed: errcode=0x%08x", err.code);
}
usleep(3000000);
count++;
}
return true;
}
-
カラムの作成
初めに、カラムを作成しています。
このコンポーネントでは32bit整数のカラムHIVE_OUTCOLUMN clm_count = hive_outport_create_column(comp, OUTPORT1, "count", HIVE_DATA_SCALAR(HIVE_TYPE_INT32), HIVE_COLUMN_OPTION_NONE); HIVE_OUTCOLUMN clm_random = hive_outport_create_column(comp, OUTPORT1, "random", HIVE_DATA_SCALAR(HIVE_TYPE_DOUBLE), HIVE_COLUMN_OPTION_NONE);
count
と倍精度浮動小数点のカラムrandom
を利用しますので、hive_outport_create_column()
関数を使用して引数にカラム名、データ型等を指定して、カラムを生成しています。 戻り値は後にカラムにデータを登録する際に使用しますので、変数に保持しておいてください。コンポーネントが出力するすべてのデータは、このように生成したカラムに対して登録していく必要があります。
-
定期的な処理の繰り返し
定期的にデータを登録するため、whileループで繰り返し処理を実装しています
ループの判定条件で呼び出しているint count = 1; while (hive_component_runnable(comp)) { : usleep(3000000); count++; }
hive_component_runnable()
は、コンポーネントが実行状態の間はtrueを返します。画面からコンポーネントの停止を要求すると、この関数がfalseを返すようになりますので、その場合には速やかにこのmainloop
関数を終わらせる必要があります。繰り返し処理にはこのようなwhileループの他に、
hive_component_interval_call()
を使用することもできます。 使用方法はリンク先を参照してください。 -
カラムへのデータ登録
繰り返し処理の中で、カラムに対してデータを登録します。
データの登録はif (!hive_outcolumn_insert(clm_count, &count)) { HIVE_API_ERROR err = hive_get_api_error(); HIVE_LOG_ERROR("insert 'count' failed: errcode=0x%08x", err.code); } double r = 100 * (double)rand() / (double)RAND_MAX; if (!hive_outcolumn_insert(clm_random, &r)) { HIVE_API_ERROR err = hive_get_api_error(); HIVE_LOG_ERROR("insert 'count' failed: errcode=0x%08x", err.code); }
hive_outcolumn_insert()
で行えます。 第一引数には事前に作成したカラム変数を、第二引数には登録するデータのポインタを指定します。 このときのポインタは、事前にカラムを作成した時に指定したデータ型と合わせる必要があります。異なる型のデータを指定すると、ソフトウェアが異常終了する原因となりえますので注意してください。
5.1.3.3.5 入力ポートからのデータ読み込み(read_and_log
)#
下記はカスタムコンポーネントread_and_log
のメインの処理です。(コメントやログ出力は省略)
static bool mainloop(HIVE_COMPONENT comp, const char *param, HIVE_STATUS status) {
HIVE_OUTCOLUMN clm_log = hive_outport_create_column(comp, OUTPORT1, "log", HIVE_DATA_SCALAR(HIVE_TYPE_STRING), HIVE_COLUMN_OPTION_NONE);
HIVE_CONTINUOUS_READER *creader
= hive_inport_continuous_reader(comp, INPORT1, hive_timestamp()+5000000000UL);
hive_time_t last_updated = 0;
while (hive_component_runnable(comp)) {
const HIVE_COLUMN_READ_RESULT *read_result = hive_continuous_reader_read(creader);
if (!read_result) {
continue;
}
if (last_updated < read_result->incolumn_list->updated_at) {
last_updated = read_result->incolumn_list->updated_at;
}
HIVE_RECORD_ITERATOR iter = HIVE_GET_RECORD_ITERATOR(read_result);
const HIVE_RECORD *record;
while ((record = HIVE_RECORD_ITERATOR_GET_NEXT(&iter)) != NULL) {
for (int i=0; i < record->data_count; i++) {
const HIVE_RECORD_DATA *rd = HIVE_RECORD_GET_DATA(record, i);
if (rd && rd->data_size != 0) {
log_record(clm_log, record->timestamp, &read_result->incolumn_list->incolumns[i], rd);
}
}
}
}
hive_continuous_reader_release(creader);
return true;
}
-
カラムの作成
このコンポーネントでも、初めにカラムを作成しています。
このカラムは文字列を出力するためのものです。HIVE_OUTCOLUMN clm_log = hive_outport_create_column(comp, OUTPORT1, "log", HIVE_DATA_SCALAR(HIVE_TYPE_STRING), HIVE_COLUMN_OPTION_NONE);
-
データ取得ループ
入力ポートからのデータを繰り返し受け取る処理が下記になります。
HIVE_CONTINUOUS_READER *creader = hive_inport_continuous_reader(comp, INPORT1, hive_timestamp()+5000000000UL); : while (hive_component_runnable(comp)) { const HIVE_COLUMN_READ_RESULT *read_result = hive_continuous_reader_read(creader); if (!read_result) { continue; } : } hive_continuous_reader_release(creader);
ここでは4つの関数を利用しています
hive_inport_continuous_reader()
継続的に入力ポートからのデータを取得するためのハンドルを作成しますhive_continuous_reader_release()
データの取得を停止する場合、この関数でハンドルを解放しますhive_continuous_reader_read()
作成したハンドルからデータを取得します。取得したデータはHIVE_COLUMN_READ_RESULT
構造体のポインタとして返されますが、データがまだない場合にはNULL
が返されることもありますので、その場合は無視してください。hive_component_runnable()
こちらは前節でも使用しました。 コンポーネントが実行状態の間はtrueを返します。画面からコンポーネントの停止を要求すると、この関数がfalseを返すようになりますので、その場合には速やかにこのmainloop
関数を終わらせる必要があります。
-
取得したデータのレコードを参照
hive_continuous_reader_read()
関数により、入力ポートに入ってきたデータをある時間幅分まとめて取得し、HIVE_COLUMN_READ_RESULT
構造体のポインタ、read_result
に格納します。HIVE_COLUMN_READ_RESULT
構造体は、下図のように同一時刻のデータを収めた複数のレコードからなり、1つのレコードに複数のカラムのデータが収められています。下記のような二重ループにより、この構造体から1レコード、1データずつ処理することができます。
HIVE_GET_RECORD_ITERATOR
()とHIVE_RECORD_ITERATOR_GET_NEXT
がレコードのループ、その内部にある変数i
によるfor文がレコード内のデータ処理の繰り返しですHIVE_RECORD_ITERATOR iter = HIVE_GET_RECORD_ITERATOR(read_result); const HIVE_RECORD *record; while ((record = HIVE_RECORD_ITERATOR_GET_NEXT(&iter)) != NULL) { for (int i=0; i < record->data_count; i++) { const HIVE_RECORD_DATA *rd = HIVE_RECORD_GET_DATA(record, i); if (rd && rd->data_size != 0) { // ここでrdが1カラム分のデータを保持している log_record(clm_log, record->timestamp, &read_result->incolumn_list->incolumns[i], rd); } } }
-
レコード内の1データの扱い
1つのデータの情報は、
HIVE_RECORD
構造体とHIVE_COLUMN_READ_RESULT
構造体のincolumn_list
メンバから取得することができます。項目 型 本サンプルプログラムでの参照例 内容 データ名 const char* read_result->incolumn_list->incolumns[i]->data_name
データを登録しているカラム名 コンポーネント名 const char* read_result->incolumn_list->incolumns[i]->source_name
データを生成したコンポーネント名 データ型 HIVE_DATA_TYPE read_result->incolumn_list->incolumns[i]->data_type
データを登録しているカラムのデータ型 データサイズ uint32_t HIVE_RECORD_GET_DATA(record, i)->data_size
実データのデータサイズ データアドレス char * HIVE_RECORD_GET_DATA(record, i)->data
実データを格納したアドレス 上記の通り、実データは
const char
ポインタとなっています。様々なデータ型を受ける可能性があるため、HIVE_DATA_TYPE
で識別してそれぞれの型に応じてキャストして参照する必要が有ることにご注意ください。
5.1.3.3.6 ログ出力#
コンポーネントでは任意のタイミングでログを出力することができます。
HIVE_LOG_INFO("create columns");
HIVE_LOG_INFO("x=%d, y=%f, z=%s", x, 12.34, "aiueo");
HIVE_LOG_ERROR("insert 'count' failed: errcode=0x%08x", err.code);
上記のように、標準ライブラリのprintf()
と同じ形式でログを出力できます。
出力したログは、画面からダウンロードすることができます。
「各種ログ」を参照してください。
5.1.3.3.7 コンポーネントパラメータ#
画面上からコンポーネントインスタンスを生成すると、その設定項目としてコンポーネントの実行時パラメータを設定することができます。
パラメータの種別としてはJSONでもSTRINGでも利用可能です。
どちらを指定した場合でも、Cで実装されたコンポーネントにおいては、main関数の第二引数にconst char*
型の文字列として受け渡されますので、これを標準ライブラリのsscanfで解析したり、直接文字列として扱ったりすることが可能です。
static bool mainloop(HIVE_COMPONENT comp, const char *param, HIVE_STATUS status) {
int val1, val2;
sscanf(param, "%d,%d", &val1, &val2); // perform parameter as a number string
// some process using 'val1' and 'val2'.
}
JSON形式のパラメータを扱う場合も、paramに文字列としてJSON形式が格納されますので、これをパースする必要があります。 JSONの扱いは本システムのAPIとしては用意していませんので、汎用的なライブラリの使用を検討してください。
参考
5.1.3.4 カスタムコンポーネント用API#
本項で紹介した関数以外にも、多数のAPI関数が用意されています。 詳細は付録のカスタムコンポーネントC-APIリファレンスを参照してください。
5.1.3.5 カスタムコンポーネントを使用する#
ここでは、作成したカスタムコンポーネントをSpeeDBee Synapseで使用する方法について説明します。
5.1.3.5.1 登録#
次の手順で、ビルドしたカスタムコンポーネントをSpeeDBee Synapseに登録することができます。
-
設定メニューアイコンを押下し、「カスタム(C)」を選択します。
-
「追加」を押下し、ビルド済みのカスタムコンポーネントのsoファイルを登録します。
-
「閉じる」を押下します。
-
確認ダイアログで「はい」を選択し、変更を反映するために、システムを再起動させます。
-
左メニューに、登録したカスタムコンポーネントが反映されます。
ファイルの保存先
登録したカスタムコンポーネントのファイルは、次のディレクトリに保存されます。
/var/speedbeesynapse/custom_component_so
保存先のディレクトリは、画面項目「保存先」に表示されます。
5.1.3.5.2 設定画面#
カスタムコンポーネントの設定画面の項目は次の通りです。
項目 | 説明 |
---|---|
名称 | コンポーネントの名前を入力 ※他のコンポーネント名と重複する事はできません。 |
自動起動無効 | コンポーネントの自動起動を無効にする場合ON |
パラメータータイプ | パラメータの種類を次の中から選択 ・STRING:文字列 ・JSON:JSON文字列 |
パラメーター | コンポーネントパラメータを入力 |
スクリプトで使用するファイルをアップロードする(証明書など) | カスタムコンポーネントの処理で使用する、証明書などのファイルをアップロードする場合ON |
ファイル追加 | 押下すると、スクリプトで使用するファイルをアップロードすることができます。 ※新しく追加されたファイルは、コンポーネントの設定を保存した時にアップロードされます。 |
ファイルパス | アップロードしたファイルの保存先 ※パラメータでファイルパスを使用する場合、変数を用いることができます。詳細は、表題の「ファイルパス」の右のアイコンをクリックすると確認することができます。 |
設定画面のパラメータ入力部分に、ユーザーが定義した画面項目を表示するように変更することができます。 詳細は「カスタムUI作成」をご覧ください。
5.1.3.5.3 削除#
次の手順で、登録したカスタムコンポーネントをSpeeDBee Synapseから削除することができます。
-
設定メニューアイコンを押下し、「カスタム(C)」を選択します。
-
カスタムコンポーネントの「削除」を押下します。
-
確認ダイアログで「はい」を選択します。
-
カスタムコンポーネントが削除されます。
5.1.3.5.4 ダウンロード#
次の手順で、登録したカスタムコンポーネントをダウンロードすることができます。
-
設定メニューアイコンを押下し、「カスタム(C)」を選択します。
-
カスタムコンポーネントの「取得」を押下します。
-
カスタムコンポーネントのファイルがダウンロードされます。