icom-wlan-node 0.1.1 → 0.2.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.
@@ -14,4 +14,11 @@ export declare const IcomRigCommands: {
14
14
  getConnectorWLanLevel(ctrAddr: number, rigAddr: number): Buffer;
15
15
  setConnectorWLanLevel(ctrAddr: number, rigAddr: number, level: number): Buffer;
16
16
  setConnectorDataMode(ctrAddr: number, rigAddr: number, mode: number): Buffer;
17
+ getSquelchStatus(ctrAddr: number, rigAddr: number): Buffer;
18
+ getAudioSquelch(ctrAddr: number, rigAddr: number): Buffer;
19
+ getOvfStatus(ctrAddr: number, rigAddr: number): Buffer;
20
+ getPowerLevel(ctrAddr: number, rigAddr: number): Buffer;
21
+ getCompLevel(ctrAddr: number, rigAddr: number): Buffer;
22
+ getVoltage(ctrAddr: number, rigAddr: number): Buffer;
23
+ getCurrent(ctrAddr: number, rigAddr: number): Buffer;
17
24
  };
@@ -69,5 +69,33 @@ exports.IcomRigCommands = {
69
69
  setConnectorDataMode(ctrAddr, rigAddr, mode) {
70
70
  // FE FE [rig] [ctr] 0x1A 0x05 0x01 0x19 [mode] FD
71
71
  return Buffer.from([0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x1a, 0x05, 0x01, 0x19, mode & 0xff, 0xfd]);
72
+ },
73
+ getSquelchStatus(ctrAddr, rigAddr) {
74
+ // FE FE [rig] [ctr] 0x15 0x01 FD
75
+ return Buffer.from([0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x15, 0x01, 0xfd]);
76
+ },
77
+ getAudioSquelch(ctrAddr, rigAddr) {
78
+ // FE FE [rig] [ctr] 0x15 0x05 FD
79
+ return Buffer.from([0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x15, 0x05, 0xfd]);
80
+ },
81
+ getOvfStatus(ctrAddr, rigAddr) {
82
+ // FE FE [rig] [ctr] 0x15 0x07 FD
83
+ return Buffer.from([0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x15, 0x07, 0xfd]);
84
+ },
85
+ getPowerLevel(ctrAddr, rigAddr) {
86
+ // FE FE [rig] [ctr] 0x15 0x11 FD
87
+ return Buffer.from([0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x15, 0x11, 0xfd]);
88
+ },
89
+ getCompLevel(ctrAddr, rigAddr) {
90
+ // FE FE [rig] [ctr] 0x15 0x14 FD
91
+ return Buffer.from([0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x15, 0x14, 0xfd]);
92
+ },
93
+ getVoltage(ctrAddr, rigAddr) {
94
+ // FE FE [rig] [ctr] 0x15 0x15 FD
95
+ return Buffer.from([0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x15, 0x15, 0xfd]);
96
+ },
97
+ getCurrent(ctrAddr, rigAddr) {
98
+ // FE FE [rig] [ctr] 0x15 0x16 FD
99
+ return Buffer.from([0xfe, 0xfe, rigAddr & 0xff, ctrAddr & 0xff, 0x15, 0x16, 0xfd]);
72
100
  }
73
101
  };
@@ -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
- this.socket.send(buf, port, ip);
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,253 @@ export interface LevelMeterReading {
158
162
  /** Percentage (0-100%) */
159
163
  percent: number;
160
164
  }
165
+ /**
166
+ * Squelch status reading (CI-V 0x15/0x01)
167
+ * Indicates whether noise/signal squelch gate is open or closed
168
+ */
169
+ export interface SquelchStatusReading {
170
+ /** Raw BCD value (0 or 1) */
171
+ raw: number;
172
+ /** True if squelch is open (signal present), false if closed (no signal) */
173
+ isOpen: boolean;
174
+ }
175
+ /**
176
+ * Audio squelch reading (CI-V 0x15/0x05)
177
+ * Indicates audio squelch state
178
+ */
179
+ export interface AudioSquelchReading {
180
+ /** Raw BCD value (0 or 1) */
181
+ raw: number;
182
+ /** True if audio squelch is open, false if closed */
183
+ isOpen: boolean;
184
+ }
185
+ /**
186
+ * OVF (ADC overload) status reading (CI-V 0x15/0x07)
187
+ * Indicates whether the ADC (analog-to-digital converter) is overloading
188
+ */
189
+ export interface OvfStatusReading {
190
+ /** Raw BCD value (0 or 1) */
191
+ raw: number;
192
+ /** True if ADC is overloading, false if normal */
193
+ isOverload: boolean;
194
+ }
195
+ /**
196
+ * Power output level reading (CI-V 0x15/0x11)
197
+ * Represents transmitter output power level
198
+ */
199
+ export interface PowerLevelReading {
200
+ /**
201
+ * Raw BCD value (0-255)
202
+ * Calibration: 0143≈50%, 0213≈100%
203
+ */
204
+ raw: number;
205
+ /** Percentage of maximum power (0-100%) */
206
+ percent: number;
207
+ }
208
+ /**
209
+ * COMP (voice compression) level reading (CI-V 0x15/0x14)
210
+ * Represents speech compressor level during transmission
211
+ */
212
+ export interface CompLevelReading {
213
+ /** Raw BCD value (0-255) */
214
+ raw: number;
215
+ /** Percentage of maximum compression (0-100%) */
216
+ percent: number;
217
+ }
218
+ /**
219
+ * Voltage reading (CI-V 0x15/0x15)
220
+ * Represents power supply voltage
221
+ */
222
+ export interface VoltageReading {
223
+ /**
224
+ * Raw BCD value (0-255)
225
+ * Calibration: 0075≈5V, 0241≈16V
226
+ */
227
+ raw: number;
228
+ /** Voltage in volts */
229
+ volts: number;
230
+ }
231
+ /**
232
+ * Current reading (CI-V 0x15/0x16)
233
+ * Represents power supply current draw
234
+ */
235
+ export interface CurrentReading {
236
+ /**
237
+ * Raw BCD value (0-255)
238
+ * Calibration: 0121≈2A, 0241≈4A
239
+ */
240
+ raw: number;
241
+ /** Current in amperes */
242
+ amps: number;
243
+ }
244
+ /**
245
+ * Connection state enumeration
246
+ * Represents the current state of a UDP session
247
+ */
248
+ export declare enum ConnectionState {
249
+ /** Connection is active and receiving data */
250
+ CONNECTED = "CONNECTED",
251
+ /** Connection has been lost (timeout detected) */
252
+ DISCONNECTED = "DISCONNECTED",
253
+ /** Attempting to restore connection */
254
+ RECONNECTING = "RECONNECTING"
255
+ }
256
+ /**
257
+ * Connection phase state machine
258
+ * Represents the high-level connection lifecycle state
259
+ */
260
+ export declare enum ConnectionPhase {
261
+ /** Initial state or fully disconnected */
262
+ IDLE = "IDLE",
263
+ /** Establishing connection (AreYouThere, Login, sub-sessions) */
264
+ CONNECTING = "CONNECTING",
265
+ /** All sessions established and ready */
266
+ CONNECTED = "CONNECTED",
267
+ /** Actively disconnecting */
268
+ DISCONNECTING = "DISCONNECTING",
269
+ /** Attempting to reconnect after connection loss */
270
+ RECONNECTING = "RECONNECTING"
271
+ }
272
+ /**
273
+ * Connection session tracking information
274
+ * Used to prevent race conditions and track connection lifecycle
275
+ */
276
+ export interface ConnectionSession {
277
+ /** Current connection phase */
278
+ phase: ConnectionPhase;
279
+ /** Unique session ID to prevent race conditions */
280
+ sessionId: number;
281
+ /** When this connection session started */
282
+ startTime: number;
283
+ /** When the connection was last lost (for downtime calculation) */
284
+ lastDisconnectTime?: number;
285
+ }
286
+ /**
287
+ * Connection metrics for observability
288
+ */
289
+ export interface ConnectionMetrics {
290
+ /** Current connection phase */
291
+ phase: ConnectionPhase;
292
+ /** Current session ID */
293
+ sessionId: number;
294
+ /** Total uptime in milliseconds (since last successful connect) */
295
+ uptime: number;
296
+ /** Individual session states */
297
+ sessions: {
298
+ control: ConnectionState;
299
+ civ: ConnectionState;
300
+ audio: ConnectionState;
301
+ };
302
+ /** Last disconnect timestamp (if any) */
303
+ lastDisconnectTime?: number;
304
+ /** Whether currently reconnecting */
305
+ isReconnecting: boolean;
306
+ }
307
+ /**
308
+ * UDP session type identifier
309
+ */
310
+ export declare enum SessionType {
311
+ /** Main control session (login, status, token management) */
312
+ CONTROL = "CONTROL",
313
+ /** CI-V command session */
314
+ CIV = "CIV",
315
+ /** Audio streaming session */
316
+ AUDIO = "AUDIO"
317
+ }
318
+ /**
319
+ * Information about a connection loss event
320
+ */
321
+ export interface ConnectionLostInfo {
322
+ /** Which session lost connection */
323
+ sessionType: SessionType;
324
+ /** Reason for disconnection */
325
+ reason: string;
326
+ /** Time since last received data (ms) */
327
+ timeSinceLastData: number;
328
+ /** Timestamp when the event occurred */
329
+ timestamp: number;
330
+ }
331
+ /**
332
+ * Information about a connection restored event
333
+ */
334
+ export interface ConnectionRestoredInfo {
335
+ /** Which session was restored */
336
+ sessionType: SessionType;
337
+ /** How long the connection was down (ms) */
338
+ downtime: number;
339
+ /** Timestamp when the connection was restored */
340
+ timestamp: number;
341
+ }
342
+ /**
343
+ * Information about a reconnection attempt
344
+ */
345
+ export interface ReconnectAttemptInfo {
346
+ /** Which session is attempting to reconnect */
347
+ sessionType: SessionType;
348
+ /** Current attempt number (1-based) */
349
+ attemptNumber: number;
350
+ /** Delay before this attempt (ms) */
351
+ delay: number;
352
+ /** Timestamp of this attempt */
353
+ timestamp: number;
354
+ /** Whether this is a full reconnect (all sessions) */
355
+ fullReconnect: boolean;
356
+ }
357
+ /**
358
+ * Information about a failed reconnection attempt
359
+ */
360
+ export interface ReconnectFailedInfo {
361
+ /** Which session failed to reconnect */
362
+ sessionType: SessionType;
363
+ /** Attempt number that failed */
364
+ attemptNumber: number;
365
+ /** Error that caused the failure */
366
+ error: string;
367
+ /** Timestamp of the failure */
368
+ timestamp: number;
369
+ /** Whether this was a full reconnect attempt */
370
+ fullReconnect: boolean;
371
+ /** Whether we will retry */
372
+ willRetry: boolean;
373
+ /** Next retry delay (ms), if retrying */
374
+ nextRetryDelay?: number;
375
+ }
376
+ /**
377
+ * Configuration for connection monitoring
378
+ */
379
+ export interface ConnectionMonitorConfig {
380
+ /**
381
+ * Timeout threshold in milliseconds
382
+ * If no data received within this period, connection is considered lost
383
+ * Default: 5000ms (5 seconds)
384
+ */
385
+ timeout?: number;
386
+ /**
387
+ * How often to check for timeout (ms)
388
+ * Default: 1000ms (1 second)
389
+ */
390
+ checkInterval?: number;
391
+ /**
392
+ * Enable automatic reconnection attempts
393
+ * Default: false
394
+ */
395
+ autoReconnect?: boolean;
396
+ /**
397
+ * Maximum number of reconnection attempts
398
+ * Set to undefined for infinite retries
399
+ * Default: undefined (infinite retries)
400
+ */
401
+ maxReconnectAttempts?: number;
402
+ /**
403
+ * Base delay between reconnection attempts (ms)
404
+ * Uses exponential backoff: attempt N uses delay * 2^(N-1)
405
+ * Default: 2000ms (2 seconds)
406
+ */
407
+ reconnectBaseDelay?: number;
408
+ /**
409
+ * Maximum delay between reconnection attempts (ms)
410
+ * Caps the exponential backoff
411
+ * Default: 30000ms (30 seconds)
412
+ */
413
+ reconnectMaxDelay?: number;
414
+ }
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 = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icom-wlan-node",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Icom WLAN (CI‑V, audio) protocol implementation for Node.js/TypeScript.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",