toppers / hakoniwa-core-cpp-client

5 stars 3 forks source link

Build

このリポジトリは、C/C++で開発された箱庭コア機能(hakoniwa-core-cpp) を、C言語の API として提供します。

APIのカテゴリとしては、その目的の応じて以下の3ケースあります。

  1. 箱庭アセット
  2. 箱庭コマンド
  3. 箱庭コンダクタ

スクリーンショット 2024-01-04 14 04 47

目次

箱庭アセット API

箱庭アセットのユースケース分類

箱庭アセットのユースケースは、大別して以下の3つのカテゴリがあります。

image

箱庭アセットの処理フロー

箱庭アセットのシミュレーションの処理フローは以下のとおりです。

  1. 箱庭アセットを登録する (シミュレーション向け)
  2. 箱庭アセットのシミュレーションを開始する (シミュレーション向け)
  3. 箱箱庭アセットの処理を実行する (アプリケーション固有)
  4. シミュレーション終了まで 3 を繰り返す

アプリケーション固有の処理 は、3 の処理であり、シミュレーションの必要なタイミングで、箱庭コア機能からコールバックされます。 箱庭アセット開発者は、このコールバック関数を実装することで、箱庭アセットとしてシミュレーションを実行できます。

ここで、コールバック処理としては、以下の4種類あります。

また、アプリケーション固有の処理実装では、以下の アプリケーション共通の機能 を利用できます。

スクリーンショット 2024-01-11 7 55 07

箱庭 API 仕様

箱庭アセットを登録する

ユースケースID: UC-HAKO-ASSET-SIM-1

関数名: hako_asset_register

目的:
箱庭シミュレーション環境内で使用されるアセットを登録し、コールバック関数を関連付けます。

引数:

戻り値:
成功時は 0 を返します。失敗時は非 0 のエラーコードを返します。

エラーハンドリング:

使用例:

#include <errno.h>
#include "hako_asset.h"

// コールバック関数の実装
hako_asset_callbacks_t callbacks = {
    .on_initialize = my_initialize_callback,
    .on_simulation_step = my_simulation_step_callback,
    .on_reset = my_reset_callback
};

int main() {
    const char* asset_name = "my_asset";
    const char* config_path = "/path/to/config.json";

    // コールバック関数と時間ステップを指定してアセットを登録
    int result = hako_asset_register(asset_name, config_path, &callbacks, 1000000, HAKO_ASSET_MODEL_CONTROLLER); // 1秒ごとに更新

    if (result != 0) {
        // エラーハンドリング
        printf("Error: %d\n", result);
    }

    // シミュレーションの実行など、他の処理を続ける
    return 0;
}

箱庭アセットのシミュレーションを開始する

ユースケースID: UC-HAKO-ASSET-SIM-2

関数名: hako_asset_start

目的:
登録済みの箱庭アセットのシミュレーションを開始するように依頼します。 本関数を呼び出すと、シミュレーション開始イベントが発生するまでスリープ状態になります。シミュレーション開始イベントの発生により、箱庭アセットの初期化処理が呼び出された後、以下のいずれかのコールバック処理が呼び出されます。

シミュレーション実行中にリセットイベントが発生した場合、EINTR エラーコードを返して処理を復帰します。復帰する前に、箱庭アセットのリセット処理が呼び出されます。

リセットイベントが発生後に、本関数を再度呼び出すことにより、シミュレーション開始タイミングで再実行することができます。

引数:
なし

戻り値:
成功時は 0 を返します。失敗時は非 0 のエラーコードを返します。

エラーハンドリング:

使用例:

#include "hako_asset.h"

// 他の初期化処理 ...

int main() {
    while (1) {
        int result = hako_asset_start();

        if (result == EINTR) {
            // シミュレーションリセットイベントが発生したための処理
            continue; // 必要に応じてリセット処理を行い、ループを継続
        } else {
            // その他のエラー処理
            printf("Error: %d\n", result);
            break; // 致命的なエラーの場合はループを抜ける
        }

        // 必要に応じてスリープを挿入してリアルタイム性を制御
    }
    return 0;
}

箱庭アセットの処理を実行する

ユースケースID: UC-HAKO-ASSET-APP-1

箱庭アセットを作成するユーザは、hako_asset_callbacks_t のイベントコールバック関数を実装する必要があります。

上記コールバック処理では、箱庭アセットAPIを利用することができますが、一部、利用できないものがあります。 詳細は以下の通りです。

箱庭アセットAPI on_initialize on_simulation_step on_manual_timing_control on_reset
hako_asset_register X X X X
hako_asset_start X X X X
hako_asset_pdu_read O O O O
hako_asset_pdu_write O O O O
hako_asset_simulation_time O O O O
hako_asset_usleep X X O X

コールバック関数テーブル:

typedef struct {
    int (*on_initialize)(hako_asset_context_t*);
    int (*on_simulation_step)(hako_asset_context_t*);
    int (*on_manual_timing_control)(hako_asset_context_t*);
    int (*on_reset)(hako_asset_context_t*);
} hako_asset_callbacks_t;

コールバック関数の引数について: hako_asset_context_t は、将来的な機能拡張や追加データの管理を容易にするためのプレースホルダーとして設計されています。現時点では特に情報を持っていませんが、箱庭アセットの状態や環境設定など、箱庭アセットのユースケースの拡張に伴い、必要に応じてコンテキスト設定することを想定しています。

コールバック関数の戻り値について: 現時点では、コールバック関数の戻り値は特定のエラーコードを返すことを想定していませんので、常に0を返してください。将来的なバージョンの箱庭アセットAPIでは、エラーハンドリングやその他の通知機能のためにこれらの戻り値を利用する可能性があります。成功時の 0 と失敗時の非 0 の値によって、将来的なエラーや状態の変更を通知できる柔軟性を持たせています。

使用例:

int my_initialize_callback(hako_asset_context_t* context) {
    // 初期化時の処理
}

int my_simulation_step_callback(hako_asset_context_t* context) {
    // 各シミュレーションステップ時の処理
}

int my_reset_callback(hako_asset_context_t* context) {
    // リセット時の処理
}

// 使用例
hako_asset_callbacks_t callbacks = {
    .on_initialize = my_initialize_callback,
    .on_simulation_step = my_simulation_step_callback,
    .on_manual_timing_control = NULL,
    .on_reset = my_reset_callback
};

箱庭アセットの初期化処理

ユースケースID: UC-HAKO-ASSET-APP-1a

コールバック関数名: on_initialize

目的:
このコールバック関数は、箱庭のシミュレーション開始時点で一度だけ呼び出され、箱庭アセットの初期設定や準備を行います。ここでは、リソースの割り当て、初期パラメータの設定、環境の構成など、シミュレーション開始前に必要な手順を実装してください。

引数:

戻り値:

箱庭アセットの周期イベント実行処理

ユースケースID: UC-HAKO-ASSET-APP-1b

コールバック関数名: on_simulation_step

目的:
本コールバック関数は、箱庭のシミュレーション時間ステップ毎に呼び出されます。 箱庭アセットとして、シミュレーション実行処理をこの関数内で実装してください。

引数:

戻り値:

箱庭アセットの手動タイミング制御処理

ユースケースID: UC-HAKO-ASSET-APP-1c

コールバック関数名: on_manual_timing_control

目的:
本コールバック関数は、箱庭のシミュレーションが開始されたときに一度だけ呼び出され、ユーザーは独自のタイミングで処理を制御できます。箱庭アセットとして、シミュレーション実行処理をこの関数内で実装してください。

引数:

戻り値:

箱庭アセットのリセット処理

ユースケースID: UC-HAKO-ASSET-APP-1d

コールバック関数名: on_reset

目的:
本コールバック関数は、箱庭のシミュレーションがリセットされたタイミングで呼び出されます。 箱庭アセットとして、シミュレーションリセット処理をこの関数内で実装してください。

引数:

戻り値:

箱庭PDUデータを読み込みする

ユースケースID: UC-HAKO-ASSET-APP-COMMON-1

関数名: hako_asset_pdu_read

目的:
指定されたロボット名とチャンネルIDに基づいて、箱庭PDUデータを読み込みます。

引数:

戻り値:
成功時は 0 を返します。失敗時は非 0 のエラーコードを返します。

エラーハンドリング:

使用例:

#include "hako_asset.h"

// PDU読み込みのサンプル実装
int read_pdu_data(const char* robo_name) {
    char buffer[1024]; // PDUデータを読み込むためのバッファ
    HakoPduChannelIdType channel_id = 1; // 使用するチャンネルID

    int result = hako_asset_pdu_read(robo_name, channel_id, buffer, sizeof(buffer));

    if (result != 0) {
        // エラーハンドリング
        printf("Failed to read PDU data: %d\n", result);
        return -1;
    }

    // 読み込んだデータの処理
    // ...

    return 0;
}

int main() {
    const char* robo_name = "example_robot";
    int result = read_pdu_data(robo_name);

    if (result != 0) {
        // エラーハンドリングが必要な場合の処理
    }

    // その他の処理...
    return 0;
}

箱庭PDUデータを書き込みする

ユースケースID: UC-HAKO-ASSET-APP-COMMON-2

関数名: hako_asset_pdu_write

目的:
指定されたロボット名とチャンネルIDを使用して、箱庭PDUデータを書き込みます。

引数:

戻り値:
成功時は 0 を返します。失敗時は非 0 のエラーコードを返します。

エラーハンドリング:

使用例:

#include "hako_asset.h"

// PDU書き込みのサンプル実装
int write_pdu_data(const char* robo_name, const char* data, size_t data_len) {
    HakoPduChannelIdType channel_id = 1; // 使用するチャンネルID

    int result = hako_asset_pdu_write(robo_name, channel_id, data, data_len);

    if (result != 0) {
        // エラーハンドリング
        printf("Failed to write PDU data: %d\n", result);
        return -1;
    }

    // 書き込みが成功したことを確認
    // ...

    return 0;
}

int main() {
    const char* robo_name = "example_robot";
    const char* data = "Some PDU data";
    size_t data_len = strlen(data);

    int result = write_pdu_data(robo_name, data, data_len);

    if (result != 0) {
        // エラーハンドリングが必要な場合の処理
    }

    // その他の処理...
    return 0;
}

箱庭シミュレーション時間を取得する

ユースケースID: UC-HAKO-ASSET-APP-COMMON-3

関数名: hako_asset_simulation_time

目的:
現在の箱庭シミュレーションの時間を取得します。

引数:
この関数は引数を取りません。

戻り値:
現在の箱庭シミュレーション時間を hako_time_t 型で返します。この値は、シミュレーション開始時からの経過時間をマイクロ秒単位で表します。

エラーハンドリング:
この関数は、時間取得の失敗を示すためにエラーコードを返すことはありません。ただし、異常な動作が検出された場合(例えば、シミュレーション時間が初期化されていないなど)の戻り値は不定です。

使用例:

#include "hako_asset.h"

int main() {
    hako_time_t sim_time = hako_asset_simulation_time();

    // シミュレーション時間の表示やその他の処理...
    printf("Current simulation time: %lld microseconds\n", (long long) sim_time);

    // その他の処理...
    return 0;
}

スリープする

ユースケースID: UC-HAKO-ASSET-APP-COMMON-4

関数名: hako_asset_usleep

目的:
指定された時間(マイクロ秒単位)だけスリープ状態にします。 スリープすると、sleep_time_usecだけシミュレーション時間が進んだところで復帰します。 0 が指定された場合は、最小の時間ステップ(delta_usec)で復帰します。 sleep_time_usecが、最小の時間ステップ(delta_usec)で端数が出る場合は、切り上げられます。

引数:

戻り値:
成功時は 0 を返します。失敗時は非 0 のエラーコードを返します。

エラーハンドリング:

使用例:

#include "hako_asset.h"

int main() {
    hako_time_t sleep_duration = 1000000; // 1秒間スリープする

    int result = hako_asset_usleep(sleep_duration);

    if (result != 0) {
        // エラーハンドリング
        printf("usleep failed: %d\n", result);
        // エラー処理...
    }

    // スリープ後の処理...
    return 0;
}

サンプルコード

箱庭アセットAPIを深く利用するための動作確認およびサンプルコードを用意しています。 こちらを参照ください。

インストール手順

リポジトリのクローン:

git clone --recursive https://github.com/toppers/hakoniwa-core-cpp-client.git

ビルド:

cd hakoniwa-core-cpp-client
bash build.bash

インストール:

bash install.bash

インストール時に、以下へのアクセス許可が必要になることがあります。この許可を与えると、箱庭のライブラリやコマンドがこれらのディレクトリに配置されます。

インストールが成功している場合は、以下のコマンドでチェックできます。

bash hako-setup-check.bash

成功している場合は、以下のログが出力されます。

=== HAKO ENV ===
OS=Linux , OS_TYPE=posix
HAKO_CORE_PREFIX=/usr/local
HAKO_CONFIG_PREFIX=/etc/hakoniwa
HAKO_CORE_MMAP_PREFIX=/var/lib/hakoniwa
HAKO_CONFIGNFIG_PATH=/etc/hakoniwa/cpp_core_config.json
HAKO_CORE_MMAP_PATH=/var/lib/hakoniwa/mmap

OK Directory exists: /usr/local/bin
OK Directory exists: /usr/local/bin/hakoniwa
OK Directory exists: /usr/local/lib
OK Directory exists: /usr/local/lib/hakoniwa
OK Directory exists: /etc/hakoniwa
OK Directory exists: /var/lib/hakoniwa
OK Directory exists: /var/lib/hakoniwa/mmap
OK File exists: /etc/hakoniwa/cpp_core_config.json
OK File exists: /usr/local/bin/hakoniwa/hako-cmd
OK File exists: /usr/local/lib/hakoniwa/libhakoarun.a
OK File exists: /usr/local/lib/hakoniwa/libshakoc.dylib
OK File exists: /usr/local/lib/hakoniwa/hakoc.so
OK File exists: /usr/local/lib/hakoniwa/libassets.dylib
OK File exists: /usr/local/lib/hakoniwa/libconductor.dylib
OK File exists: /usr/local/lib/hakoniwa/py
OK File exists: /usr/local/bin/hakoniwa/hako-proxy
Check complete.

環境変数の設定:

インストールが成功した後、これらのパスを環境変数に設定してください。

Ubuntuの場合:

~/.bashrc ファイルに以下の行を追加してください。

export LD_LIBRARY_PATH=/usr/local/lib/hakoniwa:$LD_LIBRARY_PATH
export PATH=/usr/local/bin/hakoniwa:$PATH

変更を反映させるために、次のコマンドを実行します。

source ~/.bashrc

macOSの場合:

使用しているシェルに応じて ~/.bash_profile または ~/.zshrc に以下の行を追加します。

export DYLD_LIBRARY_PATH=/usr/local/lib/hakoniwa:$DYLD_LIBRARY_PATH
export PATH=/usr/local/bin/hakoniwa:$PATH

変更を反映させるために、次のコマンドを実行します。

source ~/.bash_profile  # Bashの場合
source ~/.zshrc         # Zshの場合

MMAPファイルの設定

箱庭アセット間のPDU通信は、は共有メモリベースで行います。

共有メモリ方式として、以下の2方式を選択できます。

インストール直後は、デフォルトとして、SHM に設定されています。

MMAP の特徴:

インストール完了後、以下の方法で MMAP に設定変更できます。

sudo bash hako-mmap-set.bash -p /var/lib/hakoniwa/mmap

成功すると、/etc/hakoniwa/cpp_core_config.json が以下のように変更されます。

{
  "shm_type": "mmap",
  "core_mmap_path": "/var/lib/hakoniwa/mmap"
}

MMAPファイルは、core_mmap_path 配下に自動作成されます。

また、MMAPファイルを ramdisk に配置するこで、処理性能を向上させることができます。

MacOSとLinuxの場合は、hako-ramdisk.bash を利用して ramdisk を作成できます。

作成した ramdisk パスを hako-mmap-set.bash で再設定することで反映されます。

仕様:

Usage:
  hako-ramdisk.bash -c /path/to -s size   # Create a RAM disk with size in MB
  hako-ramdisk.bash -d /path/to           # Delete a RAM disk
  hako-ramdisk.bash -l                    # List created RAM disks

例:64MBの ramdisk/Volumes/hakoniwa-ramdisk に作成します。

sudo bash hako-ramdisk.bash -c /Volumes/hakoniwa-ramdisk -s 64
% sudo bash hako-ramdisk.bash -l                                
Listing RAM disks...
/dev/disk4 is mounted at /Volumes/hakoniwa-ramdisk
Operation completed.

ramdisk を削除したい場合:

sudo bash hako-ramdisk.bash -d /Volumes/hakoniwa-ramdisk 

Windows向けインストール手順

Windows 向けに箱庭コア機能をインストールする場合、以下のツールを利用します。

リポジトリのクローン:

WSL2 を利用して、クローンします。

git clone --recursive https://github.com/toppers/hakoniwa-core-cpp-client.git

ビルド:

  1. Visual Stuido を起動し、「ローカルフォルダーを開く」を選択します。
  2. hakoniwa-core-cpp-client を選択します。
  3. 「ビルド」→「すべてビルド」を選択するとビルドが始まります。

インストール:

Windowsコンソールを開き、hakoniwa-core-cpp-client に移動し、インストールコマンドを実行します。

 .\install.bat

成功すると、以下の環境変数が設定されます。

もし、設定されていない場合は、手動で設定してください。

MMAPファイルの設定

Windows版の箱庭コア機能は、MMAP通信が前提となります。

.\hako-mmap-set.bat -p Z:\\mmap

成功すると、cpp_core_config.json が以下のように変更されます。

{
    "shm_type":  "mmap",
    "core_mmap_path":  "Z:\\mmap"
}

MMAPファイルは、core_mmap_path 配下に自動作成されます。

また、MMAPファイルを ramdisk に配置するこで、処理性能を向上させることができます。

作成した ramdisk パスを hako-mmap-set.bat で再設定することで反映されます。

Windows向け(gitbash)インストール手順

Windows 向けに箱庭コア機能をインストールする場合、以下のツールを利用します。

wingetコマンドでのツールのインストール

コマンドプロンプトもしくはPowerShellからwingetコマンドを用いてツールをインストールします。

Visual Studio Build Tools:

winget install Microsoft.VisualStudio.2022.BuildTools --override "--add Microsoft.VisualStudio.Workload.VCTools --includeRecommended"

Git for Windows

winget install --id Git.Git -e --source winget

CMake:

winget install Kitware.CMake

Python

winget install Python.Python.3.12

jq

winget install jqlang.jq

クローンとビルド

Git for WindowsのGit Bashを使用します。 VSCodeを起動するとTerminalの選択で「Git Bash」を選びます

ビルド:

cd hakoniwa-core-cpp-client
bash build.bash

インストール:

bash install.bash

設定の確認:

bash ./hako-setup-check.bash 

箱庭コマンド API

TODO(コントリビュート募集中)

箱庭コンダクタ API

TODO(コントリビュート募集中)

箱庭アセットコンフィグ

箱庭アセットのコンフィグは、json形式で定義します。 コンフィグ内容は、プラントモデルのPDUデータの読み書き情報になります。

例:

{
  "robots": [
    {
      "name": "ROBOT",
      "rpc_pdu_readers": [],
      "rpc_pdu_writers": [],
      "shm_pdu_readers": [
        {
          "type": "geometry_msgs/Twist",
          "org_name": "motor",
          "name": "ROBOT_motor",
          "channel_id": 0,
          "pdu_size": 48,
          "write_cycle": 1,
          "method_type": "SHM"
        }        
      ],
      "shm_pdu_writers": []
    }
  ]
}

コンフィグ項目の詳細

robots

プラントモデル(ロボット)のコンフィグデータを管理するコンテナです。複数のロボットを定義することが可能です。

プラントモデル(ロボット)コンフィグデータ

プラントモデルのコンフィグデータの内容は以下のとおり。

RPCとSHMの違い:

pdu_readersとpdu_writers:

PDUデータのコンフィグ定義

PDUデータに関する各設定項目の定義は以下の通りです。