@aurora-blue/signalk-connector 0.2.0-dev.20
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/LICENSE +21 -0
- package/README.md +122 -0
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/VERSION +1 -0
- package/dist/src/backoff.d.ts +1 -0
- package/dist/src/backoff.js +9 -0
- package/dist/src/config.d.ts +192 -0
- package/dist/src/config.js +317 -0
- package/dist/src/connector.d.ts +69 -0
- package/dist/src/connector.js +487 -0
- package/dist/src/delta.d.ts +4 -0
- package/dist/src/delta.js +137 -0
- package/dist/src/geometry.d.ts +3 -0
- package/dist/src/geometry.js +25 -0
- package/dist/src/http-client.d.ts +22 -0
- package/dist/src/http-client.js +158 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +93 -0
- package/dist/src/safety/anchor-alarm.d.ts +33 -0
- package/dist/src/safety/anchor-alarm.js +79 -0
- package/dist/src/state-store.d.ts +45 -0
- package/dist/src/state-store.js +491 -0
- package/dist/src/storage-budget.d.ts +35 -0
- package/dist/src/storage-budget.js +88 -0
- package/dist/src/telemetry-catalog.d.ts +19 -0
- package/dist/src/telemetry-catalog.js +93 -0
- package/dist/src/telemetry-reducer.d.ts +22 -0
- package/dist/src/telemetry-reducer.js +150 -0
- package/dist/src/types.d.ts +115 -0
- package/dist/src/types.js +5 -0
- package/dist/src/version.d.ts +1 -0
- package/dist/src/version.js +4 -0
- package/package.json +60 -0
- package/public/aurora-blue-aboard-mark.svg +19 -0
- package/public/aurora-blue-archipelago-dark.svg +32 -0
- package/public/aurora-blue-archipelago-light.svg +81 -0
- package/public/ci-brand.css +77 -0
- package/public/ci-fonts.css +28 -0
- package/public/ci-tokens.css +139 -0
- package/public/favicon.svg +19 -0
- package/public/fonts/LICENSE +96 -0
- package/public/fonts/ubuntu-latin-300-normal.woff2 +0 -0
- package/public/fonts/ubuntu-latin-400-normal.woff2 +0 -0
- package/public/fonts/ubuntu-latin-500-normal.woff2 +0 -0
- package/public/fonts/ubuntu-latin-700-normal.woff2 +0 -0
- package/public/index.html +177 -0
- package/public/locales/de.json +82 -0
- package/public/locales/en.json +82 -0
- package/public/locales/es.json +82 -0
- package/public/locales/fr.json +82 -0
- package/public/status.css +262 -0
- package/public/status.js +237 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.telemetryDefinition = exports.isAisContext = exports.AIS_SUBSCRIPTION_PATHS = exports.SELF_SUBSCRIPTION_PATHS = exports.AIS_TELEMETRY = exports.SELF_TELEMETRY = void 0;
|
|
4
|
+
const degrees = (value) => (value * Math.PI) / 180;
|
|
5
|
+
const self = (path, valueKind, changeKind, minimumIntervalMs, maximumHeartbeatMs, changeThreshold) => ({
|
|
6
|
+
scope: "self",
|
|
7
|
+
path,
|
|
8
|
+
valueKind,
|
|
9
|
+
changeKind,
|
|
10
|
+
minimumIntervalMs,
|
|
11
|
+
maximumHeartbeatMs,
|
|
12
|
+
changeThreshold,
|
|
13
|
+
});
|
|
14
|
+
const ais = (path, valueKind, changeKind, minimumIntervalMs, maximumHeartbeatMs, changeThreshold) => ({
|
|
15
|
+
scope: "ais",
|
|
16
|
+
path,
|
|
17
|
+
valueKind,
|
|
18
|
+
changeKind,
|
|
19
|
+
minimumIntervalMs,
|
|
20
|
+
maximumHeartbeatMs,
|
|
21
|
+
changeThreshold,
|
|
22
|
+
});
|
|
23
|
+
exports.SELF_TELEMETRY = [
|
|
24
|
+
self("navigation.position", "position", "position", 2_000, 60_000, 5),
|
|
25
|
+
self("navigation.courseOverGroundTrue", "number", "angle", 2_000, 60_000, degrees(3)),
|
|
26
|
+
self("navigation.courseOverGroundMagnetic", "number", "angle", 2_000, 60_000, degrees(3)),
|
|
27
|
+
self("navigation.headingTrue", "number", "angle", 2_000, 60_000, degrees(3)),
|
|
28
|
+
self("navigation.headingMagnetic", "number", "angle", 2_000, 60_000, degrees(3)),
|
|
29
|
+
self("navigation.magneticVariation", "number", "angle", 30_000, 600_000, degrees(1)),
|
|
30
|
+
self("navigation.speedOverGround", "number", "scalar", 2_000, 60_000, 0.1),
|
|
31
|
+
self("navigation.speedThroughWater", "number", "scalar", 2_000, 60_000, 0.1),
|
|
32
|
+
self("navigation.rateOfTurn", "number", "scalar", 2_000, 60_000, 0.01),
|
|
33
|
+
self("navigation.log", "number", "scalar", 30_000, 600_000, 10),
|
|
34
|
+
self("navigation.trip.log", "number", "scalar", 30_000, 600_000, 10),
|
|
35
|
+
self("environment.wind.angleApparent", "number", "angle", 5_000, 60_000, degrees(5)),
|
|
36
|
+
self("environment.wind.speedApparent", "number", "scalar", 5_000, 60_000, 0.3),
|
|
37
|
+
self("environment.wind.angleTrueWater", "number", "angle", 5_000, 60_000, degrees(5)),
|
|
38
|
+
self("environment.wind.directionTrue", "number", "angle", 5_000, 60_000, degrees(5)),
|
|
39
|
+
self("environment.wind.speedTrue", "number", "scalar", 5_000, 60_000, 0.3),
|
|
40
|
+
self("environment.depth.belowKeel", "number", "scalar", 2_000, 60_000, 0.2),
|
|
41
|
+
self("environment.depth.belowTransducer", "number", "scalar", 2_000, 60_000, 0.2),
|
|
42
|
+
self("environment.depth.belowSurface", "number", "scalar", 2_000, 60_000, 0.2),
|
|
43
|
+
self("environment.water.temperature", "number", "scalar", 30_000, 600_000, 0.2),
|
|
44
|
+
self("environment.outside.temperature", "number", "scalar", 30_000, 600_000, 0.5),
|
|
45
|
+
self("environment.outside.pressure", "number", "scalar", 30_000, 600_000, 50),
|
|
46
|
+
self("environment.outside.relativeHumidity", "number", "scalar", 30_000, 600_000, 0.02),
|
|
47
|
+
self("electrical.batteries.*.voltage", "number", "scalar", 10_000, 300_000, 0.1),
|
|
48
|
+
self("electrical.batteries.*.current", "number", "scalar", 5_000, 120_000, 1),
|
|
49
|
+
self("electrical.batteries.*.temperature", "number", "scalar", 30_000, 600_000, 0.5),
|
|
50
|
+
self("electrical.batteries.*.capacity.stateOfCharge", "number", "scalar", 30_000, 600_000, 0.01),
|
|
51
|
+
self("electrical.batteries.*.capacity.timeRemaining", "number", "scalar", 300_000, 1_800_000, 60),
|
|
52
|
+
self("propulsion.*.revolutions", "number", "scalar", 5_000, 60_000, 5),
|
|
53
|
+
self("propulsion.*.temperature", "number", "scalar", 30_000, 300_000, 1),
|
|
54
|
+
self("propulsion.*.coolantTemperature", "number", "scalar", 30_000, 300_000, 1),
|
|
55
|
+
self("propulsion.*.oilPressure", "number", "scalar", 30_000, 300_000, 5_000),
|
|
56
|
+
self("propulsion.*.alternatorVoltage", "number", "scalar", 10_000, 300_000, 0.2),
|
|
57
|
+
self("propulsion.*.engineLoad", "number", "scalar", 10_000, 120_000, 0.05),
|
|
58
|
+
self("propulsion.*.runTime", "number", "scalar", 300_000, 1_800_000, 60),
|
|
59
|
+
self("propulsion.*.fuel.rate", "number", "scalar", 10_000, 300_000, 0.000001),
|
|
60
|
+
self("tanks.*.*.currentLevel", "number", "scalar", 30_000, 600_000, 0.01),
|
|
61
|
+
self("tanks.*.*.currentVolume", "number", "scalar", 30_000, 600_000, 0.005),
|
|
62
|
+
];
|
|
63
|
+
exports.AIS_TELEMETRY = [
|
|
64
|
+
ais("navigation.position", "position", "position", 10_000, 120_000, 50),
|
|
65
|
+
ais("navigation.courseOverGroundTrue", "number", "angle", 10_000, 120_000, degrees(10)),
|
|
66
|
+
ais("navigation.speedOverGround", "number", "scalar", 10_000, 120_000, 0.5),
|
|
67
|
+
ais("navigation.headingTrue", "number", "angle", 10_000, 120_000, degrees(10)),
|
|
68
|
+
ais("navigation.rateOfTurn", "number", "scalar", 10_000, 120_000, 0.02),
|
|
69
|
+
ais("navigation.state", "string", "identity", 60_000, 21_600_000, 0),
|
|
70
|
+
ais("name", "string", "identity", 60_000, 21_600_000, 0),
|
|
71
|
+
ais("mmsi", "string", "identity", 60_000, 21_600_000, 0),
|
|
72
|
+
ais("communication.callsignVhf", "string", "identity", 60_000, 21_600_000, 0),
|
|
73
|
+
ais("design.aisShipType", "aisShipType", "identity", 60_000, 21_600_000, 0),
|
|
74
|
+
];
|
|
75
|
+
exports.SELF_SUBSCRIPTION_PATHS = exports.SELF_TELEMETRY.map((definition) => definition.path);
|
|
76
|
+
exports.AIS_SUBSCRIPTION_PATHS = exports.AIS_TELEMETRY.map((definition) => definition.path);
|
|
77
|
+
const matchesPath = (pattern, path) => {
|
|
78
|
+
const patternParts = pattern.split(".");
|
|
79
|
+
const pathParts = path.split(".");
|
|
80
|
+
return (patternParts.length === pathParts.length &&
|
|
81
|
+
patternParts.every((part, index) => part === "*" || part === pathParts[index]));
|
|
82
|
+
};
|
|
83
|
+
const isAisContext = (context) => /^vessels\.(?:urn:mrn:imo:mmsi:)?[0-9]{9}$/.test(context);
|
|
84
|
+
exports.isAisContext = isAisContext;
|
|
85
|
+
const telemetryDefinition = (context, path) => {
|
|
86
|
+
const definitions = context === "vessels.self"
|
|
87
|
+
? exports.SELF_TELEMETRY
|
|
88
|
+
: (0, exports.isAisContext)(context)
|
|
89
|
+
? exports.AIS_TELEMETRY
|
|
90
|
+
: [];
|
|
91
|
+
return (definitions.find((definition) => matchesPath(definition.path, path)) ?? null);
|
|
92
|
+
};
|
|
93
|
+
exports.telemetryDefinition = telemetryDefinition;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { BufferPressure } from "./storage-budget";
|
|
2
|
+
import type { EventInput } from "./types";
|
|
3
|
+
export type OperatingProfile = "anchored" | "moored" | "underway";
|
|
4
|
+
export interface ReductionRule {
|
|
5
|
+
minimumIntervalMs: number;
|
|
6
|
+
maximumHeartbeatMs: number;
|
|
7
|
+
changeThreshold: number;
|
|
8
|
+
changeKind: "scalar" | "angle" | "position" | "identity";
|
|
9
|
+
}
|
|
10
|
+
export interface ReductionRules {
|
|
11
|
+
profile: OperatingProfile;
|
|
12
|
+
}
|
|
13
|
+
export declare const rulesForProfile: (profile: OperatingProfile) => ReductionRules;
|
|
14
|
+
export declare class TelemetryReducer {
|
|
15
|
+
private readonly enabled;
|
|
16
|
+
private readonly rules;
|
|
17
|
+
private readonly emitted;
|
|
18
|
+
constructor(enabled: boolean, rules: ReductionRules);
|
|
19
|
+
shouldEmit(event: EventInput, pressure?: BufferPressure): boolean;
|
|
20
|
+
record(event: EventInput): void;
|
|
21
|
+
private key;
|
|
22
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TelemetryReducer = exports.rulesForProfile = void 0;
|
|
4
|
+
const geometry_1 = require("./geometry");
|
|
5
|
+
const telemetry_catalog_1 = require("./telemetry-catalog");
|
|
6
|
+
const rulesForProfile = (profile) => ({
|
|
7
|
+
profile,
|
|
8
|
+
});
|
|
9
|
+
exports.rulesForProfile = rulesForProfile;
|
|
10
|
+
const positionRule = (profile) => profile === "anchored"
|
|
11
|
+
? {
|
|
12
|
+
minimumIntervalMs: 2_000,
|
|
13
|
+
maximumHeartbeatMs: 30_000,
|
|
14
|
+
changeThreshold: 2,
|
|
15
|
+
changeKind: "position",
|
|
16
|
+
}
|
|
17
|
+
: profile === "moored"
|
|
18
|
+
? {
|
|
19
|
+
minimumIntervalMs: 10_000,
|
|
20
|
+
maximumHeartbeatMs: 300_000,
|
|
21
|
+
changeThreshold: 10,
|
|
22
|
+
changeKind: "position",
|
|
23
|
+
}
|
|
24
|
+
: {
|
|
25
|
+
minimumIntervalMs: 2_000,
|
|
26
|
+
maximumHeartbeatMs: 60_000,
|
|
27
|
+
changeThreshold: 5,
|
|
28
|
+
changeKind: "position",
|
|
29
|
+
};
|
|
30
|
+
const ruleForEvent = (event, profile) => {
|
|
31
|
+
const definition = (0, telemetry_catalog_1.telemetryDefinition)(event.context, event.path);
|
|
32
|
+
if (definition === null) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
if (event.context === "vessels.self" &&
|
|
36
|
+
event.path === "navigation.position") {
|
|
37
|
+
return positionRule(profile);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
minimumIntervalMs: definition.minimumIntervalMs,
|
|
41
|
+
maximumHeartbeatMs: definition.maximumHeartbeatMs,
|
|
42
|
+
changeThreshold: definition.changeThreshold,
|
|
43
|
+
changeKind: definition.changeKind,
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const changedEnough = (kind, previous, current, threshold) => {
|
|
47
|
+
if (kind === "position") {
|
|
48
|
+
if (typeof previous !== "object" ||
|
|
49
|
+
typeof current !== "object" ||
|
|
50
|
+
!("latitude" in previous) ||
|
|
51
|
+
!("latitude" in current)) {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
return (0, geometry_1.distanceMeters)(previous, current) >= threshold;
|
|
55
|
+
}
|
|
56
|
+
if (kind === "identity") {
|
|
57
|
+
return JSON.stringify(previous) !== JSON.stringify(current);
|
|
58
|
+
}
|
|
59
|
+
if (typeof previous !== "number" || typeof current !== "number") {
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
return kind === "angle"
|
|
63
|
+
? (0, geometry_1.circularDistance)(previous, current) >= threshold
|
|
64
|
+
: Math.abs(previous - current) >= threshold;
|
|
65
|
+
};
|
|
66
|
+
const AIS_EMERGENCY_PATHS = new Set([
|
|
67
|
+
"navigation.position",
|
|
68
|
+
"navigation.speedOverGround",
|
|
69
|
+
"navigation.courseOverGroundTrue",
|
|
70
|
+
]);
|
|
71
|
+
const pressureAdjustedRule = (rule, pressure) => {
|
|
72
|
+
const multipliers = pressure === "elevated"
|
|
73
|
+
? { interval: 2, heartbeat: 2, threshold: 1.5 }
|
|
74
|
+
: pressure === "critical"
|
|
75
|
+
? { interval: 6, heartbeat: 5, threshold: 3 }
|
|
76
|
+
: pressure === "emergency"
|
|
77
|
+
? { interval: 12, heartbeat: 15, threshold: 6 }
|
|
78
|
+
: { interval: 1, heartbeat: 1, threshold: 1 };
|
|
79
|
+
return {
|
|
80
|
+
...rule,
|
|
81
|
+
minimumIntervalMs: rule.minimumIntervalMs * multipliers.interval,
|
|
82
|
+
maximumHeartbeatMs: rule.maximumHeartbeatMs * multipliers.heartbeat,
|
|
83
|
+
changeThreshold: rule.changeThreshold * multipliers.threshold,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
class TelemetryReducer {
|
|
87
|
+
enabled;
|
|
88
|
+
rules;
|
|
89
|
+
emitted = new Map();
|
|
90
|
+
constructor(enabled, rules) {
|
|
91
|
+
this.enabled = enabled;
|
|
92
|
+
this.rules = rules;
|
|
93
|
+
}
|
|
94
|
+
shouldEmit(event, pressure = "normal") {
|
|
95
|
+
const isAis = event.context !== "vessels.self";
|
|
96
|
+
if (!this.enabled && (!isAis || pressure === "normal")) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
const baseRule = ruleForEvent(event, this.rules.profile);
|
|
100
|
+
if (isAis &&
|
|
101
|
+
pressure === "emergency" &&
|
|
102
|
+
(baseRule?.changeKind === "identity" ||
|
|
103
|
+
!AIS_EMERGENCY_PATHS.has(event.path))) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const key = this.key(event);
|
|
107
|
+
const previous = this.emitted.get(key);
|
|
108
|
+
if (previous === undefined) {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (previous.event.source !== event.source) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
const receivedAt = Date.parse(event.received_at);
|
|
115
|
+
if (!Number.isFinite(receivedAt)) {
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
const elapsed = Math.max(0, receivedAt - previous.emittedAt);
|
|
119
|
+
const rule = baseRule === null || !isAis
|
|
120
|
+
? baseRule
|
|
121
|
+
: pressureAdjustedRule(baseRule, pressure);
|
|
122
|
+
if (rule === null || elapsed >= rule.maximumHeartbeatMs) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
if (elapsed < rule.minimumIntervalMs) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
return changedEnough(rule.changeKind, previous.event.value, event.value, rule.changeThreshold);
|
|
129
|
+
}
|
|
130
|
+
record(event) {
|
|
131
|
+
const receivedAt = Date.parse(event.received_at);
|
|
132
|
+
const key = this.key(event);
|
|
133
|
+
this.emitted.delete(key);
|
|
134
|
+
this.emitted.set(key, {
|
|
135
|
+
event: structuredClone(event),
|
|
136
|
+
emittedAt: Number.isFinite(receivedAt) ? receivedAt : Date.now(),
|
|
137
|
+
});
|
|
138
|
+
while (this.emitted.size > 5_000) {
|
|
139
|
+
const oldest = this.emitted.keys().next().value;
|
|
140
|
+
if (oldest === undefined) {
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
this.emitted.delete(oldest);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
key(event) {
|
|
147
|
+
return `${event.context}\u0000${event.path}`;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
exports.TelemetryReducer = TelemetryReducer;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { BufferPressure, ResolvedStorageProfile, StorageProfile } from "./storage-budget";
|
|
2
|
+
export declare const PROTOCOL_VERSION: "1.1";
|
|
3
|
+
export declare const SUPPORTED_PROTOCOL_VERSIONS: readonly ["1.0", "1.1"];
|
|
4
|
+
export type ProtocolVersion = (typeof SUPPORTED_PROTOCOL_VERSIONS)[number];
|
|
5
|
+
export type OperatingMode = "active" | "standby" | "disabled";
|
|
6
|
+
export interface RuntimeConfiguration {
|
|
7
|
+
version: number;
|
|
8
|
+
operating_mode: OperatingMode;
|
|
9
|
+
poll_after_seconds: number;
|
|
10
|
+
}
|
|
11
|
+
export type EventContext = "vessels.self" | `vessels.${number}` | `vessels.urn:mrn:imo:mmsi:${number}`;
|
|
12
|
+
export type AllowedPath = string;
|
|
13
|
+
export interface PositionValue {
|
|
14
|
+
latitude: number;
|
|
15
|
+
longitude: number;
|
|
16
|
+
}
|
|
17
|
+
export interface AisShipTypeValue {
|
|
18
|
+
id: number;
|
|
19
|
+
name: string;
|
|
20
|
+
}
|
|
21
|
+
export type EventValue = number | string | PositionValue | AisShipTypeValue;
|
|
22
|
+
export interface EventInput {
|
|
23
|
+
boot_id: string;
|
|
24
|
+
context: EventContext;
|
|
25
|
+
path: AllowedPath;
|
|
26
|
+
value: EventValue;
|
|
27
|
+
source_timestamp: string | null;
|
|
28
|
+
received_at: string;
|
|
29
|
+
source: string | null;
|
|
30
|
+
}
|
|
31
|
+
export interface BufferedEvent extends EventInput {
|
|
32
|
+
sequence: number;
|
|
33
|
+
}
|
|
34
|
+
export interface IngestEvent {
|
|
35
|
+
sequence: number;
|
|
36
|
+
context: EventContext;
|
|
37
|
+
path: AllowedPath;
|
|
38
|
+
value: EventValue;
|
|
39
|
+
source_timestamp: string | null;
|
|
40
|
+
received_at: string;
|
|
41
|
+
source: string | null;
|
|
42
|
+
}
|
|
43
|
+
export interface IngestBatch {
|
|
44
|
+
protocol_version: ProtocolVersion;
|
|
45
|
+
device_id: string;
|
|
46
|
+
boot_id: string;
|
|
47
|
+
batch_id: string;
|
|
48
|
+
first_sequence: number;
|
|
49
|
+
last_sequence: number;
|
|
50
|
+
created_at: string;
|
|
51
|
+
events: IngestEvent[];
|
|
52
|
+
}
|
|
53
|
+
export interface PairingResponse {
|
|
54
|
+
protocol_version: typeof PROTOCOL_VERSION;
|
|
55
|
+
device_id: string;
|
|
56
|
+
token_type: "Bearer";
|
|
57
|
+
access_token: string;
|
|
58
|
+
runtime_configuration: RuntimeConfiguration;
|
|
59
|
+
}
|
|
60
|
+
export interface IngestAcknowledgement {
|
|
61
|
+
accepted_through: number;
|
|
62
|
+
server_time: string;
|
|
63
|
+
runtime_configuration: RuntimeConfiguration;
|
|
64
|
+
}
|
|
65
|
+
export interface RuntimeConfigurationResponse {
|
|
66
|
+
runtime_configuration: RuntimeConfiguration;
|
|
67
|
+
server_time: string;
|
|
68
|
+
}
|
|
69
|
+
export interface PersistedState {
|
|
70
|
+
version: 2;
|
|
71
|
+
protocol_version: ProtocolVersion;
|
|
72
|
+
device_id: string | null;
|
|
73
|
+
access_token: string | null;
|
|
74
|
+
next_sequence: number;
|
|
75
|
+
accepted_through: number;
|
|
76
|
+
events: BufferedEvent[];
|
|
77
|
+
pending_batch: IngestBatch | null;
|
|
78
|
+
last_success_at: string | null;
|
|
79
|
+
operating_mode: OperatingMode;
|
|
80
|
+
}
|
|
81
|
+
export interface PersistedMetadata {
|
|
82
|
+
version: 2;
|
|
83
|
+
protocol_version: ProtocolVersion;
|
|
84
|
+
device_id: string | null;
|
|
85
|
+
access_token: string | null;
|
|
86
|
+
next_sequence: number;
|
|
87
|
+
accepted_through: number;
|
|
88
|
+
pending_batch: IngestBatch | null;
|
|
89
|
+
last_success_at: string | null;
|
|
90
|
+
operating_mode: OperatingMode;
|
|
91
|
+
}
|
|
92
|
+
export interface ConnectorStatus {
|
|
93
|
+
paired: boolean;
|
|
94
|
+
deviceId: string | null;
|
|
95
|
+
bufferedEvents: number;
|
|
96
|
+
acceptedThrough: number;
|
|
97
|
+
lastSuccessAt: string | null;
|
|
98
|
+
droppedEvents: number;
|
|
99
|
+
suppressedEvents: number;
|
|
100
|
+
anchorAlarmState: string;
|
|
101
|
+
bufferedBytes: number;
|
|
102
|
+
bufferByteLimit: number;
|
|
103
|
+
bufferEventLimit: number;
|
|
104
|
+
bufferUtilization: number;
|
|
105
|
+
bufferPressure: BufferPressure;
|
|
106
|
+
storageProfile: StorageProfile;
|
|
107
|
+
resolvedStorageProfile: ResolvedStorageProfile;
|
|
108
|
+
filesystemFreeBytes: number | null;
|
|
109
|
+
estimatedRemainingBufferHours: number | null;
|
|
110
|
+
operatingMode: OperatingMode;
|
|
111
|
+
transferState: "waiting" | "healthy" | "delayed";
|
|
112
|
+
transferMessage: string | null;
|
|
113
|
+
runtimeConfigurationState: "cached" | "current" | "unavailable";
|
|
114
|
+
runtimeConfigurationUpdatedAt: string | null;
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const CONNECTOR_VERSION = "0.2.0-dev.20";
|
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aurora-blue/signalk-connector",
|
|
3
|
+
"version": "0.2.0-dev.20",
|
|
4
|
+
"description": "Signal K edge connector for the Aurora Blue ABOARD ingest protocol",
|
|
5
|
+
"private": false,
|
|
6
|
+
"main": "dist/src/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/src",
|
|
9
|
+
"public",
|
|
10
|
+
"README.md",
|
|
11
|
+
"VERSION",
|
|
12
|
+
"LICENSE",
|
|
13
|
+
"THIRD_PARTY_NOTICES.md"
|
|
14
|
+
],
|
|
15
|
+
"keywords": [
|
|
16
|
+
"signalk-node-server-plugin",
|
|
17
|
+
"signalk-webapp",
|
|
18
|
+
"signalk-category-cloud"
|
|
19
|
+
],
|
|
20
|
+
"signalk": {
|
|
21
|
+
"displayName": "Aurora Blue ABOARD Connector Status"
|
|
22
|
+
},
|
|
23
|
+
"signalk-plugin-enabled-by-default": false,
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=20"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"prebuild": "node tools/sync-web-ui.mjs",
|
|
29
|
+
"build": "npm run clean && tsc -p tsconfig.json",
|
|
30
|
+
"capture:signalk": "node tools/capture-signalk.mjs",
|
|
31
|
+
"check": "npm run format:check && npm run typecheck && npm run test:tools && npm test",
|
|
32
|
+
"ci": "npm run check && npm run verify:package && npm audit --audit-level=high",
|
|
33
|
+
"clean": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
|
|
34
|
+
"format": "prettier --write .",
|
|
35
|
+
"format:check": "prettier --check .",
|
|
36
|
+
"prepack": "npm run build",
|
|
37
|
+
"replay:signalk": "npm run build && node tools/replay-signalk.mjs",
|
|
38
|
+
"simulate:offline": "npm run build && node tools/simulate-offline-buffer.mjs",
|
|
39
|
+
"test": "npm run build && node --test dist/test/*.test.js",
|
|
40
|
+
"test:tools": "node --test tools/test/*.test.mjs",
|
|
41
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
42
|
+
"verify:package": "npm pack --dry-run",
|
|
43
|
+
"verify:platforms": "sh tools/verify-platforms.sh",
|
|
44
|
+
"release:build": "node tools/release.mjs build",
|
|
45
|
+
"release:stage": "node tools/release.mjs stage",
|
|
46
|
+
"release:verify": "node tools/release.mjs verify"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@signalk/server-api": "2.31.1",
|
|
50
|
+
"@types/node": "22.20.1",
|
|
51
|
+
"prettier": "3.9.6",
|
|
52
|
+
"typescript": "7.0.2"
|
|
53
|
+
},
|
|
54
|
+
"license": "MIT",
|
|
55
|
+
"author": "Aurora Blue",
|
|
56
|
+
"publishConfig": {
|
|
57
|
+
"access": "public",
|
|
58
|
+
"registry": "https://registry.npmjs.org/"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 591 591" role="img" aria-labelledby="title">
|
|
3
|
+
<title id="title">Aurora Blue sailing boat</title>
|
|
4
|
+
<g fill="#142f49" transform="matrix(1,0,0,1,-889,-2810)">
|
|
5
|
+
<g transform="matrix(1,0,0,1,-637,622)">
|
|
6
|
+
<g transform="matrix(2.572721,0,0,2.572721,1472.604933,2173.221564)">
|
|
7
|
+
<g transform="matrix(0.720795,0,0,0.720795,199.370511,207.270387)">
|
|
8
|
+
<path d="M0,-27.601C-0.598,-22.004 -8.13,-1.988 -14.059,-1.624C-15.269,-1.55 -16.679,-2.299 -18.105,-2.317C-46.484,-2.679 -74.547,0 -102.901,-3.056C-120.07,-4.907 -134.481,-8.002 -152.253,-7.068C-171.242,-6.07 -190.423,-0.368 -209.403,-4.342C-209.592,-6.108 -209.405,-5.432 -208.403,-5.727C-203.691,-7.117 -199.244,-6.969 -194.802,-9.927C-189.206,-13.652 -187.485,-20.514 -180.241,-21.37C-163.895,-23.301 -141.452,-21.51 -124.174,-22.102C-122.555,-22.157 -121.06,-22.785 -119.44,-22.843C-89.331,-23.907 -59.169,-25.712 -29.11,-26.949C-19.432,-27.347 -9.68,-27.44 0,-27.601"/>
|
|
9
|
+
</g>
|
|
10
|
+
<g transform="matrix(0.720795,0,0,0.720795,182.596611,79.764338)">
|
|
11
|
+
<path d="M0,135.605C-23.259,131.026 -48.357,133.867 -71.164,139.718C-71.314,141.522 -71.235,140.848 -70.123,141.056C-62.834,142.414 -55.402,142.96 -48.293,145.241L-34.897,149.969L-135.486,152.026C-132.288,141.349 -124.753,139.673 -114.646,138.999C-102.718,138.203 -89.501,139.14 -77.623,140.202C-76.123,139.938 -74.307,128.401 -73.998,126.269C-69.126,92.69 -73.63,49.599 -76.642,15.511C-77.558,5.141 -78.61,-5.321 -80.059,-15.623C-78.948,-16.421 -73.362,-11.237 -72.171,-10.174C-44.126,14.87 -21.608,53.604 -10.078,89.233C-8.18,95.098 2.129,132.614 0,135.605"/>
|
|
12
|
+
</g>
|
|
13
|
+
<g transform="matrix(143.572567,33.611768,33.611768,-143.572567,81.107453,77.32971)">
|
|
14
|
+
<path d="M0,0.023C0.091,0.161 0.247,0.251 0.425,0.251C0.706,0.251 0.934,0.023 0.934,-0.258C0.934,-0.306 0.928,-0.352 0.915,-0.395C0.969,-0.315 1,-0.218 1,-0.114C1,0.167 0.772,0.395 0.491,0.395C0.451,0.395 0.411,0.391 0.374,0.382C0.193,0.34 0.05,0.201 0,0.023Z"/>
|
|
15
|
+
</g>
|
|
16
|
+
</g>
|
|
17
|
+
</g>
|
|
18
|
+
</g>
|
|
19
|
+
</svg>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 840" role="img" aria-labelledby="title description">
|
|
2
|
+
<title id="title">Aurora Blue Archipelago chart - dark</title>
|
|
3
|
+
<desc id="description">An original vector sea chart with islands, navigation lines, a route, a compass rose, lighthouses, boats, a treasure mark, and a pirate flag.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<pattern id="grid" width="30" height="30" patternUnits="userSpaceOnUse"><path d="M30 0H0V30" fill="none" stroke="#65b6c1" stroke-opacity=".08" stroke-width=".7"/></pattern>
|
|
6
|
+
<g id="rose" fill="none" stroke="#65b6c1"><circle r="74" stroke-width="1.2"/><circle r="58" stroke-opacity=".65" stroke-width=".7" stroke-dasharray="2 5"/><circle r="17" fill="#061826" stroke-width="1.3"/><circle r="8" stroke-width="1"/><path d="M0-92 9-18 0-4-9-18ZM92 0 18 9 4 0 18-9ZM0 92-9 18 0 4 9 18ZM-92 0-18-9-4 0-18 9Z" fill="#65b6c1" fill-opacity=".7" stroke-width=".7"/><path d="M0-68 5-15 0-6-5-15ZM48-48 15-5 6 0 5-15ZM68 0 15 5 6 0 15-5ZM48 48 5 15 0 6 15 5ZM0 68-5 15 0 6 5 15ZM-48 48-15 5-6 0-5 15ZM-68 0-15-5-6 0-15 5ZM-48-48-5-15 0-6-15-5Z" fill="#80d8d8" stroke-width=".5"/></g>
|
|
7
|
+
<g id="ship" fill="none" stroke="#65b6c1" stroke-linecap="round" stroke-linejoin="round"><path d="M-34 18H37L27 29H-25Z" fill="#0d2d3b" stroke-width="1.4"/><path d="M-4 18V-38M-4-34-27 15M0-33 22 15M-24 15h43" stroke-width="1.2"/><path d="M-6-31-24 13h18ZM-2-30 19 12H1Z" fill="#65b6c1" fill-opacity=".08" stroke-width="1"/><path d="M-30 31c17 4 39 4 58 0" stroke-opacity=".55" stroke-width=".8"/></g>
|
|
8
|
+
<g id="lighthouse" fill="none" stroke="#65b6c1" stroke-linejoin="round"><path d="m-7 22 3-32h8l3 32M-11 22h22M-7-10l3-7h8l3 7M0-17v-7M-19-18l12 3M19-18 7-15" stroke-width="1.2"/></g>
|
|
9
|
+
<g id="anchor" fill="none" stroke="#65b6c1" stroke-linecap="round" stroke-linejoin="round"><circle cy="-8" r="3"/><path d="M0-5v20M-10 5H10M-12 10c3 10 21 10 24 0M-12 10l-3 3M12 10l3 3" stroke-width="1.1"/></g>
|
|
10
|
+
<g id="motorboat" fill="none" stroke="#65b6c1" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4"><g transform="translate(-60 -40)"><path d="M12 58h100l-12 14H28c-8 0-14-5-16-14Z"/><path d="M31 58 45 39h35l19 19ZM50 45h14M72 45h14"/></g></g>
|
|
11
|
+
<g id="catamaran" fill="none" stroke="#65b6c1" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4"><g transform="translate(-60 -40)"><path d="M87 57h27l-7 11h-9"/><path d="M34 59 43 49h34l12 10ZM48 54h11M65 54h11"/><path d="M57 12 28 46h29ZM63 15c14 8 23 18 27 31H63ZM60 7v51"/><path d="M10 59h96l-10 13H27c-8 0-14-5-17-13Z"/></g></g>
|
|
12
|
+
<g id="treasure" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M-8-8 8 8M8-8-8 8" stroke="#e1b56f" stroke-width="2.8"/></g>
|
|
13
|
+
<g id="pirate-flag" fill="none" stroke="#e1b56f" stroke-linecap="round" stroke-linejoin="round"><path d="M-11 23V-24M-15 23H-7" stroke-width="2"/><path d="M-11-22c9-6 19 5 30-2v34C8 16-2 3-11 10Z" fill="#e1b56f" fill-opacity=".2" stroke-width="1.8"/><g transform="translate(-2.4 -1.5) scale(.8)"><path d="M0-13c0-5 3.5-8 8-8s8 3 8 8c0 4-2 6.5-4.5 7.5v3.5h-7v-3.5C2-6.5 0-9 0-13Z" fill="#061826" stroke-width="1.4"/><circle cx="4" cy="-13" r="1.6" fill="#e1b56f" stroke="none"/><circle cx="12" cy="-13" r="1.6" fill="#e1b56f" stroke="none"/><path d="M5-8h6M1 0 15 7M15 0 1 7" stroke-width="1.7"/></g></g>
|
|
14
|
+
</defs>
|
|
15
|
+
<rect width="600" height="840" fill="#061826"/><rect width="600" height="840" fill="url(#grid)"/>
|
|
16
|
+
<g fill="none" stroke="#65b6c1" stroke-linecap="round" opacity=".88">
|
|
17
|
+
<path d="M0 0h185c-14 34-4 68-29 92-31 31-88 21-103 63-14 40 22 72 9 111-10 30-43 45-62 63Z" fill="#0d2d3b" stroke-width="1.7"/>
|
|
18
|
+
<path d="M600 0H478c-11 31-3 61-27 83-26 24-68 23-76 58-8 36 30 54 25 92-4 29-31 47-28 78 4 36 43 50 47 84 4 33-27 54-19 87 8 31 39 44 40 76 2 41-41 60-41 103 1 37 31 60 28 98-3 36-37 53-43 81H600Z" fill="#0d2d3b" stroke-width="1.7"/>
|
|
19
|
+
<path d="M0 678c31-13 62-12 88 7 29 21 39 59 75 69 38 10 66-25 104-20 42 5 57 51 94 65 32 12 64-1 93 11 18 7 29 17 41 30H0Z" fill="#0d2d3b" stroke-width="1.7"/>
|
|
20
|
+
<g stroke-opacity=".48"><path d="M0 23h158c-13 29-5 56-27 76-28 25-73 17-87 55-12 34 20 66 5 97-10 21-30 34-49 48"/><path d="M600 27H499c-12 26-7 51-27 70-24 22-60 24-67 48-9 30 28 51 21 83-6 27-29 46-24 74 6 31 39 45 42 74 3 28-26 50-17 82 9 31 37 44 37 76 0 39-39 61-38 96 1 34 26 57 21 88-4 24-22 39-31 60"/><path d="M0 704c30-11 53-7 75 9 27 20 36 56 73 65 36 9 65-21 101-17 39 5 54 45 91 58"/></g>
|
|
21
|
+
<g stroke-opacity=".24"><path d="M0 48h129c-9 24-6 43-21 59-20 21-58 22-70 49-12 28 13 58-3 82L0 269"/><path d="M600 54h-79c-10 20-7 43-23 58-18 17-50 22-57 42-8 24 24 45 15 71-7 22-27 42-19 64 9 26 33 39 34 64 1 27-22 49-14 76 9 27 33 43 31 69-2 32-31 56-27 87 3 23 17 39 16 60"/><path d="M0 731c27-9 47-4 66 11 25 20 36 49 69 58 37 10 66-17 98-10 25 6 42 26 62 42"/></g>
|
|
22
|
+
<g stroke-opacity=".16" stroke-width=".8"><path d="M-20 100 620 565M-20 352 620 120M-20 602 620 286M48 860 508-20M200 860 424-20M-20 470 620 760"/><circle cx="202" cy="181" r="10"/><circle cx="202" cy="181" r="18"/><circle cx="353" cy="668" r="9"/><circle cx="353" cy="668" r="18"/></g>
|
|
23
|
+
<path d="M282 102C352 183 279 236 329 313S276 442 319 526s-25 131 45 214" stroke="#5be0d5" stroke-width="1.7" stroke-dasharray="4 7"/>
|
|
24
|
+
<g fill="#061826" stroke="#5be0d5" stroke-width="1.4"><circle cx="282" cy="102" r="5"/><circle cx="329" cy="313" r="5"/><circle cx="319" cy="526" r="5"/><circle cx="364" cy="740" r="5"/></g>
|
|
25
|
+
<path d="M329 313C372 307 396 324 428 344C447 357 467 359 484 353C494 350 502 346 507 342" stroke="#5be0d5" stroke-width="1.7" stroke-dasharray="5 7"/>
|
|
26
|
+
<g fill="#061826" stroke="#5be0d5" stroke-width="1.3"><circle cx="428" cy="344" r="4"/><circle cx="484" cy="353" r="4"/></g>
|
|
27
|
+
<path d="m505 335 12 7-12 7Z" fill="#5be0d5" stroke="none"/>
|
|
28
|
+
<use href="#rose" transform="translate(121 580) scale(.64)"/><use href="#lighthouse" transform="translate(92 742) scale(.75)"/><use href="#lighthouse" transform="translate(527 510) scale(.7)"/><use href="#anchor" transform="translate(176 285) scale(.55)"/><use href="#anchor" transform="translate(365 421) scale(.55)"/><use href="#anchor" transform="translate(207 622) scale(.5)"/><use href="#ship" transform="translate(332 164) scale(.7)"/><use href="#ship" transform="translate(352 628) scale(.48)"/><use href="#catamaran" transform="translate(118 403) scale(.5)"/><use href="#motorboat" transform="translate(275 694) scale(.5)"/><use href="#pirate-flag" transform="translate(542 294) scale(.92)"/><use href="#treasure" transform="translate(531 336)"/>
|
|
29
|
+
<g fill="#65b6c1" stroke="none" font-family="serif" font-size="5" opacity=".46"><text x="225" y="72">21</text><text x="337" y="105">32</text><text x="464" y="146">16</text><text x="245" y="208">28</text><text x="335" y="253">37</text><text x="232" y="365">19</text><text x="361" y="403">42</text><text x="268" y="471">25</text><text x="347" y="584">31</text><text x="243" y="681">18</text><text x="505" y="748">27</text><text x="91" y="448">34</text></g>
|
|
30
|
+
</g>
|
|
31
|
+
<rect x="6" y="6" width="588" height="828" fill="none" stroke="#65b6c1" stroke-opacity=".42"/>
|
|
32
|
+
</svg>
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 600 840" role="img" aria-labelledby="title description">
|
|
2
|
+
<title id="title">Aurora Blue Archipelago chart - light</title>
|
|
3
|
+
<desc id="description">An original vector sea chart with islands, navigation lines, a route, a compass rose, lighthouses, boats, a treasure mark, and a pirate flag.</desc>
|
|
4
|
+
<defs>
|
|
5
|
+
<pattern id="grid" width="30" height="30" patternUnits="userSpaceOnUse">
|
|
6
|
+
<path d="M30 0H0V30" fill="none" stroke="#356e82" stroke-opacity=".08" stroke-width=".7"/>
|
|
7
|
+
</pattern>
|
|
8
|
+
<g id="rose" fill="none" stroke="#356e82">
|
|
9
|
+
<circle r="74" stroke-width="1.2"/><circle r="58" stroke-opacity=".65" stroke-width=".7" stroke-dasharray="2 5"/>
|
|
10
|
+
<circle r="17" fill="#e7f2f2" stroke-width="1.3"/><circle r="8" stroke-width="1"/>
|
|
11
|
+
<path d="M0-92 9-18 0-4-9-18ZM92 0 18 9 4 0 18-9ZM0 92-9 18 0 4 9 18ZM-92 0-18-9-4 0-18 9Z" fill="#356e82" fill-opacity=".7" stroke-width=".7"/>
|
|
12
|
+
<path d="M0-68 5-15 0-6-5-15ZM48-48 15-5 6 0 5-15ZM68 0 15 5 6 0 15-5ZM48 48 5 15 0 6 15 5ZM0 68-5 15 0 6 5 15ZM-48 48-15 5-6 0-5 15ZM-68 0-15-5-6 0-15 5ZM-48-48-5-15 0-6-15-5Z" fill="#6ea9b2" stroke-width=".5"/>
|
|
13
|
+
</g>
|
|
14
|
+
<g id="ship" fill="none" stroke="#356e82" stroke-linecap="round" stroke-linejoin="round">
|
|
15
|
+
<path d="M-34 18H37L27 29H-25Z" fill="#d2e4e4" stroke-width="1.4"/>
|
|
16
|
+
<path d="M-4 18V-38M-4-34-27 15M0-33 22 15M-24 15h43" stroke-width="1.2"/>
|
|
17
|
+
<path d="M-6-31-24 13h18ZM-2-30 19 12H1Z" fill="#356e82" fill-opacity=".08" stroke-width="1"/>
|
|
18
|
+
<path d="M-30 31c17 4 39 4 58 0" stroke-opacity=".55" stroke-width=".8"/>
|
|
19
|
+
</g>
|
|
20
|
+
<g id="lighthouse" fill="none" stroke="#356e82" stroke-linejoin="round">
|
|
21
|
+
<path d="m-7 22 3-32h8l3 32M-11 22h22M-7-10l3-7h8l3 7M0-17v-7M-19-18l12 3M19-18 7-15" stroke-width="1.2"/>
|
|
22
|
+
</g>
|
|
23
|
+
<g id="anchor" fill="none" stroke="#356e82" stroke-linecap="round" stroke-linejoin="round">
|
|
24
|
+
<circle cy="-8" r="3"/><path d="M0-5v20M-10 5H10M-12 10c3 10 21 10 24 0M-12 10l-3 3M12 10l3 3" stroke-width="1.1"/>
|
|
25
|
+
</g>
|
|
26
|
+
<g id="motorboat" fill="none" stroke="#356e82" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4">
|
|
27
|
+
<g transform="translate(-60 -40)"><path d="M12 58h100l-12 14H28c-8 0-14-5-16-14Z"/><path d="M31 58 45 39h35l19 19ZM50 45h14M72 45h14"/></g>
|
|
28
|
+
</g>
|
|
29
|
+
<g id="catamaran" fill="none" stroke="#356e82" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.4">
|
|
30
|
+
<g transform="translate(-60 -40)"><path d="M87 57h27l-7 11h-9"/><path d="M34 59 43 49h34l12 10ZM48 54h11M65 54h11"/><path d="M57 12 28 46h29ZM63 15c14 8 23 18 27 31H63ZM60 7v51"/><path d="M10 59h96l-10 13H27c-8 0-14-5-17-13Z"/></g>
|
|
31
|
+
</g>
|
|
32
|
+
<g id="treasure" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
|
33
|
+
<path d="M-8-8 8 8M8-8-8 8" stroke="#a87536" stroke-width="2.8"/>
|
|
34
|
+
</g>
|
|
35
|
+
<g id="pirate-flag" fill="none" stroke="#a87536" stroke-linecap="round" stroke-linejoin="round">
|
|
36
|
+
<path d="M-11 23V-24M-15 23H-7" stroke-width="2"/>
|
|
37
|
+
<path d="M-11-22c9-6 19 5 30-2v34C8 16-2 3-11 10Z" fill="#a87536" fill-opacity=".16" stroke-width="1.8"/>
|
|
38
|
+
<g transform="translate(-2.4 -1.5) scale(.8)">
|
|
39
|
+
<path d="M0-13c0-5 3.5-8 8-8s8 3 8 8c0 4-2 6.5-4.5 7.5v3.5h-7v-3.5C2-6.5 0-9 0-13Z" fill="#e7f2f2" stroke-width="1.4"/>
|
|
40
|
+
<circle cx="4" cy="-13" r="1.6" fill="#a87536" stroke="none"/><circle cx="12" cy="-13" r="1.6" fill="#a87536" stroke="none"/>
|
|
41
|
+
<path d="M5-8h6M1 0 15 7M15 0 1 7" stroke-width="1.7"/>
|
|
42
|
+
</g>
|
|
43
|
+
</g>
|
|
44
|
+
</defs>
|
|
45
|
+
<rect width="600" height="840" fill="#e7f2f2"/>
|
|
46
|
+
<rect width="600" height="840" fill="url(#grid)"/>
|
|
47
|
+
<g fill="none" stroke="#356e82" stroke-linecap="round" opacity=".88">
|
|
48
|
+
<path d="M0 0h185c-14 34-4 68-29 92-31 31-88 21-103 63-14 40 22 72 9 111-10 30-43 45-62 63Z" fill="#d2e4e4" stroke-width="1.7"/>
|
|
49
|
+
<path d="M600 0H478c-11 31-3 61-27 83-26 24-68 23-76 58-8 36 30 54 25 92-4 29-31 47-28 78 4 36 43 50 47 84 4 33-27 54-19 87 8 31 39 44 40 76 2 41-41 60-41 103 1 37 31 60 28 98-3 36-37 53-43 81H600Z" fill="#d2e4e4" stroke-width="1.7"/>
|
|
50
|
+
<path d="M0 678c31-13 62-12 88 7 29 21 39 59 75 69 38 10 66-25 104-20 42 5 57 51 94 65 32 12 64-1 93 11 18 7 29 17 41 30H0Z" fill="#d2e4e4" stroke-width="1.7"/>
|
|
51
|
+
<g stroke-opacity=".48">
|
|
52
|
+
<path d="M0 23h158c-13 29-5 56-27 76-28 25-73 17-87 55-12 34 20 66 5 97-10 21-30 34-49 48"/>
|
|
53
|
+
<path d="M600 27H499c-12 26-7 51-27 70-24 22-60 24-67 48-9 30 28 51 21 83-6 27-29 46-24 74 6 31 39 45 42 74 3 28-26 50-17 82 9 31 37 44 37 76 0 39-39 61-38 96 1 34 26 57 21 88-4 24-22 39-31 60"/>
|
|
54
|
+
<path d="M0 704c30-11 53-7 75 9 27 20 36 56 73 65 36 9 65-21 101-17 39 5 54 45 91 58"/>
|
|
55
|
+
</g>
|
|
56
|
+
<g stroke-opacity=".24">
|
|
57
|
+
<path d="M0 48h129c-9 24-6 43-21 59-20 21-58 22-70 49-12 28 13 58-3 82L0 269"/>
|
|
58
|
+
<path d="M600 54h-79c-10 20-7 43-23 58-18 17-50 22-57 42-8 24 24 45 15 71-7 22-27 42-19 64 9 26 33 39 34 64 1 27-22 49-14 76 9 27 33 43 31 69-2 32-31 56-27 87 3 23 17 39 16 60"/>
|
|
59
|
+
<path d="M0 731c27-9 47-4 66 11 25 20 36 49 69 58 37 10 66-17 98-10 25 6 42 26 62 42"/>
|
|
60
|
+
</g>
|
|
61
|
+
<g stroke-opacity=".16" stroke-width=".8">
|
|
62
|
+
<path d="M-20 100 620 565M-20 352 620 120M-20 602 620 286M48 860 508-20M200 860 424-20M-20 470 620 760"/>
|
|
63
|
+
<circle cx="202" cy="181" r="10"/><circle cx="202" cy="181" r="18"/><circle cx="353" cy="668" r="9"/><circle cx="353" cy="668" r="18"/>
|
|
64
|
+
</g>
|
|
65
|
+
<path d="M282 102C352 183 279 236 329 313S276 442 319 526s-25 131 45 214" stroke="#168c91" stroke-width="1.7" stroke-dasharray="4 7"/>
|
|
66
|
+
<g fill="#e7f2f2" stroke="#168c91" stroke-width="1.4"><circle cx="282" cy="102" r="5"/><circle cx="329" cy="313" r="5"/><circle cx="319" cy="526" r="5"/><circle cx="364" cy="740" r="5"/></g>
|
|
67
|
+
<path d="M329 313C372 307 396 324 428 344C447 357 467 359 484 353C494 350 502 346 507 342" stroke="#168c91" stroke-width="1.7" stroke-dasharray="5 7"/>
|
|
68
|
+
<g fill="#e7f2f2" stroke="#168c91" stroke-width="1.3"><circle cx="428" cy="344" r="4"/><circle cx="484" cy="353" r="4"/></g>
|
|
69
|
+
<path d="m505 335 12 7-12 7Z" fill="#168c91" stroke="none"/>
|
|
70
|
+
<use href="#rose" transform="translate(121 580) scale(.64)"/>
|
|
71
|
+
<use href="#lighthouse" transform="translate(92 742) scale(.75)"/><use href="#lighthouse" transform="translate(527 510) scale(.7)"/>
|
|
72
|
+
<use href="#anchor" transform="translate(176 285) scale(.55)"/><use href="#anchor" transform="translate(365 421) scale(.55)"/><use href="#anchor" transform="translate(207 622) scale(.5)"/>
|
|
73
|
+
<use href="#ship" transform="translate(332 164) scale(.7)"/><use href="#ship" transform="translate(352 628) scale(.48)"/>
|
|
74
|
+
<use href="#catamaran" transform="translate(118 403) scale(.5)"/><use href="#motorboat" transform="translate(275 694) scale(.5)"/>
|
|
75
|
+
<use href="#pirate-flag" transform="translate(542 294) scale(.92)"/><use href="#treasure" transform="translate(531 336)"/>
|
|
76
|
+
<g fill="#356e82" stroke="none" font-family="serif" font-size="5" opacity=".46">
|
|
77
|
+
<text x="225" y="72">21</text><text x="337" y="105">32</text><text x="464" y="146">16</text><text x="245" y="208">28</text><text x="335" y="253">37</text><text x="232" y="365">19</text><text x="361" y="403">42</text><text x="268" y="471">25</text><text x="347" y="584">31</text><text x="243" y="681">18</text><text x="505" y="748">27</text><text x="91" y="448">34</text>
|
|
78
|
+
</g>
|
|
79
|
+
</g>
|
|
80
|
+
<rect x="6" y="6" width="588" height="828" fill="none" stroke="#356e82" stroke-opacity=".42"/>
|
|
81
|
+
</svg>
|