Files
MARTe-Integrated-Components/Client/udpstreamer/static/index.html
T
Martino FerrariandClaude Sonnet 4.6 1f8592f854 fix: address all 9 findings from final calibration code review
- calibration.js: fix baseSignalName('[0]') parity with Go/C++ (>= 0 not > 0)
- calibration.test.js: add assertions for '[0]' edge case in two existing tests
- app.js: remove stale typeof guard around refreshVScaleMenu (always defined)
- app.js: call refreshTrigThresholdField on trig-signal change (both assignment sites)
- index.html: drop maxlength='16' on unit input; normaliseCal is the sole enforcer
- configcheck/main.go: delete dead nextOneOf function (no callers)
- hub_calibration_test.go: delete orphaned waitBroadcast comment (function never existed)
- calibration.go: correct arrayIndexSuffix comment to document known Go/C++ difference
- Docs/StreamHub-API.md: add calibration entry count and unit byte limits to §5 table
- spec: fix configReloaded missing path field, '16 chars'→'16 UTF-8 bytes', StreamString→char[], chain scenario→configcheck program, four→five new frames

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-08-17 07:57:28 +02:00

234 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>UDP Scope</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="stylesheet" href="/uPlot.min.css">
<link rel="stylesheet" href="/style.css">
<script src="/uPlot.iife.min.js"></script>
</head>
<body>
<!-- ── Top bar ───────────────────────────────────────────────── -->
<div id="topbar">
<button id="btn-sidebar" class="ctrl-btn active" title="Toggle sidebar"></button>
<span id="app-title">UDP Scope</span>
<div class="topbar-vsep"></div>
<button id="btn-layout" class="ctrl-btn layout-toggle" title="Select layout">⊞ 1×1 ▾</button>
<div class="topbar-sep"></div>
<div id="cursor-readout">
<span id="cur-ta">A: —</span><span class="cur-sep"></span>
<span id="cur-tb">B: —</span><span class="cur-sep"></span>
<span id="cur-dt">ΔT: —</span>
<span id="ruler-readout" style="display:none">
<span class="cur-sep"></span>
<span id="cur-y1">Y1: —</span><span class="cur-sep"></span>
<span id="cur-y2">Y2: —</span><span class="cur-sep"></span>
<span id="cur-dy">ΔY: —</span>
</span>
</div>
<span class="ctrl-label" id="lbl-window">Window:</span>
<select id="window-select" class="ctrl-select">
<option value="1">1 s</option><option value="5" selected>5 s</option>
<option value="10">10 s</option><option value="30">30 s</option>
<option value="60">60 s</option>
</select>
<button id="btn-cursor" class="ctrl-btn">Cursors</button>
<button id="btn-ruler" class="ctrl-btn" title="Horizontal value rulers">Rulers</button>
<button id="btn-zoom-back" class="ctrl-btn" style="display:none">← Back</button>
<button id="btn-zoom-fit" class="ctrl-btn">Fit</button>
<button id="btn-csv-all" class="ctrl-btn" title="Export all signals to CSV">⬇ CSV</button>
<button id="btn-sync-resume" class="ctrl-btn resume-btn" style="display:none">↺ Auto</button>
<button id="btn-trigger" class="ctrl-btn">⚡ Trigger</button>
<button id="btn-pause-global" class="ctrl-btn">⏸ Pause</button>
<label class="ctrl-check" title="Snap jittery inter-frame timestamps to ideal spacing (eliminates overlaps/gaps from software-dispatch jitter)">
<input type="checkbox" id="cb-monotonic"> Sync TS
</label>
</div>
<!-- ── Trigger bar ───────────────────────────────────────────── -->
<div id="trigbar">
<div class="trig-group">
<span class="trig-label">Signal</span>
<select id="trig-signal" class="trig-select"><option value="">— none —</option></select>
</div>
<div class="trig-sep"></div>
<div class="trig-group">
<span class="trig-label">Edge</span>
<select id="trig-edge" class="trig-select">
<option value="rising">Rising ↑</option>
<option value="falling">Falling ↓</option>
<option value="both">Both ↕</option>
</select>
</div>
<div class="trig-sep"></div>
<div class="trig-group">
<span class="trig-label">Threshold</span>
<input id="trig-threshold" class="trig-input" type="number" value="0" step="any">
</div>
<div class="trig-sep"></div>
<div class="trig-group">
<span class="trig-label">Window</span>
<select id="trig-window" class="trig-select">
<option value="0.0001">100 μs</option><option value="0.001">1 ms</option>
<option value="0.01">10 ms</option><option value="0.1">100 ms</option>
<option value="0.5">500 ms</option><option value="1" selected>1 s</option>
<option value="5">5 s</option><option value="10">10 s</option>
</select>
</div>
<div class="trig-sep"></div>
<div class="trig-group">
<span class="trig-label">Pre</span>
<input id="trig-pre" class="trig-range" type="range" min="0" max="100" value="20">
<span class="trig-range-val" id="trig-pre-val">20%</span>
</div>
<div class="trig-sep"></div>
<div class="trig-group">
<span class="trig-label">Mode</span>
<select id="trig-mode" class="trig-select">
<option value="normal">Normal</option>
<option value="single">Single</option>
</select>
</div>
<div class="trig-sep"></div>
<div class="trig-group" style="gap:8px">
<span id="trig-status-badge">IDLE</span>
<button id="btn-trig-force" title="Capture now, ignoring the threshold">Force</button>
<button id="btn-trig-stop" style="display:none">Stop</button>
<button id="btn-trig-rearm">Rearm</button>
</div>
</div>
<!-- ── Body ─────────────────────────────────────────────────── -->
<div id="body">
<div id="sidebar">
<div id="sidebar-header">Signals</div>
<div id="signal-list"></div>
</div>
<div id="main">
<div id="plot-grid" class="l1x1"></div>
</div>
</div>
<!-- ── Stats panel ─────────────────────────────────────────────── -->
<div id="stats-panel">
<div id="stats-panel-hdr">
<span class="stats-hdr-label">Source Statistics</span>
<select id="stats-source-sel" class="stats-source-sel"></select>
<button id="btn-stats-close"></button>
</div>
<div id="stats-body"></div>
</div>
<!-- ── Status bar ─────────────────────────────────────────────── -->
<div id="statusbar">
<div id="sb-left">
<div id="status-led"></div>
<span id="status-text">Disconnected</span>
<span id="sb-tsage"></span>
<button id="btn-stats" class="ctrl-btn" style="height:16px;padding:0 7px;font-size:10px;line-height:1">📊 Stats</button>
<span id="history-badge" style="display:none;font-size:10px;color:#f9e2af;margin-left:8px"></span>
</div>
<span id="build-version"></span>
</div>
<div id="layout-menu"></div>
<!-- ── Signal style context menu ─────────────────────────────── -->
<div id="sig-ctx-menu" style="display:none">
<div class="ctx-menu-header">Style:
<span id="ctx-menu-key" class="ctx-menu-key"></span></div>
<div class="ctx-row">
<label>Color</label>
<input type="color" id="ctx-color">
</div>
<div class="ctx-row">
<label>Width</label>
<div class="ctx-btns" id="ctx-width-btns">
<button class="ctx-btn" data-w="1">1px</button>
<button class="ctx-btn active" data-w="1.5">1.5</button>
<button class="ctx-btn" data-w="2">2px</button>
<button class="ctx-btn" data-w="3">3px</button>
</div>
</div>
<div class="ctx-row">
<label>Line</label>
<div class="ctx-btns" id="ctx-dash-btns">
<button class="ctx-btn active" data-dash="solid">——</button>
<button class="ctx-btn" data-dash="dashed">╌╌</button>
<button class="ctx-btn" data-dash="dotted">·····</button>
</div>
</div>
<div class="ctx-row">
<label>Marker</label>
<div class="ctx-btns" id="ctx-marker-btns">
<button class="ctx-btn active" data-marker="none">none</button>
<button class="ctx-btn" data-marker="circle"></button>
<button class="ctx-btn" data-marker="square"></button>
<button class="ctx-btn" data-marker="cross"></button>
<button class="ctx-btn" data-marker="diamond"></button>
</div>
</div>
<div class="ctx-row">
<label>Size</label>
<input type="range" class="ctx-range" id="ctx-marker-size" min="2" max="10" value="4">
<span class="ctx-range-val" id="ctx-marker-size-val">4px</span>
</div>
</div>
<!-- ── Array index picker (trigger signal) ──────────────────────── -->
<div id="array-idx-picker" style="display:none">
<div class="ctx-menu-header">Element index: <span id="aip-sig" class="ctx-menu-key"></span></div>
<div class="ctx-row">
<label>Index</label>
<input type="number" id="aip-idx" class="ctx-num" min="0" step="1" value="0">
<span id="aip-range" style="font-size:10px;color:var(--overlay0)"></span>
</div>
<div class="ctx-row" style="justify-content:flex-end;gap:6px">
<button class="ctx-btn" id="aip-cancel">Cancel</button>
<button class="ctx-btn active" id="aip-ok">OK</button>
</div>
</div>
<!-- ── VScale toolbar (moved into plot card when active) ─────────── -->
<div id="vscale-menu" style="display:none">
<div class="vstb-header">
<span class="vstb-label">V-Scale: <span id="vscale-menu-key" class="ctx-menu-key"></span></span>
<div class="ctx-btns" id="vscale-mode-btns">
<button class="ctx-btn active" data-mode="auto">Auto</button>
<button class="ctx-btn" data-mode="range">Range</button>
<button class="ctx-btn" data-mode="manual">Manual</button>
</div>
<div id="vscale-manual-row" style="display:none;align-items:center;gap:4px">
<label class="vstb-lbl">V/div</label>
<input type="number" id="vscale-vdiv" class="ctx-num" min="1e-30" step="any" value="1">
</div>
<div id="vscale-offset-row" style="display:none;align-items:center;gap:4px">
<label class="vstb-lbl" title="Raw value at screen centre — unbounded, may lie outside the plotted range">Offset</label>
<input type="number" id="vscale-offset" class="ctx-num" step="any" value="0">
</div>
<div id="vscale-pos-row" style="display:none;align-items:center;gap:4px">
<label class="vstb-lbl">Pos</label>
<input type="number" id="vscale-pos" class="ctx-num" step="0.1" value="0">
</div>
<div id="vscale-type-row" style="display:none;align-items:center;gap:4px">
<label class="vstb-lbl">Type</label>
<div class="ctx-btns" id="vscale-type-btns">
<button class="ctx-btn active" data-type="analog">Analog</button>
<button class="ctx-btn" data-type="digital">Digital</button>
</div>
</div>
<div class="vstb-sep"></div>
<div id="vscale-cal-row" style="display:flex;align-items:center;gap:4px">
<label class="vstb-lbl" id="vscale-cal-lbl"
title="Data calibration: value = raw × Scale + Offset. Applies to the plot, cursors, hover readout, CSV export and trigger threshold.">Cal</label>
<label class="vstb-lbl">Scale</label>
<input type="number" id="vscale-cal-scale" class="ctx-num ctx-num-sm" step="any" value="1">
<label class="vstb-lbl">Offset</label>
<input type="number" id="vscale-cal-offset" class="ctx-num ctx-num-sm" step="any" value="0">
<label class="vstb-lbl">Unit</label>
<input type="text" id="vscale-cal-unit" class="ctx-num ctx-num-xs" placeholder="—">
<button class="ctx-btn" id="btn-cal-reset" title="Clear this signal's calibration">Reset</button>
</div>
<button id="btn-vscale-close" class="vstb-close" title="Close"></button>
</div>
</div>
<!-- Follows the mouse over a plot: time + per-trace values. -->
<div id="hover-readout" style="display:none"></div>
<script src="/calibration.js"></script>
<script src="/app.js"></script>
</body>
</html>