signalk-polar-performance-plugin 0.0.59 → 0.0.60
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/index.js +14 -2
- package/public/index.html +132 -30
- package/ratio_off.dump +8047 -0
package/package.json
CHANGED
package/plugin/index.js
CHANGED
|
@@ -116,13 +116,25 @@ module.exports = function (app) {
|
|
|
116
116
|
plugin.registerWithRouter = function (router) {
|
|
117
117
|
// Will appear here; plugins/signalk-polar-performance-plugin/
|
|
118
118
|
app.debug('registerWithRouter')
|
|
119
|
+
// Middleware to set Origin-Agent-Cluster on every response from this plugin
|
|
120
|
+
router.use((req, res, next) => {
|
|
121
|
+
res.set('Origin-Agent-Cluster', '?1')
|
|
122
|
+
next()
|
|
123
|
+
})
|
|
119
124
|
router.get('/polar', (req, res) => {
|
|
120
125
|
res.contentType('application/json')
|
|
121
126
|
res.send(JSON.stringify(polar))
|
|
122
127
|
})
|
|
123
128
|
router.get('/chartData', (req, res) => {
|
|
124
|
-
|
|
125
|
-
|
|
129
|
+
const chart = getChartData();
|
|
130
|
+
if (!chart || !chart.datasets || chart.datasets.length === 0) {
|
|
131
|
+
return res.status(500).json({
|
|
132
|
+
error: "No polar data",
|
|
133
|
+
reason: "Polar CSV is empty or invalid. Please configure a valid polar in plugin settings."
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
res.contentType('application/json');
|
|
137
|
+
res.send(JSON.stringify(chart));
|
|
126
138
|
})
|
|
127
139
|
}
|
|
128
140
|
|
package/public/index.html
CHANGED
|
@@ -35,6 +35,26 @@
|
|
|
35
35
|
padding: 4px;
|
|
36
36
|
font-size: 14px;
|
|
37
37
|
}
|
|
38
|
+
|
|
39
|
+
#error-banner {
|
|
40
|
+
position: fixed;
|
|
41
|
+
bottom: 0;
|
|
42
|
+
left: 0;
|
|
43
|
+
right: 0;
|
|
44
|
+
background: rgba(220, 60, 60, 0.95);
|
|
45
|
+
color: white;
|
|
46
|
+
text-align: center;
|
|
47
|
+
padding: 14px 20px;
|
|
48
|
+
font-size: 15px;
|
|
49
|
+
z-index: 1000;
|
|
50
|
+
box-shadow: 0 -2px 8px rgba(0,0,0,0.3);
|
|
51
|
+
display: none;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
#error-banner.show {
|
|
55
|
+
display: block;
|
|
56
|
+
}
|
|
57
|
+
|
|
38
58
|
</style>
|
|
39
59
|
</head>
|
|
40
60
|
<body>
|
|
@@ -47,12 +67,17 @@
|
|
|
47
67
|
<label for="labelPos">Place polar wind speed label at TWA:</label>
|
|
48
68
|
<input type="number" id="labelPos" value="135" min="0" max="180" onchange="updateLabelPos(this.value)">
|
|
49
69
|
</div>
|
|
70
|
+
|
|
71
|
+
<div id="error-banner"></div>
|
|
50
72
|
</div>
|
|
51
73
|
|
|
52
74
|
<script>
|
|
53
75
|
|
|
54
|
-
var polarSpeed, boatSpeed, TWA, TWS
|
|
55
76
|
var myChart, chartData
|
|
77
|
+
let ws
|
|
78
|
+
let polarSpeed = 0, boatSpeed = 0, TWA = 0, TWS = 0
|
|
79
|
+
let dataReceived = { polar: false, boatSpeed: false, twa: false }
|
|
80
|
+
let dataTimeout
|
|
56
81
|
|
|
57
82
|
// GLOBAL CONFIG: Load from storage or default to 135
|
|
58
83
|
window.labelTWA = localStorage.getItem('polarLabelTWA') || 135;
|
|
@@ -143,41 +168,80 @@ Chart.plugins.register({
|
|
|
143
168
|
}
|
|
144
169
|
});
|
|
145
170
|
|
|
146
|
-
$.getJSON("/plugins/signalk-polar-performance-plugin/chartData"
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
borderColor: 'rgba(0,255,0,0.2)',
|
|
153
|
-
borderWidth: 1,
|
|
154
|
-
radius: 10,
|
|
155
|
-
pointStyle: 'circle',
|
|
156
|
-
data: [{ y: 0, x: 0, r: 10 }]
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
label: 'Boat Speed',
|
|
160
|
-
backgroundColor: 'rgba(0,150,255,0.7)',
|
|
161
|
-
borderColor: 'rgba(0,150,255,0.2)',
|
|
162
|
-
borderWidth: 1,
|
|
163
|
-
radius: 10,
|
|
164
|
-
pointStyle: 'circle',
|
|
165
|
-
data: [{ y: 0, x: 0, r: 10 }]
|
|
166
|
-
})
|
|
171
|
+
$.getJSON("/plugins/signalk-polar-performance-plugin/chartData")
|
|
172
|
+
.done(function(json) {
|
|
173
|
+
if (!json || !json.datasets || json.datasets.length < 3) {
|
|
174
|
+
showError("No polar diagram data available.<br><br>Make sure you have entered a valid polar CSV in the plugin settings.");
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
167
177
|
|
|
168
|
-
|
|
169
|
-
|
|
178
|
+
// Add actual values dots (Polar Speed + Boat Speed)
|
|
179
|
+
json.datasets.unshift({
|
|
180
|
+
label: 'Polar Speed',
|
|
181
|
+
backgroundColor: 'rgba(0,255,0,0.7)',
|
|
182
|
+
borderColor: 'rgba(0,255,0,0.2)',
|
|
183
|
+
borderWidth: 1,
|
|
184
|
+
radius: 10,
|
|
185
|
+
pointStyle: 'circle',
|
|
186
|
+
data: [{ y: 0, x: 0, r: 10 }]
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
label: 'Boat Speed',
|
|
190
|
+
backgroundColor: 'rgba(0,150,255,0.7)',
|
|
191
|
+
borderColor: 'rgba(0,150,255,0.2)',
|
|
192
|
+
borderWidth: 1,
|
|
193
|
+
radius: 10,
|
|
194
|
+
pointStyle: 'circle',
|
|
195
|
+
data: [{ y: 0, x: 0, r: 10 }]
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Backup original colors
|
|
199
|
+
for (var i = 2; i < json.datasets.length; i++) {
|
|
170
200
|
if (!json.datasets[i].borderColor) {
|
|
171
|
-
|
|
201
|
+
json.datasets[i].borderColor = 'rgba(100, 100, 100, 0.5)';
|
|
172
202
|
}
|
|
173
203
|
json.datasets[i]._originalColor = json.datasets[i].borderColor;
|
|
174
204
|
json.datasets[i].borderWidth = 1;
|
|
175
|
-
|
|
205
|
+
}
|
|
176
206
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
})
|
|
207
|
+
chartData = json;
|
|
208
|
+
showChart(chartData);
|
|
209
|
+
})
|
|
210
|
+
.fail(function(jqXHR, textStatus, errorThrown) {
|
|
211
|
+
let reason = "Unknown error";
|
|
212
|
+
|
|
213
|
+
if (jqXHR.status === 404) {
|
|
214
|
+
reason = "Plugin endpoint not found — is the plugin enabled?";
|
|
215
|
+
} else if (jqXHR.status === 500) {
|
|
216
|
+
reason = "Server error — check plugin configuration (invalid CSV polar?)";
|
|
217
|
+
} else if (textStatus === "parsererror") {
|
|
218
|
+
reason = "Invalid data received from server";
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
showError(`Failed to load polar data.<br><br>Reason: ${reason}`);
|
|
222
|
+
});
|
|
180
223
|
|
|
224
|
+
// New error display function
|
|
225
|
+
function showError(message) {
|
|
226
|
+
let banner = document.getElementById('error-banner');
|
|
227
|
+
|
|
228
|
+
if (!banner) {
|
|
229
|
+
// Fallback: create banner dynamically if missing
|
|
230
|
+
banner = document.createElement('div');
|
|
231
|
+
banner.id = 'error-banner';
|
|
232
|
+
document.body.appendChild(banner);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
banner.innerHTML = `
|
|
236
|
+
⚠️ ${message}
|
|
237
|
+
|
|
238
|
+
<button onclick="location.reload()" style="background:white; color:#c33; border:none; padding:4px 10px; border-radius:4px; cursor:pointer;">
|
|
239
|
+
Reload
|
|
240
|
+
</button>
|
|
241
|
+
`;
|
|
242
|
+
|
|
243
|
+
banner.classList.add('show');
|
|
244
|
+
}
|
|
181
245
|
function showChart (data) {
|
|
182
246
|
console.log(data)
|
|
183
247
|
myChart = new Chart("myChart", {
|
|
@@ -293,6 +357,11 @@ function startListeners () {
|
|
|
293
357
|
}
|
|
294
358
|
|
|
295
359
|
ws.send(JSON.stringify(subscriptionObject));
|
|
360
|
+
|
|
361
|
+
// Start timeout to detect missing data
|
|
362
|
+
dataReceived = { polar: false, boatSpeed: false, twa: false, tws: false };
|
|
363
|
+
clearTimeout(dataTimeout);
|
|
364
|
+
dataTimeout = setTimeout(checkDataReceived, 20000);
|
|
296
365
|
}
|
|
297
366
|
|
|
298
367
|
function handleData (data) {
|
|
@@ -304,18 +373,51 @@ function handleData (data) {
|
|
|
304
373
|
if (path == 'performance.polarSpeed') {
|
|
305
374
|
polarSpeed = roundDec(msToKts(value), 1)
|
|
306
375
|
chartData.datasets[0].data[0].y = polarSpeed
|
|
307
|
-
|
|
376
|
+
dataReceived.polar = true;
|
|
377
|
+
} else if (path === 'environment.wind.angleTrueWaterDamped') {
|
|
308
378
|
TWA = roundDec(Math.abs(radToDeg(value)),1)
|
|
309
379
|
chartData.datasets[0].data[0].x = TWA
|
|
310
380
|
chartData.datasets[1].data[0].x = TWA
|
|
381
|
+
dataReceived.twa = true;
|
|
311
382
|
} else if (path == 'performance.boatSpeedDamped') {
|
|
312
383
|
boatSpeed = roundDec(msToKts(value),1)
|
|
313
384
|
chartData.datasets[1].data[0].y = boatSpeed
|
|
385
|
+
dataReceived.boatSpeed = true;
|
|
314
386
|
} else if (path == 'environment.wind.speedTrue') {
|
|
315
387
|
TWS = msToKts(value);
|
|
388
|
+
dataReceived.tws = true;
|
|
316
389
|
}
|
|
317
390
|
}
|
|
318
391
|
|
|
392
|
+
function checkDataReceived() {
|
|
393
|
+
let missing = [];
|
|
394
|
+
|
|
395
|
+
if (!dataReceived.polar) missing.push("Polar Speed (performance.polarSpeed)");
|
|
396
|
+
if (!dataReceived.boatSpeed) missing.push("Boat Speed (performance.boatSpeedDamped)");
|
|
397
|
+
if (!dataReceived.twa) missing.push("True Wind Angle (environment.wind.angleTrueWater)");
|
|
398
|
+
if (!dataReceived.tws) missing.push("True Wind Speed (environment.wind.speedTrue)");
|
|
399
|
+
|
|
400
|
+
if (missing.length > 0) {
|
|
401
|
+
showError(`Missing data: ${missing.join(', ')}. Check your instruments and polar settings.`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Enhanced error display
|
|
406
|
+
function showError(message) {
|
|
407
|
+
const container = document.querySelector('.chart-container');
|
|
408
|
+
container.innerHTML = `
|
|
409
|
+
<div style="padding: 40px 20px; text-align: center; color: #ff7777;">
|
|
410
|
+
<h2 style="color: #ff6666;">⚠️ Cannot display Polar Performance</h2>
|
|
411
|
+
<div style="margin: 25px 0; line-height: 1.6; font-size: 17px;">
|
|
412
|
+
${message}
|
|
413
|
+
</div>
|
|
414
|
+
<button onclick="location.reload()"
|
|
415
|
+
style="padding: 12px 24px; font-size: 16px; background: #333; color: white; border: 1px solid #666; border-radius: 6px; cursor: pointer;">
|
|
416
|
+
Reload Page
|
|
417
|
+
</button>
|
|
418
|
+
</div>
|
|
419
|
+
`;
|
|
420
|
+
}
|
|
319
421
|
|
|
320
422
|
connect()
|
|
321
423
|
|