notes
ToolsFibril can

fibril_can 開発者向け内部構造 (レイヤ責務と検証分担)

パッケージ間の責務境界と依存方向。ワイヤ+IR は core 一元、runtime は ROS/C++/malloc なし、bridge はスキーマ依存生成しない

要点: 各パッケージが分離されていることは「見栄え」ではなく機能要件。ワイヤ+IR を core に集めないと bridge が独自解釈してワイヤ不一致、runtime に malloc を撒くと決定性が壊れる、bridge がスキーマ依存生成すると slave のスキーマ変更で bridge 再ビルド。

依存方向 (ビルド時 + 実行時)#

                        (build-time only)      (run-time)
                              │                     │
  ┌────────────────────┐      │                     │
  │ fibril_can_core    │◀──┐  │  ┌──────────────────┴───┐
  │ (proto + IR + 採番) │   │  │  │ fibril_can_bridge    │
  └────────────────────┘   │  │  │ (rclcpp)             │
            ▲              │  │  └──────────────────────┘
            │              │  │              │
  ┌─────────┴──────────┐   │  │              ▼
  │ fibril_can_codegen │───┘  │   ┌──────────────────────┐
  │ (YAML → C/C++)     │      │   │ fibril_can_msgs      │
  └────────────────────┘      │   │ (NodeInfo, srv)      │
            │                 │   └──────────────────────┘
            ▼                 │   ┌──────────────────────┐
  schema_gen.{h,c,hpp,cpp}    │   │ ros_babel_fish       │
  schema_blob.c               │   │ (動的 ROS 型解決)     │
  master_gen.{h,c}            │   └──────────────────────┘
            │                 │
            ▼                 ▼
  ┌────────────────────┐  ┌──────────────────────────┐
  │ fibril_can_runtime │  │ fibril_can_master_runtime│
  │ (C99 slave)        │  │ (C99 embedded master)    │
  └────────────────────┘  └──────────────────────────┘
   ament 化なし、FetchContent / submodule で FW に取り込む

  fibril_can_web (並列 TS 実装、C++ 側と独立)
  @fibril/can-transport → @fibril/can-transport-{gsusb,mock}
                        → @fibril/can-core → apps/{sniffer,master}

矢印はビルド時 or 実行時依存。bridge と runtime は CAN バス経由でワイヤプロトコルで結ばれるが矢印は描けない (実体は SocketCAN + 物理 CAN FD、正本は SPEC.md)。

レイヤごとの責務#

fibril_can_core — 共有知識の正本#

ROS / CAN 非依存の C++ ライブラリ。ワイヤ + IR 知識の単一の正本。

  • Protobuf schema (.proto) + encode/decode
  • IR (Field, Topic, Service, Param, BlockType, BlockArray, NodeSchema, SchemaDocument)
  • スカラ型表 (バイト数、C 型名、変換規則)
  • 採番ロジック (compute_numbering = blob と instance_counts から topic_index / service_index / param_index を導出する純関数)
  • packing (DLC 量子化、wire_size、frame 合計)
  • ROS 型変換対応表
  • SPEC 由来の定数 (protocol version、frame 最大長、予約 service_index)

fibril_can_codegen — YAML → C/C++ ソース#

4 責務:

  1. 読み込み (yaml_loader): YAML → IR
  2. 静的検証 (static_validator): SPEC §9.4 の検査を IR に適用
  3. IR 構築: 採番、ハッシュ計算、レポート用メタ情報 (core の純関数呼ぶ)
  4. emit: IR → 出力テキスト・バイト列 (C emitter / C++ emitter は別ファイルだが入出力は同型)

fibril_can_runtime — スレーブ C99 ランタイム#

ROS / C++ / 動的確保 いずれも非依存。ament 化なし (submodule / FetchContent)。責務:

  • SPEC §5〜§8 のスレーブ側全機能
  • 状態機械 (UNPROVISIONED → PROVISIONED → RUNNING、FAULT、T_listen、ID 衝突)
  • フレームテーブル (FRAME_DEFINE で構築、DISCOVER でクリア)
  • データプレーンホットパス (送信スケジューラ、受信展開、Topic seqlock)
  • Service (SINGLE / SEGMENT 転送)
  • Parameter 保持 + PARAM_SET 適用
  • 設定プレーンコマンドディスパッチ + ACK/NAK 生成
  • master-lost watchdog
  • HAL 境界 (CAN driver 抽象化 fcan_hal_t)

スキーマ非依存: スキーマで変わるのは「生成 const テーブル」と「生成ディスパッチャ」だけ、ランタイム本体は共通。

fibril_can_master_runtime — 組み込みマスター C99#

fibril_can_bridge のサブセット、ROS / ros_babel_fish / SocketCAN 依存なし。fcan_codegen --role=master の const テーブルを消費。PC ではない MCU 上の master 用。

fibril_can_bridge — ROS ブリッジ#

rclcpp コンポーネント。責務:

  • DISCOVER / ANNOUNCE / SCHEMA_READ でのスレーブ発見と blob 取得
  • スキーマから ROS entity 動的構築 (GenericPublisher / GenericSubscription / generic service / parameter declare)
  • ROS ↔ CAN 変換 (field 単位)
  • フレーム配置決定 (SPEC §6.3 のビンパッキング)
  • FRAME_DEFINE + START の発行、ACK 待ち、ロールバック
  • Service 呼び出し追跡、分割転送組立、timeout
  • 再起動検知 (boot_id 不一致) と自動再プロビジョン
  • ROS param 永続化 + PARAM_SET 注入
  • 名前衝突検出 (NameOwnership)
  • Field 再構成 (field_overrides + full reprovision)

スキーマ依存の生成コードを持たない、全 ROS entity は blob と実数ベクトルから runtime 組立。slave 側スキーマ変更で bridge 再ビルド不要。

fibril_can_msgs — 最小 ROS interface#

NodeInfo.msg + ReprovisionNode.srv のみ。builtin_interfaces だけ依存 (重い ROS パッケージ引き込み防止)。

fibril_can_web — TypeScript 並列実装#

C++ bridge と同じワイヤプロトコルを TS で並列実装。責務分けは C++ 側と対称:

  • @fibril/can-transport: raw CAN frame 抽象、fibril_can セマンティクス無し
  • @fibril/can-transport-{gsusb,mock}: 実装
  • @fibril/can-core: TS 側のワイヤ+IR 単一正本 (C++ 側 fibril_can_core と対、二重実装)
  • apps/{sniffer,master}: React app

typed bindings (schema.blob → TS interface + path リテラル型) 生成器は未着手。詳細は fibril_can Web マスター。

境界をまたぐ鉄則#

① ワイヤ + IR 知識は core に集める#

ハッシュの取り方、wire_size の数え方、採番表、ROS 型対応は fibril_can_core (or @fibril/can-core) 以外に書かない。bridge が独自に「実は MotorState は X バイト」と書いた瞬間、スキーマ変更で片側だけ追従してワイヤ不一致になる。

② codegen と bridge は同じ採番関数を呼ぶ#

SPEC §4.2 は「master (ANNOUNCE 受信時) と slave (fcan_init 時) が同一の表を導出」を要求。同じロジックでは足りず、同じコードを両者が呼ぶ。compute_numbering を bridge 側でコピペしない。

③ ランタイムに ROS / C++ / 動的確保を持ち込まない#

MCU 上で動くため。fcan_init 後にアロケータを呼ばない制約は性能ではなく決定性のため、断片化と非決定性を構造的に排除。

④ ブリッジはスキーマ依存コードを生成しない#

find_package(some_schema_pkg) を要求しない (slave 側スキーマ変更で bridge を再ビルドさせる罠)。ROS 型解決は ros_babel_fish が実行時に行う。

⑤ セーフティ (ウォッチドッグ等) はスキーマに持たせない#

指令途絶時のサーボオフはアプリ層の責務、フレームワークが判断しない (SPEC §1.3-6)。

検証の分担#

検査実行場所タイミング
構文 / サイズ / 採番 / 型カテゴリ整合fibril_can_codegen の static_validatorコード生成時 (ROS 不要)
field_map の path 実在fibril_can_bridgeノード接続時

ファームウェア側 CI は ROS 抜きで完結する。ROS 側改修も bridge 単独でテスト可。

開発者向けドキュメントの入口#

docs/developer-guide/ 配下:

ファイル内容
architecture-layers.md責務境界と依存方向 (本ノートの元)
core-internals.mdIR、Protobuf blob、採番、packing、ROS 型変換表の内部
codegen-internals.mdYAML → IR → emitters のフェーズ分解、C/C++ emitter のサブモジュール
runtime-internals.md状態機械、フレームテーブル、seqlock、サービス、設定プレーン
bridge-internals.mdBridgeNode / NodeAgent / TopicIoEngine / FramePlanner / SocketCAN
router-internals.mdルータの状態・学習テーブル・転送規則・self HAL ラッパ
web-internals.md@fibril/can-core の TS 実装、既知の落とし穴
spec-supplements.mdSPEC.md の節と実装対応、設計判断の理由
file-overview.mdソースツリー全体の 1 行サマリ索引

ドキュメント更新責務#

コードを変えたら同じ PR で対応 doc も更新する (ズレは不変条件違反扱い)。

変更種別必須更新先
ワイヤ / プロトコルSPEC.md (先に) → docs/06-protocol.md
公開 API03-schema / 04-runtime / 05-bridge / 08-cpp-wrapper の該当章
責務境界 / 依存方向developer-guide/architecture-layers.md
ファイル / パッケージ追加削除developer-guide/file-overview.md, 07-packages.md
内部実装の構造変更developer-guide/*-internals.md
非自明な設計判断docs/adr/ に新規 ADR

ADR を書くのは: 不変条件の増減 / レイヤ越境例外 / 外部依存追加 / 互換性破壊 / 性能・決定性のための非自明な選択。選択の理由と却下した選択肢を残す。

関連#

最終更新:

On this page