Panel id on spots and the panel table in the start message

SpotToSave gains int16_t panel (-1 = no panel table). It sits in the padding
after the two bools, so sizeof(SpotToSave) stays 80. It is serialised only
where it is set: CBOR spot key "panel", HDF5 /entry/MX/peakPanel (data file,
plus a virtual dataset in a VDS master when the start message has panels),
read back by HDF5MetadataSource. For a flat detector none of these is written,
so files and streams for existing data are byte-for-byte unchanged.

The start message carries the panel table (detector_modules, CBOR key absent
when empty), filled from the experiment; JFJochHttpReader takes it, so a
stream viewer knows the panels too.

AssignModules takes the spot's panel id where it has one - the stored pixel box
it was found in - and falls back to the plane position otherwise (every spot
until per-panel spot finding sets the id).

Reflection deliberately gets no panel id (owner decision): it is needed only
between prediction and integration, and the struct feeds GPU layout and
memory budgets.

Tests: CBORSerialize_SpotPanelId, CBORSerialize_StartDetectorModules,
JFJochReader_PanelId (VDS/legacy/integrated, with and without panels).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015CeEGgpKdfkCTJb3YNMJcu
This commit is contained in:
leonarski_fandClaude Opus 5.5 committed 2026-10-21 10:28:23 +02:00
1 parent c8fb472f4d
commit dc784c073d
15 files changed
+274 -4

No files matched your search

@@ -59,6 +59,16 @@ namespace {
throw JFJochException(JFJochExceptionCategory::CBORError, "Float expected");
}
double GetCBORDouble(CborValue &value) {
if (cbor_value_is_double(&value)) {
double tmp;
cborErr(cbor_value_get_double(&value, &tmp));
cborErr(cbor_value_advance(&value));
return tmp;
}
return GetCBORFloat(value);
}
bool GetCBORBool(CborValue &value) {
if (!cbor_value_is_boolean(&value)) {
throw JFJochException(JFJochExceptionCategory::CBORError,
@@ -514,6 +524,8 @@ namespace {
s.dist_ewald_sphere = GetCBORFloat(map_value);
else if (key == "image")
s.image = GetCBORInt(map_value);
else if (key == "panel")
s.panel = static_cast<int16_t>(GetCBORInt(map_value));
else
cbor_value_advance(&map_value);
}
@@ -1299,6 +1311,45 @@ namespace {
}
}
void GetDetectorModules(CborValue &value, std::vector<DetectorModule> &modules) {
const size_t array_len = GetCBORArrayLen(value);
CborValue array_value;
cborErr(cbor_value_enter_container(&value, &array_value));
for (size_t i = 0; i < array_len; i++) {
DetectorModule m;
CborValue map_value;
cborErr(cbor_value_enter_container(&array_value, &map_value));
while (!cbor_value_at_end(&map_value)) {
const auto key = GetCBORString(map_value);
if (key == "x0")
m.x0 = GetCBORInt(map_value);
else if (key == "y0")
m.y0 = GetCBORInt(map_value);
else if (key == "width")
m.width = GetCBORInt(map_value);
else if (key == "height")
m.height = GetCBORInt(map_value);
else if (key == "ox")
m.ox = GetCBORDouble(map_value);
else if (key == "oy")
m.oy = GetCBORDouble(map_value);
else if (key == "fx")
m.fx = GetCBORDouble(map_value);
else if (key == "fy")
m.fy = GetCBORDouble(map_value);
else if (key == "sx")
m.sx = GetCBORDouble(map_value);
else if (key == "sy")
m.sy = GetCBORDouble(map_value);
else
cbor_value_advance(&map_value);
}
cborErr(cbor_value_leave_container(&array_value, &map_value));
modules.push_back(m);
}
cborErr(cbor_value_leave_container(&value, &array_value));
}
bool ProcessStartMessageElement(StartMessage &message, CborValue &value) {
if (cbor_value_at_end(&value))
return false;
@@ -1370,6 +1421,8 @@ namespace {
ProcessAzintMapElement(message, value);
else if (key == "roi_map")
ProcessROIMapElement(message, value);
else if (key == "detector_modules")
GetDetectorModules(value, message.detector_modules);
else if (key == "channels")
GetCBORStringArray(value, message.channels);
else if (key == "detector_translation")