Split- och depåstatistik är implementerad

This commit is contained in:
larssand
2026-08-25 20:00:55 +02:00
parent 8ea8b3a84e
commit abf887dccd
16 changed files with 564 additions and 46 deletions

View File

@@ -18,9 +18,12 @@ function createHarness() {
events: [{
id: "event_1",
mode: "race",
teams: [{ id: "team_1", name: "Team One", driverIds: ["driver_1"], carIds: [] }],
teams: [{ id: "team_1", name: "Team One", driverIds: ["driver_1", "driver_2"], carIds: [] }],
}],
drivers: [{ id: "driver_1", name: "Driver One", transponder: "123" }],
drivers: [
{ id: "driver_1", name: "Driver One", transponder: "123" },
{ id: "driver_2", name: "Driver Two", transponder: "456" },
],
cars: [],
resultsBySession: {},
};
@@ -44,9 +47,9 @@ function createHarness() {
lastOverlayTop3BySession: {},
lastOverlayBestLapByKey: {},
};
const passing = (timestamp, role) => processDecoderMessageHelper({
const passing = (timestamp, role, transponder = "123") => processDecoderMessageHelper({
msg: "PASSING",
transponder: "123",
transponder,
rtc_time: new Date(timestamp).toISOString(),
}, deps, role);
return { state, session, passing, overlayEvents, getSaveCount: () => saveCount };
@@ -74,7 +77,7 @@ test("pit entry and exit create one timed stop without adding laps", () => {
harness.passing(17_000, "main");
harness.passing(30_000, "pit_entry");
harness.passing(31_000, "pit_entry");
harness.passing(42_500, "pit_exit");
harness.passing(42_500, "pit_exit", "456");
const result = harness.state.resultsBySession.session_1;
const entry = result.competitors.team_team_1;
@@ -85,6 +88,10 @@ test("pit entry and exit create one timed stop without adding laps", () => {
assert.equal(entry.totalPitMs, 12_500);
assert.equal(result.pitEvents.length, 2);
assert.equal(result.pitStops.length, 1);
assert.equal(result.pitStops[0].driverId, "driver_1");
assert.equal(result.pitStops[0].driverName, "Driver One");
assert.equal(result.pitStops[0].transponder, "123");
assert.equal(result.pitStops[0].exitTransponder, "456");
assert.equal(result.passings.length, 1);
});

View File

@@ -13,6 +13,7 @@ import {
} from "../src/event_race_logic.js";
import {
buildFinalStandings,
buildSessionChannelStats,
compareNumberSet,
formatLeaderboardGap,
getCompetitorSeedMetric,
@@ -423,3 +424,26 @@ test("final standings count the configured best legs per main", () => {
assert.equal(standings[0].score, "2 (1+1+2)");
assert.equal(standings[1].score, "3 (1+2+2)");
});
test("session channel statistics summarize splits and pit stops per competitor", () => {
const result = {
competitors: {
team_1: { displayName: "Team One", driverName: "Driver One", carName: "Car One" },
},
splits: [
{ competitorKey: "team_1", splitMs: 8000 },
{ competitorKey: "team_1", splitMs: 7600 },
],
pitStops: [
{ competitorKey: "team_1", driverName: "Driver One", carName: "Car One", enteredAt: 100000, exitedAt: 112000, pitStopMs: 12000 },
{ competitorKey: "team_1", driverName: "Driver Two", carName: "Car Two", enteredAt: 200000, exitedAt: 209000, pitStopMs: 9000 },
],
};
const stats = buildSessionChannelStats({ id: "session_1" }, { ensureSessionResult: () => result });
assert.equal(stats.splitCount, 2);
assert.equal(stats.pitStopCount, 2);
assert.equal(stats.totalPitMs, 21000);
assert.equal(stats.competitors[0].averageSplitMs, 7800);
assert.equal(stats.competitors[0].bestPitStopMs, 9000);
assert.equal(stats.pitStops[1].driverName, "Driver Two");
});

View File

@@ -4,6 +4,7 @@ import assert from "node:assert/strict";
import {
buildDriverActivePracticeStats,
buildDriverTrainingStats,
buildDriverTimingChannelStats,
buildTrainingRecommendation,
getBestConsecutiveTrainingLaps,
getBestTrainingDurationResult,
@@ -110,6 +111,48 @@ test("driver statistics remain isolated and aggregate days and sessions", () =>
assert.equal(stats.dailySummaries[1].laps, 2);
});
test("split and pit statistics aggregate by personal profile and selected class", () => {
const state = {
drivers: [
{ id: "driver_2wd_a", name: "Driver", profileId: "profile_1", classId: "class_2wd" },
{ id: "driver_2wd_b", name: "Driver", profileId: "profile_1", classId: "class_2wd" },
{ id: "driver_4wd", name: "Driver", profileId: "profile_1", classId: "class_4wd" },
],
sessions: [
{ id: "session_1", name: "Practice", eventId: "event_1" },
{ id: "session_2", name: "Endurance", eventId: "event_1" },
],
events: [{ id: "event_1", name: "Club day" }],
resultsBySession: {
session_1: {
splits: [
{ driverId: "driver_2wd_a", splitMs: 8100, timestamp: 1000 },
{ driverId: "driver_2wd_b", splitMs: 7900, timestamp: 2000 },
{ driverId: "driver_4wd", splitMs: 6500, timestamp: 3000 },
],
pitStops: [],
},
session_2: {
splits: [],
pitStops: [
{ driverId: "driver_2wd_a", pitStopMs: 12500, enteredAt: 4000 },
{ driverId: "driver_2wd_b", pitStopMs: 9500, enteredAt: 5000 },
{ driverId: "driver_4wd", pitStopMs: 7000, enteredAt: 6000 },
],
},
},
};
const stats = buildDriverTimingChannelStats(state, "profile_1", "class_2wd");
assert.equal(stats.splitCount, 2);
assert.equal(stats.bestSplitMs, 7900);
assert.equal(stats.averageSplitMs, 8000);
assert.equal(stats.pitStopCount, 2);
assert.equal(stats.totalPitMs, 22000);
assert.equal(stats.bestPitStopMs, 9500);
assert.equal(stats.recentPitStops[0].sessionName, "Endurance");
});
test("one driver profile combines transponders within a class but keeps 2WD and 4WD separate", () => {
const state = {
drivers: [