id: 727d6dbdb58b495f96a2bbb6aed55000
parent_id: f2f94a6b08574000b7b7f5cfee0d8109
item_type: 1
item_id: 6c0dcb2a567348fd9796f50c790082e4
item_updated_time: 1780559297526
title_diff: "[]"
body_diff: "[{\"diffs\":[[0,\"ructure\\\n\"],[1,\"- Both PCARS1 and PCARS2 use default port **5606**\\\n\"],[0,\"- Config\"]],\"start1\":2103,\"start2\":2103,\"length1\":16,\"length2\":67},{\"diffs\":[[0,\" Cars 1\\\"\"],[1,\" or \\\"2\\\"\"],[0,\" recomme\"]],\"start1\":2208,\"start2\":2208,\"length1\":16,\"length2\":23},{\"diffs\":[[0,\"ommended\"],[1,\"\\\n- **IMPLEMENTED in v0.4.0** — parser with auto-detection between V1 (1367 bytes) and V2 (559 bytes)\"],[0,\"\\\n\\\n---\\\n\\\n#\"]],\"start1\":2227,\"start2\":2227,\"length1\":16,\"length2\":116},{\"diffs\":[[0,\"e — \"],[-1,\"Dual\"],[1,\"Triple\"],[0,\"-Sou\"]],\"start1\":3066,\"start2\":3066,\"length1\":12,\"length2\":14},{\"diffs\":[[0,\"r AC\"],[1,\" + PCARS\"],[0,\"\\\n\\\n### T\"],[-1,\"wo\"],[1,\"hree\"],[0,\" Ind\"]],\"start1\":3096,\"start2\":3096,\"length1\":17,\"length2\":27},{\"diffs\":[[0,\" Sources\"],[1,\" (AC) + One PCARS Source\"],[0,\"\\\n\\\nAC pro\"]],\"start1\":3136,\"start2\":3136,\"length1\":16,\"length2\":40},{\"diffs\":[[0,\"\\\n\\\n**\"],[-1,\"Both feeds work on pure Windows without Proton — neither depends on CSP.**\\\n\\\nIf more data is ever needed, AC's Python plugin system can be used (same mechanism as the Telemetry Tool plugin). The abandoned CSP Lua plugin approach is documented in Section 4 for reference only.\"],[1,\"Feed 3: PCARS (port 5606) — Project CARS 1 & 2**\\\n- Both PCARS1 and PCARS2 broadcast on port 5606\\\n- Game sends TO a configured IP:port — rusty-telemetry binds and listens\\\n- Auto-detects PCARS1 vs PCARS2 packet format\\\n- PCARS1: ~1367 bytes, 3-byte header, 10 Hz\\\n- PCARS2: ~559 bytes (Car Physics), 12-byte header, configurable Hz\\\n- **IMPLEMENTED in v0.4.0**\\\n\\\n**All AC feeds work on pure Windows without Proton — neither depends on CSP.**\"],[0,\"\\\n\\\n##\"]],\"start1\":4162,\"start2\":4162,\"length1\":282,\"length2\":443},{\"diffs\":[[0,\"──────────────┐\\\n\"],[1,\"│                GAME MACHINE (PCARS 1 or 2)                        │\\\n│                                                                   │\\\n│  ┌──────────────────────────┐                                    │\\\n│  │ PCARS Engine              │                                    │\\\n│  │ Binary, ~10 Hz            │                                    │\\\n│  │ SENDS to :5606            │                                    │\\\n│  └──────────┬───────────────┘                                    │\\\n└─────────────┼─────────────────────────────────────────────────────┘\\\n              │\\\n              ▼\\\n┌──────────────────────────────────────────────────────────────────┐\\\n\"],[0,\"│               \"]],\"start1\":5539,\"start2\":5539,\"length1\":32,\"length2\":691},{\"diffs\":[[0,\"Model     │   │\\\n\"],[1,\"│  │  :5606  (bind/listen)──→ parser-pcars     ──┘              │   │\\\n\"],[0,\"│  └────────────\"]],\"start1\":6547,\"start2\":6547,\"length1\":32,\"length2\":102},{\"diffs\":[[0,\"l) |\"],[-1,\"\\\n|---|---|---|\\\n| **Origin** | AC engine (built-in) | Python plugin (IkoRein) |\\\n| **Protocol** | 3-phase handshake (AC binds :9996, tool registers) | Send-only (unbound socket) |\\\n| **Status** | ⚠️ Needs protocol fix — handshake bugs found | ✅ **WORKING** — 6.3 MB captured |\\\n| **Format** | Binary (official Google Doc) | Binary (struct in Python source) |\\\n| **Rate** | ~20 Hz fixed | Per-frame |\\\n| **Data depth** | Basic | **Full** (wheel slip, tyres, ERS) |\\\n| **All cars** | No (focused car) | **Yes** (multi-driver packets) |\\\n| **Config** | No config file (discovered via handshake) | config.ini (IP/port) |\\\n| **World position** | ✅ Yes (carCoordinates[3]) | ✅ Yes (x, y, z) |\\\n| **Wheel slip** | ✅ Yes (slipAngle, slipRatio, tyreSlip, ndSlip) | ✅ Yes (slipRatio, slipAngle, ndSlip, wheel_slip) |\\\n| **Tyre temps** | ❌ No | ✅ Core temps per wheel |\\\n| **Suspension** | ✅ suspensionHeight[4] | ✅ rideHeight (front/rear) |\\\n| **Crash resilience** | Never crashes (engine built-in) | Python process |\\\n| **Installation** | None required | Optional — requires plugin install |\\\n\\\n### Coaching Capability by Source\\\n\\\n| Use Case | 9996 (KSUDP) | 10101 (Telemetry Tool) |\\\n|---|---|---|\\\n| Lap time analysis | ✅ | ✅ |\\\n| Braking point coaching | ✅ | ✅ |\\\n| Racing line analysis | ✅ (carCoordinates) | ✅ (world position) |\\\n| Throttle/traction coaching | ✅ (slipRatio, tyreSlip) | ✅ (slipRatio, wheel_slip) |\\\n| Understeer/oversteer | ✅ (~85% with slip data) | ✅ (~95% with slip data) |\\\n| Tyre management | ⚠️ (dirtyLevel only) | ✅ (core temps) |\\\n| Damage monitoring | ❌ | ❌ |\\\n| Multi-car tracking | ❌ (focused car) | ✅ (all cars) |\\\n\\\n### Rust Listener Requirements (Next Steps)\\\n\\\nThe `rusty-telemetry` listener needs these changes:\\\n\\\n1. **Port 9996 (KSUDP) — rewrite to 3-phase handshake protocol**:\\\n   - Do NOT bind to 9996 — let AC own it\\\n   - Bind a receiver socket to a different port (e.g., OS-assigned ephemeral)\\\n   - **Phase 1**: Send HANDSHAKE `{1, 1, 0}` (12 bytes) to `127.0.0.1:9996`\\\n   - **Phase 2**: Receive 408-byte HandshakerResponse (parse car/driver/track info)\\\n   - **Phase 3**: Send SUBSCRIBE_UPDATE `{1, 1, 1}` (12 bytes) to `127.0.0.1:9996`\\\n   - Receive continuous RTCarInfo structs on the receiver socket\\\n   - Reference: https://docs.google.com/document/d/1KfkZiIluXZ6mMhLWfDX1qAGbvhGRC3ZUzjVIt5FQpp4/pub\\\n\\\n2. **Port 10101 — keep current bind-and-listen pattern** (send-only feed, tool binds to receive):\\\n   - Bind to `:10101`\\\n   - `recv_from()` loop\\\n   - **Optional** — if bind fails, show as \\\"not available\\\" and continue\\\n\\\n3. **Per-feed status reporting**: For each port/feed, log:\\\n   - Feed name (ksudp / telemetry_tool)\\\n   - Bind status (listening / error)\\\n   - Packet count, packets/sec\\\n   - Data rate (KB/s)\\\n   - Last packet timestamp\\\n   - Feed health (alive / stale / dead)\\\n\\\n4. **Data dump to files**: Write raw received data to per-feed files:\\\n   - `./data/<session_id>/feed_9996_ksudp_<timestamp>.bin` (raw binary)\\\n   - `./data/<session_id>/feed_10101_telemetrytool_<timestamp>.bin` (raw binary)\\\n   - Session ID = `YYYYMMDD_HHMMSS` at startup\\\n   - File rotation every N minutes or N MB\\\n\\\n5. **Startup banner**: Show all configured ports and their bind status:\\\n   ```\\\n   rusty-telemetry v0.3.0 — Multi-feed telemetry listener\\\n   ─────────────────────────────────────────────────────\\\n   Feed :9996  [KSUDP]            ⏳ Handshake (sending to AC)\\\n   Feed :10101 [Telemetry Tool]   ✅ Listening (60.3 pkt/s)\\\n   ─────────────────────────────────────────────────────\\\n   Data dir: ./data/20260603_193200/\\\n   ```\\\n\\\n---\\\n\\\n## 4. Lua Plugin — ABANDONED\\\n\\\nThe CSP Lua plugin approach (port 5005) is **abandoned**. Root cause:\\\n\\\n- CSP ships only the pure-Lua wrapper of LuaSocket (`socket.lua`), NOT the compiled C core (`socket/core.dll`)\\\n- `socket.lua` line 12: `require(\\\"socket.core\\\")` → FAILS because the C library doesn't exist\\\n- `type(ac)` returns `\\\"userdata\\\"` (not `\\\"table\\\"`) in CSP's LuaJIT, so the `ac_available` check failed, diagnostic `ac.log()` calls were never reached\\\n- CSP has no raw UDP socket API — only `web.get()` (HTTP), `web.socket()` (WebSocket), `ac.connect()` (shared memory IPC)\\\n- File-based or HTTP-based IPC cannot achieve target frequency (60Hz)\\\n- The repo `ac-telemetry-plugin` should be archived\\\n\\\nIf more data is ever needed, use AC's Python plugin system (same mechanism as Telemetry Tool) — it has full access to `ac.getCarState()` and can use Python's `socket` module without restrictions.\\\n\\\n### Abandoned Repo: `ac-telemetry-plugin`\\\n\\\n| Field | Value |\\\n|---|---|\\\n| **Language** | Lua (LuaJIT) |\\\n| **IDE** | IntelliJ IDEA + EmmyLua |\\\n| **Project path** | `~/IdeaProjects/ac-telemetry-plugin` |\\\n| **Remote** | `ssh://jan@192.168.1.2/home/jan/Development/Repositories/ac-telemetry-plugin.git` |\\\n| **Codeberg** | `ssh://git@codeberg.org/jhunnius/ac-telemetry-plugin` |\\\n| **Status** | **ABANDONED** — CSP lacks socket.core, cannot send UDP from Lua |\\\n\\\n---\\\n\\\n## 5. KSUDP (Port 9996) — Complete Protocol Documentation\\\n\\\n**Reference**: https://docs.google.com/document/d/1KfkZiIluXZ6mMhLWfDX1qAGbvhGRC3ZUzjVIt5FQpp4/pub\\\nThis is the official AC Remote Telemetry documentation by Kunos/Stefano Casillo.\\\n\\\n### 5.1 Handshake Protocol — 3-Phase\\\n\\\n```\\\n┌─────────────────────┐                  ┌──────────────────────┐\\\n│   Assetto Corsa      │                  │   rusty-telemetry    │\\\n│                      │                  │                      │\\\n│   BINDS to :9996     │                  │   Receiver socket    │\\\n│   (listener socket)  │                  │   (any port)         │\\\n│                      │                  │                      │\\\n│                      │  ◄── Phase 1 ──  │  Send HANDSHAKE      │\\\n│                      │     12 bytes      │  {1, 1, 0}          │\\\n│                      │                  │                      │\\\n│                      │  ── Phase 2 ──►  │  Receive 408-byte    │\\\n│                      │     408 bytes     │  HandshakerResponse  │\\\n│                      │                  │                      │\\\n│                      │  ◄── Phase 3 ──  │  Send SUBSCRIBE      │\\\n│                      │     12 bytes      │  {1, 1, 1}          │\\\n│                      │                  │                      │\\\n│                      │  ── Continuous ─► │  RTCarInfo structs   │\\\n│                      │     ~20 Hz        │  (until DISMISS)    │\\\n└─────────────────────┘                  └──────────────────────┘\\\n```\\\n\\\n**Phase 1: Client sends HANDSHAKE to AC (port 9996)**\\\n```\\\nstruct Handshake {\\\n    int32 identifier;    // Platform type: 0=iPhone, 1=iPad, 2=AndroidPhone, 3=AndroidTablet (currently unused, set to 1)\\\n    int32 version;       // Protocol version (currently unused, set to 1)\\\n    int32 operationId;   // 0=HANDSHAKE, 1=SUBSCRIBE_UPDATE, 2=SUBSCRIBE_SPOT, 3=DISMISS\\\n};\\\n// For phase 1: { identifier=1, version=1, operationId=0 }\\\n// Total: 12 bytes, little-endian\\\n```\\\n\\\n**Phase 2: AC responds with HandshakerResponse**\\\n```\\\nstruct HandshakerResponse {\\\n    wchar_t[50] carName;      // 100 bytes (UTF-16LE, NOT ASCII as doc says)\\\n    wchar_t[50] driverName;   // 100 bytes (UTF-16LE)\\\n    int32       identifier;   // Always 4242 (connection valid indicator)\\\n    int32       version;      // Always 1\\\n    wchar_t[50] trackName;    // 100 bytes (UTF-16LE)\\\n    wchar_t[50] trackConfig;  // 100 bytes (UTF-16LE)\\\n};\\\n// Total: 408 bytes (6×100 + 2×4)\\\n```\\\n\\\n**NOTE**: The Google Doc says `char[50]` but the actual implementation uses UTF-16LE (2 bytes per char = 100 bytes per field). Total 408 bytes, confirmed by captured data.\\\n\\\n**Phase 3: Client sends SUBSCRIBE_UPDATE to AC (port 9996)**\\\n```\\\n// Same struct as Phase 1, but operationId = 1 (SUBSCRIBE_UPDATE)\\\n{ identifier=1, version=1, operationId=1 }\\\n```\\\n\\\nAfter Phase 3, AC adds the client as a listener and sends continuous telemetry updates.\\\n\\\n**SUBSCRIBE_SPOT (operationId=2)**: Subscribe to spot events only (e.g., lap completion), for ALL cars in session.\\\n**DISMISS (operationId=3)**: Unsubscribe. AC forgets the client.\\\n\\\n### 5.2 Continuous Telemetry — RTCarInfo (after SUBSCRIBE_UPDATE)\\\n\\\nAC sends this struct for every physics step to subscribed clients:\\\n\\\n```\\\nstruct RTCarInfo {\\\n    char    identifier;               // Always 'a' (0x61)\\\n    int32   size;                      // Size of this struct in bytes\\\n    float   speed_Kmh;\\\n    float   speed_Mph;\\\n    float   speed_Ms;\\\n    bool    isAbsEnabled;\\\n    bool    isAbsInAction;\\\n    bool    isTcInAction;\\\n    bool    isTcEnabled;\\\n    bool    isInPit;\\\n    bool    isEngineLimiterOn;\\\n    float   accG_vertical;\\\n    float   accG_horizontal;\\\n    float   accG_frontal;\\\n    int32   lapTime;                   // Current lap time (ms)\\\n    int32   lastLap;                   // Last completed lap (ms)\\\n    int32   bestLap;                   // Best lap (ms)\\\n    int32   lapCount;\\\n    float   gas;                       // 0–1\\\n    float   brake;                     // 0–1\\\n    float   clutch;                    // 0–1\\\n    float   engineRPM;\\\n    float   steer;\\\n    int32   gear;\\\n    float   cgHeight;\\\n    float   wheelAngularSpeed[4];\\\n    float   slipAngle[4];\\\n    float   slipAngle_ContactPatch[4];\\\n    float   slipRatio[4];\\\n    float   tyreSlip[4];\\\n    float   ndSlip[4];\\\n    float   load[4];\\\n    float   Dy[4];\\\n    float   Mz[4];\\\n    float   tyreDirtyLevel[4];\\\n    float   camberRAD[4];\\\n    float   tyreRadius[4];\\\n    float   tyreLoadedRadius[4];\\\n    float   suspensionHeight[4];\\\n    float   carPositionNormalized;     // Spline position 0–1\\\n    float   carSlope;\\\n    float   carCoordinates[3];         // World position X, Y, Z\\\n};\\\n```\\\n\\\n### 5.3 Spot Event — RTLap (after SUBSCRIBE_SPOT)\\\n\\\n```\\\nstruct RTLap {\\\n    int32   carIdentifierNumber;\\\n    int32   lap;\\\n    char    driverName[50];            // Likely also UTF-16LE in practice\\\n    char    carName[50];\\\n    int32   time;                      // Lap time in ms\\\n};\\\n```\\\n\\\n### 5.4 KSUDP Implementation Bugs Found (2026-06-03) — FIXED in v0.3.0\\\n\\\n**Bug 1: Wrong packet size** — Code sent 4 bytes (`1u32.to_le_bytes()`) instead of 12 bytes (3 × int32). AC reads operationId from uninitialized buffer, gets HANDSHAKE (0).\\\n\\\n**Bug 2: Missing subscribe phase** — Code only sent HANDSHAKE (phase 1), never sent SUBSCRIBE_UPDATE (phase 3). AC responded once per handshake but never started continuous streaming.\\\n\\\n**Evidence**: 14 packets × 408 bytes over 3 minutes = 0.08 Hz (1 per handshake attempt every 10 seconds). Expected: ~20 Hz continuous.\\\n\\\n**Fix (implemented in v0.3.0)**: Proper 3-phase protocol:\\\n1. Send `{1, 1, 0}` (HANDSHAKE) to port 9996 — 12 bytes\\\n2. Receive 408-byte response (parse UTF-16LE car/driver/track info, validate identifier=4242)\\\n3. Send `{1, 1, 1}` (SUBSCRIBE_UPDATE) to port 9996 — 12 bytes\\\n4. Receive continuous RTCarInfo structs at ~20 Hz\\\n5. Periodic keepalive: re-send SUBSCRIBE_UPDATE every 10 seconds\\\n\\\n---\\\n\\\n## 6. Telemetry Tool Plugin (Port 10101) — Reference\\\n\\\n**Source code**: `<AC>/apps/python/Telemetry_Tool_plugin/Telemetry_Tool_plugin.py`\\\n**Download**: https://www.racedepartment.com/downloads/telemetry-tool-for-ac.37231/\\\n**Author**: IkoRein, version 1.3\\\n**License**: Third-party, check RaceDepartment terms\\\n\\\n### Installation & Configuration\\\n1. Extract to `<AC>/apps/python/Telemetry_Tool_plugin/`\\\n2. Edit `<AC>/apps/python/Telemetry_Tool_plugin/config.ini`:\\\n   ```ini\\\n   [telemetry_tool_plugin]\\\n   to_ip = 127.0.0.1\\\n   to_port = 10101\\\n   ```\\\n3. Enable in `python.ini` (in same directory): `[TELEMETRY_TOOL_PLUGIN] ACTIVE=1`\\\n4. Restart AC\\\n\\\n### Packet Structure (binary, little-endian)\\\n\\\n**Outer wrapper**: `byte packet_type` + `[driver_data × cars_count]`\\\n- `packet_type = 22` → telemetry data\\\n- `packet_type = 23` → version info (sent every ~100 frames)\\\n\\\n**Per-driver structure (357 bytes each)** — from `getBytesForTelemetry(False)` in Telemetry_Tool_plugin.py:\\\n\\\n| Offset | Type | Field | Source API | Notes |\\\n|--------|------|-------|-----------|-------|\\\n| 0 | uint32 | driver_id | constructor | Car index |\\\n| 4 | uint32 | lap_time | ac.getCarState(CS.LapTime) | Current lap ms |\\\n| 8 | uint32 | last_lap | ac.getCarState(CS.LastLap) | Last lap ms |\\\n| 12 | uint32 | best_lap | ac.getCarState(CS.BestLap) | Best lap ms |\\\n| 16 | byte | current_splits_num | len(ac.getCurrentSplits()) | |\\\n| 17 | uint32×3 | cur_split[1-3] | ac.getCurrentSplits() | Sector times ms |\\\n| 29 | uint32×3 | last_split[1-3] | ac.getLastSplits() | Last lap sectors ms |\\\n| 41 | float | steer | ac.getCarState(CS.Steer) | -2π…2π |\\\n| 45 | float | throttle | ac.getCarState(CS.Gas) | 0–1 |\\\n| 49 | float | brake | ac.getCarState(CS.Brake) | 0–1 |\\\n| 53 | float | clutch | ac.getCarState(CS.Clutch) | 0–1 |\\\n| 57 | int16 | gear | ac.getCarState(CS.Gear) | |\\\n| 59 | float | engineRPM | ac.getCarState(CS.RPM) | |\\\n| 63 | float | speed | 3.6 × CS.SpeedMS | km/h |\\\n| 67 | int16 | track_position | ac.getCarRealTimeLeaderboardPosition() | |\\\n| 69 | int16 | leaderboard_position | ac.getCarLeaderboardPosition() | |\\\n| 71 | int16 | lap_count | ac.getCarState(CS.LapCount) | |\\\n| 73 | float | x | CS.WorldPosition | World pos X |\\\n| 77 | float | y | CS.WorldPosition | World pos Y |\\\n| 81 | float | z | CS.WorldPosition | World pos Z |\\\n| 85 | double | spline_position | CS.NormalizedSplinePosition | 0–1 |\\\n| 93 | float | test_float | hardcoded 666.666 | Validation |\\\n| 97 | char[33] | driver_name | ac.getDriverName() | Null-padded |\\\n| 130 | char[4] | nationality | ac.getDriverNationCode() | |\\\n| 134 | char[33] | car_name | ac.getCarName() | Null-padded |\\\n| 167 | char[10] | tyre_name | ac.getCarTyreCompound() | |\\\n| 177 | uint32 | test_int | hardcoded 666 | Validation |\\\n| 181 | byte | is_connected | ac.isConnected() | |\\\n| 182 | byte | is_in_pit | ac.isCarInPit() | |\\\n| 183 | byte | is_car_in_pitlane | ac.isCarInPitlane() | |\\\n| 184 | byte | valid | CS.LapInvalidated | |\\\n| 185 | float | acc_vertical | CS.AccG[0] | Vertical G |\\\n| 189 | float | acc_horizontal | CS.AccG[1] | Horizontal G |\\\n| 193 | float | acc_frontal | CS.AccG[2] | Longitudinal G |\\\n| 196 | byte | is_engine_limiter_on | CS.IsEngineLimiterOn | |\\\n| 197 | byte | is_drs_available | CS.DrsAvailable | |\\\n| 198 | byte | is_drs_enabled | CS.DrsEnabled | |\\\n| 199 | byte | is_race_finished | CS.RaceFinished | |\\\n| 200 | byte | ptp_status | CS.P2PStatus | Push-to-pass |\\\n| 201 | uint32 | ptp_activations | CS.P2PActivations | |\\\n| 205 | float | ers_max_j | CS.ERSMaxJ | ERS capacity |\\\n| 209 | float | ers_current_kj | CS.ERSCurrentKJ | ERS charge |\\\n| 213 | float×4 | tyre_dirty_level | CS.TyreDirtyLevel | 0–1 per wheel |\\\n| 229 | float | turbo_boost | CS.TurboBoost | |\\\n| 233 | float×2 | ride_height | CS.RideHeight | Front/rear |\\\n| 241 | float×4 | current_tyres_core_temp | CS.CurrentTyresCoreTemp | Per wheel |\\\n| 257 | float×4 | tyres_slip_ratio | CS.SlipRatio | Per wheel |\\\n| 273 | float×4 | tyres_slip_angle | CS.SlipAngle | Per wheel |\\\n| 289 | float×4 | nd_slip | CS.NdSlip | Normalized slip per wheel |\\\n| 305 | float×4 | wheel_slip | CS.TyreSlip | Per wheel |\\\n| 321 | float×3 | world_speed | CS.Velocity | World velocity |\\\n| 333 | float×3 | local_speed | CS.LocalVelocity | Local velocity |\\\n| 345 | int32 | valid (trailer) | CS.LapInvalidated | Validation check |\\\n| 349 | double | test_float (trailer) | hardcoded 666.666 | Validation (as double) |\\\n\\\nTotal per driver: 357 bytes.\\\n\\\n---\\\n\\\n## 7. Repositories & Deployment\\\n\\\n### Repo: `rusty-telemetry` (Main Rust Backend)\\\n\\\n| Field | Value |\\\n|---|---|\\\n| **Language** | Rust |\\\n| **IDE** | RustRover |\\\n| **Local path** | `~/RustroverProjects/rusty-telemetry` |\\\n| **Remote** | `ssh://jan@192.168.1.2/home/jan/Development/Repositories/rusty-telemetry.git` |\\\n| **Status** | **Phase 1.0 — KSUDP 3-phase handshake implemented in v0.3.0** |\\\n\\\n#### Planned Cargo Workspace Layout (Phase 1.1+)\\\n\\\n```\\\nrusty-telemetry/\\\n├── Cargo.toml                    (workspace root)\\\n├── crates/\\\n│   ├── core/                     (shared types, traits, config)\\\n│   ├── receiver/                 (multi-port UDP listener)\\\n│   ├── parser-ksudp/             (AC binary :9996 → generic model)\\\n│   ├── parser-telemetry-tool/    (AC binary :10101 → generic model)\\\n│   ├── parser-dr2/               (DR2 binary :5015 → generic model)\\\n│   ├── parser-pcars/             (PCARS binary :5006 → generic model)\\\n│   ├── plausibility/             (cross-source data validation)\\\n│   ├── storage/                  (SQLite persistence, migrations)\\\n│   ├── analyzer/                 (derived metrics, lap comparison)\\\n│   ├── api/                      (REST + WebSocket server, Axum)\\\n│   └── web/                      (frontend)\\\n├── bin/\\\n│   └── rusty-telemetry.rs\\\n└── data/                         (runtime data dumps)\\\n    └── <session>/\\\n        ├── feed_9996_*.bin\\\n        └── feed_10101_*.bin\\\n```\\\n\\\n### Deployment Paths\\\n\\\n| Path | Purpose |\\\n|---|---|\\\n| `<AC>/apps/python/Telemetry_Tool_plugin/` | **Third-party plugin** — config.ini for IP/port |\\\n| `~/RustroverProjects/rusty-telemetry/` | **Rust listener** — multi-port receiver |\\\n| `~/IdeaProjects/ac-telemetry-plugin/` | **ARCHIVED** — abandoned Lua plugin repo |\\\n\\\n`<AC>` = `/home/jan/.steam/debian-installation/steamapps/common/assettocorsa`\\\n`<Proton Docs>` = `~/.steam/.../compatdata/244210/pfx/drive_c/users/steamuser/Documents/Assetto Corsa`\\\n\\\n---\\\n\\\n## 8\"],[1,\" Port 5606 (PCARS) |\\\n|---|---|---|---|\\\n| **Origin** | AC engine (built-in) | Python plugin (IkoRein) | PCARS 1/2 engine |\\\n| **Protocol** | 3-phase handshake | Send-only (unbound socket) | Game sends to target |\\\n| **Status** | ✅ **WORKING** — v0.3.0 | ✅ **WORKING** | ✅ **IMPLEMENTED** — v0.4.0 |\\\n| **Format** | Binary (official Google Doc) | Binary (struct in Python source) | Binary (auto-detect V1/V2) |\\\n| **Rate** | ~20 Hz fixed | Per-frame | ~10 Hz (configurable in PCARS2) |\\\n| **Data depth** | Basic | **Full** (wheel slip, tyres, ERS) | **Full** (temps, damage, weather) |\\\n| **All cars** | No (focused car) | **Yes** (multi-driver packets) | V1: 56 entries, V2: separate |\\\n| **World position** | ✅ Yes | ✅ Yes | V2: ✅ (full 3D), V1: ❌ |\\\n| **Wheel data** | ✅ Yes | ✅ Yes | ✅ Yes (temps, wear, damage) |\\\n| **Engine temps** | ❌ No | ❌ No | ✅ Oil, water temps |\\\n| **Damage** | ❌ No | ❌ No | ✅ Aero, engine, suspension |\\\n| **Tyre management** | ⚠️ (dirtyLevel only) | ✅ (core temps) | ✅ (wear, temps per wheel) |\\\n\\\n---\\\n\\\n## 4. Lua Plugin — ABANDONED\\\n\\\n*(Unchanged — see note history for details)*\\\n\\\n---\\\n\\\n## 5. KSUDP (Port 9996) — Complete Protocol Documentation\\\n\\\n*(Unchanged — see note history for details)*\\\n\\\n---\\\n\\\n## 6. Telemetry Tool Plugin (Port 10101) — Reference\\\n\\\n*(Unchanged — see note history for details)*\\\n\\\n---\\\n\\\n## 7. PCARS (Port 5606) — UDP Protocol Reference\\\n\\\nSee separate Joplin note `c6bd2c45938246fa9d61776deae9874b` for the full byte-level protocol specification.\\\n\\\n### Parser Implementation (v0.4.0)\\\n\\\n- File: `src/parser_pcars.rs`\\\n- Auto-detects V1 vs V2 based on packet size and header structure\\\n- V1 detection: large packet (≥1367 bytes) with type=0 in packed byte at offset 2\\\n- V2 detection: 12-byte header with `packet_type` at offset 10 ≤ 8\\\n- Maps PCARS fields to generic `TelemetryFrame` model\\\n- 5 unit tests (V1 parse, V2 parse, too short, unknown format, V2 other types)\\\n- PCARS-specific fields added to `TelemetryFrame`: oil/water temp, fuel capacity, boost, crash state, odometer, aero/engine damage, ambient/track temp, rain density, track length, full 3D position, tyre wear, brake temp, suspension damage\\\n\\\n---\\\n\\\n## 8. Repositories & Deployment\\\n\\\n### Repo: `rusty-telemetry` (Main Rust Backend)\\\n\\\n| Field | Value |\\\n|---|---|\\\n| **Language** | Rust |\\\n| **IDE** | RustRover |\\\n| **Local path** | `~/RustroverProjects/rusty-telemetry` |\\\n| **Remote** | `ssh://jan@192.168.1.2/home/jan/Development/Repositories/rusty-telemetry.git` |\\\n| **Codeberg** | `ssh://git@codeberg.org/jhunnius/rusty-telemetry` |\\\n| **Status** | **Phase 1.1 — Binary parsers implemented, PCARS added in v0.4.0** |\\\n\\\n### Module Structure (current — single crate)\\\n\\\n```\\\nrusty-telemetry/\\\n├── Cargo.toml\\\n├── src/\\\n│   ├── main.rs                    (multi-port UDP listener + TUI event loop)\\\n│   ├── binary_utils.rs            (shared safe binary read helpers)\\\n│   ├── feed_state.rs              (per-feed health/frequency state machine)\\\n│   ├── telemetry_frame.rs         (unified telemetry data model)\\\n│   ├── parser_ksudp.rs            (AC port 9996 binary parser)\\\n│   ├── parser_telemetry_tool.rs   (AC port 10101 binary parser)\\\n│   ├── parser_pcars.rs            (PCARS port 5606 binary parser, V1+V2)\\\n│   └── ui.rs                      (ratatui TUI rendering)\\\n└── data/                          (runtime data dumps)\\\n    └── <session>/\\\n        ├── feed_9996_ksudp.bin\\\n        ├── feed_10101_telemetry_tool.bin\\\n        └── feed_5606_pcars.bin\\\n```\\\n\\\n---\\\n\\\n## 9\"],[0,\". Im\"]],\"start1\":7059,\"start2\":7059,\"length1\":17064,\"length2\":3446},{\"diffs\":[[0,\"0101\"],[-1,\") — **DONE**\"],[1,\", 5606)\"],[0,\"\\\n  -\"]],\"start1\":10684,\"start2\":10684,\"length1\":20,\"length2\":15},{\"diffs\":[[0,\"iles\"],[-1,\" (`./data/<session>/feed_<port>_*.bin`)\\\n  - [x] Startup banner showing all feed ports and bind status\\\n  - [x] **Rewrite port 9996 handler** — 3-phase handshake implemented\\\n  - [x] Phase 1: Send `{1, 1, 0}` HANDSHAKE (12 bytes) to AC :9996\\\n  - [x] Phase 2: Receive 408-byte HandshakerResponse, parse UTF-16LE fields\\\n  - [x] Phase 3: Send `{1, 1, 1}` SUBSCRIBE_UPDATE (12 bytes) to AC :9996\\\n  - [x] Receive continuous RTCarInfo structs (~20 Hz)\\\n  - [x] Periodic keepalive: re-send SUBSCRIBE_UPDATE every 10s\\\n  - [x] Remove port 5005 (Lua plugin) — **DONE, abandoned**\\\n  - [x] Make port 10101 (Telemetry Tool) optional — **DONE, shows as \\\"not available\\\" if bind fails**\\\n  - [ ] Configurable via env vars or config file\\\n  - [ ] Verify KSUDP streaming works with live AC session\\\n\\\n- **1.1 Binary parsers — START NOW**\\\n  - [ ] `parser-ksudp` for port 9996 (RTCarInfo struct from official Google Doc)\\\n  - [ ] `parser-telemetry-tool` for port 10101 (struct layout documented in Python source)\\\n  - [ ] Map both to generic `TelemetryFrame` model\\\n\\\n- **1.2 REMOVED** (was Lua plugin — abandoned, see Section 4)\\\n\\\n- **1.3 Telemetry Tool — WORKING, OPTIONAL**\\\n  - [x] Added `[TELEMETRY_TOOL_PLUGIN] ACTIVE=1` to `python.ini`\\\n  - [x] **Verified** — 6.3 MB of valid binary data captured on port 10101\\\n  - [ ] Confirm data includes wheel slip, tyre temps, all cars (parse the binary dump)\\\n  - [x] Make Telemetry Tool feed optional in rusty-telemetry (v0.3.0)\\\n\\\n- **1.4 Workspace refactor**\\\n  - [ ] Split into Cargo workspace with `core`, `receiver`, parsers\\\n  - [ ] SQLite database with migrations\\\n  - [ ] Basic REST API endpoints\\\n\\\n- **1.5 DR2.0 + PCARS parsers** (deferred until AC feeds work)\\\n\\\n### Phase 2: Data Analysis & Storage\\\n- Plausibility engine: cross-reference feeds\\\n- Derived metrics: curvature (G_LAT / v²), slip ratios, derivatives\\\n- Track mapping from world position data\\\n- Reference lap system\\\n\\\n### Phase 3: Web Visualization\\\n- Real-time telemetry dashboard\\\n- Track map with racing line (world position)\\\n- Lap comparison overlays\\\n\\\n### Phase 4: AI Coaching with GLM-5.1\\\n- Basic coaching from KSUDP feed (braking, lap consistency, racing line)\\\n- Advanced coaching from Telemetry Tool feed (traction, tyre strategy, multi-car)\\\n\\\n---\\\n\\\n## 9. Development Tools & References\\\n\\\n| Component | IDE | Project Path |\\\n|---|---|---|\\\n| `rusty-telemetry` | RustRover | `~/RustroverProjects/rusty-telemetry` |\\\n| `ac-telemetry-plugin` | IntelliJ IDEA + EmmyLua | `~/IdeaProjects/ac-telemetry-plugin` (ARCHIVED) |\\\n\\\n### Key References\\\n\\\n| Resource | URL | Description |\\\n|---|---|---|\\\n| AC Remote Telemetry Protocol | https://docs.google.com/document/d/1KfkZiIluXZ6mMhLWfDX1qAGbvhGRC3ZUzjVIt5FQpp4/pub | Official AC KSUDP handshake and telemetry protocol by Kunos |\\\n| Telemetry Tool Plugin | https://www.racedepartment.com/downloads/telemetry-tool-for-ac.37231/ | Third-party Python plugin for AC |\\\n| Telemetry Tool Plugin Source | `<AC>/apps/python/Telemetry_Tool_plugin/Telemetry_Tool_plugin.py` | Packet structure defined in `getBytesForTelemetry()` |\\\n| Custom Shaders Patch (CSP) | https://github.com/ac-custom-shaders-patch | CSP Lua source code, examples, and SDK |\\\n\\\n---\\\n\\\n## 10–12. [Unchanged — Rally, References, Metrics, Game Priority]\\\n\\\n*(See previous versions of this note\"],[1,\"\\\n  - [x] 3-phase handshake for KSUDP\\\n  - [x] Remove port 5005 (Lua plugin) — abandoned\\\n  - [x] Make port 10101 (Telemetry Tool) optional\\\n  - [ ] Configurable via env vars or config file\\\n  - [ ] Verify KSUDP streaming works with live AC session\\\n\\\n- **1.1 Binary parsers — IMPLEMENTED IN v0.3.0 + v0.4.0**\\\n  - [x] `parser-ksudp` for port 9996 (RTCarInfo struct, 6 unit tests)\\\n  - [x] `parser-telemetry-tool` for port 10101 (357-byte driver struct, 7 unit tests)\\\n  - [x] `binary_utils` shared module for safe packet reading\\\n  - [x] `parser-pcars` for port 5606 (auto-detect V1/V2, 5 unit tests)\\\n  - [x] Map all parsers to generic `TelemetryFrame` model\\\n  - [x] `parse_first_driver()` optimization for Telemetry Tool\\\n\\\n- **1.2 REMOVED** (was Lua plugin — abandoned, see Section 4)\\\n\\\n- **1.3 Telemetry Tool — WORKING, OPTIONAL**\\\n  - [x] Verified — 6.3 MB of valid binary data captured on port 10101\\\n  - [x] Binary parser implemented with validation checks\\\n  - [x] `parse_first_driver()` avoids unnecessary Vec allocation\\\n  - [x] Made optional in rusty-telemetry (shows as \\\"not available\\\" if bind fails)\\\n\\\n- **1.4 PCARS Support — IMPLEMENTED IN v0.4.0**\\\n  - [x] Auto-detect PCARS 1 (1367-byte packets) vs PCARS 2 (559-byte packets)\\\n  - [x] Parse V1 telemetry (speed, RPM, gear, inputs, tyres, engine, weather)\\\n  - [x] Parse V2 car physics (speed, RPM, gear, inputs, tyres, 3D position)\\\n  - [x] PCARS-specific fields in TelemetryFrame (oil/water temp, damage, weather)\\\n  - [x] Port 5606 as optional bind-listen feed (like Telemetry Tool)\\\n  - [x] 5 unit tests for parser\\\n\\\n- **1.5 Code Quality — IMPLEMENTED IN v0.3.0 + v0.4.0**\\\n  - [x] Mutex not held during TUI rendering (clone FeedState before draw)\\\n  - [x] Parsing moved outside mutex lock (minimizes contention)\\\n  - [x] `expect()` replaced with graceful error handling on env vars\\\n  - [x] Shared `binary_utils` module eliminates parser duplication\\\n  - [x] Dead code removed (unused variants, methods)\\\n  - [x] `data_rate_kbps` renamed to `data_rate_kb_s` (correct unit)\\\n  - [x] 32 unit tests total (14 feed_state + 6 ksudp + 7 telemetry_tool + 5 pcars)\\\n\\\n- **1.6 Workspace refactor** (deferred)\\\n  - [ ] Split into Cargo workspace with `core`, `receiver`, parsers\\\n  - [ ] SQLite database with migrations\\\n  - [ ] Basic REST API endpoints\\\n\\\n---\\\n\\\n## 10–12. [Unchanged — Rally, References, Metrics]\\\n\\\n*(See note history\"],[0,\" for\"]],\"start1\":10784,\"start2\":10784,\"length1\":3261,\"length2\":2360},{\"diffs\":[[0,\" log\"],[-1,\" (check for \\\"RemoteTelemetryUDP\\\", Python errors)\"],[0,\"\\\n├──\"]],\"start1\":13473,\"start2\":13473,\"length1\":56,\"length2\":8},{\"diffs\":[[0,\"``\\\n\\\n\"],[-1,\"**In-game debugging:**\\\n- **Lua Debug App** — app launcher → developer section → shows `ac.debug()` and `ac.log()` live\\\n- **AC console** — backtick/tilde key → shows `ac.console()` output\\\n\\\n### Port 9996 (KSUDP) — 3-Phase Handshake Protocol\\\n- AC binds to port 9996 as a **LISTENER** at startup: `RemoteTelemetryUDP :: creating socket on port 9996`\\\n- External tools must **register** via 3-phase handshake:\\\n  1. Send HANDSHAKE `{1, 1, 0}` (12 bytes) to `:9996`\\\n  2. Receive 408-byte HandshakerResponse (UTF-16LE car/driver/track + identifier=4242)\\\n  3. Send SUBSCRIBE_UPDATE `{1, 1, 1}` (12 bytes) to `:9996`\\\n  4. Receive continuous RTCarInfo structs (~20 Hz)\\\n- No configuration file controls the destination — it's discovered dynamically\\\n- ⚠️ If another process binds to 9996 first, AC fails: `ERROR BINDING LISTENER SOCKET`\"],[1,\"### Port 9996 (KSUDP) — 3-Phase Handshake Protocol\\\n- AC binds to port 9996 as a **LISTENER** at startup\\\n- External tools must **register** via 3-phase handshake\"],[0,\"\\\n- *\"]],\"start1\":13576,\"start2\":13576,\"length1\":830,\"length2\":168},{\"diffs\":[[0,\") — \"],[-1,\"WORKING ✅ (OPTIONAL)\"],[1,\"OPTIONAL ✅\"],[0,\"\\\n- C\"]],\"start1\":13880,\"start2\":13880,\"length1\":28,\"length2\":18},{\"diffs\":[[0,\"`\\\n- \"],[-1,\"Sends to 127.0.0.1:10101 (configurable in config.ini)\\\n- Send-only: unbound socket, no port conflict possible\\\n- Confirmed working: 6.3 MB captured in 2026-06-03 session\\\n- **Optional** — if rusty-telemetry can't bind :10101, it shows as \\\"not available\\\" and continues\\\n\\\n### Port 5005 (AC Lua Plugin) — REMOVED in v0.3.0\\\n- Feed completely removed from rusty-telemetry\\\n- CSP lacks compiled LuaSocket C core (`socket/core.dll` / `socket.core.so`)\\\n- If more data needed: use AC's Python plugin system (same as Telemetry Tool)\\\n\\\n---\\\n\\\n## 14. Session Diagnostics\\\n\\\n### Session 2026-06-03 ~09:45 — Initial Capture\\\n\\\n| Port | Feed | File | Size | Status |\\\n|------|------|------|------|--------|\\\n| 9996 | KSUDP | `feed_9996_ksudp.bin` | 0 bytes | ❌ AC failed to bind (rusty-telemetry had the port) |\\\n| 10101 | Telemetry Tool | `feed_10101_telemetry_tool.bin` | 4.55 MB | ✅ Valid binary telemetry |\\\n| 5005 | AC Lua Plugin | `feed_5005_ac_plugin.json` | 0 bytes | ❌ Script failed to init |\\\n\\\n**Key findings:**\\\n- Port 9996 port conflict: rusty-telemetry bound first → AC couldn't start RemoteTelemetryUDP\\\n- Port 5005: CSP `impl.: null` → Lua script failed at `require(\\\"socket\\\")`\\\n- Port 10101: Confirmed working, Python plugin sends valid data\\\n\\\n### Session 2026-06-03 19:32 — Investigation Results\\\n\\\n| Port | Feed | File | Size | Status |\\\n|------|------|------|------|--------|\\\n| 9996 | KSUDP | `feed_9996_ksudp.bin` | 5,768 bytes | ⚠️ Partial — 14 × 408-byte handshake responses only, no continuous stream |\\\n| 10101 | Telemetry Tool | `feed_10101_telemetry_tool.bin` | 6,335,955 bytes | ✅ Valid binary telemetry |\\\n| 5005 | AC Lua Plugin | `feed_5005_ac_plugin.json` | 0 bytes | ❌ Abandoned — CSP lacks socket.core |\\\n\\\n**KSUDP Analysis**:\\\n- AC successfully bound to port 9996 (no error in log)\\\n- rusty-telemetry sent 4-byte handshake (wrong, should be 12 bytes)\\\n- AC responded with valid 408-byte HandshakerResponse per attempt (identifier=4242 ✓, version=1 ✓)\\\n- 14 responses in 3 minutes = 1 per 10-second handshake interval\\\n- Never sent SUBSCRIBE_UPDATE → AC never started continuous streaming\\\n- **Fixed in v0.3.0**: proper 12-byte 3-phase handshake\\\n\\\n**Lua Plugin Root Cause**:\\\n- `require(\\\"socket\\\")` → loads `socket.lua` → `require(\\\"socket.core\\\")` → FAILS (no C library)\\\n- CSP's `extension/internal/lua-shared/socket.lua` exists but `socket/core.dll` does not\\\n- `type(ac)` is `\\\"userdata\\\"` not `\\\"table\\\"`, so diagnostic `ac.log()` calls were bypassed to `print()` which goes nowhere\\\n- Decision: abandoned, removed from rusty-telemetry v0.3.0\\\n\\\n### Startup Order for Next Session\\\n\\\n1. **Start AC first** — let it bind port 9996\\\n2. **Start rusty-telemetry after AC loads** — binds to 10101 (optional, won't fail if AC isn't ready)\\\n3. KSUDP: rusty-telemetry performs 3-phase handshake to AC's port 9996 automatically\\\n4. Telemetry Tool: if plugin is installed and configured, data appears on :10101\\\n\\\n---\\\n\\\n*Last updated: 2026-06-04 — v0.3.0: KSUDP 3-phase handshake implemented, Lua plugin removed, Telemetry Tool made optional*\\\n*Next: verify KSUDP streaming with live AC session, implement binary parsers\"],[1,\"**Optional** — if rusty-telemetry can't bind :10101, continues without it\\\n\\\n### Port 5606 (PCARS) — Bind/Listen ✅ (IMPLEMENTED v0.4.0)\\\n- PCARS sends to a configured target IP:port (default 5606)\\\n- rusty-telemetry binds to :5606 and listens\\\n- Configure in PCARS: Options → System → UDP Protocol → set target IP/port\\\n- PCARS 1: set format to \\\"Project CARS\\\"\\\n- PCARS 2: set format to \\\"2\\\" for native format, or \\\"1\\\" for PCARS1-compatible\\\n- **Optional** — if bind fails, shows as \\\"not available\\\" and continues\\\n\\\n### Port 5005 (AC Lua Plugin) — REMOVED in v0.3.0\\\n- Abandoned — CSP lacks compiled LuaSocket C core\\\n\\\n---\\\n\\\n## 14. Session Diagnostics\\\n\\\n### Session 2026-06-03 ~09:45 — Initial Capture\\\n*(See note history for details)*\\\n\\\n### Session 2026-06-03 19:32 — Investigation Results\\\n*(See note history for details)*\\\n\\\n### Startup Order for Next Session\\\n\\\n1. **Start game first** — let it initialize its UDP listeners/senders\\\n2. **Start rusty-telemetry after game loads**\\\n3. KSUDP: rusty-telemetry performs 3-phase handshake to AC's port 9996 automatically\\\n4. Telemetry Tool: if plugin is installed and configured, data appears on :10101\\\n5. PCARS: if game is configured to send to your IP:5606, data appears automatically\\\n\\\n---\\\n\\\n## 15. Change Log\\\n\\\n### v0.4.0 (2026-06-04) — PCARS Support + Code Quality\\\n\\\n**New Features:**\\\n- Added PCARS 1 & 2 UDP telemetry parser (port 5606) with auto-detection\\\n- Auto-detects PCARS1 (1367-byte V1 format) vs PCARS2 (559-byte V2 format)\\\n- Maps PCARS-specific data: engine temps, damage, weather, tyre wear, 3D position\\\n- PCARS feed appears as third panel in TUI (optional, degrades gracefully)\\\n\\\n**Code Quality:**\\\n- Created shared `binary_utils` module — eliminates duplicated read helpers across parsers\\\n- All read helpers use safe `get()` bounds checking (return zero fallback instead of panicking)\\\n- Moved parsing outside mutex lock to minimize contention with TUI render thread\\\n- Added `parse_first_driver()` for Telemetry Tool — avoids unnecessary multi-driver Vec allocation\\\n- Removed dead code: `speed_display()`, `ValidationFailed` variant, `NoDrivers` variant\\\n- Removed unused `serde` dependency from Cargo.toml\\\n- Renamed `data_rate_kbps` → `data_rate_kb_s` (correct unit: kilobytes/sec)\\\n- 32 unit tests (14 feed_state + 6 ksudp + 7 telemetry_tool + 5 pcars)\\\n\\\n### v0.3.0 (2026-06-04) — TUI + Binary Parsers + Handshake Fix\\\n\\\n**Major Changes:**\\\n- Replaced stdout data dump with ratatui/crossterm TUI\\\n- Implemented KSUDP 3-phase handshake protocol (was broken — only sent 4-byte handshake)\\\n- Added binary parsers for KSUDP (port 9996) and Telemetry Tool (port 10101)\\\n- Added ring-buffer frequency calculation and feed health state machine\\\n- Made Telemetry Tool feed optional (degrades gracefully if bind fails)\\\n- Removed port 5005 (Lua plugin — abandoned)\\\n- Per-feed TUI panels with health indicators, frequency, data rate, telemetry snapshot\\\n\\\n---\\\n\\\n*Last updated: 2026-06-04 — v0.4.0: PCARS support added, code quality improvements*\\\n*Next: verify KSUDP streaming with live AC session, verify PCARS with live game, workspace refactor\"],[0,\"*\"]],\"start1\":14016,\"start2\":14016,\"length1\":3079,\"length2\":3060}]"
metadata_diff: {"new":{},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2026-06-04T07:58:00.852Z
created_time: 2026-06-04T07:58:00.852Z
type_: 13