From 7ed01ea2f97c0eda043f4794bfc5aade756d8c0b Mon Sep 17 00:00:00 2001 From: EvenAR Date: Tue, 14 Nov 2023 22:18:58 +0100 Subject: [PATCH] Add new input event APIs from SU 13 (needs testing) Issue #86 --- samples/typescript/inputEvents.ts | 51 ++++++++++++ src/RawBuffer.ts | 12 +++ src/SimConnectConnection.ts | 96 ++++++++++++++++++++++ src/SimConnectPacketBuilder.ts | 10 +++ src/SimConnectSocket.ts | 5 ++ src/datastructures/InputEventDescriptor.ts | 16 ++++ src/enums/InputEventType.ts | 4 + src/enums/SimConnectException.ts | 5 ++ src/recv/RecvEnumerateInputEventParams.ts | 12 +++ src/recv/RecvEnumerateInputEvents.ts | 16 ++++ src/recv/RecvGetInputEvent.ts | 27 ++++++ src/recv/RecvSubscribeInputEvent.ts | 26 ++++++ 12 files changed, 280 insertions(+) create mode 100644 samples/typescript/inputEvents.ts create mode 100644 src/datastructures/InputEventDescriptor.ts create mode 100644 src/enums/InputEventType.ts create mode 100644 src/recv/RecvEnumerateInputEventParams.ts create mode 100644 src/recv/RecvEnumerateInputEvents.ts create mode 100644 src/recv/RecvGetInputEvent.ts create mode 100644 src/recv/RecvSubscribeInputEvent.ts diff --git a/samples/typescript/inputEvents.ts b/samples/typescript/inputEvents.ts new file mode 100644 index 0000000..52de737 --- /dev/null +++ b/samples/typescript/inputEvents.ts @@ -0,0 +1,51 @@ +import { open, Protocol } from '../../dist'; + +/** + * Collects information about all available input events for the aircraft and prints them + */ +open('My app', Protocol.KittyHawk) + .then(({ recvOpen, handle }) => { + console.log('Connected: ', recvOpen); + + let allInputEvents: { + inputEventName: string; + inputEventIdHash: string; + params?: string; + }[] = []; + + handle.on('inputEventsList', recvEnumerateInputEvents => { + recvEnumerateInputEvents.inputEventDescriptors.forEach(e => { + allInputEvents.push({ + inputEventName: e.name, + inputEventIdHash: e.inputEventIdHash.toString(), + }); + handle.enumerateInputEventParams(e.inputEventIdHash); + }); + }); + + handle.on('enumerateInputEventParams', recvEnumerateInputEventParams => { + // Update the list with the received value + allInputEvents = allInputEvents.map(inputEvent => { + if ( + inputEvent.inputEventIdHash === + recvEnumerateInputEventParams.inputEventIdHash.toString() + ) { + return { ...inputEvent, params: recvEnumerateInputEventParams.value }; + } else { + return inputEvent; + } + }); + + // Finally, when no input events are missing the params value, print the list + if (allInputEvents.filter(ev => ev.params === undefined).length === 0) { + console.log(allInputEvents); + } + }); + + handle.on('exception', ex => console.log('Exception', ex)); + + handle.enumerateInputEvents(0 /* request-id */); + }) + .catch(error => { + console.log('Failed to connect', error); + }); diff --git a/src/RawBuffer.ts b/src/RawBuffer.ts index 1fee1a4..88ad166 100644 --- a/src/RawBuffer.ts +++ b/src/RawBuffer.ts @@ -71,6 +71,10 @@ class RawBuffer { return this.buffer.readInt64().toNumber(); } + readUint64(): Long { + return this.buffer.readUint64(); + } + /** @deprecated use readInt64() instead */ readLong = this.readInt64; @@ -78,6 +82,14 @@ class RawBuffer { this.buffer.writeInt64(value); } + writeUint32(value: number, offset?: number) { + this.buffer.writeUint64(value, offset); + } + + writeUint64(value: Long, offset?: number) { + this.buffer.writeUint64(value, offset); + } + /** @deprecated use writeInt64() instead */ writeLong = this.writeInt64; diff --git a/src/SimConnectConnection.ts b/src/SimConnectConnection.ts index 42de938..355512e 100644 --- a/src/SimConnectConnection.ts +++ b/src/SimConnectConnection.ts @@ -56,6 +56,10 @@ import { } from './Types'; import Timeout = NodeJS.Timeout; import { RecvControllersList } from './recv/RecvControllersList'; +import { RecvEnumerateInputEvents } from './recv/RecvEnumerateInputEvents'; +import { RecvGetInputEvent } from './recv/RecvGetInputEvent'; +import { RecvSubscribeInputEvent } from './recv/RecvSubscribeInputEvent'; +import { RecvEnumerateInputEventParams } from './recv/RecvEnumerateInputEventParams'; type OpenPacketData = { major: number; @@ -131,6 +135,12 @@ interface SimConnectRecvEvents { facilityMinimalList: (recvFacilityMinimalList: RecvFacilityMinimalList) => void; jetwayData: (recvJetwayData: RecvJetwayData) => void; controllersList: (recvControllersList: RecvControllersList) => void; + inputEventsList: (recvEnumerateInputEvents: RecvEnumerateInputEvents) => void; + getInputEvent: (recvGetInputEvent: RecvGetInputEvent) => void; + subscribeInputEvent: (recvSubscribeInputEvent: RecvSubscribeInputEvent) => void; + enumerateInputEventParams: ( + recvEnumerateInputEventParams: RecvEnumerateInputEventParams + ) => void; } type ConnectionOptions = @@ -1526,6 +1536,78 @@ class SimConnectConnection extends EventEmitter { ); } + // TODO: implement 0x4e: executeAction(dataRequestID: number, actionID: string, values: RawBuffer) + + /** + * + * @returns sendId of packet (can be used to identify packet when exception event occurs) + */ + enumerateInputEvents(dataRequestID: number): number { + if (this._ourProtocol < Protocol.KittyHawk) throw Error(SimConnectError.BadVersion); + + const packet = this._beginPacket(0x4f).putInt32(dataRequestID); + return this._buildAndSend(packet); + } + + /** + * + * @returns sendId of packet (can be used to identify packet when exception event occurs) + */ + getInputEvent(dataRequestID: number, inputEventHashID: Long): number { + if (this._ourProtocol < Protocol.KittyHawk) throw Error(SimConnectError.BadVersion); + + const packet = this._beginPacket(0x50).putInt32(dataRequestID).putUint64(inputEventHashID); + return this._buildAndSend(packet); + } + + /** + * + * @returns sendId of packet (can be used to identify packet when exception event occurs) + */ + setInputEvent(inputEventHashID: Long, value: number | string): number { + if (this._ourProtocol < Protocol.KittyHawk) throw Error(SimConnectError.BadVersion); + + const packet = this._beginPacket(0x51).putUint64(inputEventHashID); + + if (typeof value === 'string') { + packet.putInt32(value.length).putString(value); + } else { + packet.putInt32(4).putFloat32(value); + } + + return this._buildAndSend(packet); + } + + /** + * + * @returns sendId of packet (can be used to identify packet when exception event occurs) + */ + subscribeInputEvent(inputEventHashID: Long): number { + if (this._ourProtocol < Protocol.KittyHawk) throw Error(SimConnectError.BadVersion); + + return this._buildAndSend(this._beginPacket(0x52).putUint64(inputEventHashID)); + } + + /** + * + * @returns sendId of packet (can be used to identify packet when exception event occurs) + */ + unsubscribeInputEvent(inputEventHashID: Long): number { + if (this._ourProtocol < Protocol.KittyHawk) throw Error(SimConnectError.BadVersion); + + return this._buildAndSend(this._beginPacket(0x53).putUint64(inputEventHashID)); + } + + /** + * + * @returns sendId of packet (can be used to identify packet when exception event occurs) + */ + enumerateInputEventParams(inputEventHashID: Long): number { + if (this._ourProtocol < Protocol.KittyHawk) throw Error(SimConnectError.BadVersion); + + return this._buildAndSend(this._beginPacket(0x54).putUint64(inputEventHashID)); + } + close() { if (this._openTimeout !== null) { clearTimeout(this._openTimeout); @@ -1657,6 +1739,20 @@ class SimConnectConnection extends EventEmitter { case RecvID.ID_CONTROLLERS_LIST: this.emit('controllersList', new RecvControllersList(data)); break; + case RecvID.ID_ACTION_CALLBACK: + break; + case RecvID.ID_ENUMERATE_INPUT_EVENTS: + this.emit('inputEventsList', new RecvEnumerateInputEvents(data)); + break; + case RecvID.ID_GET_INPUT_EVENT: + this.emit('getInputEvent', new RecvGetInputEvent(data)); + break; + case RecvID.ID_SUBSCRIBE_INPUT_EVENT: + this.emit('subscribeInputEvent', new RecvSubscribeInputEvent(data)); + break; + case RecvID.ID_ENUMERATE_INPUT_EVENT_PARAMS: + this.emit('enumerateInputEventParams', new RecvEnumerateInputEventParams(data)); + break; } } diff --git a/src/SimConnectPacketBuilder.ts b/src/SimConnectPacketBuilder.ts index 45c07e5..893c66f 100644 --- a/src/SimConnectPacketBuilder.ts +++ b/src/SimConnectPacketBuilder.ts @@ -49,6 +49,16 @@ export class SimConnectPacketBuilder { return this; } + putUint32(value: number, offset?: number) { + this.packetContent.writeUint32(value, offset); + return this; + } + + putUint64(value: Long, offset?: number) { + this.packetContent.writeUint64(value, offset); + return this; + } + putByte(value: number) { this.packetContent.writeByte(value); return this; diff --git a/src/SimConnectSocket.ts b/src/SimConnectSocket.ts index a4b5b19..8c9c372 100644 --- a/src/SimConnectSocket.ts +++ b/src/SimConnectSocket.ts @@ -40,6 +40,11 @@ enum RecvID { ID_FACILITY_MINIMAL_LIST, ID_JETWAY_DATA, ID_CONTROLLERS_LIST, + ID_ACTION_CALLBACK, + ID_ENUMERATE_INPUT_EVENTS, + ID_GET_INPUT_EVENT, + ID_SUBSCRIBE_INPUT_EVENT, + ID_ENUMERATE_INPUT_EVENT_PARAMS, } interface SimConnectMessage { diff --git a/src/datastructures/InputEventDescriptor.ts b/src/datastructures/InputEventDescriptor.ts new file mode 100644 index 0000000..54631b9 --- /dev/null +++ b/src/datastructures/InputEventDescriptor.ts @@ -0,0 +1,16 @@ +import { InputEventType } from '../enums/InputEventType'; +import { RawBuffer } from '../RawBuffer'; + +export class InputEventDescriptor { + name: string; + + inputEventIdHash: Long; + + type: InputEventType; + + constructor(data: RawBuffer) { + this.name = data.readString64(); + this.inputEventIdHash = data.readUint64(); + this.type = data.readUint32() as InputEventType; + } +} diff --git a/src/enums/InputEventType.ts b/src/enums/InputEventType.ts new file mode 100644 index 0000000..650f747 --- /dev/null +++ b/src/enums/InputEventType.ts @@ -0,0 +1,4 @@ +export const enum InputEventType { + DOUBLE, + STRING, +} diff --git a/src/enums/SimConnectException.ts b/src/enums/SimConnectException.ts index 992f6f3..851d9d5 100644 --- a/src/enums/SimConnectException.ts +++ b/src/enums/SimConnectException.ts @@ -38,6 +38,11 @@ export enum SimConnectException { OBJECT_ATC, OBJECT_SCHEDULE, JETWAY_DATA, + ACTION_NOT_FOUND, + NOT_AN_ACTION, + INCORRECT_ACTION_PARAMS, + GET_INPUT_EVENT_FAILED, + SET_INPUT_EVENT_FAILED, } module.exports = { diff --git a/src/recv/RecvEnumerateInputEventParams.ts b/src/recv/RecvEnumerateInputEventParams.ts new file mode 100644 index 0000000..be9f9f2 --- /dev/null +++ b/src/recv/RecvEnumerateInputEventParams.ts @@ -0,0 +1,12 @@ +import { RawBuffer } from '../RawBuffer'; + +export class RecvEnumerateInputEventParams { + inputEventIdHash: Long; + + value: string; + + constructor(data: RawBuffer) { + this.inputEventIdHash = data.readUint64(); + this.value = data.readStringV(); + } +} diff --git a/src/recv/RecvEnumerateInputEvents.ts b/src/recv/RecvEnumerateInputEvents.ts new file mode 100644 index 0000000..c678117 --- /dev/null +++ b/src/recv/RecvEnumerateInputEvents.ts @@ -0,0 +1,16 @@ +import { RecvListTemplate } from './RecvListTemplate'; +import { RawBuffer } from '../RawBuffer'; +import { InputEventDescriptor } from '../datastructures/InputEventDescriptor'; + +export class RecvEnumerateInputEvents extends RecvListTemplate { + inputEventDescriptors: InputEventDescriptor[] = []; + + constructor(data: RawBuffer) { + super(data); + + this.inputEventDescriptors = []; + for (let i = 0; i < this.arraySize; i++) { + this.inputEventDescriptors.push(new InputEventDescriptor(data)); + } + } +} diff --git a/src/recv/RecvGetInputEvent.ts b/src/recv/RecvGetInputEvent.ts new file mode 100644 index 0000000..867c8ba --- /dev/null +++ b/src/recv/RecvGetInputEvent.ts @@ -0,0 +1,27 @@ +import { RawBuffer } from '../RawBuffer'; +import { DataRequestId } from '../Types'; +import { InputEventType } from '../enums/InputEventType'; + +export class RecvGetInputEvent { + requestID: DataRequestId; + + type: InputEventType; + + value: number | string; + + constructor(data: RawBuffer) { + this.requestID = data.readInt32(); + this.type = data.readUint32(); + + switch (this.type) { + case InputEventType.STRING: + this.value = data.readString256(); + break; + case InputEventType.DOUBLE: + this.value = data.readFloat64(); + break; + default: + throw Error(`Unknown input event type: ${this.type}`); + } + } +} diff --git a/src/recv/RecvSubscribeInputEvent.ts b/src/recv/RecvSubscribeInputEvent.ts new file mode 100644 index 0000000..1999e93 --- /dev/null +++ b/src/recv/RecvSubscribeInputEvent.ts @@ -0,0 +1,26 @@ +import { RawBuffer } from '../RawBuffer'; +import { InputEventType } from '../enums/InputEventType'; + +export class RecvSubscribeInputEvent { + inputEventIdHash: Long; + + type: InputEventType; + + value: number | string; + + constructor(data: RawBuffer) { + this.inputEventIdHash = data.readUint64(); + this.type = data.readUint32(); + + switch (this.type) { + case InputEventType.STRING: + this.value = data.readString256(); + break; + case InputEventType.DOUBLE: + this.value = data.readFloat64(); + break; + default: + throw Error(`Unknown input event type: ${this.type}`); + } + } +}