docs: fix three review findings in calibration documentation

- Finding 1 (Critical): replace "16 chars" with "16 UTF-8 bytes" in the
  setCalibration unit field description (StreamHub-API.md) and the Cal·Unit
  toolbar row (WebUI.md); note that multi-byte characters consume more than
  one byte and that truncation never splits a character.

- Finding 2 (Important): correct the claim that a sources broadcast after
  reloadConfig is conditional on new sources being added — that is true only
  of the Go hub. The C++ hub calls BroadcastSources() unconditionally on
  success. Both the reloadConfig command description and the configReloaded
  event description in StreamHub-API.md are updated; the Reload bullet in
  WebUI.md is updated with a brief note. Clients must tolerate an unsolicited
  sources frame after any reload.

- Finding 3 (Minor): the configSaved failure example used "no SourcesFile
  configured", which matches neither hub. Corrected to the C++ form
  "no sources file configured" and added a note that the exact error text
  is not part of the protocol contract (Go uses "no sources-file configured").

Source evidence: calibration.go (maxUnitLen, len(), rune-repair loop),
StreamHub.cpp (kMaxUnitLen, byte strncpy, HandleReloadConfig unconditional
BroadcastSources, HandleSaveSources error string).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Martino Ferrari
2026-08-17 07:34:48 +02:00
co-authored by Claude Sonnet 4.6
parent d26b78b7f6
commit 686fc2ce7d
2 changed files with 19 additions and 9 deletions
+15 -7
View File
@@ -66,7 +66,7 @@ name — one entry covers every element of an array signal.
| `signal` | string | — | non-empty after trimming; any trailing `[i]` is stripped | | `signal` | string | — | non-empty after trimming; any trailing `[i]` is stripped |
| `scale` | number | `1` | finite and non-zero | | `scale` | number | `1` | finite and non-zero |
| `offset` | number | `0` | finite | | `offset` | number | `0` | finite |
| `unit` | string | `""` | trimmed, truncated to 16 chars; empty = use the streamer's own unit | | `unit` | string | `""` | trimmed, truncated to 16 UTF-8 bytes (a multi-byte character such as `°C` consumes more than one byte; truncation never splits a character); empty = use the streamer's own unit |
Calibration is **metadata only**: the hub stores and redistributes it but never Calibration is **metadata only**: the hub stores and redistributes it but never
applies it. Ring buffers, recorded history, the `zoom` reply, both binary frames applies it. Ring buffers, recorded history, the `zoom` reply, both binary frames
@@ -96,7 +96,11 @@ a live UDP session that must not be interrupted. An unsaved source the user adde
keeps streaming. keeps streaming.
The hub replies with [`configReloaded`](#configreloaded), followed on success by a The hub replies with [`configReloaded`](#configreloaded), followed on success by a
`calibration` broadcast and a `sources` broadcast. `calibration` broadcast. Whether a `sources` broadcast follows depends on the
hub implementation: the Go hub emits `sources` only when the file adds at least
one new source (each `sm.Add()` call triggers it individually), while the C++
hub always emits `sources` unconditionally after a successful reload. Clients
must therefore tolerate an unsolicited `sources` frame after any reload.
### `getSources` / `getConfig` / `getStats` ### `getSources` / `getConfig` / `getStats`
@@ -284,10 +288,12 @@ after a successful `reloadConfig`. It is a separate frame rather than a field on
```json ```json
{"type":"configSaved","ok":true,"path":"/etc/streamhub/sources.json"} {"type":"configSaved","ok":true,"path":"/etc/streamhub/sources.json"}
{"type":"configSaved","ok":false,"path":"","error":"no SourcesFile configured"} {"type":"configSaved","ok":false,"path":"","error":"no sources file configured"}
``` ```
Broadcast in reply to `saveSources`. `path` is always present (empty when the hub Broadcast in reply to `saveSources`. `path` is always present (empty when the hub
has no config file configured); `error` only when `ok` is false. has no config file configured); `error` only when `ok` is false. The exact error
text is not part of the protocol contract and differs between hubs (the Go hub
uses `"no sources-file configured"`, the C++ hub `"no sources file configured"`).
### `configReloaded` ### `configReloaded`
@@ -295,9 +301,11 @@ has no config file configured); `error` only when `ok` is false.
{"type":"configReloaded","ok":true,"path":"/etc/streamhub/sources.json"} {"type":"configReloaded","ok":true,"path":"/etc/streamhub/sources.json"}
{"type":"configReloaded","ok":false,"path":"/etc/streamhub/sources.json","error":"cannot read sources file"} {"type":"configReloaded","ok":false,"path":"/etc/streamhub/sources.json","error":"cannot read sources file"}
``` ```
Broadcast in reply to `reloadConfig`; same shape as `configSaved`. On success it Broadcast in reply to `reloadConfig`; same shape as `configSaved`. On success
is followed by a `calibration` broadcast and, if the file added any source, a it is followed by a `calibration` broadcast. Whether a `sources` broadcast also
`sources` broadcast. follows is hub-specific: the Go hub sends it only if the reload added at least
one new source; the C++ hub sends it unconditionally. Clients must tolerate an
unsolicited `sources` frame after any reload.
--- ---
+4 -2
View File
@@ -91,7 +91,9 @@ At the bottom of the sidebar, the collapsible **Sources & Config** section holds
config file; config file;
- **Reload** — re-reads that file. Calibration is replaced wholesale (so unsaved - **Reload** — re-reads that file. Calibration is replaced wholesale (so unsaved
edits are discarded), sources present in the file but not running are added, edits are discarded), sources present in the file but not running are added,
and no running source is stopped or reconnected; and no running source is stopped or reconnected. The hub may send an updated
`sources` list even when nothing changed (see the API doc for the per-hub
difference);
- a status line showing the written path on success or the hub's error text on - a status line showing the written path on success or the hub's error text on
failure. failure.
@@ -160,7 +162,7 @@ plot header showing per-signal vertical scale controls:
| **Type** (Mixed mode only) | Toggle between **Analog** and **Digital** for this signal | | **Type** (Mixed mode only) | Toggle between **Analog** and **Digital** for this signal |
| **Cal · Scale** | Data calibration gain. `value = raw × Scale + Offset` | | **Cal · Scale** | Data calibration gain. `value = raw × Scale + Offset` |
| **Cal · Offset** | Data calibration bias, in calibrated units | | **Cal · Offset** | Data calibration bias, in calibrated units |
| **Cal · Unit** | Overrides the unit reported by the streamer (max 16 chars) | | **Cal · Unit** | Overrides the unit reported by the streamer (max 16 UTF-8 bytes; a multi-byte character such as `°C` counts as more than one byte) |
| **Reset** | Clears this signal's calibration (`Scale = 1`, `Offset = 0`, no unit override) | | **Reset** | Clears this signal's calibration (`Scale = 1`, `Offset = 0`, no unit override) |
| **✕** | Close the toolbar and deselect the signal | | **✕** | Close the toolbar and deselect the signal |