Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ See the [Test Usage](#Test-usage) section for more details.
## Examples

- [node-dump-sdk-data](./node-dump-sdk-data/) - Simple commonjs node.js app that dumps the current SDK data to output files. Useful for generating test data.
- [node-lap-timer](./node-lap-timer/) - Long-running application example that demonstrates how to detect joining and leaving an iRacing session. Written in TypeScript.
- [node-sdk-server](./node-sdk-server/) - ESM Node.js server exposing the current SDK data via endpoints, as well as a websocket connection for real-time updates.

## Release testing usage
Expand Down
2 changes: 1 addition & 1 deletion examples/node-dump-sdk-data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
},
"keywords": [],
"author": "",
"license": "ISC",
"license": "MIT",
"packageManager": "[email protected]",
"dependencies": {
"fs-extra": "^11.3.2",
Expand Down
6 changes: 6 additions & 0 deletions examples/node-lap-timer/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"recommendations": [
"dbaeumer.vscode-eslint",
"yoavbls.pretty-ts-errors",
]
}
32 changes: 32 additions & 0 deletions examples/node-lap-timer/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/CVS": true,
"**/.DS_Store": true,
"**/Thumbs.db": true,
"**/build": true,
"**/dist": true,
"**/node_modules": true,
},
"npm.packageManager": "pnpm",
"typescript.format.enable": true,
"eslint.format.enable": true,
"editor.formatOnSave": true,
"eslint.workingDirectories": [
"."
],
"eslint.validate": [
"javascript, typescript",
"json"
],
"eslint.useFlatConfig": false,
"[typescript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
},
"[javascript]": {
"editor.maxTokenizationLineLength": 2500,
"editor.defaultFormatter": "dbaeumer.vscode-eslint",
}
}
23 changes: 23 additions & 0 deletions examples/node-lap-timer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# @irsdk-node/example-node-dump-sdk-data

## 1.0.1

### Patch Changes

- 86eeabe: Updated license to MIT.
- Updated dependencies [2830fbd]
- Updated dependencies [86eeabe]
- [email protected]

## 1.0.0

### Major Changes

- c1138e9: Initial SDK usage examples added.

### Patch Changes

- Updated dependencies [fa4206a]
- Updated dependencies [c1138e9]
- Updated dependencies [09e935a]
- [email protected]
16 changes: 16 additions & 0 deletions examples/node-lap-timer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# @irsdk-node/example-node-lap-timer

Example node.js script that waits for a session to start, and then will simply output lap data as a session is active. Good starting point for long-running processes that need to start and stop automatically when a session is joined.

## Usage

1. Clone and run install: `pnpm install`
2. Start the script via `pnpm script`.
3. Once a server/session is joined on iRacing, it should log lap data to the console.

> ❕NOTE: This project was made using pnpm, and makes use of the workspace:^ protocol. To use with any other package manager, just replace the workspace:^ protocol with the latest version.

## Future

This project is just a copy of an old test script, and is not representative of
production-level code. It will be replaced in due time.
27 changes: 27 additions & 0 deletions examples/node-lap-timer/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@irsdk-node/example-node-lap-timer",
"version": "1.0.1",
"description": "",
"main": "src/main.ts",
"type": "module",
"scripts": {
"start": "pnpm dlx tsx ./src/main.ts",
"build": "tsc",
"watch": "tsc --watch",
"test": "vitest",
"clean": "rm -rf dist *.tsbuildinfo"
},
"keywords": [],
"author": "",
"license": "MIT",
"packageManager": "[email protected]",
"dependencies": {
"fs-extra": "^11.3.2",
"irsdk-node": "workspace:^"
},
"devDependencies": {
"tsx": "^4.20.6",
"typescript": "^4.5.2",
"vitest": "^3.2.4"
}
}
11 changes: 11 additions & 0 deletions examples/node-lap-timer/src/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { describe, expect, it } from 'vitest';
import { formatDuration } from '../utils';

describe('formatDuration()', () => {
it('should display a formatted version of a seconds-value', () => {
expect(formatDuration(.53888)).toEqual('0:00.539');
expect(formatDuration(55.43)).toEqual('0:55.430');
expect(formatDuration(109)).toEqual('1:49.000');
expect(formatDuration(129.35222222)).toEqual('2:09.352');
});
});
122 changes: 122 additions & 0 deletions examples/node-lap-timer/src/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { exit } from 'node:process';
import { log } from 'node:console';
import { IRacingSDK, SessionData, TelemetryVarList } from 'irsdk-node';
import { formatDuration } from './utils.js';

const LOG_WAIT_DELAY = 5000; // 5s.
const DATA_INIT_BUFFER = 1000; // 1s
const MAX_TICK_LENGTH = 1 / 60; // 60fps

async function main() {
// Init SDK instance.
const sdk = new IRacingSDK();
sdk.autoEnableTelemetry = true;

// Initialize state.
let connected = false;
let startTriggered = false;
let lastWaitingLog = -1;
let localDriverIdx = -1;
let currentLapNum = -1;

// Try to start the SDK. If there is already a session running, it will connect
// and internally cache the first batch of data.
sdk.startSDK();

// Create a data loop.
//
// This is what will be called every tick, and where your actual logic will go.
const tick = () => {
const now = Date.now();

// Wait a maximum of 16ms (60fps) for data.
if (sdk.waitForData(MAX_TICK_LENGTH)) {
// We have data.
//
// Try to cache the session and telemetry immediately, and use the cached
// data for the remainder of the tick.
const sessionData = sdk.getSessionData();
const telemetry = sdk.getTelemetry();

// If our `connected` variable was false, it means we have just connected.
// We initialize our 'active session' state.
if (!connected) {
// When a session starts, this MIGHT toggle quickly between connected
// and disconnected. To prevent this, the first time we detect data we
// delay further input for 1s to give the SDK a moment to fully init.
if (!startTriggered) {
log('Detected session - waiting for init...');
startTriggered = true;
setTimeout(tick, DATA_INIT_BUFFER);
return;
}

// Cache that we are connected.
connected = true;
log('Session joined - getting data');

// Log driver information if data is available.
if (sessionData) {
localDriverIdx = getLocalDriverIdx(telemetry, sessionData);
const [driverData] = sessionData.DriverInfo.Drivers;
log(`Local driver detected ${driverData.UserName} (${driverData.CarScreenName})`);
} else {
log('Error: Session data not available!');
exit(1);
}
}

// Cache telemetry variables.
const lapNum = telemetry.LapCompleted.value[0];
const lapTime = telemetry.LapCurrentLapTime.value[0];
const bestLapTime = telemetry.LapBestLapTime.value[0];
const deltaToBest = lapTime - bestLapTime;

// If the lap number has increased, log the last lap data.
if (lapNum > 0 && currentLapNum != lapNum) {
currentLapNum = lapNum;
const deltaSign = deltaToBest < 0 ? '-' : '';
log(`${lapNum}: ${formatDuration(lapTime)} (${deltaSign + formatDuration(Math.abs(deltaToBest))})`);
}
} else { // User is not in a session.
// If our `connected` variable was true, that means we have just left a
// session. Reset all of our state variables so they do not corrupt our
// next session.
if (connected) {
log('Session left - no longer getting data.');
connected = false;
lastWaitingLog = now;
startTriggered = false;
currentLapNum = 0;

// Since we have fully reset -- early out.
setTimeout(tick, MAX_TICK_LENGTH);
return;
}

// Log some user feedback every Xs.
if (now >= lastWaitingLog + LOG_WAIT_DELAY) {
lastWaitingLog = now;
log('No data found - waiting...');
}
}

// Register the next tick.
// You could do this with setImmediate as well, but in most cases setTimeout
// with your target FPS is sufficient and more performant.
setTimeout(tick, MAX_TICK_LENGTH);
};

// Run the first tick.
tick();
}

main();

// Utils
function getLocalDriverIdx(telemetry: TelemetryVarList, session: SessionData) {
if (telemetry.PlayerCarIdx.value.length > 0) {
return telemetry.PlayerCarIdx.value[0];
}
return session.DriverInfo.DriverCarIdx;
}
32 changes: 32 additions & 0 deletions examples/node-lap-timer/src/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Restricts the given value to a certain number of decimal places, but does NOT
* force that number of places like `.toFixed()` does. Trailing 0's will be removed.
*/
export function restrictDecimals(value: number, maxDecimalPlaces: number): number {
if (maxDecimalPlaces < 0) return value;
// When 0, dont use .toFixed since it rounds
if (maxDecimalPlaces === 0) {
return Math.floor(value);
}

// Force to fixed -> Convert back to number to remove trailing 0's.
return Number(value.toFixed(maxDecimalPlaces));
}

/**
* Takes a given duration in milliseconds and formats it into a more readable
* string value suitable for logging or display.
*/
export function formatDuration(durationSecs: number): string {
const roundedSecs = restrictDecimals(durationSecs, 3);

// Calculate each individual portion of final time.
const minutesStr = Math.floor(roundedSecs / 60);
const seconds = roundedSecs % 60;
const [
secondsStrRaw = '00',
msStrRaw = '000',
] = `${seconds}`.split('.');

return `${minutesStr}:${secondsStrRaw.padStart(2, '0')}.${msStrRaw.slice(0, 3).padEnd(3, '0')}`;
}
40 changes: 40 additions & 0 deletions examples/node-lap-timer/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"references": [
{
"path": "../../packages/irsdk-node"
},
],
"include": [
"./src/*.ts",
],
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"declarationDir": "./dist",
"target": "esnext",
"lib": [
"ESNext"
],
"module": "NodeNext",
"skipLibCheck": true,
"declaration": true,
/* Compilation */
"composite": true,
"moduleResolution": "nodenext",
"isolatedModules": true,
"moduleDetection": "force",
"allowJs": true,
"esModuleInterop": true,
"resolveJsonModule": true,
/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictPropertyInitialization": true,
"noImplicitReturns": true,
}
}
4 changes: 2 additions & 2 deletions examples/node-sdk-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
},
"keywords": [],
"author": "",
"license": "ISC",
"license": "MIT",
"packageManager": "[email protected]",
"dependencies": {
"@hono/node-server": "^1.19.5",
Expand All @@ -18,4 +18,4 @@
"hono": "^4.9.9",
"irsdk-node": "workspace:^"
}
}
}
Loading