- 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::
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"