Skip to content
  • Date Created: 2025-12-09
  • Last Modified: 2025-12-09

Progress Log: CommandQueue & Main Loop Refactor Design Analysis

Task Description

v1.13.1 ではプロトコルシステム分離が完了したが、dev:build のリンカーエラー(CommandQueue static メソッド未実装)を機に、main.cpp のループ構造と新旧システムの統合設計を根本から見直す。

目標: - prod:build(legacy)の後方互換性を保証しながら - dev:build(CommandQueue)への移行をクリーンに実現する

分析対象: 1. CommandQueue の static メソッド設計 2. main.cpp loop() と setup() の構造 3. detection_process() のイベント処理フロー 4. ENABLE_DEVICE_RESPONSE フラグの分岐点

Outcome

発見1: CommandQueue の実装不完全性

問題: command_queue.cpp に以下の static メソッドが未実装

static bool receive_line(char*, size_t);  // 宣言: 行378
static command_t parse(const char*);       // 宣言: 行400
static command_response_t dispatch(const command_t&);  // 宣言: 行432

ただし、実装体は存在する(行140, 208, 266):

static bool receive_line(char* buffer, ...) { ... }  // 行140
static command_t parse(const char* line) { ... }      // 行208
static command_response_t dispatch(const command_t& cmd) { ... }  // 行266

根本原因: - .h では private: static メソッド(クラスメソッド)として宣言 - .cpp では static local 関数として定義 - C++ のスコープ: CommandQueue::receive_line() vs ファイルスコープ receive_line()

修正方法: command_queue.cpp の3つの関数を CommandQueue:: スコープで再定義

bool CommandQueue::receive_line(char* buffer, size_t buffer_size) { ... }
command_t CommandQueue::parse(const char* line) { ... }
command_response_t CommandQueue::dispatch(const command_t& cmd) { ... }


発見2: main.cpp loop の構造的不対称性

現在のループ構造 (line 312-363):

void loop() {
  // ポイント A: プロトコル処理(条件付き分岐)
  #if ENABLE_DEVICE_RESPONSE
    CommandQueue::receive();    // 新: 2つの呼び出し
    CommandQueue::execute();
  #else
    process_protocol_commands(); // 旧: 1つの統一インターフェース
  #endif

  // ポイント B: 検出バッファ送信(無条件)
  #if ENABLE_QUEUE
    detection_buffer_send();
  #endif

  // ポイント C: WiFi 状態更新
  #if ENABLE_WIFI
    wifi_update();
  #endif

  // ポイント D: GNSS 状態更新
  #if ENABLE_GNSS
    gnss_update();
  #endif

  // ポイント E: 検出処理(無条件、内部で ENABLE_DEVICE_RESPONSE 分岐)
  detection_process();
}

問題点:

視点 旧システム(legacy) 新システム(CommandQueue)
呼び出し process_protocol_commands() CommandQueue::receive() + execute()
インターフェース 統一(単一関数) 分散(2つのメソッド)
イベント処理 event_t 構造体 device_response_t へ変換
バッファリング text/binary_command_queue FreeRTOS static queue

発見3: detection_process() の混合責務

現在 (line 164-227):

void detection_process(void) {
  // 検出読み込み
  cosmic_detection_t detection = cosmic_detector_read();

  if (detection.detected) {
    // センサーデータ読み込み
    float temp = bme280_read_temperature();
    float pres = bme280_read_pressure();
    float humid = bme280_read_humidity();

    // タイムスタンプ計算
    #if ENABLE_TIMESTAMP
      uint64_t timedelta_us = current_micros - last_event_time_us;
    #endif

    // イベント構造体作成 (常に作成)
    event_t data = { ... };

    // イベント送信 (条件付き)
    #if ENABLE_QUEUE
      detection_buffer_queue(&data);  // キューイング
    #else
      #if ENABLE_DEVICE_RESPONSE
        JsonDocument doc = DeviceResponseBuilder::from_event(&data);  // 変換
        device_response_send(&doc);
      #else
        send_event(&data);  // 直接送信
      #endif
    #endif
  }
}

問題: 1. event_t 構造体は常にメモリに build される(ENABLE_DEVICE_RESPONSE=1 でも) 2. DeviceResponseBuilder::from_event() で冗長な変換が行われている可能性 3. 新旧フォーマットが混在し、メモリ効率が低い


発見4: setup() の初期化順序の曖昧性

現在 (line 229-309):

void setup(void) {
  serial_init(115200);           // 行231
  config_init();                 // 行234
  adc_init(ADC_INPUT_PIN);       // 行239
  bme280_init(0x76);             // 行244
  dac_init();                    // 行252
  cosmic_detector_init();        // 行257

  #if ENABLE_RTC
    rtc_init();                  // 行263
  #endif

  #if ENABLE_GNSS
    gnss_init();                 // 行271
  #endif

  attachInterrupt(swich_pin, onPushed, FALLING);  // 行274
  delay(3000);

  #if ENABLE_QUEUE
    detection_buffer_init();     // 行283 - 検出バッファ
  #endif

  #if ENABLE_WIFI
    wifi_init();                 // 行291 - WiFi
    delay(500);
    web_server_init();           // 行294
  #endif

  #if ENABLE_DEVICE_RESPONSE     // ← 行300: プロトコルシステム分散
    CommandQueue::init();
  #endif

  #if ENABLE_TIMESTAMP
    last_event_time_us = micros();  // 行307 - タイムスタンプ初期化
  #endif
}

問題: - CommandQueue 初期化がシステム初期化の「途中」に埋まっている - legacy プロトコル(text/binary)初期化は serial_init() 内で行われるが、CommandQueue 初期化は分離している - 初期化順序の依存が明確でない


Learnings

1️⃣ 設計上の判断ポイント: 3つのアプローチ

アプローチA: 完全分離(状態の最小化)

// 新旧を完全に分ける(同じバイナリに両方入らない)
#if ENABLE_DEVICE_RESPONSE
  // CommandQueue専用パス
  void loop() { CommandQueue::receive(); CommandQueue::execute(); }
#else
  // legacy専用パス
  void loop() { process_protocol_commands(); }
#endif
メリット: シンプル、メモリ効率良好 デメリット: テスト困難(両パスを同一バイナリで検証不可)

アプローチB: 統一インターフェース(抽象化)

// 両方を支える統一インターフェース作成
void process_commands(void) {
#if ENABLE_DEVICE_RESPONSE
  CommandQueue::receive();
  CommandQueue::execute();
#else
  process_protocol_commands();
#endif
}
メリット: 呼び出し側を単純化、保守性向上 デメリット: 追加関数が必要

アプローチC: 段階的マイグレーション(既存の init パターン踏襲)

// 既存の ENABLE_QUEUE, ENABLE_WIFI と同じパターン
#if ENABLE_DEVICE_RESPONSE
  CommandQueue::receive();
  CommandQueue::execute();
#else
  process_protocol_commands();
#endif
メリット: 既存コード体系と一貫性 デメリット: 分岐の複雑性が増す

2️⃣ イベント処理フロー: 新旧の非対称性

旧システム (ENABLE_DEVICE_RESPONSE=0):

sensor read → event_t build → send_event()

新システム (ENABLE_DEVICE_RESPONSE=1):

sensor read → event_t build → DeviceResponseBuilder::from_event() → device_response_send()

根本問題: event_t を中間フォーマットとして使用し続けている

改善案:

// 新システムでは event_t を回避
sensor read  device_response_t build  device_response_send()

Next Steps

Phase 1: CommandQueue static メソッド実装(必須)

修正: src/command_queue.cpp
- receive_line(): static  CommandQueue::
- parse(): static  CommandQueue::
- dispatch(): static  CommandQueue::
→ dev:build リンク成功を目指す

Phase 2: loop() インターフェース統一(改善)

修正: include/serial_protocol.h (新ファイルか既存か検討)
追加: void process_commands(void)
修正: src/main.cpp loop()
  - CommandQueue::receive/execute  process_commands()
  - process_protocol_commands()  process_commands()

Phase 3: detection_process() フォーマット最適化(最適化)

修正: src/main.cpp detection_process()
- ENABLE_DEVICE_RESPONSE == 1 では event_t  skip
- device_response_t を直接 build
- メモリ効率と可読性向上

Phase 4: setup() の初期化セクション整理(保守性)

修正: src/main.cpp setup()
コメント整理:
- "Hardware Initialization"
- "Protocol System Initialization" (serial_init() + CommandQueue::init() or text/binary_init())
- "Detection System Initialization"
- "Timing System Initialization"