signalk-wakelogger 0.2.0-beta.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/CHANGELOG.md +32 -0
- package/LICENSE +201 -0
- package/README.md +49 -0
- package/SECURITY.md +7 -0
- package/assets/screenshots/plugin-config.png +0 -0
- package/assets/screenshots/plugin-status.png +0 -0
- package/assets/wakelogger.svg +6 -0
- package/dist/config/defaults.d.ts +18 -0
- package/dist/config/defaults.js +30 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/schema.d.ts +43 -0
- package/dist/config/schema.js +47 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +338 -0
- package/dist/index.js.map +1 -0
- package/dist/outbox/database-outbox.d.ts +25 -0
- package/dist/outbox/database-outbox.js +186 -0
- package/dist/outbox/database-outbox.js.map +1 -0
- package/dist/outbox/database-types.d.ts +19 -0
- package/dist/outbox/database-types.js +3 -0
- package/dist/outbox/database-types.js.map +1 -0
- package/dist/outbox/factory.d.ts +12 -0
- package/dist/outbox/factory.js +98 -0
- package/dist/outbox/factory.js.map +1 -0
- package/dist/outbox/file-outbox.d.ts +27 -0
- package/dist/outbox/file-outbox.js +280 -0
- package/dist/outbox/file-outbox.js.map +1 -0
- package/dist/outbox/interface.d.ts +33 -0
- package/dist/outbox/interface.js +3 -0
- package/dist/outbox/interface.js.map +1 -0
- package/dist/pairing/association-client.d.ts +3 -0
- package/dist/pairing/association-client.js +31 -0
- package/dist/pairing/association-client.js.map +1 -0
- package/dist/pairing/credentials.d.ts +46 -0
- package/dist/pairing/credentials.js +168 -0
- package/dist/pairing/credentials.js.map +1 -0
- package/dist/pairing/pairing-client.d.ts +19 -0
- package/dist/pairing/pairing-client.js +102 -0
- package/dist/pairing/pairing-client.js.map +1 -0
- package/dist/signalk/normaliser.d.ts +11 -0
- package/dist/signalk/normaliser.js +119 -0
- package/dist/signalk/normaliser.js.map +1 -0
- package/dist/signalk/paths.d.ts +2 -0
- package/dist/signalk/paths.js +14 -0
- package/dist/signalk/paths.js.map +1 -0
- package/dist/signalk/subscriber.d.ts +2 -0
- package/dist/signalk/subscriber.js +20 -0
- package/dist/signalk/subscriber.js.map +1 -0
- package/dist/telemetry/profile-store.d.ts +7 -0
- package/dist/telemetry/profile-store.js +39 -0
- package/dist/telemetry/profile-store.js.map +1 -0
- package/dist/telemetry/profile.d.ts +32 -0
- package/dist/telemetry/profile.js +94 -0
- package/dist/telemetry/profile.js.map +1 -0
- package/dist/telemetry/sampler.d.ts +14 -0
- package/dist/telemetry/sampler.js +44 -0
- package/dist/telemetry/sampler.js.map +1 -0
- package/dist/telemetry/types.d.ts +73 -0
- package/dist/telemetry/types.js +3 -0
- package/dist/telemetry/types.js.map +1 -0
- package/dist/transport/adaptive-mode.d.ts +23 -0
- package/dist/transport/adaptive-mode.js +63 -0
- package/dist/transport/adaptive-mode.js.map +1 -0
- package/dist/transport/mqtt-client.d.ts +69 -0
- package/dist/transport/mqtt-client.js +365 -0
- package/dist/transport/mqtt-client.js.map +1 -0
- package/dist/transport/topics.d.ts +10 -0
- package/dist/transport/topics.js +13 -0
- package/dist/transport/topics.js.map +1 -0
- package/dist/trips/state-machine.d.ts +38 -0
- package/dist/trips/state-machine.js +118 -0
- package/dist/trips/state-machine.js.map +1 -0
- package/docs/architecture.md +17 -0
- package/docs/development.md +49 -0
- package/docs/implementation-plan.md +1943 -0
- package/docs/original-plan-gap-audit.md +47 -0
- package/docs/pairing.md +17 -0
- package/docs/release.md +52 -0
- package/docs/telemetry-protocol.md +50 -0
- package/docs/trip-detection.md +7 -0
- package/docs/validation.md +35 -0
- package/package.json +80 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.WakeLoggerTransport = void 0;
|
|
7
|
+
const mqtt_1 = __importDefault(require("mqtt"));
|
|
8
|
+
const profile_1 = require("../telemetry/profile");
|
|
9
|
+
const adaptive_mode_1 = require("./adaptive-mode");
|
|
10
|
+
const topics_1 = require("./topics");
|
|
11
|
+
class WakeLoggerTransport {
|
|
12
|
+
credentials;
|
|
13
|
+
outbox;
|
|
14
|
+
options;
|
|
15
|
+
client;
|
|
16
|
+
stopped = true;
|
|
17
|
+
reconnectAttempt = 0;
|
|
18
|
+
reconnectTimer;
|
|
19
|
+
pumpTimer;
|
|
20
|
+
healthTimer;
|
|
21
|
+
pumpRunning = false;
|
|
22
|
+
current;
|
|
23
|
+
statePublishInFlight = false;
|
|
24
|
+
statePublishPending = false;
|
|
25
|
+
authenticationFailed = false;
|
|
26
|
+
statusMetrics;
|
|
27
|
+
profile;
|
|
28
|
+
monitor;
|
|
29
|
+
topics;
|
|
30
|
+
inFlight = [];
|
|
31
|
+
nextPublishAfter = 0;
|
|
32
|
+
lastLivePublishedAt = 0;
|
|
33
|
+
lastLivePublishedSequence = 0;
|
|
34
|
+
lastAcknowledgedAt;
|
|
35
|
+
acknowledgementLatencyMs;
|
|
36
|
+
publishedBytes = 0;
|
|
37
|
+
lastMode;
|
|
38
|
+
backlogMessageCount = 0;
|
|
39
|
+
backlogTokens = 0;
|
|
40
|
+
backlogTokenAt;
|
|
41
|
+
constructor(credentials, outbox, options) {
|
|
42
|
+
this.credentials = credentials;
|
|
43
|
+
this.outbox = outbox;
|
|
44
|
+
this.options = options;
|
|
45
|
+
this.topics = (0, topics_1.deviceTopics)(credentials.deviceId);
|
|
46
|
+
this.profile = options.profile;
|
|
47
|
+
this.monitor = new adaptive_mode_1.AdaptiveModeMonitor(() => this.now());
|
|
48
|
+
}
|
|
49
|
+
start() {
|
|
50
|
+
if (!this.stopped)
|
|
51
|
+
return;
|
|
52
|
+
this.stopped = false;
|
|
53
|
+
this.healthTimer = setInterval(() => {
|
|
54
|
+
this.monitor.tick();
|
|
55
|
+
this.syncMode();
|
|
56
|
+
void this.checkAcknowledgementTimeout();
|
|
57
|
+
}, 5000);
|
|
58
|
+
void this.outbox.latest().then((sample) => {
|
|
59
|
+
if (this.stopped)
|
|
60
|
+
return;
|
|
61
|
+
this.current = sample;
|
|
62
|
+
this.connect();
|
|
63
|
+
}).catch((error) => this.options.onState('degraded', sanitizeError(String(error))));
|
|
64
|
+
}
|
|
65
|
+
async stop() {
|
|
66
|
+
this.stopped = true;
|
|
67
|
+
if (this.reconnectTimer)
|
|
68
|
+
clearTimeout(this.reconnectTimer);
|
|
69
|
+
if (this.pumpTimer)
|
|
70
|
+
clearInterval(this.pumpTimer);
|
|
71
|
+
if (this.healthTimer)
|
|
72
|
+
clearInterval(this.healthTimer);
|
|
73
|
+
const client = this.client;
|
|
74
|
+
this.client = undefined;
|
|
75
|
+
this.monitor.disconnected('plugin_stopped');
|
|
76
|
+
this.syncMode();
|
|
77
|
+
if (!client)
|
|
78
|
+
return;
|
|
79
|
+
if (client.connected)
|
|
80
|
+
await this.publish(client, this.topics.status, JSON.stringify(statusPayload('offline', this.effectiveStatusMetrics())), { qos: 1, retain: true }).catch(() => undefined);
|
|
81
|
+
await new Promise((resolve) => client.end(false, {}, () => resolve()));
|
|
82
|
+
}
|
|
83
|
+
updateCurrent(sample) {
|
|
84
|
+
this.current = sample;
|
|
85
|
+
this.statePublishPending = true;
|
|
86
|
+
if (this.client?.connected) {
|
|
87
|
+
void this.drainCurrentState().catch((error) => this.transportFailure(error));
|
|
88
|
+
void this.publishLiveSample().catch((error) => this.transportFailure(error));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
updateProfile(profile) { this.profile = profile; }
|
|
92
|
+
updateStatus(metrics) {
|
|
93
|
+
this.statusMetrics = metrics;
|
|
94
|
+
if (this.client?.connected) {
|
|
95
|
+
void this.publish(this.client, this.topics.status, JSON.stringify(statusPayload(metrics.connectionState, this.effectiveStatusMetrics())), { qos: 1, retain: true })
|
|
96
|
+
.catch((error) => this.transportFailure(error));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
transportMetrics() {
|
|
100
|
+
const health = this.monitor.current();
|
|
101
|
+
return { networkMode: health.mode, modeReason: health.reason, lastAcknowledgedAt: this.lastAcknowledgedAt,
|
|
102
|
+
acknowledgementLatencyMs: this.acknowledgementLatencyMs, reconnectCount: health.reconnectCount, publishedBytes: this.publishedBytes };
|
|
103
|
+
}
|
|
104
|
+
connect() {
|
|
105
|
+
if (this.stopped)
|
|
106
|
+
return;
|
|
107
|
+
this.options.onState('connecting');
|
|
108
|
+
this.authenticationFailed = false;
|
|
109
|
+
const scheme = this.credentials.tls ? 'mqtts' : 'mqtt';
|
|
110
|
+
const url = `${scheme}://${this.credentials.mqttHost}:${this.credentials.mqttPort}`;
|
|
111
|
+
const mqttOptions = {
|
|
112
|
+
protocolVersion: 5, clean: false, reconnectPeriod: 0, connectTimeout: 15_000,
|
|
113
|
+
...(this.credentials.tls ? { rejectUnauthorized: true, minVersion: 'TLSv1.2' } : {}), clientId: this.credentials.clientId,
|
|
114
|
+
username: this.credentials.username, password: this.credentials.password,
|
|
115
|
+
properties: { sessionExpiryInterval: 24 * 60 * 60 },
|
|
116
|
+
will: { topic: this.topics.status, payload: Buffer.from(JSON.stringify(statusPayload('offline'))), qos: 1, retain: true }
|
|
117
|
+
};
|
|
118
|
+
const client = mqtt_1.default.connect(url, mqttOptions);
|
|
119
|
+
this.client = client;
|
|
120
|
+
client.on('connect', () => void this.onConnect(client).catch((error) => { this.transportFailure(error); client.end(true); }));
|
|
121
|
+
client.on('message', (topic, payload) => void this.onMessage(topic, payload));
|
|
122
|
+
client.on('error', (error) => {
|
|
123
|
+
const auth = /auth|not authorized|bad user/i.test(error.message);
|
|
124
|
+
this.authenticationFailed = auth;
|
|
125
|
+
if (auth)
|
|
126
|
+
this.monitor.disconnected('authentication_failed');
|
|
127
|
+
else
|
|
128
|
+
this.monitor.failure('mqtt_error');
|
|
129
|
+
this.syncMode();
|
|
130
|
+
this.options.onState(auth ? 'authentication_failed' : 'degraded', sanitizeError(error.message));
|
|
131
|
+
});
|
|
132
|
+
client.on('close', () => {
|
|
133
|
+
if (client !== this.client || this.stopped)
|
|
134
|
+
return;
|
|
135
|
+
this.monitor.disconnected(this.authenticationFailed ? 'authentication_failed' : 'mqtt_disconnected');
|
|
136
|
+
this.syncMode();
|
|
137
|
+
if (this.authenticationFailed)
|
|
138
|
+
this.scheduleReconnect(true);
|
|
139
|
+
else {
|
|
140
|
+
this.options.onState('offline');
|
|
141
|
+
this.scheduleReconnect();
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
async onConnect(client) {
|
|
146
|
+
if (client !== this.client || this.stopped)
|
|
147
|
+
return;
|
|
148
|
+
this.reconnectAttempt = 0;
|
|
149
|
+
this.inFlight = [];
|
|
150
|
+
const stats = await this.outbox.stats();
|
|
151
|
+
this.backlogMessageCount = stats.messageCount;
|
|
152
|
+
this.nextPublishAfter = stats.acknowledgedSequence;
|
|
153
|
+
await subscribe(client, [this.topics.ack, this.topics.profile]);
|
|
154
|
+
this.monitor.connected();
|
|
155
|
+
this.syncMode();
|
|
156
|
+
this.backlogTokenAt = this.now();
|
|
157
|
+
this.backlogTokens = this.profile.transport.normalBacklogBytesPerSecond;
|
|
158
|
+
await this.publish(client, this.topics.status, JSON.stringify(statusPayload('online', this.effectiveStatusMetrics())), { qos: 1, retain: true });
|
|
159
|
+
this.statePublishPending = this.current !== undefined;
|
|
160
|
+
await this.drainCurrentState();
|
|
161
|
+
this.options.onState('online');
|
|
162
|
+
if (this.pumpTimer)
|
|
163
|
+
clearInterval(this.pumpTimer);
|
|
164
|
+
this.pumpTimer = setInterval(() => void this.pump(), 1000);
|
|
165
|
+
void this.pump();
|
|
166
|
+
}
|
|
167
|
+
async drainCurrentState() {
|
|
168
|
+
if (this.statePublishInFlight)
|
|
169
|
+
return;
|
|
170
|
+
this.statePublishInFlight = true;
|
|
171
|
+
try {
|
|
172
|
+
while (this.statePublishPending && this.current && this.client?.connected && !this.stopped) {
|
|
173
|
+
this.statePublishPending = false;
|
|
174
|
+
await this.publish(this.client, this.topics.state, JSON.stringify(this.current), { qos: 1, retain: true });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
finally {
|
|
178
|
+
this.statePublishInFlight = false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
async publishLiveSample() {
|
|
182
|
+
const client = this.client;
|
|
183
|
+
const current = this.current;
|
|
184
|
+
if (!client?.connected || !current || this.backlogMessageCount <= 1 || current.sequence <= this.lastLivePublishedSequence || this.now() - this.lastLivePublishedAt < 1000)
|
|
185
|
+
return;
|
|
186
|
+
const batch = { v: 1, deviceId: this.credentials.deviceId, samples: [current] };
|
|
187
|
+
await this.publish(client, this.topics.telemetry, JSON.stringify(batch), { qos: 1 });
|
|
188
|
+
this.lastLivePublishedAt = this.now();
|
|
189
|
+
this.lastLivePublishedSequence = current.sequence;
|
|
190
|
+
}
|
|
191
|
+
async pump() {
|
|
192
|
+
const client = this.client;
|
|
193
|
+
if (!client?.connected || this.stopped || this.pumpRunning || this.inFlight.length >= 4)
|
|
194
|
+
return;
|
|
195
|
+
this.pumpRunning = true;
|
|
196
|
+
try {
|
|
197
|
+
const health = this.monitor.current();
|
|
198
|
+
const byteRate = health.mode === 'CONSTRAINED' ? this.profile.transport.constrainedBacklogBytesPerSecond : this.profile.transport.normalBacklogBytesPerSecond;
|
|
199
|
+
const budget = this.refillBacklogBudget(byteRate);
|
|
200
|
+
if (budget < 512)
|
|
201
|
+
return;
|
|
202
|
+
const samples = await this.outbox.pendingAfter(this.nextPublishAfter, this.profile.transport.batchSize, Math.min(budget, this.profile.transport.maxPayloadBytes - 1024));
|
|
203
|
+
if (!samples.length)
|
|
204
|
+
return;
|
|
205
|
+
const stats = await this.outbox.stats();
|
|
206
|
+
this.backlogMessageCount = stats.messageCount;
|
|
207
|
+
const batch = { v: 1, deviceId: this.credentials.deviceId,
|
|
208
|
+
droppedThrough: stats.droppedThrough > stats.acknowledgedSequence ? stats.droppedThrough : undefined, samples };
|
|
209
|
+
const payload = JSON.stringify(batch);
|
|
210
|
+
if (Buffer.byteLength(payload) > this.profile.transport.maxPayloadBytes)
|
|
211
|
+
throw new Error('Telemetry batch exceeds payload limit');
|
|
212
|
+
for (const sample of samples)
|
|
213
|
+
if (sample.evidence) {
|
|
214
|
+
await this.publish(client, this.topics.events, JSON.stringify({ v: 1, deviceId: sample.deviceId, sequence: sample.sequence, ...sample.evidence }), { qos: 1 });
|
|
215
|
+
}
|
|
216
|
+
await this.publish(client, this.topics.telemetry, payload, { qos: 1 });
|
|
217
|
+
this.backlogTokens -= Buffer.byteLength(payload);
|
|
218
|
+
const through = samples.at(-1)?.sequence ?? this.nextPublishAfter;
|
|
219
|
+
this.inFlight.push({ through, sentAt: this.now() });
|
|
220
|
+
this.nextPublishAfter = through;
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
this.transportFailure(error);
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
this.pumpRunning = false;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async onMessage(topic, payload) {
|
|
230
|
+
if (topic === this.topics.ack)
|
|
231
|
+
await this.handleAcknowledgement(payload);
|
|
232
|
+
else if (topic === this.topics.profile)
|
|
233
|
+
await this.handleProfile(payload);
|
|
234
|
+
}
|
|
235
|
+
async handleAcknowledgement(payload) {
|
|
236
|
+
if (payload.length > 4096)
|
|
237
|
+
return;
|
|
238
|
+
try {
|
|
239
|
+
const ack = JSON.parse(payload.toString('utf8'));
|
|
240
|
+
if (ack.v !== 1 || ack.deviceId !== this.credentials.deviceId || !Number.isSafeInteger(ack.ackSequence))
|
|
241
|
+
return;
|
|
242
|
+
const acknowledgedBatch = this.inFlight.find((batch) => ack.ackSequence >= batch.through);
|
|
243
|
+
if (acknowledgedBatch) {
|
|
244
|
+
this.acknowledgementLatencyMs = this.now() - acknowledgedBatch.sentAt;
|
|
245
|
+
this.monitor.acknowledgement(this.acknowledgementLatencyMs);
|
|
246
|
+
}
|
|
247
|
+
await this.outbox.acknowledge(ack.ackSequence);
|
|
248
|
+
this.backlogMessageCount = (await this.outbox.stats()).messageCount;
|
|
249
|
+
this.lastAcknowledgedAt = this.now();
|
|
250
|
+
this.inFlight = this.inFlight.filter((batch) => batch.through > ack.ackSequence);
|
|
251
|
+
this.syncMode();
|
|
252
|
+
this.options.debug?.(`Application acknowledged telemetry through sequence ${ack.ackSequence}`);
|
|
253
|
+
void this.pump();
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
this.monitor.failure('invalid_acknowledgement');
|
|
257
|
+
this.syncMode();
|
|
258
|
+
this.options.onState('degraded', 'Invalid application acknowledgement');
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
async handleProfile(payload) {
|
|
262
|
+
let acknowledgement;
|
|
263
|
+
try {
|
|
264
|
+
if (payload.length > 16_384)
|
|
265
|
+
throw new Error('profile_too_large');
|
|
266
|
+
const profile = (0, profile_1.parseTelemetryProfile)(JSON.parse(payload.toString('utf8')));
|
|
267
|
+
if (!profile)
|
|
268
|
+
throw new Error('profile_invalid');
|
|
269
|
+
if (profile.revision < this.profile.revision)
|
|
270
|
+
throw new Error('profile_revision_rollback');
|
|
271
|
+
await this.options.onProfile?.(profile);
|
|
272
|
+
this.profile = profile;
|
|
273
|
+
acknowledgement = { v: 1, profileId: profile.profileId, revision: profile.revision, status: 'applied', at: this.now() };
|
|
274
|
+
}
|
|
275
|
+
catch (error) {
|
|
276
|
+
const candidate = safeProfileIdentity(payload);
|
|
277
|
+
acknowledgement = { v: 1, profileId: candidate.profileId, revision: candidate.revision, status: 'rejected', at: this.now(), errorCode: sanitizeProfileError(error) };
|
|
278
|
+
}
|
|
279
|
+
if (this.client?.connected)
|
|
280
|
+
await this.publish(this.client, this.topics.profileAck, JSON.stringify(acknowledgement), { qos: 1, retain: true });
|
|
281
|
+
}
|
|
282
|
+
async checkAcknowledgementTimeout() {
|
|
283
|
+
const oldest = this.inFlight[0];
|
|
284
|
+
if (!oldest || this.now() - oldest.sentAt < 30_000)
|
|
285
|
+
return;
|
|
286
|
+
this.monitor.failure('acknowledgement_timeout');
|
|
287
|
+
this.syncMode();
|
|
288
|
+
const stats = await this.outbox.stats();
|
|
289
|
+
this.inFlight = [];
|
|
290
|
+
this.nextPublishAfter = stats.acknowledgedSequence;
|
|
291
|
+
void this.pump();
|
|
292
|
+
}
|
|
293
|
+
scheduleReconnect(authenticationFailure = false) {
|
|
294
|
+
if (this.reconnectTimer)
|
|
295
|
+
clearTimeout(this.reconnectTimer);
|
|
296
|
+
this.monitor.reconnect();
|
|
297
|
+
this.syncMode();
|
|
298
|
+
const base = authenticationFailure ? 5 * 60_000 : Math.min(60_000, 1000 * 2 ** Math.min(this.reconnectAttempt, 6));
|
|
299
|
+
const random = this.options.random?.() ?? Math.random();
|
|
300
|
+
const delay = Math.round(base * (0.75 + random * 0.5));
|
|
301
|
+
this.reconnectAttempt += 1;
|
|
302
|
+
this.reconnectTimer = setTimeout(() => { this.client?.end(true); this.connect(); }, delay);
|
|
303
|
+
}
|
|
304
|
+
syncMode() {
|
|
305
|
+
const health = this.monitor.current();
|
|
306
|
+
if (health.mode !== this.lastMode) {
|
|
307
|
+
this.lastMode = health.mode;
|
|
308
|
+
this.options.onMode?.(health.mode, health.reason);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
effectiveStatusMetrics() {
|
|
312
|
+
return this.statusMetrics ? { ...this.statusMetrics, ...this.transportMetrics(), profileId: this.profile.profileId, profileRevision: this.profile.revision } : undefined;
|
|
313
|
+
}
|
|
314
|
+
transportFailure(error) {
|
|
315
|
+
this.monitor.failure('publish_failure');
|
|
316
|
+
this.syncMode();
|
|
317
|
+
this.options.onState('degraded', sanitizeError(error instanceof Error ? error.message : String(error)));
|
|
318
|
+
}
|
|
319
|
+
async publish(client, topic, payload, options) {
|
|
320
|
+
await publish(client, topic, payload, options);
|
|
321
|
+
this.publishedBytes += Buffer.byteLength(payload);
|
|
322
|
+
}
|
|
323
|
+
now() { return this.options.now?.() ?? Date.now(); }
|
|
324
|
+
refillBacklogBudget(byteRate) {
|
|
325
|
+
const now = this.now();
|
|
326
|
+
const elapsed = this.backlogTokenAt === undefined ? 0 : Math.max(0, now - this.backlogTokenAt);
|
|
327
|
+
this.backlogTokenAt = now;
|
|
328
|
+
this.backlogTokens = Math.min(byteRate, this.backlogTokens + elapsed * byteRate / 1000);
|
|
329
|
+
return Math.max(0, Math.floor(this.backlogTokens));
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
exports.WakeLoggerTransport = WakeLoggerTransport;
|
|
333
|
+
function statusPayload(state, metrics) {
|
|
334
|
+
return { v: 1, state, at: Date.now(), ...(metrics ? {
|
|
335
|
+
pluginVersion: metrics.pluginVersion, queueMessageCount: metrics.queueMessageCount, queueDiskBytes: metrics.queueDiskBytes,
|
|
336
|
+
queueOldestCapturedAt: metrics.queueOldestCapturedAt, queueDroppedCount: metrics.queueDroppedCount,
|
|
337
|
+
acknowledgedSequence: metrics.acknowledgedSequence, currentSequence: metrics.currentSequence, trackingState: metrics.trackingState,
|
|
338
|
+
storageBackend: metrics.storageBackend, networkMode: metrics.networkMode, modeReason: metrics.modeReason,
|
|
339
|
+
profileId: metrics.profileId, profileRevision: metrics.profileRevision, lastAcknowledgedAt: metrics.lastAcknowledgedAt,
|
|
340
|
+
acknowledgementLatencyMs: metrics.acknowledgementLatencyMs, reconnectCount: metrics.reconnectCount, publishedBytes: metrics.publishedBytes
|
|
341
|
+
} : {}) };
|
|
342
|
+
}
|
|
343
|
+
function safeProfileIdentity(payload) {
|
|
344
|
+
try {
|
|
345
|
+
const value = JSON.parse(payload.toString('utf8'));
|
|
346
|
+
return { profileId: typeof value.profileId === 'string' && /^[A-Za-z0-9_-]{1,80}$/.test(value.profileId) ? value.profileId : 'unknown',
|
|
347
|
+
revision: Number.isSafeInteger(value.revision) && Number(value.revision) > 0 ? Number(value.revision) : 1 };
|
|
348
|
+
}
|
|
349
|
+
catch {
|
|
350
|
+
return { profileId: 'unknown', revision: 1 };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function sanitizeProfileError(error) {
|
|
354
|
+
const value = error instanceof Error ? error.message : String(error);
|
|
355
|
+
return ['profile_too_large', 'profile_invalid', 'profile_revision_rollback'].includes(value) ? value : 'profile_apply_failed';
|
|
356
|
+
}
|
|
357
|
+
function sanitizeError(value) { return value.replace(/(password|token|secret)=\S+/gi, '$1=[redacted]').slice(0, 500); }
|
|
358
|
+
function publish(client, topic, payload, options) {
|
|
359
|
+
return new Promise((resolve, reject) => client.publish(topic, payload, options, (error) => error ? reject(error) : resolve()));
|
|
360
|
+
}
|
|
361
|
+
function subscribe(client, topics) {
|
|
362
|
+
const subscriptions = Object.fromEntries(topics.map((topic) => [topic, { qos: 1 }]));
|
|
363
|
+
return new Promise((resolve, reject) => client.subscribe(subscriptions, (error) => error ? reject(error) : resolve()));
|
|
364
|
+
}
|
|
365
|
+
//# sourceMappingURL=mqtt-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mqtt-client.js","sourceRoot":"","sources":["../../src/transport/mqtt-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deviceTopics = deviceTopics;
|
|
4
|
+
function deviceTopics(deviceId) {
|
|
5
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(deviceId))
|
|
6
|
+
throw new Error('Invalid device identifier');
|
|
7
|
+
const base = `wakelogger/v1/devices/${deviceId}`;
|
|
8
|
+
return {
|
|
9
|
+
telemetry: `${base}/telemetry`, state: `${base}/state`, status: `${base}/status`,
|
|
10
|
+
events: `${base}/events`, ack: `${base}/ack`, profile: `${base}/profile`, profileAck: `${base}/profile-ack`
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=topics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"topics.js","sourceRoot":"","sources":["../../src/transport/topics.ts"],"names":[],"mappings":";;AAUA,oCAOC;AAPD,SAAgB,YAAY,CAAC,QAAgB;IAC3C,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,QAAQ,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAA;IAC1F,MAAM,IAAI,GAAG,yBAAyB,QAAQ,EAAE,CAAA;IAChD,OAAO;QACL,SAAS,EAAE,GAAG,IAAI,YAAY,EAAE,KAAK,EAAE,GAAG,IAAI,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,SAAS;QAChF,MAAM,EAAE,GAAG,IAAI,SAAS,EAAE,GAAG,EAAE,GAAG,IAAI,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,UAAU,EAAE,UAAU,EAAE,GAAG,IAAI,cAAc;KAC5G,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TelemetryDraft, TripEvidence } from '../telemetry/types';
|
|
2
|
+
export type TripState = 'STOPPED' | 'START_CANDIDATE' | 'MOVING' | 'STOP_CANDIDATE';
|
|
3
|
+
export interface TripSnapshot {
|
|
4
|
+
state: TripState;
|
|
5
|
+
candidateAt?: number;
|
|
6
|
+
candidatePosition?: {
|
|
7
|
+
lat: number;
|
|
8
|
+
lon: number;
|
|
9
|
+
};
|
|
10
|
+
stationaryPosition?: {
|
|
11
|
+
lat: number;
|
|
12
|
+
lon: number;
|
|
13
|
+
};
|
|
14
|
+
trackingSessionId?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface TripThresholds {
|
|
17
|
+
startSogKn: number;
|
|
18
|
+
startDistanceM: number;
|
|
19
|
+
startDwellMs: number;
|
|
20
|
+
stopSogKn: number;
|
|
21
|
+
stopRadiusM: number;
|
|
22
|
+
stopDwellMs: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class TripStateMachine {
|
|
25
|
+
private readonly thresholds;
|
|
26
|
+
private snapshot;
|
|
27
|
+
constructor(snapshot?: Partial<TripSnapshot>, thresholds?: TripThresholds);
|
|
28
|
+
process(sample: TelemetryDraft): TripEvidence | undefined;
|
|
29
|
+
currentState(): TripSnapshot;
|
|
30
|
+
trackingSessionId(): string | undefined;
|
|
31
|
+
}
|
|
32
|
+
export declare function distanceMetres(a: {
|
|
33
|
+
lat: number;
|
|
34
|
+
lon: number;
|
|
35
|
+
}, b: {
|
|
36
|
+
lat: number;
|
|
37
|
+
lon: number;
|
|
38
|
+
}): number;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TripStateMachine = void 0;
|
|
4
|
+
exports.distanceMetres = distanceMetres;
|
|
5
|
+
const node_crypto_1 = require("node:crypto");
|
|
6
|
+
const DEFAULT_THRESHOLDS = {
|
|
7
|
+
startSogKn: 1.5,
|
|
8
|
+
startDistanceM: 200,
|
|
9
|
+
startDwellMs: 2 * 60_000,
|
|
10
|
+
stopSogKn: 0.5,
|
|
11
|
+
stopRadiusM: 75,
|
|
12
|
+
stopDwellMs: 15 * 60_000
|
|
13
|
+
};
|
|
14
|
+
class TripStateMachine {
|
|
15
|
+
thresholds;
|
|
16
|
+
snapshot;
|
|
17
|
+
constructor(snapshot, thresholds = DEFAULT_THRESHOLDS) {
|
|
18
|
+
this.thresholds = thresholds;
|
|
19
|
+
this.snapshot = {
|
|
20
|
+
state: validState(snapshot?.state) ? snapshot.state : 'STOPPED',
|
|
21
|
+
candidateAt: finite(snapshot?.candidateAt) ? snapshot.candidateAt : undefined,
|
|
22
|
+
candidatePosition: validPosition(snapshot?.candidatePosition) ? snapshot.candidatePosition : undefined,
|
|
23
|
+
stationaryPosition: validPosition(snapshot?.stationaryPosition) ? snapshot.stationaryPosition : undefined,
|
|
24
|
+
trackingSessionId: typeof snapshot?.trackingSessionId === 'string' ? snapshot.trackingSessionId : undefined
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
process(sample) {
|
|
28
|
+
const position = { lat: sample.values.lat, lon: sample.values.lon };
|
|
29
|
+
const sog = sample.values.sog_kn ?? 0;
|
|
30
|
+
const at = sample.capturedAt;
|
|
31
|
+
switch (this.snapshot.state) {
|
|
32
|
+
case 'STOPPED': {
|
|
33
|
+
const origin = this.snapshot.stationaryPosition ?? position;
|
|
34
|
+
this.snapshot.stationaryPosition = origin;
|
|
35
|
+
const distance = distanceMetres(origin, position);
|
|
36
|
+
if (sog > this.thresholds.startSogKn || distance > this.thresholds.startDistanceM) {
|
|
37
|
+
this.snapshot = { state: 'START_CANDIDATE', candidateAt: at, candidatePosition: origin };
|
|
38
|
+
return evidence('trip_start_candidate', at, sog, distance, 0);
|
|
39
|
+
}
|
|
40
|
+
if (distance <= this.thresholds.stopRadiusM) {
|
|
41
|
+
this.snapshot.stationaryPosition = {
|
|
42
|
+
lat: origin.lat * 0.9 + position.lat * 0.1,
|
|
43
|
+
lon: origin.lon * 0.9 + position.lon * 0.1
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
case 'START_CANDIDATE': {
|
|
49
|
+
const origin = this.snapshot.candidatePosition ?? position;
|
|
50
|
+
const distance = distanceMetres(origin, position);
|
|
51
|
+
const duration = at - (this.snapshot.candidateAt ?? at);
|
|
52
|
+
if (sog <= this.thresholds.stopSogKn && distance < this.thresholds.stopRadiusM) {
|
|
53
|
+
this.snapshot = { state: 'STOPPED', stationaryPosition: position };
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
if (duration >= this.thresholds.startDwellMs) {
|
|
57
|
+
const trackingSessionId = (0, node_crypto_1.randomUUID)();
|
|
58
|
+
const effectiveAt = this.snapshot.candidateAt ?? at;
|
|
59
|
+
this.snapshot = { state: 'MOVING', trackingSessionId };
|
|
60
|
+
return { ...evidence('trip_started', effectiveAt, sog, distance, duration), trackingSessionId };
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
case 'MOVING':
|
|
65
|
+
if (sog < this.thresholds.stopSogKn) {
|
|
66
|
+
this.snapshot = {
|
|
67
|
+
state: 'STOP_CANDIDATE', candidateAt: at, candidatePosition: position,
|
|
68
|
+
trackingSessionId: this.snapshot.trackingSessionId
|
|
69
|
+
};
|
|
70
|
+
return { ...evidence('trip_stop_candidate', at, sog, 0, 0), trackingSessionId: this.snapshot.trackingSessionId };
|
|
71
|
+
}
|
|
72
|
+
return undefined;
|
|
73
|
+
case 'STOP_CANDIDATE': {
|
|
74
|
+
const origin = this.snapshot.candidatePosition ?? position;
|
|
75
|
+
const distance = distanceMetres(origin, position);
|
|
76
|
+
const duration = at - (this.snapshot.candidateAt ?? at);
|
|
77
|
+
if (sog >= this.thresholds.stopSogKn || distance > this.thresholds.stopRadiusM) {
|
|
78
|
+
this.snapshot = { state: 'MOVING', trackingSessionId: this.snapshot.trackingSessionId };
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
if (duration >= this.thresholds.stopDwellMs) {
|
|
82
|
+
const trackingSessionId = this.snapshot.trackingSessionId;
|
|
83
|
+
const effectiveAt = this.snapshot.candidateAt ?? at;
|
|
84
|
+
this.snapshot = { state: 'STOPPED', stationaryPosition: position };
|
|
85
|
+
return { ...evidence('trip_stopped', effectiveAt, sog, distance, duration), trackingSessionId };
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
currentState() { return structuredClone(this.snapshot); }
|
|
92
|
+
trackingSessionId() { return this.snapshot.trackingSessionId; }
|
|
93
|
+
}
|
|
94
|
+
exports.TripStateMachine = TripStateMachine;
|
|
95
|
+
function evidence(event, effectiveAt, sogKn, distance, durationMs) {
|
|
96
|
+
return {
|
|
97
|
+
event, effectiveAt,
|
|
98
|
+
reason: { sogKn, distanceMovedMetres: Math.round(distance), movementDurationSeconds: Math.round(durationMs / 1000) }
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function validState(value) {
|
|
102
|
+
return value === 'STOPPED' || value === 'START_CANDIDATE' || value === 'MOVING' || value === 'STOP_CANDIDATE';
|
|
103
|
+
}
|
|
104
|
+
function finite(value) { return typeof value === 'number' && Number.isFinite(value); }
|
|
105
|
+
function validPosition(value) {
|
|
106
|
+
const position = value;
|
|
107
|
+
return finite(position?.lat) && finite(position?.lon) && Math.abs(position.lat) <= 90 && Math.abs(position.lon) <= 180;
|
|
108
|
+
}
|
|
109
|
+
function distanceMetres(a, b) {
|
|
110
|
+
const radius = 6_371_000;
|
|
111
|
+
const dLat = (b.lat - a.lat) * Math.PI / 180;
|
|
112
|
+
const dLon = (b.lon - a.lon) * Math.PI / 180;
|
|
113
|
+
const lat1 = a.lat * Math.PI / 180;
|
|
114
|
+
const lat2 = b.lat * Math.PI / 180;
|
|
115
|
+
const h = Math.sin(dLat / 2) ** 2 + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2) ** 2;
|
|
116
|
+
return radius * 2 * Math.atan2(Math.sqrt(h), Math.sqrt(1 - h));
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=state-machine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state-machine.js","sourceRoot":"","sources":["../../src/trips/state-machine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Signal K deltas pass through a path allowlist and validation layer into a one-second normalised sample. Valid samples require position; all other fields are optional. The trip state machine adds advisory evidence, then the outbox atomically assigns `deviceId + sequence` and persists the record before the MQTT transport can observe it.
|
|
4
|
+
|
|
5
|
+
Each device identity has its own outbox and sequence space, so replacing a paired device preserves but never misattributes the retired device's pending records. `OutboxStore` is the only interface used by telemetry and transport. When Signal K exposes its emerging Database API, a plugin-scoped database is preferred as a bounded write-ahead delivery buffer. The portable fallback uses 4 MiB append-only segments. Each file record is a big-endian 32-bit payload length, UTF-8 JSON payload and SHA-256 checksum. Record appends are synced; metadata is atomic and synced at segment rotation, acknowledgement, retention and orderly shutdown. Recovery derives the next sequence from valid records and truncates only an incomplete or corrupt tail. Metadata retains the acknowledgement cursor and dropped-record range/count.
|
|
6
|
+
|
|
7
|
+
Backend choice is persisted per device and bound into the protected credential record after first initialization. A later startup refuses to create sequence state when that binding's marker is missing or inconsistent. A database-backed device will not silently fall back to files if the Database API later disappears because that could reuse sequences. An existing file queue is drained before a safe one-way selection of the database backend. Legacy files are retained rather than deleted. No native SQLite module is shipped.
|
|
8
|
+
|
|
9
|
+
MQTT uses version 5, TLS hostname/certificate verification and QoS 1. A retained state topic publishes the newest sample immediately; new live samples bypass historical replay, while a non-retained telemetry topic drains ordered backlog batches. Replay has at most four unacknowledged application batches and uses a one-second token budget from the active profile, so rapid acknowledgements cannot bypass the NORMAL or CONSTRAINED byte rate. Historical records leave the outbox only after an application acknowledgement published following Wake Logger's durable database commit. Duplicates are safe by the cloud uniqueness constraint on `device_id + sequence`.
|
|
10
|
+
|
|
11
|
+
Pairing is HTTPS and outbound only. Per-device MQTT credentials scope the installation to its own namespace. Signal K configuration stores the single-use pairing code and operational settings, while provisioned credentials live separately in the plugin data directory with mode `0600`. Temporary exchange failures use a bounded six-attempt retry; invalid/expired codes stop immediately. Each new credential also has a device-scoped HTTPS association-status token so a broker rejection can be identified as a confirmed cloud revocation.
|
|
12
|
+
|
|
13
|
+
Forgetting credentials is deliberately not an outbox deletion operation. The administrator action closes the active runtime, deletes only the credential file and retains a non-secret tombstone with the consumed-code fingerprint and retired device ID. Device-scoped file or Database API outboxes and their monotonic sequences remain untouched, preventing a new identity from mispublishing old records.
|
|
14
|
+
|
|
15
|
+
Wake Logger owns vessel identity mapping, authorization, entitlements, derived wind, current-state selection and final trip construction. The plugin never trusts a payload-supplied vessel ID and never implements cloud business rules.
|
|
16
|
+
|
|
17
|
+
The local trip state machine produces private Wake Logger protocol evidence only. It does not register a Signal K Track API provider, publish trip deltas into Signal K, or compete with Signal K's own track facilities.
|