Initial release

This commit is contained in:
Martino Ferrari
2026-05-29 13:29:59 +02:00
commit 617b5bd712
110 changed files with 29234 additions and 0 deletions
+514
View File
@@ -0,0 +1,514 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MARTe2 Integrated Client</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">
<span id="app-title">MARTe2</span>
<div class="topbar-vsep"></div>
<!-- Debug connection menu -->
<div class="menu-wrap">
<button class="ctrl-btn" id="conn-menu-btn" onclick="toggleMenu('conn-dropdown')">
<span id="conn-status"></span>
Debug ▾
</button>
<div class="dropdown" id="conn-dropdown">
<div class="menu-row">
<input type="text" id="host" value="127.0.0.1" placeholder="host" style="flex:2">
<input type="number" id="port" value="8080" placeholder="ctrl" style="width:60px">
<button id="btn-connect" onclick="toggleConnect()">Connect</button>
</div>
<div class="menu-row" id="port-manual-row" style="display:none">
<label style="color:#a6adc8;font-size:11px;flex-shrink:0">UDP:</label>
<input type="number" id="udp-port" value="8081" placeholder="udp" style="width:72px">
<label style="color:#a6adc8;font-size:11px;flex-shrink:0">Log:</label>
<input type="number" id="log-port" value="8082" placeholder="log" style="width:72px">
</div>
<div class="menu-row">
<label class="form-check" style="margin:0;font-size:11px">
<input type="checkbox" id="auto-ports" checked onchange="toggleAutoPorts(this.checked)">
<span style="color:#a6adc8">Auto ports (SERVICE_INFO)</span>
</label>
</div>
<hr class="menu-sep">
<div class="menu-btn-row">
<button onclick="discoverCmd()">Discover</button>
<button onclick="treeCmd()">Tree</button>
<button onclick="serviceInfoCmd()">Info</button>
</div>
</div>
</div>
<button id="btn-pause" class="ctrl-btn" onclick="togglePause()">⏸ Pause</button>
<button class="ctrl-btn" onclick="openStepDialog()">⚙ Step</button>
<button class="ctrl-btn" onclick="openForceDialog()">⚡ Force</button>
<button class="ctrl-btn" onclick="openBreakDialog()">Break</button>
<button class="ctrl-btn" onclick="openMsgDialog()">✉ Msg</button>
<div class="topbar-vsep"></div>
<!-- Scope controls -->
<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>
</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" style="display:none">Cursor</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>
<div class="topbar-vsep"></div>
<span id="udp-stats" style="color:#585b70;font-size:11px">0 pkts</span>
<div id="status-led"></div>
<span id="status-text" style="font-size:11px;color:#a6adc8">Disconnected</span>
<span id="sb-tsage" style="font-size:11px;color:#585b70"></span>
<button id="btn-stats" class="ctrl-btn" style="height:20px;padding:0 7px;font-size:10px;line-height:1">Stats</button>
<span id="build-version" style="font-size:10px;color:#585b70;margin-left:4px"></span>
</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-stop" style="display:none">Stop</button>
<button id="btn-trig-rearm">Rearm</button>
</div>
</div>
<!-- ── Body ─────────────────────────────────────────────────── -->
<div id="body">
<!-- ── Step status bar (inside body, visible when paused) ── -->
<div id="step-bar">
<span>⏹ PAUSED at <b id="paused-gam"></b></span>
<span id="step-remaining"></span>
<select id="step-thread" style="width:120px"></select>
<button onclick="step(1)">Step 1</button>
<button onclick="step(5)">Step 5</button>
<button onclick="step(20)">Step 20</button>
<button class="ok" onclick="togglePause()">▶ Resume</button>
</div>
<div id="body-row">
<!-- LEFT: signal list (scope) + object tree (debugger) -->
<div id="sidebar">
<div class="panel-tabs">
<div class="panel-tab active" onclick="switchSidebarTab('tree')">Object Tree</div>
</div>
<!-- Signals tab (hidden — signals come from Traced panel in debugger) -->
<div id="sidebar-signals" class="sidebar-tab-body" style="display:none">
<div id="signal-list"></div>
</div>
<!-- Object tree tab -->
<div id="sidebar-tree" class="sidebar-tab-body active">
<div class="panel-search">
<input id="tree-search" oninput="filterTree(this.value)" placeholder="Search tree…">
</div>
<div class="panel-body" id="tree-body">
<div class="empty-hint">Connect to MARTe2 then click Tree</div>
</div>
</div>
</div>
<!-- Left resize/collapse strip -->
<div id="left-strip" class="panel-strip" title="Drag to resize · Click to collapse"></div>
<!-- CENTER: plots -->
<div id="main">
<div id="plot-grid" class="l1x1"></div>
</div>
<!-- Right resize/collapse strip -->
<div id="right-strip" class="panel-strip" title="Drag to resize · Click to collapse"></div>
<!-- RIGHT: debug tabs (Traced, Forced, Breaks, Msgs) -->
<div id="right-panel">
<div class="tabs">
<div class="tab active" onclick="switchTab('traced')">Traced</div>
<div class="tab" onclick="switchTab('forced')">Forced</div>
<div class="tab" onclick="switchTab('breaks')">Breaks</div>
<div class="tab" onclick="switchTab('msgs')">Msgs</div>
</div>
<div class="tab-content active" id="tab-traced">
<div class="empty-hint" id="no-traced-hint">No signals traced</div>
</div>
<div class="tab-content" id="tab-forced">
<div class="empty-hint" id="no-forced-hint">No signals forced</div>
</div>
<div class="tab-content" id="tab-breaks">
<div class="empty-hint" id="no-breaks-hint">No breakpoints set</div>
</div>
<div class="tab-content" id="tab-msgs">
<div class="empty-hint" id="no-msgs-hint">No messages sent</div>
</div>
</div>
</div><!-- #body-row -->
<!-- ── Bottom: Logs ─────────────────────────────────────────── -->
<div id="log-panel">
<div id="log-toolbar">
<button class="panel-toggle" title="Collapse logs" onclick="togglePanel('log-panel','▼','▲',this)" id="log-toggle-btn"></button>
<span style="font-weight:600;color:#89b4fa">Logs</span>
<label><input type="checkbox" id="lf-service" onchange="renderLogs()"> Service</label>
<label><input type="checkbox" id="lf-debug" checked onchange="renderLogs()"> Debug</label>
<label><input type="checkbox" id="lf-info" checked onchange="renderLogs()"> Info</label>
<label><input type="checkbox" id="lf-warn" checked onchange="renderLogs()"> Warn</label>
<label><input type="checkbox" id="lf-error" checked onchange="renderLogs()"> Error</label>
<input type="text" id="log-filter" placeholder="Filter…" oninput="renderLogs()" style="width:150px">
<button onclick="logs=[];renderLogs()" style="margin-left:auto">Clear</button>
<label><input type="checkbox" id="log-autoscroll" checked> Auto-scroll</label>
</div>
<div id="log-body"></div>
</div><!-- #log-panel -->
</div><!-- #body -->
<!-- ── 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>
<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-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>
<button id="btn-vscale-close" class="vstb-close" title="Close"></button>
</div>
</div>
<!-- ═══ DIALOGS ═══ -->
<!-- Force dialog -->
<div class="dialog-overlay" id="dlg-force" style="display:none" onclick="if(event.target===this)closeDlg('dlg-force')">
<div class="dialog">
<h3>Force Signal</h3>
<label>Signal</label>
<input id="force-sig" list="force-sig-list" placeholder="Signal path…">
<datalist id="force-sig-list"></datalist>
<label>Value</label>
<input id="force-val" placeholder="e.g. 42">
<div class="btns">
<button onclick="closeDlg('dlg-force')">Cancel</button>
<button class="active" onclick="doForce()">Force</button>
</div>
</div>
</div>
<!-- Unforce confirm -->
<div class="dialog-overlay" id="dlg-unforce" style="display:none" onclick="if(event.target===this)closeDlg('dlg-unforce')">
<div class="dialog">
<h3>Remove Force</h3>
<p id="unforce-msg" style="margin-bottom:12px;color:#cdd6f4"></p>
<div class="btns">
<button onclick="closeDlg('dlg-unforce')">Cancel</button>
<button class="danger" onclick="doUnforce()">Unforce</button>
</div>
</div>
</div>
<!-- Break dialog -->
<div class="dialog-overlay" id="dlg-break" style="display:none" onclick="if(event.target===this)closeDlg('dlg-break')">
<div class="dialog">
<h3>Set Breakpoint</h3>
<label>Signal</label>
<input id="break-sig" list="break-sig-list" placeholder="Signal path…">
<datalist id="break-sig-list"></datalist>
<div class="form-row">
<div>
<label>Operator</label>
<select id="break-op">
<option>></option><option>&gt;=</option>
<option>&lt;</option><option>&lt;=</option>
<option>==</option><option>!=</option>
</select>
</div>
<div>
<label>Threshold</label>
<input id="break-thresh" placeholder="0">
</div>
</div>
<div class="btns">
<button onclick="closeDlg('dlg-break')">Cancel</button>
<button class="active" onclick="doBreak()">Set Break</button>
</div>
</div>
</div>
<!-- Message dialog -->
<div class="dialog-overlay" id="dlg-msg" style="display:none" onclick="if(event.target===this)closeDlg('dlg-msg')">
<div class="dialog">
<h3>Send Message</h3>
<label>Destination</label>
<input id="msg-dest" list="msg-dest-list" placeholder="e.g. App.Functions.GAM1">
<datalist id="msg-dest-list"></datalist>
<label>Function</label>
<input id="msg-func" placeholder="FunctionName">
<label>Payload (key=value lines)</label>
<textarea id="msg-payload" placeholder="Key = Value&#10;Key2 = Value2"></textarea>
<div class="form-check">
<input type="checkbox" id="msg-wait">
<label for="msg-wait">Wait for reply</label>
</div>
<div class="btns">
<button onclick="closeDlg('dlg-msg')">Cancel</button>
<button class="active" onclick="doSendMsg()">Send</button>
</div>
</div>
</div>
<!-- Array signal dialog (Trace / Force / Break with index/range selection) -->
<div class="dialog-overlay" id="dlg-array" style="display:none" onclick="if(event.target===this)closeDlg('dlg-array')">
<div class="dialog" style="min-width:360px">
<h3 id="arr-dlg-title">Array Signal</h3>
<p style="font-size:11px;color:#a6adc8;margin-bottom:14px">
<b id="arr-dlg-sig" style="color:#cdd6f4"></b>
&nbsp;·&nbsp;<span id="arr-dlg-n" style="color:#89b4fa"></span> elements
</p>
<label style="margin-bottom:6px">Apply to</label>
<div class="arr-seg">
<button id="arr-sel-all" class="active" onclick="setArrSel('all')">All</button>
<button id="arr-sel-idx" onclick="setArrSel('idx')">Index</button>
<button id="arr-sel-rng" onclick="setArrSel('rng')">Range</button>
</div>
<div id="arr-idx-sect" style="display:none">
<label>Element index &nbsp;<span style="color:#585b70;font-weight:normal">(0 <span id="arr-idx-max"></span>)</span></label>
<input id="arr-idx" type="number" min="0" value="0">
</div>
<div id="arr-rng-sect" style="display:none">
<div class="form-row">
<div>
<label>From index</label>
<input id="arr-r0" type="number" min="0" value="0">
</div>
<div>
<label>To index <span style="color:#585b70;font-weight:normal">inclusive</span></label>
<input id="arr-r1" type="number" min="0" value="0">
</div>
</div>
</div>
<div id="arr-force-sect" style="display:none">
<label>Force value</label>
<input id="arr-force-val" placeholder="e.g. 0">
</div>
<div id="arr-break-sect" style="display:none">
<div class="form-row">
<div>
<label>Condition</label>
<select id="arr-break-op">
<option>></option><option>>=</option>
<option>&lt;</option><option>&lt;=</option>
<option>==</option><option>!=</option>
</select>
</div>
<div>
<label>Threshold</label>
<input id="arr-break-thr" placeholder="0">
</div>
</div>
</div>
<div class="btns">
<button onclick="closeDlg('dlg-array')">Cancel</button>
<button id="arr-ok-btn" class="active" onclick="doArrayOp()">Apply</button>
</div>
</div>
</div>
<!-- Array → plot mode dialog (Sequential / Waterfall) -->
<div class="dialog-overlay" id="dlg-arr-plot" style="display:none" onclick="if(event.target===this)closeDlg('dlg-arr-plot')">
<div class="dialog" style="min-width:360px">
<h3>Plot Array Signal</h3>
<p style="font-size:11px;color:#a6adc8;margin-bottom:14px">
<b id="arrp-name" style="color:#cdd6f4"></b>
&nbsp;·&nbsp;<span id="arrp-n" style="color:#89b4fa"></span> elements
</p>
<div class="arr-seg" style="margin-bottom:8px">
<button id="arrp-sel-sequential" class="active" onclick="setArrpMode('sequential')">Sequential</button>
<button id="arrp-sel-waterfall" onclick="setArrpMode('waterfall')">Waterfall</button>
</div>
<p id="arrp-desc" style="font-size:10px;color:#a6adc8;margin-bottom:16px;min-height:28px"></p>
<div class="btns">
<button onclick="closeDlg('dlg-arr-plot')">Cancel</button>
<button class="active" onclick="doArrayPlotMode()">Add</button>
</div>
</div>
</div>
<!-- Info dialog -->
<div class="dialog-overlay" id="dlg-info" style="display:none" onclick="if(event.target===this)closeDlg('dlg-info')">
<div class="dialog" style="max-width:600px;width:90vw">
<h3 id="info-title">Info</h3>
<pre id="info-body" style="background:#11111b;padding:8px;border-radius:4px;overflow:auto;max-height:400px;font-size:11px;color:#cdd6f4;white-space:pre-wrap"></pre>
<div class="btns" style="margin-top:12px">
<button onclick="closeDlg('dlg-info')">Close</button>
</div>
</div>
</div>
<!-- Step dialog -->
<div class="dialog-overlay" id="dlg-step" style="display:none" onclick="if(event.target===this)closeDlg('dlg-step')">
<div class="dialog">
<h3>Step Execution</h3>
<div class="form-row">
<div>
<label>Count</label>
<input type="number" id="step-count" value="1" min="1">
</div>
<div>
<label>Thread (optional)</label>
<input id="step-thread-inp" list="step-thread-list" placeholder="all">
<datalist id="step-thread-list"></datalist>
</div>
</div>
<div class="btns">
<button onclick="closeDlg('dlg-step')">Cancel</button>
<button class="active" onclick="doStep()">Step</button>
</div>
</div>
</div>
<script src="/app.js"></script>
</body>
</html>