@sailingrotevista/rotevista-dash 1.0.19 → 1.0.21
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/app.js +74 -59
- package/index.js +54 -23
- package/package.json +1 -1
package/app.js
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
1
|
// ==========================================================================
|
|
2
|
-
// 1. CONFIGURAZIONE
|
|
2
|
+
// 1. CONFIGURAZIONE E COSTANTI
|
|
3
3
|
// ==========================================================================
|
|
4
4
|
let CONFIG = {
|
|
5
|
-
alarms: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
},
|
|
9
|
-
averages: {
|
|
10
|
-
smoothWindow: 2000,
|
|
11
|
-
longWindow: 60000,
|
|
12
|
-
stabilityTolerance: 2000,
|
|
13
|
-
stabilityThreshold: 0.90,
|
|
14
|
-
minSpeed: 0.5
|
|
15
|
-
},
|
|
16
|
-
graphs: {
|
|
17
|
-
reef1: 15.0, // Soglia Arancio
|
|
18
|
-
reef2: 20.0, // Soglia Rossa
|
|
19
|
-
realInterval: 5000,
|
|
20
|
-
samples: 60
|
|
21
|
-
},
|
|
5
|
+
alarms: { depthDanger: 2.5, depthWarning: 5.0 },
|
|
6
|
+
averages: { smoothWindow: 2000, longWindow: 60000, stabilityTolerance: 2000, stabilityThreshold: 0.90, minSpeed: 0.5 },
|
|
7
|
+
graphs: { reef1: 15.0, reef2: 20.0, realInterval: 5000, samples: 60 },
|
|
22
8
|
scales: {
|
|
23
|
-
stw:
|
|
24
|
-
sog:
|
|
25
|
-
tws:
|
|
9
|
+
stw: { stdMax: 12, hercSpan: 4, step: 2 },
|
|
10
|
+
sog: { stdMax: 12, hercSpan: 4, step: 2 },
|
|
11
|
+
tws: { stdMax: 25, hercSpan: 10, step: 5 },
|
|
26
12
|
depth: { stdMax: 20, hercSpan: 10, step: 10 }
|
|
27
13
|
},
|
|
28
|
-
server: {
|
|
29
|
-
fallbackIp: "192.168.111.240:3000"
|
|
30
|
-
}
|
|
14
|
+
server: { fallbackIp: "192.168.111.240:3000" }
|
|
31
15
|
};
|
|
32
16
|
|
|
33
17
|
const RENDER_INTERVAL_MS = 1000;
|
|
@@ -50,7 +34,8 @@ const graphModes = {
|
|
|
50
34
|
};
|
|
51
35
|
|
|
52
36
|
const store = {
|
|
53
|
-
raw: {},
|
|
37
|
+
raw: {},
|
|
38
|
+
timestamps: {},
|
|
54
39
|
smoothBuf: { hdg: [], cog: [], awa: [], twa: [], twd: [] },
|
|
55
40
|
longBuf: { hdg: [], cog: [], awa: [], twa: [], twd: [] },
|
|
56
41
|
histories: { stw: [], sog: [], depth: [], tws: [] },
|
|
@@ -67,42 +52,54 @@ const ui = {
|
|
|
67
52
|
twdAvg: document.getElementById('twd-avg'), twdArrow: document.getElementById('twd-arrow'),
|
|
68
53
|
leewayMask: document.getElementById('leeway-mask-rect'), leewayVal: document.getElementById('leeway-val'),
|
|
69
54
|
tackHdg: document.getElementById('tack-hdg'), tackCog: document.getElementById('tack-cog'),
|
|
70
|
-
status: document.getElementById('status'),
|
|
71
|
-
hotspot: document.getElementById('fullscreen-hotspot')
|
|
55
|
+
status: document.getElementById('status'), hotspot: document.getElementById('fullscreen-hotspot')
|
|
72
56
|
};
|
|
73
57
|
|
|
74
58
|
// ==========================================================================
|
|
75
|
-
// 3.
|
|
59
|
+
// 3. CARICAMENTO CONFIGURAZIONE (SK-SERVER CONFIG)
|
|
76
60
|
// ==========================================================================
|
|
77
61
|
async function fetchServerConfig() {
|
|
78
62
|
if (!window.location.protocol.includes("http")) return;
|
|
79
63
|
const pluginID = 'rotevista-dash';
|
|
80
|
-
const possibleUrls = [
|
|
81
|
-
`/skServer/plugins/${pluginID}/config`,
|
|
82
|
-
`/plugins/${pluginID}/config`
|
|
83
|
-
];
|
|
64
|
+
const possibleUrls = [`/skServer/plugins/${pluginID}/config`, `/plugins/${pluginID}/config` ];
|
|
84
65
|
|
|
85
66
|
for (let url of possibleUrls) {
|
|
86
67
|
try {
|
|
87
68
|
const response = await fetch(url);
|
|
88
69
|
if (response.ok) {
|
|
89
70
|
const data = await response.json();
|
|
90
|
-
const
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
71
|
+
const actual = data.configuration || data;
|
|
72
|
+
if (actual && typeof actual === 'object') {
|
|
73
|
+
|
|
74
|
+
// Funzione per pulire e convertire i numeri
|
|
75
|
+
const parseNumbers = (obj) => {
|
|
76
|
+
for (let k in obj) {
|
|
77
|
+
if (typeof obj[k] === 'object') parseNumbers(obj[k]);
|
|
78
|
+
else if (!isNaN(obj[k]) && obj[k] !== "") obj[k] = parseFloat(obj[k]);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
parseNumbers(actual);
|
|
82
|
+
|
|
83
|
+
// Merge intelligente dei blocchi
|
|
84
|
+
if (actual.alarms) CONFIG.alarms = { ...CONFIG.alarms, ...actual.alarms };
|
|
85
|
+
if (actual.graphs) CONFIG.graphs = { ...CONFIG.graphs, ...actual.graphs };
|
|
86
|
+
if (actual.averaging) CONFIG.averages = { ...CONFIG.averages, ...actual.averaging }; // Nota: mappiamo averaging -> averages
|
|
87
|
+
if (actual.scales) {
|
|
88
|
+
for (let key in actual.scales) {
|
|
89
|
+
CONFIG.scales[key] = { ...CONFIG.scales[key], ...actual.scales[key] };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log("SUCCESS: Dashboard Config updated from Server:", CONFIG);
|
|
96
94
|
return;
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
97
|
} catch (e) { }
|
|
100
98
|
}
|
|
101
|
-
console.log("Info: Uso configurazione di default.");
|
|
102
99
|
}
|
|
103
100
|
|
|
104
101
|
// ==========================================================================
|
|
105
|
-
// 4. MATEMATICA
|
|
102
|
+
// 4. MATEMATICA VETTORIALE (CIRCULAR AVERAGING)
|
|
106
103
|
// ==========================================================================
|
|
107
104
|
function radToDeg(rad) { return rad * (180 / Math.PI); }
|
|
108
105
|
function degToRad(deg) { return deg * (Math.PI / 180); }
|
|
@@ -110,6 +107,7 @@ function msToKts(ms) { return ms * 1.94384; }
|
|
|
110
107
|
function ktsToMs(kts) { return kts / 1.94384; }
|
|
111
108
|
function getShortestRotation(curr, target) { let diff = (target - curr) % 360; if (diff > 180) diff -= 360; else if (diff < -180) diff += 360; return curr + diff; }
|
|
112
109
|
|
|
110
|
+
// Calcolo media basato su componenti Seno/Coseno (Vettoriale)
|
|
113
111
|
function getCircularAverageFromBuffer(bufferArray, windowMs, signed = false) {
|
|
114
112
|
const now = Date.now();
|
|
115
113
|
const validData = bufferArray.filter(item => (now - item.time) <= windowMs);
|
|
@@ -123,17 +121,32 @@ function getCircularAverageFromBuffer(bufferArray, windowMs, signed = false) {
|
|
|
123
121
|
return { val: signed ? avgDeg : (avgDeg + 360) % 360, stable: isStable };
|
|
124
122
|
}
|
|
125
123
|
|
|
124
|
+
// ==========================================================================
|
|
125
|
+
// 5. GESTIONE DATI IN INGRESSO
|
|
126
|
+
// ==========================================================================
|
|
126
127
|
function processIncomingData(path, val) {
|
|
127
128
|
const now = Date.now(); store.timestamps[path] = now; store.raw[path] = val;
|
|
128
129
|
if (path === "navigation.headingTrue") { store.smoothBuf.hdg.push({ val: val, time: now }); store.longBuf.hdg.push({ val: val, time: now }); }
|
|
129
130
|
if (path === "navigation.courseOverGroundTrue") { store.smoothBuf.cog.push({ val: val, time: now }); store.longBuf.cog.push({ val: val, time: now }); }
|
|
130
131
|
if (path === "environment.wind.angleApparent") { store.smoothBuf.awa.push({ val: val, time: now }); store.longBuf.awa.push({ val: val, time: now }); }
|
|
131
132
|
if (path === "environment.wind.angleTrueWater") { store.smoothBuf.twa.push({ val: val, time: now }); store.longBuf.twa.push({ val: val, time: now }); }
|
|
132
|
-
|
|
133
|
+
|
|
134
|
+
// Calcolo o ricezione TWD (True Wind Direction)
|
|
135
|
+
if (path === "navigation.headingTrue" || path === "environment.wind.angleTrueWater" || path === "environment.wind.directionTrue") {
|
|
136
|
+
let twdRad = 0;
|
|
137
|
+
if (path === "environment.wind.directionTrue") twdRad = val;
|
|
138
|
+
else if (store.raw["navigation.headingTrue"] !== undefined && store.raw["environment.wind.angleTrueWater"] !== undefined) {
|
|
139
|
+
twdRad = (store.raw["navigation.headingTrue"] + store.raw["environment.wind.angleTrueWater"]) % (2 * Math.PI);
|
|
140
|
+
if (twdRad < 0) twdRad += (2 * Math.PI);
|
|
141
|
+
} else return;
|
|
142
|
+
|
|
143
|
+
store.smoothBuf.twd.push({ val: twdRad, time: now });
|
|
144
|
+
store.longBuf.twd.push({ val: twdRad, time: now });
|
|
145
|
+
}
|
|
133
146
|
}
|
|
134
147
|
|
|
135
148
|
// ==========================================================================
|
|
136
|
-
//
|
|
149
|
+
// 6. MOTORE DI RENDERING
|
|
137
150
|
// ==========================================================================
|
|
138
151
|
function startDisplayLoop() {
|
|
139
152
|
renderInterval = setInterval(() => {
|
|
@@ -190,7 +203,7 @@ function startDisplayLoop() {
|
|
|
190
203
|
}
|
|
191
204
|
|
|
192
205
|
// ==========================================================================
|
|
193
|
-
//
|
|
206
|
+
// 7. CONNESSIONE SIGNALK (subscribe=self)
|
|
194
207
|
// ==========================================================================
|
|
195
208
|
function connect() {
|
|
196
209
|
if (simulationMode) return;
|
|
@@ -198,24 +211,19 @@ function connect() {
|
|
|
198
211
|
try {
|
|
199
212
|
socket = new WebSocket(`ws://${addr}/signalk/v1/stream?subscribe=self`);
|
|
200
213
|
socket.onopen = () => { ui.status.className = "online"; ui.status.innerText = "ONLINE"; };
|
|
201
|
-
socket.onmessage = (e) => {
|
|
202
|
-
const d = JSON.parse(e.data);
|
|
203
|
-
if (d.updates) d.updates.forEach(u => u.values && u.values.forEach(v => processIncomingData(v.path, v.value)));
|
|
204
|
-
};
|
|
214
|
+
socket.onmessage = (e) => { const d = JSON.parse(e.data); if (d.updates) d.updates.forEach(u => u.values && u.values.forEach(v => processIncomingData(v.path, v.value))); };
|
|
205
215
|
socket.onclose = () => !simulationMode && setTimeout(connect, 5000);
|
|
206
216
|
} catch (e) { setTimeout(connect, 5000); }
|
|
207
217
|
}
|
|
208
218
|
|
|
209
219
|
// ==========================================================================
|
|
210
|
-
//
|
|
220
|
+
// 8. FUNZIONI GRAFICHE (Hercules Scalable)
|
|
211
221
|
// ==========================================================================
|
|
212
222
|
function updateLeewayDisplay(deg) { const c = 125, px = 125/20; let w = Math.min(Math.abs(deg)*px, 125); ui.leewayMask.setAttribute('x', deg >= 0 ? c : c - w); ui.leewayMask.setAttribute('width', w); ui.leewayVal.textContent = `LEEWAY: ${deg.toFixed(1)}°`; }
|
|
213
223
|
|
|
214
224
|
function manageHistory(t, v) {
|
|
215
225
|
const n = Date.now(), i = simulationMode ? SIM_SAMPLE_INTERVAL : CONFIG.graphs.realInterval;
|
|
216
|
-
if (n - store.lastUpdates[t] > i || store.histories[t].length === 0) {
|
|
217
|
-
store.histories[t].push(v); if (store.histories[t].length > CONFIG.graphs.samples) store.histories[t].shift(); store.lastUpdates[t] = n;
|
|
218
|
-
}
|
|
226
|
+
if (n - store.lastUpdates[t] > i || store.histories[t].length === 0) { store.histories[t].push(v); if (store.histories[t].length > CONFIG.graphs.samples) store.histories[t].shift(); store.lastUpdates[t] = n; }
|
|
219
227
|
const mode = graphModes[t];
|
|
220
228
|
const cfg = calculateScale(t, store.histories[t], mode);
|
|
221
229
|
const box = document.getElementById(t + '-graph').closest('.data-box');
|
|
@@ -245,8 +253,9 @@ function updateScaleLabels(t, min, max) {
|
|
|
245
253
|
|
|
246
254
|
function drawGraph(d, id, min, max, isTws, isHercules) {
|
|
247
255
|
const svg = document.getElementById(id); if (!svg || d.length < 2) return;
|
|
248
|
-
const w = 200, h = 40, range = max - min;
|
|
256
|
+
const w = 200, h = 40, range = max - min || 1;
|
|
249
257
|
let gH = ""; [0.25, 0.5, 0.75].forEach(p => { gH += `<line x1="0" y1="${h-(p*h)}" x2="${w}" y2="${h-(p*h)}" stroke="rgba(255,255,255,0.08)" stroke-width="0.5" />`; });
|
|
258
|
+
|
|
250
259
|
let pD = "", cS = "";
|
|
251
260
|
d.forEach((v, i) => {
|
|
252
261
|
const x = (i/(CONFIG.graphs.samples-1))*w, y = h-(Math.max(0,Math.min(1,(v-min)/range))*h); pD += `${i===0?'M':'L'} ${x} ${y} `;
|
|
@@ -256,18 +265,25 @@ function drawGraph(d, id, min, max, isTws, isHercules) {
|
|
|
256
265
|
cS += `<line x1="${px}" y1="${py}" x2="${x}" y2="${y}" stroke="${c}" stroke-width="${isHercules?3:2}" class="${isHercules?'line-hercules':''}" />`;
|
|
257
266
|
}
|
|
258
267
|
});
|
|
268
|
+
|
|
269
|
+
const areaPath = pD + ` L ${((d.length-1)/(CONFIG.graphs.samples-1))*w} ${h} L 0 ${h} Z`;
|
|
259
270
|
const clrs = { 'stw-graph': '#2ecc71', 'sog-graph': '#f39c12', 'depth-graph': '#3498db', 'tws-graph': '#f1c40f' };
|
|
260
|
-
|
|
271
|
+
const lClass = isHercules ? 'line-hercules' : '';
|
|
272
|
+
|
|
273
|
+
if (isTws) {
|
|
274
|
+
svg.innerHTML = `${gH}<path d="${areaPath}" fill="rgba(241,196,15,0.1)" stroke="none" />${cS}`;
|
|
275
|
+
} else {
|
|
276
|
+
svg.innerHTML = `${gH}<path d="${areaPath}" fill="${clrs[id]}22" stroke="none" /><path d="${pD}" class="${lClass}" fill="none" stroke="${clrs[id]}" stroke-width="1.5" />`;
|
|
277
|
+
}
|
|
261
278
|
}
|
|
262
279
|
|
|
263
280
|
// ==========================================================================
|
|
264
|
-
//
|
|
281
|
+
// 9. EVENTI E INIZIALIZZAZIONE
|
|
265
282
|
// ==========================================================================
|
|
266
283
|
['stw', 'sog', 'tws', 'depth'].forEach(type => {
|
|
267
284
|
const el = document.getElementById(type + '-graph').closest('.data-box');
|
|
268
285
|
el.addEventListener('dblclick', (e) => {
|
|
269
|
-
e.preventDefault();
|
|
270
|
-
graphModes[type] = graphModes[type] === 'standard' ? 'hercules' : 'standard';
|
|
286
|
+
e.preventDefault(); graphModes[type] = graphModes[type] === 'standard' ? 'hercules' : 'standard';
|
|
271
287
|
localStorage.setItem('mode_' + type, graphModes[type]);
|
|
272
288
|
el.style.backgroundColor = "rgba(255,255,255,0.15)"; setTimeout(() => el.style.backgroundColor = "", 200);
|
|
273
289
|
});
|
|
@@ -286,7 +302,7 @@ if (ui.hotspot) {
|
|
|
286
302
|
async function init() { await fetchServerConfig(); startDisplayLoop(); connect(); }
|
|
287
303
|
window.addEventListener('load', init);
|
|
288
304
|
|
|
289
|
-
function checkDepthAlarm(
|
|
305
|
+
function checkDepthAlarm(d) { ui.depth.classList.remove('alarm-warning', 'alarm-danger'); if (d < CONFIG.alarms.depthDanger) { ui.depth.classList.add('alarm-danger'); playBingBing(); } else if (d < CONFIG.alarms.depthWarning) ui.depth.classList.add('alarm-warning'); }
|
|
290
306
|
function playBingBing() { if (!audioCtx) return; const n = Date.now(); if (n - lastAlarmTime < 3000) return; lastAlarmTime = n; function b(f, s) { const o = audioCtx.createOscillator(); const g = audioCtx.createGain(); o.connect(g); g.connect(audioCtx.destination); o.frequency.value = f; g.gain.setValueAtTime(0.1, s); g.gain.exponentialRampToValueAtTime(0.01, s + 0.4); o.start(s); o.stop(s + 0.5); } b(880, audioCtx.currentTime); b(880, audioCtx.currentTime + 0.6); }
|
|
291
307
|
|
|
292
308
|
// Simulatore (Triple click su Depth)
|
|
@@ -294,7 +310,6 @@ ui.depth.closest('.data-box').addEventListener('click', (function() {
|
|
|
294
310
|
let dC = 0, lC = 0;
|
|
295
311
|
return function() {
|
|
296
312
|
const n = Date.now(); if (n - lC < 500) dC++; else dC = 1; lC = n;
|
|
297
|
-
if (dC === 3) { simulationMode = !simulationMode; if (simulationMode) { if (socket) socket.close(); ui.status.innerText = "SIM ATTIVO"; simInterval = setInterval(() => {
|
|
313
|
+
if (dC === 3) { simulationMode = !simulationMode; if (simulationMode) { if (socket) socket.close(); ui.status.innerText = "SIM ATTIVO"; simInterval = setInterval(() => { processIncomingData("navigation.headingTrue", degToRad(Math.random()*360)); processIncomingData("navigation.speedOverGround", ktsToMs(8)); }, 200); } else location.reload(); dC = 0; }
|
|
298
314
|
};
|
|
299
315
|
})());
|
|
300
|
-
---Fine Codice ---
|
package/index.js
CHANGED
|
@@ -1,45 +1,76 @@
|
|
|
1
1
|
module.exports = function (app) {
|
|
2
2
|
const plugin = {};
|
|
3
|
+
plugin.id = 'rotevista-dash';
|
|
4
|
+
plugin.name = 'Rotevista Dash Configuration';
|
|
3
5
|
|
|
4
|
-
plugin.
|
|
5
|
-
plugin.
|
|
6
|
-
plugin.description = 'Impostazioni centralizzate per la Dashboard Rotevista';
|
|
6
|
+
plugin.start = function (options) { };
|
|
7
|
+
plugin.stop = function () { };
|
|
7
8
|
|
|
8
|
-
plugin.start = function (options, restartServer) {
|
|
9
|
-
app.debug('Plugin Rotevista Dash avviato con opzioni:', options);
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
plugin.stop = function () {
|
|
13
|
-
app.debug('Plugin Rotevista Dash fermato');
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
// Schema conforme alla doc di SignalK per generare la UI
|
|
17
9
|
plugin.schema = {
|
|
18
10
|
type: 'object',
|
|
19
|
-
title: '
|
|
11
|
+
title: 'Rotevista Dashboard Settings',
|
|
20
12
|
properties: {
|
|
21
13
|
alarms: {
|
|
22
14
|
type: 'object',
|
|
23
|
-
title: '
|
|
15
|
+
title: 'Depth Alarms (meters)',
|
|
24
16
|
properties: {
|
|
25
|
-
depthDanger: { type: 'number', title: '
|
|
26
|
-
depthWarning: { type: 'number', title: '
|
|
17
|
+
depthDanger: { type: 'number', title: 'Danger Threshold (Red + Sound)', default: 2.5 },
|
|
18
|
+
depthWarning: { type: 'number', title: 'Warning Threshold (Yellow)', default: 5.0 }
|
|
27
19
|
}
|
|
28
20
|
},
|
|
29
21
|
graphs: {
|
|
30
22
|
type: 'object',
|
|
31
|
-
title: '
|
|
23
|
+
title: 'Wind Reef Alerts (TWS Knots)',
|
|
24
|
+
properties: {
|
|
25
|
+
reef1: { type: 'number', title: '1st Reef Threshold (Orange Line)', default: 15.0 },
|
|
26
|
+
reef2: { type: 'number', title: '2nd Reef Threshold (Red Line)', default: 20.0 }
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
averaging: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
title: 'Averaging & Stability',
|
|
32
32
|
properties: {
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
longWindow: { type: 'number', title: 'Long Average Window (ms)', default: 60000 },
|
|
34
|
+
smoothWindow: { type: 'number', title: 'Pointer Smoothing Window (ms)', default: 2000 },
|
|
35
|
+
minSpeed: { type: 'number', title: 'Min Speed for Stability (knots)', default: 0.5 }
|
|
35
36
|
}
|
|
36
37
|
},
|
|
37
|
-
|
|
38
|
+
scales: {
|
|
38
39
|
type: 'object',
|
|
39
|
-
title: '
|
|
40
|
+
title: 'Graph Scale Configurations',
|
|
40
41
|
properties: {
|
|
41
|
-
|
|
42
|
-
|
|
42
|
+
stw: {
|
|
43
|
+
type: 'object', title: 'STW (Speed Through Water)',
|
|
44
|
+
properties: {
|
|
45
|
+
stdMax: { type: 'number', title: 'Standard Mode Max Value', default: 12 },
|
|
46
|
+
hercSpan: { type: 'number', title: 'Hercules Mode Zoom Span', default: 4 },
|
|
47
|
+
step: { type: 'number', title: 'Rounding Step', default: 2 }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
sog: {
|
|
51
|
+
type: 'object', title: 'SOG (Speed Over Ground)',
|
|
52
|
+
properties: {
|
|
53
|
+
stdMax: { type: 'number', title: 'Standard Mode Max Value', default: 12 },
|
|
54
|
+
hercSpan: { type: 'number', title: 'Hercules Mode Zoom Span', default: 4 },
|
|
55
|
+
step: { type: 'number', title: 'Rounding Step', default: 2 }
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
tws: {
|
|
59
|
+
type: 'object', title: 'TWS (True Wind Speed)',
|
|
60
|
+
properties: {
|
|
61
|
+
stdMax: { type: 'number', title: 'Standard Mode Max Value', default: 25 },
|
|
62
|
+
hercSpan: { type: 'number', title: 'Hercules Mode Zoom Span', default: 10 },
|
|
63
|
+
step: { type: 'number', title: 'Rounding Step', default: 5 }
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
depth: {
|
|
67
|
+
type: 'object', title: 'Depth',
|
|
68
|
+
properties: {
|
|
69
|
+
stdMax: { type: 'number', title: 'Standard Mode Max Value', default: 20 },
|
|
70
|
+
hercSpan: { type: 'number', title: 'Hercules Mode Zoom Span', default: 10 },
|
|
71
|
+
step: { type: 'number', title: 'Rounding Step', default: 10 }
|
|
72
|
+
}
|
|
73
|
+
}
|
|
43
74
|
}
|
|
44
75
|
}
|
|
45
76
|
}
|