実装上の注意点

Thing-IF SDK を使って Thing 上で実装する際には、以下の点にご注意ください。

タスク

Thing-IF SDK を用いた実装では、複数のタスク(スレッド)を利用します。

一例として、Thing の実装ではいくつかのコールバック関数を用意しますが、これらは SDK 内部で生成された実行タスクから呼び出されます。また、初期化処理等、Thing 上のプログラム側から SDK を呼び出すタスクもあります。

以下の表にタスクをまとめます。

処理 実行タスク 説明
初期化、
初期登録
任意 初期化処理は Thing 上のプログラムの任意のタスクから実行できます。初期化処理が完了すると、これ以降このタスクより Thing-IF SDK の処理を実行する必要はありません。待機状態とするか、Thing Interaction Framework 以外の処理を割り当てることができます。
アクションハンドラーの実行 コマンド受信タスク アクションハンドラーは、SDK が作成したコマンド受信用タスクから呼び出されます。
ステートハンドラーの実行 ステート更新タスク、コマンド受信タスク ステートハンドラーは、以下のように、アップロードのタイミングによって実行されるタスクが異なります。
  • 定期的なステート更新が行われる場合、SDK が作成したステート更新用タスクから呼び出されます。
  • アクション実行後にステート更新が行われる場合、アクションハンドラーを呼び出したのと同じタスク(コマンド受信タスク)から呼び出されます。
これら 2 つが同時に実行される可能性もあるため、再入可能にして同時に動くようにするか、同期処理によってシリアライズする必要があります。
JSON 解析用リソースの確保 解析呼び出し元タスク JSON のトークン解析に必要なリソースを確保するために Thing 上のプログラムに用意したコールバック関数は、JSON の解析を行うすべてのタスクからそれぞれ呼び出されます。同時に実行される可能性もあるため、再入可能にして同時に動くようにするか、同期処理によってシリアライズする必要があります。

その他、以下の点にご注意ください。

  • この表のタスク以外にも、Thing SDK Embedded 内で MQTT の PINGREQ を定期的に送信するタスクを起動します。このタスクはアイドル状態で MQTT コネクションが切断されないための処理で、SDK 外部の処理には影響がありません。

  • Thing 上のすべての API はブロッキング API として動作します。API を呼び出すと、処理が完了するまでの間、呼び出したタスクの動作は停止します。

データ型

Thing-IF SDK 内では、様々なデータ型が使用されています。本ガイドでは、それらのデータ型をサンプルコードとともに説明していますが、共通で使用されるものをここに示します。

kii_bool_t

以下の kii_bool_t は SDK 全体で共通して使用される、ブール型のデータ型です。

typedef enum kii_bool_t {
    KII_FALSE = 0,
    KII_TRUE
} kii_bool_t;

このデータ型は kii_thing_if.h をインクルードすれば自動的に組み込まれます。実際の宣言は、kii_thing_if.h が内部で使用する kii_core.h で行われています。

なお、ブール型を返す API とエラーコードを返す API で実装が混乱しないようにご注意ください。ほとんどの API は kii_bool_t を返しますが、一部エラーコードを返す API も存在します。以下のように、戻り値の比較を省略するスタイルで実装した場合、呼び出し元のコードからは成功と失敗の見分けが付きにくくなります。

/* Initialize the Thing-IF SDK. */
if (init_kii_thing_if(...)) {
  /* The function succeeded and returned kii_bool_t. */
}

/* Parse a JSON string. */
if (kii_json_read_object(...)) {
  /* The function failed and returned kii_json_parse_result_t. */
}

文字列の扱い

Thing-IF SDK の C ライブラリで扱う文字列は、特に記載がないものを除いて UTF-8 でエンコードされた NULL ターミネートされている文字列となります。

通常は ASCII 範囲内の文字を扱いますが、たとえばコマンドのパラメータとして ASCII 文字のコード範囲外の文字列が指定された場合などは、UTF-8 の多バイト文字を扱う必要があります。

メモリ管理

Thing-IF SDK では、特定の用途に使用する専用のメモリ領域をユーザープログラムから受け取ります。これ以外はヒープ領域を使用せずに処理します。

専用領域を使った処理

SDK は初期化の際に以下の目的で使用する専用のメモリ領域を取得します。初期化 API では、ユーザープログラムが各メモリ領域を確保し、SDK を初期化します。

  • コマンドハンドラーで利用する領域
  • MQTT の受信処理で利用する領域
  • ステート更新で利用する領域

メモリの指定方法の詳細は、初期登録 のサンプルコードで示します。

一般の API の処理

専用領域を利用する API 以外では、メモリ領域のライフサイクルに注意が必要です。SDK ではヒープ領域を使用せずに処理を行うため、API の結果を利用する期間全体に渡って、引数に指定したメモリ領域を保持し続ける必要があります。

以下は Thing-IF SDK を利用するプログラムの擬似コードです。このような実装では、存在しないメモリを参照することになり、後続の処理で予期しない動作を招く可能性があります。

kii_bool_t myInitialize(kii_thing_if_t* kii_thing_if, ...) {
  /* Assume that the lengths of the AppID, AppKey and site have been checked. */
  char appID[100], charAppKey[100], site[10];
  strcpy(appID, config_getAppID());
  strcpy(charAppKey, config_getAppKey());
  strcpy(site, config_getSite());

  /* The program might crash when the SDK uses kii_thing_if */
  return init_kii_thing_if_with_onboarded_thing(kii_thing_if, appID, appKey, site, ...);
}

この例では、init_kii_thing_if_with_onboarded_thing の引数として、アプリケーションの AppID と、Site を指定し、kii_thing_if 構造体を初期化しています。AppKey は任意の値を指定します。myInitialize の呼び出し元では、初期化した kii_thing_if を利用する想定です。

一方、API の内部では、以下のような実装を行っています。

typedef struct kii_thing_if_t {
  char* appID;
  char* appKey;
  char* site;
} kii_thing_if_t;

kii_bool_t init_kii_thing_if_with_onboarded_thing(kii_thing_if* thingIf, char* appID, char* appKey, char* site, ...) {
  thingIf->appID = appID;
  thingIf->appKey = appKey;
  thingIf->site = site;
  return KII_TRUE;
}

呼び出し元のコード myInitialize では、スタック上に文字列用のバッファを確保して API を呼び出しています。API 内部では、その値をヒープ領域にコピーせず、呼び出し元の領域へのポインタをそのまま使って kii_thing_if を初期化しています。呼び出し元で myInitialize を抜けるとスタック上のバッファは破棄されるため、最終的に参照先が無効な kii_thing_if を使用することになります。これは非常に見つけにくいバグにつながります。

このような問題は、初期化だけでなく、Thing-IF のすべての API で発生する可能性があります。API にメモリ領域を渡したあとは、API の結果を利用する期間全体において、渡した領域が生存し続けるように実装してください。

ビルド設定用のマクロ

Thing-IF SDK ではマクロにより、ビルド条件をカスタマイズできる箇所がいくつかあります。これらは基本的に変更する必要がありません。SDK の組み込み時にソースの参照が必要になった際には、以下の情報を参考にしてください。

ビルド設定用マクロを以下にまとめます。なお、下記の「デフォルト」は、リファレンス実装での初期設定を表します。

シンボル 利用モジュール デフォルト 説明
DEBUG KiiThingSDK-Embedded 未定義 デバッグ情報をログ出力する際に定義します。実行された位置のソースコードのファイル名と行番号、REST API のリクエストとレスポンスなどが出力されます。ログの出力先は、リファレンス実装で logger_cb_impl として実装されている関数です。
KII_JSON_FIXED_
TOKEN_NUM
kii_json, KiiThingSDK-Embedded, thing-if-ThingSDK 128 JSON の解析に使用するバッファサイズを指定します。値は確保するトークンの数を表します。詳細は こちら をご覧ください。アクションのパラメータが複雑になる場合、設定の変更を検討してください。FLEXIBLE_JSON_TOKEN の指定とは排他です。
FLEXIBLE_JSON_
TOKEN
kii_json, KiiThingSDK-Embedded, thing-if-ThingSDK 未定義 動的に確保したメモリを使って JSON の解析を行う際にマクロを定義します。マクロが定義されていると、kii_json はメモリが必要になったとき kii_json_t.resource_cb で定義されたコールバック関数を呼び出します。コールバック関数でメモリを確保して返すと、kii_json はその領域を使って JSON を解析します。実装方法は、resource_cb のコメントを参照してください。KII_JSON_FIXED_TOKEN_NUM の指定とは排他です。
KII_PUSH_KEEP_ALIVE_
INTERVAL_SECONDS
KiiThingSDK-Embedded, thing-if-ThingSDK 300 MQTT のキープアライブタイマーの設定値を秒単位で指定します。CONNECT コマンドで指定するキープアライブタイマーの設定値と、クライアントから PINGREQ コマンドを送信する間隔の両方で使用されます。60 以上の値を指定することを推奨します。
KII_SOCKET_MAX_
BUFF_SIZE
KiiThingSDK-Embedded 未定義 Thing Interaction Framework とのソケット通信で使用する送受信 1 回分のバッファサイズをバイト単位で指定します。HTTP メッセージをこのサイズに分割して送受信します。未定義の場合は、SDK 内部で適切な値(256)が使用されます。
KII_THING_IF_NOASSERT thing-if SDK 未定義 システムのライブラリに assert がないシステムではこのマクロを定義します。リファレンス実装では環境に合わせて定義されるため、未定義のまま使用します。
KII_JSON_NOASSERT kii_json 未定義 システムのライブラリに assert がないシステムではこのマクロを定義します。リファレンス実装では環境に合わせて定義されるため、未定義のまま使用します。
JSMN_PARENT_LINKS kii_json, KiiThingSDK-Embedded 未定義 JSON の解析用に内部で使用しているライブラリの設定です。Thing Interaction Framework では未定義のまま使用します。
JSMN_STRICT kii_json, KiiThingSDK-Embedded 未定義 JSON の解析用に内部で使用しているライブラリの設定です。Thing Interaction Framework では未定義のまま使用します。
KII_USE_CUSTOM_
HTTP_CLIENT
KiiThingSDK-Embedded 未定義 将来の拡張用です。HTTP / HTTPS クライアントを別の実装に差し替えたい場合に使用する仕組みです。未定義のまま使用します。