@hotlap.ai/telemetry 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +135 -0
- package/LICENSE +21 -0
- package/README.md +208 -0
- package/dist/binary-reader.d.ts +18 -0
- package/dist/binary-reader.d.ts.map +1 -0
- package/dist/binary-reader.js +206 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +261 -0
- package/dist/constants.d.ts +244 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +288 -0
- package/dist/ibt.d.ts +40 -0
- package/dist/ibt.d.ts.map +1 -0
- package/dist/ibt.js +298 -0
- package/dist/index-k5bgtn4c.js +6955 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/live.d.ts +59 -0
- package/dist/live.d.ts.map +1 -0
- package/dist/live.js +420 -0
- package/dist/telemetry-server.d.ts +80 -0
- package/dist/telemetry-server.d.ts.map +1 -0
- package/dist/telemetry-server.js +234 -0
- package/dist/types.d.ts +212 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +90 -0
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Contributing to @hotlap/telemetry
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to the HotLap.ai telemetry SDK!
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
1. Fork the repository
|
|
8
|
+
2. Clone your fork:
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/YOUR_USERNAME/telemetry.git
|
|
11
|
+
cd telemetry
|
|
12
|
+
```
|
|
13
|
+
3. Install dependencies:
|
|
14
|
+
```bash
|
|
15
|
+
bun install
|
|
16
|
+
```
|
|
17
|
+
4. Create a branch for your changes:
|
|
18
|
+
```bash
|
|
19
|
+
git checkout -b feature/your-feature-name
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Development
|
|
23
|
+
|
|
24
|
+
### Prerequisites
|
|
25
|
+
|
|
26
|
+
- [Bun](https://bun.sh) >= 1.0.0
|
|
27
|
+
- Windows (required for live telemetry testing)
|
|
28
|
+
- iRacing (for testing live telemetry features)
|
|
29
|
+
|
|
30
|
+
### Running the Project
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# Run the CLI in watch mode
|
|
34
|
+
bun run dev
|
|
35
|
+
|
|
36
|
+
# Run the telemetry server
|
|
37
|
+
bun run server
|
|
38
|
+
|
|
39
|
+
# Run tests
|
|
40
|
+
bun run test
|
|
41
|
+
|
|
42
|
+
# Run benchmarks
|
|
43
|
+
bun run bench
|
|
44
|
+
|
|
45
|
+
# Type check
|
|
46
|
+
bun run typecheck
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Project Structure
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
src/
|
|
53
|
+
binary-reader.ts # Low-level binary parsing utilities
|
|
54
|
+
cli.ts # Command-line interface
|
|
55
|
+
constants.ts # iRacing SDK constants and enums
|
|
56
|
+
ibt.ts # IBT file parser
|
|
57
|
+
index.ts # Main exports
|
|
58
|
+
live.ts # Live telemetry via Windows FFI
|
|
59
|
+
telemetry-server.ts # WebSocket server for streaming
|
|
60
|
+
types.ts # TypeScript type definitions
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Code Style
|
|
64
|
+
|
|
65
|
+
- Use TypeScript for all new code
|
|
66
|
+
- Follow existing code patterns and naming conventions
|
|
67
|
+
- Use meaningful variable and function names
|
|
68
|
+
- Keep functions focused and small
|
|
69
|
+
- Add types for all function parameters and return values
|
|
70
|
+
|
|
71
|
+
## Commit Messages
|
|
72
|
+
|
|
73
|
+
We use [Conventional Commits](https://www.conventionalcommits.org/):
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
<type>(<scope>): <description>
|
|
77
|
+
|
|
78
|
+
[optional body]
|
|
79
|
+
|
|
80
|
+
[optional footer]
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Types:**
|
|
84
|
+
- `feat` - New feature
|
|
85
|
+
- `fix` - Bug fix
|
|
86
|
+
- `docs` - Documentation only
|
|
87
|
+
- `style` - Code style (formatting, etc.)
|
|
88
|
+
- `refactor` - Code refactoring
|
|
89
|
+
- `perf` - Performance improvement
|
|
90
|
+
- `test` - Adding/updating tests
|
|
91
|
+
- `chore` - Maintenance tasks
|
|
92
|
+
|
|
93
|
+
**Examples:**
|
|
94
|
+
```
|
|
95
|
+
feat(ibt): add support for parsing stint data
|
|
96
|
+
fix(live): handle disconnection gracefully
|
|
97
|
+
docs: update API reference for IRSDK class
|
|
98
|
+
perf(binary-reader): optimize variable batch reading
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Pull Requests
|
|
102
|
+
|
|
103
|
+
1. Ensure your code follows the existing style
|
|
104
|
+
2. Update documentation if needed
|
|
105
|
+
3. Add tests for new functionality
|
|
106
|
+
4. Make sure all tests pass: `bun run test`
|
|
107
|
+
5. Make sure types are correct: `bun run typecheck`
|
|
108
|
+
6. Write a clear PR description explaining your changes
|
|
109
|
+
|
|
110
|
+
## Reporting Issues
|
|
111
|
+
|
|
112
|
+
When reporting issues, please include:
|
|
113
|
+
|
|
114
|
+
- Bun version (`bun --version`)
|
|
115
|
+
- Operating system and version
|
|
116
|
+
- iRacing version (if applicable)
|
|
117
|
+
- Steps to reproduce the issue
|
|
118
|
+
- Expected vs actual behavior
|
|
119
|
+
- Any error messages or logs
|
|
120
|
+
|
|
121
|
+
## Feature Requests
|
|
122
|
+
|
|
123
|
+
We welcome feature requests! Please open an issue describing:
|
|
124
|
+
|
|
125
|
+
- The problem you're trying to solve
|
|
126
|
+
- Your proposed solution
|
|
127
|
+
- Any alternatives you've considered
|
|
128
|
+
|
|
129
|
+
## Questions?
|
|
130
|
+
|
|
131
|
+
Feel free to open an issue for any questions about contributing.
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
By contributing, you agree that your contributions will be licensed under the MIT License.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 HotLap.ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# @hotlap.ai/telemetry
|
|
2
|
+
|
|
3
|
+
High-performance iRacing telemetry SDK for [Bun](https://bun.sh). Parse IBT telemetry files and stream live data from iRacing via Windows shared memory.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **IBT File Parsing** - Read and analyze iRacing telemetry binary files (.ibt)
|
|
8
|
+
- **Live Telemetry** - Stream real-time data from iRacing via Windows shared memory (FFI)
|
|
9
|
+
- **WebSocket Server** - Built-in telemetry server for real-time data streaming
|
|
10
|
+
- **Full TypeScript Support** - Complete type definitions for all APIs
|
|
11
|
+
- **High Performance** - Optimized binary parsing with minimal allocations
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- [Bun](https://bun.sh) >= 1.0.0
|
|
16
|
+
- Windows (for live telemetry features)
|
|
17
|
+
- iRacing (for live telemetry)
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bun add @hotlap.ai/telemetry
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Parse IBT Files
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { IBT } from '@hotlap.ai/telemetry';
|
|
31
|
+
|
|
32
|
+
const ibt = new IBT();
|
|
33
|
+
await ibt.open('./telemetry.ibt');
|
|
34
|
+
|
|
35
|
+
// Get session info
|
|
36
|
+
const weekendInfo = ibt.getSessionInfo('WeekendInfo');
|
|
37
|
+
console.log(`Track: ${weekendInfo.TrackDisplayName}`);
|
|
38
|
+
|
|
39
|
+
// Read telemetry data
|
|
40
|
+
const speed = ibt.get('Speed', 0); // Get speed at tick 0
|
|
41
|
+
const allSpeeds = ibt.getAll('Speed'); // Get all speed values
|
|
42
|
+
|
|
43
|
+
// Iterate through records
|
|
44
|
+
for (let i = 0; i < ibt.recordCount; i++) {
|
|
45
|
+
const lap = ibt.get('Lap', i);
|
|
46
|
+
const throttle = ibt.get('Throttle', i);
|
|
47
|
+
const brake = ibt.get('Brake', i);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Live Telemetry
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { createIRSDK } from '@hotlap.ai/telemetry';
|
|
55
|
+
|
|
56
|
+
const ir = createIRSDK();
|
|
57
|
+
|
|
58
|
+
// Connect to iRacing
|
|
59
|
+
const connected = await ir.startup();
|
|
60
|
+
if (!connected) {
|
|
61
|
+
console.error('Failed to connect to iRacing');
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Read live telemetry
|
|
66
|
+
setInterval(() => {
|
|
67
|
+
if (ir.isConnected) {
|
|
68
|
+
ir.freezeVarBufferLatest();
|
|
69
|
+
|
|
70
|
+
const speed = ir.get('Speed');
|
|
71
|
+
const rpm = ir.get('RPM');
|
|
72
|
+
const gear = ir.get('Gear');
|
|
73
|
+
|
|
74
|
+
console.log(`Speed: ${speed}, RPM: ${rpm}, Gear: ${gear}`);
|
|
75
|
+
|
|
76
|
+
ir.unfreezeVarBufferLatest();
|
|
77
|
+
}
|
|
78
|
+
}, 16); // ~60Hz
|
|
79
|
+
|
|
80
|
+
// Cleanup on exit
|
|
81
|
+
process.on('SIGINT', () => {
|
|
82
|
+
ir.shutdown();
|
|
83
|
+
process.exit(0);
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### WebSocket Telemetry Server
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
import { spawn } from 'bun';
|
|
91
|
+
|
|
92
|
+
// Start the telemetry server
|
|
93
|
+
const server = spawn(['bun', 'run', 'server'], {
|
|
94
|
+
cwd: './node_modules/@hotlap.ai/telemetry',
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Connect from client
|
|
98
|
+
const ws = new WebSocket('ws://127.0.0.1:32100/ws');
|
|
99
|
+
|
|
100
|
+
ws.onmessage = (event) => {
|
|
101
|
+
const msg = JSON.parse(event.data);
|
|
102
|
+
|
|
103
|
+
switch (msg.type) {
|
|
104
|
+
case 'session_info':
|
|
105
|
+
console.log(`Track: ${msg.data.trackDisplayName}`);
|
|
106
|
+
break;
|
|
107
|
+
case 'telemetry':
|
|
108
|
+
console.log(`Speed: ${msg.data.speed}`);
|
|
109
|
+
break;
|
|
110
|
+
case 'lap_change':
|
|
111
|
+
console.log(`Lap ${msg.data.fromLap} completed: ${msg.data.lapTime}`);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## API Reference
|
|
118
|
+
|
|
119
|
+
### IBT Class
|
|
120
|
+
|
|
121
|
+
Parse and read iRacing telemetry binary files.
|
|
122
|
+
|
|
123
|
+
| Method | Description |
|
|
124
|
+
|--------|-------------|
|
|
125
|
+
| `open(path)` | Open an IBT file asynchronously |
|
|
126
|
+
| `get(varName, tick)` | Get a telemetry value at a specific tick |
|
|
127
|
+
| `getAll(varName)` | Get all values for a telemetry variable |
|
|
128
|
+
| `getSessionInfo(key)` | Get parsed session info by key |
|
|
129
|
+
| `getRawSessionInfo()` | Get raw YAML session info string |
|
|
130
|
+
|
|
131
|
+
| Property | Description |
|
|
132
|
+
|----------|-------------|
|
|
133
|
+
| `recordCount` | Number of telemetry records |
|
|
134
|
+
| `sessionLapCount` | Number of laps in session |
|
|
135
|
+
| `tickRate` | Telemetry sample rate (typically 60Hz) |
|
|
136
|
+
| `varHeaderNames` | List of available telemetry variables |
|
|
137
|
+
|
|
138
|
+
### IRSDK Class
|
|
139
|
+
|
|
140
|
+
Connect to iRacing and read live telemetry data.
|
|
141
|
+
|
|
142
|
+
| Method | Description |
|
|
143
|
+
|--------|-------------|
|
|
144
|
+
| `startup()` | Connect to iRacing shared memory |
|
|
145
|
+
| `shutdown()` | Disconnect and cleanup |
|
|
146
|
+
| `get(varName)` | Get current value of a telemetry variable |
|
|
147
|
+
| `getSessionInfo(key)` | Get parsed session info by key |
|
|
148
|
+
| `freezeVarBufferLatest()` | Freeze telemetry buffer for consistent reads |
|
|
149
|
+
| `unfreezeVarBufferLatest()` | Unfreeze telemetry buffer |
|
|
150
|
+
|
|
151
|
+
| Property | Description |
|
|
152
|
+
|----------|-------------|
|
|
153
|
+
| `isConnected` | Whether connected to iRacing |
|
|
154
|
+
| `isInitialized` | Whether SDK is initialized |
|
|
155
|
+
| `sessionInfoUpdate` | Session info update counter |
|
|
156
|
+
|
|
157
|
+
### Broadcast Commands
|
|
158
|
+
|
|
159
|
+
Control iRacing camera, replay, and more:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
ir.camSwitchPos(position, group, camera);
|
|
163
|
+
ir.camSwitchNum(carNumber, group, camera);
|
|
164
|
+
ir.replaySetPlaySpeed(speed, slowMotion);
|
|
165
|
+
ir.replaySearch(searchMode);
|
|
166
|
+
ir.chatCommand(chatCommandMode);
|
|
167
|
+
ir.pitCommand(pitCommandMode, variable);
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Common Telemetry Variables
|
|
171
|
+
|
|
172
|
+
| Variable | Type | Description |
|
|
173
|
+
|----------|------|-------------|
|
|
174
|
+
| `Speed` | float | Vehicle speed (m/s) |
|
|
175
|
+
| `RPM` | float | Engine RPM |
|
|
176
|
+
| `Gear` | int | Current gear (-1=R, 0=N, 1-6=gears) |
|
|
177
|
+
| `Throttle` | float | Throttle position (0-1) |
|
|
178
|
+
| `Brake` | float | Brake position (0-1) |
|
|
179
|
+
| `Clutch` | float | Clutch position (0-1) |
|
|
180
|
+
| `SteeringWheelAngle` | float | Steering angle (radians) |
|
|
181
|
+
| `Lap` | int | Current lap number |
|
|
182
|
+
| `LapDistPct` | float | Lap completion percentage (0-1) |
|
|
183
|
+
| `LapCurrentLapTime` | float | Current lap time (seconds) |
|
|
184
|
+
| `SessionTime` | float | Session time (seconds) |
|
|
185
|
+
| `FuelLevel` | float | Fuel remaining (liters) |
|
|
186
|
+
| `OilTemp` | float | Oil temperature (C) |
|
|
187
|
+
| `WaterTemp` | float | Water temperature (C) |
|
|
188
|
+
|
|
189
|
+
See iRacing SDK documentation for the complete list of variables.
|
|
190
|
+
|
|
191
|
+
## Scripts
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
bun run build # Build the library
|
|
195
|
+
bun run build:server # Build standalone telemetry server executable
|
|
196
|
+
bun run server # Run telemetry WebSocket server
|
|
197
|
+
bun run dev # Run CLI in watch mode
|
|
198
|
+
bun run test # Run tests
|
|
199
|
+
bun run bench # Run benchmarks
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT - see [LICENSE](LICENSE)
|
|
205
|
+
|
|
206
|
+
## Contributing
|
|
207
|
+
|
|
208
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { VarType } from './constants';
|
|
2
|
+
import type { Header, VarBuffer, VarHeader, DiskSubHeader } from './types';
|
|
3
|
+
export declare function readInt32LE(view: DataView, offset: number): number;
|
|
4
|
+
export declare function readUint32LE(view: DataView, offset: number): number;
|
|
5
|
+
export declare function readFloat32LE(view: DataView, offset: number): number;
|
|
6
|
+
export declare function readFloat64LE(view: DataView, offset: number): number;
|
|
7
|
+
export declare function readBigUint64LE(view: DataView, offset: number): bigint;
|
|
8
|
+
export declare function readBoolLE(view: DataView, offset: number): boolean;
|
|
9
|
+
export declare function readCharLE(view: DataView, offset: number): number;
|
|
10
|
+
export declare function readString(buffer: Uint8Array, offset: number, length: number): string;
|
|
11
|
+
export declare function readVarValue(view: DataView, offset: number, type: VarType, count: number): number | number[] | boolean | boolean[];
|
|
12
|
+
export declare function readVarValueBatch<T extends Float32Array | Float64Array | Int32Array | Uint32Array>(view: DataView, baseOffset: number, varOffset: number, type: VarType, bufLen: number, recordCount: number, output: T): void;
|
|
13
|
+
export declare function parseHeader(view: DataView): Header;
|
|
14
|
+
export declare function parseVarHeader(view: DataView, buffer: Uint8Array, offset: number): VarHeader;
|
|
15
|
+
export declare function parseDiskSubHeader(view: DataView, offset: number): DiskSubHeader;
|
|
16
|
+
export declare function parseVarHeaders(view: DataView, buffer: Uint8Array, header: Header): Map<string, VarHeader>;
|
|
17
|
+
export declare function getLatestVarBuffer(varBufs: VarBuffer[]): VarBuffer;
|
|
18
|
+
//# sourceMappingURL=binary-reader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary-reader.d.ts","sourceRoot":"","sources":["../src/binary-reader.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAI3E,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAElE;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAElE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAKrF;AAED,wBAAgB,YAAY,CAC1B,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO,GAAG,OAAO,EAAE,CAkCzC;AAED,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,YAAY,GAAG,YAAY,GAAG,UAAU,GAAG,WAAW,EAChG,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,OAAO,EACb,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,CAAC,GACR,IAAI,CA0BN;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAkClD;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAU5F;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAQhF;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,CAO1G;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,SAAS,CAmBlE"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import {
|
|
3
|
+
VAR_TYPE_SIZES
|
|
4
|
+
} from "./constants.js";
|
|
5
|
+
|
|
6
|
+
// src/binary-reader.ts
|
|
7
|
+
var textDecoder = new TextDecoder;
|
|
8
|
+
function readInt32LE(view, offset) {
|
|
9
|
+
return view.getInt32(offset, true);
|
|
10
|
+
}
|
|
11
|
+
function readUint32LE(view, offset) {
|
|
12
|
+
return view.getUint32(offset, true);
|
|
13
|
+
}
|
|
14
|
+
function readFloat32LE(view, offset) {
|
|
15
|
+
return view.getFloat32(offset, true);
|
|
16
|
+
}
|
|
17
|
+
function readFloat64LE(view, offset) {
|
|
18
|
+
return view.getFloat64(offset, true);
|
|
19
|
+
}
|
|
20
|
+
function readBigUint64LE(view, offset) {
|
|
21
|
+
return view.getBigUint64(offset, true);
|
|
22
|
+
}
|
|
23
|
+
function readBoolLE(view, offset) {
|
|
24
|
+
return view.getUint8(offset) !== 0;
|
|
25
|
+
}
|
|
26
|
+
function readCharLE(view, offset) {
|
|
27
|
+
return view.getInt8(offset);
|
|
28
|
+
}
|
|
29
|
+
function readString(buffer, offset, length) {
|
|
30
|
+
let end = offset;
|
|
31
|
+
const maxEnd = offset + length;
|
|
32
|
+
while (end < maxEnd && buffer[end] !== 0)
|
|
33
|
+
end++;
|
|
34
|
+
return textDecoder.decode(buffer.subarray(offset, end));
|
|
35
|
+
}
|
|
36
|
+
function readVarValue(view, offset, type, count) {
|
|
37
|
+
const size = VAR_TYPE_SIZES[type];
|
|
38
|
+
if (count === 1) {
|
|
39
|
+
switch (type) {
|
|
40
|
+
case 0:
|
|
41
|
+
return readCharLE(view, offset);
|
|
42
|
+
case 1:
|
|
43
|
+
return readBoolLE(view, offset);
|
|
44
|
+
case 2:
|
|
45
|
+
return readInt32LE(view, offset);
|
|
46
|
+
case 3:
|
|
47
|
+
return readUint32LE(view, offset);
|
|
48
|
+
case 4:
|
|
49
|
+
return readFloat32LE(view, offset);
|
|
50
|
+
case 5:
|
|
51
|
+
return readFloat64LE(view, offset);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (type === 1) {
|
|
55
|
+
const result2 = new Array(count);
|
|
56
|
+
for (let i = 0;i < count; i++) {
|
|
57
|
+
result2[i] = readBoolLE(view, offset + i);
|
|
58
|
+
}
|
|
59
|
+
return result2;
|
|
60
|
+
}
|
|
61
|
+
const result = new Array(count);
|
|
62
|
+
for (let i = 0;i < count; i++) {
|
|
63
|
+
const pos = offset + i * size;
|
|
64
|
+
switch (type) {
|
|
65
|
+
case 0:
|
|
66
|
+
result[i] = readCharLE(view, pos);
|
|
67
|
+
break;
|
|
68
|
+
case 2:
|
|
69
|
+
result[i] = readInt32LE(view, pos);
|
|
70
|
+
break;
|
|
71
|
+
case 3:
|
|
72
|
+
result[i] = readUint32LE(view, pos);
|
|
73
|
+
break;
|
|
74
|
+
case 4:
|
|
75
|
+
result[i] = readFloat32LE(view, pos);
|
|
76
|
+
break;
|
|
77
|
+
case 5:
|
|
78
|
+
result[i] = readFloat64LE(view, pos);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
function readVarValueBatch(view, baseOffset, varOffset, type, bufLen, recordCount, output) {
|
|
85
|
+
const size = VAR_TYPE_SIZES[type];
|
|
86
|
+
let writeIdx = 0;
|
|
87
|
+
switch (type) {
|
|
88
|
+
case 4:
|
|
89
|
+
for (let i = 0;i < recordCount; i++) {
|
|
90
|
+
output[writeIdx++] = view.getFloat32(baseOffset + i * bufLen + varOffset, true);
|
|
91
|
+
}
|
|
92
|
+
break;
|
|
93
|
+
case 5:
|
|
94
|
+
for (let i = 0;i < recordCount; i++) {
|
|
95
|
+
output[writeIdx++] = view.getFloat64(baseOffset + i * bufLen + varOffset, true);
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
case 2:
|
|
99
|
+
for (let i = 0;i < recordCount; i++) {
|
|
100
|
+
output[writeIdx++] = view.getInt32(baseOffset + i * bufLen + varOffset, true);
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
case 3:
|
|
104
|
+
for (let i = 0;i < recordCount; i++) {
|
|
105
|
+
output[writeIdx++] = view.getUint32(baseOffset + i * bufLen + varOffset, true);
|
|
106
|
+
}
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
function parseHeader(view) {
|
|
111
|
+
const version = readInt32LE(view, 0);
|
|
112
|
+
const status = readInt32LE(view, 4);
|
|
113
|
+
const tickRate = readInt32LE(view, 8);
|
|
114
|
+
const sessionInfoUpdate = readInt32LE(view, 12);
|
|
115
|
+
const sessionInfoLen = readInt32LE(view, 16);
|
|
116
|
+
const sessionInfoOffset = readInt32LE(view, 20);
|
|
117
|
+
const numVars = readInt32LE(view, 24);
|
|
118
|
+
const varHeaderOffset = readInt32LE(view, 28);
|
|
119
|
+
const numBuf = readInt32LE(view, 32);
|
|
120
|
+
const bufLen = readInt32LE(view, 36);
|
|
121
|
+
const varBuf = new Array(numBuf);
|
|
122
|
+
for (let i = 0;i < numBuf; i++) {
|
|
123
|
+
const offset = 48 + i * 16;
|
|
124
|
+
varBuf[i] = {
|
|
125
|
+
tickCount: readInt32LE(view, offset),
|
|
126
|
+
bufOffset: readInt32LE(view, offset + 4)
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
return {
|
|
130
|
+
version,
|
|
131
|
+
status,
|
|
132
|
+
tickRate,
|
|
133
|
+
sessionInfoUpdate,
|
|
134
|
+
sessionInfoLen,
|
|
135
|
+
sessionInfoOffset,
|
|
136
|
+
numVars,
|
|
137
|
+
varHeaderOffset,
|
|
138
|
+
numBuf,
|
|
139
|
+
bufLen,
|
|
140
|
+
varBuf
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function parseVarHeader(view, buffer, offset) {
|
|
144
|
+
return {
|
|
145
|
+
type: readInt32LE(view, offset),
|
|
146
|
+
offset: readInt32LE(view, offset + 4),
|
|
147
|
+
count: readInt32LE(view, offset + 8),
|
|
148
|
+
countAsTime: readBoolLE(view, offset + 12),
|
|
149
|
+
name: readString(buffer, offset + 16, 32),
|
|
150
|
+
desc: readString(buffer, offset + 48, 64),
|
|
151
|
+
unit: readString(buffer, offset + 112, 32)
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function parseDiskSubHeader(view, offset) {
|
|
155
|
+
return {
|
|
156
|
+
sessionStartDate: readBigUint64LE(view, offset),
|
|
157
|
+
sessionStartTime: readFloat64LE(view, offset + 8),
|
|
158
|
+
sessionEndTime: readFloat64LE(view, offset + 16),
|
|
159
|
+
sessionLapCount: readInt32LE(view, offset + 24),
|
|
160
|
+
sessionRecordCount: readInt32LE(view, offset + 28)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function parseVarHeaders(view, buffer, header) {
|
|
164
|
+
const headers = new Map;
|
|
165
|
+
for (let i = 0;i < header.numVars; i++) {
|
|
166
|
+
const varHeader = parseVarHeader(view, buffer, header.varHeaderOffset + i * 144);
|
|
167
|
+
headers.set(varHeader.name, varHeader);
|
|
168
|
+
}
|
|
169
|
+
return headers;
|
|
170
|
+
}
|
|
171
|
+
function getLatestVarBuffer(varBufs) {
|
|
172
|
+
if (varBufs.length === 0) {
|
|
173
|
+
throw new Error("varBufs array is empty");
|
|
174
|
+
}
|
|
175
|
+
let latest = varBufs[0];
|
|
176
|
+
let secondLatest = varBufs[1] ?? varBufs[0];
|
|
177
|
+
for (let i = 1;i < varBufs.length; i++) {
|
|
178
|
+
const current = varBufs[i];
|
|
179
|
+
if (current.tickCount > latest.tickCount) {
|
|
180
|
+
secondLatest = latest;
|
|
181
|
+
latest = current;
|
|
182
|
+
} else if (current.tickCount > secondLatest.tickCount) {
|
|
183
|
+
secondLatest = current;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return secondLatest;
|
|
187
|
+
}
|
|
188
|
+
export {
|
|
189
|
+
readVarValueBatch,
|
|
190
|
+
readVarValue,
|
|
191
|
+
readUint32LE,
|
|
192
|
+
readString,
|
|
193
|
+
readInt32LE,
|
|
194
|
+
readFloat64LE,
|
|
195
|
+
readFloat32LE,
|
|
196
|
+
readCharLE,
|
|
197
|
+
readBoolLE,
|
|
198
|
+
readBigUint64LE,
|
|
199
|
+
parseVarHeaders,
|
|
200
|
+
parseVarHeader,
|
|
201
|
+
parseHeader,
|
|
202
|
+
parseDiskSubHeader,
|
|
203
|
+
getLatestVarBuffer
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
export { readInt32LE, readUint32LE, readFloat32LE, readFloat64LE, readBigUint64LE, readBoolLE, readCharLE, readString, readVarValue, readVarValueBatch, parseHeader, parseVarHeader, parseDiskSubHeader, parseVarHeaders, getLatestVarBuffer };
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|