@marineyachtradar/signalk-plugin 0.2.1 → 0.5.0-beta.2
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/README.md +48 -70
- package/package.json +42 -6
- package/plugin/config/schema.d.ts +13 -0
- package/plugin/config/schema.d.ts.map +1 -0
- package/plugin/config/schema.js +48 -0
- package/plugin/config/schema.js.map +1 -0
- package/plugin/index.d.ts +2 -0
- package/plugin/index.d.ts.map +1 -0
- package/plugin/index.js +359 -289
- package/plugin/index.js.map +1 -0
- package/plugin/mayara-client.d.ts +28 -0
- package/plugin/mayara-client.d.ts.map +1 -0
- package/plugin/mayara-client.js +102 -194
- package/plugin/mayara-client.js.map +1 -0
- package/plugin/radar-provider.d.ts +5 -0
- package/plugin/radar-provider.d.ts.map +1 -0
- package/plugin/radar-provider.js +192 -305
- package/plugin/radar-provider.js.map +1 -0
- package/plugin/spoke-forwarder.d.ts +30 -0
- package/plugin/spoke-forwarder.d.ts.map +1 -0
- package/plugin/spoke-forwarder.js +104 -136
- package/plugin/spoke-forwarder.js.map +1 -0
- package/plugin/types.d.ts +21 -0
- package/plugin/types.d.ts.map +1 -0
- package/plugin/types.js +3 -0
- package/plugin/types.js.map +1 -0
- package/public/540.js +2 -0
- package/public/540.js.LICENSE.txt +9 -0
- package/public/805.js +1 -0
- package/public/index.html +27 -39
- package/public/main.js +1 -0
- package/public/remoteEntry.js +1 -0
- package/public/api.js +0 -402
- package/public/assets/mayara_logo.png +0 -0
- package/public/base.css +0 -91
- package/public/control.html +0 -23
- package/public/control.js +0 -1155
- package/public/controls.css +0 -538
- package/public/discovery.css +0 -478
- package/public/favicon.ico +0 -0
- package/public/layout.css +0 -87
- package/public/mayara.js +0 -510
- package/public/proto/RadarMessage.proto +0 -41
- package/public/protobuf/protobuf.js +0 -9112
- package/public/protobuf/protobuf.js.map +0 -1
- package/public/protobuf/protobuf.min.js +0 -8
- package/public/protobuf/protobuf.min.js.map +0 -1
- package/public/radar.svg +0 -29
- package/public/render_webgpu.js +0 -886
- package/public/responsive.css +0 -29
- package/public/van-1.5.2.debug.js +0 -126
- package/public/van-1.5.2.js +0 -140
- package/public/van-1.5.2.min.js +0 -1
- package/public/viewer.html +0 -30
- package/public/viewer.js +0 -797
package/plugin/index.js
CHANGED
|
@@ -1,306 +1,376 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const child_process_1 = require("child_process");
|
|
4
|
+
const util_1 = require("util");
|
|
5
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
6
|
+
const mayara_client_1 = require("./mayara-client");
|
|
7
|
+
const radar_provider_1 = require("./radar-provider");
|
|
8
|
+
const spoke_forwarder_1 = require("./spoke-forwarder");
|
|
9
|
+
const schema_1 = require("./config/schema");
|
|
10
|
+
const MAYARA_IMAGE = 'ghcr.io/marineyachtradar/mayara-server';
|
|
11
|
+
const SAFE_TAG = /^[a-zA-Z0-9._-]+$/;
|
|
12
12
|
module.exports = function (app) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
type: 'string',
|
|
33
|
-
title: 'mayara-server Host',
|
|
34
|
-
description: 'IP address or hostname of mayara-server',
|
|
35
|
-
default: 'localhost'
|
|
13
|
+
let client = null;
|
|
14
|
+
let currentSettings = null;
|
|
15
|
+
const spokeForwarders = new Map();
|
|
16
|
+
let discoveryInterval = null;
|
|
17
|
+
let reconnectInterval = null;
|
|
18
|
+
let isConnected = false;
|
|
19
|
+
const knownRadars = new Set();
|
|
20
|
+
const plugin = {
|
|
21
|
+
id: 'mayara-server-signalk-plugin',
|
|
22
|
+
name: 'MaYaRa Radar (Server)',
|
|
23
|
+
description: 'Connect SignalK to mayara-server for multi-brand marine radar integration',
|
|
24
|
+
enabledByDefault: true,
|
|
25
|
+
schema: schema_1.ConfigSchema,
|
|
26
|
+
start(config) {
|
|
27
|
+
app.debug('Starting mayara-server-signalk-plugin');
|
|
28
|
+
currentSettings = config;
|
|
29
|
+
void asyncStart(config).catch((err) => {
|
|
30
|
+
app.setPluginError(`Startup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
31
|
+
});
|
|
36
32
|
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
stop() {
|
|
34
|
+
app.debug('Stopping mayara-server-signalk-plugin');
|
|
35
|
+
try {
|
|
36
|
+
app.radarApi.unRegister(plugin.id);
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
app.debug(`Error unregistering: ${err instanceof Error ? err.message : String(err)}`);
|
|
40
|
+
}
|
|
41
|
+
if (discoveryInterval) {
|
|
42
|
+
clearInterval(discoveryInterval);
|
|
43
|
+
discoveryInterval = null;
|
|
44
|
+
}
|
|
45
|
+
if (reconnectInterval) {
|
|
46
|
+
clearInterval(reconnectInterval);
|
|
47
|
+
reconnectInterval = null;
|
|
48
|
+
}
|
|
49
|
+
for (const forwarder of spokeForwarders.values()) {
|
|
50
|
+
forwarder.stop();
|
|
51
|
+
}
|
|
52
|
+
spokeForwarders.clear();
|
|
53
|
+
knownRadars.clear();
|
|
54
|
+
if (client) {
|
|
55
|
+
client.close();
|
|
56
|
+
client = null;
|
|
57
|
+
}
|
|
58
|
+
isConnected = false;
|
|
59
|
+
app.setPluginStatus('Stopped');
|
|
44
60
|
},
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
registerWithRouter(router) {
|
|
62
|
+
router.get('/status', async (req, res) => {
|
|
63
|
+
let containerState = 'unknown';
|
|
64
|
+
let containerImage = '';
|
|
65
|
+
try {
|
|
66
|
+
const containers = globalThis.__signalk_containerManager;
|
|
67
|
+
if (containers) {
|
|
68
|
+
containerState = await containers.getState('mayara-server');
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// ignore
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const tag = currentSettings?.mayaraVersion ?? 'latest';
|
|
76
|
+
containerImage = `${MAYARA_IMAGE}:${tag}`;
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// ignore
|
|
80
|
+
}
|
|
81
|
+
res.json({
|
|
82
|
+
connected: isConnected,
|
|
83
|
+
radars: Array.from(knownRadars),
|
|
84
|
+
spokeForwarders: Array.from(spokeForwarders.keys()).map((id) => ({
|
|
85
|
+
radarId: id,
|
|
86
|
+
connected: spokeForwarders.get(id)?.isConnected() ?? false
|
|
87
|
+
})),
|
|
88
|
+
container: {
|
|
89
|
+
state: containerState,
|
|
90
|
+
image: containerImage,
|
|
91
|
+
managed: currentSettings?.managedContainer !== false
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
router.post('/api/check-update', async (req, res) => {
|
|
96
|
+
try {
|
|
97
|
+
const containers = globalThis.__signalk_containerManager;
|
|
98
|
+
if (!containers) {
|
|
99
|
+
res.status(400).json({ error: 'signalk-container not available' });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const runtime = containers.getRuntime();
|
|
103
|
+
if (!runtime) {
|
|
104
|
+
res.status(400).json({ error: 'No container runtime detected' });
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const rt = runtime.runtime;
|
|
108
|
+
const tag = req.body.tag ??
|
|
109
|
+
currentSettings?.mayaraVersion ??
|
|
110
|
+
'latest';
|
|
111
|
+
if (!SAFE_TAG.test(tag)) {
|
|
112
|
+
res.status(400).json({ error: 'Invalid tag format' });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const image = `${MAYARA_IMAGE}:${tag}`;
|
|
116
|
+
// Get image ID of running container
|
|
117
|
+
let runningImageId = '';
|
|
118
|
+
try {
|
|
119
|
+
const { stdout } = await execAsync(`${rt} inspect sk-mayara-server --format '{{.Image}}'`);
|
|
120
|
+
runningImageId = stdout.trim();
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// container not running
|
|
124
|
+
}
|
|
125
|
+
// Pull latest
|
|
126
|
+
app.debug(`Checking for update: pulling ${image}`);
|
|
127
|
+
await containers.pullImage(image);
|
|
128
|
+
// Get image ID of pulled image
|
|
129
|
+
let pulledImageId = '';
|
|
130
|
+
try {
|
|
131
|
+
const { stdout } = await execAsync(`${rt} image inspect ${image} --format '{{.Id}}'`);
|
|
132
|
+
pulledImageId = stdout.trim();
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// image inspect failed
|
|
136
|
+
}
|
|
137
|
+
if (!runningImageId) {
|
|
138
|
+
res.json({ updateAvailable: false, message: 'Container not running' });
|
|
139
|
+
}
|
|
140
|
+
else if (runningImageId === pulledImageId) {
|
|
141
|
+
res.json({ updateAvailable: false, message: `Up to date (${tag})` });
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
res.json({ updateAvailable: true, message: `Update available for ${tag}` });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
router.post('/api/update', async (req, res) => {
|
|
152
|
+
try {
|
|
153
|
+
const containers = globalThis.__signalk_containerManager;
|
|
154
|
+
if (!containers) {
|
|
155
|
+
res.status(400).json({ error: 'signalk-container not available' });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const tag = req.body.tag ??
|
|
159
|
+
currentSettings?.mayaraVersion ??
|
|
160
|
+
'latest';
|
|
161
|
+
if (!SAFE_TAG.test(tag)) {
|
|
162
|
+
res.status(400).json({ error: 'Invalid tag format' });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
const image = `${MAYARA_IMAGE}:${tag}`;
|
|
166
|
+
app.setPluginStatus(`Updating mayara-server to ${image}...`);
|
|
167
|
+
await containers.pullImage(image);
|
|
168
|
+
await containers.stop('mayara-server');
|
|
169
|
+
await containers.remove('mayara-server');
|
|
170
|
+
const args = currentSettings?.mayaraArgs ?? [];
|
|
171
|
+
await containers.ensureRunning('mayara-server', {
|
|
172
|
+
image: MAYARA_IMAGE,
|
|
173
|
+
tag,
|
|
174
|
+
networkMode: 'host',
|
|
175
|
+
command: args.length > 0 ? ['mayara-server', ...args] : undefined,
|
|
176
|
+
restart: 'unless-stopped'
|
|
177
|
+
});
|
|
178
|
+
res.json({ success: true, tag });
|
|
179
|
+
app.setPluginStatus(`Updated to ${tag} and running.`);
|
|
180
|
+
}
|
|
181
|
+
catch (err) {
|
|
182
|
+
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
router.get('/api/gui-url', (req, res) => {
|
|
186
|
+
const host = currentSettings?.host ?? 'localhost';
|
|
187
|
+
const port = currentSettings?.port ?? 6502;
|
|
188
|
+
const proto = currentSettings?.secure ? 'https' : 'http';
|
|
189
|
+
res.json({ url: `${proto}://${host}:${port}/gui/` });
|
|
190
|
+
});
|
|
191
|
+
router.get('/api/versions', async (req, res) => {
|
|
192
|
+
try {
|
|
193
|
+
const ghRes = await fetch('https://api.github.com/repos/MarineYachtRadar/mayara-server/releases?per_page=10', {
|
|
194
|
+
headers: { Accept: 'application/vnd.github+json' },
|
|
195
|
+
signal: AbortSignal.timeout(10000)
|
|
196
|
+
});
|
|
197
|
+
if (!ghRes.ok) {
|
|
198
|
+
res.status(502).json({ error: 'Failed to fetch releases' });
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const releases = (await ghRes.json());
|
|
202
|
+
res.json(releases
|
|
203
|
+
.filter((r) => !r.draft)
|
|
204
|
+
.map((r) => ({ tag: r.tag_name, prerelease: r.prerelease })));
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
res.status(500).json({
|
|
208
|
+
error: err instanceof Error ? err.message : 'Unknown error'
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
});
|
|
66
212
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
// Create RadarProvider implementation
|
|
82
|
-
provider = createRadarProvider(client, app)
|
|
83
|
-
|
|
84
|
-
// Check if radar API is available
|
|
85
|
-
if (!app.radarApi) {
|
|
86
|
-
app.setPluginError('SignalK Radar API not available (requires SignalK >= 2.0.0)')
|
|
87
|
-
return
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Register with SignalK Radar API
|
|
91
|
-
try {
|
|
92
|
-
app.radarApi.register(plugin.id, {
|
|
93
|
-
name: plugin.name,
|
|
94
|
-
methods: provider
|
|
95
|
-
})
|
|
96
|
-
app.debug('Registered as radar provider')
|
|
97
|
-
} catch (err) {
|
|
98
|
-
app.setPluginError(`Failed to register radar provider: ${err.message}`)
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Start connection and discovery
|
|
103
|
-
connectAndDiscover(settings)
|
|
104
|
-
},
|
|
105
|
-
|
|
106
|
-
stop: function () {
|
|
107
|
-
app.debug('Stopping mayara-server-signalk-plugin')
|
|
108
|
-
|
|
109
|
-
// Unregister from radar API
|
|
110
|
-
if (app.radarApi) {
|
|
213
|
+
};
|
|
214
|
+
async function asyncStart(settings) {
|
|
215
|
+
if (settings.managedContainer) {
|
|
216
|
+
await startManagedContainer(settings);
|
|
217
|
+
}
|
|
218
|
+
client = new mayara_client_1.MayaraClient({
|
|
219
|
+
host: settings.host ?? 'localhost',
|
|
220
|
+
port: settings.port ?? 6502,
|
|
221
|
+
secure: settings.secure ?? false,
|
|
222
|
+
debug: app.debug.bind(app)
|
|
223
|
+
});
|
|
224
|
+
const provider = (0, radar_provider_1.createRadarProvider)(client, app);
|
|
111
225
|
try {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
226
|
+
app.radarApi.register(plugin.id, {
|
|
227
|
+
name: plugin.name,
|
|
228
|
+
methods: provider
|
|
229
|
+
});
|
|
230
|
+
app.debug('Registered as radar provider');
|
|
231
|
+
}
|
|
232
|
+
catch (err) {
|
|
233
|
+
app.setPluginError(`Failed to register radar provider: ${err instanceof Error ? err.message : String(err)}`);
|
|
234
|
+
return;
|
|
115
235
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
// Clear intervals
|
|
119
|
-
if (discoveryInterval) {
|
|
120
|
-
clearInterval(discoveryInterval)
|
|
121
|
-
discoveryInterval = null
|
|
122
|
-
}
|
|
123
|
-
if (reconnectInterval) {
|
|
124
|
-
clearInterval(reconnectInterval)
|
|
125
|
-
reconnectInterval = null
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Stop all spoke forwarders
|
|
129
|
-
for (const forwarder of spokeForwarders.values()) {
|
|
130
|
-
forwarder.stop()
|
|
131
|
-
}
|
|
132
|
-
spokeForwarders.clear()
|
|
133
|
-
knownRadars.clear()
|
|
134
|
-
|
|
135
|
-
// Close client
|
|
136
|
-
if (client) {
|
|
137
|
-
client.close()
|
|
138
|
-
client = null
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
isConnected = false
|
|
142
|
-
app.setPluginStatus('Stopped')
|
|
143
|
-
},
|
|
144
|
-
|
|
145
|
-
registerWithRouter: function (router) {
|
|
146
|
-
// Health check endpoint
|
|
147
|
-
router.get('/status', (req, res) => {
|
|
148
|
-
res.json({
|
|
149
|
-
connected: isConnected,
|
|
150
|
-
radars: Array.from(knownRadars),
|
|
151
|
-
spokeForwarders: Array.from(spokeForwarders.keys()).map(id => ({
|
|
152
|
-
radarId: id,
|
|
153
|
-
connected: spokeForwarders.get(id)?.isConnected() || false
|
|
154
|
-
}))
|
|
155
|
-
})
|
|
156
|
-
})
|
|
236
|
+
await connectAndDiscover(settings);
|
|
157
237
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
238
|
+
async function startManagedContainer(settings) {
|
|
239
|
+
let containers;
|
|
240
|
+
const waitDeadline = Date.now() + 30000;
|
|
241
|
+
while (Date.now() < waitDeadline) {
|
|
242
|
+
containers = globalThis.__signalk_containerManager;
|
|
243
|
+
if (containers?.getRuntime())
|
|
244
|
+
break;
|
|
245
|
+
app.setPluginStatus('Waiting for container runtime detection...');
|
|
246
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
247
|
+
}
|
|
248
|
+
if (!containers) {
|
|
249
|
+
app.setPluginError('signalk-container plugin required for managed mode. Install it or set managedContainer=false.');
|
|
250
|
+
throw new Error('Container manager not available');
|
|
251
|
+
}
|
|
252
|
+
if (!containers.getRuntime()) {
|
|
253
|
+
app.setPluginError('No container runtime detected. Check signalk-container plugin.');
|
|
254
|
+
throw new Error('Container runtime not detected');
|
|
255
|
+
}
|
|
256
|
+
app.debug('Container runtime ready, starting mayara-server');
|
|
257
|
+
app.setPluginStatus('Starting mayara-server container...');
|
|
258
|
+
const args = settings.mayaraArgs ?? [];
|
|
259
|
+
await containers.ensureRunning('mayara-server', {
|
|
260
|
+
image: MAYARA_IMAGE,
|
|
261
|
+
tag: settings.mayaraVersion ?? 'latest',
|
|
262
|
+
networkMode: 'host',
|
|
263
|
+
command: args.length > 0 ? ['mayara-server', ...args] : undefined,
|
|
264
|
+
restart: 'unless-stopped'
|
|
265
|
+
});
|
|
266
|
+
app.debug('mayara-server container ready');
|
|
267
|
+
}
|
|
268
|
+
async function connectAndDiscover(settings) {
|
|
269
|
+
if (!client)
|
|
270
|
+
return;
|
|
185
271
|
try {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}, pollMs)
|
|
204
|
-
|
|
205
|
-
} catch (e) {
|
|
206
|
-
// Still disconnected, keep trying
|
|
207
|
-
app.debug(`Reconnect failed: ${e.message}`)
|
|
272
|
+
const radars = await client.getRadars();
|
|
273
|
+
isConnected = true;
|
|
274
|
+
const radarIds = Object.keys(radars);
|
|
275
|
+
app.setPluginStatus(`Connected - ${radarIds.length} radar(s) found`);
|
|
276
|
+
updateRadars(radarIds, settings);
|
|
277
|
+
const pollMs = (settings.discoveryPollInterval || 10) * 1000;
|
|
278
|
+
discoveryInterval = setInterval(() => {
|
|
279
|
+
void pollForRadarChanges(settings);
|
|
280
|
+
}, pollMs);
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
isConnected = false;
|
|
284
|
+
app.setPluginError(`Cannot connect to mayara-server: ${err instanceof Error ? err.message : String(err)}`);
|
|
285
|
+
const reconnectMs = (settings.reconnectInterval || 5) * 1000;
|
|
286
|
+
reconnectInterval = setInterval(() => {
|
|
287
|
+
void attemptReconnect(settings);
|
|
288
|
+
}, reconnectMs);
|
|
208
289
|
}
|
|
209
|
-
}, reconnectMs)
|
|
210
290
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
app.debug('binaryStreamManager not available - spoke streaming disabled')
|
|
291
|
+
async function attemptReconnect(settings) {
|
|
292
|
+
if (!client)
|
|
293
|
+
return;
|
|
294
|
+
try {
|
|
295
|
+
const radars = await client.getRadars();
|
|
296
|
+
isConnected = true;
|
|
297
|
+
const radarIds = Object.keys(radars);
|
|
298
|
+
app.setPluginStatus(`Connected - ${radarIds.length} radar(s) found`);
|
|
299
|
+
if (reconnectInterval) {
|
|
300
|
+
clearInterval(reconnectInterval);
|
|
301
|
+
reconnectInterval = null;
|
|
302
|
+
}
|
|
303
|
+
updateRadars(radarIds, settings);
|
|
304
|
+
if (discoveryInterval) {
|
|
305
|
+
clearInterval(discoveryInterval);
|
|
306
|
+
}
|
|
307
|
+
const pollMs = (settings.discoveryPollInterval || 10) * 1000;
|
|
308
|
+
discoveryInterval = setInterval(() => {
|
|
309
|
+
void pollForRadarChanges(settings);
|
|
310
|
+
}, pollMs);
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
// Still disconnected, keep trying
|
|
235
314
|
}
|
|
236
|
-
}
|
|
237
315
|
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
316
|
+
function updateRadars(radarIds, settings) {
|
|
317
|
+
if (!client)
|
|
318
|
+
return;
|
|
319
|
+
const currentIds = new Set(radarIds);
|
|
320
|
+
for (const radarId of currentIds) {
|
|
321
|
+
if (!knownRadars.has(radarId)) {
|
|
322
|
+
app.debug(`New radar discovered: ${radarId}`);
|
|
323
|
+
knownRadars.add(radarId);
|
|
324
|
+
if (app.binaryStreamManager) {
|
|
325
|
+
const forwarder = new spoke_forwarder_1.SpokeForwarder({
|
|
326
|
+
radarId,
|
|
327
|
+
url: client.getSpokeStreamUrl(radarId),
|
|
328
|
+
binaryStreamManager: app.binaryStreamManager,
|
|
329
|
+
debug: app.debug.bind(app),
|
|
330
|
+
reconnectInterval: (settings.reconnectInterval || 5) * 1000
|
|
331
|
+
});
|
|
332
|
+
spokeForwarders.set(radarId, forwarder);
|
|
333
|
+
forwarder.start();
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
app.debug('binaryStreamManager not available - spoke streaming disabled');
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
for (const radarId of knownRadars) {
|
|
341
|
+
if (!currentIds.has(radarId)) {
|
|
342
|
+
app.debug(`Radar disconnected: ${radarId}`);
|
|
343
|
+
knownRadars.delete(radarId);
|
|
344
|
+
const forwarder = spokeForwarders.get(radarId);
|
|
345
|
+
if (forwarder) {
|
|
346
|
+
forwarder.stop();
|
|
347
|
+
spokeForwarders.delete(radarId);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
250
350
|
}
|
|
251
|
-
}
|
|
252
351
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
const radars = await client.getRadars()
|
|
258
|
-
const radarIds = Object.keys(radars)
|
|
259
|
-
|
|
260
|
-
await updateRadars(radarIds, settings)
|
|
261
|
-
|
|
262
|
-
app.setPluginStatus(`Connected - ${radarIds.length} radar(s)`)
|
|
263
|
-
|
|
264
|
-
} catch (err) {
|
|
265
|
-
// Lost connection
|
|
266
|
-
isConnected = false
|
|
267
|
-
app.setPluginError(`Lost connection: ${err.message}`)
|
|
268
|
-
|
|
269
|
-
// Stop discovery polling
|
|
270
|
-
if (discoveryInterval) {
|
|
271
|
-
clearInterval(discoveryInterval)
|
|
272
|
-
discoveryInterval = null
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Start reconnect timer
|
|
276
|
-
const reconnectMs = (settings.reconnectInterval || 5) * 1000
|
|
277
|
-
reconnectInterval = setInterval(async () => {
|
|
352
|
+
async function pollForRadarChanges(settings) {
|
|
353
|
+
if (!client)
|
|
354
|
+
return;
|
|
278
355
|
try {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
pollForRadarChanges(settings)
|
|
296
|
-
}, pollMs)
|
|
297
|
-
|
|
298
|
-
} catch (e) {
|
|
299
|
-
// Still disconnected
|
|
356
|
+
const radars = await client.getRadars();
|
|
357
|
+
const radarIds = Object.keys(radars);
|
|
358
|
+
updateRadars(radarIds, settings);
|
|
359
|
+
app.setPluginStatus(`Connected - ${radarIds.length} radar(s)`);
|
|
360
|
+
}
|
|
361
|
+
catch (err) {
|
|
362
|
+
isConnected = false;
|
|
363
|
+
app.setPluginError(`Lost connection: ${err instanceof Error ? err.message : String(err)}`);
|
|
364
|
+
if (discoveryInterval) {
|
|
365
|
+
clearInterval(discoveryInterval);
|
|
366
|
+
discoveryInterval = null;
|
|
367
|
+
}
|
|
368
|
+
const reconnectMs = (settings.reconnectInterval || 5) * 1000;
|
|
369
|
+
reconnectInterval = setInterval(() => {
|
|
370
|
+
void attemptReconnect(settings);
|
|
371
|
+
}, reconnectMs);
|
|
300
372
|
}
|
|
301
|
-
}, reconnectMs)
|
|
302
373
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
}
|
|
374
|
+
return plugin;
|
|
375
|
+
};
|
|
376
|
+
//# sourceMappingURL=index.js.map
|