7. Cによる独自エレメントの開発¶
Device Connector Frameworkでは、Cインターフェイスを使って独自エレメントのプラグインを開発することができます。
7.1. 開発用のファイルの準備¶
Cによる開発を行うためには、以下の2つのファイルが必要です。
Device Connector Frameworkのリポジトリに含まれる インクルードファイル
common/include/device_connector.h
静的リンク用のライブラリファイル
libdevice_connector_common.a
。以下のコマンドによりビルドしてください。git clone https://github.com/aptpod/device-connector-framework.git cd device-connector-framework cargo build -p device-connector-common --release
このコマンドにより、
target/release
以下にlibdevice_connector_common.a
が生成されます。これを任意のディレクトリにコピーしてください。
cp target/release/libdevice_connector_common.a </path/to/library_dir>
7.2. エレメントの実装¶
例として "example-plugin"
というsrcエレメントを実装します。
このエレメントは設定を持たず、1秒間隔で hello, world from plugin
という文字列のメッセージを送信します。
以下のコードブロックを example_plugin.c
という名前のファイルとして保存してください。
#include <stdbool.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <device_connector.h>
#define N_ELEMENT 1
#define PLUGIN_NAME "example-plugin"
typedef struct {
const char* text;
} ExamplePlugin;
void *example_plugin_new(const char *config);
DcElementResult example_plugin_next(void* element, DcPipeline *pipeline, DcMsgReceiver *msg_receiver);
bool example_plugin_finalizer(void *element, struct DcFinalizer *finalizer);
void example_plugin_free(void* element);
bool dc_load(DcPlugin *plugin) {
dc_init(PLUGIN_NAME);
plugin->version = "0.1.0";
plugin->n_element = N_ELEMENT;
DcElement *elements = (DcElement *)malloc(sizeof(DcElement) * N_ELEMENT);
// Element settings
elements[0].name = PLUGIN_NAME;
elements[0].recv_ports = 0;
elements[0].send_ports = 1;
elements[0].acceptable_msg_types = NULL;
elements[0].config_format = "json";
elements[0].new_ = example_plugin_new;
elements[0].next = example_plugin_next;
elements[0].finalizer = example_plugin_finalizer;
elements[0].free = example_plugin_free;
plugin->elements = elements;
return true;
}
void *example_plugin_new(const char *config) {
ExamplePlugin *example_plugin = (ExamplePlugin *)malloc(sizeof(ExamplePlugin));
example_plugin->text = "hello, world from plugin";
return example_plugin;
}
DcElementResult example_plugin_next(void* element, DcPipeline *pipeline, DcMsgReceiver *msg_receiver) {
ExamplePlugin *example_plugin = (ExamplePlugin *)element;
if (!dc_pipeline_send_msg_type_checked(pipeline)) {
DcMsgType msg_type;
if (dc_msg_type_new("mime:text/plain", &msg_type)) {
dc_pipeline_check_send_msg_type(pipeline, 0, msg_type);
}
}
sleep(1);
DcMsgBuf *msg_buf = dc_pipeline_msg_buf(pipeline);
const uint8_t *data = (const uint8_t *)example_plugin->text;
const size_t len = strlen(example_plugin->text);
dc_msg_buf_write(msg_buf, data, len);
return DcElementResult_MsgBuf;
}
bool example_plugin_finalizer(void *element, struct DcFinalizer *finalizer) {
return true;
}
void example_plugin_free(void* element) {
ExamplePlugin *example_plugin = (ExamplePlugin *)element;
free(example_plugin);
}
以下、このソースコードについて解説していきます。
#include <device_connector.h>
プラグイン開発のために device_connector.h
をインクルードします。
typedef struct {
const char* text;
} ExamplePlugin;
エレメントの本体を定義します。
bool dc_load(DcPlugin *plugin) {
dc_init(PLUGIN_NAME);
プラグインをロードする時に呼び出される dc_load
関数を定義します。最初に、 dc_init
にプラグインの名称を渡します。
plugin->version = "0.1.0";
plugin->n_element = N_ELEMENT;
このプラグインがターゲットとするデバイスコネクターのバージョンと、読み込ませたいエレメントの数を指定します。
DcElement *elements = (DcElement *)malloc(sizeof(DcElement) * N_ELEMENT);
DcElement
の配列を malloc
で用意します。この長さは読み込ませたいエレメントの数(N_ELEMENT
)と同じです。
elements[0].name = PLUGIN_NAME; // エレメントの名前(実行時に他のエレメントと重複しているとエラーになる)
elements[0].recv_ports = 0; // 受信用ポートの数
elements[0].send_ports = 1; // 送信用ポートの数
elements[0].acceptable_msg_types = NULL; // 受け取ることのできるデータ型。何も受信しない場合はNULL
elements[0].config_format = "json"; // 設定フォーマット(json or yaml)。このフォーマットがnew_に指定した関数に渡される
elements[0].new_ = example_plugin_new; // エレメントを生成する関数
elements[0].next = example_plugin_next; // エレメントを実行する関数
elements[0].finalizer = example_plugin_finalizer; // プロセス終了時呼び出されるファイナライザを設定する関数
elements[0].free = example_plugin_free; // エレメントを終了・解放する関数
エレメントの詳細を定義します。
plugin->elements = elements;
return true;
}
plugin->elements
にDcElementの配列を設定し、 dc_load
が成功したら true
を返します。
void *example_plugin_new(const char *config) {
ExamplePlugin *example_plugin = (ExamplePlugin *)malloc(sizeof(ExamplePlugin));
example_plugin->text = "hello, world from plugin";
return example_plugin;
}
エレメントを生成する関数です。 config
には、パイプライン設定ファイルに記述されたのエレメントの設定( conf
フィールドの値)が、 config_format
で指定したフォーマットに変換され文字列として渡されます。(この例では config
の値は使用していません。)
ExamplePlugin
のための領域を malloc
で確保し、初期化後に void
ポインタとして返します。失敗時には NULL
を返却します。
DcElementResult example_plugin_next(void* element, DcPipeline *pipeline, DcMsgReceiver *msg_receiver) {
ExamplePlugin *example_plugin = (ExamplePlugin *)element;
example_plugin_next
はエレメントを実行するための関数です。
受け取った element
を ExamplePlugin *
にキャストします。
if (!dc_pipeline_send_msg_type_checked(pipeline)) {
DcMsgType msg_type;
if (dc_msg_type_new("mime:text/plain", &msg_type)) {
dc_pipeline_check_send_msg_type(pipeline, 0, msg_type);
}
}
dc_pipeline_send_msg_type_checked()
で送信するメッセージの型チェックが行われているか調べ、行われていなければ、 DcMsgType
を作成して dc_pipeline_check_send_msg_type()
に渡します。
sleep(1);
DcMsgBuf *msg_buf = dc_pipeline_msg_buf(pipeline);
const uint8_t *data = (const uint8_t *)example_plugin->text;
const size_t len = strlen(example_plugin->text);
dc_msg_buf_write(msg_buf, data, len);
1秒間スリープした後、 msg_buf
を取得し、送信したいデータを dc_msg_buf_write()
で書き込みます。
ここで書き込むのは example_plugin_new
で設定したテキストです。
return DcElementResult_MsgBuf;
}
DcElementResult_MsgBuf
を返し、 msg_buf
に書き込んだデータを送信することを示します。
bool example_plugin_finalizer(void *element, struct DcFinalizer *finalizer) {
return true;
}
プロセス終了時に呼び出されるファイナライザを登録するための関数です。ここでは特に何も行いませんが、プロセス終了時にエレメントが占有するリソースを解放する必要がある場合、ファイナライザに記述します。
void example_plugin_free(void* element) {
ExamplePlugin *example_plugin = (ExamplePlugin *)element;
free(example_plugin);
}
終了処理を記述します。ここでは malloc()
で確保した領域を free()
に渡すだけです。
7.3. コンパイル¶
上記の example_plugin.c
を、GCCでコンパイルするには以下のコマンドを実行します。
gcc -I/include_dir -Wall -O2 -fPIC -shared -L/library_dir \
-o libdc_example_plugin.so example_plugin.c -ldevice_connector_common
-I
オプションで、 device_connector.h
ファイルが含まれるディレクトリを、 -L
オプションで、 libdevice_connector_common.a
ファイルが含まれるディレクトリを指定してください。
これにより、 プラグインファイル libdc_example_plugin.so
が生成されます。
プラグインのロード方法については pluginの設定 を参照してください。