Skip to content

Commit

Permalink
Select display time
Browse files Browse the repository at this point in the history
Fixes #1114
  • Loading branch information
mpapenbr committed Jul 28, 2024
1 parent 21fa7a8 commit a5e681f
Show file tree
Hide file tree
Showing 15 changed files with 127 additions and 33 deletions.
3 changes: 2 additions & 1 deletion src/components/events/liveEventsGrpc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { useAppDispatch, useAppSelector } from "../../stores";
import { updateFromDriverData } from "../../stores/grpc/slices/availableCarsSlice";
import { updateClassification } from "../../stores/grpc/slices/classificationSlice";
import { updateEvent, updateTrack } from "../../stores/grpc/slices/eventInfoSlice";
import { updateSession } from "../../stores/grpc/slices/sessionSlice";
import { updateRecordstamp, updateSession } from "../../stores/grpc/slices/sessionSlice";

import { CarLaps } from "@buf/mpapenbr_iracelog.community_timostamm-protobuf-ts/iracelog/analysis/v1/car_laps_pb";
import { CarOccupancy } from "@buf/mpapenbr_iracelog.community_timostamm-protobuf-ts/iracelog/analysis/v1/car_occupancy_pb";
Expand Down Expand Up @@ -135,6 +135,7 @@ export const LiveEvents: React.FC = () => {
stateCount++;
// console.log(`state msg: ${stateCount}: ${res.toJsonString().length}`);
dispatch(updateSession({ ...res.session } as Session));
dispatch(updateRecordstamp(res.timestamp!.toDate()));

const pureCarJsonObj = res.cars.map((c) => ({ ...c }));
dispatch(updateClassification(pureCarJsonObj as Car[]));
Expand Down
7 changes: 6 additions & 1 deletion src/components/events/loaderGrpc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ import { initSnapshotData } from "../../stores/grpc/slices/eventSnapshotData";
import { loadedMessages } from "../../stores/grpc/slices/messagesSlice";
import { initialRaceGraph } from "../../stores/grpc/slices/raceGraphSlice";
import { updateRaceOrder } from "../../stores/grpc/slices/raceOrderSlice";
import { updateSession } from "../../stores/grpc/slices/sessionSlice";
import { updateRecordstamp, updateSession } from "../../stores/grpc/slices/sessionSlice";
import { updateSpeedmap } from "../../stores/grpc/slices/speedmapSlice";
import {
updateReplayInfo,
Expand Down Expand Up @@ -106,6 +106,11 @@ export const LoaderPageGrpc: React.FC<MyProps> = (props: MyProps) => {
dispatch(initialRaceGraph(res.analysis?.raceGraph as RaceGraph[]));
// updates from RaceState
dispatch(updateSession(res.state?.session as Session));
const x =
res.event!.replayInfo!.minTimestamp!.toDate().getTime() -
res.event!.replayInfo!.minSessionTime * 1000;
dispatch(updateRecordstamp(new Date(x)));

dispatch(updateClassification(res.state?.cars as Car[]));
dispatch(loadedMessages(res.state?.messages as MessageContainer[]));
// updates from Speedmap
Expand Down
11 changes: 11 additions & 0 deletions src/components/globalSettingsControl.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from "react";
import { useAppDispatch, useAppSelector } from "../stores";
import {
setTheme,
setTimeMode,
toggleCompact,
toggleFilterOrderByPosition,
toggleSyncSelection,
Expand All @@ -27,6 +28,9 @@ export const GlobalSettings: React.FC = () => {
const onGlobalThemeSelect = (e: RadioChangeEvent) => {
dispatch(setTheme(e.target.value));
};
const onGlobalTimeMode = (e: RadioChangeEvent) => {
dispatch(setTimeMode(e.target.value));
};
return (
<>
<Row>
Expand All @@ -44,6 +48,13 @@ export const GlobalSettings: React.FC = () => {
Order cars in filter by race position
</Checkbox>
</p>
<p>
<Radio.Group defaultValue={stateGlobalSettings.timeMode} onChange={onGlobalTimeMode}>
<Radio.Button value="session">Session time</Radio.Button>
<Radio.Button value="sim">Simualtion time</Radio.Button>
<Radio.Button value="real">Real time</Radio.Button>
</Radio.Group>
</p>
<p>
<Radio.Group defaultValue={stateGlobalSettings.theme} onChange={onGlobalThemeSelect}>
<Radio.Button value="light">Light</Radio.Button>
Expand Down
19 changes: 15 additions & 4 deletions src/components/speedmap/laptimeEvolution.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,21 @@ export const LaptimeEvolution: React.FC = () => {
if (!hasLaptime) {
return;
}
const xKey = e.timeOfDay.toString();
var xKey = "";
switch (globalSettings.timeMode) {
case "session":
xKey = secAsHHMM(e.sessionTime);
break;
case "sim":
xKey = secAsHHMM(e.timeOfDay);
break;
case "real":
const d: Date = e.recordStamp?.toDate();
// JS hell when calculating days. In order to use own formatter we need the seconds.
// Note: TZ-offset -60 on GMT+0100 ;)
xKey = secAsHHMM((d.getTime() / 1000 - d.getTimezoneOffset() * 60) % 86400);
break;
}
trackTempData.push({
x: xKey,
"Track temp": parseFloat(e.trackTemp.toPrecision(3)),
Expand Down Expand Up @@ -84,9 +98,6 @@ export const LaptimeEvolution: React.FC = () => {
"Track temp": {
formatter: (d: number) => d.toFixed(1) + "°C",
},
x: {
formatter: (d: number) => secAsHHMM(d),
},
},
animation: false,
};
Expand Down
17 changes: 14 additions & 3 deletions src/components/speedmap/speedinfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,35 @@ import * as React from "react";
import { sprintf } from "sprintf-js";

import { useAppSelector } from "../../stores";
import { lapTimeString, secAsString } from "../../utils/output";
import { lapTimeString, secAsHHMMSS } from "../../utils/output";

export const SpeedInfo: React.FC = () => {
const payload = useAppSelector((state) => state.speedmap);
const carClasses = useAppSelector((state) => state.carClasses);
const trackInfo = useAppSelector((state) => state.eventInfo.track);
const globalSettings = useAppSelector((state) => state.userSettings.global);

const carClassLookup = carClasses.reduce((prev, cur) => {
prev.set(cur.id.toString(), cur.name);
return prev;
}, new Map());

const data = Object.entries(payload.data).map((cur) => {
var xKey = "";
switch (globalSettings.timeMode) {
case "session":
xKey = secAsHHMMSS(payload.sessionTime);
break;
case "sim":
case "real": // don't have real time data, so we use sim time instead
xKey = secAsHHMMSS(payload.timeOfDay);
break;
}
return {
carClass: carClassLookup.get(cur[0]) ?? "CarClass " + cur[0],
avgSpeed: (trackInfo.length / cur[1].laptime) * 3.6,
avgLaptime: cur[1].laptime,
lastRead: payload.timeOfDay,
lastRead: xKey,
};
});
// eslint-disable-next-line @typescript-eslint/ban-types
Expand All @@ -45,7 +56,7 @@ export const SpeedInfo: React.FC = () => {
{
key: "lastRead",
title: "Last update",
render: (d) => secAsString(d.lastRead),
render: (d) => d.lastRead,
width: 20,
align: "right",
},
Expand Down
2 changes: 1 addition & 1 deletion src/components/standings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ export const Standings: React.FC<Props> = (props: Props) => {
};
const getCarNum = getCarNumByIdx;

const sessionTime = sessionInfo.sessionTime;
const sessionTime = sessionInfo.session.sessionTime;

// key: carNum
const carOccLookup = Object.assign({}, ...carOcc.map((v) => ({ [v.carNum]: v })));
Expand Down
6 changes: 4 additions & 2 deletions src/components/stintRanking/rankingSvg.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { StintInfo } from "@buf/mpapenbr_iracelog.community_timostamm-protobuf-t
import { Empty, Tooltip, theme } from "antd";
import _ from "lodash";
import { useAppSelector } from "../../stores";
import { lapTimeString, secAsHHMM } from "../../utils/output";
import { lapTimeString } from "../../utils/output";
import { boxPlotDataFor } from "../live/statsutil";
import { findDriverByStint } from "../live/util";
import StintTooltip from "../nivo/stintTooltip";
Expand All @@ -21,6 +21,7 @@ interface MyProps {
showCars: string[];
hightlightCars: string[];
toggleHighlightCar: (carNum: string) => void;
displayTime: (sec: number) => string;
}

interface IStintInfoExt extends StintInfo {
Expand All @@ -29,6 +30,7 @@ interface IStintInfoExt extends StintInfo {
const { useToken } = theme;
const StintRankingSvg: React.FC<MyProps> = (props: MyProps) => {
const carOccs = useAppSelector((state) => state.carOccupancies);
const globalSettings = useAppSelector((state) => state.userSettings.globalSettings);
const { token } = useToken();
const width = props.width ?? 800;
const height = props.height ?? 600;
Expand Down Expand Up @@ -152,7 +154,7 @@ const StintRankingSvg: React.FC<MyProps> = (props: MyProps) => {
textAnchor="middle"
alignmentBaseline="hanging"
>
{secAsHHMM(d)}
{props.displayTime(d)}
</text>
))}
</g>
Expand Down
20 changes: 16 additions & 4 deletions src/components/weather/weather.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,22 @@ export const WeatherEvolution: React.FC = () => {
let lastTime = snapshots[0].timeOfDay.toString();
const annos = [] as any[];
snapshots.forEach((e) => {
const xKey = e.timeOfDay.toString();
var xKey;
switch (globalSettings.timeMode) {
case "session":
xKey = secAsHHMM(e.sessionTime);
break;
case "sim":
xKey = secAsHHMM(e.timeOfDay);
break;
case "real":
const d: Date = e.recordStamp?.toDate();
// JS hell when calculating days. In order to use own formatter we need the seconds.
// Note: TZ-offset -60 on GMT+0100 ;)
xKey = secAsHHMM((d.getTime() / 1000 - d.getTimezoneOffset() * 60) % 86400);
break;
}

precipitationData.push({
x: xKey,
Precipitation: parseFloat((e.precipitation * 100.0).toPrecision(2)),
Expand Down Expand Up @@ -102,9 +117,6 @@ export const WeatherEvolution: React.FC = () => {

tickCount: 9,
},
x: {
formatter: (d: number) => secAsHHMM(d),
},
},
animation: false,
};
Expand Down
18 changes: 9 additions & 9 deletions src/container/EventHeaderContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const EventHeaderContainer: React.FC = () => {
const sInfo = useAppSelector((state) => state.session);
const eInfo = useAppSelector((state) => state.eventInfo);

if (sInfo.flagState === "") {
if (sInfo.session.flagState === "") {
return <></>;
}
const eventInfo = { name: eInfo.event.name, trackDisplayName: eInfo.track.name };
Expand Down Expand Up @@ -92,11 +92,11 @@ export const EventHeaderContainer: React.FC = () => {
return v + "";
}
};
return "Track " + trackCondidtion(sInfo.trackWetness);
return "Track " + trackCondidtion(sInfo.session.trackWetness);
};

let flagBackground = "";
switch (sInfo.flagState) {
switch (sInfo.session.flagState) {
case "CHECKERED":
flagBackground = "iracelog-background-checkered";
break;
Expand All @@ -111,15 +111,15 @@ export const EventHeaderContainer: React.FC = () => {
<tbody>
<tr>
<td>State</td>
<td align="right">{sInfo.flagState}</td>
<td align="right">{sInfo.session.flagState}</td>
</tr>
<tr>
<td>Air</td>
<td align="right">{numOut(sInfo.airTemp)}</td>
<td align="right">{numOut(sInfo.session.airTemp)}</td>
</tr>
<tr>
<td>{trackLabel()}</td>
<td align="right">{numOut(sInfo.trackTemp)}</td>
<td align="right">{numOut(sInfo.session.trackTemp)}</td>
</tr>
</tbody>
</table>
Expand Down Expand Up @@ -151,16 +151,16 @@ export const EventHeaderContainer: React.FC = () => {
<tbody>
<tr>
<td>Sim-Time</td>
<td align="right">{secAsHHMMSS(sInfo.timeOfDay)}</td>
<td align="right">{secAsHHMMSS(sInfo.session.timeOfDay)}</td>
</tr>
<tr>
<td>Elapsed</td>
<td align="right">{secAsString(sInfo.sessionTime)}</td>
<td align="right">{secAsString(sInfo.session.sessionTime)}</td>
</tr>
<tr>
<td>Remaining</td>
<td align="right">
{<RemainingRace time={sInfo.timeRemain} laps={sInfo.lapsRemain} />}
{<RemainingRace time={sInfo.session.timeRemain} laps={sInfo.session.lapsRemain} />}
</td>
</tr>
</tbody>
Expand Down
11 changes: 11 additions & 0 deletions src/container/SettingsContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as React from "react";
import { useAppDispatch, useAppSelector } from "../stores";
import {
setTheme,
setTimeMode,
toggleCompact,
toggleFilterOrderByPosition,
toggleSyncSelection,
Expand All @@ -27,6 +28,9 @@ export const SettingsContainer: React.FC = () => {
const onGlobalThemeSelect = (e: RadioChangeEvent) => {
dispatch(setTheme(e.target.value));
};
const onGlobalTimeMode = (e: RadioChangeEvent) => {
dispatch(setTimeMode(e.target.value));
};
return (
<>
<Row>
Expand All @@ -45,6 +49,13 @@ export const SettingsContainer: React.FC = () => {
Order cars in filter by race position
</Checkbox>
</p>
<p>
<Radio.Group defaultValue={stateGlobalSettings.timeMode} onChange={onGlobalTimeMode}>
<Radio.Button value="session">Session time</Radio.Button>
<Radio.Button value="sim">Simualtion time</Radio.Button>
<Radio.Button value="real">Real time</Radio.Button>
</Radio.Group>
</p>
<p>
<Radio.Group defaultValue={stateGlobalSettings.theme} onChange={onGlobalThemeSelect}>
<Radio.Button value="light">Light</Radio.Button>
Expand Down
2 changes: 1 addition & 1 deletion src/container/SpeedmapContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const SpeedmapContainer: React.FC = () => {
</Row>
<Row>
<Col>
<h3>Laptime over session time</h3>
<h3>Laptime progression</h3>
</Col>
</Row>
<Row>
Expand Down
23 changes: 20 additions & 3 deletions src/container/StintRankingContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
updateStintRankings,
} from "../stores/grpc/slices/userSettingsSlice";

import { secAsHHMMSS } from "../utils/output";
import { secAsHHMM, secAsHHMMSS } from "../utils/output";
import { InputData, prepareFilterData } from "./multiCarSelectFilterHelper";

const { Option } = Select;
Expand All @@ -29,7 +29,7 @@ export const StintRankingContainer: React.FC = () => {
const raceOrder = useAppSelector((state) => state.raceOrder);
const carLaps = useAppSelector((state) => state.carLaps);
const carStints = useAppSelector((state) => state.carStints);
const session = useAppSelector((state) => state.session);
const sessionData = useAppSelector((state) => state.session);
const dispatch = useAppDispatch();

// containerWidth state and ref (copilot proposal)
Expand Down Expand Up @@ -80,6 +80,21 @@ export const StintRankingContainer: React.FC = () => {
}
},
};
const displayTimeFromSettings = (d: number) => {
const toAdd = sessionData.session.timeOfDay - sessionData.session.sessionTime;
switch (stateGlobalSettings.timeMode) {
case "session":
return secAsHHMM(d);
case "sim":
return secAsHHMM(d + toAdd);
case "real":
const ref: Date = sessionData.recordDate;
// // JS hell when calculating days. In order to use own formatter we need the seconds.
// // Note: TZ-offset -60 on GMT+0100 ;)
const val = secAsHHMM((ref.getTime() / 1000 + d - ref.getTimezoneOffset() * 60) % 86400);
return val;
}
};
const filterProps = prepareFilterData(inputData);
const props = {
...filterProps,
Expand All @@ -94,6 +109,7 @@ export const StintRankingContainer: React.FC = () => {
toggleHighlightCar: (carNum: string) => {
dispatch(toggleHighlightCar(carNum));
},
displayTime: displayTimeFromSettings,
};

const carColors = assignCarColors(availableCars);
Expand All @@ -102,7 +118,7 @@ export const StintRankingContainer: React.FC = () => {
// console.log(`user: ${userSettings.lowerRangeTime} ${userSettings.upperRangeTime}`);
const currentSettings: IStintRankingSettings =
globalWamp.currentLiveId != undefined
? { ...userSettings, lowerRangeTime: 0, upperRangeTime: session.sessionTime }
? { ...userSettings, lowerRangeTime: 0, upperRangeTime: sessionData.session.sessionTime }
: userSettings;

// console.log(`current ${currentSettings.lowerRangeTime} ${currentSettings.upperRangeTime}`);
Expand Down Expand Up @@ -160,6 +176,7 @@ export const StintRankingContainer: React.FC = () => {
showCars={props.selectedCars}
hightlightCars={props.highlightCars}
toggleHighlightCar={props.toggleHighlightCar}
displayTime={props.displayTime}
/>
</Row>
{globalWamp.currentLiveId === undefined ? (
Expand Down
Loading

0 comments on commit a5e681f

Please sign in to comment.