Files
Jungfraujoch/broker/JFJochBrokerParser.cpp
T
leonarski_fandClaude Opus 4.8 b51036ad30
Build Packages / build:rpm (ubuntu2404_nocuda) (push) Successful in 14m41s
Build Packages / build:rpm (rocky8_nocuda) (push) Successful in 15m32s
Build Packages / build:rpm (rocky8) (push) Successful in 15m27s
Build Packages / build:rpm (rocky8_sls9) (push) Successful in 15m37s
Build Packages / build:rpm (ubuntu2204_nocuda) (push) Successful in 15m46s
Build Packages / build:rpm (rocky9_nocuda) (push) Successful in 15m54s
Build Packages / build:rpm (rocky9_sls9) (push) Successful in 16m1s
Build Packages / XDS test (neggia plugin) (push) Successful in 7m38s
Build Packages / XDS test (durin plugin) (push) Successful in 8m7s
Build Packages / XDS test (JFJoch plugin) (push) Successful in 8m7s
Build Packages / Create release (push) Skipped
Build Packages / Generate python client (push) Successful in 26s
Build Packages / Build documentation (push) Successful in 57s
Build Packages / build:rpm (ubuntu2404) (push) Successful in 11m22s
Build Packages / build:rpm (rocky9) (push) Successful in 12m20s
Build Packages / build:rpm (ubuntu2204) (push) Successful in 12m17s
Build Packages / DIALS test (push) Successful in 12m43s
Build Packages / Unit tests (push) Successful in 59m55s
Build Packages / build:windows:nocuda (push) Failing after 14m5s
Build Packages / build:windows:cuda (push) Failing after 14m5s
broker: don't clobber sensor thickness/material with the API default
JFJochBrokerParser set SensorThickness_um / SensorMaterial unconditionally from
the request's Detector model, but that model defaults them to 320 um / Si with
IsSet=false. So any start request that didn't explicitly carry the sensor
overwrote the detector-reported value (DECTRIS SIMPLON read) or the
detector-specific default with 320 um -- the "PILATUS4 ends up 320 um" symptom.
Guard both with the IsSet flag, mirroring highVoltageV just above. The
receiver -> FillMessage -> CBOR -> writer chain was already correct; the value
was simply wrong at the source.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-29 23:00:06 +02:00

272 lines
12 KiB
C++

// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
// SPDX-License-Identifier: GPL-3.0-only
#include "JFJochBrokerParser.h"
#include "../common/NetworkAddressConvert.h"
#include "../image_pusher/ZMQStream2Pusher.h"
#include "../image_pusher/CBORFilePusher.h"
#include "../image_pusher/HDF5FilePusher.h"
#include "OpenAPIConvert.h"
#include "Detector_type.h"
#include "../image_pusher/NonePusher.h"
#include "../image_pusher/TCPStreamPusher.h"
DetectorGeometryModular ParseStandardDetectorGeometry(const org::openapitools::server::model::Detector &j) {
auto s = j.getStandardGeometry();
return DetectorGeometryModular(s.getNmodules(), s.getModulesInRow(), s.getGapX(), s.getGapY(), j.isMirrorY());
}
DetectorModuleGeometry::Direction Convert(const org::openapitools::server::model::Detector_module_direction& d) {
switch (d.getValue()) {
case org::openapitools::server::model::Detector_module_direction::eDetector_module_direction::XP:
return DetectorModuleGeometry::Direction::Xpos;
case org::openapitools::server::model::Detector_module_direction::eDetector_module_direction::XN:
return DetectorModuleGeometry::Direction::Xneg;
case org::openapitools::server::model::Detector_module_direction::eDetector_module_direction::YP:
return DetectorModuleGeometry::Direction::Ypos;
case org::openapitools::server::model::Detector_module_direction::eDetector_module_direction::YN:
return DetectorModuleGeometry::Direction::Yneg;
default:
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "invalid detector direction");
}
}
DetectorType Convert(const org::openapitools::server::model::Detector_type &d) {
switch (d.getValue()) {
case org::openapitools::server::model::Detector_type::eDetector_type::EIGER:
return DetectorType::EIGER;
case org::openapitools::server::model::Detector_type::eDetector_type::JUNGFRAU:
return DetectorType::JUNGFRAU;
case org::openapitools::server::model::Detector_type::eDetector_type::DECTRIS:
return DetectorType::DECTRIS;
default:
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "invalid detector type");
}
}
DetectorGeometryModular ParseCustomDetectorGeometry(const org::openapitools::server::model::Detector &j) {
std::vector<DetectorModuleGeometry> modules;
for (const auto &iter: j.getCustomGeometry()) {
auto fast = Convert(iter.getFastAxis());
auto slow = Convert(iter.getSlowAxis());
modules.emplace_back(iter.getX0(), iter.getY0(), fast, slow);
}
return DetectorGeometryModular(modules, j.isMirrorY());
}
DetectorGeometryModular ParseDetectorGeometry(const org::openapitools::server::model::Detector &d) {
if (d.standardGeometryIsSet() && d.customGeometryIsSet())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Need to set EITHER standard or custom geometry");
if (d.standardGeometryIsSet())
return ParseStandardDetectorGeometry(d);
else if (d.customGeometryIsSet())
return ParseCustomDetectorGeometry(d);
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Need to set EITHER standard or custom geometry");
}
DetectorSetup ParseDetectorSetup(const org::openapitools::server::model::Detector &d) {
DetectorType detector_type = Convert(d.getType());
if (detector_type == DetectorType::DECTRIS) {
std::string hostname;
if (d.getHostname().size() > 1)
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"DECTRIS detector requires single hostname (or none)");
else if (d.getHostname().size() == 1)
hostname = d.getHostname()[0];
DetectorSetup setup = DetDECTRIS(1,1, d.getDescription(), hostname);
if (d.roiModeIsSet())
setup.DECTRISROI(d.getRoiMode());
return setup;
}
DetectorGeometryModular geom = ParseDetectorGeometry(d);
DetectorSetup setup(geom, detector_type, d.getDescription(), d.getHostname());
auto calib = d.getCalibrationFile();
auto trim_energies = d.getTrimEnergiesEV();
if (!calib.empty()) {
switch (detector_type) {
case DetectorType::EIGER:
setup.SetTrimFiles(calib);
if (trim_energies.empty())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Trimming energies not provided");
setup.TrimEnergies_eV(trim_energies);
break;
case DetectorType::JUNGFRAU:
setup.LoadGain(calib);
break;
default:
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Detector type not supported");
}
}
switch (detector_type) {
case DetectorType::EIGER:
case DetectorType::JUNGFRAU:
setup.PixelSize_um(75.0f);
break;
default:
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
"Detector type not supported");
}
if (d.highVoltageVIsSet())
setup.HighVoltage(d.getHighVoltageV());
setup.UDPInterfaceCount(d.getUdpInterfaceCount())
.SerialNumber(d.getSerialNumber())
.ModuleSync(d.isModuleSync());
// Only override the sensor from the request when it explicitly sets these. The API model defaults
// them (320 um / Si) with IsSet=false, so an unconditional set would clobber the detector-reported
// value (DECTRIS SIMPLON read) or a detector-specific default with 320 um (highVoltageV above is
// guarded the same way).
if (d.sensorThicknessUmIsSet())
setup.SensorThickness_um(d.getSensorThicknessUm());
if (d.sensorMaterialIsSet())
setup.SensorMaterial(d.getSensorMaterial());
if (d.readoutTimeNsIsSet())
setup.ReadOutTime(std::chrono::nanoseconds(d.getReadoutTimeNs()));
if (d.baseDataIpv4AddressIsSet())
setup.BaseIPv4Addr(d.getBaseDataIpv4Address());
if (d.txDelayIsSet())
setup.TxDelay(d.getTxDelay());
if (d.minCountTimeNsIsSet())
setup.MinCountTime(std::chrono::nanoseconds(d.getMinCountTimeNs()));
if (d.minFrameTimeNsIsSet())
setup.MinFrameTime(std::chrono::nanoseconds(d.getMinFrameTimeNs()));
if (d.defaultSettingsIsSet())
setup.DefaultSettings(Convert(d.getDefaultSettings()));
if (d.tempThresoldDegCIsSet())
setup.TempThreshold_degC(d.getTempThresoldDegC());
return setup;
}
void ParseFacilityConfiguration(const org::openapitools::server::model::Jfjoch_settings &j, DiffractionExperiment &experiment) {
if (j.instrumentIsSet())
experiment.ImportInstrumentMetadata(Convert(j.getInstrument()));
if (j.fileWriterIsSet())
experiment.ImportFileWriterSettings(Convert(j.getFileWriter()));
if (j.detectorSettingsIsSet())
experiment.ImportDetectorSettings(Convert(j.getDetectorSettings()));
if (j.azimIntIsSet())
experiment.ImportAzimuthalIntegrationSettings(Convert(j.getAzimInt()));
if (j.imageFormatIsSet())
experiment.ImportImageFormatSettings(Convert(j.getImageFormat()));
if (j.indexingIsSet())
experiment.ImportIndexingSettings(Convert(j.getIndexing()));
if (j.darkMaskIsSet())
experiment.ImportDarkMaskSettings(Convert(j.getDarkMask()));
}
std::unique_ptr<ImagePusher> ParseZMQImagePusher(const org::openapitools::server::model::Jfjoch_settings &j) {
if (!j.zeromqIsSet())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "ZeroMQ settings must be provided");
std::optional<int32_t> send_buffer_size;
if (j.getZeromq().sendBufferSizeIsSet())
send_buffer_size = j.getZeromq().getSendBufferSize();
std::optional<int32_t> send_watermark;
if (j.getZeromq().sendWatermarkIsSet())
send_watermark = j.getZeromq().getSendWatermark();
auto tmp = std::make_unique<ZMQStream2Pusher>(j.getZeromq().getImageSocket(),
send_watermark,
send_buffer_size);
if (j.getZeromq().writerNotificationSocketIsSet())
tmp->WriterNotificationSocket(j.getZeromq().getWriterNotificationSocket());
return std::move(tmp);
}
std::unique_ptr<ImagePusher> ParseTCPImagePusher(const org::openapitools::server::model::Jfjoch_settings &j) {
if (!j.tcpIsSet())
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "TCP/IP Socket settings must be provided");
std::optional<int32_t> send_buffer_size;
if (j.getTcp().sendBufferSizeIsSet())
send_buffer_size = j.getTcp().getSendBufferSize();
auto tmp = std::make_unique<TCPStreamPusher>(j.getTcp().getImageSocket(), j.getTcp().getNwriters(), send_buffer_size);
// Optional liveness/backpressure tuning; unset -> keep the pusher's built-in defaults.
if (j.getTcp().peerLivenessTimeoutMsIsSet() && j.getTcp().getPeerLivenessTimeoutMs() > 0)
tmp->SetPeerLivenessTimeout(std::chrono::milliseconds(j.getTcp().getPeerLivenessTimeoutMs()));
if (j.getTcp().maxBackpressureTimeoutMsIsSet() && j.getTcp().getMaxBackpressureTimeoutMs() > 0)
tmp->SetMaxBackpressureTimeout(std::chrono::milliseconds(j.getTcp().getMaxBackpressureTimeoutMs()));
return std::move(tmp);
}
std::unique_ptr<ImagePusher> ParseImagePusher(const org::openapitools::server::model::Jfjoch_settings &j) {
switch (j.getImagePusher().getValue()) {
case org::openapitools::server::model::Image_pusher_type::eImage_pusher_type::ZEROMQ:
return ParseZMQImagePusher(j);
case org::openapitools::server::model::Image_pusher_type::eImage_pusher_type::TCP:
return ParseTCPImagePusher(j);
case org::openapitools::server::model::Image_pusher_type::eImage_pusher_type::HDF5:
return std::make_unique<HDF5FilePusher>();
case org::openapitools::server::model::Image_pusher_type::eImage_pusher_type::NONE:
return std::make_unique<NonePusher>();
case org::openapitools::server::model::Image_pusher_type::eImage_pusher_type::CBOR:
return std::make_unique<CBORFilePusher>();
default:
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Invalid value");
}
}
void ParseAcquisitionDeviceGroup(const org::openapitools::server::model::Jfjoch_settings &input,
AcquisitionDeviceGroup &aq_devices) {
if (!input.pcieIsSet())
aq_devices.AddHLSDevice(256);
else for (auto &p: input.getPcie()) {
std::optional<uint32_t> ipv4_addr = {};
if (p.ipv4IsSet())
ipv4_addr = IPv4AddressFromStr(p.getIpv4());
aq_devices.AddPCIeDevice(p.getBlk(), ipv4_addr);
}
}
void ParseReceiverSettings(const org::openapitools::server::model::Jfjoch_settings &input, JFJochReceiverService &service) {
// Using default in case
service.NumThreads(input.getReceiverThreads());
if (input.zeromqPreviewIsSet()) {
service.PreviewSocket(input.getZeromqPreview().getSocketAddress());
service.PreviewSocketSettings(Convert(input.getZeromqPreview()));
}
if (input.zeromqMetadataIsSet()) {
service.MetadataSocket(input.getZeromqMetadata().getSocketAddress());
service.MetadataSocketSettings(Convert(input.getZeromqMetadata()));
}
}
SpotFindingSettings ParseSpotFindingSettings(const org::openapitools::server::model::Jfjoch_settings &input) {
if (input.spotFindingIsSet())
return Convert(input.getSpotFinding());
return SpotFindingSettings();
}