@z0mt3c/f1-telemetry-client
Version:
[](https://github.com/z0mt3c/f1-telemetry-client/actions/workflows/node.js.yml) [ • 8.7 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.constants = exports.F1TelemetryClient = exports.BIGINT_ENABLED = exports.FORWARD_ADDRESSES = exports.DEFAULT_PORT = void 0;
const dgram = __importStar(require("dgram"));
const events_1 = require("events");
const packets_1 = require("./parsers/packets");
const types_1 = require("./types");
const PacketTyreSetsDataParser_1 = require("./parsers/packets/PacketTyreSetsDataParser");
const PacketMotionExDataParser_1 = require("./parsers/packets/PacketMotionExDataParser");
const PacketTimeTrialDataParser_1 = require("./parsers/packets/PacketTimeTrialDataParser");
const PacketLapPositionsDataParser_1 = require("./parsers/packets/PacketLapPositionsDataParser");
const constants = __importStar(require("./constants"));
exports.constants = constants;
const { PACKET_SIZES, PACKET_ID_TO_PACKET, PACKETS } = constants;
exports.DEFAULT_PORT = 20777;
exports.FORWARD_ADDRESSES = undefined;
exports.BIGINT_ENABLED = true;
/**
*
*/
class F1TelemetryClient extends events_1.EventEmitter {
constructor(opts = {}) {
super();
const { port = exports.DEFAULT_PORT, bigintEnabled = exports.BIGINT_ENABLED, forwardAddresses = exports.FORWARD_ADDRESSES } = opts;
this.port = port;
this.bigintEnabled = bigintEnabled;
this.forwardAddresses = forwardAddresses;
this.socket = dgram.createSocket('udp4');
}
/**
*
* @param {Buffer} message
* @param bigintEnabled
* @param remoteInfo
*/
static parseBufferMessage(message, bigintEnabled = false, remoteInfo) {
const packetHeader = F1TelemetryClient.parsePacketHeader(message, bigintEnabled);
const { m_packetFormat: format, m_packetId: id, m_gameYear: year } = packetHeader;
const context = { id, year, format, message, remoteInfo, name: 'unknown', data: packetHeader };
const Parser = F1TelemetryClient.getParserByPacketId(id);
if (Parser == null)
throw new types_1.ParserError('No parser available', undefined, context);
context.name = Object.keys(PACKETS)[id];
try {
const { data } = new Parser(message, format, bigintEnabled);
return { ...context, data };
}
catch (error) {
throw new types_1.ParserError('Parsing failed', error, context);
}
}
/**
*
* @param {Buffer} buffer
* @param {Boolean} bigintEnabled
*/
static parsePacketHeader(buffer, bigintEnabled) {
const packetFormatParser = new packets_1.PacketFormatParser();
const { m_packetFormat } = packetFormatParser.fromBuffer(buffer);
const packetHeaderParser = new packets_1.PacketHeaderParser(m_packetFormat, bigintEnabled);
return packetHeaderParser.fromBuffer(buffer);
}
/**
*
* @param {Number} packetFormat
* @param {Number} packetId
*/
static getPacketSize(packetFormat, packetId) {
const packetValues = Object.values(PACKET_SIZES);
return packetValues[packetId][packetFormat];
}
/**
*
* @param {Number} packetId
*/
static getParserByPacketId(packetId) {
const packetType = PACKET_ID_TO_PACKET[packetId];
switch (packetType) {
case PACKETS.session:
return packets_1.PacketSessionDataParser;
case PACKETS.motion:
return packets_1.PacketMotionDataParser;
case PACKETS.lapData:
return packets_1.PacketLapDataParser;
case PACKETS.event:
return packets_1.PacketEventDataParser;
case PACKETS.participants:
return packets_1.PacketParticipantsDataParser;
case PACKETS.carSetups:
return packets_1.PacketCarSetupDataParser;
case PACKETS.carTelemetry:
return packets_1.PacketCarTelemetryDataParser;
case PACKETS.carStatus:
return packets_1.PacketCarStatusDataParser;
case PACKETS.finalClassification:
return packets_1.PacketFinalClassificationDataParser;
case PACKETS.lobbyInfo:
return packets_1.PacketLobbyInfoDataParser;
case PACKETS.carDamage:
return packets_1.PacketCarDamageDataParser;
case PACKETS.sessionHistory:
return packets_1.PacketSessionHistoryDataParser;
case PACKETS.tyreSets:
return PacketTyreSetsDataParser_1.PacketTyreSetsDataParser;
case PACKETS.motionEx:
return PacketMotionExDataParser_1.PacketMotionExDataParser;
case PACKETS.timeTrial:
return PacketTimeTrialDataParser_1.PacketTimeTrialDataParser;
case PACKETS.lapPositions:
return PacketLapPositionsDataParser_1.PacketLapPositionsDataParser;
default:
return null;
}
}
/**
*
* @param {Buffer} message
* @param remoteInfo
*/
handleMessage(message, remoteInfo) {
if (this.forwardAddresses != null) {
// bridge message
this.bridgeMessage(message);
}
try {
const parsedMessage = F1TelemetryClient.parseBufferMessage(message, this.bigintEnabled, remoteInfo);
this.emitPackage(parsedMessage);
}
catch (error) {
this.emit('error', error);
if (error instanceof types_1.ParserError)
this.emit('*', error.context);
}
}
emitPackage(parsedMessage) {
if (parsedMessage?.data == null)
return;
this.emit(parsedMessage.name + ':raw', parsedMessage);
this.emit(parsedMessage.name, parsedMessage.data);
this.emit('*', parsedMessage);
}
/**
*
* @param {Buffer} message
*/
bridgeMessage(message) {
if (this.socket == null) {
throw new Error('Socket is not initialized');
}
if (this.forwardAddresses == null) {
throw new Error('No ports to bridge over');
}
for (const address of this.forwardAddresses) {
this.socket.send(message, 0, message.length, address.port, address.ip ?? '0.0.0.0');
}
}
/**
* Method to start listening for packets
*/
start() {
if (this.socket == null) {
return;
}
this.socket.on('listening', () => {
if (this.socket == null) {
return;
}
const address = this.socket.address();
console.log(`UDP Client listening on ${address.address}:${address.port} 🏎`);
this.socket.setBroadcast(true);
});
this.socket.on('message', (m, remoteInfo) => {
this.handleMessage(m, remoteInfo);
});
this.socket.bind({
port: this.port,
exclusive: false,
});
}
/**
* Method to close the client
*/
stop() {
if (this.socket == null) {
return;
}
return this.socket.close(() => {
console.log('UDP Client closed 🏁');
this.socket = undefined;
});
}
}
exports.F1TelemetryClient = F1TelemetryClient;
exports.default = F1TelemetryClient;