id: 845607e06add48e898b46a813d79a1f7
parent_id: 97c1eae003b54611a17c9c5db3d69a6a
item_type: 1
item_id: 6c0dcb2a567348fd9796f50c790082e4
item_updated_time: 1780320089667
title_diff: "[]"
body_diff: "[{\"diffs\":[[0,\"Platform\"],[1,\" — Project Plan\"],[0,\"\\\n\\\n## 1. \"]],\"start1\":32,\"start2\":32,\"length1\":16,\"length2\":31},{\"diffs\":[[0,\"te** | `\"],[-1,\"git@<host>:sim-racing\"],[1,\"ssh://jan@192.168.1.2/home/jan/Development/Repositories\"],[0,\"/ac-tele\"]],\"start1\":6047,\"start2\":6047,\"length1\":37,\"length2\":71},{\"diffs\":[[0,\"ith \"],[-1,\"AC), dkjson or Protobuf |\\\n| **S\"],[1,\"CSP) |\\\n| **Status** | **Phase 1.1 complete** — commit `3900d2a` |\\\n\\\n**Implemen\"],[0,\"ta\"],[-1,\"r\"],[0,\"ti\"],[-1,\"ng point** | Fork of `ac-tracer` by Tobi on GitHub |\\\n\\\n**Components**: Reads telemetry via `ac.*` API, serializes to JSON, sends via UDP to the analysis machine.\"],[1,\"on details:**\\\n- Built from scratch (not forked from ac-tracer — simpler approach for Phase 1)\\\n- Purpose-built `string.format`-based JSON serializer — zero table allocation, zero double traversal\\\n- Pre-allocated buffer tables at module scope, updated in-place each frame (zero GC pressure at 60Hz)\\\n- Named `do_send()` function avoids per-frame closure allocation\\\n- Uses `os.time()` for Unix epoch timestamps\\\n- Lap count from `car.lapCount` / `car.lapIndex` (not `session.laps`)\\\n- Suspension travel fallbacks: `car.wheels[].suspensionTravel` → `car.suspensionTravel` → `car.wheelSuspensionTravel`\\\n- Graceful fallback when `ac` global is absent (testing outside AC)\\\n- Configurable target host/port via `config.lua`\\\n\\\n**Files:**\\\n```\\\nac-telemetry-plugin/\\\n├── manifest.ini          (CSP app descriptor)\\\n├── config.lua            (UDP target: 127.0.0.1:5005, 60Hz interval)\\\n├── main.lua              (Entry point — wires script.update)\\\n├── src/\\\n│   ├── json.lua          (Purpose-built telemetry serializer)\\\n│   └── telemetry.lua     (CSP API reader + UDP sender)\\\n└── .gitignore\\\n```\"],[0,\"\\\n\\\n##\"]],\"start1\":6231,\"start2\":6231,\"length1\":204,\"length2\":1163},{\"diffs\":[[0,\" | `\"],[-1,\"git@<host>:sim-racing\"],[1,\"ssh://jan@192.168.1.2/home/jan/Development/Repositories\"],[0,\"/rus\"]],\"start1\":7590,\"start2\":7590,\"length1\":29,\"length2\":63},{\"diffs\":[[0,\"N), \"],[-1,\"SQLx + SQLite (storage), Axum (API), Yew/Leptos (frontend) |\\\n\\\n**Components**: Multi-so\"],[1,\"Chrono |\\\n| **Status** | **Phase 1.1 complete** (simple listener) — commit `30543fd` |\\\n\\\n**Implementation details (Phase 1 — simple listener):**\\\n- Single-crate struct\"],[0,\"ur\"],[-1,\"c\"],[0,\"e \"],[-1,\"UDP receiver, game-specific parsers (AC JSON, DR2 binary), SQLite storage, REST/WebSocket API, web dashboa\"],[1,\"for now (workspace split deferred to Phase 1.2)\\\n- Async UDP listener on configurable bind address (`RUSTY_TELEMETRY_BIND` env var, default `127.0.0.1:5005`)\\\n- `#[serde(deny_unknown_fields)]` on both `TelemetryFrame` and `GForces` — schema drift causes immediate parse errors\\\n- Locked stdout writes (single lock per frame, amortized syscalls)\\\n- `ReceiveStats` tracker: packet count, pkt/s rate, KB/s data rate\\\n\\\n**Files (Phase 1):**\\\n```\\\nrusty-telemetry/\\\n├── Cargo.toml            (tokio, se\"],[0,\"rd\"],[1,\"e\"],[0,\", \"],[-1,\"AI coaching integration.\\\n\\\n####\"],[1,\"serde_json, chrono)\\\n├── src/\\\n│   ├── main.rs           (UDP listener + banner + output loop)\\\n│   ├── telemetry.rs      (TelemetryFrame + GForces structs with Display)\\\n│   └── stats.rs          (ReceiveStats tracker)\\\n└── .gitignore\\\n```\\\n\\\n#### Planned\"],[0,\" Car\"]],\"start1\":7780,\"start2\":7780,\"length1\":239,\"length2\":917},{\"diffs\":[[0,\"e Layout\"],[1,\" (Phase 1.2+)\"],[0,\"\\\n\\\n```\\\nru\"]],\"start1\":8708,\"start2\":8708,\"length1\":16,\"length2\":29},{\"diffs\":[[0,\"3456\"],[-1,\".789\"],[0,\",\\\n  \"]],\"start1\":10441,\"start2\":10441,\"length1\":12,\"length2\":8},{\"diffs\":[[0,\"e\\\": 0.85\"],[1,\"0\"],[0,\",\\\n  \\\"bra\"]],\"start1\":10507,\"start2\":10507,\"length1\":16,\"length2\":17},{\"diffs\":[[0,\"ke\\\": 0.0\"],[1,\"00\"],[0,\",\\\n  \\\"ste\"]],\"start1\":10524,\"start2\":10524,\"length1\":16,\"length2\":18},{\"diffs\":[[0,\"g\\\": 0.12\"],[1,\"0\"],[0,\",\\\n  \\\"g_f\"]],\"start1\":10546,\"start2\":10546,\"length1\":16,\"length2\":17},{\"diffs\":[[0,\"\\\"x\\\": 0.5\"],[1,\"00\"],[0,\", \\\"y\\\": -\"]],\"start1\":10573,\"start2\":10573,\"length1\":16,\"length2\":18},{\"diffs\":[[0,\"y\\\": -0.2\"],[1,\"00\"],[0,\", \\\"z\\\": 1\"]],\"start1\":10586,\"start2\":10586,\"length1\":16,\"length2\":18},{\"diffs\":[[0,\"\\\"z\\\": 1.0\"],[1,\"00\"],[0,\" },\\\n  \\\"w\"]],\"start1\":10598,\"start2\":10598,\"length1\":16,\"length2\":18},{\"diffs\":[[0,\"[0.1\"],[1,\"00\"],[0,\", 0.1\"],[1,\"00\"],[0,\", 0.0\"],[1,\"00\"],[0,\", 0.0\"],[1,\"00\"],[0,\"],\\\n \"]],\"start1\":10628,\"start2\":10628,\"length1\":23,\"length2\":31},{\"diffs\":[[0,\"0.15\"],[1,\"0\"],[0,\", 0.14\"],[1,\"0\"],[0,\", 0.16\"],[1,\"0\"],[0,\", 0.15\"],[1,\"0\"],[0,\"],\\\n \"]],\"start1\":10682,\"start2\":10682,\"length1\":26,\"length2\":30},{\"diffs\":[[0,\"\\\n}\\\n```\\\n\\\n\"],[1,\"**Notes:**\\\n- `timestamp` is a Unix epoch in integer seconds (`os.time()` in Lua, deserialized as `f64` in Rust)\\\n- All floating-point values use 3 decimal places via `string.format`\\\n- `lap_count` is the current lap number (1-indexed), not the total session laps\\\n- The Rust structs use `#[serde(deny_unknown_fields)]` so any field name mismatch causes an immediate parse error\\\n\\\n\"],[0,\"Future: \"]],\"start1\":10749,\"start2\":10749,\"length1\":16,\"length2\":392},{\"diffs\":[[0,\"–6)\\\n\"],[1,\"\\\n\"],[0,\"- **1.1\"],[-1,\"**\"],[0,\" AC \"]],\"start1\":11357,\"start2\":11357,\"length1\":17,\"length2\":16},{\"diffs\":[[0,\"type\"],[-1,\" (IntelliJ + EmmyLua\"],[1,\"** — **DONE** (commit `3900d2a`, pushed 2026-06-01\"],[0,\")\\\n  -\"],[1,\" [x]\"],[0,\" Set\"]],\"start1\":11389,\"start2\":11389,\"length1\":33,\"length2\":67},{\"diffs\":[[0,\"ject\"],[-1,\", implement shared memo\"],[1,\" at `~/IdeaProjects/ac-telemetry-plugin`\\\n  - [x] Implement telemet\"],[0,\"ry r\"]],\"start1\":11463,\"start2\":11463,\"length1\":31,\"length2\":74},{\"diffs\":[[0,\"ate(\"],[1,\"0\"],[0,\")`\"],[-1,\"\\\n  - Create UDP sender module with JSON encoding\\\n  - Test with simple Rust listener\\\n  - Target: 60Hz+ data frequency\"],[1,\" with field fallbacks\\\n  - [x] Create purpose-built JSON serializer (zero-allocation `string.format`)\\\n  - [x] Create UDP sender module with configurable target\\\n  - [x] Pre-allocated buffer tables for zero GC pressure at 60Hz\\\n  - [x] Test with simple Rust listener\\\n  - [x] Code review: fixed timestamp (`os.time()`), lap count source, suspension fallbacks\\\n\"],[0,\"\\\n- **1.2\"],[-1,\"**\"],[0,\" `ru\"]],\"start1\":11559,\"start2\":11559,\"length1\":136,\"length2\":373},{\"diffs\":[[0,\"eton\"],[-1,\" (RustRover)\\\n  - Create\"],[1,\"** — **PARTIAL** (commit `30543fd`, pushed 2026-06-01)\\\n  - [x] Simple single-crate UDP listener on `127.0.0.1:5005` (configurable via env)\\\n  - [x] JSON deserialization with `deny_unknown_fields` for schema contract\\\n  - [x] ReceiveStats tracker (packet count, rates)\\\n  - [x] Code review: bind address security, stdout lock consolidation\\\n  - [ ] Refactor to\"],[0,\" Car\"]],\"start1\":11961,\"start2\":11961,\"length1\":31,\"length2\":363},{\"diffs\":[[0,\"ates\\\n  -\"],[1,\" [ ]\"],[0,\" Multi-p\"]],\"start1\":12376,\"start2\":12376,\"length1\":16,\"length2\":20},{\"diffs\":[[0,\"er (\"],[-1,\"port 5005 for AC, \"],[0,\"sepa\"]],\"start1\":12410,\"start2\":12410,\"length1\":26,\"length2\":8},{\"diffs\":[[0,\"DR2)\\\n  -\"],[1,\" [ ]\"],[0,\" SQLite \"]],\"start1\":12432,\"start2\":12432,\"length1\":16,\"length2\":20},{\"diffs\":[[0,\"ions\\\n  -\"],[1,\" [ ]\"],[0,\" Basic R\"]],\"start1\":12472,\"start2\":12472,\"length1\":16,\"length2\":20},{\"diffs\":[[0,\"nts\\\n\"],[1,\"\\\n\"],[0,\"- **1.3\"],[-1,\"**\"],[0,\" DR2\"]],\"start1\":12506,\"start2\":12506,\"length1\":17,\"length2\":16},{\"diffs\":[[0,\"er crate\"],[1,\"**\"],[0,\"\\\n  -\"],[1,\" [ ]\"],[0,\" Add `pa\"]],\"start1\":12529,\"start2\":12529,\"length1\":20,\"length2\":26},{\"diffs\":[[0,\"pace\\\n  -\"],[1,\" [ ]\"],[0,\" Analyze\"]],\"start1\":12579,\"start2\":12579,\"length1\":16,\"length2\":20},{\"diffs\":[[0,\"rser\\\n  -\"],[1,\" [ ]\"],[0,\" Map to \"]],\"start1\":12647,\"start2\":12647,\"length1\":16,\"length2\":20},{\"diffs\":[[0,\"gin → UDP | \"],[1,\"**\"],[0,\"Phase 1\"],[1,\".1 done** — plugin + listener working\"],[0,\" |\\\n| 2 | Dir\"]],\"start1\":15774,\"start2\":15774,\"length1\":31,\"length2\":70},{\"diffs\":[[0,\" design*\"],[1,\"\\\n*Phase 1.1 completed: 2026-06-01 — ac-telemetry-plugin (3900d2a), rusty-telemetry (30543fd)*\"]],\"start1\":16257,\"start2\":16257,\"length1\":8,\"length2\":101}]"
metadata_diff: {"new":{},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-06-01T13:27:41.752Z
created_time: 2026-06-01T13:27:41.752Z
type_: 13