signalk-siparu 0.1.21 → 0.1.22
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/package.json +1 -1
- package/plugin/dist/contract.d.ts +30 -0
- package/plugin/dist/index.js +5 -0
- package/plugin/dist/live.d.ts +19 -4
- package/plugin/dist/live.js +49 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "signalk-siparu",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"description": "Kept aboard, proven ashore. An impartial, timestamped record of every voyage: position, wind, depth and logbook, written on the boat and readable from anywhere. A read-only Signal K plugin with a built-in dashboard.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"signalk-node-server-plugin",
|
|
@@ -189,6 +189,32 @@ export type SnapshotsResponse = {
|
|
|
189
189
|
message: string;
|
|
190
190
|
};
|
|
191
191
|
};
|
|
192
|
+
/**
|
|
193
|
+
* The voyages RPC, a third sibling on the same live socket. Where history asks for one gauge's
|
|
194
|
+
* series and snapshots for whole rows over a window, this asks for the boat's recent voyages -
|
|
195
|
+
* the list the local /voyages REST serves. It carries no query, only how many of the newest to
|
|
196
|
+
* return; like its siblings it is a read of the boat's own store and reaches nothing near
|
|
197
|
+
* Signal K. The boat clamps the count, so a request cannot ask for more than she will give.
|
|
198
|
+
*/
|
|
199
|
+
export interface VoyagesRequest {
|
|
200
|
+
type: 'voyages';
|
|
201
|
+
id: string;
|
|
202
|
+
/** How many of the newest voyages to return. Clamped boat-side to the REST bounds. */
|
|
203
|
+
limit: number;
|
|
204
|
+
}
|
|
205
|
+
/** The boat's answer to one VoyagesRequest. The voyages or a reason, never both. */
|
|
206
|
+
export type VoyagesResponse = {
|
|
207
|
+
type: 'voyages';
|
|
208
|
+
id: string;
|
|
209
|
+
result: VoyageListResult;
|
|
210
|
+
} | {
|
|
211
|
+
type: 'voyages';
|
|
212
|
+
id: string;
|
|
213
|
+
error: {
|
|
214
|
+
code: string;
|
|
215
|
+
message: string;
|
|
216
|
+
};
|
|
217
|
+
};
|
|
192
218
|
export interface LiveResult extends Snapshot {
|
|
193
219
|
/** Seconds since the newest delta touched any subscribed path; null before first delta. */
|
|
194
220
|
data_age_s: number | null;
|
|
@@ -295,6 +321,10 @@ export interface Voyage {
|
|
|
295
321
|
end_port: string | null;
|
|
296
322
|
status: 'open' | 'closed';
|
|
297
323
|
}
|
|
324
|
+
/** The boat's answer to one VoyagesRequest: her recent voyages, newest first. */
|
|
325
|
+
export interface VoyageListResult {
|
|
326
|
+
voyages: Voyage[];
|
|
327
|
+
}
|
|
298
328
|
/** One aggregation window on the voyage stats card. */
|
|
299
329
|
export interface VoyageWindowStats {
|
|
300
330
|
distance_nm: number;
|
package/plugin/dist/index.js
CHANGED
|
@@ -349,6 +349,11 @@ module.exports = (app) => {
|
|
|
349
349
|
// Her whole recorded rows over a window, the logbook read - the same store the local
|
|
350
350
|
// /snapshots serves, reached here and nowhere near Signal K.
|
|
351
351
|
onSnapshotsQuery: (q) => qs.snapshots(q, Date.now()),
|
|
352
|
+
// Her recent voyages, the list the local /voyages serves. The count is clamped to the
|
|
353
|
+
// same 1..500 bounds the REST route enforces, since vl.list does not clamp its own.
|
|
354
|
+
onVoyagesQuery: async (limit) => ({
|
|
355
|
+
voyages: vl.list(Math.min(Math.max(1, limit || 50), 500))
|
|
356
|
+
}),
|
|
352
357
|
debug: (msg) => app.debug(msg)
|
|
353
358
|
});
|
|
354
359
|
liveUplink = ws;
|
package/plugin/dist/live.d.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* fleet, and every owner would have to walk down to their boat to fix a bug that was ours.
|
|
17
17
|
*/
|
|
18
18
|
import type { RemoteLink } from './remotelink';
|
|
19
|
-
import type { PathSeriesResult, SnapshotsQuery, SnapshotsResult } from './contract';
|
|
19
|
+
import type { PathSeriesResult, SnapshotsQuery, SnapshotsResult, VoyageListResult } from './contract';
|
|
20
20
|
/**
|
|
21
21
|
* How often a frame goes up while the socket is open, when she is under way.
|
|
22
22
|
*
|
|
@@ -99,6 +99,13 @@ export interface LiveDeps {
|
|
|
99
99
|
* so an old relay or a boat wired without it simply never grows the ear.
|
|
100
100
|
*/
|
|
101
101
|
onSnapshotsQuery?: (query: SnapshotsQuery) => Promise<SnapshotsResult>;
|
|
102
|
+
/**
|
|
103
|
+
* Answers a shore voyages request - her recent voyages, the list the local REST /voyages
|
|
104
|
+
* serves - from the same store. A third sibling of onHistoryQuery: a read, never a command,
|
|
105
|
+
* and it never reaches Signal K. Absent leaves the socket deaf to voyages requests, so an old
|
|
106
|
+
* relay or a boat wired without it simply never grows the ear.
|
|
107
|
+
*/
|
|
108
|
+
onVoyagesQuery?: (limit: number) => Promise<VoyageListResult>;
|
|
102
109
|
debug: (msg: string) => void;
|
|
103
110
|
/** Injected in tests. In production this is the `ws` adapter at the bottom of the file. */
|
|
104
111
|
connect?: (url: string, token: string) => LiveSocket;
|
|
@@ -186,9 +193,17 @@ export declare class LiveUplink {
|
|
|
186
193
|
*/
|
|
187
194
|
private handleSnapshots;
|
|
188
195
|
/**
|
|
189
|
-
*
|
|
190
|
-
*
|
|
191
|
-
*
|
|
196
|
+
* A voyages request from the shore, answered from the boat's own store - a third sibling of
|
|
197
|
+
* handleHistory, and just as narrow. Parse, act only if it is a voyages request, and read the
|
|
198
|
+
* store, never Signal K. The boat clamps the count before it reads, so a request cannot ask
|
|
199
|
+
* her for more than she will give.
|
|
200
|
+
*/
|
|
201
|
+
private handleVoyages;
|
|
202
|
+
/**
|
|
203
|
+
* Send a history, snapshots or voyages answer, but only if it still belongs to the socket that
|
|
204
|
+
* asked. A query reads the disk while the line may drop and redial underneath it; the
|
|
205
|
+
* generation guard is what keeps a slow answer from landing on a fresh connection that never
|
|
206
|
+
* asked.
|
|
192
207
|
*/
|
|
193
208
|
private reply;
|
|
194
209
|
private keepalive;
|
package/plugin/dist/live.js
CHANGED
|
@@ -202,11 +202,13 @@ class LiveUplink {
|
|
|
202
202
|
return;
|
|
203
203
|
}
|
|
204
204
|
// Beyond a pong, the shore may ask the boat to send back her own recorded history: one
|
|
205
|
-
// gauge's series (handleHistory)
|
|
206
|
-
// command; each drops in silence anything that is not
|
|
207
|
-
// is
|
|
205
|
+
// gauge's series (handleHistory), whole snapshot rows (handleSnapshots) or her recent
|
|
206
|
+
// voyages (handleVoyages). None is a command; each drops in silence anything that is not
|
|
207
|
+
// its own request, and anything that is none is not acted on at all, because the shore
|
|
208
|
+
// may not steer a boat.
|
|
208
209
|
this.handleHistory(gen, data);
|
|
209
210
|
this.handleSnapshots(gen, data);
|
|
211
|
+
this.handleVoyages(gen, data);
|
|
210
212
|
});
|
|
211
213
|
sock.onClose((code) => {
|
|
212
214
|
if (gen !== this.gen)
|
|
@@ -353,9 +355,39 @@ class LiveUplink {
|
|
|
353
355
|
});
|
|
354
356
|
}
|
|
355
357
|
/**
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
358
|
+
* A voyages request from the shore, answered from the boat's own store - a third sibling of
|
|
359
|
+
* handleHistory, and just as narrow. Parse, act only if it is a voyages request, and read the
|
|
360
|
+
* store, never Signal K. The boat clamps the count before it reads, so a request cannot ask
|
|
361
|
+
* her for more than she will give.
|
|
362
|
+
*/
|
|
363
|
+
handleVoyages(gen, data) {
|
|
364
|
+
const handler = this.deps.onVoyagesQuery;
|
|
365
|
+
if (!handler)
|
|
366
|
+
return;
|
|
367
|
+
let msg;
|
|
368
|
+
try {
|
|
369
|
+
msg = JSON.parse(data);
|
|
370
|
+
}
|
|
371
|
+
catch {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
if (!isVoyagesRequest(msg))
|
|
375
|
+
return;
|
|
376
|
+
const { id, limit } = msg;
|
|
377
|
+
handler(limit).then((result) => this.reply(gen, { type: 'voyages', id, result }), (err) => {
|
|
378
|
+
this.deps.debug(`voyages query failed: ${String(err)}`);
|
|
379
|
+
this.reply(gen, {
|
|
380
|
+
type: 'voyages',
|
|
381
|
+
id,
|
|
382
|
+
error: { code: 'VOYAGES_FAILED', message: 'voyages query failed' }
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Send a history, snapshots or voyages answer, but only if it still belongs to the socket that
|
|
388
|
+
* asked. A query reads the disk while the line may drop and redial underneath it; the
|
|
389
|
+
* generation guard is what keeps a slow answer from landing on a fresh connection that never
|
|
390
|
+
* asked.
|
|
359
391
|
*/
|
|
360
392
|
reply(gen, msg) {
|
|
361
393
|
if (gen !== this.gen || !this.sock)
|
|
@@ -493,6 +525,17 @@ function isSnapshotsRequest(m) {
|
|
|
493
525
|
typeof o.query === 'object' &&
|
|
494
526
|
o.query !== null);
|
|
495
527
|
}
|
|
528
|
+
/**
|
|
529
|
+
* A voyages request, told apart the same way: the type tag is the gate. It carries no query,
|
|
530
|
+
* only a count - so the tag, the id and a numeric limit are checked. The limit's bounds are the
|
|
531
|
+
* boat's to enforce (she clamps it before reading), so they are not re-checked here.
|
|
532
|
+
*/
|
|
533
|
+
function isVoyagesRequest(m) {
|
|
534
|
+
if (typeof m !== 'object' || m === null)
|
|
535
|
+
return false;
|
|
536
|
+
const o = m;
|
|
537
|
+
return o.type === 'voyages' && typeof o.id === 'string' && typeof o.limit === 'number';
|
|
538
|
+
}
|
|
496
539
|
/**
|
|
497
540
|
* The real socket.
|
|
498
541
|
*
|