File size: 3,159 Bytes
af286ff
92300b7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
af286ff
92300b7
af286ff
 
 
 
 
92300b7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import html
from dash import ClientsideFunction, Input, Output, State, dcc, no_update  #type: ignore
from .dash_data import load_session
from .dash_figures import multiline, track_map

def register_callbacks(app, cfg: dict):
    sessions_dir = cfg["output"]["sessions_dir"]
    theme        = cfg["theme"]
    chart_cfg    = cfg["charts"]
    tabs_cfg     = cfg["tabs"]
    tab_plots    = {t["id"]: t.get("plots", []) for t in tabs_cfg}

    @app.callback(Output("session-header", "children"), Input("session-select", "value"))
    def update_header(session):
        if not session:
            return ""
        _, info = load_session(sessions_dir, session)
        track = html.escape(info["track"])
        if info.get("track_configuration"):
            track += f" ({html.escape(info['track_configuration'])})"
        car      = html.escape(str(info['car']))
        max_rpm  = html.escape(str(info['max_rpm']))
        max_fuel = html.escape(str(info['max_fuel_kg']))
        return f"Car: {car}  |  Track: {track}  |  Max RPM: {max_rpm}  |  Fuel: {max_fuel} kg"

    # show/hide the two content areas based on active tab
    @app.callback(
        Output("tab-content",     "style"),
        Output("map-tab-content", "style"),
        Input("tabs", "value"),
    )
    def toggle_content_areas(tab):
        show = {"display": "block", "marginTop": "12px"}
        hide = {"display": "none",  "marginTop": "12px"}
        if tab == "map":
            return hide, show
        return show, hide

    # regular (non-map) tabs
    @app.callback(
        Output("tab-content", "children"),
        Input("session-select", "value"),
        Input("tabs", "value"),
    )
    def update_tab(session, tab):
        if not session or tab == "map":
            return ""
        df, _ = load_session(sessions_dir, session)
        return [
            dcc.Graph(
                id=f"g-{p['id']}",
                figure=multiline(df, p, theme, chart_cfg),
                config={"displayModeBar": False},
            )
            for p in tab_plots.get(tab, [])
        ]

    # rebuild map figure when session or colour changes
    @app.callback(
        Output("g-map",        "figure"),
        Output("map-slider",   "max"),
        Output("map-pos-store","data"),
        Input("session-select",   "value"),
        Input("map-color-select", "value"),
    )
    def update_map(session, color_col):
        if not session:
            return no_update, no_update, no_update
        df, _ = load_session(sessions_dir, session)
        if "pos_x" not in df.columns or "pos_z" not in df.columns:
            return no_update, no_update, no_update
        fig   = track_map(df, 0, color_col or "speed_kmh", theme)
        store = {"pos_x": df["pos_x"].tolist(), "pos_z": df["pos_z"].tolist()}
        return fig, len(df) - 1, store

    # move car marker — pure JS, no server round-trip
    app.clientside_callback(
        ClientsideFunction(namespace="map", function_name="move_marker"),
        Output("map-dummy",    "data"),
        Input("map-slider",    "value"),
        State("map-pos-store", "data"),
        prevent_initial_call=True,
    )