From 16e76732abb0dc2c5f1f515755c925a4b4edeb17 Mon Sep 17 00:00:00 2001 From: tariknz Date: Sun, 11 May 2025 17:57:36 +1200 Subject: [PATCH 1/9] attempt to fix relative bbox --- .../components/Standings/hooks/useDriverRelatives.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/frontend/components/Standings/hooks/useDriverRelatives.tsx b/src/frontend/components/Standings/hooks/useDriverRelatives.tsx index 7089898..eba829b 100644 --- a/src/frontend/components/Standings/hooks/useDriverRelatives.tsx +++ b/src/frontend/components/Standings/hooks/useDriverRelatives.tsx @@ -28,16 +28,18 @@ export const useDriverRelatives = ({ buffer }: { buffer: number }) => { return 0; } + // Initial time difference let delta = (oppositionEstTime - playerEstTime); + // First normalize to within half a lap time (this is what iRacing does first) + while (delta < -0.5 * driverEstLapTime) delta += driverEstLapTime; + while (delta > 0.5 * driverEstLapTime) delta -= driverEstLapTime; + + // Then ensure positive for ahead, negative for behind if (isAhead) { - // For cars ahead, use their lap time since they determine the full lap duration while (delta < 0) delta += driverEstLapTime; - while (delta > 0.5 * driverEstLapTime) delta -= driverEstLapTime; } else { - // For cars behind, use player's lap time since we're measuring against our lap while (delta > 0) delta -= driverEstLapTime; - while (delta < -0.5 * driverEstLapTime) delta += driverEstLapTime; } return delta; From a205121e792f92f891b354638765a7415a940e1b Mon Sep 17 00:00:00 2001 From: tariknz Date: Sun, 11 May 2025 21:21:48 +1200 Subject: [PATCH 2/9] save screenshot when dumping telemetry --- src/app/bridge/iracingSdk/dumpTelemetry.ts | 3 ++ src/app/setupTaskbar.ts | 37 +++++++++++++++++++--- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/src/app/bridge/iracingSdk/dumpTelemetry.ts b/src/app/bridge/iracingSdk/dumpTelemetry.ts index f0d1875..4a56dc4 100644 --- a/src/app/bridge/iracingSdk/dumpTelemetry.ts +++ b/src/app/bridge/iracingSdk/dumpTelemetry.ts @@ -24,8 +24,11 @@ export async function dumpCurrentTelemetry() { await writeFile(`${dirPath}/session.json`, session, 'utf-8'), ]); console.log(`Saved to: ${dirPath}`); + return { dirPath }; } else { console.warn('No telemetry data received'); } } + + return { dirPath: null }; } diff --git a/src/app/setupTaskbar.ts b/src/app/setupTaskbar.ts index 8883d3c..f7937e2 100644 --- a/src/app/setupTaskbar.ts +++ b/src/app/setupTaskbar.ts @@ -1,6 +1,8 @@ -import { nativeImage, Tray, Menu, app, globalShortcut } from 'electron'; +import { nativeImage, Tray, Menu, app, globalShortcut, desktopCapturer } from 'electron'; import { TelemetrySink } from './bridge/iracingSdk/telemetrySink'; import { OverlayManager } from './overlayManager'; +import { writeFile } from 'node:fs/promises'; +import path from 'node:path'; class Taskbar { private tray: Tray; @@ -65,11 +67,36 @@ class Taskbar { this.overlayManager.toggleLockOverlays(); } - private saveTelemetry(): void { + private async saveTelemetry(): Promise { if (process.platform === 'darwin') return; - import('./bridge/iracingSdk/dumpTelemetry').then( - async ({ dumpCurrentTelemetry }) => await dumpCurrentTelemetry() - ); + + try { + // First, import and call dumpTelemetry to get the directory path + const { dumpCurrentTelemetry } = await import('./bridge/iracingSdk/dumpTelemetry'); + const telemetryResult = await dumpCurrentTelemetry(); + + // Check if dirPath exists and is not null + const dirPath = telemetryResult && 'dirPath' in telemetryResult ? telemetryResult.dirPath : null; + if (dirPath) { + // Capture all screens + const sources = await desktopCapturer.getSources({ + types: ['screen'], + thumbnailSize: { width: 1920, height: 1080 } // Use a standard resolution + }); + + // Save each screen as a separate file + await Promise.all(sources.map(async (source, index) => { + if (source.thumbnail) { + const screenshotPath = path.join(dirPath, `screenshot_${index + 1}.png`); + const pngData = source.thumbnail.toPNG(); + await writeFile(screenshotPath, pngData); + console.log(`Screenshot ${index + 1} saved to: ${screenshotPath}`); + } + })); + } + } catch (error) { + console.error('Error capturing screenshots:', error); + } } private registerShortcuts(): void { From cf553d02751749d44ced2cceeac90258b05def55 Mon Sep 17 00:00:00 2001 From: tariknz Date: Tue, 13 May 2025 12:51:09 +1200 Subject: [PATCH 3/9] debug --- src/app/setupTaskbar.ts | 2 - .../Standings/DebugCarIdxPosition.stories.tsx | 61 +++++++++++++++++++ .../Standings/DebugCarIdxPosition.tsx | 56 +++++++++++++++++ 3 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 src/frontend/components/Standings/DebugCarIdxPosition.stories.tsx create mode 100644 src/frontend/components/Standings/DebugCarIdxPosition.tsx diff --git a/src/app/setupTaskbar.ts b/src/app/setupTaskbar.ts index f7937e2..e47df18 100644 --- a/src/app/setupTaskbar.ts +++ b/src/app/setupTaskbar.ts @@ -68,8 +68,6 @@ class Taskbar { } private async saveTelemetry(): Promise { - if (process.platform === 'darwin') return; - try { // First, import and call dumpTelemetry to get the directory path const { dumpCurrentTelemetry } = await import('./bridge/iracingSdk/dumpTelemetry'); diff --git a/src/frontend/components/Standings/DebugCarIdxPosition.stories.tsx b/src/frontend/components/Standings/DebugCarIdxPosition.stories.tsx new file mode 100644 index 0000000..d0491a4 --- /dev/null +++ b/src/frontend/components/Standings/DebugCarIdxPosition.stories.tsx @@ -0,0 +1,61 @@ +import { Meta, StoryObj } from '@storybook/react'; +import { DebugCarIdxPosition } from './DebugCarIdxPosition'; +import { TelemetryDecorator } from '../../../../.storybook/telemetryDecorator'; +import { DynamicTelemetrySelector, TEST_DATA_DIRS } from './DynamicTelemetrySelector'; +import { useState } from 'react'; + +export default { + component: DebugCarIdxPosition, + parameters: { + controls: { + exclude: ['telemetryPath'], + }, + }, + loaders: [ + async () => { + return { testDataDirs: TEST_DATA_DIRS }; + }, + ], +} as Meta; + +type Story = StoryObj; + +export const Primary: Story = { + decorators: [TelemetryDecorator()], +}; + +export const DynamicTelemetry: Story = { + decorators: [(Story, context) => { + const [selectedPath, setSelectedPath] = useState('/test-data/1746952742703'); + + return ( + <> + + {TelemetryDecorator(selectedPath)(Story, context)} + + ); + }], +}; + +export const MultiClassPCCWithClio: Story = { + decorators: [TelemetryDecorator('/test-data/1746952742230')], +}; + +export const SupercarsRace: Story = { + decorators: [TelemetryDecorator('/test-data/1732274253573')], +}; + +export const AdvancedMX5: Story = { + decorators: [TelemetryDecorator('/test-data/1732260478001')], +}; + +export const GT3Practice: Story = { + decorators: [TelemetryDecorator('/test-data/1732355190142')], +}; + +export const PCCPacing: Story = { + decorators: [TelemetryDecorator('/test-data/1735296198162')], +}; \ No newline at end of file diff --git a/src/frontend/components/Standings/DebugCarIdxPosition.tsx b/src/frontend/components/Standings/DebugCarIdxPosition.tsx new file mode 100644 index 0000000..c43bd54 --- /dev/null +++ b/src/frontend/components/Standings/DebugCarIdxPosition.tsx @@ -0,0 +1,56 @@ +import { useTelemetry, useSessionDrivers } from '@irdashies/context'; + +export const DebugCarIdxPosition = () => { + const carIdxPosition = useTelemetry('CarIdxPosition'); + const carIdxEstTime = useTelemetry('CarIdxEstTime'); + const carIdxLapDistPct = useTelemetry('CarIdxLapDistPct'); + const sessionDrivers = useSessionDrivers(); + + if (!carIdxPosition?.value || !sessionDrivers) { + return
Loading telemetry data...
; + } + + // Filter out invalid car indices (typically -1 values) + const validCars = carIdxPosition.value + .map((position, index) => ({ + carIdx: index, + position, + estTime: carIdxEstTime?.value?.[index], + lapDistPct: carIdxLapDistPct?.value?.[index], + driver: sessionDrivers.find(d => d.CarIdx === index) + })) + .filter(car => car.position > 0 && car.driver); + + // Sort by position + validCars.sort((a, b) => a.position - b.position); + + return ( +
+

Debug CarIdxPosition Data

+ + + + + + + + + + + + + {validCars.map((car) => ( + + + + + + + + + ))} + +
Car#DriverIdxPositionLap Dist %Est Time
{car.driver?.CarNumber}{car.driver?.UserName}{car.carIdx}{car.position}{car.lapDistPct?.toFixed(3)}{car.estTime?.toFixed(3)}
+
+ ); +}; \ No newline at end of file From a04dfccb11df70b42db8265d7aa311beb206c73f Mon Sep 17 00:00:00 2001 From: tariknz Date: Wed, 14 May 2025 21:02:46 +1200 Subject: [PATCH 4/9] update readme to point to releases --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 03cfff1..8d96e0d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,12 @@ This is built with the intention of being easily approachable by developers who This project is still in the early stages of development, so there may be bugs and many missing features. If you are interested in contributing please reach out and we can discuss how we can collaborate. +## Try it out + +You can try it out by downloading the latest release from the [releases page](https://github.com/tariknz/irdashies/releases). + +Install the .exe and run it. The application will automatically update when a new version is available. + ## Table of Contents - [Introduction](#introduction) @@ -37,7 +43,7 @@ This project is built with React and Electron and uses the iRacing SDK to retrie > Note: Developing on MacOS is fully supported and does not require iRacing or any additional tools to be installed as it uses a mocked SDK. -## Installation +## Installation (for development) To install IRDashies, follow these steps: @@ -56,7 +62,7 @@ To install IRDashies, follow these steps: npm run storybook ``` -## Usage +## Usage (for development) To start using IRDashies, run the following command: ```bash From 640eb9e2aaf54f03b42f94ae609ebf61780eb0ac Mon Sep 17 00:00:00 2001 From: tariknz Date: Fri, 16 May 2025 17:38:03 +1200 Subject: [PATCH 5/9] Revert "attempt to fix relative bbox" This reverts commit e6413edaf70741232066f044e3e191491bfd3ca8. --- .../components/Standings/hooks/useDriverRelatives.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/frontend/components/Standings/hooks/useDriverRelatives.tsx b/src/frontend/components/Standings/hooks/useDriverRelatives.tsx index eba829b..7089898 100644 --- a/src/frontend/components/Standings/hooks/useDriverRelatives.tsx +++ b/src/frontend/components/Standings/hooks/useDriverRelatives.tsx @@ -28,18 +28,16 @@ export const useDriverRelatives = ({ buffer }: { buffer: number }) => { return 0; } - // Initial time difference let delta = (oppositionEstTime - playerEstTime); - // First normalize to within half a lap time (this is what iRacing does first) - while (delta < -0.5 * driverEstLapTime) delta += driverEstLapTime; - while (delta > 0.5 * driverEstLapTime) delta -= driverEstLapTime; - - // Then ensure positive for ahead, negative for behind if (isAhead) { + // For cars ahead, use their lap time since they determine the full lap duration while (delta < 0) delta += driverEstLapTime; + while (delta > 0.5 * driverEstLapTime) delta -= driverEstLapTime; } else { + // For cars behind, use player's lap time since we're measuring against our lap while (delta > 0) delta -= driverEstLapTime; + while (delta < -0.5 * driverEstLapTime) delta += driverEstLapTime; } return delta; From 407eaa675611c056cf9ff1ee407870a7bd3f1373 Mon Sep 17 00:00:00 2001 From: tariknz Date: Fri, 16 May 2025 19:37:07 +1200 Subject: [PATCH 6/9] trackid fix --- src/frontend/components/TrackMap/hooks/useTrackId.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/components/TrackMap/hooks/useTrackId.tsx b/src/frontend/components/TrackMap/hooks/useTrackId.tsx index b68531e..a73b823 100644 --- a/src/frontend/components/TrackMap/hooks/useTrackId.tsx +++ b/src/frontend/components/TrackMap/hooks/useTrackId.tsx @@ -2,7 +2,7 @@ import { useSessionStore } from '@irdashies/context'; export const useTrackId = () => { const trackId = useSessionStore( - (state) => state.session?.WeekendInfo.TrackID + (state) => state.session?.WeekendInfo?.TrackID ); return trackId; }; From 470fd77cf6ce8aed6fdb95ce1d31fea4ef99aed6 Mon Sep 17 00:00:00 2001 From: tariknz Date: Fri, 16 May 2025 21:27:26 +1200 Subject: [PATCH 7/9] apply possible fix --- .../Standings/hooks/useDriverRelatives.tsx | 70 ++++++++++++------- 1 file changed, 43 insertions(+), 27 deletions(-) diff --git a/src/frontend/components/Standings/hooks/useDriverRelatives.tsx b/src/frontend/components/Standings/hooks/useDriverRelatives.tsx index 7089898..963b8ec 100644 --- a/src/frontend/components/Standings/hooks/useDriverRelatives.tsx +++ b/src/frontend/components/Standings/hooks/useDriverRelatives.tsx @@ -1,50 +1,56 @@ import { useMemo } from 'react'; import { useDriverCarIdx, + useSessionStore, useTelemetryValues, } from '@irdashies/context'; import { useDriverStandings } from './useDriverPositions'; export const useDriverRelatives = ({ buffer }: { buffer: number }) => { - const carIdxEstTime = useTelemetryValues('CarIdxEstTime'); const drivers = useDriverStandings(); const carIdxLapDistPct = useTelemetryValues('CarIdxLapDistPct'); + const carIdxLap = useTelemetryValues('CarIdxLap'); + const playerIndex = useDriverCarIdx(); + const driverCarEstLapTime = useSessionStore( + (s) => s.session?.DriverInfo?.DriverCarEstLapTime ?? 0 + ); const standings = useMemo(() => { - const player = drivers.find((d) => d.carIdx === playerIndex); - if (!player) { - return []; - } + const calculateDelta = (otherCarIdx: number) => { + const playerCarIdx = playerIndex ?? 0; - const driverEstLapTime = player.carClass.estLapTime ?? 0; + const playerLapNum = carIdxLap?.[playerCarIdx]; + const playerDistPct = carIdxLapDistPct?.[playerCarIdx]; - const calculateDelta = (carIdx: number, isAhead: boolean) => { - const playerEstTime = carIdxEstTime?.[playerIndex ?? 0]; - const oppositionEstTime = carIdxEstTime?.[carIdx]; - const opposition = drivers.find((d) => d.carIdx === carIdx); + const otherLapNum = carIdxLap?.[otherCarIdx]; + const otherDistPct = carIdxLapDistPct?.[otherCarIdx]; - if (!opposition) { - return 0; + if ( + playerLapNum === undefined || playerLapNum < 0 || + playerDistPct === undefined || playerDistPct < 0 || playerDistPct > 1 || + otherLapNum === undefined || otherLapNum < 0 || + otherDistPct === undefined || otherDistPct < 0 || otherDistPct > 1 || + driverCarEstLapTime <= 0 + ) { + return NaN; } - let delta = (oppositionEstTime - playerEstTime); + let distPctDifference = otherDistPct - playerDistPct; - if (isAhead) { - // For cars ahead, use their lap time since they determine the full lap duration - while (delta < 0) delta += driverEstLapTime; - while (delta > 0.5 * driverEstLapTime) delta -= driverEstLapTime; - } else { - // For cars behind, use player's lap time since we're measuring against our lap - while (delta > 0) delta -= driverEstLapTime; - while (delta < -0.5 * driverEstLapTime) delta += driverEstLapTime; + if (distPctDifference > 0.5) { + distPctDifference -= 1.0; + } else if (distPctDifference < -0.5) { + distPctDifference += 1.0; } + + const timeDelta = distPctDifference * driverCarEstLapTime; - return delta; + return timeDelta; }; const isHalfLapDifference = (car1: number, car2: number) => { - const diff = (car1 - car2 + 1) % 1; // Normalize the difference to [0, 1) + const diff = (car1 - car2 + 1) % 1; return diff <= 0.5; }; @@ -60,7 +66,7 @@ export const useDriverRelatives = ({ buffer }: { buffer: number }) => { }) .map((result) => ({ ...result, - delta: calculateDelta(result.carIdx, isAhead), + delta: calculateDelta(result.carIdx), })) .filter((result) => (isAhead ? result.delta > 0 : result.delta < 0)) .sort((a, b) => (isAhead ? a.delta - b.delta : b.delta - a.delta)) @@ -69,14 +75,24 @@ export const useDriverRelatives = ({ buffer }: { buffer: number }) => { }; const carsAhead = filterAndMapDrivers(true); + const player = drivers.find((result) => result.carIdx === playerIndex); const carsBehind = filterAndMapDrivers(false); - const relatives = [...carsAhead, { ...player, delta: 0 }, ...carsBehind]; + if (!player) { + return []; + } - // TODO: remove pace car if not under caution or pacing + const relatives = [...carsAhead, { ...player, delta: 0 }, ...carsBehind]; return relatives; - }, [drivers, buffer, carIdxEstTime, playerIndex, carIdxLapDistPct]); + }, [ + drivers, + buffer, + playerIndex, + driverCarEstLapTime, + carIdxLapDistPct, + carIdxLap, + ]); return standings; }; From a4a417eec507f9530a8e996c035e0f925846b721 Mon Sep 17 00:00:00 2001 From: tariknz Date: Thu, 22 May 2025 07:51:38 +1200 Subject: [PATCH 8/9] update gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bd50d9e..5a01e8b 100644 --- a/.gitignore +++ b/.gitignore @@ -92,7 +92,7 @@ typings/ out/ *storybook.log -test-data/*.png +test-data/**/*.png test-data/unused src/**/tracks/*.svg asset-data From 483eca88cd9b05141074e80e28b2b22c41101d1a Mon Sep 17 00:00:00 2001 From: tariknz Date: Thu, 22 May 2025 07:53:44 +1200 Subject: [PATCH 9/9] remove debug car story --- .../Standings/DebugCarIdxPosition.stories.tsx | 61 ------------------- .../Standings/DebugCarIdxPosition.tsx | 56 ----------------- 2 files changed, 117 deletions(-) delete mode 100644 src/frontend/components/Standings/DebugCarIdxPosition.stories.tsx delete mode 100644 src/frontend/components/Standings/DebugCarIdxPosition.tsx diff --git a/src/frontend/components/Standings/DebugCarIdxPosition.stories.tsx b/src/frontend/components/Standings/DebugCarIdxPosition.stories.tsx deleted file mode 100644 index d0491a4..0000000 --- a/src/frontend/components/Standings/DebugCarIdxPosition.stories.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { Meta, StoryObj } from '@storybook/react'; -import { DebugCarIdxPosition } from './DebugCarIdxPosition'; -import { TelemetryDecorator } from '../../../../.storybook/telemetryDecorator'; -import { DynamicTelemetrySelector, TEST_DATA_DIRS } from './DynamicTelemetrySelector'; -import { useState } from 'react'; - -export default { - component: DebugCarIdxPosition, - parameters: { - controls: { - exclude: ['telemetryPath'], - }, - }, - loaders: [ - async () => { - return { testDataDirs: TEST_DATA_DIRS }; - }, - ], -} as Meta; - -type Story = StoryObj; - -export const Primary: Story = { - decorators: [TelemetryDecorator()], -}; - -export const DynamicTelemetry: Story = { - decorators: [(Story, context) => { - const [selectedPath, setSelectedPath] = useState('/test-data/1746952742703'); - - return ( - <> - - {TelemetryDecorator(selectedPath)(Story, context)} - - ); - }], -}; - -export const MultiClassPCCWithClio: Story = { - decorators: [TelemetryDecorator('/test-data/1746952742230')], -}; - -export const SupercarsRace: Story = { - decorators: [TelemetryDecorator('/test-data/1732274253573')], -}; - -export const AdvancedMX5: Story = { - decorators: [TelemetryDecorator('/test-data/1732260478001')], -}; - -export const GT3Practice: Story = { - decorators: [TelemetryDecorator('/test-data/1732355190142')], -}; - -export const PCCPacing: Story = { - decorators: [TelemetryDecorator('/test-data/1735296198162')], -}; \ No newline at end of file diff --git a/src/frontend/components/Standings/DebugCarIdxPosition.tsx b/src/frontend/components/Standings/DebugCarIdxPosition.tsx deleted file mode 100644 index c43bd54..0000000 --- a/src/frontend/components/Standings/DebugCarIdxPosition.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { useTelemetry, useSessionDrivers } from '@irdashies/context'; - -export const DebugCarIdxPosition = () => { - const carIdxPosition = useTelemetry('CarIdxPosition'); - const carIdxEstTime = useTelemetry('CarIdxEstTime'); - const carIdxLapDistPct = useTelemetry('CarIdxLapDistPct'); - const sessionDrivers = useSessionDrivers(); - - if (!carIdxPosition?.value || !sessionDrivers) { - return
Loading telemetry data...
; - } - - // Filter out invalid car indices (typically -1 values) - const validCars = carIdxPosition.value - .map((position, index) => ({ - carIdx: index, - position, - estTime: carIdxEstTime?.value?.[index], - lapDistPct: carIdxLapDistPct?.value?.[index], - driver: sessionDrivers.find(d => d.CarIdx === index) - })) - .filter(car => car.position > 0 && car.driver); - - // Sort by position - validCars.sort((a, b) => a.position - b.position); - - return ( -
-

Debug CarIdxPosition Data

- - - - - - - - - - - - - {validCars.map((car) => ( - - - - - - - - - ))} - -
Car#DriverIdxPositionLap Dist %Est Time
{car.driver?.CarNumber}{car.driver?.UserName}{car.carIdx}{car.position}{car.lapDistPct?.toFixed(3)}{car.estTime?.toFixed(3)}
-
- ); -}; \ No newline at end of file