Skip to content

Commit c709b99

Browse files
committed
initial dbc integration - backend and frontend!
1 parent 52529af commit c709b99

File tree

5 files changed

+80
-22
lines changed

5 files changed

+80
-22
lines changed

backend/src/http_server.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
data_dir = os.environ.get("DATA_DIR", "")
77
parquet_files = set()
88
if not os.path.isdir(data_dir):
9-
raise ValueError("Invalid recording dir: ", data_dir)
9+
raise ValueError("Invalid data dir: ", data_dir)
1010

1111

1212
def check_subdir(d):
@@ -33,12 +33,44 @@ async def available_recordings(request):
3333
routes.static("/api/get-recording", data_dir, show_index=True)
3434

3535

36-
@routes.get("/api/get-dbc/{car}")
36+
# The fs-data repo is structured such that differen't Parquet recordings are
37+
# "associated" with different DBC files, where the "closest" .dbc files are
38+
# correct and if the current directory doesn't have any we keep checking its
39+
# parents. So basically just walk up the directory tree until we find a .dbc
40+
@routes.get("/api/get-dbc-for-recording/{recording:.*}")
3741
async def get_dbc(request: web.Request):
38-
car = request.match_info.get("car")
39-
db = cantools.db.database.Database()
40-
41-
return web.json_response(response_obj)
42+
recording = request.match_info.get("recording")
43+
if not recording:
44+
return web.HTTPBadRequest(reason="Invalid recording parameter!")
45+
46+
dbc_path_dir = os.path.dirname(os.path.join(data_dir, recording))
47+
if not os.path.isdir(dbc_path_dir):
48+
return web.HTTPNotFound(reason="Recording path invalid!")
49+
50+
db = cantools.db.Database()
51+
52+
dir_has_dbc = False
53+
while not dir_has_dbc:
54+
dbc_path_dir_items = os.listdir(dbc_path_dir)
55+
dbcs = [f for f in dbc_path_dir_items if os.path.splitext(f)[1] == ".dbc"]
56+
if len(dbcs) > 0:
57+
dir_has_dbc = True
58+
for dbc in dbcs:
59+
db.add_dbc_file(os.path.join(dbc_path_dir, dbc))
60+
print(os.path.join(dbc_path_dir, dbc))
61+
else:
62+
dbc_path_dir = os.path.dirname(dbc_path_dir)
63+
64+
if not dir_has_dbc:
65+
return web.HTTPNotFound(reason="DBC file not found!")
66+
67+
return web.json_response(
68+
[
69+
(s.name, s.unit, (s.minimum, s.maximum))
70+
for m in db.messages
71+
for s in m.signals # m.signal_tree ??
72+
]
73+
)
4274

4375

4476
app = web.Application()

compose.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ services:
3232
- "9000"
3333
environment:
3434
# Directory where http server will look for parquet files (recursively)
35-
RECORDING_DIRS: /recording_dirs
35+
DATA_DIR: /data_dir
3636
volumes:
3737
# ./fs-data is a git submodule, optional
38-
- ./fs-data/FS-2/Parquet:/recording_dirs/FS-2
38+
- ./fs-data/FS-2/Parquet:/data_dir/FS-2
3939

4040
volumes:
4141
caddy_data:

frontend/app/components/Navbar.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import Link from "next/link";
1616
import { useEffect, useRef, useState } from "react";
1717

1818
const subsystems = ["accumulator", "suspension", "imu-data", "faults", "3d-tests"];
19-
import { availableRecordings } from "../data-processing/http";
19+
import { availableRecordings, getDBCForRecording } from "../data-processing/http";
2020
import { useDataMethods } from "../data-processing/DataMethodsProvider";
2121
import AutocompleteSearchbar from "./Autocomplete";
2222

@@ -44,7 +44,7 @@ function createFileTree(paths: string[] | undefined) {
4444
const isLeaf = i === root.length - 1;
4545

4646
let existingNode = currentLevel.find(
47-
(node: pathType) => node.value === currentPath
47+
(node: pathType) => node.value === currentPath,
4848
);
4949

5050
if (!existingNode) {
@@ -165,9 +165,17 @@ export default function Navbar() {
165165
if (tree.hoveredNode?.includes("/")) {
166166
setFileName(tree.hoveredNode);
167167
setLoading(true);
168-
await switchToRecording(tree.hoveredNode);
168+
switchToRecording(tree.hoveredNode);
169169
setLoading(false);
170170
close();
171+
172+
console.log(
173+
`Associated DBC for ${tree.hoveredNode}:`,
174+
await getDBCForRecording(
175+
tree.hoveredNode,
176+
isProduction,
177+
),
178+
);
171179
}
172180
}}
173181
className={

frontend/app/data-processing/DataMethodsProvider.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,8 @@ export function DataMethodsProvider({ children }: PropsWithChildren) {
289289
reset();
290290

291291
const table = await getRecording(filename);
292+
if (!table) return;
293+
292294
let arraysTyped = {} as DataArraysTyped;
293295
// TODO: Make this actually use the schema of the incoming
294296
// websocket stream!!! This assumes all columns are present
@@ -309,6 +311,11 @@ export function DataMethodsProvider({ children }: PropsWithChildren) {
309311
n == 0.0 ? NaN : n
310312
);
311313

314+
// TODO: somehow some BigInt64Array's are getting passed through to lcjs
315+
// even though we have the claude filtering in http.ts?? Try out
316+
// "2025-01-21-IMU-Calibration.parquet" which fails every time. Also,
317+
// uncommenting the above two lines changes the error?? TODO: investigate.
318+
312319
numRowsRef.current = table.numRows;
313320
subscriptionsNumRows.current.forEach((s) => s(numRowsRef.current));
314321

frontend/app/data-processing/http.ts

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as arrowJSFFI from "arrow-js-ffi";
66
import { DataArrays, DataRow, nullDataArrays, schema } from "./datatypes";
77
import { Dispatch, RefObject, SetStateAction } from "react";
88
import { RESPONSE_LIMIT_DEFAULT } from "next/dist/server/api-utils";
9+
import { Table } from "apache-arrow";
910

1011
// TODO(jack): this setup is kinda bad cuz every time the function is called it
1112
// tried all URLs, including bad ones, which cause Network Errors to be thrown
@@ -66,26 +67,36 @@ async function tryFetch(path: string, production: boolean) {
6667
}
6768
}
6869

69-
export async function availableRecordings(production: boolean): Promise<string[] | null> {
70-
// console.log(process.env.NODE_ENV);
71-
return tryFetch("/api/available-recordings", production).then((response) =>
72-
response != null ? response.json() : null
70+
export async function getDBCForRecording(
71+
filepath: string,
72+
production: boolean,
73+
): Promise<string[] | null> {
74+
return tryFetch(`/api/get-dbc-for-recording/${filepath}`, production).then((resp) =>
75+
resp?.json(),
7376
);
7477
}
7578

76-
export async function getRecording(filepath: string) {
79+
export async function availableRecordings(production: boolean): Promise<string[] | null> {
80+
return tryFetch("/api/available-recordings", production).then((resp) => resp?.json());
81+
}
82+
83+
export async function getRecording(filepath: string): Promise<Table<DataRow> | undefined> {
7784
// Initializes WebAssembly memory for parquet-wasm, and gets a reference to it
7885
await wasmInit();
7986
const WASM_MEMORY = wasmMemory();
8087

81-
const parquet = await tryFetch(`/api/get-recording/${filepath}`, true).then((resp) =>
82-
resp ? resp.arrayBuffer() : null
83-
);
84-
const arrowTableWasm = readParquet(new Uint8Array(parquet!)).intoFFI();
88+
const resp = await tryFetch(`/api/get-recording/${filepath}`, false)
89+
const arrayBuffer = await resp?.arrayBuffer();
90+
if (!arrayBuffer) {
91+
console.error("Response is not a valid ArrayBuffer!");
92+
return;
93+
}
94+
95+
const arrowTableWasm = readParquet(new Uint8Array(arrayBuffer)).intoFFI();
8596
const arrowTable = arrowJSFFI.parseTable<DataRow>(
8697
WASM_MEMORY.buffer,
8798
arrowTableWasm.arrayAddrs(),
88-
arrowTableWasm.schemaAddr()
99+
arrowTableWasm.schemaAddr(),
89100
);
90101

91102
// Free arrow table in wasm memory
@@ -105,7 +116,7 @@ export async function initRecordingSource(
105116
data: RefObject<DataArrays>,
106117
dataTrimmed: RefObject<DataArrays>,
107118
setNumRows: Dispatch<SetStateAction<number>>,
108-
viewLength: number
119+
viewLength: number,
109120
) {
110121
const arrowTable = (await getRecording(filepath))!;
111122

0 commit comments

Comments
 (0)