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.
@@ -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,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 = {}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icom-wlan-node",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
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",