@hla4ts/hla-api 0.1.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.
@@ -0,0 +1,1441 @@
1
+ /**
2
+ * RTI Ambassador
3
+ *
4
+ * This module provides the high-level HLA 4 RTI Ambassador API for TypeScript.
5
+ * The RTI Ambassador is the main interface for federates to interact with the RTI.
6
+ */
7
+
8
+ import { Session, type HlaCallbackRequestListener } from "@hla4ts/session";
9
+ import { type Transport, TlsTransport, TcpTransport } from "@hla4ts/transport";
10
+ import {
11
+ CallRequestCodec,
12
+ CallResponseCodec,
13
+ type CallRequest,
14
+ type CallResponse,
15
+ ResignAction,
16
+ } from "@hla4ts/proto";
17
+
18
+ import type { FederateAmbassador } from "./federate-ambassador.ts";
19
+ import { CallbackDispatcher } from "./callback-dispatcher.ts";
20
+ import {
21
+ NotConnected,
22
+ AlreadyConnected,
23
+ createExceptionFromProto,
24
+ CallNotAllowedFromWithinCallback,
25
+ } from "./exceptions.ts";
26
+ import {
27
+ convertJoinResult,
28
+ convertObjectInstanceHandle,
29
+ convertConfigurationResult,
30
+ convertLogicalTime,
31
+ convertLogicalTimeInterval,
32
+ convertTimeQueryReturn,
33
+ toProtoObjectClassHandle,
34
+ toProtoAttributeHandleSet,
35
+ toProtoInteractionClassHandle,
36
+ toProtoObjectInstanceHandle,
37
+ toProtoAttributeHandleValueMap,
38
+ toProtoParameterHandleValueMap,
39
+ toProtoLogicalTime,
40
+ toProtoLogicalTimeInterval,
41
+ toProtoFederateHandleSet,
42
+ toProtoRtiConfiguration,
43
+ toProtoCredentials,
44
+ toProtoFomModule,
45
+ toProtoFomModuleSet,
46
+ } from "./converters.ts";
47
+ import type {
48
+ ObjectClassHandle,
49
+ AttributeHandleSet,
50
+ InteractionClassHandle,
51
+ ObjectInstanceHandle,
52
+ AttributeHandleValueMap,
53
+ ParameterHandleValueMap,
54
+ UserSuppliedTag,
55
+ LogicalTime,
56
+ LogicalTimeInterval,
57
+ MessageRetractionReturn,
58
+ TimeQueryReturn,
59
+ JoinResult,
60
+ FederateHandleSet,
61
+ FomModule,
62
+ FomModuleSet,
63
+ ConfigurationResult,
64
+ ConnectionOptions,
65
+ } from "./types.ts";
66
+
67
+ // =============================================================================
68
+ // RTI Ambassador Options
69
+ // =============================================================================
70
+
71
+ /**
72
+ * Options for creating an RTI Ambassador.
73
+ */
74
+ export interface RTIAmbassadorOptions {
75
+ /** RTI host address */
76
+ host: string;
77
+
78
+ /** RTI port (default: 15165 for TLS, 15164 for TCP) */
79
+ port?: number;
80
+
81
+ /** Use TLS (default: true) */
82
+ useTls?: boolean;
83
+
84
+ /** Connection timeout in milliseconds (default: 30000) */
85
+ connectionTimeout?: number;
86
+
87
+ /** Response timeout in milliseconds (default: 180000) */
88
+ responseTimeout?: number;
89
+
90
+ /** TLS options (certificate paths, etc.) */
91
+ tlsOptions?: {
92
+ /** Path to CA certificate file */
93
+ ca?: string;
94
+ /** Path to client certificate file */
95
+ cert?: string;
96
+ /** Path to client private key file */
97
+ key?: string;
98
+ /** Skip server verification (INSECURE) */
99
+ rejectUnauthorized?: boolean;
100
+ };
101
+ }
102
+
103
+ // =============================================================================
104
+ // RTI Ambassador
105
+ // =============================================================================
106
+
107
+ /**
108
+ * RTI Ambassador - Main interface for federate-to-RTI communication.
109
+ *
110
+ * The RTI Ambassador provides methods for all HLA services:
111
+ * - Federation Management (create, join, resign, destroy)
112
+ * - Declaration Management (publish, subscribe)
113
+ * - Object Management (register, update, delete, interactions)
114
+ * - Time Management (constrained, regulation, advance)
115
+ * - Ownership Management (acquire, divest)
116
+ * - Support Services (handle/name lookups)
117
+ *
118
+ * @example
119
+ * ```ts
120
+ * import { RTIAmbassador, BaseFederateAmbassador, CallbackModel } from '@hla4ts/hla-api';
121
+ *
122
+ * // Create federate ambassador
123
+ * class MyFederateAmbassador extends BaseFederateAmbassador {
124
+ * discoverObjectInstance(objectInstance, objectClass, name) {
125
+ * console.log(`Discovered: ${name}`);
126
+ * }
127
+ * }
128
+ *
129
+ * // Create RTI ambassador
130
+ * const rtiAmbassador = new RTIAmbassador({ host: 'rti.example.com' });
131
+ *
132
+ * // Connect and join
133
+ * const federateAmbassador = new MyFederateAmbassador();
134
+ * await rtiAmbassador.connect(federateAmbassador, { callbackModel: CallbackModel.EVOKED });
135
+ * const joinResult = await rtiAmbassador.joinFederationExecution('MyFederate', 'FederateType', 'TestFederation');
136
+ *
137
+ * // ... do HLA operations ...
138
+ *
139
+ * // Clean up
140
+ * await rtiAmbassador.resignFederationExecution(ResignAction.DELETE_OBJECTS_THEN_DIVEST);
141
+ * await rtiAmbassador.disconnect();
142
+ * ```
143
+ */
144
+ export class RTIAmbassador {
145
+ private readonly _options: Required<RTIAmbassadorOptions>;
146
+ private _transport: Transport | null = null;
147
+ private _session: Session | null = null;
148
+ private _callbackDispatcher: CallbackDispatcher | null = null;
149
+ private _inCallback = false;
150
+
151
+ /**
152
+ * Create a new RTI Ambassador.
153
+ *
154
+ * @param options - Connection options
155
+ */
156
+ constructor(options: RTIAmbassadorOptions) {
157
+ this._options = {
158
+ host: options.host,
159
+ port: options.port ?? (options.useTls !== false ? 15165 : 15164),
160
+ useTls: options.useTls ?? true,
161
+ connectionTimeout: options.connectionTimeout ?? 30000,
162
+ responseTimeout: options.responseTimeout ?? 180000,
163
+ tlsOptions: options.tlsOptions ?? {},
164
+ };
165
+ }
166
+
167
+ // ===========================================================================
168
+ // Connection Management
169
+ // ===========================================================================
170
+
171
+ /**
172
+ * Check if connected to the RTI.
173
+ */
174
+ get isConnected(): boolean {
175
+ return this._session?.isOperational ?? false;
176
+ }
177
+
178
+ /**
179
+ * Connect to the RTI.
180
+ *
181
+ * @param federateAmbassador - The federate ambassador to receive callbacks
182
+ * @param options - Connection options
183
+ * @returns Configuration result
184
+ */
185
+ async connect(
186
+ federateAmbassador: FederateAmbassador,
187
+ options: ConnectionOptions
188
+ ): Promise<ConfigurationResult> {
189
+ if (this._session) {
190
+ throw new AlreadyConnected("Already connected to RTI");
191
+ }
192
+
193
+ this._callbackDispatcher = new CallbackDispatcher(federateAmbassador);
194
+
195
+ // Create transport
196
+ if (this._options.useTls) {
197
+ this._transport = new TlsTransport({
198
+ host: this._options.host,
199
+ port: this._options.port,
200
+ ...this._options.tlsOptions,
201
+ });
202
+ } else {
203
+ this._transport = new TcpTransport({
204
+ host: this._options.host,
205
+ port: this._options.port,
206
+ });
207
+ }
208
+
209
+ // Create session
210
+ this._session = new Session(this._transport, {
211
+ connectionTimeout: this._options.connectionTimeout,
212
+ responseTimeout: this._options.responseTimeout,
213
+ });
214
+
215
+ // Create callback listener
216
+ const callbackListener: HlaCallbackRequestListener = {
217
+ onHlaCallbackRequest: (seqNum: number, callbackData: Uint8Array) => {
218
+ this._handleCallback(seqNum, callbackData);
219
+ },
220
+ };
221
+
222
+ try {
223
+ // Start session
224
+ await this._session.start(callbackListener);
225
+
226
+ // Send Connect request
227
+ let callRequest: CallRequest;
228
+
229
+ if (options.credentials && options.rtiConfiguration) {
230
+ callRequest = {
231
+ connectWithConfigurationAndCredentialsRequest: {
232
+ rtiConfiguration: toProtoRtiConfiguration(options.rtiConfiguration),
233
+ credentials: toProtoCredentials(options.credentials),
234
+ },
235
+ };
236
+ } else if (options.credentials) {
237
+ callRequest = {
238
+ connectWithCredentialsRequest: {
239
+ credentials: toProtoCredentials(options.credentials),
240
+ },
241
+ };
242
+ } else if (options.rtiConfiguration) {
243
+ callRequest = {
244
+ connectWithConfigurationRequest: {
245
+ rtiConfiguration: toProtoRtiConfiguration(options.rtiConfiguration),
246
+ },
247
+ };
248
+ } else {
249
+ callRequest = {
250
+ connectRequest: {},
251
+ };
252
+ }
253
+
254
+ const response = await this._doHlaCall(callRequest);
255
+
256
+ // Extract configuration result
257
+ if (response.connectResponse) {
258
+ return convertConfigurationResult(response.connectResponse.configurationResult);
259
+ }
260
+ if (response.connectWithCredentialsResponse) {
261
+ return convertConfigurationResult(response.connectWithCredentialsResponse.configurationResult);
262
+ }
263
+ if (response.connectWithConfigurationResponse) {
264
+ return convertConfigurationResult(response.connectWithConfigurationResponse.configurationResult);
265
+ }
266
+ if (response.connectWithConfigurationAndCredentialsResponse) {
267
+ return convertConfigurationResult(response.connectWithConfigurationAndCredentialsResponse.configurationResult);
268
+ }
269
+
270
+ return { configurationUsed: false };
271
+ } catch (error) {
272
+ // Clean up on failure
273
+ this._cleanup();
274
+ throw error;
275
+ }
276
+ }
277
+
278
+ /**
279
+ * Disconnect from the RTI.
280
+ */
281
+ async disconnect(): Promise<void> {
282
+ if (!this._session) {
283
+ return; // Already disconnected
284
+ }
285
+
286
+ try {
287
+ // Send Disconnect request
288
+ await this._doHlaCall({ disconnectRequest: {} });
289
+ } finally {
290
+ // Terminate session
291
+ await this._session.terminate().catch(() => {});
292
+ this._cleanup();
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Disconnect immediately without waiting for RTI responses.
298
+ */
299
+ async disconnectNow(): Promise<void> {
300
+ if (!this._session) {
301
+ return;
302
+ }
303
+ try {
304
+ await this._session.terminate(0);
305
+ } catch {
306
+ // Best effort; fall through to cleanup.
307
+ } finally {
308
+ this._cleanup();
309
+ }
310
+ }
311
+
312
+ // ===========================================================================
313
+ // Federation Management
314
+ // ===========================================================================
315
+
316
+ /**
317
+ * Create a federation execution.
318
+ *
319
+ * @param federationName - Name of the federation
320
+ * @param fomModule - FOM module
321
+ */
322
+ async createFederationExecution(federationName: string, fomModule: FomModule): Promise<void> {
323
+ await this._doHlaCall({
324
+ createFederationExecutionRequest: {
325
+ federationName,
326
+ fomModule: toProtoFomModule(fomModule),
327
+ },
328
+ });
329
+ }
330
+
331
+ /**
332
+ * Create a federation execution with time representation.
333
+ *
334
+ * @param federationName - Name of the federation
335
+ * @param fomModule - FOM module
336
+ * @param logicalTimeImplementationName - Name of the logical time factory
337
+ */
338
+ async createFederationExecutionWithTime(
339
+ federationName: string,
340
+ fomModule: FomModule,
341
+ logicalTimeImplementationName: string
342
+ ): Promise<void> {
343
+ await this._doHlaCall({
344
+ createFederationExecutionWithTimeRequest: {
345
+ federationName,
346
+ fomModule: toProtoFomModule(fomModule),
347
+ logicalTimeImplementationName,
348
+ },
349
+ });
350
+ }
351
+
352
+ /**
353
+ * Create a federation execution with multiple FOM modules.
354
+ *
355
+ * @param federationName - Name of the federation
356
+ * @param fomModules - FOM modules
357
+ */
358
+ async createFederationExecutionWithModules(
359
+ federationName: string,
360
+ fomModules: FomModuleSet
361
+ ): Promise<void> {
362
+ await this._doHlaCall({
363
+ createFederationExecutionWithModulesRequest: {
364
+ federationName,
365
+ fomModules: toProtoFomModuleSet(fomModules),
366
+ },
367
+ });
368
+ }
369
+
370
+ /**
371
+ * Create a federation execution with multiple FOM modules and time.
372
+ *
373
+ * @param federationName - Name of the federation
374
+ * @param fomModules - FOM modules
375
+ * @param logicalTimeImplementationName - Name of the logical time factory
376
+ */
377
+ async createFederationExecutionWithModulesAndTime(
378
+ federationName: string,
379
+ fomModules: FomModuleSet,
380
+ logicalTimeImplementationName: string
381
+ ): Promise<void> {
382
+ await this._doHlaCall({
383
+ createFederationExecutionWithModulesAndTimeRequest: {
384
+ federationName,
385
+ fomModules: toProtoFomModuleSet(fomModules),
386
+ logicalTimeImplementationName,
387
+ },
388
+ });
389
+ }
390
+
391
+ /**
392
+ * Destroy a federation execution.
393
+ *
394
+ * @param federationName - Name of the federation to destroy
395
+ */
396
+ async destroyFederationExecution(federationName: string): Promise<void> {
397
+ await this._doHlaCall({
398
+ destroyFederationExecutionRequest: { federationName },
399
+ });
400
+ }
401
+
402
+ /**
403
+ * List federation executions (async - results come via callback).
404
+ */
405
+ async listFederationExecutions(): Promise<void> {
406
+ await this._doHlaCall({ listFederationExecutionsRequest: {} });
407
+ }
408
+
409
+ /**
410
+ * List federation execution members (async - results come via callback).
411
+ *
412
+ * @param federationName - Name of the federation
413
+ */
414
+ async listFederationExecutionMembers(federationName: string): Promise<void> {
415
+ await this._doHlaCall({
416
+ listFederationExecutionMembersRequest: { federationName },
417
+ });
418
+ }
419
+
420
+ /**
421
+ * Join a federation execution.
422
+ *
423
+ * @param federateType - Type of the federate
424
+ * @param federationName - Name of the federation to join
425
+ * @returns Join result with federate handle and time implementation
426
+ */
427
+ async joinFederationExecution(
428
+ federateType: string,
429
+ federationName: string
430
+ ): Promise<JoinResult> {
431
+ this._throwIfInCallback("joinFederationExecution");
432
+ const response = await this._doHlaCall({
433
+ joinFederationExecutionRequest: { federateType, federationName },
434
+ });
435
+ return convertJoinResult(response.joinFederationExecutionResponse?.result);
436
+ }
437
+
438
+ /**
439
+ * Join a federation execution with a specific name.
440
+ *
441
+ * @param federateName - Name for this federate
442
+ * @param federateType - Type of the federate
443
+ * @param federationName - Name of the federation to join
444
+ * @returns Join result
445
+ */
446
+ async joinFederationExecutionWithName(
447
+ federateName: string,
448
+ federateType: string,
449
+ federationName: string
450
+ ): Promise<JoinResult> {
451
+ this._throwIfInCallback("joinFederationExecution");
452
+ const response = await this._doHlaCall({
453
+ joinFederationExecutionWithNameRequest: { federateName, federateType, federationName },
454
+ });
455
+ return convertJoinResult(response.joinFederationExecutionWithNameResponse?.result);
456
+ }
457
+
458
+ /**
459
+ * Join a federation execution with additional FOM modules.
460
+ *
461
+ * @param federateType - Type of the federate
462
+ * @param federationName - Name of the federation to join
463
+ * @param additionalFomModules - Additional FOM modules
464
+ * @returns Join result
465
+ */
466
+ async joinFederationExecutionWithModules(
467
+ federateType: string,
468
+ federationName: string,
469
+ additionalFomModules: FomModuleSet
470
+ ): Promise<JoinResult> {
471
+ this._throwIfInCallback("joinFederationExecution");
472
+ const response = await this._doHlaCall({
473
+ joinFederationExecutionWithModulesRequest: {
474
+ federateType,
475
+ federationName,
476
+ additionalFomModules: toProtoFomModuleSet(additionalFomModules),
477
+ },
478
+ });
479
+ return convertJoinResult(response.joinFederationExecutionWithModulesResponse?.result);
480
+ }
481
+
482
+ /**
483
+ * Join a federation execution with name and additional FOM modules.
484
+ *
485
+ * @param federateName - Name for this federate
486
+ * @param federateType - Type of the federate
487
+ * @param federationName - Name of the federation to join
488
+ * @param additionalFomModules - Additional FOM modules
489
+ * @returns Join result
490
+ */
491
+ async joinFederationExecutionWithNameAndModules(
492
+ federateName: string,
493
+ federateType: string,
494
+ federationName: string,
495
+ additionalFomModules: FomModuleSet
496
+ ): Promise<JoinResult> {
497
+ this._throwIfInCallback("joinFederationExecution");
498
+ const response = await this._doHlaCall({
499
+ joinFederationExecutionWithNameAndModulesRequest: {
500
+ federateName,
501
+ federateType,
502
+ federationName,
503
+ additionalFomModules: toProtoFomModuleSet(additionalFomModules),
504
+ },
505
+ });
506
+ return convertJoinResult(response.joinFederationExecutionWithNameAndModulesResponse?.result);
507
+ }
508
+
509
+ /**
510
+ * Resign from a federation execution.
511
+ *
512
+ * @param resignAction - Action to take when resigning
513
+ */
514
+ async resignFederationExecution(resignAction: ResignAction): Promise<void> {
515
+ this._throwIfInCallback("resignFederationExecution");
516
+ await this._doHlaCall({
517
+ resignFederationExecutionRequest: { resignAction },
518
+ });
519
+ }
520
+
521
+ // ===========================================================================
522
+ // Synchronization Point Services
523
+ // ===========================================================================
524
+
525
+ /**
526
+ * Register a federation synchronization point.
527
+ *
528
+ * @param label - Synchronization point label
529
+ * @param userSuppliedTag - User-supplied tag
530
+ */
531
+ async registerFederationSynchronizationPoint(
532
+ label: string,
533
+ userSuppliedTag: UserSuppliedTag
534
+ ): Promise<void> {
535
+ await this._doHlaCall({
536
+ registerFederationSynchronizationPointRequest: {
537
+ synchronizationPointLabel: label,
538
+ userSuppliedTag,
539
+ },
540
+ });
541
+ }
542
+
543
+ /**
544
+ * Register a federation synchronization point with a specific set of federates.
545
+ *
546
+ * @param label - Synchronization point label
547
+ * @param userSuppliedTag - User-supplied tag
548
+ * @param synchronizationSet - Set of federates to synchronize
549
+ */
550
+ async registerFederationSynchronizationPointWithSet(
551
+ label: string,
552
+ userSuppliedTag: UserSuppliedTag,
553
+ synchronizationSet: FederateHandleSet
554
+ ): Promise<void> {
555
+ await this._doHlaCall({
556
+ registerFederationSynchronizationPointWithSetRequest: {
557
+ synchronizationPointLabel: label,
558
+ userSuppliedTag,
559
+ synchronizationSet: toProtoFederateHandleSet(synchronizationSet),
560
+ },
561
+ });
562
+ }
563
+
564
+ /**
565
+ * Indicate that this federate has achieved a synchronization point.
566
+ *
567
+ * @param label - Synchronization point label
568
+ * @param successfully - Whether the synchronization was successful
569
+ */
570
+ async synchronizationPointAchieved(label: string, successfully: boolean = true): Promise<void> {
571
+ await this._doHlaCall({
572
+ synchronizationPointAchievedRequest: {
573
+ synchronizationPointLabel: label,
574
+ successfully,
575
+ },
576
+ });
577
+ }
578
+
579
+ // ===========================================================================
580
+ // Declaration Management
581
+ // ===========================================================================
582
+
583
+ /**
584
+ * Publish object class attributes.
585
+ *
586
+ * @param objectClass - Object class handle
587
+ * @param attributes - Set of attribute handles to publish
588
+ */
589
+ async publishObjectClassAttributes(
590
+ objectClass: ObjectClassHandle,
591
+ attributes: AttributeHandleSet
592
+ ): Promise<void> {
593
+ await this._doHlaCall({
594
+ publishObjectClassAttributesRequest: {
595
+ objectClass: toProtoObjectClassHandle(objectClass),
596
+ attributes: toProtoAttributeHandleSet(attributes),
597
+ },
598
+ });
599
+ }
600
+
601
+ /**
602
+ * Unpublish all attributes of an object class.
603
+ *
604
+ * @param objectClass - Object class handle
605
+ */
606
+ async unpublishObjectClass(objectClass: ObjectClassHandle): Promise<void> {
607
+ await this._doHlaCall({
608
+ unpublishObjectClassRequest: {
609
+ objectClass: toProtoObjectClassHandle(objectClass),
610
+ },
611
+ });
612
+ }
613
+
614
+ /**
615
+ * Unpublish specific attributes of an object class.
616
+ *
617
+ * @param objectClass - Object class handle
618
+ * @param attributes - Set of attribute handles to unpublish
619
+ */
620
+ async unpublishObjectClassAttributes(
621
+ objectClass: ObjectClassHandle,
622
+ attributes: AttributeHandleSet
623
+ ): Promise<void> {
624
+ await this._doHlaCall({
625
+ unpublishObjectClassAttributesRequest: {
626
+ objectClass: toProtoObjectClassHandle(objectClass),
627
+ attributes: toProtoAttributeHandleSet(attributes),
628
+ },
629
+ });
630
+ }
631
+
632
+ /**
633
+ * Publish an interaction class.
634
+ *
635
+ * @param interactionClass - Interaction class handle
636
+ */
637
+ async publishInteractionClass(interactionClass: InteractionClassHandle): Promise<void> {
638
+ await this._doHlaCall({
639
+ publishInteractionClassRequest: {
640
+ interactionClass: toProtoInteractionClassHandle(interactionClass),
641
+ },
642
+ });
643
+ }
644
+
645
+ /**
646
+ * Unpublish an interaction class.
647
+ *
648
+ * @param interactionClass - Interaction class handle
649
+ */
650
+ async unpublishInteractionClass(interactionClass: InteractionClassHandle): Promise<void> {
651
+ await this._doHlaCall({
652
+ unpublishInteractionClassRequest: {
653
+ interactionClass: toProtoInteractionClassHandle(interactionClass),
654
+ },
655
+ });
656
+ }
657
+
658
+ /**
659
+ * Subscribe to object class attributes.
660
+ *
661
+ * @param objectClass - Object class handle
662
+ * @param attributes - Set of attribute handles to subscribe to
663
+ */
664
+ async subscribeObjectClassAttributes(
665
+ objectClass: ObjectClassHandle,
666
+ attributes: AttributeHandleSet
667
+ ): Promise<void> {
668
+ await this._doHlaCall({
669
+ subscribeObjectClassAttributesRequest: {
670
+ objectClass: toProtoObjectClassHandle(objectClass),
671
+ attributes: toProtoAttributeHandleSet(attributes),
672
+ },
673
+ });
674
+ }
675
+
676
+ /**
677
+ * Subscribe to object class attributes passively.
678
+ *
679
+ * @param objectClass - Object class handle
680
+ * @param attributes - Set of attribute handles
681
+ */
682
+ async subscribeObjectClassAttributesPassively(
683
+ objectClass: ObjectClassHandle,
684
+ attributes: AttributeHandleSet
685
+ ): Promise<void> {
686
+ await this._doHlaCall({
687
+ subscribeObjectClassAttributesPassivelyRequest: {
688
+ objectClass: toProtoObjectClassHandle(objectClass),
689
+ attributes: toProtoAttributeHandleSet(attributes),
690
+ },
691
+ });
692
+ }
693
+
694
+ /**
695
+ * Unsubscribe from all attributes of an object class.
696
+ *
697
+ * @param objectClass - Object class handle
698
+ */
699
+ async unsubscribeObjectClass(objectClass: ObjectClassHandle): Promise<void> {
700
+ await this._doHlaCall({
701
+ unsubscribeObjectClassRequest: {
702
+ objectClass: toProtoObjectClassHandle(objectClass),
703
+ },
704
+ });
705
+ }
706
+
707
+ /**
708
+ * Unsubscribe from specific attributes of an object class.
709
+ *
710
+ * @param objectClass - Object class handle
711
+ * @param attributes - Set of attribute handles
712
+ */
713
+ async unsubscribeObjectClassAttributes(
714
+ objectClass: ObjectClassHandle,
715
+ attributes: AttributeHandleSet
716
+ ): Promise<void> {
717
+ await this._doHlaCall({
718
+ unsubscribeObjectClassAttributesRequest: {
719
+ objectClass: toProtoObjectClassHandle(objectClass),
720
+ attributes: toProtoAttributeHandleSet(attributes),
721
+ },
722
+ });
723
+ }
724
+
725
+ /**
726
+ * Subscribe to an interaction class.
727
+ *
728
+ * @param interactionClass - Interaction class handle
729
+ */
730
+ async subscribeInteractionClass(interactionClass: InteractionClassHandle): Promise<void> {
731
+ await this._doHlaCall({
732
+ subscribeInteractionClassRequest: {
733
+ interactionClass: toProtoInteractionClassHandle(interactionClass),
734
+ },
735
+ });
736
+ }
737
+
738
+ /**
739
+ * Subscribe to an interaction class passively.
740
+ *
741
+ * @param interactionClass - Interaction class handle
742
+ */
743
+ async subscribeInteractionClassPassively(interactionClass: InteractionClassHandle): Promise<void> {
744
+ await this._doHlaCall({
745
+ subscribeInteractionClassPassivelyRequest: {
746
+ interactionClass: toProtoInteractionClassHandle(interactionClass),
747
+ },
748
+ });
749
+ }
750
+
751
+ /**
752
+ * Unsubscribe from an interaction class.
753
+ *
754
+ * @param interactionClass - Interaction class handle
755
+ */
756
+ async unsubscribeInteractionClass(interactionClass: InteractionClassHandle): Promise<void> {
757
+ await this._doHlaCall({
758
+ unsubscribeInteractionClassRequest: {
759
+ interactionClass: toProtoInteractionClassHandle(interactionClass),
760
+ },
761
+ });
762
+ }
763
+
764
+ // ===========================================================================
765
+ // Object Management
766
+ // ===========================================================================
767
+
768
+ /**
769
+ * Reserve an object instance name.
770
+ *
771
+ * @param objectInstanceName - Name to reserve
772
+ */
773
+ async reserveObjectInstanceName(objectInstanceName: string): Promise<void> {
774
+ await this._doHlaCall({
775
+ reserveObjectInstanceNameRequest: { objectInstanceName },
776
+ });
777
+ }
778
+
779
+ /**
780
+ * Release a reserved object instance name.
781
+ *
782
+ * @param objectInstanceName - Name to release
783
+ */
784
+ async releaseObjectInstanceName(objectInstanceName: string): Promise<void> {
785
+ await this._doHlaCall({
786
+ releaseObjectInstanceNameRequest: { objectInstanceName },
787
+ });
788
+ }
789
+
790
+ /**
791
+ * Register a new object instance.
792
+ *
793
+ * @param objectClass - Object class handle
794
+ * @returns Handle of the registered object instance
795
+ */
796
+ async registerObjectInstance(objectClass: ObjectClassHandle): Promise<ObjectInstanceHandle> {
797
+ const response = await this._doHlaCall({
798
+ registerObjectInstanceRequest: {
799
+ objectClass: toProtoObjectClassHandle(objectClass),
800
+ },
801
+ });
802
+ return convertObjectInstanceHandle(response.registerObjectInstanceResponse?.result);
803
+ }
804
+
805
+ /**
806
+ * Register a new object instance with a specific name.
807
+ *
808
+ * @param objectClass - Object class handle
809
+ * @param objectInstanceName - Name for the object instance
810
+ * @returns Handle of the registered object instance
811
+ */
812
+ async registerObjectInstanceWithName(
813
+ objectClass: ObjectClassHandle,
814
+ objectInstanceName: string
815
+ ): Promise<ObjectInstanceHandle> {
816
+ const response = await this._doHlaCall({
817
+ registerObjectInstanceWithNameRequest: {
818
+ objectClass: toProtoObjectClassHandle(objectClass),
819
+ objectInstanceName,
820
+ },
821
+ });
822
+ return convertObjectInstanceHandle(response.registerObjectInstanceWithNameResponse?.result);
823
+ }
824
+
825
+ /**
826
+ * Update attribute values of an object instance.
827
+ *
828
+ * @param objectInstance - Object instance handle
829
+ * @param attributeValues - Map of attribute handles to values
830
+ * @param userSuppliedTag - User-supplied tag
831
+ */
832
+ async updateAttributeValues(
833
+ objectInstance: ObjectInstanceHandle,
834
+ attributeValues: AttributeHandleValueMap,
835
+ userSuppliedTag: UserSuppliedTag
836
+ ): Promise<void> {
837
+ await this._doHlaCall({
838
+ updateAttributeValuesRequest: {
839
+ objectInstance: toProtoObjectInstanceHandle(objectInstance),
840
+ attributeValues: toProtoAttributeHandleValueMap(attributeValues),
841
+ userSuppliedTag,
842
+ },
843
+ });
844
+ }
845
+
846
+ /**
847
+ * Update attribute values with a timestamp.
848
+ *
849
+ * @param objectInstance - Object instance handle
850
+ * @param attributeValues - Map of attribute handles to values
851
+ * @param userSuppliedTag - User-supplied tag
852
+ * @param time - Logical time
853
+ * @returns Message retraction info
854
+ */
855
+ async updateAttributeValuesWithTime(
856
+ objectInstance: ObjectInstanceHandle,
857
+ attributeValues: AttributeHandleValueMap,
858
+ userSuppliedTag: UserSuppliedTag,
859
+ time: LogicalTime
860
+ ): Promise<MessageRetractionReturn> {
861
+ const response = await this._doHlaCall({
862
+ updateAttributeValuesWithTimeRequest: {
863
+ objectInstance: toProtoObjectInstanceHandle(objectInstance),
864
+ attributeValues: toProtoAttributeHandleValueMap(attributeValues),
865
+ userSuppliedTag,
866
+ time: toProtoLogicalTime(time),
867
+ },
868
+ });
869
+ const result = response.updateAttributeValuesWithTimeResponse?.result;
870
+ return {
871
+ retractionHandleIsValid: result?.retractionHandleIsValid ?? false,
872
+ retractionHandle: result?.messageRetractionHandle?.data,
873
+ };
874
+ }
875
+
876
+ /**
877
+ * Send an interaction.
878
+ *
879
+ * @param interactionClass - Interaction class handle
880
+ * @param parameterValues - Map of parameter handles to values
881
+ * @param userSuppliedTag - User-supplied tag
882
+ */
883
+ async sendInteraction(
884
+ interactionClass: InteractionClassHandle,
885
+ parameterValues: ParameterHandleValueMap,
886
+ userSuppliedTag: UserSuppliedTag
887
+ ): Promise<void> {
888
+ await this._doHlaCall({
889
+ sendInteractionRequest: {
890
+ interactionClass: toProtoInteractionClassHandle(interactionClass),
891
+ parameterValues: toProtoParameterHandleValueMap(parameterValues),
892
+ userSuppliedTag,
893
+ },
894
+ });
895
+ }
896
+
897
+ /**
898
+ * Send an interaction with a timestamp.
899
+ *
900
+ * @param interactionClass - Interaction class handle
901
+ * @param parameterValues - Map of parameter handles to values
902
+ * @param userSuppliedTag - User-supplied tag
903
+ * @param time - Logical time
904
+ * @returns Message retraction info
905
+ */
906
+ async sendInteractionWithTime(
907
+ interactionClass: InteractionClassHandle,
908
+ parameterValues: ParameterHandleValueMap,
909
+ userSuppliedTag: UserSuppliedTag,
910
+ time: LogicalTime
911
+ ): Promise<MessageRetractionReturn> {
912
+ const response = await this._doHlaCall({
913
+ sendInteractionWithTimeRequest: {
914
+ interactionClass: toProtoInteractionClassHandle(interactionClass),
915
+ parameterValues: toProtoParameterHandleValueMap(parameterValues),
916
+ userSuppliedTag,
917
+ time: toProtoLogicalTime(time),
918
+ },
919
+ });
920
+ const result = response.sendInteractionWithTimeResponse?.result;
921
+ return {
922
+ retractionHandleIsValid: result?.retractionHandleIsValid ?? false,
923
+ retractionHandle: result?.messageRetractionHandle?.data,
924
+ };
925
+ }
926
+
927
+ /**
928
+ * Delete an object instance.
929
+ *
930
+ * @param objectInstance - Object instance handle
931
+ * @param userSuppliedTag - User-supplied tag
932
+ */
933
+ async deleteObjectInstance(
934
+ objectInstance: ObjectInstanceHandle,
935
+ userSuppliedTag: UserSuppliedTag
936
+ ): Promise<void> {
937
+ await this._doHlaCall({
938
+ deleteObjectInstanceRequest: {
939
+ objectInstance: toProtoObjectInstanceHandle(objectInstance),
940
+ userSuppliedTag,
941
+ },
942
+ });
943
+ }
944
+
945
+ /**
946
+ * Delete an object instance with a timestamp.
947
+ *
948
+ * @param objectInstance - Object instance handle
949
+ * @param userSuppliedTag - User-supplied tag
950
+ * @param time - Logical time
951
+ * @returns Message retraction info
952
+ */
953
+ async deleteObjectInstanceWithTime(
954
+ objectInstance: ObjectInstanceHandle,
955
+ userSuppliedTag: UserSuppliedTag,
956
+ time: LogicalTime
957
+ ): Promise<MessageRetractionReturn> {
958
+ const response = await this._doHlaCall({
959
+ deleteObjectInstanceWithTimeRequest: {
960
+ objectInstance: toProtoObjectInstanceHandle(objectInstance),
961
+ userSuppliedTag,
962
+ time: toProtoLogicalTime(time),
963
+ },
964
+ });
965
+ const result = response.deleteObjectInstanceWithTimeResponse?.result;
966
+ return {
967
+ retractionHandleIsValid: result?.retractionHandleIsValid ?? false,
968
+ retractionHandle: result?.messageRetractionHandle?.data,
969
+ };
970
+ }
971
+
972
+ /**
973
+ * Locally delete an object instance (remove from local object model without notifying others).
974
+ *
975
+ * @param objectInstance - Object instance handle
976
+ */
977
+ async localDeleteObjectInstance(objectInstance: ObjectInstanceHandle): Promise<void> {
978
+ await this._doHlaCall({
979
+ localDeleteObjectInstanceRequest: {
980
+ objectInstance: toProtoObjectInstanceHandle(objectInstance),
981
+ },
982
+ });
983
+ }
984
+
985
+ /**
986
+ * Request attribute value update for a specific object instance.
987
+ *
988
+ * @param objectInstance - Object instance handle
989
+ * @param attributes - Set of attributes to request
990
+ * @param userSuppliedTag - User-supplied tag
991
+ */
992
+ async requestInstanceAttributeValueUpdate(
993
+ objectInstance: ObjectInstanceHandle,
994
+ attributes: AttributeHandleSet,
995
+ userSuppliedTag: UserSuppliedTag
996
+ ): Promise<void> {
997
+ await this._doHlaCall({
998
+ requestInstanceAttributeValueUpdateRequest: {
999
+ objectInstance: toProtoObjectInstanceHandle(objectInstance),
1000
+ attributes: toProtoAttributeHandleSet(attributes),
1001
+ userSuppliedTag,
1002
+ },
1003
+ });
1004
+ }
1005
+
1006
+ /**
1007
+ * Request attribute value update for all objects of a class.
1008
+ *
1009
+ * @param objectClass - Object class handle
1010
+ * @param attributes - Set of attributes to request
1011
+ * @param userSuppliedTag - User-supplied tag
1012
+ */
1013
+ async requestClassAttributeValueUpdate(
1014
+ objectClass: ObjectClassHandle,
1015
+ attributes: AttributeHandleSet,
1016
+ userSuppliedTag: UserSuppliedTag
1017
+ ): Promise<void> {
1018
+ await this._doHlaCall({
1019
+ requestClassAttributeValueUpdateRequest: {
1020
+ objectClass: toProtoObjectClassHandle(objectClass),
1021
+ attributes: toProtoAttributeHandleSet(attributes),
1022
+ userSuppliedTag,
1023
+ },
1024
+ });
1025
+ }
1026
+
1027
+ // ===========================================================================
1028
+ // Time Management
1029
+ // ===========================================================================
1030
+
1031
+ /**
1032
+ * Enable time regulation.
1033
+ *
1034
+ * @param lookahead - Lookahead interval
1035
+ */
1036
+ async enableTimeRegulation(lookahead: LogicalTimeInterval): Promise<void> {
1037
+ await this._doHlaCall({
1038
+ enableTimeRegulationRequest: {
1039
+ lookahead: toProtoLogicalTimeInterval(lookahead),
1040
+ },
1041
+ });
1042
+ }
1043
+
1044
+ /**
1045
+ * Disable time regulation.
1046
+ */
1047
+ async disableTimeRegulation(): Promise<void> {
1048
+ await this._doHlaCall({ disableTimeRegulationRequest: {} });
1049
+ }
1050
+
1051
+ /**
1052
+ * Enable time constrained mode.
1053
+ */
1054
+ async enableTimeConstrained(): Promise<void> {
1055
+ await this._doHlaCall({ enableTimeConstrainedRequest: {} });
1056
+ }
1057
+
1058
+ /**
1059
+ * Disable time constrained mode.
1060
+ */
1061
+ async disableTimeConstrained(): Promise<void> {
1062
+ await this._doHlaCall({ disableTimeConstrainedRequest: {} });
1063
+ }
1064
+
1065
+ /**
1066
+ * Request a time advance.
1067
+ *
1068
+ * @param time - Target logical time
1069
+ */
1070
+ async timeAdvanceRequest(time: LogicalTime): Promise<void> {
1071
+ await this._doHlaCall({
1072
+ timeAdvanceRequestRequest: {
1073
+ time: toProtoLogicalTime(time),
1074
+ },
1075
+ });
1076
+ }
1077
+
1078
+ /**
1079
+ * Request a time advance available.
1080
+ *
1081
+ * @param time - Target logical time
1082
+ */
1083
+ async timeAdvanceRequestAvailable(time: LogicalTime): Promise<void> {
1084
+ await this._doHlaCall({
1085
+ timeAdvanceRequestAvailableRequest: {
1086
+ time: toProtoLogicalTime(time),
1087
+ },
1088
+ });
1089
+ }
1090
+
1091
+ /**
1092
+ * Request next message.
1093
+ *
1094
+ * @param time - Target logical time
1095
+ */
1096
+ async nextMessageRequest(time: LogicalTime): Promise<void> {
1097
+ await this._doHlaCall({
1098
+ nextMessageRequestRequest: {
1099
+ time: toProtoLogicalTime(time),
1100
+ },
1101
+ });
1102
+ }
1103
+
1104
+ /**
1105
+ * Request next message available.
1106
+ *
1107
+ * @param time - Target logical time
1108
+ */
1109
+ async nextMessageRequestAvailable(time: LogicalTime): Promise<void> {
1110
+ await this._doHlaCall({
1111
+ nextMessageRequestAvailableRequest: {
1112
+ time: toProtoLogicalTime(time),
1113
+ },
1114
+ });
1115
+ }
1116
+
1117
+ /**
1118
+ * Flush the message queue.
1119
+ *
1120
+ * @param time - Target logical time
1121
+ */
1122
+ async flushQueueRequest(time: LogicalTime): Promise<void> {
1123
+ await this._doHlaCall({
1124
+ flushQueueRequestRequest: {
1125
+ time: toProtoLogicalTime(time),
1126
+ },
1127
+ });
1128
+ }
1129
+
1130
+ /**
1131
+ * Modify the lookahead interval.
1132
+ *
1133
+ * @param lookahead - New lookahead interval
1134
+ */
1135
+ async modifyLookahead(lookahead: LogicalTimeInterval): Promise<void> {
1136
+ await this._doHlaCall({
1137
+ modifyLookaheadRequest: {
1138
+ lookahead: toProtoLogicalTimeInterval(lookahead),
1139
+ },
1140
+ });
1141
+ }
1142
+
1143
+ /**
1144
+ * Query the Greatest Available Logical Time (GALT).
1145
+ */
1146
+ async queryGALT(): Promise<TimeQueryReturn> {
1147
+ const response = await this._doHlaCall({ queryGALTRequest: {} });
1148
+ return convertTimeQueryReturn(response.queryGALTResponse?.result);
1149
+ }
1150
+
1151
+ /**
1152
+ * Query the current logical time.
1153
+ */
1154
+ async queryLogicalTime(): Promise<LogicalTime> {
1155
+ const response = await this._doHlaCall({ queryLogicalTimeRequest: {} });
1156
+ return convertLogicalTime(response.queryLogicalTimeResponse?.result);
1157
+ }
1158
+
1159
+ /**
1160
+ * Query the Least Incoming Time Stamp (LITS).
1161
+ */
1162
+ async queryLITS(): Promise<TimeQueryReturn> {
1163
+ const response = await this._doHlaCall({ queryLITSRequest: {} });
1164
+ return convertTimeQueryReturn(response.queryLITSResponse?.result);
1165
+ }
1166
+
1167
+ /**
1168
+ * Query the current lookahead interval.
1169
+ */
1170
+ async queryLookahead(): Promise<LogicalTimeInterval> {
1171
+ const response = await this._doHlaCall({ queryLookaheadRequest: {} });
1172
+ return convertLogicalTimeInterval(response.queryLookaheadResponse?.result);
1173
+ }
1174
+
1175
+ // ===========================================================================
1176
+ // Support Services
1177
+ // ===========================================================================
1178
+
1179
+ /**
1180
+ * Get the handle for an object class by name.
1181
+ *
1182
+ * @param objectClassName - Name of the object class
1183
+ * @returns Object class handle
1184
+ */
1185
+ async getObjectClassHandle(objectClassName: string): Promise<ObjectClassHandle> {
1186
+ const response = await this._doHlaCall({
1187
+ getObjectClassHandleRequest: { objectClassName },
1188
+ });
1189
+ return response.getObjectClassHandleResponse?.result?.data ?? new Uint8Array(0);
1190
+ }
1191
+
1192
+ /**
1193
+ * Get the name of an object class by handle.
1194
+ *
1195
+ * @param objectClass - Object class handle
1196
+ * @returns Object class name
1197
+ */
1198
+ async getObjectClassName(objectClass: ObjectClassHandle): Promise<string> {
1199
+ const response = await this._doHlaCall({
1200
+ getObjectClassNameRequest: {
1201
+ objectClass: toProtoObjectClassHandle(objectClass),
1202
+ },
1203
+ });
1204
+ return response.getObjectClassNameResponse?.result ?? "";
1205
+ }
1206
+
1207
+ /**
1208
+ * Get the handle for an attribute by name.
1209
+ *
1210
+ * @param objectClass - Object class handle
1211
+ * @param attributeName - Name of the attribute
1212
+ * @returns Attribute handle
1213
+ */
1214
+ async getAttributeHandle(
1215
+ objectClass: ObjectClassHandle,
1216
+ attributeName: string
1217
+ ): Promise<Uint8Array> {
1218
+ const response = await this._doHlaCall({
1219
+ getAttributeHandleRequest: {
1220
+ objectClass: toProtoObjectClassHandle(objectClass),
1221
+ attributeName,
1222
+ },
1223
+ });
1224
+ return response.getAttributeHandleResponse?.result?.data ?? new Uint8Array(0);
1225
+ }
1226
+
1227
+ /**
1228
+ * Get the name of an attribute by handle.
1229
+ *
1230
+ * @param objectClass - Object class handle
1231
+ * @param attribute - Attribute handle
1232
+ * @returns Attribute name
1233
+ */
1234
+ async getAttributeName(
1235
+ objectClass: ObjectClassHandle,
1236
+ attribute: Uint8Array
1237
+ ): Promise<string> {
1238
+ const response = await this._doHlaCall({
1239
+ getAttributeNameRequest: {
1240
+ objectClass: toProtoObjectClassHandle(objectClass),
1241
+ attribute: { data: attribute },
1242
+ },
1243
+ });
1244
+ return response.getAttributeNameResponse?.result ?? "";
1245
+ }
1246
+
1247
+ /**
1248
+ * Get the handle for an interaction class by name.
1249
+ *
1250
+ * @param interactionClassName - Name of the interaction class
1251
+ * @returns Interaction class handle
1252
+ */
1253
+ async getInteractionClassHandle(interactionClassName: string): Promise<InteractionClassHandle> {
1254
+ const response = await this._doHlaCall({
1255
+ getInteractionClassHandleRequest: { interactionClassName },
1256
+ });
1257
+ return response.getInteractionClassHandleResponse?.result?.data ?? new Uint8Array(0);
1258
+ }
1259
+
1260
+ /**
1261
+ * Get the name of an interaction class by handle.
1262
+ *
1263
+ * @param interactionClass - Interaction class handle
1264
+ * @returns Interaction class name
1265
+ */
1266
+ async getInteractionClassName(interactionClass: InteractionClassHandle): Promise<string> {
1267
+ const response = await this._doHlaCall({
1268
+ getInteractionClassNameRequest: {
1269
+ interactionClass: toProtoInteractionClassHandle(interactionClass),
1270
+ },
1271
+ });
1272
+ return response.getInteractionClassNameResponse?.result ?? "";
1273
+ }
1274
+
1275
+ /**
1276
+ * Get the handle for a parameter by name.
1277
+ *
1278
+ * @param interactionClass - Interaction class handle
1279
+ * @param parameterName - Name of the parameter
1280
+ * @returns Parameter handle
1281
+ */
1282
+ async getParameterHandle(
1283
+ interactionClass: InteractionClassHandle,
1284
+ parameterName: string
1285
+ ): Promise<Uint8Array> {
1286
+ const response = await this._doHlaCall({
1287
+ getParameterHandleRequest: {
1288
+ interactionClass: toProtoInteractionClassHandle(interactionClass),
1289
+ parameterName,
1290
+ },
1291
+ });
1292
+ return response.getParameterHandleResponse?.result?.data ?? new Uint8Array(0);
1293
+ }
1294
+
1295
+ /**
1296
+ * Get the name of a parameter by handle.
1297
+ *
1298
+ * @param interactionClass - Interaction class handle
1299
+ * @param parameter - Parameter handle
1300
+ * @returns Parameter name
1301
+ */
1302
+ async getParameterName(
1303
+ interactionClass: InteractionClassHandle,
1304
+ parameter: Uint8Array
1305
+ ): Promise<string> {
1306
+ const response = await this._doHlaCall({
1307
+ getParameterNameRequest: {
1308
+ interactionClass: toProtoInteractionClassHandle(interactionClass),
1309
+ parameter: { data: parameter },
1310
+ },
1311
+ });
1312
+ return response.getParameterNameResponse?.result ?? "";
1313
+ }
1314
+
1315
+ /**
1316
+ * Get the handle for an object instance by name.
1317
+ *
1318
+ * @param objectInstanceName - Name of the object instance
1319
+ * @returns Object instance handle
1320
+ */
1321
+ async getObjectInstanceHandle(objectInstanceName: string): Promise<ObjectInstanceHandle> {
1322
+ const response = await this._doHlaCall({
1323
+ getObjectInstanceHandleRequest: { objectInstanceName },
1324
+ });
1325
+ return response.getObjectInstanceHandleResponse?.result?.data ?? new Uint8Array(0);
1326
+ }
1327
+
1328
+ /**
1329
+ * Get the name of an object instance by handle.
1330
+ *
1331
+ * @param objectInstance - Object instance handle
1332
+ * @returns Object instance name
1333
+ */
1334
+ async getObjectInstanceName(objectInstance: ObjectInstanceHandle): Promise<string> {
1335
+ const response = await this._doHlaCall({
1336
+ getObjectInstanceNameRequest: {
1337
+ objectInstance: toProtoObjectInstanceHandle(objectInstance),
1338
+ },
1339
+ });
1340
+ return response.getObjectInstanceNameResponse?.result ?? "";
1341
+ }
1342
+
1343
+ // ===========================================================================
1344
+ // Callback Handling
1345
+ // ===========================================================================
1346
+
1347
+ /**
1348
+ * Evoke a single callback.
1349
+ *
1350
+ * @param _approximateMinimumTimeInSeconds - Minimum time to wait for a callback
1351
+ * @returns true if a callback was processed
1352
+ */
1353
+ async evokeCallback(_approximateMinimumTimeInSeconds: number): Promise<boolean> {
1354
+ this._throwIfInCallback("evokeCallback");
1355
+ // In this implementation, callbacks are handled immediately by the session layer
1356
+ // This method is provided for API compatibility but may need enhancement
1357
+ // for full evoked callback model support
1358
+ return false;
1359
+ }
1360
+
1361
+ /**
1362
+ * Evoke multiple callbacks.
1363
+ *
1364
+ * @param _minimumTime - Minimum time to wait
1365
+ * @param _maximumTime - Maximum time to wait
1366
+ * @returns true if callbacks were processed
1367
+ */
1368
+ async evokeMultipleCallbacks(_minimumTime: number, _maximumTime: number): Promise<boolean> {
1369
+ this._throwIfInCallback("evokeMultipleCallbacks");
1370
+ // In this implementation, callbacks are handled immediately by the session layer
1371
+ return false;
1372
+ }
1373
+
1374
+ // ===========================================================================
1375
+ // Private Methods
1376
+ // ===========================================================================
1377
+
1378
+ /**
1379
+ * Handle an incoming callback from the RTI.
1380
+ */
1381
+ private _handleCallback(seqNum: number, callbackData: Uint8Array): void {
1382
+ if (!this._callbackDispatcher || !this._session) {
1383
+ return;
1384
+ }
1385
+
1386
+ this._inCallback = true;
1387
+ try {
1388
+ const responseData = this._callbackDispatcher.processCallback(callbackData);
1389
+ this._session.sendHlaCallbackResponse(seqNum, responseData).catch((err) => {
1390
+ console.error("[RTIAmbassador] Failed to send callback response:", err);
1391
+ });
1392
+ } finally {
1393
+ this._inCallback = false;
1394
+ }
1395
+ }
1396
+
1397
+ /**
1398
+ * Execute an HLA call and return the response.
1399
+ */
1400
+ private async _doHlaCall(callRequest: CallRequest): Promise<CallResponse> {
1401
+ if (!this._session) {
1402
+ throw new NotConnected("Not connected to RTI");
1403
+ }
1404
+
1405
+ // Encode the request
1406
+ const encodedRequest = CallRequestCodec.encode(callRequest).finish();
1407
+
1408
+ // Send and wait for response
1409
+ const encodedResponse = await this._session.sendHlaCallRequest(encodedRequest);
1410
+
1411
+ // Decode response
1412
+ const response = CallResponseCodec.decode(encodedResponse);
1413
+
1414
+ // Check for exception
1415
+ if (response.exceptionData) {
1416
+ throw createExceptionFromProto(response.exceptionData);
1417
+ }
1418
+
1419
+ return response;
1420
+ }
1421
+
1422
+ /**
1423
+ * Throw if we're currently in a callback.
1424
+ */
1425
+ private _throwIfInCallback(methodName: string): void {
1426
+ if (this._inCallback) {
1427
+ throw new CallNotAllowedFromWithinCallback(
1428
+ `Call to ${methodName} not allowed from within callback`
1429
+ );
1430
+ }
1431
+ }
1432
+
1433
+ /**
1434
+ * Clean up resources.
1435
+ */
1436
+ private _cleanup(): void {
1437
+ this._session = null;
1438
+ this._transport = null;
1439
+ this._callbackDispatcher = null;
1440
+ }
1441
+ }