diff --git a/frame_serialize/EndMessage.h b/frame_serialize/EndMessage.h index 2527bb2a..e6bb462c 100644 --- a/frame_serialize/EndMessage.h +++ b/frame_serialize/EndMessage.h @@ -4,6 +4,9 @@ #ifndef JUNGFRAUJOCH_ENDMESSAGE_H #define JUNGFRAUJOCH_ENDMESSAGE_H +#include +#include + struct EndMessage { uint64_t number_of_images; uint64_t max_receiver_delay; @@ -15,6 +18,8 @@ struct EndMessage { std::string series_unique_id; uint64_t series_id; + + std::map> rad_int_result; }; #endif //JUNGFRAUJOCH_ENDMESSAGE_H diff --git a/frame_serialize/JFJochFrameDeserializer.cpp b/frame_serialize/JFJochFrameDeserializer.cpp index b89e3f8f..a7c98f10 100644 --- a/frame_serialize/JFJochFrameDeserializer.cpp +++ b/frame_serialize/JFJochFrameDeserializer.cpp @@ -402,6 +402,19 @@ void JFJochFrameDeserializer::ProcessPixelMaskElement(CborValue &value) { cborErr(cbor_value_leave_container(&value, &map_value)); } +void JFJochFrameDeserializer::ProcessRadIntResultElement(CborValue &value) { + CborValue map_value; + cborErr(cbor_value_enter_container(&value, &map_value)); + while (! cbor_value_at_end(&map_value)) { + auto key = GetCBORString(map_value); + std::vector val; + GetCBORFloatArray(map_value, val); + end_message.rad_int_result[key] = val; + } + cborErr(cbor_value_leave_container(&value, &map_value)); +} + + void JFJochFrameDeserializer::ProcessChannels(CborValue &value) { if (!cbor_value_is_array(&value)) throw JFJochException(JFJochExceptionCategory::CBORError, "Array expected"); @@ -579,7 +592,8 @@ bool JFJochFrameDeserializer::ProcessEndMessageElement(CborValue &value) { end_message.series_unique_id = GetCBORString(value); else if (key == "series_id") end_message.series_id = GetCBORUInt(value); - + else if (key == "rad_int_result") + ProcessRadIntResultElement(value); else cbor_value_advance(&value); return true; diff --git a/frame_serialize/JFJochFrameDeserializer.h b/frame_serialize/JFJochFrameDeserializer.h index 42172498..e78ce8d0 100644 --- a/frame_serialize/JFJochFrameDeserializer.h +++ b/frame_serialize/JFJochFrameDeserializer.h @@ -39,6 +39,7 @@ private: void ProcessChannels(CborValue &value); void ProcessImageData(CborValue &value); void ProcessPixelMaskElement(CborValue &value); + void ProcessRadIntResultElement(CborValue &value); void ProcessUnitCellElement(CborValue &value); void ProcessStartMessageUserDataElement(CborValue &value); void ProcessImageMessageUserDataElement(CborValue &value); diff --git a/frame_serialize/JFJochFrameSerializer.cpp b/frame_serialize/JFJochFrameSerializer.cpp index 786a9e98..058ef616 100644 --- a/frame_serialize/JFJochFrameSerializer.cpp +++ b/frame_serialize/JFJochFrameSerializer.cpp @@ -124,9 +124,9 @@ inline void CBOR_ENC_MULTIDIM_TYPED_ARRAY(CborEncoder &encoder, const char* key, cborErr(cbor_encoder_close_container(&encoder, &arrayEncoder)); } -inline void CBOR_ENC(CborEncoder &encoder, const char* key, - const std::map> &pixel_mask, - size_t xpixel, size_t ypixel) { +inline void CBOR_ENC_PIXEL_MASK(CborEncoder &encoder, const char* key, + const std::map> &pixel_mask, + size_t xpixel, size_t ypixel) { CborEncoder mapEncoder; cborErr(cbor_encode_text_stringz(&encoder, key)); cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, pixel_mask.size())); @@ -156,6 +156,16 @@ inline void CBOR_ENC(CborEncoder &encoder, const char* key, const std::vector> &rad_int_result) { + CborEncoder mapEncoder; + cborErr(cbor_encode_text_stringz(&encoder, key)); + cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, rad_int_result.size())); + for (auto &[map_key, map_val]: rad_int_result) + CBOR_ENC(mapEncoder, map_key.c_str(), map_val); + cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); +} + inline void CBOR_ENC(CborEncoder &encoder, const char* key, const std::vector& spots) { CborEncoder arrayEncoder; @@ -367,7 +377,7 @@ void JFJochFrameSerializer::SerializeSequenceStart(const StartMessage& message) CBOR_ENC_GONIOMETER_MAP(mapEncoder, "goniometer", message.goniometer); CBOR_ENC_USER_DATA(mapEncoder, message); - CBOR_ENC(mapEncoder, "pixel_mask", message.pixel_mask, message.image_size_x, message.image_size_y); + CBOR_ENC_PIXEL_MASK(mapEncoder, "pixel_mask", message.pixel_mask, message.image_size_x, message.image_size_y); CBOR_ENC_CHANNELS(mapEncoder, "channels", message.channels); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); @@ -378,7 +388,7 @@ void JFJochFrameSerializer::SerializeSequenceEnd(const EndMessage& message) { CborEncoder encoder, mapEncoder; cbor_encoder_init(&encoder, buffer.data(), buffer.size(), 0); cborErr(cbor_encode_tag(&encoder,CborSignatureTag )); - cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 8)); + cborErr(cbor_encoder_create_map(&encoder, &mapEncoder, 9)); CBOR_ENC(mapEncoder, "type", "end"); CBOR_ENC(mapEncoder, "series_unique_id", message.series_unique_id); @@ -388,7 +398,7 @@ void JFJochFrameSerializer::SerializeSequenceEnd(const EndMessage& message) { CBOR_ENC(mapEncoder, "receiver_efficiency", message.efficiency); CBOR_ENC(mapEncoder, "write_master_file", message.write_master_file); CBOR_ENC(mapEncoder, "end_date", message.end_date); - + CBOR_ENC_RAD_INT_RESULT(mapEncoder, "rad_int_result", message.rad_int_result); cborErr(cbor_encoder_close_container(&encoder, &mapEncoder)); curr_size = cbor_encoder_get_buffer_size(&encoder, buffer.data()); diff --git a/frame_serialize/JFJochFrameSerializer.h b/frame_serialize/JFJochFrameSerializer.h index fc48c895..f69e756a 100644 --- a/frame_serialize/JFJochFrameSerializer.h +++ b/frame_serialize/JFJochFrameSerializer.h @@ -26,7 +26,6 @@ public: void SerializeSequenceEnd(const EndMessage& message); void SerializeImage(const DataMessage& message); void AppendImage(size_t image_size); - }; diff --git a/image_analysis/RadialIntegrationProfile.cpp b/image_analysis/RadialIntegrationProfile.cpp index cd81c795..7b3c654a 100644 --- a/image_analysis/RadialIntegrationProfile.cpp +++ b/image_analysis/RadialIntegrationProfile.cpp @@ -23,14 +23,19 @@ void RadialIntegrationProfile::Add(const std::vector &in_sum, const std throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Mismatch in size of sum/count datasets"); } -void RadialIntegrationProfile::GetPlot(JFJochProtoBuf::Plot &plot) const { - std::unique_lock ul(m); - - std::vector rad_int_profile(sum.size()); +std::vector RadialIntegrationProfile::GetResult() const { + std::vector rad_int_profile(sum.size(), 0); for (int i = 0; i < sum.size(); i++) { if (count[i] > 0) rad_int_profile[i] = static_cast(sum[i]) / static_cast(count[i]); } + return rad_int_profile; +} + +void RadialIntegrationProfile::GetPlot(JFJochProtoBuf::Plot &plot) const { + std::unique_lock ul(m); + + std::vector rad_int_profile = GetResult(); *plot.mutable_x() = {bin_to_q.begin(), bin_to_q.end()}; *plot.mutable_y() = {rad_int_profile.begin(), rad_int_profile.end()}; diff --git a/image_analysis/RadialIntegrationProfile.h b/image_analysis/RadialIntegrationProfile.h index 867b2dd5..0961345f 100644 --- a/image_analysis/RadialIntegrationProfile.h +++ b/image_analysis/RadialIntegrationProfile.h @@ -18,6 +18,7 @@ class RadialIntegrationProfile { public: explicit RadialIntegrationProfile(RadialIntegrationMapping &mapping); void Add(const std::vector &sum, const std::vector &count); + std::vector GetResult() const; void GetPlot(JFJochProtoBuf::Plot &plot) const; }; diff --git a/receiver/JFJochReceiver.cpp b/receiver/JFJochReceiver.cpp index 7b6a8c6b..7fd4524b 100644 --- a/receiver/JFJochReceiver.cpp +++ b/receiver/JFJochReceiver.cpp @@ -602,6 +602,11 @@ void JFJochReceiver::FinalizeMeasurement() { message.end_date = time_UTC(std::chrono::system_clock::now()); message.write_master_file = true; + if (rad_int_profile) + message.rad_int_result["dataset"] = rad_int_profile->GetResult(); + for (int i = 0; i < rad_int_profile_per_file.size(); i++) + message.rad_int_result["file" + std::to_string(i)] = rad_int_profile_per_file[i]->GetResult(); + image_pusher.EndDataCollection(message); logger.Info("Disconnected from writers"); } diff --git a/tests/CBORTest.cpp b/tests/CBORTest.cpp index 29ff78eb..1e6e2bb0 100644 --- a/tests/CBORTest.cpp +++ b/tests/CBORTest.cpp @@ -152,6 +152,38 @@ TEST_CASE("CBORSerialize_End", "[CBOR]") { REQUIRE(output_message.write_master_file == message.write_master_file); REQUIRE(output_message.series_id == message.series_id); REQUIRE(output_message.series_unique_id == message.series_unique_id); + REQUIRE(output_message.rad_int_result.empty()); +} + +TEST_CASE("CBORSerialize_End_RadIntResult", "[CBOR]") { + JFJochFrameSerializer serializer(8*1024*1024); + + EndMessage message { + .number_of_images = 57789, + .max_receiver_delay = 3456, + .efficiency = 0.99, + .write_master_file = true, + .end_date = "ccc", + .series_unique_id = "bla5", + .series_id = 45676782 + }; + + message.rad_int_result["avg"] = {11.0, 12.0, 13.0}; + message.rad_int_result["file0"] = {56.0, 75.0, 34.0}; + + REQUIRE_NOTHROW(serializer.SerializeSequenceEnd(message)); + + JFJochFrameDeserializer deserializer; + REQUIRE_NOTHROW(deserializer.Process(serializer.GetBuffer(), serializer.GetBufferSize())); + REQUIRE(deserializer.GetType() == JFJochFrameDeserializer::Type::END); + + EndMessage output_message{}; + REQUIRE_NOTHROW(output_message = deserializer.GetEndMessage()); + REQUIRE(output_message.rad_int_result.size() == 2); + REQUIRE(output_message.rad_int_result.contains("avg")); + REQUIRE(output_message.rad_int_result.contains("file0")); + CHECK(message.rad_int_result["avg"] == output_message.rad_int_result["avg"]); + CHECK(message.rad_int_result["file0"] == output_message.rad_int_result["file0"]); } TEST_CASE("CBORSerialize_Image", "[CBOR]") { diff --git a/tests/HDF5WritingTest.cpp b/tests/HDF5WritingTest.cpp index 73638298..d115a8c0 100644 --- a/tests/HDF5WritingTest.cpp +++ b/tests/HDF5WritingTest.cpp @@ -137,6 +137,34 @@ TEST_CASE("HDF5MasterFile", "[HDF5][Full]") { REQUIRE (H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0); } +TEST_CASE("HDF5MasterFile_RadInt", "[HDF5][Full]") { + { + RegisterHDF5Filter(); + DiffractionExperiment x(DetectorGeometry(8, 2, 8, 36)); + + x.DetectorDistance_mm(50).BeamX_pxl(1000).BeamY_pxl(1000); + x.QSpacingForRadialInt_recipA(0.1).LowQForRadialInt_recipA(0.1).HighQForRadialInt_recipA(4); + + x.FilePrefix("test01_rad_int").ImagesPerTrigger(950); + + RadialIntegrationMapping mapping(x); + RadialIntegrationProfile profile(mapping); + + StartMessage start_message; + x.FillMessage(start_message); + start_message.rad_int_bin_to_q = mapping.GetBinToQ(); + EndMessage end_message; + end_message.number_of_images = x.GetImageNum(); + end_message.rad_int_result["avg1"] = profile.GetResult(); + end_message.rad_int_result["avg2"] = profile.GetResult(); + + REQUIRE_NOTHROW(HDF5Metadata::NXmx(start_message, end_message)); + } + remove("test01_rad_int_master.h5"); + // No leftover HDF5 objects + REQUIRE (H5Fget_obj_count(H5F_OBJ_ALL, H5F_OBJ_ALL) == 0); +} + TEST_CASE("HDF5Writer", "[HDF5][Full]") { { RegisterHDF5Filter(); @@ -208,7 +236,7 @@ TEST_CASE("HDF5Writer_Rad_Int_Profile", "[HDF5][Full]") { std::vector rad_int_profile(mapping.GetBinNumber(), 4.0); std::vector rad_int_avg(mapping.GetBinNumber(), 0.33); - x.FilePrefix("test02_1p10_spots").ImagesPerTrigger(5).DataFileCount(2).Compression(JFJochProtoBuf::NO_COMPRESSION); + x.FilePrefix("test02_1p10_rad_int").ImagesPerTrigger(5).DataFileCount(2).Compression(JFJochProtoBuf::NO_COMPRESSION); StartMessage start_message; x.FillMessage(start_message); start_message.rad_int_bin_to_q = mapping.GetBinToQ(); diff --git a/writer/HDF5NXmx.cpp b/writer/HDF5NXmx.cpp index c7ad4a9d..a107d2c0 100644 --- a/writer/HDF5NXmx.cpp +++ b/writer/HDF5NXmx.cpp @@ -28,6 +28,7 @@ void HDF5Metadata::NXmx( const StartMessage &start, const EndMessage &end) { Beam(&hdf5_file, start, end); Sample(&hdf5_file, start, end); Calibration(&hdf5_file, start, end); + RadInt(&hdf5_file, start, end); } void HDF5Metadata::Time(HDF5File *hdf5_file, const StartMessage &start, const EndMessage &end) { @@ -254,4 +255,14 @@ std::string HDF5Metadata::DataFileName(const std::string &prefix, int64_t file_n "Format doesn't allow for more than 1 thousand files"); else return fmt::format("{:s}_data_{:03d}.h5", prefix, file_number); -} \ No newline at end of file +} + +void HDF5Metadata::RadInt(HDF5File *hdf5_file, const StartMessage &start, const EndMessage &end) { + if (!start.rad_int_bin_to_q.empty()) { + HDF5Group(*hdf5_file, "/entry/result").NXClass("NXcollection"); + HDF5Group rad_int_group(*hdf5_file, "/entry/result/rad_int"); + rad_int_group.SaveVector("bin_to_q", start.rad_int_bin_to_q); + for (const auto &[x,y] : end.rad_int_result) + rad_int_group.SaveVector(x, y); + } +} diff --git a/writer/HDF5NXmx.h b/writer/HDF5NXmx.h index 7ca6200f..05516b87 100644 --- a/writer/HDF5NXmx.h +++ b/writer/HDF5NXmx.h @@ -27,6 +27,7 @@ namespace HDF5Metadata { void Mask(HDF5File *hdf5_file, const StartMessage &start, const EndMessage &end); void Calibration(HDF5File *hdf5_file, const StartMessage &start, const EndMessage &end); + void RadInt(HDF5File *hdf5_file, const StartMessage &start, const EndMessage &end); std::string DataFileName(const std::string& prefix, int64_t file_number); }