@hla4ts/session 0.1.0 → 0.1.1
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/README.md +377 -377
- package/package.json +3 -3
- package/src/errors.ts +98 -98
- package/src/index.ts +147 -147
- package/src/messages.ts +329 -329
- package/src/sequence-number.ts +200 -200
- package/src/session.ts +976 -976
- package/src/timeout-timer.ts +204 -204
- package/src/types.ts +235 -235
package/README.md
CHANGED
|
@@ -1,377 +1,377 @@
|
|
|
1
|
-
# @hla4ts/session
|
|
2
|
-
|
|
3
|
-
**HLA 4 Federate Protocol Session Layer**
|
|
4
|
-
|
|
5
|
-
This package provides session management for the HLA 4 Federate Protocol, handling connection establishment, state machine, heartbeats, request/response correlation, and session resumption.
|
|
6
|
-
|
|
7
|
-
## Overview
|
|
8
|
-
|
|
9
|
-
The session layer sits between the transport layer and the HLA API layer, providing:
|
|
10
|
-
|
|
11
|
-
- **Session lifecycle management** - NEW → STARTING → RUNNING ⇄ DROPPED → TERMINATED
|
|
12
|
-
- **Request/response correlation** - Matches HLA call responses to requests
|
|
13
|
-
- **Heartbeat support** - Keep-alive mechanism for connection health
|
|
14
|
-
- **Session resumption** - Reconnect and resume after network drops
|
|
15
|
-
- **Timeout management** - Connection and response timeouts
|
|
16
|
-
|
|
17
|
-
## Installation
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
bun add @hla4ts/session
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Quick Start
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
import { Session, SessionState } from '@hla4ts/session';
|
|
27
|
-
import { TlsTransport } from '@hla4ts/transport';
|
|
28
|
-
|
|
29
|
-
// Create transport and session
|
|
30
|
-
const transport = new TlsTransport({
|
|
31
|
-
host: 'rti.example.com',
|
|
32
|
-
port: 15165,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const session = new Session(transport, {
|
|
36
|
-
connectionTimeout: 30000,
|
|
37
|
-
responseTimeout: 180000,
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// Listen for state changes
|
|
41
|
-
session.addStateListener({
|
|
42
|
-
onStateTransition: (oldState, newState, reason) => {
|
|
43
|
-
console.log(`Session: ${oldState} -> ${newState} (${reason})`);
|
|
44
|
-
|
|
45
|
-
// Handle connection drops
|
|
46
|
-
if (newState === SessionState.DROPPED) {
|
|
47
|
-
session.resume().catch((err) => {
|
|
48
|
-
console.error('Resume failed:', err);
|
|
49
|
-
session.terminate();
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Start the session
|
|
56
|
-
await session.start({
|
|
57
|
-
onHlaCallbackRequest: (seqNum, callbackData) => {
|
|
58
|
-
// Process the callback (decode protobuf, handle it, encode response)
|
|
59
|
-
const response = processCallback(callbackData);
|
|
60
|
-
session.sendHlaCallbackResponse(seqNum, response);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// Send HLA calls
|
|
65
|
-
const response = await session.sendHlaCallRequest(encodedCall);
|
|
66
|
-
|
|
67
|
-
// When done
|
|
68
|
-
await session.terminate();
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
## Sequence Diagram
|
|
72
|
-
|
|
73
|
-
```mermaid
|
|
74
|
-
sequenceDiagram
|
|
75
|
-
participant App as Federate Code
|
|
76
|
-
participant Session
|
|
77
|
-
participant Transport
|
|
78
|
-
participant RTI
|
|
79
|
-
App->>Session: start(callbackListener)
|
|
80
|
-
Session->>Transport: CTRL_NEW_SESSION
|
|
81
|
-
Transport-->>RTI: framed message
|
|
82
|
-
RTI-->>Transport: CTRL_NEW_SESSION_STATUS
|
|
83
|
-
Transport-->>Session: onMessage
|
|
84
|
-
App->>Session: sendHlaCallRequest(bytes)
|
|
85
|
-
Session->>Transport: HLA_CALL_REQUEST
|
|
86
|
-
RTI-->>Transport: HLA_CALL_RESPONSE
|
|
87
|
-
Transport-->>Session: onMessage
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
## Session States
|
|
91
|
-
|
|
92
|
-
The session follows a well-defined state machine:
|
|
93
|
-
|
|
94
|
-
```
|
|
95
|
-
┌───────┐
|
|
96
|
-
│ NEW │────────────────────┐
|
|
97
|
-
└───┬───┘ │
|
|
98
|
-
│ start() │
|
|
99
|
-
▼ │
|
|
100
|
-
┌──────────┐ │
|
|
101
|
-
│ STARTING │─────────────────┤
|
|
102
|
-
└────┬─────┘ │
|
|
103
|
-
│ success │ failure
|
|
104
|
-
▼ │
|
|
105
|
-
┌─────────┐ │
|
|
106
|
-
│ RUNNING │◄─────────────┐ │
|
|
107
|
-
└────┬────┘ │ │
|
|
108
|
-
│ │ │
|
|
109
|
-
│ connection lost │ resume()
|
|
110
|
-
▼ │ │
|
|
111
|
-
┌─────────┐ ┌────┴────┐
|
|
112
|
-
│ DROPPED │────────►│ RESUMING│
|
|
113
|
-
└────┬────┘ └────┬────┘
|
|
114
|
-
│ │
|
|
115
|
-
│ terminate() │ failure
|
|
116
|
-
▼ │
|
|
117
|
-
┌─────────────┐ │
|
|
118
|
-
│ TERMINATING │ │
|
|
119
|
-
└──────┬──────┘ │
|
|
120
|
-
│ │
|
|
121
|
-
▼ ▼
|
|
122
|
-
┌────────────┐◄──────────┘
|
|
123
|
-
│ TERMINATED │
|
|
124
|
-
└────────────┘
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### State Descriptions
|
|
128
|
-
|
|
129
|
-
| State | Description |
|
|
130
|
-
|-------|-------------|
|
|
131
|
-
| `NEW` | Session created but not started |
|
|
132
|
-
| `STARTING` | Connecting to RTI, establishing session |
|
|
133
|
-
| `RUNNING` | Session active, can send/receive HLA messages |
|
|
134
|
-
| `DROPPED` | Connection lost, can attempt resume |
|
|
135
|
-
| `RESUMING` | Attempting to resume dropped session |
|
|
136
|
-
| `TERMINATING` | Graceful shutdown in progress |
|
|
137
|
-
| `TERMINATED` | Session ended, cannot be reused |
|
|
138
|
-
|
|
139
|
-
## API Reference
|
|
140
|
-
|
|
141
|
-
### Session Class
|
|
142
|
-
|
|
143
|
-
#### Constructor
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
new Session(transport: Transport, options?: SessionOptions)
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
**Options:**
|
|
150
|
-
|
|
151
|
-
| Option | Type | Default | Description |
|
|
152
|
-
|--------|------|---------|-------------|
|
|
153
|
-
| `connectionTimeout` | `number` | `30000` | Timeout for initial connection (ms) |
|
|
154
|
-
| `responseTimeout` | `number` | `180000` | Timeout for server responses (ms) |
|
|
155
|
-
| `maxRetryAttempts` | `number` | `3` | Max connection retry attempts |
|
|
156
|
-
| `messageQueueSize` | `number` | `1000` | Size of outgoing message queue |
|
|
157
|
-
| `rateLimitEnabled` | `boolean` | `false` | Enable rate limiting |
|
|
158
|
-
| `heartbeatInterval` | `number` | `10000` | Heartbeat check interval (ms) |
|
|
159
|
-
|
|
160
|
-
#### Properties
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
session.id: bigint // Session ID (0 if not established)
|
|
164
|
-
session.state: SessionState // Current state
|
|
165
|
-
session.isOperational: boolean // True if can send/receive messages
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
#### Methods
|
|
169
|
-
|
|
170
|
-
##### `start(callbackListener)`
|
|
171
|
-
|
|
172
|
-
Start the session and connect to the RTI.
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
await session.start({
|
|
176
|
-
onHlaCallbackRequest: (seqNum: number, callback: Uint8Array) => {
|
|
177
|
-
// Handle callback
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
```
|
|
181
|
-
|
|
182
|
-
##### `resume()`
|
|
183
|
-
|
|
184
|
-
Resume a dropped session.
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
const success = await session.resume();
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
##### `sendHeartbeat()`
|
|
191
|
-
|
|
192
|
-
Send a heartbeat message (useful for keeping connection alive).
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
await session.sendHeartbeat();
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
##### `sendHlaCallRequest(encodedHlaCall)`
|
|
199
|
-
|
|
200
|
-
Send an HLA call and wait for the response.
|
|
201
|
-
|
|
202
|
-
```typescript
|
|
203
|
-
const response = await session.sendHlaCallRequest(encodedCall);
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
##### `sendHlaCallbackResponse(responseToSequenceNumber, encodedResponse)`
|
|
207
|
-
|
|
208
|
-
Send a response to an HLA callback.
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
await session.sendHlaCallbackResponse(seqNum, encodedResponse);
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
##### `terminate(timeoutMs?)`
|
|
215
|
-
|
|
216
|
-
Gracefully terminate the session.
|
|
217
|
-
|
|
218
|
-
```typescript
|
|
219
|
-
await session.terminate();
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
##### `addStateListener(listener)`
|
|
223
|
-
|
|
224
|
-
Register a state change listener.
|
|
225
|
-
|
|
226
|
-
```typescript
|
|
227
|
-
session.addStateListener({
|
|
228
|
-
onStateTransition: (oldState, newState, reason) => {
|
|
229
|
-
console.log(`${oldState} -> ${newState}: ${reason}`);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
##### `setMessageSentListener(listener)`
|
|
235
|
-
|
|
236
|
-
Set a listener for outgoing messages (useful for heartbeat timing).
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
session.setMessageSentListener({
|
|
240
|
-
onMessageSent: () => {
|
|
241
|
-
// Reset heartbeat timer
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
## Error Handling
|
|
247
|
-
|
|
248
|
-
The session layer throws specific errors:
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
import {
|
|
252
|
-
SessionLostError,
|
|
253
|
-
SessionAlreadyTerminatedError,
|
|
254
|
-
SessionIllegalStateError,
|
|
255
|
-
BadMessageError,
|
|
256
|
-
ConnectionTimeoutError,
|
|
257
|
-
ResponseTimeoutError,
|
|
258
|
-
} from '@hla4ts/session';
|
|
259
|
-
|
|
260
|
-
try {
|
|
261
|
-
await session.start(listener);
|
|
262
|
-
} catch (err) {
|
|
263
|
-
if (err instanceof ConnectionTimeoutError) {
|
|
264
|
-
console.error('Could not connect to RTI');
|
|
265
|
-
} else if (err instanceof SessionLostError) {
|
|
266
|
-
console.error('Session lost:', err.message);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
## Sequence Numbers
|
|
272
|
-
|
|
273
|
-
The package exports utilities for working with Federate Protocol sequence numbers:
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
276
|
-
import {
|
|
277
|
-
SequenceNumber,
|
|
278
|
-
AtomicSequenceNumber,
|
|
279
|
-
isValidSequenceNumber,
|
|
280
|
-
nextSequenceNumber,
|
|
281
|
-
NO_SEQUENCE_NUMBER,
|
|
282
|
-
INITIAL_SEQUENCE_NUMBER,
|
|
283
|
-
MAX_SEQUENCE_NUMBER,
|
|
284
|
-
} from '@hla4ts/session';
|
|
285
|
-
|
|
286
|
-
// Mutable sequence number
|
|
287
|
-
const seq = new SequenceNumber(0);
|
|
288
|
-
seq.increment(); // Returns 1
|
|
289
|
-
seq.getAndIncrement(); // Returns 1, then increments to 2
|
|
290
|
-
|
|
291
|
-
// For concurrent access
|
|
292
|
-
const atomic = new AtomicSequenceNumber(0);
|
|
293
|
-
atomic.compareAndSet(0, 1); // Returns true if successful
|
|
294
|
-
|
|
295
|
-
// Utilities
|
|
296
|
-
isValidSequenceNumber(100); // true
|
|
297
|
-
isValidSequenceNumber(-1); // false
|
|
298
|
-
nextSequenceNumber(MAX_SEQUENCE_NUMBER); // Returns 0 (wrap-around)
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
## Timers
|
|
302
|
-
|
|
303
|
-
The package includes timer utilities for session management:
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
import { TimeoutTimer, OneShotTimer } from '@hla4ts/session';
|
|
307
|
-
|
|
308
|
-
// Periodic timeout timer
|
|
309
|
-
const timer = TimeoutTimer.createLazy(30000); // 30 second timeout
|
|
310
|
-
timer.start(() => {
|
|
311
|
-
console.log('Timeout!');
|
|
312
|
-
});
|
|
313
|
-
timer.extend(); // Reset the timeout
|
|
314
|
-
timer.pause(); // Stop checking
|
|
315
|
-
timer.resume(); // Resume checking
|
|
316
|
-
timer.cancel(); // Permanently cancel
|
|
317
|
-
|
|
318
|
-
// One-shot timer
|
|
319
|
-
const oneShot = new OneShotTimer();
|
|
320
|
-
oneShot.schedule(() => {
|
|
321
|
-
console.log('Fired!');
|
|
322
|
-
}, 5000);
|
|
323
|
-
oneShot.clear(); // Cancel before it fires
|
|
324
|
-
oneShot.cancel(); // Permanently cancel
|
|
325
|
-
```
|
|
326
|
-
|
|
327
|
-
## Message Encoding (Advanced)
|
|
328
|
-
|
|
329
|
-
For advanced use cases, you can encode/decode session control messages directly:
|
|
330
|
-
|
|
331
|
-
```typescript
|
|
332
|
-
import {
|
|
333
|
-
createNewSessionMessage,
|
|
334
|
-
createHeartbeatMessage,
|
|
335
|
-
createHlaCallRequestMessage,
|
|
336
|
-
decodeHlaCallResponse,
|
|
337
|
-
decodeHlaCallbackRequest,
|
|
338
|
-
} from '@hla4ts/session';
|
|
339
|
-
|
|
340
|
-
// Create a new session message
|
|
341
|
-
const newSessionMsg = createNewSessionMessage();
|
|
342
|
-
|
|
343
|
-
// Create an HLA call request
|
|
344
|
-
const callMsg = createHlaCallRequestMessage(
|
|
345
|
-
sequenceNumber,
|
|
346
|
-
sessionId,
|
|
347
|
-
lastReceivedSequenceNumber,
|
|
348
|
-
protobufPayload
|
|
349
|
-
);
|
|
350
|
-
|
|
351
|
-
// Decode an HLA call response
|
|
352
|
-
const response = decodeHlaCallResponse(payload);
|
|
353
|
-
console.log(response.responseToSequenceNumber);
|
|
354
|
-
console.log(response.hlaServiceReturnValueOrException);
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
## Testing
|
|
358
|
-
|
|
359
|
-
```bash
|
|
360
|
-
cd packages/session
|
|
361
|
-
bun test
|
|
362
|
-
```
|
|
363
|
-
|
|
364
|
-
## Related Packages
|
|
365
|
-
|
|
366
|
-
- [`@hla4ts/transport`](../transport) - Transport layer (TCP/TLS)
|
|
367
|
-
- [`@hla4ts/proto`](../proto) - Protocol buffer types
|
|
368
|
-
- [`@hla4ts/hla-api`](../hla-api) - High-level HLA API facade
|
|
369
|
-
|
|
370
|
-
## References
|
|
371
|
-
|
|
372
|
-
- [IEEE 1516-2025](https://standards.ieee.org/standard/1516-2025.html) - HLA 4 Standard
|
|
373
|
-
- [Pitch FedProClient](https://github.com/Pitch-Technologies/FedProClient) - Reference implementation
|
|
374
|
-
|
|
375
|
-
## License
|
|
376
|
-
|
|
377
|
-
MIT
|
|
1
|
+
# @hla4ts/session
|
|
2
|
+
|
|
3
|
+
**HLA 4 Federate Protocol Session Layer**
|
|
4
|
+
|
|
5
|
+
This package provides session management for the HLA 4 Federate Protocol, handling connection establishment, state machine, heartbeats, request/response correlation, and session resumption.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The session layer sits between the transport layer and the HLA API layer, providing:
|
|
10
|
+
|
|
11
|
+
- **Session lifecycle management** - NEW → STARTING → RUNNING ⇄ DROPPED → TERMINATED
|
|
12
|
+
- **Request/response correlation** - Matches HLA call responses to requests
|
|
13
|
+
- **Heartbeat support** - Keep-alive mechanism for connection health
|
|
14
|
+
- **Session resumption** - Reconnect and resume after network drops
|
|
15
|
+
- **Timeout management** - Connection and response timeouts
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
bun add @hla4ts/session
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { Session, SessionState } from '@hla4ts/session';
|
|
27
|
+
import { TlsTransport } from '@hla4ts/transport';
|
|
28
|
+
|
|
29
|
+
// Create transport and session
|
|
30
|
+
const transport = new TlsTransport({
|
|
31
|
+
host: 'rti.example.com',
|
|
32
|
+
port: 15165,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const session = new Session(transport, {
|
|
36
|
+
connectionTimeout: 30000,
|
|
37
|
+
responseTimeout: 180000,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Listen for state changes
|
|
41
|
+
session.addStateListener({
|
|
42
|
+
onStateTransition: (oldState, newState, reason) => {
|
|
43
|
+
console.log(`Session: ${oldState} -> ${newState} (${reason})`);
|
|
44
|
+
|
|
45
|
+
// Handle connection drops
|
|
46
|
+
if (newState === SessionState.DROPPED) {
|
|
47
|
+
session.resume().catch((err) => {
|
|
48
|
+
console.error('Resume failed:', err);
|
|
49
|
+
session.terminate();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// Start the session
|
|
56
|
+
await session.start({
|
|
57
|
+
onHlaCallbackRequest: (seqNum, callbackData) => {
|
|
58
|
+
// Process the callback (decode protobuf, handle it, encode response)
|
|
59
|
+
const response = processCallback(callbackData);
|
|
60
|
+
session.sendHlaCallbackResponse(seqNum, response);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Send HLA calls
|
|
65
|
+
const response = await session.sendHlaCallRequest(encodedCall);
|
|
66
|
+
|
|
67
|
+
// When done
|
|
68
|
+
await session.terminate();
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Sequence Diagram
|
|
72
|
+
|
|
73
|
+
```mermaid
|
|
74
|
+
sequenceDiagram
|
|
75
|
+
participant App as Federate Code
|
|
76
|
+
participant Session
|
|
77
|
+
participant Transport
|
|
78
|
+
participant RTI
|
|
79
|
+
App->>Session: start(callbackListener)
|
|
80
|
+
Session->>Transport: CTRL_NEW_SESSION
|
|
81
|
+
Transport-->>RTI: framed message
|
|
82
|
+
RTI-->>Transport: CTRL_NEW_SESSION_STATUS
|
|
83
|
+
Transport-->>Session: onMessage
|
|
84
|
+
App->>Session: sendHlaCallRequest(bytes)
|
|
85
|
+
Session->>Transport: HLA_CALL_REQUEST
|
|
86
|
+
RTI-->>Transport: HLA_CALL_RESPONSE
|
|
87
|
+
Transport-->>Session: onMessage
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Session States
|
|
91
|
+
|
|
92
|
+
The session follows a well-defined state machine:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
┌───────┐
|
|
96
|
+
│ NEW │────────────────────┐
|
|
97
|
+
└───┬───┘ │
|
|
98
|
+
│ start() │
|
|
99
|
+
▼ │
|
|
100
|
+
┌──────────┐ │
|
|
101
|
+
│ STARTING │─────────────────┤
|
|
102
|
+
└────┬─────┘ │
|
|
103
|
+
│ success │ failure
|
|
104
|
+
▼ │
|
|
105
|
+
┌─────────┐ │
|
|
106
|
+
│ RUNNING │◄─────────────┐ │
|
|
107
|
+
└────┬────┘ │ │
|
|
108
|
+
│ │ │
|
|
109
|
+
│ connection lost │ resume()
|
|
110
|
+
▼ │ │
|
|
111
|
+
┌─────────┐ ┌────┴────┐
|
|
112
|
+
│ DROPPED │────────►│ RESUMING│
|
|
113
|
+
└────┬────┘ └────┬────┘
|
|
114
|
+
│ │
|
|
115
|
+
│ terminate() │ failure
|
|
116
|
+
▼ │
|
|
117
|
+
┌─────────────┐ │
|
|
118
|
+
│ TERMINATING │ │
|
|
119
|
+
└──────┬──────┘ │
|
|
120
|
+
│ │
|
|
121
|
+
▼ ▼
|
|
122
|
+
┌────────────┐◄──────────┘
|
|
123
|
+
│ TERMINATED │
|
|
124
|
+
└────────────┘
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### State Descriptions
|
|
128
|
+
|
|
129
|
+
| State | Description |
|
|
130
|
+
|-------|-------------|
|
|
131
|
+
| `NEW` | Session created but not started |
|
|
132
|
+
| `STARTING` | Connecting to RTI, establishing session |
|
|
133
|
+
| `RUNNING` | Session active, can send/receive HLA messages |
|
|
134
|
+
| `DROPPED` | Connection lost, can attempt resume |
|
|
135
|
+
| `RESUMING` | Attempting to resume dropped session |
|
|
136
|
+
| `TERMINATING` | Graceful shutdown in progress |
|
|
137
|
+
| `TERMINATED` | Session ended, cannot be reused |
|
|
138
|
+
|
|
139
|
+
## API Reference
|
|
140
|
+
|
|
141
|
+
### Session Class
|
|
142
|
+
|
|
143
|
+
#### Constructor
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
new Session(transport: Transport, options?: SessionOptions)
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Options:**
|
|
150
|
+
|
|
151
|
+
| Option | Type | Default | Description |
|
|
152
|
+
|--------|------|---------|-------------|
|
|
153
|
+
| `connectionTimeout` | `number` | `30000` | Timeout for initial connection (ms) |
|
|
154
|
+
| `responseTimeout` | `number` | `180000` | Timeout for server responses (ms) |
|
|
155
|
+
| `maxRetryAttempts` | `number` | `3` | Max connection retry attempts |
|
|
156
|
+
| `messageQueueSize` | `number` | `1000` | Size of outgoing message queue |
|
|
157
|
+
| `rateLimitEnabled` | `boolean` | `false` | Enable rate limiting |
|
|
158
|
+
| `heartbeatInterval` | `number` | `10000` | Heartbeat check interval (ms) |
|
|
159
|
+
|
|
160
|
+
#### Properties
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
session.id: bigint // Session ID (0 if not established)
|
|
164
|
+
session.state: SessionState // Current state
|
|
165
|
+
session.isOperational: boolean // True if can send/receive messages
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### Methods
|
|
169
|
+
|
|
170
|
+
##### `start(callbackListener)`
|
|
171
|
+
|
|
172
|
+
Start the session and connect to the RTI.
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
await session.start({
|
|
176
|
+
onHlaCallbackRequest: (seqNum: number, callback: Uint8Array) => {
|
|
177
|
+
// Handle callback
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
##### `resume()`
|
|
183
|
+
|
|
184
|
+
Resume a dropped session.
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
const success = await session.resume();
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
##### `sendHeartbeat()`
|
|
191
|
+
|
|
192
|
+
Send a heartbeat message (useful for keeping connection alive).
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
await session.sendHeartbeat();
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
##### `sendHlaCallRequest(encodedHlaCall)`
|
|
199
|
+
|
|
200
|
+
Send an HLA call and wait for the response.
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
const response = await session.sendHlaCallRequest(encodedCall);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
##### `sendHlaCallbackResponse(responseToSequenceNumber, encodedResponse)`
|
|
207
|
+
|
|
208
|
+
Send a response to an HLA callback.
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
await session.sendHlaCallbackResponse(seqNum, encodedResponse);
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
##### `terminate(timeoutMs?)`
|
|
215
|
+
|
|
216
|
+
Gracefully terminate the session.
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
await session.terminate();
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
##### `addStateListener(listener)`
|
|
223
|
+
|
|
224
|
+
Register a state change listener.
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
session.addStateListener({
|
|
228
|
+
onStateTransition: (oldState, newState, reason) => {
|
|
229
|
+
console.log(`${oldState} -> ${newState}: ${reason}`);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
##### `setMessageSentListener(listener)`
|
|
235
|
+
|
|
236
|
+
Set a listener for outgoing messages (useful for heartbeat timing).
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
session.setMessageSentListener({
|
|
240
|
+
onMessageSent: () => {
|
|
241
|
+
// Reset heartbeat timer
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Error Handling
|
|
247
|
+
|
|
248
|
+
The session layer throws specific errors:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import {
|
|
252
|
+
SessionLostError,
|
|
253
|
+
SessionAlreadyTerminatedError,
|
|
254
|
+
SessionIllegalStateError,
|
|
255
|
+
BadMessageError,
|
|
256
|
+
ConnectionTimeoutError,
|
|
257
|
+
ResponseTimeoutError,
|
|
258
|
+
} from '@hla4ts/session';
|
|
259
|
+
|
|
260
|
+
try {
|
|
261
|
+
await session.start(listener);
|
|
262
|
+
} catch (err) {
|
|
263
|
+
if (err instanceof ConnectionTimeoutError) {
|
|
264
|
+
console.error('Could not connect to RTI');
|
|
265
|
+
} else if (err instanceof SessionLostError) {
|
|
266
|
+
console.error('Session lost:', err.message);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Sequence Numbers
|
|
272
|
+
|
|
273
|
+
The package exports utilities for working with Federate Protocol sequence numbers:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
import {
|
|
277
|
+
SequenceNumber,
|
|
278
|
+
AtomicSequenceNumber,
|
|
279
|
+
isValidSequenceNumber,
|
|
280
|
+
nextSequenceNumber,
|
|
281
|
+
NO_SEQUENCE_NUMBER,
|
|
282
|
+
INITIAL_SEQUENCE_NUMBER,
|
|
283
|
+
MAX_SEQUENCE_NUMBER,
|
|
284
|
+
} from '@hla4ts/session';
|
|
285
|
+
|
|
286
|
+
// Mutable sequence number
|
|
287
|
+
const seq = new SequenceNumber(0);
|
|
288
|
+
seq.increment(); // Returns 1
|
|
289
|
+
seq.getAndIncrement(); // Returns 1, then increments to 2
|
|
290
|
+
|
|
291
|
+
// For concurrent access
|
|
292
|
+
const atomic = new AtomicSequenceNumber(0);
|
|
293
|
+
atomic.compareAndSet(0, 1); // Returns true if successful
|
|
294
|
+
|
|
295
|
+
// Utilities
|
|
296
|
+
isValidSequenceNumber(100); // true
|
|
297
|
+
isValidSequenceNumber(-1); // false
|
|
298
|
+
nextSequenceNumber(MAX_SEQUENCE_NUMBER); // Returns 0 (wrap-around)
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## Timers
|
|
302
|
+
|
|
303
|
+
The package includes timer utilities for session management:
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
import { TimeoutTimer, OneShotTimer } from '@hla4ts/session';
|
|
307
|
+
|
|
308
|
+
// Periodic timeout timer
|
|
309
|
+
const timer = TimeoutTimer.createLazy(30000); // 30 second timeout
|
|
310
|
+
timer.start(() => {
|
|
311
|
+
console.log('Timeout!');
|
|
312
|
+
});
|
|
313
|
+
timer.extend(); // Reset the timeout
|
|
314
|
+
timer.pause(); // Stop checking
|
|
315
|
+
timer.resume(); // Resume checking
|
|
316
|
+
timer.cancel(); // Permanently cancel
|
|
317
|
+
|
|
318
|
+
// One-shot timer
|
|
319
|
+
const oneShot = new OneShotTimer();
|
|
320
|
+
oneShot.schedule(() => {
|
|
321
|
+
console.log('Fired!');
|
|
322
|
+
}, 5000);
|
|
323
|
+
oneShot.clear(); // Cancel before it fires
|
|
324
|
+
oneShot.cancel(); // Permanently cancel
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
## Message Encoding (Advanced)
|
|
328
|
+
|
|
329
|
+
For advanced use cases, you can encode/decode session control messages directly:
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import {
|
|
333
|
+
createNewSessionMessage,
|
|
334
|
+
createHeartbeatMessage,
|
|
335
|
+
createHlaCallRequestMessage,
|
|
336
|
+
decodeHlaCallResponse,
|
|
337
|
+
decodeHlaCallbackRequest,
|
|
338
|
+
} from '@hla4ts/session';
|
|
339
|
+
|
|
340
|
+
// Create a new session message
|
|
341
|
+
const newSessionMsg = createNewSessionMessage();
|
|
342
|
+
|
|
343
|
+
// Create an HLA call request
|
|
344
|
+
const callMsg = createHlaCallRequestMessage(
|
|
345
|
+
sequenceNumber,
|
|
346
|
+
sessionId,
|
|
347
|
+
lastReceivedSequenceNumber,
|
|
348
|
+
protobufPayload
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
// Decode an HLA call response
|
|
352
|
+
const response = decodeHlaCallResponse(payload);
|
|
353
|
+
console.log(response.responseToSequenceNumber);
|
|
354
|
+
console.log(response.hlaServiceReturnValueOrException);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Testing
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
cd packages/session
|
|
361
|
+
bun test
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Related Packages
|
|
365
|
+
|
|
366
|
+
- [`@hla4ts/transport`](../transport) - Transport layer (TCP/TLS)
|
|
367
|
+
- [`@hla4ts/proto`](../proto) - Protocol buffer types
|
|
368
|
+
- [`@hla4ts/hla-api`](../hla-api) - High-level HLA API facade
|
|
369
|
+
|
|
370
|
+
## References
|
|
371
|
+
|
|
372
|
+
- [IEEE 1516-2025](https://standards.ieee.org/standard/1516-2025.html) - HLA 4 Standard
|
|
373
|
+
- [Pitch FedProClient](https://github.com/Pitch-Technologies/FedProClient) - Reference implementation
|
|
374
|
+
|
|
375
|
+
## License
|
|
376
|
+
|
|
377
|
+
MIT
|