Implement a modular vehicle decoding system

- Add Komatsu specific rules
- Possibility to expand to any brand
This commit is contained in:
2026-07-23 00:03:00 +02:00
parent fab448785b
commit dd38751a7c
4 changed files with 253 additions and 0 deletions
+67
View File
@@ -11,6 +11,7 @@ import dash
from dash import dcc, html, Input, Output, State
import dash_bootstrap_components as dbc
import numpy as np
import pandas as pd
from parser import parse_log, parse_csv
from decoder import decode_j1939_frames
@@ -20,6 +21,7 @@ from stats.frequency import calculate_frequency, plot_frequency
from stats.correlation import calculate_correlation, plot_correlation_heatmap
from stats.entropy import calculate_byte_entropy, plot_entropy_heatmap
from logs.view import get_logs_table_component, prepare_logs_data
from vehicle import get_vehicle_module
RAW_LOG_DIR = "data/logs"
PAGE_SIZE = 25000
@@ -131,6 +133,18 @@ app.layout = dbc.Container([
dbc.Tab(label="Overview", tab_id="overview", children=[
html.Div(id="overview-content")
]),
dbc.Tab(label="Vehicles", tab_id="vehicles", children=[
dbc.Row([
dbc.Col(html.Label("Select Vehicle:", className="mt-2"), width="auto"),
dbc.Col(dcc.Dropdown(
id='vehicles-vehicle-selector',
options=[{'label': v, 'value': v} for v in DATA.keys()],
value=list(DATA.keys())[0] if DATA else None,
clearable=False
), width=3, className="me-4"),
], className="mb-3 mt-3", align="end"),
html.Div(id='vehicles-content', className="mt-3")
]),
dbc.Tab(label="Logs", tab_id="logs", children=[
dbc.Row([
dbc.Col(html.Label("Select Vehicle:", className="mt-2"), width="auto"),
@@ -391,5 +405,58 @@ def update_corr(method, target, vehicle, bus, tab):
return plot_correlation_heatmap(corr_df, target_id=target_id, title=title)
@app.callback(
Output('vehicles-content', 'children'),
Input('vehicles-vehicle-selector', 'value')
)
def render_vehicles(vehicle):
"""Render small graph boxes for every decoded signal, combining both buses."""
if not vehicle or vehicle not in DATA:
return html.Div("No data available", className="text-muted")
brand = VEHICLE_META.get(vehicle, {}).get("brand", "")
vehicle_module = get_vehicle_module(brand)
dfs = []
for bus_df in DATA[vehicle].values():
dfs.append(bus_df)
if not dfs:
return html.Div("No data available", className="text-muted")
df = pd.concat(dfs, ignore_index=True)
if 'Timestamp' in df.columns:
df = df.sort_values('Timestamp', kind='stable').reset_index(drop=True)
cards = []
for nid, frame_def in vehicle_module.DECODER_RULES.items():
decoded = vehicle_module.decode_dataframe(df, frame_def.can_id)
for sig in frame_def.signals:
unit_str = f" ({sig.unit})" if sig.unit else ""
title = f"{frame_def.can_id} - {sig.name}{unit_str}"
fig = vehicle_module.plot_signal(decoded, sig.name, title=title)
card = dbc.Card([
dbc.CardBody([
dcc.Graph(figure=fig, config={'displayModeBar': False},
style={'height': '280px'})
], className="p-2"),
], className="shadow-sm border-0 h-100")
cards.append(
dbc.Col(card, xs=12, sm=6, md=4, lg=3, className="mb-3")
)
if not cards:
return html.Div(
"No decoded signals available. Add rules in the vehicle module.",
className="text-muted"
)
return dbc.Row(cards)
if __name__ == '__main__':
app.run(debug=False)