spindle: the per-image severity travels the whole data path, and absence travels with it
The score was computed and then thrown away - carried on the per-image message but transported nowhere - so the automation it exists for could not read it. It now flows like bkg_estimate at every layer: CBOR (per-image key, END-block run mean and per-image array), HDF5 (per-image /entry/MX/spindleBlindFraction in the data files, the array and spindleBlindFractionMean in the master), read-back into a re-opened dataset, the scan result, the receiver plots, the REST plot and scan_result schemas, and the viewer and frontend plot menus. Absence is load-bearing and every transport keeps it distinguishable from a measured zero: the CBOR key is simply missing, the HDF5 array holds NaN, and read-back turns NaN back into an absent optional rather than a value. A pipeline that read 0 where the truth is "no value" would take exactly the wrong action - a measured 0 says one sweep loses nothing, absence says nobody could look, and the second must engage the recovery protocol while the first must not. The round-trip tests pin all three states through CBOR and through a written-and-reopened file. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EFEJG6WBQv8th4UJFNe53N
This commit is contained in:
@@ -557,6 +557,7 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen
|
||||
|
||||
dataset->indexing_result = master_file->ReadOptVector<float>("/entry/MX/imageIndexed");
|
||||
dataset->bkg_estimate = master_file->ReadOptVector<float>("/entry/MX/bkgEstimate");
|
||||
dataset->spindle_blind_fraction = master_file->ReadOptVector<float>("/entry/MX/spindleBlindFraction");
|
||||
dataset->ice_ring_score = master_file->ReadOptVector<float>("/entry/MX/iceRingScore");
|
||||
dataset->resolution_estimate = master_file->ReadOptVector<float>("/entry/MX/resolutionEstimate");
|
||||
dataset->profile_radius = master_file->ReadOptVector<float>("/entry/MX/profileRadius");
|
||||
@@ -700,6 +701,10 @@ HDF5MetadataSource::OpenResult HDF5MetadataSource::Open(const std::string &filen
|
||||
data_file, "/entry/MX/bkgEstimate",
|
||||
number_of_images, fimages);
|
||||
|
||||
ReadVector(dataset->spindle_blind_fraction,
|
||||
data_file, "/entry/MX/spindleBlindFraction",
|
||||
number_of_images, fimages);
|
||||
|
||||
ReadVector(dataset->ice_ring_score,
|
||||
data_file, "/entry/MX/iceRingScore",
|
||||
number_of_images, fimages);
|
||||
@@ -1327,6 +1332,11 @@ void HDF5MetadataSource::FillPerImage(DataMessage &message, int64_t requested_im
|
||||
message.indexing_lattice_count = dataset->indexing_lattice_count[image_number];
|
||||
if (dataset->bkg_estimate.size() > image_number)
|
||||
message.bkg_estimate = dataset->bkg_estimate[image_number];
|
||||
// NaN is how the file stores a frame with no value; the optional must come back absent, not
|
||||
// carrying a NaN, because absence is the CANNOT-SAY trigger state and a value is not.
|
||||
if (dataset->spindle_blind_fraction.size() > image_number
|
||||
&& std::isfinite(dataset->spindle_blind_fraction[image_number]))
|
||||
message.spindle_blind_fraction = dataset->spindle_blind_fraction[image_number];
|
||||
if (dataset->ice_ring_score.size() > image_number)
|
||||
message.ice_ring_score = dataset->ice_ring_score[image_number];
|
||||
if (dataset->efficiency.size() > image_number)
|
||||
|
||||
Reference in New Issue
Block a user