feat(viewer): merge USV M1+M2 into one graph, add history window control
- USV panel: usv-m1 + usv-m2 → single usv-motors multi-trace (2 lines) - AUV panel: auv-motors already combined (6 lines), no change - Add window dropdown (10s/30s/60s/5min/15min/ALL) next to trail control - updateCursor applies xaxis.range=[T-win, T] on all panel graphs - Plots before: ~9 USV + ~9 AUV = ~18; after: ~7 USV + ~8 AUV = ~15
This commit is contained in:
@@ -306,6 +306,15 @@ flowchart LR
|
||||
</select>
|
||||
<button id="btn-viewall" onclick="viewAll()">View all</button>
|
||||
<button id="btn-play">▶</button>
|
||||
<label for="window-select" style="font-size:10px;color:#666;white-space:nowrap;">win</label>
|
||||
<select id="window-select" style="background:#0f3460;border:1px solid #a855f7;color:#a855f7;font-family:monospace;font-size:11px;padding:2px 6px;border-radius:2px;cursor:pointer;">
|
||||
<option value="10000">10s</option>
|
||||
<option value="30000">30s</option>
|
||||
<option value="60000" selected>60s</option>
|
||||
<option value="300000">5min</option>
|
||||
<option value="900000">15min</option>
|
||||
<option value="0">ALL</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="ctrl-row2">
|
||||
<span id="cursor-info" style="font-size:10px;color:#888;flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">—</span>
|
||||
@@ -340,8 +349,7 @@ flowchart LR
|
||||
<div class="graph-cell" id="usv-gps"></div>
|
||||
<div class="graph-cell" id="usv-usbl-dist"></div>
|
||||
<div class="graph-cell" id="usv-usbl-angle"></div>
|
||||
<div class="graph-cell" id="usv-m1"></div>
|
||||
<div class="graph-cell" id="usv-m2"></div>
|
||||
<div class="graph-cell wide" id="usv-motors"></div>
|
||||
<div class="graph-cell wide" id="usv-status"></div>
|
||||
</div>
|
||||
<div class="panel-header" id="auv-panel-header">
|
||||
@@ -981,11 +989,13 @@ function renderUSV(signals) {
|
||||
const [at, av] = _pts(signals.usbl_angle);
|
||||
Plotly.react('usv-usbl-angle', [{x:at, y:av, type:'scatter', mode:'lines', line:{color:'#c77dff',width:1}}], _layout('USBL angle','°'), cfg);
|
||||
|
||||
const [m1t, m1v] = _pts(signals.M1);
|
||||
Plotly.react('usv-m1', [{x:m1t, y:m1v, type:'scatter', mode:'lines', line:{color:'#ef476f',width:1}}], _layout('Motor 1','cmd'), cfg);
|
||||
|
||||
const [m2t, m2v] = _pts(signals.M2);
|
||||
Plotly.react('usv-m2', [{x:m2t, y:m2v, type:'scatter', mode:'lines', line:{color:'#ff6b6b',width:1}}], _layout('Motor 2','cmd'), cfg);
|
||||
const motorColorsUSV = ['#ef476f','#ff8800'];
|
||||
const motorTracesUSV = ['M1','M2'].map((mk,i) => {
|
||||
const [t,v] = _pts(signals[mk]);
|
||||
return {x:t,y:v,type:'scatter',mode:'lines',name:mk,line:{color:motorColorsUSV[i],width:1}};
|
||||
});
|
||||
Plotly.react('usv-motors', motorTracesUSV,
|
||||
Object.assign(_layout('Motors USV','cmd'), {showlegend:true, legend:{font:{size:8},bgcolor:'transparent',orientation:'h',x:0,y:1}}), cfg);
|
||||
|
||||
const armPts = _pts(signals.Armed);
|
||||
const modePts = _pts(signals.Mode);
|
||||
@@ -1066,24 +1076,32 @@ function renderAUV(signals) {
|
||||
const ALL_GRAPH_IDS = [
|
||||
'chart-depth', 'chart-pwm-auv', 'chart-pwm-usv', 'chart-usbl',
|
||||
'usv-yaw', 'usv-heading', 'usv-batt', 'usv-gps',
|
||||
'usv-usbl-dist', 'usv-usbl-angle', 'usv-m1', 'usv-m2', 'usv-status',
|
||||
'usv-usbl-dist', 'usv-usbl-angle', 'usv-motors', 'usv-status',
|
||||
'auv-pry', 'auv-depth', 'auv-alt', 'auv-obs',
|
||||
'auv-usbl-dist', 'auv-usbl-angle', 'auv-batt', 'auv-status', 'auv-motors',
|
||||
];
|
||||
|
||||
function updateCursor(epochSec) {
|
||||
const ts = new Date(epochSec * 1000).toISOString();
|
||||
const tMs = epochSec * 1000;
|
||||
const winMs = +document.getElementById('window-select').value;
|
||||
const t0 = winMs === 0 ? null : new Date(tMs - winMs).toISOString();
|
||||
const t1 = new Date(tMs).toISOString();
|
||||
const shape = {
|
||||
type: 'line', x0: ts, x1: ts, y0: 0, y1: 1,
|
||||
yref: 'paper', line: {color: '#e94560', width: 1, dash: 'dot'},
|
||||
};
|
||||
const rangeUpdate = t0 ? {'xaxis.range': [t0, t1]} : {'xaxis.autorange': true};
|
||||
ALL_GRAPH_IDS.forEach(id => {
|
||||
const el = document.getElementById(id);
|
||||
if (el && el._fullLayout) {
|
||||
Plotly.relayout(id, {'shapes': [shape]});
|
||||
Plotly.relayout(id, {...rangeUpdate, 'shapes': [shape]});
|
||||
}
|
||||
});
|
||||
}
|
||||
document.getElementById('window-select').addEventListener('change', () => {
|
||||
if (tNow) updateCursor(tNow / 1000);
|
||||
});
|
||||
|
||||
// == Task 11: loadSortieData + sorties loading + wiring ==
|
||||
async function loadSortieData(sortieId) {
|
||||
|
||||
Reference in New Issue
Block a user