@aurora-blue/signalk-connector 0.2.0-dev.28 → 0.2.0-dev.30

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0-dev.28
1
+ 0.2.0-dev.30
@@ -45,7 +45,11 @@ export declare class ConnectorRuntime {
45
45
  private readonly emissionTimes;
46
46
  private publisher;
47
47
  private captureTail;
48
- private pendingCaptureEvents;
48
+ private captureResult;
49
+ private captureRunning;
50
+ private readonly pendingCaptures;
51
+ private captureInFlight;
52
+ private captureTransition;
49
53
  private nextCaptureStatusAt;
50
54
  constructor(app: ServerAPI, store: StateStore, config: ConnectorConfig, options?: ConnectorRuntimeOptions);
51
55
  start(): Promise<void>;
@@ -59,7 +63,8 @@ export declare class ConnectorRuntime {
59
63
  source: string | null;
60
64
  } | null;
61
65
  stop(): Promise<void>;
62
- capture(delta: Delta, subscriptionScope?: SubscriptionScope): Promise<void>;
66
+ capture(delta: Delta, subscriptionScope?: SubscriptionScope, waitForPersistence?: boolean): Promise<void>;
67
+ private drainCaptures;
63
68
  private captureEvents;
64
69
  status(): Promise<ConnectorStatus>;
65
70
  flush(): Promise<void>;
@@ -57,7 +57,11 @@ class ConnectorRuntime {
57
57
  emissionTimes = [];
58
58
  publisher = null;
59
59
  captureTail = Promise.resolve();
60
- pendingCaptureEvents = 0;
60
+ captureResult = Promise.resolve();
61
+ captureRunning = false;
62
+ pendingCaptures = new Map();
63
+ captureInFlight = 0;
64
+ captureTransition = 0;
61
65
  nextCaptureStatusAt = 0;
62
66
  constructor(app, store, config, options = {}) {
63
67
  this.app = app;
@@ -204,7 +208,7 @@ class ConnectorRuntime {
204
208
  await this.captureTail;
205
209
  await this.store.summary();
206
210
  }
207
- async capture(delta, subscriptionScope = "self") {
211
+ async capture(delta, subscriptionScope = "self", waitForPersistence = true) {
208
212
  if (this.stopped) {
209
213
  return;
210
214
  }
@@ -229,19 +233,66 @@ class ConnectorRuntime {
229
233
  events.length === 0) {
230
234
  return;
231
235
  }
232
- const admitted = events.slice(0, MAXIMUM_PENDING_CAPTURE_EVENTS - this.pendingCaptureEvents);
233
- this.droppedEvents += events.length - admitted.length;
234
- if (admitted.length === 0) {
236
+ let admitted = false;
237
+ for (const event of events) {
238
+ const kind = (0, telemetry_catalog_1.telemetryDefinition)(event.context, event.path)?.valueKind;
239
+ // Keep transitions in order. Only unpersisted sampled values may be replaced.
240
+ const sampled = kind === "number" || kind === "position" || kind === "json";
241
+ const key = sampled
242
+ ? `${event.context}\0${event.path}\0${event.source ?? ""}`
243
+ : this.captureTransition++;
244
+ if (this.pendingCaptures.has(key)) {
245
+ // Map.set preserves the original FIFO position, preventing hot-path starvation.
246
+ this.pendingCaptures.set(key, event);
247
+ admitted = true;
248
+ this.suppressedEvents += 1;
249
+ }
250
+ else if (this.pendingCaptures.size + this.captureInFlight <
251
+ MAXIMUM_PENDING_CAPTURE_EVENTS) {
252
+ this.pendingCaptures.set(key, event);
253
+ admitted = true;
254
+ }
255
+ else {
256
+ this.droppedEvents += 1;
257
+ }
258
+ }
259
+ if (!admitted)
235
260
  return;
261
+ if (!this.captureRunning && this.pendingCaptures.size > 0) {
262
+ this.captureRunning = true;
263
+ const current = Promise.resolve().then(() => this.drainCaptures());
264
+ this.captureTail = current.catch((error) => {
265
+ this.app.setPluginError("Signal K data could not be buffered safely.");
266
+ this.app.error(error instanceof Error ? error.message : String(error));
267
+ });
268
+ // All admitted callbacks observe write failures; the tail remains safe for stop().
269
+ this.captureResult = current;
236
270
  }
237
- this.pendingCaptureEvents += admitted.length;
238
- const current = this.captureTail.then(() => this.captureEvents(admitted));
239
- this.captureTail = current.catch(() => undefined);
271
+ // Subscription callbacks must not retain a waiter for a continuously busy drain.
272
+ if (waitForPersistence)
273
+ await this.captureResult;
274
+ }
275
+ async drainCaptures() {
240
276
  try {
241
- await current;
277
+ while (this.pendingCaptures.size > 0) {
278
+ const [key, event] = this.pendingCaptures.entries().next().value;
279
+ this.pendingCaptures.delete(key);
280
+ this.captureInFlight = 1;
281
+ try {
282
+ await this.captureEvents([event]);
283
+ }
284
+ finally {
285
+ this.captureInFlight = 0;
286
+ }
287
+ }
288
+ }
289
+ catch (error) {
290
+ this.droppedEvents += this.pendingCaptures.size;
291
+ this.pendingCaptures.clear();
292
+ throw error;
242
293
  }
243
294
  finally {
244
- this.pendingCaptureEvents -= admitted.length;
295
+ this.captureRunning = false;
245
296
  }
246
297
  }
247
298
  async captureEvents(events) {
@@ -412,7 +463,7 @@ class ConnectorRuntime {
412
463
  this.app.setPluginError("Signal K subscription failed.");
413
464
  this.app.error(error instanceof Error ? error.message : String(error));
414
465
  }, (delta) => {
415
- void this.capture(delta, context === "vessels.self" ? "self" : "ais").catch((error) => {
466
+ void this.capture(delta, context === "vessels.self" ? "self" : "ais", false).catch((error) => {
416
467
  this.app.setPluginError("Signal K data could not be buffered safely.");
417
468
  this.app.error(error instanceof Error ? error.message : String(error));
418
469
  });
@@ -55,6 +55,42 @@ exports.SELF_TELEMETRY = [
55
55
  self("environment.outside.temperature", "number", "scalar", 30_000, 60_000, 0.5),
56
56
  self("environment.outside.pressure", "number", "scalar", 30_000, 60_000, 50),
57
57
  self("environment.outside.relativeHumidity", "number", "scalar", 30_000, 60_000, 0.02),
58
+ self("electrical.batteries.*.power", "number", "scalar", 5_000, 60_000, 1),
59
+ self("electrical.batteries.*.capacity.remaining", "number", "scalar", 5_000, 60_000, 1),
60
+ self("electrical.batteries.*.capacity.nominal", "number", "scalar", 5_000, 60_000, 1),
61
+ self("electrical.solar.*.voltage", "number", "scalar", 5_000, 60_000, 1),
62
+ self("electrical.solar.*.current", "number", "scalar", 5_000, 60_000, 1),
63
+ self("electrical.solar.*.power", "number", "scalar", 5_000, 60_000, 1),
64
+ self("electrical.chargers.*.voltage", "number", "scalar", 5_000, 60_000, 1),
65
+ self("electrical.chargers.*.current", "number", "scalar", 5_000, 60_000, 1),
66
+ self("electrical.chargers.*.power", "number", "scalar", 5_000, 60_000, 1),
67
+ self("electrical.alternators.*.voltage", "number", "scalar", 5_000, 60_000, 1),
68
+ self("electrical.alternators.*.current", "number", "scalar", 5_000, 60_000, 1),
69
+ self("electrical.alternators.*.power", "number", "scalar", 5_000, 60_000, 1),
70
+ self("electrical.solar.*.panelPower", "number", "scalar", 5_000, 60_000, 1),
71
+ self("electrical.solar.*.yieldToday", "number", "scalar", 5_000, 60_000, 1),
72
+ self("electrical.inverters.*.dc.voltage", "number", "scalar", 5_000, 60_000, 1),
73
+ self("electrical.inverters.*.dc.current", "number", "scalar", 5_000, 60_000, 1),
74
+ self("electrical.inverters.*.dc.power", "number", "scalar", 5_000, 60_000, 1),
75
+ self("electrical.inverters.*.ac.realPower", "number", "scalar", 5_000, 60_000, 1),
76
+ self("electrical.inverters.*.ac.lineNeutralVoltage", "number", "scalar", 5_000, 60_000, 1),
77
+ self("electrical.inverters.*.ac.frequency", "number", "scalar", 5_000, 60_000, 1),
78
+ self("electrical.ac.*.phase.*.realPower", "number", "scalar", 5_000, 60_000, 1),
79
+ self("electrical.ac.*.phase.*.lineNeutralVoltage", "number", "scalar", 5_000, 60_000, 1),
80
+ self("electrical.ac.*.phase.*.current", "number", "scalar", 5_000, 60_000, 1),
81
+ self("electrical.ac.*.phase.*.frequency", "number", "scalar", 5_000, 60_000, 1),
82
+ self("electrical.dc.*.power", "number", "scalar", 5_000, 60_000, 1),
83
+ self("electrical.dc.*.voltage", "number", "scalar", 5_000, 60_000, 1),
84
+ self("electrical.dc.*.current", "number", "scalar", 5_000, 60_000, 1),
85
+ self("electrical.batteries.*.name", "string", "identity", 30_000, 600_000, 0),
86
+ self("electrical.solar.*.name", "string", "identity", 30_000, 600_000, 0),
87
+ self("electrical.chargers.*.name", "string", "identity", 30_000, 600_000, 0),
88
+ self("electrical.alternators.*.name", "string", "identity", 30_000, 600_000, 0),
89
+ self("electrical.inverters.*.name", "string", "identity", 30_000, 600_000, 0),
90
+ self("electrical.ac.*.name", "string", "identity", 30_000, 600_000, 0),
91
+ self("electrical.dc.*.name", "string", "identity", 30_000, 600_000, 0),
92
+ self("electrical.inverters.*.inverterMode", "string", "identity", 30_000, 60_000, 0),
93
+ self("electrical.chargers.*.chargingMode", "string", "identity", 30_000, 60_000, 0),
58
94
  self("electrical.batteries.*.voltage", "number", "scalar", 10_000, 300_000, 0.1),
59
95
  self("electrical.batteries.*.current", "number", "scalar", 5_000, 120_000, 1),
60
96
  self("electrical.batteries.*.temperature", "number", "scalar", 30_000, 600_000, 0.5),
@@ -1 +1 @@
1
- export declare const CONNECTOR_VERSION = "0.2.0-dev.28";
1
+ export declare const CONNECTOR_VERSION = "0.2.0-dev.30";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CONNECTOR_VERSION = void 0;
4
- exports.CONNECTOR_VERSION = "0.2.0-dev.28";
4
+ exports.CONNECTOR_VERSION = "0.2.0-dev.30";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurora-blue/signalk-connector",
3
- "version": "0.2.0-dev.28",
3
+ "version": "0.2.0-dev.30",
4
4
  "description": "Signal K edge connector for the Aurora Blue ABOARD ingest protocol",
5
5
  "private": false,
6
6
  "main": "dist/src/index.js",
@@ -48,9 +48,9 @@
48
48
  "release:verify": "node tools/release.mjs verify"
49
49
  },
50
50
  "devDependencies": {
51
- "@signalk/server-api": "2.31.1",
52
- "@types/node": "22.20.1",
53
- "prettier": "3.9.6",
51
+ "@signalk/server-api": "2.35.0",
52
+ "@types/node": "24.19.2",
53
+ "prettier": "3.9.10",
54
54
  "typescript": "7.0.2"
55
55
  },
56
56
  "license": "MIT",