icom-wlan-node 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +157 -16
- package/dist/core/Session.d.ts +9 -0
- package/dist/core/Session.js +34 -5
- package/dist/index.js +1 -1
- package/dist/rig/IcomCiv.js +6 -1
- package/dist/rig/IcomControl.d.ts +108 -7
- package/dist/rig/IcomControl.js +651 -67
- package/dist/transport/UdpClient.js +18 -4
- package/dist/types.d.ts +175 -0
- package/dist/types.js +46 -0
- package/package.json +1 -1
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.UdpClient = void 0;
|
|
7
7
|
const dgram_1 = __importDefault(require("dgram"));
|
|
8
8
|
const events_1 = require("events");
|
|
9
|
+
const debug_1 = require("../utils/debug");
|
|
9
10
|
class UdpClient extends events_1.EventEmitter {
|
|
10
11
|
constructor() {
|
|
11
12
|
super(...arguments);
|
|
@@ -13,8 +14,11 @@ class UdpClient extends events_1.EventEmitter {
|
|
|
13
14
|
}
|
|
14
15
|
get localPort() { return this._localPort; }
|
|
15
16
|
open(localPort) {
|
|
16
|
-
if (this.socket)
|
|
17
|
+
if (this.socket) {
|
|
18
|
+
(0, debug_1.dbgV)(`[UDP] Socket already open on port ${this._localPort}, skipping`);
|
|
17
19
|
return;
|
|
20
|
+
}
|
|
21
|
+
(0, debug_1.dbgV)(`[UDP] Opening socket on port ${localPort ?? 0}`);
|
|
18
22
|
this.socket = dgram_1.default.createSocket('udp4');
|
|
19
23
|
const sock = this.socket;
|
|
20
24
|
sock.on('message', (msg, rinfo) => this.emit('data', rinfo, Buffer.from(msg)));
|
|
@@ -23,8 +27,10 @@ class UdpClient extends events_1.EventEmitter {
|
|
|
23
27
|
if (!sock)
|
|
24
28
|
return;
|
|
25
29
|
const addr = sock.address();
|
|
26
|
-
if (typeof addr === 'object')
|
|
30
|
+
if (typeof addr === 'object') {
|
|
27
31
|
this._localPort = addr.port;
|
|
32
|
+
(0, debug_1.dbgV)(`[UDP] Socket bound to port ${this._localPort}`);
|
|
33
|
+
}
|
|
28
34
|
});
|
|
29
35
|
}
|
|
30
36
|
close() {
|
|
@@ -39,9 +45,17 @@ class UdpClient extends events_1.EventEmitter {
|
|
|
39
45
|
}
|
|
40
46
|
}
|
|
41
47
|
send(buf, ip, port) {
|
|
42
|
-
if (!this.socket)
|
|
48
|
+
if (!this.socket) {
|
|
49
|
+
console.error('[UDP] ERROR: Cannot send - socket not initialized!');
|
|
43
50
|
throw new Error('UDP socket not opened');
|
|
44
|
-
|
|
51
|
+
}
|
|
52
|
+
(0, debug_1.dbgV)(`[UDP] send to ${ip}:${port}, ${buf.length} bytes from port ${this._localPort}`);
|
|
53
|
+
this.socket.send(buf, port, ip, (err) => {
|
|
54
|
+
if (err) {
|
|
55
|
+
console.error(`[UDP] send error: ${err.message}`);
|
|
56
|
+
this.emit('error', err);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
45
59
|
}
|
|
46
60
|
}
|
|
47
61
|
exports.UdpClient = UdpClient;
|
package/dist/types.d.ts
CHANGED
|
@@ -41,6 +41,10 @@ export interface IcomRigEvents {
|
|
|
41
41
|
civFrame: (frame: Buffer) => void;
|
|
42
42
|
audio: (frame: AudioFrame) => void;
|
|
43
43
|
error: (err: Error) => void;
|
|
44
|
+
connectionLost: (info: ConnectionLostInfo) => void;
|
|
45
|
+
connectionRestored: (info: ConnectionRestoredInfo) => void;
|
|
46
|
+
reconnectAttempting: (info: ReconnectAttemptInfo) => void;
|
|
47
|
+
reconnectFailed: (info: ReconnectFailedInfo) => void;
|
|
44
48
|
}
|
|
45
49
|
export interface Disposable {
|
|
46
50
|
dispose(): void;
|
|
@@ -158,3 +162,174 @@ export interface LevelMeterReading {
|
|
|
158
162
|
/** Percentage (0-100%) */
|
|
159
163
|
percent: number;
|
|
160
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Connection state enumeration
|
|
167
|
+
* Represents the current state of a UDP session
|
|
168
|
+
*/
|
|
169
|
+
export declare enum ConnectionState {
|
|
170
|
+
/** Connection is active and receiving data */
|
|
171
|
+
CONNECTED = "CONNECTED",
|
|
172
|
+
/** Connection has been lost (timeout detected) */
|
|
173
|
+
DISCONNECTED = "DISCONNECTED",
|
|
174
|
+
/** Attempting to restore connection */
|
|
175
|
+
RECONNECTING = "RECONNECTING"
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Connection phase state machine
|
|
179
|
+
* Represents the high-level connection lifecycle state
|
|
180
|
+
*/
|
|
181
|
+
export declare enum ConnectionPhase {
|
|
182
|
+
/** Initial state or fully disconnected */
|
|
183
|
+
IDLE = "IDLE",
|
|
184
|
+
/** Establishing connection (AreYouThere, Login, sub-sessions) */
|
|
185
|
+
CONNECTING = "CONNECTING",
|
|
186
|
+
/** All sessions established and ready */
|
|
187
|
+
CONNECTED = "CONNECTED",
|
|
188
|
+
/** Actively disconnecting */
|
|
189
|
+
DISCONNECTING = "DISCONNECTING",
|
|
190
|
+
/** Attempting to reconnect after connection loss */
|
|
191
|
+
RECONNECTING = "RECONNECTING"
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Connection session tracking information
|
|
195
|
+
* Used to prevent race conditions and track connection lifecycle
|
|
196
|
+
*/
|
|
197
|
+
export interface ConnectionSession {
|
|
198
|
+
/** Current connection phase */
|
|
199
|
+
phase: ConnectionPhase;
|
|
200
|
+
/** Unique session ID to prevent race conditions */
|
|
201
|
+
sessionId: number;
|
|
202
|
+
/** When this connection session started */
|
|
203
|
+
startTime: number;
|
|
204
|
+
/** When the connection was last lost (for downtime calculation) */
|
|
205
|
+
lastDisconnectTime?: number;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Connection metrics for observability
|
|
209
|
+
*/
|
|
210
|
+
export interface ConnectionMetrics {
|
|
211
|
+
/** Current connection phase */
|
|
212
|
+
phase: ConnectionPhase;
|
|
213
|
+
/** Current session ID */
|
|
214
|
+
sessionId: number;
|
|
215
|
+
/** Total uptime in milliseconds (since last successful connect) */
|
|
216
|
+
uptime: number;
|
|
217
|
+
/** Individual session states */
|
|
218
|
+
sessions: {
|
|
219
|
+
control: ConnectionState;
|
|
220
|
+
civ: ConnectionState;
|
|
221
|
+
audio: ConnectionState;
|
|
222
|
+
};
|
|
223
|
+
/** Last disconnect timestamp (if any) */
|
|
224
|
+
lastDisconnectTime?: number;
|
|
225
|
+
/** Whether currently reconnecting */
|
|
226
|
+
isReconnecting: boolean;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* UDP session type identifier
|
|
230
|
+
*/
|
|
231
|
+
export declare enum SessionType {
|
|
232
|
+
/** Main control session (login, status, token management) */
|
|
233
|
+
CONTROL = "CONTROL",
|
|
234
|
+
/** CI-V command session */
|
|
235
|
+
CIV = "CIV",
|
|
236
|
+
/** Audio streaming session */
|
|
237
|
+
AUDIO = "AUDIO"
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Information about a connection loss event
|
|
241
|
+
*/
|
|
242
|
+
export interface ConnectionLostInfo {
|
|
243
|
+
/** Which session lost connection */
|
|
244
|
+
sessionType: SessionType;
|
|
245
|
+
/** Reason for disconnection */
|
|
246
|
+
reason: string;
|
|
247
|
+
/** Time since last received data (ms) */
|
|
248
|
+
timeSinceLastData: number;
|
|
249
|
+
/** Timestamp when the event occurred */
|
|
250
|
+
timestamp: number;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Information about a connection restored event
|
|
254
|
+
*/
|
|
255
|
+
export interface ConnectionRestoredInfo {
|
|
256
|
+
/** Which session was restored */
|
|
257
|
+
sessionType: SessionType;
|
|
258
|
+
/** How long the connection was down (ms) */
|
|
259
|
+
downtime: number;
|
|
260
|
+
/** Timestamp when the connection was restored */
|
|
261
|
+
timestamp: number;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Information about a reconnection attempt
|
|
265
|
+
*/
|
|
266
|
+
export interface ReconnectAttemptInfo {
|
|
267
|
+
/** Which session is attempting to reconnect */
|
|
268
|
+
sessionType: SessionType;
|
|
269
|
+
/** Current attempt number (1-based) */
|
|
270
|
+
attemptNumber: number;
|
|
271
|
+
/** Delay before this attempt (ms) */
|
|
272
|
+
delay: number;
|
|
273
|
+
/** Timestamp of this attempt */
|
|
274
|
+
timestamp: number;
|
|
275
|
+
/** Whether this is a full reconnect (all sessions) */
|
|
276
|
+
fullReconnect: boolean;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Information about a failed reconnection attempt
|
|
280
|
+
*/
|
|
281
|
+
export interface ReconnectFailedInfo {
|
|
282
|
+
/** Which session failed to reconnect */
|
|
283
|
+
sessionType: SessionType;
|
|
284
|
+
/** Attempt number that failed */
|
|
285
|
+
attemptNumber: number;
|
|
286
|
+
/** Error that caused the failure */
|
|
287
|
+
error: string;
|
|
288
|
+
/** Timestamp of the failure */
|
|
289
|
+
timestamp: number;
|
|
290
|
+
/** Whether this was a full reconnect attempt */
|
|
291
|
+
fullReconnect: boolean;
|
|
292
|
+
/** Whether we will retry */
|
|
293
|
+
willRetry: boolean;
|
|
294
|
+
/** Next retry delay (ms), if retrying */
|
|
295
|
+
nextRetryDelay?: number;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Configuration for connection monitoring
|
|
299
|
+
*/
|
|
300
|
+
export interface ConnectionMonitorConfig {
|
|
301
|
+
/**
|
|
302
|
+
* Timeout threshold in milliseconds
|
|
303
|
+
* If no data received within this period, connection is considered lost
|
|
304
|
+
* Default: 5000ms (5 seconds)
|
|
305
|
+
*/
|
|
306
|
+
timeout?: number;
|
|
307
|
+
/**
|
|
308
|
+
* How often to check for timeout (ms)
|
|
309
|
+
* Default: 1000ms (1 second)
|
|
310
|
+
*/
|
|
311
|
+
checkInterval?: number;
|
|
312
|
+
/**
|
|
313
|
+
* Enable automatic reconnection attempts
|
|
314
|
+
* Default: false
|
|
315
|
+
*/
|
|
316
|
+
autoReconnect?: boolean;
|
|
317
|
+
/**
|
|
318
|
+
* Maximum number of reconnection attempts
|
|
319
|
+
* Set to undefined for infinite retries
|
|
320
|
+
* Default: undefined (infinite retries)
|
|
321
|
+
*/
|
|
322
|
+
maxReconnectAttempts?: number;
|
|
323
|
+
/**
|
|
324
|
+
* Base delay between reconnection attempts (ms)
|
|
325
|
+
* Uses exponential backoff: attempt N uses delay * 2^(N-1)
|
|
326
|
+
* Default: 2000ms (2 seconds)
|
|
327
|
+
*/
|
|
328
|
+
reconnectBaseDelay?: number;
|
|
329
|
+
/**
|
|
330
|
+
* Maximum delay between reconnection attempts (ms)
|
|
331
|
+
* Caps the exponential backoff
|
|
332
|
+
* Default: 30000ms (30 seconds)
|
|
333
|
+
*/
|
|
334
|
+
reconnectMaxDelay?: number;
|
|
335
|
+
}
|
package/dist/types.js
CHANGED
|
@@ -1,2 +1,48 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SessionType = exports.ConnectionPhase = exports.ConnectionState = void 0;
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Connection Monitoring Types
|
|
6
|
+
// ============================================================================
|
|
7
|
+
/**
|
|
8
|
+
* Connection state enumeration
|
|
9
|
+
* Represents the current state of a UDP session
|
|
10
|
+
*/
|
|
11
|
+
var ConnectionState;
|
|
12
|
+
(function (ConnectionState) {
|
|
13
|
+
/** Connection is active and receiving data */
|
|
14
|
+
ConnectionState["CONNECTED"] = "CONNECTED";
|
|
15
|
+
/** Connection has been lost (timeout detected) */
|
|
16
|
+
ConnectionState["DISCONNECTED"] = "DISCONNECTED";
|
|
17
|
+
/** Attempting to restore connection */
|
|
18
|
+
ConnectionState["RECONNECTING"] = "RECONNECTING";
|
|
19
|
+
})(ConnectionState || (exports.ConnectionState = ConnectionState = {}));
|
|
20
|
+
/**
|
|
21
|
+
* Connection phase state machine
|
|
22
|
+
* Represents the high-level connection lifecycle state
|
|
23
|
+
*/
|
|
24
|
+
var ConnectionPhase;
|
|
25
|
+
(function (ConnectionPhase) {
|
|
26
|
+
/** Initial state or fully disconnected */
|
|
27
|
+
ConnectionPhase["IDLE"] = "IDLE";
|
|
28
|
+
/** Establishing connection (AreYouThere, Login, sub-sessions) */
|
|
29
|
+
ConnectionPhase["CONNECTING"] = "CONNECTING";
|
|
30
|
+
/** All sessions established and ready */
|
|
31
|
+
ConnectionPhase["CONNECTED"] = "CONNECTED";
|
|
32
|
+
/** Actively disconnecting */
|
|
33
|
+
ConnectionPhase["DISCONNECTING"] = "DISCONNECTING";
|
|
34
|
+
/** Attempting to reconnect after connection loss */
|
|
35
|
+
ConnectionPhase["RECONNECTING"] = "RECONNECTING";
|
|
36
|
+
})(ConnectionPhase || (exports.ConnectionPhase = ConnectionPhase = {}));
|
|
37
|
+
/**
|
|
38
|
+
* UDP session type identifier
|
|
39
|
+
*/
|
|
40
|
+
var SessionType;
|
|
41
|
+
(function (SessionType) {
|
|
42
|
+
/** Main control session (login, status, token management) */
|
|
43
|
+
SessionType["CONTROL"] = "CONTROL";
|
|
44
|
+
/** CI-V command session */
|
|
45
|
+
SessionType["CIV"] = "CIV";
|
|
46
|
+
/** Audio streaming session */
|
|
47
|
+
SessionType["AUDIO"] = "AUDIO";
|
|
48
|
+
})(SessionType || (exports.SessionType = SessionType = {}));
|