Issue #66/#68で実装したPC Webシミュレータを、経緯を知らない開発者やAIが安全に調査・変更するための
入口。利用手順はnmea-simulation.md、挙動の正本は
p6-pc-simulator-spec.mdを先に参照する。本書は仕様を上書きせず、
実装の所在、状態所有、変更時の確認範囲を説明する。
Browser (MapLibre + TypeScript)
未送信draft / 編集履歴 / このbrowserが送った経路形状
│ HTTP 127.0.0.1:47651
▼
pc-tool JVM HTTP server
入力検証 / TCP操作の直列化 / classpath地図資産配信
│ TCP 127.0.0.1:47650
▼
adb forward tcp:47650 tcp:47650
▼
Drogger Tablet debug SimulatorTcpService
RegisteredRouteEngine / SimulationController / SimulatedNmeaLink
│ 疑似GGA・RMC・GST
▼
BluetoothNmeaController → NmeaParser → FixRepository → 製品UI・.dtrk記録
PCは製品状態を直接書き換えない。debug TCPで疑似車両を操作し、その後は実Bluetooth受信と共通の
製品経路を通す。releaseにはdebug Service・疑似NMEA実装を含めない。
| 状態 | 正本/寿命 | 主な実装 |
|---|---|---|
| 未送信経路、選択ツール、undo/redo | Browserメモリ。reloadで消える | pc-tool/src/main/typescript/simulator-ui/src/main.ts、draftRoute*.ts、draftHistory.ts |
| このbrowserが送った灰色破線 | Browserメモリ。reloadで形状は消える | submittedRoute.ts |
| 登録経路、position anchor、進捗、RunState | Tabletアプリプロセス。再起動で消える | simulator-protocol/RegisteredRouteEngine.kt |
| 速度、fix quality、stale要求、通信断時刻 | Tabletアプリプロセス | app/src/debug/.../SimulationController.kt |
| 送出ticker、stale heartbeat、疑似通信断 | 現在のdebug NMEA link | SimulatedNmeaLink.kt、SyntheticNmeaGenerator.kt |
| PC表示用endpoint/RunState/quality/stale | Tabletのquery_run_stateをpollして復元 |
SimulatorTcpRequestHandler.kt → /api/status → main.ts |
| ピンク実績軌跡 | 製品側FixRepository以降の記録 |
製品コードと.dtrk。シミュレータ経路とは別 |
この表に無い状態を新設するときは、reload、PC server再起動、Tabletプロセス再起動、TCP再接続のどこまで
保持するかを先に決める。Browserの推測よりTablet statusを優先する。
apps/android-tablet/pc-tool/src/main/resources/simulator-ui/index.html: UI骨格・説明文。.../resources/simulator-ui/static/app.css: UI/markerの見た目。[hidden]要素には既存の.field-row[hidden]と同様、author CSSに負けない規則が必要。.../typescript/simulator-ui/src/main.ts: DOM、MapLibre event、HTTP、pollingを結ぶentry point。draftRoute.ts/draftRouteEditor.ts/draftRouteWaypoints.ts: 経路モデル・編集・表示/送信点列。routeStartAdoption.ts/routeStartFlow.ts/routeSubmission.ts: 登録済み地点の自動採用と送信開始flow。runControl.ts/positioning.ts: 状態別の表示・操作許可を返す純粋関数。registeredEndpoint.ts/submittedRoute.ts: Tablet地点とブラウザ内送信形状。両者をドラフトと混同しない。test/*.test.ts: DOMから分離した規則のnode:test。pc-tool/.../simulatorui/SimulatorUiCommand.kt: Web server起動、TCP host/port、browser起動。SimulatorUiHttpServer.kt: static/PMTiles Range/API route、HTTP status mapping。SimulatorStatusService.kt: TCP client操作をlockで直列化。*Models.kt: HTTP JSON入力検証とstatus response。pctool/simulator/SimulatorCommand.kt: 自動化・切り分け用CLI。app/src/debug/.../simulation/tcp/SimulatorTcpService.kt、SimulatorTcpServer.kt、SimulatorTcpRequestHandler.kt: loopback単一client Serviceとrequest dispatch。app/src/debug/.../simulation/SimulationController.kt、SimulatedNmeaLink.kt: 設定、ticker、stale、通信断。simulator-protocol/src/main/...: TCP DTO/codec、route engine、ドラフトKotlin原型。app/src/debug/AndroidManifest.xmlとservice/SimulatorTcpServiceLifecycle.kt: debug起動。app/src/release/.../SimulatorTcpServiceLifecycle.kt: release no-op境界。地図原本はtools/map/staticのoffline-basemap.pmtiles、fude-overlay.geojson、vendor JS/CSS。
pc-tool/build.gradle.ktsのsyncSimulatorUiMapAssetsが必要分だけclasspath resourceへ同期し、
syncSimulatorUiTsがTypeScript出力を同梱する。実行時にrepository working directoryへ依存しない。
toExpandedPoints()の出力から作る。表示用と送信用をset_speed → submit_initial_route/append_route → start_or_resumeの一連操作。advance()はSimulationRuntime.lockで直列化する。JDK 21を使い、repository直下から次を実行する。
cd apps/android-tablet
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 ./gradlew \
:pc-tool:testSimulatorUiTs \
:pc-tool:test \
:app:testDebugUnitTest \
:simulator-protocol:test \
--max-workers=1
全モジュール回帰と配布物確認:
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 ./gradlew testDebugUnitTest test --max-workers=1
JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 ./gradlew \
:app:assembleDebug :app:assembleRelease :pc-tool:installDist --max-workers=1
testSimulatorUiTsは既存test/checkへ自動結合されていないため明示して実行する。Android側を変えた
場合は、release APKのclasses*.dexとmerged manifestにdev/drogger/tablet/simulation、
SimulatorTcpService、SimulatorIpcServiceが入っていないことも再確認する。
テスト後は運用runbook 9.1節の最小スモークテストを実ブラウザ+Emulatorで行う。MapLibreの視覚状態、
Android製品UI、実時間の通信断はJVM/Nodeテストだけでは代替できない。
simulator-protocolのrequest/resultとcodecを追加する。互換性が壊れるならprotocol versionを上げる。SimulatorTcpRequestHandlerで処理し、debug単体テストを追加する。SimulatorStatusServiceとSimulatorUiHttpServerにHTTP変換を追加する。main.tsから呼ぶ。/api/status再取得で結果を確認する。KotlinのDraftRoute系は旧Android実装から受け継いだアルゴリズム原型、TypeScriptはPC UI実装である。
許容誤差、曲線簡略化、弧長比率によるremapを変える場合は両方の意図を比較し、表示/送信parity testを
維持する。画面だけの選択highlightは別layerでよいが、走行形状を別関数から生成してはならない。
Browser永続化で隠さず、Tabletを正本にすべき状態はquery_run_state→/api/statusへ追加する。同じcommitの
PC/Tabletを使う開発運用でも、欠落fieldをどう表示するかを決める。経路全形状を返す場合はpayload上限、
reload時の灰色破線復元、途中進捗との関係を独立仕様として扱う。
query_run_stateの追加fieldはprotocol version 1のまま拡張したため、古いTabletと新しいPC UIの混在はsimulator-app/simulator-ipcは移行履歴として残るが、現行の主経路ではない。記憶や作業端末を失った場合は、次の順に読む。
AIエージェント作業規約.mdとSTARTUP_CONTEXT.md、ButlerのIssue #68記録。git statusではなくButlerで状態を確認し、対象Issueへwork_startする。試験用ツールなので、未知の組合せを先回りして全面改修しない。再現した問題は「操作手順、期待、実際、
PC/Tabletの表示、commit」を記録し、不変条件を守る最小修正と回帰テストを追加する。