Implemented new C++ logic

This commit is contained in:
Martino Ferrari
2026-06-12 15:25:13 +02:00
parent 617b5bd712
commit f25bd7f08e
220 changed files with 39185 additions and 850 deletions
+37 -22
View File
@@ -338,6 +338,12 @@ function connectWS() {
ws.onopen = () => { wsBackoff = 1000; setStatus('orange', 'Connected waiting for data'); };
ws.onclose = () => {
setStatus('red', 'Disconnected (reconnecting…)');
// Reset MARTe connection state so that when the WS reconnects the hub can
// replay the 'connected' message and trigger a fresh DISCOVER/TREE sequence.
// Without this, the marteConnected guard in onMarteConnected blocks the
// sequence from re-running after a hub-WS reconnect (even if the MARTe2 TCP
// connection stayed alive and no 'disconnected' message was sent).
marteConnected = false;
setTimeout(connectWS, wsBackoff);
wsBackoff = Math.min(wsBackoff * 2, 30000);
};
@@ -353,7 +359,7 @@ function connectWS() {
else if (msg.type === 'connected') onMarteConnected();
else if (msg.type === 'disconnected') onMarteDisconnected();
else if (msg.type === 'log') onLog(msg);
else if (msg.type === 'response') onResponse(msg);
else if (msg.type === 'response') { onResponse(msg); }
else if (msg.type === 'tree_node') { try { onTreeNode(JSON.parse(msg.data)); } catch(e) { console.error('tree_node error:', e, msg.data); appendLog('sys','ERROR','Tree render error: '+e.message); } }
else if (msg.type === 'text_line') onTextLine(msg.data);
else if (msg.type === 'service_config') onServiceConfig(msg);
@@ -411,7 +417,12 @@ function onConfig(msg) {
const src = sourcesMap[sid];
const newSigs = msg.signals || [];
const oldSigs = src.signals || [];
const fp = s => s.name + ':' + s.typeCode + ':' + (s.numRows || 1) + ':' + (s.numCols || 1) + ':' + (s.timeMode || 0);
// Fingerprint only covers fields that change the binary layout of the data.
// timeMode and samplingRate are intentionally excluded: synthesizeHubConfig
// always sets timeMode=TimeModePacket, so including it would cause a buffer
// reset on every real UDP CONFIG arrival (synthesized→real transition), wiping
// the fast-signal buffer and limiting visible history to ~0.5 s.
const fp = s => s.name + ':' + s.typeCode + ':' + numElements(s);
const changed = newSigs.length !== oldSigs.length || newSigs.some((s, i) => fp(s) !== fp(oldSigs[i]));
src.signals = newSigs;
if (changed) {
@@ -1011,14 +1022,17 @@ function buildDataFromFetched(p, fetchedSignals, targetPts) {
const t0 = p.xRange ? p.xRange[0] : -Infinity;
const t1 = p.xRange ? p.xRange[1] : Infinity;
let masterKey = p.traces[0], masterCount = -1, masterRate = -1;
let masterKey = p.traces[0], masterCount = -1, masterRate = -1, masterSpan = -1;
for (const key of p.traces) {
let sd = fetchedSignals[key];
if (!sd || !sd.t || sd.t.length < 2) sd = supplementWithBrackets(sd, buffers[key], t0, t1);
if (!sd || !sd.t.length) continue;
const rate = getKeySamplingRate(key);
if (rate > masterRate || (rate === masterRate && sd.t.length > masterCount)) {
masterRate = rate; masterCount = sd.t.length; masterKey = key;
const span = sd.t.length > 1 ? sd.t[sd.t.length - 1] - sd.t[0] : 0;
if (span > masterSpan ||
(span === masterSpan && rate > masterRate) ||
(span === masterSpan && rate === masterRate && sd.t.length > masterCount)) {
masterRate = rate; masterSpan = span; masterCount = sd.t.length; masterKey = key;
}
}
@@ -1244,7 +1258,7 @@ function drawBandSeparators(u, p) {
const dpr = window.devicePixelRatio || 1;
const bandH = 8 / n;
ctx.save();
ctx.strokeStyle = 'rgba(127,132,156,0.25)';
ctx.strokeStyle = 'rgba(127,132,156,0.55)';
ctx.lineWidth = dpr;
for (let i = 1; i < n; i++) {
const yNorm = 4 - i * bandH; // boundary between band i-1 and band i
@@ -1841,20 +1855,26 @@ function buildLiveData(p) {
}
}
// Slice all traces; pick master by sampling rate then count.
// Slice all traces; pick master by time span (widest window wins), then
// by sampling rate, then by point count. A fast signal that has only 0.5s
// of history must NOT beat a slow signal that has 5s of history — otherwise
// sharedT covers only 0.5s and all other signals appear truncated.
// In zoom mode, include one bracketing point on each side so lines are drawn
// across the visible area even when the window contains 0 or 1 samples.
const sliceFn = isRolling ? getBufferSliceRange : getBufferSliceRangeWithBrackets;
const slices = {};
let masterKey = p.traces[0], masterCount = -1, masterRate = -1;
let masterKey = p.traces[0], masterCount = -1, masterRate = -1, masterSpan = -1;
for (const key of p.traces) {
const buf = buffers[key];
if (!buf || buf.size === 0) continue;
const sl = sliceFn(buf, t0, t1);
slices[key] = sl;
const rate = getKeySamplingRate(key);
if (rate > masterRate || (rate === masterRate && sl.t.length > masterCount)) {
masterRate = rate; masterCount = sl.t.length; masterKey = key;
const span = sl.t.length > 1 ? sl.t[sl.t.length - 1] - sl.t[0] : 0;
if (span > masterSpan ||
(span === masterSpan && rate > masterRate) ||
(span === masterSpan && rate === masterRate && sl.t.length > masterCount)) {
masterRate = rate; masterSpan = span; masterCount = sl.t.length; masterKey = key;
}
}
@@ -2090,19 +2110,9 @@ document.getElementById('btn-zoom-fit').addEventListener('click', zoomFit);
/* ════════════════════════════════════════════════════════════════
Cursor controls
════════════════════════════════════════════════════════════════ */
// Show the cursor button only when paused or in trigger-snapshot mode.
// Cursor button is always visible; readout visibility follows cursors.mode.
function updateCursorBtnVisibility() {
const canUseCursors = globalPause || (trig.enabled && trig.snapshot !== null);
const btn = document.getElementById('btn-cursor');
btn.style.display = canUseCursors ? '' : 'none';
if (!canUseCursors && cursors.mode !== 'off') {
cursors.mode = 'off';
cursors.tA = null; cursors.tB = null; // context changed, clear positions
btn.textContent = 'Cursors';
btn.classList.remove('active');
document.getElementById('cursor-readout').classList.remove('visible');
cursorsDirty = true;
}
document.getElementById('btn-cursor').style.display = '';
}
document.getElementById('btn-cursor').addEventListener('click', () => {
@@ -3684,6 +3694,11 @@ function onTracedState(msg) {
}
function onMarteConnected() {
// Guard against double-invocation: onSources() may call us when it detects
// debug.state==='connected', and replayStateToClient also sends a 'connected'
// message. A second call would wipe _cmdSent mid-flight (via _clearStartupState),
// cancelling the in-progress DISCOVER/TREE sequence.
if (marteConnected) return;
marteConnected = true;
_clearStartupState();
const el = document.getElementById('conn-status');