Autodesk Fusion を外部から Python 制御する — 常駐アドインを HTTP ブリッジにする

設計ツール自動化 2026年8月5日

外から Fusion の API は呼べない

Autodesk Fusion には Python API があります。ドキュメントも揃っていて、 スケッチを描く、押し出す、フィーチャーを操作する、といったことがコードで書けます。

ところが、外部のプロセスからは呼べません。

# 外部の python.exe で実行しても動かない
import adsk.core
app = adsk.core.Application.get()   # ここで詰む

Fusion の API は Fusion 自身のプロセス内で、しかもメインスレッドから呼ぶことが前提です。 スクリプトやアドインとして Fusion の中で走らせる分には問題ありませんが、 シェルや CI、AI エージェントから叩くという使い方ができません。

EasyEDA Pro のときは CDP で外から入り込めましたが、 Fusion は Electron ではないので同じ手が使えません。

この記事が向いていない人

構成:アドインを HTTP サーバーにする

呼べないなら、中に居座って外からの依頼を受ける係を置けばいいという発想です。

外部プロセス ──HTTP POST──> [ Fusion 内の常駐アドイン ]
                              ↓ カスタムイベント
                            メインスレッドで実行

                            結果を JSON で返す

アドインは Fusion の起動時にロードされ、127.0.0.1:9330 で待ち受けます。 POST された Python コードを受け取り、メインスレッドに渡してから実行し、結果を JSON で返します。

これで、外からはただの HTTP API になります。

.\fusion-ctl.ps1 -Expression 'app.version'
.\fusion-ctl.ps1 -File .\scripts\doc-summary.py

実装で外せない一点:メインスレッドへの受け渡し

ここが設計の中心です。

HTTP サーバーを立てると、リクエストは当然別スレッドで処理されます。 そのスレッドから Fusion の API を呼ぶと、どうなるか。

Fusion ごと落ちます。 例外でも警告でもなく、プロセスが消えます。

なのでスレッド間の受け渡しが必須になります。Fusion にはこのための仕組みがあり、 カスタムイベントを使うとメインスレッド側でハンドラを走らせられます。

# アドイン側(抜粋)
import adsk.core, json, queue, threading
from http.server import BaseHTTPRequestHandler, HTTPServer

EVENT_ID = 'ClaudeBridgeExec'
_requests = queue.Queue()      # HTTPスレッド → メインスレッド
_results  = {}                 # 実行結果の受け取り箱

class _Handler(BaseHTTPRequestHandler):
    def do_POST(self):
        body = self.rfile.read(int(self.headers['Content-Length']))
        code = json.loads(body)['code']
        done = threading.Event()
        rid = id(done)
        _requests.put((rid, code, done))

        # メインスレッドに「やることがある」と伝える
        app = adsk.core.Application.get()
        app.fireCustomEvent(EVENT_ID, '')

        done.wait(timeout=300)                 # メインスレッドの完了を待つ
        payload = _results.pop(rid, {'error': 'timeout'})
        data = json.dumps(payload).encode()
        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.send_header('Content-Length', str(len(data)))
        self.end_headers()
        self.wfile.write(data)


class _ExecHandler(adsk.core.CustomEventHandler):
    """ここだけがメインスレッド。Fusion API はこの中でしか触らない"""
    def notify(self, args):
        while not _requests.empty():
            rid, code, done = _requests.get()
            try:
                _results[rid] = {'ok': True, 'value': _run(code)}
            except Exception as e:
                _results[rid] = {'ok': False, 'error': str(e)}
            finally:
                done.set()

_run() の中では、よく使う名前をあらかじめ束縛しておくと書くのが楽になります。

def _run(code):
    app = adsk.core.Application.get()
    design = app.activeProduct if app.activeProduct else None
    env = {
        'adsk': adsk, 'app': app, 'ui': app.userInterface,
        'design': design,
        'root': design.rootComponent if design else None,
    }
    # 1行の式なら値を返し、複数行なら関数として実行して return を拾う
    try:
        return eval(code, env)
    except SyntaxError:
        exec(f"def __main():\n" + "\n".join("    " + l for l in code.splitlines()), env)
        return env['__main']()

これで、こう書けるようになります。

# 単一の式 → 暗黙の戻り値
app.version

# 複数文 → 明示的な return
x = root.bRepBodies.count
return x

ハマったこと

1. ワーカースレッドから API を呼ぶと Fusion が落ちる

前述の通りです。例外ではなく、プロセスが消えます。 実際に一度落としました。

厄介なのは、うっかりやりやすいことです。 「タイムアウト監視のためにスレッドを立てる」「進捗を別スレッドで表示する」—— どれも自然な発想ですが、その中で API に触れた瞬間に終わります

対策はシンプルで、スレッドを起こす処理を書かない。 どうしても必要なら、そのスレッドからは Fusion API に一切触れないと決めることです。

2. モーダルダイアログが開くとハングする

ui.messageBox() を呼ぶと、メインスレッドがユーザーの操作待ちで止まります。 そしてメインスレッドが止まると、こちらの実行キューも止まります。 HTTP リクエストは返らず、タイムアウトまで待つことになります。

自動化の途中でデバッグ用に messageBox を挟むと、これを踏みます。 ログは戻り値に載せるか、ファイルに書く。 ダイアログは使いません。

3. 長さの単位は cm 固定

API の引数は内部単位の cm です。mm ではありません。

# 100mm の押し出しをしたいとき
distance = adsk.core.ValueInput.createByReal(10.0)   # ← 10.0 が 100mm

Fusion の UI 上では mm で表示・入力しているので、そのつもりで書くと10倍ずれます。 角度はラジアンです。

この手のずれは、エラーにならずに間違ったモデルができるので質が悪いです。 スクリプトの入口で mm → cm の変換をかます関数を1つ作って、 生の数値を API に渡さない運用にしました。

4. サインインが必要

Fusion はクラウド前提の製品で、未サインインだと起動途中で止まり、ブラウザに認証画面が出ます。 この認証は自動化できません(するべきでもありません)。

つまり完全な無人実行には向きません。 「人がログインしている作業マシンで、手順を自動化する」用途と割り切っています。

5. アドインの更新

アドインのコードを直したとき、Fusion を再起動するのは待ち時間が長すぎます。 アドインの停止 → 再ロードを外から叩けるようにしておくと、開発が回るようになります。

.\fusion-ctl.ps1 -Reload

破壊的な操作について

Fusion はファイルを直接扱うので、EasyEDA の場合より事故の影響が大きくなります。

以下は自動実行しない、と決めています。

一方、app.documents.add(...) での新規作成は既存ドキュメントに触れないので、比較的安全です。 検証はこちらで回しています。

この方法の位置づけ

利点

欠点

まとめ

GUI アプリを外から動かす方法は、アプリの作りによって変わります。 EasyEDA Pro は Electron なので CDP で入れましたが、 Fusion のように中からしか触れない APIを持つアプリでは、 「中に常駐させて外から呼ぶ」という形が有効でした。