IU Tips

Tapirにコマンドを追加する方法【Archicad】

2025.11.19

こんにちは IU BIM Studioの原田です。
今回は前回紹介したTapirに独自のコマンドを追加する方法を説明します。

Tapirについては前回の記事も参照ください

コマンド作成の基本

Tapirでコマンドを追加するにはArchicad アドオンを作成する必要があります。
アドオンを1から作成するわけではなく、Tapirのコードに追加したいコマンドのコードを記述します。

Tapirはオープンソースになっており、ソースコードが公開されています。
MITライセンスになっているため、制約は緩い方かと思います。
MITライセンスはざっくりいうと「コードを利用していいけど、トラブルがあっても責任取りませんよ」というライセンスかと思います。詳しくは各自で調べていただければと思います。

プログラム言語はC++になり、少しハードルが高いですが、TapirをカスタマイズできるようになるとPythonでできることが増えます。

今回は例として、TapirからArchicadのバージョンを取得する方法を説明します

リポジトリのクローン

まずはTapirのリポジトリをクローンします

git clone https://github.com/ENZYME-APD/tapir-archicad-automation.git

Archicadアドオンに関するコードはクローンした中のarchicad-addonフォルダの中にあります。

Visual Studioで編集するために、Visual Studioのプロジェクトを作成する必要がありますが、今回はVisual Studio周りの設定方法は省略します。

では、コマンドを追加していきましょう。

コマンドの追加

既存のコードと分けるため、ヘッダファイルとソースファイルを新規作成します。
ここではMyCommands.hppとMyCommands.cppとします。

ここからコマンド追加の作業に入りますが、1から作成するよりも似たような内容のコマンドをコピー&ペーストしたほうが早いでしょう。

今回はArchicadのバージョンを取得したいので、そこまで複雑なものではありません。ApplicationCommandsのGetAddOnVersionCommnadなどが良いでしょう。まずはヘッダファイルからです。

#pragma once

#include "CommandBase.hpp"

class GetAddOnVersionCommand : public CommandBase
{
public:
    GetAddOnVersionCommand ();
    virtual GS::String GetName () const override;
    virtual GS::Optional<GS::UniString> GetResponseSchema () const override;
    virtual GS::ObjectState Execute (const GS::ObjectState& parameters, GS::ProcessControl& processControl) const override;
};

Tapirでは作成したいコマンドごとにクラスを作成します。
クラスはCommandBaseクラスを継承して作成します。

クラス内のメソッドを見ていきましょう。内容としては下記のような内容のようです。

  • GetAddOnVersionCommand:コンストラクタ
  • GetName: Tapirで呼び出す際のコマンド名を決める
  • GetResponseSchema: 戻り値のJSONフォーマットを決める
  • Execute: 処理の内容を決める

入力データを送信する必要があるコマンドの場合はGetInputParametersSchemaというメソッドで入力フォーマットを指定する必要がありますが、今回は必要ありません。

ではコピーした先のクラス名を変えます。

#pragma once

#include "CommandBase.hpp"

class GetArchicadVersionCommand : public CommandBase
{
public:
    GetArchicadVersionCommand ();
    virtual GS::String GetName () const override;
    virtual GS::Optional<GS::UniString> GetResponseSchema () const override;
    virtual GS::ObjectState Execute (const GS::ObjectState& parameters, GS::ProcessControl& processControl) const override;
};

クラス名を新しいものに変え、それに合わせてコンストラクタの名前も変えます。
ヘッダファイルの変更は以上です。

では、ソースファイルを変えていきます。こちらも同じ部分からコピーします。
MyCommands.hppをインクルードしておきます。

#include "MyCommands.hpp"

GetAddOnVersionCommand::GetAddOnVersionCommand () :
    CommandBase (CommonSchema::NotUsed)
{
}

GS::String GetAddOnVersionCommand::GetName () const
{
    return "GetAddOnVersion";
}

GS::Optional<GS::UniString> GetAddOnVersionCommand::GetResponseSchema () const
{
    return R"({
        "type": "object",
        "properties": {
            "version": {
                "type": "string",
                "description": "Version number in the form of \"1.1.1\".",
                "minLength": 1
            }
        },
        "additionalProperties": false,
        "required": [
            "version"
        ]
    })";
}

GS::ObjectState GetAddOnVersionCommand::Execute (const GS::ObjectState& /*parameters*/, GS::ProcessControl& /*processControl*/) const
{
    return GS::ObjectState ("version", ADDON_VERSION);
}

では、まずクラス名を修正しましょう。
コンストラクタの名前も変えておきます。

GetArchicadVersionCommand::GetArchicadVersionCommand () :
    CommandBase (CommonSchema::NotUsed)
{
}

GS::String GetArchicadVersionCommand::GetName () const
{
    return "GetAddOnVersion";
}

GS::Optional<GS::UniString> GetArchicadVersionCommand::GetResponseSchema () const
{
    return R"({
        "type": "object",
        "properties": {
            "version": {
                "type": "string",
                "description": "Version number in the form of \"1.1.1\".",
                "minLength": 1
            }
        },
        "additionalProperties": false,
        "required": [
            "version"
        ]
    })";
}

GS::ObjectState GetArchicadVersionCommand::Execute (const GS::ObjectState& /*parameters*/, GS::ProcessControl& /*processControl*/) const
{
    return GS::ObjectState ("version", ADDON_VERSION);
}

次に、GetNameメソッドを修正します。

GS::String GetArchicadVersionCommand::GetName () const
{
    return "GetArchicadVersion";
}

これは名前を変えるだけです。
クラス名からCommandを除いたものにするのがルールっぽいのでそれに倣います。
これがTapirから呼び出すときのコマンド名になっていると思われます。

次にGetResponseSchemaメソッドを修正します。

GS::Optional<GS::UniString> GetArchicadVersionCommand::GetResponseSchema () const
{
    return R"({
        "type": "object",
        "properties": {
            "version": {
                "type": "string",
                "description": "Version number in the form of \"28.0.0\".",
                "minLength": 1
            }
        },
        "additionalProperties": false,
        "required": [
            "version"
        ]
    })";
}

今回は同じようなコマンドから作ったので特に修正はありません。直すとすればdescriptionのところでしょうか。サンプルデータをArchicadのバージョンに直しておきましょう。

このスキーマに従ってコマンドの戻り値を返します。今回は簡単なのでよかったですが、複雑になると結構大変ですね。
一応スキーマの再利用ができるようになっているらしく、その内容はCommonSchemaDefinitions.jsonに記述されています。
こちらに追記することで独自のデータ形式を追加することも可能です。

次にExecuteメソッドを修正します。

#include "ACAPI/LicenseInfo/LicenseInfoManager.hpp"

GS::ObjectState GetArchicadVersionCommand::Execute (const GS::ObjectState& /*parameters*/, GS::ProcessControl& /*processControl*/) const
{
    ACAPI::Result<ACAPI::LicenseInfo::LicenseInfoManager> licenseInfo = ACAPI::LicenseInfo::GetLicenseInfoManager ();
    GS::UniString version = licenseInfo.Unwrap ().GetProductVersionInfo ().GetVersionString ();
    return GS::ObjectState ("version", version);
}

このメソッドが出力する情報を作成する部分です。この出力がResponseSchemaと一致している必要があります。今回はArchicadのバージョンを返すだけなので特に難しいところはありません。

最後にコマンドを登録します。
コマンドを登録しておかないと使うことができません。
コマンドの登録はAddOnMain.cpp内のInitialize関数の中で行います。

まずはAddOnMain.cppにMyCommands.hppをインクルードします。

次にInitialize関数内でコマンドを登録します。
基本的には他のコマンドに倣っておけば大丈夫だとおもいます。

GSErrCode Initialize (void){
    // 中略

    { // Custom Commands
        CommandGroup customCommands ("My Commands");
        err |= RegisterCommand<GetArchicadVersionCommand> (
            customCommands, "1.0.0",
            "Get Archciad Version"
        );

        AddCommandGroup (customCommands);
    }
}

   

コマンドの使用

追加したコマンドが使えるかを確認します。
プロジェクトを開き、適当な画面で待機します。

次に呼び出し側のコードをpythonで作成します。詳しくは前回の内容をご確認ください。

from archicad import ACConnection

conn: ACConnection = ACConnection.connect()
acc = conn.commands
act = conn.types

def run_tapir_command(command: str, param: dict= None) -> dict:
    addOnCommandId = act.AddOnCommandId("TapirCommand", command)
    result = acc.ExecuteAddOnCommand(addOnCommandId, param)

    return result

result = run_tapir_command("GetArchicadVersion")
print(result)

# {'version': '28.0.0'}

Archicadのバージョンがjson形式で受け取れました。
マイナーバージョンが実際のものと合っていないのは、API側のバグでしょうかね…?
今回はPython側と通信できたことで良しとしましょう。

まとめ

以上で、Tapirにコマンドを追加することができました。

Tapirにコマンドを追加できるとPythonでコードを書けばすむようになるので、ケースによってはとても楽になります。ただ、全てのコマンドを追加するのは大変なので、どういう目的でどのようなデータを使うのか考えることが大事ですね。

今回は以上です。
最後までお読みいただきありがとうございました。
最後にコードを載せおきます。

・MyCommands.hpp

#pragma once

#include "CommandBase.hpp"

class GetArchicadVersionCommand : public CommandBase
{
public:
    GetArchicadVersionCommand ();
    virtual GS::String GetName () const override;
    virtual GS::Optional<GS::UniString> GetResponseSchema () const override;
    virtual GS::ObjectState Execute (const GS::ObjectState& parameters, GS::ProcessControl& processControl) const override;
};

・MyCommands.cpp

#include "MyCommands.hpp"
#include "ACAPI/LicenseInfo/LicenseInfoManager.hpp"

GetArchicadVersionCommand::GetArchicadVersionCommand () :
    CommandBase (CommonSchema::NotUsed)
{}

GS::String GetArchicadVersionCommand::GetName () const
{
    return "GetArchicadVersion";
}

GS::Optional<GS::UniString> GetArchicadVersionCommand::GetResponseSchema () const
{
    return R"({
        "type": "object",
        "properties": {
            "version": {
                "type": "string",
                "description": "Version number in the form of \"28.0.0\".",
                "minLength": 1
            }
        },
        "additionalProperties": false,
        "required": [
            "version"
        ]
    })";
}

GS::ObjectState GetArchicadVersionCommand::Execute (const GS::ObjectState& /*parameters*/, GS::ProcessControl& /*processControl*/) const
{
    ACAPI::Result<ACAPI::LicenseInfo::LicenseInfoManager> licenseInfo = ACAPI::LicenseInfo::GetLicenseInfoManager ();
    GS::UniString version = licenseInfo.Unwrap ().GetProductVersionInfo ().GetVersionString ();
    return GS::ObjectState ("version", version);
}

・AddOnMain.cpp

// 省略
#include "MyCommands.hpp"

// 省略

GSErrCode Initialize (void){
    // 省略

    { // Custom Commands
        CommandGroup customCommands ("My Commands");
        err |= RegisterCommand<GetArchicadVersionCommand> (
            customCommands, "1.0.0",
            "Get Archciad Version"
        );

        AddCommandGroup (customCommands);
    }
}

// 省略

・Python(呼び出し側)

from archicad import ACConnection

conn: ACConnection = ACConnection.connect()
acc = conn.commands
act = conn.types


def run_tapir_command(command: str, param: dict = None) -> dict:
    addOnCommandId = act.AddOnCommandId("TapirCommand", command)
    result = acc.ExecuteAddOnCommand(addOnCommandId, param)

    return result


result = run_tapir_command("GetArchicadVersion")
print(result)