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>
This commit is contained in:
Martino Ferrari
2026-08-17 07:57:28 +02:00
co-authored by Claude Sonnet 4.6
parent 42f5a726af
commit 1f8592f854
10 changed files with 238 additions and 54 deletions
+3 -3
View File
@@ -2552,7 +2552,7 @@ document.getElementById('trig-signal').addEventListener('change', e => {
const n = meta ? numElements(meta) : 1;
if (meta && !isTemporal(meta) && n > 1) {
showArrayIdxPicker(val, n, idx => {
trig.signal = val + '[' + idx + ']'; sendTrigConfig();
trig.signal = val + '[' + idx + ']'; refreshTrigThresholdField(); sendTrigConfig();
if (trig.enabled) trigArm();
}, () => {
// Cancelled: revert selection to current trig.signal base or empty.
@@ -2562,7 +2562,7 @@ document.getElementById('trig-signal').addEventListener('change', e => {
});
return;
}
trig.signal = val; sendTrigConfig();
trig.signal = val; refreshTrigThresholdField(); sendTrigConfig();
if (trig.enabled && trig.signal) trigArm(); else if (!trig.signal) trigDisarm();
});
document.getElementById('trig-edge').addEventListener('change', e => { trig.edge = e.target.value; sendTrigConfig(); });
@@ -3467,7 +3467,7 @@ function applyCalibrationChanged() {
persistCalibration();
buildSidebar(); // unit badges
plots.forEach(p => { p.needsRedraw = true; });
if (typeof refreshVScaleMenu === 'function') refreshVScaleMenu(); // Task 8
refreshVScaleMenu();
// The threshold is held in calibrated units, so a calibration change alters
// the raw value the hub must compare against — resend it.
if (trig.signal) { refreshTrigThresholdField(); sendTrigConfig(); }
+1 -1
View File
@@ -17,7 +17,7 @@
function baseSignalName(name) {
var s = String(name == null ? '' : name);
var open = s.lastIndexOf('[');
if (open > 0 && s.charAt(s.length - 1) === ']') {
if (open >= 0 && s.charAt(s.length - 1) === ']') {
var idx = s.slice(open + 1, s.length - 1);
if (idx.length > 0 && /^[0-9]+$/.test(idx)) return s.slice(0, open);
}
+1 -1
View File
@@ -220,7 +220,7 @@
<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" maxlength="16" placeholder="—">
<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>
@@ -8,6 +8,9 @@ test('baseSignalName strips an element suffix', () => {
assert.strictEqual(C.baseSignalName('Adc[12]'), 'Adc');
assert.strictEqual(C.baseSignalName('A[1]B'), 'A[1]B');
assert.strictEqual(C.baseSignalName(''), '');
// A name that is entirely the suffix "[0]" must reduce to the empty string,
// matching Go (arrayIndexSuffix regexp) and C++ (strchr truncation) behaviour.
assert.strictEqual(C.baseSignalName('[0]'), '');
});
test('calKey is stable and separates the two fields', () => {
@@ -85,6 +88,9 @@ test('normaliseCal rejects invalid entries', () => {
assert.strictEqual(C.normaliseCal({source: 'w', signal: 's', scale: Infinity}), null);
assert.strictEqual(C.normaliseCal({source: 'w', signal: 's', offset: NaN}), null);
assert.strictEqual(C.normaliseCal({source: 'w', signal: 's', scale: '2'}), null);
// A signal name that is entirely an array-index suffix reduces to the empty
// string after stripping, so the entry must be rejected — matching Go and C++.
assert.strictEqual(C.normaliseCal({source: 'w', signal: '[0]'}), null);
});
test('applyCal and invertCal round-trip', () => {