Skip to content

Commit a6ca783

Browse files
committed
Merge origin/mm/autox-telemetry — bring event_mode end-to-end into trackside
Stacks PR #289 (VCU mm/autox firmware: event-mode params per session + regen-off-except-endurance) + PR #291 (telemetry & dash for VCU event_mode toggle) into telemetry/trackside-live-fixes. Result is the READ path for Controls.event_mode (which params table the VCU is running) end-to-end: VCU 0x1C7 byte 6 -> cand decode -> publishd -> kafka-bridge flatten -> dashd CanData.event_mode -> driver-facing MODE pill Plus bridge now emits line_lock_enabled and bse3 (round-tripped through the schema-gen chain so they're in proto/sql/Prisma/ORM/generated_mapping/etc.). 0x1C9 VCU Energy Estimate packet (net_energy + regen_energy) also lands. Conflict resolutions: - BEVO/dashd/frontend/src/screens/ScreenOne.tsx: kept HEAD's lap-card + theme imports and ADDED the new eventModeLabel import from PR #291. - viewer_tool/src/app/page.tsx, trackside-live/TracksideApp.tsx, trackside-live/trackside.css: PR #291 doesn't actually modify these (verified with git diff origin/main...origin/mm/autox-telemetry -- those files). The conflicts were spurious 3-way-merge noise from the heavy divergence — took HEAD wholesale to preserve all the recent trackside polish work. Write path (Pi -> VCU mode switch) is still deferred per the bevo-mode-set-tx memory note — needs the four pieces and is blocked on VCU firmware.
2 parents 4550fe5 + 7f0c02b commit a6ca783

63 files changed

Lines changed: 3749 additions & 442 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/schema-drift.yml

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
name: schema-drift
2+
run-name: Schema codegen drift check (${{ github.actor }})
3+
4+
# Regenerates every schema/CAN artifact from its source (can_packets.csv +
5+
# can_packets.proto) and fails if the committed artifacts are out of sync.
6+
# This makes schema / telemetry-signal updates dead-easy: change the source,
7+
# run the generators, commit — and CI guarantees nothing drifts.
8+
9+
on:
10+
pull_request:
11+
types: [opened, synchronize, reopened]
12+
paths:
13+
- 'drivers/longhorn-lib/config/**'
14+
- 'drivers/longhorn-lib/protobuf/**'
15+
- 'drivers/longhorn-lib/scripts/**'
16+
- 'telemtry/scripts/**'
17+
- 'telemtry/stack/ingest/**'
18+
- 'telemtry/stack/kafka/proto/**'
19+
- 'telemtry/analysis/database/viewer_tool/prisma/**'
20+
- 'telemtry/analysis/database/viewer_tool/protobuf/**'
21+
- 'telemtry/analysis/sql_utils/models.py'
22+
- 'BEVO/nonhermetic/assets/**'
23+
- 'BEVO/codegen.py'
24+
- 'BEVO/generated_mapping.rs'
25+
- 'BEVO/loggerd/**'
26+
- 'BEVO/config.rs'
27+
- '.github/workflows/schema-drift.yml'
28+
workflow_dispatch:
29+
30+
jobs:
31+
schema-drift:
32+
name: Schema codegen drift check
33+
runs-on: ubuntu-latest
34+
env:
35+
PYTHONDONTWRITEBYTECODE: "1"
36+
steps:
37+
- name: Checkout
38+
uses: actions/checkout@v4
39+
with:
40+
fetch-depth: 0
41+
42+
- name: Set up Python
43+
uses: actions/setup-python@v5
44+
with:
45+
python-version: "3.11"
46+
47+
- name: Install protoc + python deps
48+
run: |
49+
sudo apt-get update
50+
sudo apt-get install -y protobuf-compiler
51+
python -m pip install --upgrade pip
52+
python -m pip install "protobuf>=6,<7"
53+
54+
- name: Regenerate all schema artifacts from source
55+
env:
56+
BUILD_WORKSPACE_DIRECTORY: ${{ github.workspace }}
57+
run: |
58+
set -euxo pipefail
59+
# CSV -> proto (assigns/back-annotates stable proto field ids)
60+
python3 drivers/longhorn-lib/scripts/update_can_proto.py
61+
# CSV -> can.json + descriptor + Rust mapping
62+
bash BEVO/nonhermetic/sync_assets.sh
63+
# proto -> SQL / Prisma / SQLAlchemy models / dataclasses / sensor.proto
64+
bash telemtry/scripts/sync_schema.sh Orion
65+
66+
- name: Fail if regenerated artifacts differ from committed
67+
run: |
68+
# sensor_data.desc is a binary protoc intermediate whose bytes vary by
69+
# protoc version; exclude it and gate only on the deterministic text
70+
# artifacts (proto, can.json, mapping.rs, SQL, prisma, models, ...).
71+
if ! git diff --exit-code -- . ':(exclude)BEVO/sensor_data.desc'; then
72+
{
73+
echo "### ❌ Schema artifacts are out of sync with their sources"
74+
echo ""
75+
echo "A source (\`can_packets.csv\` / \`can_packets.proto\`) changed but the"
76+
echo "generated artifacts weren't regenerated. Run locally and commit the result:"
77+
echo ""
78+
echo '```bash'
79+
echo 'export BUILD_WORKSPACE_DIRECTORY="$PWD"'
80+
echo 'python3 drivers/longhorn-lib/scripts/update_can_proto.py'
81+
echo 'bash BEVO/nonhermetic/sync_assets.sh'
82+
echo 'bash telemtry/scripts/sync_schema.sh Orion'
83+
echo '```'
84+
} >> "$GITHUB_STEP_SUMMARY"
85+
echo "::error::Generated schema artifacts are out of sync with their sources. See job summary."
86+
exit 1
87+
fi
88+
89+
loggerd-tests:
90+
name: BEVO loggerd unit tests
91+
runs-on: ubuntu-latest
92+
steps:
93+
- name: Checkout
94+
uses: actions/checkout@v4
95+
96+
- name: Install Rust
97+
uses: dtolnay/rust-toolchain@stable
98+
99+
- name: Install protoc
100+
run: |
101+
sudo apt-get update
102+
sudo apt-get install -y protobuf-compiler
103+
104+
- name: Test loggerd (schema-driven CSV header behavior)
105+
working-directory: BEVO
106+
env:
107+
PROTOC: /usr/bin/protoc
108+
run: cargo test --bin loggerd

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,7 @@ MODULE.bazel.lock
9696
rust-project.json
9797

9898
BEVO/vpn_creds.txt.claude/
99+
100+
# Python bytecode (schema-gen scripts)
101+
__pycache__/
102+
*.pyc

BEVO/dashd/frontend/src/hooks/useDemoData.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ export function useDemoData(enabled: boolean): DashMessage | null {
170170
const tcLevel = (Math.floor(Date.now() / 5000) % 3) + 1; // 1,2,3
171171
const tcEnabled = (Math.floor(Date.now() / 9000) % 3) !== 0; // ~66% on
172172
const regenEnabled = (Math.floor(Date.now() / 8000) % 4) !== 0; // ~75% on
173+
// Rotate through 1=ACCEL, 2=SKID, 3=AUTOX, 4=ENDUR so each pill
174+
// label is visible during demo.
175+
const eventMode = (Math.floor(Date.now() / 6000) % 4) + 1;
173176

174177
// Pit-diagnostic fakes — derived from the existing sim where
175178
// possible so values move with the rest of the dash.
@@ -232,6 +235,7 @@ export function useDemoData(enabled: boolean): DashMessage | null {
232235
tcLevel,
233236
tcEnabled,
234237
regenEnabled,
238+
eventMode,
235239
apps,
236240
bpps,
237241
brakePressureFront,

BEVO/dashd/frontend/src/screens/ScreenOne.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useSettings } from '../context/SettingsContext';
66
import { effectiveAutoTheme } from '../util/sunCalc';
77
import { LapCardRenderer } from '../LapCardRenderer';
88
import { validateLapCardLayout } from '../dashLayout';
9+
import { eventModeLabel } from '../types/DashData';
910
import './ScreenOne.css';
1011

1112
// Screen One: Main Dashboard (Modern EV Style)
@@ -61,6 +62,11 @@ const ScreenOne: React.FC = () => {
6162
const tcLevel = data?.can.tcLevel ?? null;
6263
const tcEnabled = data?.can.tcEnabled ?? null;
6364
const regenEnabled = data?.can.regenEnabled ?? null;
65+
// Active VCU event mode (Controls.event_mode). 0/null shows "—"; 1-4 map
66+
// to ACCEL/SKID/AUTOX/ENDUR via EVENT_MODE_LABELS in DashData.ts.
67+
const eventMode = data?.can.eventMode ?? null;
68+
const eventModeText = eventModeLabel(eventMode);
69+
const eventModeActive = eventMode !== null && eventMode !== undefined && eventMode !== 0;
6470
// BACKEND TODO: dashd needs MqttData.lapDeltaRate — d(lapDelta)/dt,
6571
// units of seconds per second. Drivers want this as the primary
6672
// glance bar because absolute delta lags. Compute off-car to keep
@@ -589,6 +595,33 @@ const ScreenOne: React.FC = () => {
589595
: regenEnabled ? 'ON' : 'OFF'}
590596
</span>
591597
</div>
598+
599+
{/* MODE pill — which VCU params table is active. Burnt
600+
orange matches TC: same "armed/active" pattern. Shows
601+
the firmware-picked event (ACCEL/SKID/AUTOX/ENDUR);
602+
the driver can't change it here, it's set by which
603+
params header was compiled into the VCU. */}
604+
<div style={{
605+
height: '26px',
606+
padding: '0 14px',
607+
borderRadius: '5px',
608+
border: `2px solid ${eventModeActive ? '#BF5700' : 'var(--card-border-hover)'}`,
609+
background: eventModeActive ? 'rgba(191,87,0,0.20)' : 'var(--card-bg)',
610+
display: 'flex',
611+
justifyContent: 'space-between',
612+
alignItems: 'center',
613+
gap: '8px'
614+
}}>
615+
<span className="label-small" style={{ marginBottom: 0, fontSize: '0.9rem', letterSpacing: '2px' }}>MODE</span>
616+
<span className="value-display" style={{
617+
fontSize: '1.3rem',
618+
fontWeight: 'bold',
619+
lineHeight: 1,
620+
color: eventModeActive ? 'var(--fg-primary)' : 'var(--fg-muted)'
621+
}}>
622+
{eventModeText ?? '—'}
623+
</span>
624+
</div>
592625
</div>
593626

594627
{/* Right cluster: GEAR + HV, mirroring TC/REGEN on the

BEVO/dashd/frontend/src/types/DashData.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,11 @@ export interface CanData {
4040
tcEnabled?: boolean | null; // TC armed?
4141
regenEnabled?: boolean | null; // Regenerative braking enabled?
4242

43+
// Active VCU event mode — which params table the firmware is running.
44+
// Source: Controls.event_mode (byte 6 of 0x1C7 VCU State). See
45+
// EVENT_MODE_LABELS for the enum->label mapping.
46+
eventMode?: number | null;
47+
4348
// Pit-diagnostic fields. Not yet emitted by dashd (per-field BACKEND
4449
// TODOs in PitDiagnostic.tsx); demo hook synthesizes values so the
4550
// layout is testable.
@@ -110,6 +115,22 @@ export interface DashMessage {
110115
layout?: unknown;
111116
}
112117

118+
// VCU event mode enum (matches firmware VCU_DEFAULT_PARAMS + per-event override
119+
// in VCU/firmware/Core/Inc/params/*.h). Anything outside this range is shown
120+
// as the raw integer so a new mode added on the VCU isn't silently dropped.
121+
export const EVENT_MODE_LABELS: Record<number, string> = {
122+
0: "—",
123+
1: "ACCEL",
124+
2: "SKID",
125+
3: "AUTOX",
126+
4: "ENDUR",
127+
};
128+
129+
export function eventModeLabel(mode: number | null | undefined): string | null {
130+
if (mode === null || mode === undefined) return null;
131+
return EVENT_MODE_LABELS[mode] ?? String(mode);
132+
}
133+
113134
// Shutdown circuit / safety-fault items, in the order dashd emits them.
114135
// LEG 1–4 are the hardware shutdown legs from DiagnosticsLow.shutdown_legX;
115136
// BMS / IMD are *_error flags inverted into shutdown convention. Electrical

BEVO/dashd/main.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,12 @@ struct CanData {
177177
/// `regenEnabled` pill.
178178
#[serde(rename = "regenEnabled")]
179179
regen_enabled: Option<bool>,
180+
181+
/// Active VCU event mode (which params table the VCU is running).
182+
/// 0 = unassigned, 1 = acceleration, 2 = skidpad, 3 = autocross, 4 = endurance.
183+
/// Lives on Controls.event_mode (byte 6 of 0x1C7 VCU State).
184+
#[serde(rename = "eventMode")]
185+
event_mode: Option<u8>,
180186
}
181187

182188
#[derive(Serialize, Clone, Default)]
@@ -646,6 +652,10 @@ fn extract_can_data(data: &OrionSensorData, last_qualified_soc: &mut Option<f32>
646652
// confirmed it carries the regen-enabled bit. Plumb to the
647653
// frontend's regenEnabled pill. Lives on the Controls message.
648654
regen_enabled: controls.map(|c| c.line_lock_enabled),
655+
656+
// Wire value is uint8 but the proto declares the field as float
657+
// (matches prndl/soc convention); cast back for the frontend enum.
658+
event_mode: controls.map(|c| c.event_mode as u8),
649659
}
650660
}
651661

BEVO/generated_mapping.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ pub fn update_proto_field_generated(data: &mut OrionSensorData, name: &str, val:
8383
"bse2_disconnect" => _h.bse2_disconnect = val != 0.0,
8484
"bse2_out_range" => _h.bse2_out_range = val != 0.0,
8585
"bse2_v" => _c.bse2_v = val,
86+
"bse3" => _c.bse3 = val,
8687
"bse3_v" => _c.bse3_v = val,
8788
"bus_bar_temp1" => _t.bus_bar_temp1 = val,
8889
"bus_bar_temp2" => _t.bus_bar_temp2 = val,
@@ -99,6 +100,7 @@ pub fn update_proto_field_generated(data: &mut OrionSensorData, name: &str, val:
99100
"direction" => _c.direction = val != 0.0,
100101
"discharge_r_temp" => _t.discharge_r_temp = val,
101102
"enable" => _c.enable = val != 0.0,
103+
"event_mode" => _c.event_mode = val,
102104
"fan_rpm" => _t.fan_rpm = val,
103105
"fl_ride_height" => _d.fl_ride_height = val,
104106
"fl_strain_gauge_v" => _d.fl_strain_gauge_v = val,
@@ -138,6 +140,7 @@ pub fn update_proto_field_generated(data: &mut OrionSensorData, name: &str, val:
138140
"motor_speed" => _c.motor_speed = val,
139141
"motor_temp" => _t.motor_temp = val,
140142
"neg_hv_contactor" => _h.neg_hv_contactor = val != 0.0,
143+
"net_energy" => _c.net_energy = val,
141144
"neutral_output_v" => _p.neutral_output_v = val,
142145
"phase_a_current" => _p.phase_a_current = val,
143146
"phase_b_current" => _p.phase_b_current = val,
@@ -150,6 +153,7 @@ pub fn update_proto_field_generated(data: &mut OrionSensorData, name: &str, val:
150153
"r2d_authorized" => _l.r2d_authorized = val != 0.0,
151154
"r2d_buzzer" => _h.r2d_buzzer = val != 0.0,
152155
"r2d_status" => _l.r2d_status = val != 0.0,
156+
"regen_energy" => _c.regen_energy = val,
153157
"ride_height" => _d.ride_height = val,
154158
"rpm_request" => _c.rpm_request = val,
155159
"rtd_fuse" => _h.rtd_fuse = val != 0.0,

0 commit comments

Comments
 (0)