@signalk/tracks-plugin 3.0.0-beta.3 → 3.0.0-beta.5
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/dist/index.d.ts +0 -2
- package/dist/index.html +1 -0
- package/dist/index.js +98 -20
- package/dist/index.js.map +1 -1
- package/dist/sourceWatch.d.ts +17 -28
- package/dist/tracks.js +7 -3
- package/package.json +3 -5
- package/public/app-icon.svg +42 -0
- package/public/index.html +1 -0
- package/public/tracks.js +7 -3
- /package/{app-icon.svg → dist/app-icon.svg} +0 -0
package/dist/sourceWatch.d.ts
CHANGED
|
@@ -1,35 +1,24 @@
|
|
|
1
1
|
import { Context } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Watches which `$source` each context's positions arrive from.
|
|
4
|
-
*
|
|
5
|
-
* `navigation.position` is a rapid update — a GPS may report at 10Hz — and a
|
|
6
|
-
* typical boat has several receivers: an internal GPS, an AIS transponder, a
|
|
7
|
-
* chart plotter echoing its own fix.
|
|
8
|
-
*
|
|
9
|
-
* Signal K resolves which one wins through source priority, and the bus this
|
|
10
|
-
* plugin listens on is the filtered one: the server applies `toPreferredDelta`
|
|
11
|
-
* before emitting `delta`, which is what feeds `getBus()`. So with a priority
|
|
12
|
-
* rule in place, only the winner arrives and there is nothing to warn about.
|
|
13
|
-
*
|
|
14
|
-
* The gap is when no rule *matches* the path. Then the engine passes every
|
|
15
|
-
* source through, the fixes from receivers metres apart interleave, and the
|
|
16
|
-
* track zigzags between them rather than following the boat. The fix is to
|
|
17
|
-
* configure source priority for `navigation.position`; this watcher exists to
|
|
18
|
-
* say so, because the symptom (a track that looks noisy) does not obviously
|
|
19
|
-
* point at the cause.
|
|
20
|
-
*
|
|
21
|
-
* Detection is therefore empirical rather than reading the priority config:
|
|
22
|
-
* seeing several sources here *is* the evidence that no rule is matching,
|
|
23
|
-
* which a config that merely exists would not tell us.
|
|
24
|
-
*/
|
|
25
2
|
export declare class SourceWatch {
|
|
3
|
+
/** Last time each source produced a position, per context. */
|
|
26
4
|
private readonly seen;
|
|
5
|
+
/** Sources found competing, per context, with when each was last seen so. */
|
|
6
|
+
private readonly competing;
|
|
7
|
+
/** The current alternation per context: who is in it, and how long it is. */
|
|
8
|
+
private readonly lastSeen;
|
|
27
9
|
/** Record that `context` produced a position from `$source`. */
|
|
28
|
-
add(context: Context, source: string | undefined): void;
|
|
29
|
-
/**
|
|
10
|
+
add(context: Context, source: string | undefined, now?: number): void;
|
|
11
|
+
/** Every source seen for a context, in the order they first appeared. */
|
|
30
12
|
sourcesFor(context: Context): string[];
|
|
31
|
-
/**
|
|
32
|
-
|
|
13
|
+
/**
|
|
14
|
+
* The sources found live at the same time, which is what a warning names.
|
|
15
|
+
*
|
|
16
|
+
* Expired entries are dropped rather than merely hidden, so a conflict that
|
|
17
|
+
* has stopped recurring leaves nothing behind.
|
|
18
|
+
*/
|
|
19
|
+
competingFor(context: Context, now?: number): string[];
|
|
20
|
+
/** Contexts that have received positions from two sources at once. */
|
|
21
|
+
conflicted(now?: number): Context[];
|
|
33
22
|
/**
|
|
34
23
|
* A status line naming the problem, or undefined when nothing is wrong.
|
|
35
24
|
*
|
|
@@ -38,7 +27,7 @@ export declare class SourceWatch {
|
|
|
38
27
|
* vessels are usually AIS, where multiple receivers are expected and less
|
|
39
28
|
* worth nagging about — they are counted, not named.
|
|
40
29
|
*/
|
|
41
|
-
warning(selfContext?: Context): string | undefined;
|
|
30
|
+
warning(selfContext?: Context, now?: number): string | undefined;
|
|
42
31
|
/** Forget everything seen; used when the plugin restarts. */
|
|
43
32
|
clear(): void;
|
|
44
33
|
}
|
package/dist/tracks.js
CHANGED
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
* days covers the passage someone is looking for while keeping the query
|
|
20
20
|
* something the store can serve.
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
// The two APIs spell a duration differently: v2 takes ISO 8601, v1 takes a
|
|
23
|
+
// number with a unit suffix. One constant for both sent `P30D` to the v1
|
|
24
|
+
// export route, which rejected it and made every GPX download a 400.
|
|
25
|
+
const WINDOW_V2 = 'P30D'
|
|
26
|
+
const WINDOW_V1 = '30d'
|
|
23
27
|
|
|
24
28
|
/**
|
|
25
29
|
* Ceiling on the list request.
|
|
@@ -31,7 +35,7 @@ const WINDOW = 'P30D'
|
|
|
31
35
|
const REQUEST_TIMEOUT_MS = 30_000
|
|
32
36
|
|
|
33
37
|
/** Relative, because the server mounts this under /@signalk/tracks-plugin. */
|
|
34
|
-
const TRACKS_URL = `../../signalk/v2/api/tracks?geometry=false&duration=${
|
|
38
|
+
const TRACKS_URL = `../../signalk/v2/api/tracks?geometry=false&duration=${WINDOW_V2}`
|
|
35
39
|
|
|
36
40
|
const status = document.getElementById('status')
|
|
37
41
|
const table = document.getElementById('tracks')
|
|
@@ -92,7 +96,7 @@ function exportCell({ context, isSelf }) {
|
|
|
92
96
|
// `self` resolves server-side; using it keeps the URL short and avoids
|
|
93
97
|
// guessing how the own vessel's context should be spelled.
|
|
94
98
|
const vessel = isSelf ? 'self/track.gpx' : `vessels/${encodeURIComponent(context)}/track.gpx`
|
|
95
|
-
link.href = `../../signalk/v1/api/${vessel}?duration=${
|
|
99
|
+
link.href = `../../signalk/v1/api/${vessel}?duration=${WINDOW_V1}`
|
|
96
100
|
link.textContent = 'GPX'
|
|
97
101
|
link.rel = 'nofollow'
|
|
98
102
|
td.append(link)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@signalk/tracks-plugin",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.5",
|
|
4
4
|
"description": "Plugin to keep track of tracks of vessels",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -15,17 +15,15 @@
|
|
|
15
15
|
"//files": "app-icon.svg ships alongside dist because `files` is an allowlist: the app store probes the declared signalk.appIcon path inside the published tarball, so an icon left out here 404s and the plugin falls back to a placeholder.",
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
|
-
"public"
|
|
19
|
-
"app-icon.svg"
|
|
18
|
+
"public"
|
|
20
19
|
],
|
|
21
20
|
"signalk": {
|
|
22
21
|
"displayName": "Tracks",
|
|
23
|
-
"appIcon": "app-icon.svg"
|
|
22
|
+
"appIcon": "./app-icon.svg"
|
|
24
23
|
},
|
|
25
24
|
"keywords": [
|
|
26
25
|
"signalk-node-server-plugin",
|
|
27
26
|
"signalk-webapp",
|
|
28
|
-
"signalk-embeddable-webapp",
|
|
29
27
|
"signalk-category-utility"
|
|
30
28
|
],
|
|
31
29
|
"author": "Teppo Kurki <teppo.kurki@iki.fi>",
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="Tracks">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="sea" x1="0" y1="0" x2="0" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#1b6ea8"/>
|
|
5
|
+
<stop offset="1" stop-color="#0b3a5e"/>
|
|
6
|
+
</linearGradient>
|
|
7
|
+
<clipPath id="card">
|
|
8
|
+
<rect x="0" y="0" width="128" height="128" rx="22"/>
|
|
9
|
+
</clipPath>
|
|
10
|
+
</defs>
|
|
11
|
+
|
|
12
|
+
<g clip-path="url(#card)">
|
|
13
|
+
<rect width="128" height="128" fill="url(#sea)"/>
|
|
14
|
+
|
|
15
|
+
<!-- Chart grid, as on the sibling plugins' icons. -->
|
|
16
|
+
<g stroke="#bee3f4" stroke-opacity="0.28" stroke-width="1" fill="none">
|
|
17
|
+
<path d="M0 32 H128 M0 64 H128 M0 96 H128"/>
|
|
18
|
+
<path d="M32 0 V128 M64 0 V128 M96 0 V128"/>
|
|
19
|
+
</g>
|
|
20
|
+
|
|
21
|
+
<!-- The track itself: the recorded path, drawn through its own points. -->
|
|
22
|
+
<path d="M24 102 C 40 94, 42 72, 58 64 S 84 58, 94 44"
|
|
23
|
+
fill="none" stroke="#0b3a5e" stroke-opacity="0.45"
|
|
24
|
+
stroke-width="9" stroke-linecap="round"/>
|
|
25
|
+
<path d="M24 102 C 40 94, 42 72, 58 64 S 84 58, 94 44"
|
|
26
|
+
fill="none" stroke="#ffd76a" stroke-width="5"
|
|
27
|
+
stroke-linecap="round" stroke-linejoin="round"/>
|
|
28
|
+
|
|
29
|
+
<!-- Position fixes. The track is a series of recorded points, not a
|
|
30
|
+
drawn line, and the icon should read that way at 32px too. -->
|
|
31
|
+
<g fill="#fffaf0" stroke="#0b3a5e" stroke-opacity="0.5" stroke-width="1.5">
|
|
32
|
+
<circle cx="24" cy="102" r="4.5"/>
|
|
33
|
+
<circle cx="49" cy="74" r="4"/>
|
|
34
|
+
<circle cx="72" cy="61" r="4"/>
|
|
35
|
+
</g>
|
|
36
|
+
|
|
37
|
+
<!-- The vessel, at the newest fix, heading along the track. -->
|
|
38
|
+
<path d="M94 34 L102 52 L94 47 L86 52 Z" fill="#fffaf0"
|
|
39
|
+
stroke="#0b3a5e" stroke-opacity="0.5" stroke-width="1.5"
|
|
40
|
+
stroke-linejoin="round"/>
|
|
41
|
+
</g>
|
|
42
|
+
</svg>
|
package/public/index.html
CHANGED
package/public/tracks.js
CHANGED
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
* days covers the passage someone is looking for while keeping the query
|
|
20
20
|
* something the store can serve.
|
|
21
21
|
*/
|
|
22
|
-
|
|
22
|
+
// The two APIs spell a duration differently: v2 takes ISO 8601, v1 takes a
|
|
23
|
+
// number with a unit suffix. One constant for both sent `P30D` to the v1
|
|
24
|
+
// export route, which rejected it and made every GPX download a 400.
|
|
25
|
+
const WINDOW_V2 = 'P30D'
|
|
26
|
+
const WINDOW_V1 = '30d'
|
|
23
27
|
|
|
24
28
|
/**
|
|
25
29
|
* Ceiling on the list request.
|
|
@@ -31,7 +35,7 @@ const WINDOW = 'P30D'
|
|
|
31
35
|
const REQUEST_TIMEOUT_MS = 30_000
|
|
32
36
|
|
|
33
37
|
/** Relative, because the server mounts this under /@signalk/tracks-plugin. */
|
|
34
|
-
const TRACKS_URL = `../../signalk/v2/api/tracks?geometry=false&duration=${
|
|
38
|
+
const TRACKS_URL = `../../signalk/v2/api/tracks?geometry=false&duration=${WINDOW_V2}`
|
|
35
39
|
|
|
36
40
|
const status = document.getElementById('status')
|
|
37
41
|
const table = document.getElementById('tracks')
|
|
@@ -92,7 +96,7 @@ function exportCell({ context, isSelf }) {
|
|
|
92
96
|
// `self` resolves server-side; using it keeps the URL short and avoids
|
|
93
97
|
// guessing how the own vessel's context should be spelled.
|
|
94
98
|
const vessel = isSelf ? 'self/track.gpx' : `vessels/${encodeURIComponent(context)}/track.gpx`
|
|
95
|
-
link.href = `../../signalk/v1/api/${vessel}?duration=${
|
|
99
|
+
link.href = `../../signalk/v1/api/${vessel}?duration=${WINDOW_V1}`
|
|
96
100
|
link.textContent = 'GPX'
|
|
97
101
|
link.rel = 'nofollow'
|
|
98
102
|
td.append(link)
|
|
File without changes
|