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
@@ -44,7 +44,7 @@ A calibration entry is keyed by `(source label, signal base name)`:
| `signal` | string | — | base signal name, no `[i]` suffix |
| `scale` | float64 | `1` | finite, non-zero |
| `offset` | float64 | `0` | finite |
| `unit` | string | `""` | trimmed, max 16 chars; empty means "use the streamer's unit" |
| `unit` | string | `""` | trimmed, max 16 UTF-8 bytes; empty means "use the streamer's unit" |
The key uses the source **label**, not the runtime id (`s1`, `s2`). Ids are
assigned in add-order at startup, so a saved calibration keyed by id would rebind
@@ -90,7 +90,7 @@ New frames, implemented identically in both hubs:
| client → hub | `{"type":"setCalibration","source","signal","scale","offset","unit"}` |
| hub → client | `{"type":"configSaved","ok":bool,"path":string,"error":string}` |
| client → hub | `{"type":"reloadConfig"}` |
| hub → client | `{"type":"configReloaded","ok":bool,"error":string}` |
| hub → client | `{"type":"configReloaded","ok":bool,"path":string,"error":string}` |
`calibration` is broadcast when a client connects and after every accepted
`setCalibration` or successful `reloadConfig`. It is a separate message rather
@@ -128,9 +128,10 @@ sibling `CalConfig` type; `Save` writes both slices into one array;
`Load` decodes into `[]map[string]json.RawMessage` and discriminates per element.
**C++ (`Source/Applications/StreamHub/StreamHub.{h,cpp}`).** A fixed
`kMaxCalibration = 256` array of
`{StreamString source, signal, unit; float64 scale, offset;}` — no STL, per the
`Source/Components` and StreamHub style rules. `HandleSetCalibration`,
`kMaxCalibration = 256` array of `CalibrationEntry` structs with fixed
`char[]` fields (`source[128]`, `signal[128]`, `unit[17]`) and `float64` scale
and offset — no STL and no per-entry heap allocation, per the `Source/Components`
and StreamHub style rules. `HandleSetCalibration`,
`HandleReloadConfig` and `BroadcastCalibration` mirror the existing
`HandleAddSource` / `BroadcastSources` shape, with `BroadcastCalibration` using
its own 16 KiB buffer like `BroadcastConfig`. `LoadSourcesFile` gains the
@@ -203,10 +204,12 @@ new-format file, unrecognised block, malformed entry), `setCalibration`
validation including `scale = 0` and non-finite values, and a save→load
round-trip asserting sources and calibration both survive.
**C++.** Extend an E2E `chain` scenario's config file with a calibration entry and
assert the hub re-serialises it unchanged after a `saveSources`, which exercises
the discriminator branch in `LoadSourcesFile` and the writer in
`HandleSaveSources`.
**C++.** A standalone Go program at `Test/E2E/suite/client/configcheck/` connects
to either hub (Go or C++) via WebSocket and asserts identical calibration
behaviour: it exercises `setCalibration`, `saveSources → configSaved`,
`reloadConfig → configReloaded`, and verifies that the saved config file and all
broadcast frames are sorted and complete. The program exits non-zero on any
deviation from the protocol, making it runnable against both hubs in CI.
**Browser.** `node --check static/app.js`, plus a manual pass: set a scale and
offset on a live signal and confirm the plot, hover readout, cursor readout, CSV
@@ -216,6 +219,7 @@ the live source keeps streaming.
## Documentation
`Docs/StreamHub-API.md` gains the four new frames; `Docs/WebUI.md` gains the
`Docs/StreamHub-API.md` gains the five new frames (`calibration`, `setCalibration`,
`configSaved`, `reloadConfig`, `configReloaded`); `Docs/WebUI.md` gains the
calibration row and the Sources & Config section; `ARCHITECTURE.md` §6 gains the
config file format.