From 5cd9031c7593be323d01fcdf70d6fff62c10caeb Mon Sep 17 00:00:00 2001 From: leonarski_f Date: Tue, 16 Jun 2026 08:39:48 +0200 Subject: [PATCH] Fix to handle EIGER missing module --- detector_control/SLSDetectorWrapper.cpp | 126 +++++++++++++++++------- detector_control/SLSDetectorWrapper.h | 23 +++++ 2 files changed, 114 insertions(+), 35 deletions(-) diff --git a/detector_control/SLSDetectorWrapper.cpp b/detector_control/SLSDetectorWrapper.cpp index 6b27c672..81187860 100644 --- a/detector_control/SLSDetectorWrapper.cpp +++ b/detector_control/SLSDetectorWrapper.cpp @@ -7,10 +7,24 @@ #include "../common/Definitions.h" #include "SLSDetectorWrapper.h" +namespace { + // Value reported for temperature / high voltage of an inactive (excluded) + // half-module, so that the position in the logical vector is preserved. + constexpr int64_t kInactiveModulePlaceholder = -1; +} + SLSDetectorWrapper::SLSDetectorWrapper() { logger.Info("SLS detector package {} client {}", det.getPackageVersion(), det.getClientVersion()); } +std::vector SLSDetectorWrapper::MapToLogical(const std::vector& sls_values, + int64_t placeholder) const { + std::vector out(n_logical_units, placeholder); + for (size_t s = 0; (s < sls_values.size()) && (s < sls_to_logical.size()); s++) + out[sls_to_logical[s]] = sls_values[s]; + return out; +} + void SLSDetectorWrapper::Initialize(DiffractionExperiment& experiment, const std::vector& net_config) { if (experiment.GetDetectorType() == DetectorType::DECTRIS) @@ -20,16 +34,47 @@ void SLSDetectorWrapper::Initialize(DiffractionExperiment& experiment, det_type = experiment.GetDetectorSetup().GetDetectorType(); try { + // Per-(half-)module hostnames in logical order. + // EIGER: one entry per half-module, logical index = 2 * module + half + // JUNGFRAU: one entry per module + // An empty entry marks an inactive half-module: it is excluded from the + // slsDetectorPackage (never connected, never configured) but keeps its + // slot in the logical numbering. auto module_hostname = experiment.GetDetectorModuleHostname(); - if (!module_hostname.empty()) - det.setHostname(module_hostname); - else + + n_logical_units = static_cast(module_hostname.size()); + + std::vector active_hostname; + sls_to_logical.clear(); + for (int k = 0; k < n_logical_units; k++) { + if (!module_hostname[k].empty()) { + active_hostname.push_back(module_hostname[k]); + sls_to_logical.push_back(k); + } else { + logger.Info("Logical half-module {} is inactive (empty hostname) - excluded from detector", k); + } + } + + if (active_hostname.empty()) throw JFJochException(JFJochExceptionCategory::Detector, - "Detector hostname not provided"); + "No active module hostname provided"); + + det.setHostname(active_hostname); + + // Reverse map: logical index -> SLS index (-1 if inactive). + std::vector logical_to_sls(n_logical_units, -1); + for (size_t s = 0; s < sls_to_logical.size(); s++) + logical_to_sls[sls_to_logical[s]] = static_cast(s); auto mod_cfg = experiment.GetDetectorModuleConfig(net_config); if (det_type == DetectorType::JUNGFRAU) { + if (n_logical_units != experiment.GetModulesNum()) + throw JFJochException(JFJochExceptionCategory::Detector, + "Hostname vector size must equal module count for JUNGFRAU"); + if (sls_to_logical.size() != static_cast(n_logical_units)) + throw JFJochException(JFJochExceptionCategory::Detector, + "Inactive modules are not supported for JUNGFRAU"); if (det.size() != experiment.GetModulesNum()) { logger.Error("Discrepancy in module number between DAQ and detector"); throw JFJochException(JFJochExceptionCategory::Detector, @@ -85,8 +130,12 @@ void SLSDetectorWrapper::Initialize(DiffractionExperiment& experiment, } } else if (det_type == DetectorType::EIGER) { - if (det.size() != 2 * experiment.GetModulesNum()) { - logger.Error("Discrepancy in module number between DAQ and detector"); + if (n_logical_units != 2 * experiment.GetModulesNum()) + throw JFJochException(JFJochExceptionCategory::Detector, + "Hostname vector size must equal 2 x module count for EIGER"); + if (det.size() != sls_to_logical.size()) { + logger.Error("Discrepancy in active module number between DAQ ({}) and detector ({})", + sls_to_logical.size(), det.size()); throw JFJochException(JFJochExceptionCategory::Detector, "Discrepancy in module number between DAQ and detector"); } @@ -104,24 +153,32 @@ void SLSDetectorWrapper::Initialize(DiffractionExperiment& experiment, auto trim_files = experiment.GetDetectorSetup().GetTrimFileNames(); for (int i = 0; i < mod_cfg.size(); i++) { - logger.Info("Configure network for module {}", i); - auto &cfg = mod_cfg[i]; - det.setDestinationUDPPort(16384 + (cfg.data_stream<<8) + cfg.module_id_in_data_stream * 2, 2 * i); - det.setDestinationUDPPort2(16384 + (cfg.data_stream<<8) + cfg.module_id_in_data_stream * 2, 2 * i); - det.setDestinationUDPPort(16384 + (cfg.data_stream<<8) + cfg.module_id_in_data_stream * 2 + 1, 2 * i + 1); - det.setDestinationUDPPort2(16384 + (cfg.data_stream<<8) + cfg.module_id_in_data_stream * 2 + 1, 2 * i + 1); + for (int h = 0; h < 2; h++) { + int logical = 2 * i + h; + int s = logical_to_sls[logical]; + if (s < 0) { + logger.Info("Skip inactive half-module {} (no slsDetectorPackage configuration)", logical); + continue; + } - det.setSourceUDPIP(sls::IpAddr(cfg.ipv4_src_addr_1), {2 * i, 2 * i + 1}); - det.setDestinationUDPIP(sls::IpAddr(cfg.ipv4_dest_addr_1), {2 * i, 2 * i + 1}); - det.setDestinationUDPMAC(sls::MacAddr(cfg.mac_addr_dest_1), {2 * i, 2 * i + 1}); - //det.setRow(static_cast(cfg.module_id_in_data_stream * 2), {2 * i}); - //det.setRow(static_cast(cfg.module_id_in_data_stream * 2 + 1), {2 * i + 1}); + logger.Info("Configure network for half-module {} (SLS index {})", logical, s); - if (!trim_files.empty()) { - det.loadTrimbits(trim_files[2 * i], {2 * i}); - det.loadTrimbits(trim_files[2 * i + 1], {2 * i + 1}); + // The UDP destination port carries the LOGICAL module id and + // half, so the surviving modules land at the correct place in + // the assembled image regardless of the SLS index shift. + uint16_t port = 16384 + (cfg.data_stream << 8) + cfg.module_id_in_data_stream * 2 + h; + det.setDestinationUDPPort(port, s); + det.setDestinationUDPPort2(port, s); + + det.setSourceUDPIP(sls::IpAddr(cfg.ipv4_src_addr_1), {s}); + det.setDestinationUDPIP(sls::IpAddr(cfg.ipv4_dest_addr_1), {s}); + det.setDestinationUDPMAC(sls::MacAddr(cfg.mac_addr_dest_1), {s}); + //det.setRow(static_cast(cfg.module_id_in_data_stream * 2 + h), {s}); + + if (!trim_files.empty() && (logical < static_cast(trim_files.size()))) + det.loadTrimbits(trim_files[logical], {s}); } } } @@ -136,15 +193,11 @@ void SLSDetectorWrapper::Initialize(DiffractionExperiment& experiment, void SLSDetectorWrapper::Start(const DiffractionExperiment& experiment) { logger.Info("Start"); - if (det_type == DetectorType::JUNGFRAU) { - if (det.size() != experiment.GetModulesNum()) - throw JFJochException(JFJochExceptionCategory::Detector, - "Discrepancy in module number between DAQ and detector"); - } else if (det_type == DetectorType::EIGER) { - if (det.size() != 2 * experiment.GetModulesNum()) - throw JFJochException(JFJochExceptionCategory::Detector, - "Discrepancy in module number between DAQ and detector"); + if (det.size() != sls_to_logical.size()) + throw JFJochException(JFJochExceptionCategory::Detector, + "Discrepancy in module number between DAQ and detector"); + if (det_type == DetectorType::EIGER) { auto energy_threshold_ev = experiment.GetEigerThreshold_keV() * 1000.0f; if (det.getThresholdEnergy().squash(0) != energy_threshold_ev) det.setThresholdEnergy(energy_threshold_ev); @@ -303,10 +356,13 @@ std::string SLSDetectorWrapper::GetDetectorServerVersion() const { std::vector SLSDetectorWrapper::GetFPGATemperatures() const { try { auto result = det.getTemperature(slsDetectorDefs::TEMPERATURE_FPGA); - std::vector ret; + std::vector sls_values; + sls_values.reserve(result.size()); for (int i = 0; i < result.size(); i++) - ret.push_back(result[i]); - return ret; + sls_values.push_back(result[i]); + // Report at the logical (Jungfraujoch) module position; inactive + // half-modules are filled with a placeholder. + return MapToLogical(sls_values, kInactiveModulePlaceholder); } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } @@ -315,10 +371,10 @@ std::vector SLSDetectorWrapper::GetFPGATemperatures() const { std::vector SLSDetectorWrapper::GetHighVoltage() const { try { auto result = det.getHighVoltage(); - std::vector ret; + std::vector sls_values; for (int i : result) - ret.push_back(i); - return ret; + sls_values.push_back(i); + return MapToLogical(sls_values, kInactiveModulePlaceholder); } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } @@ -418,4 +474,4 @@ void SLSDetectorWrapper::Configure(const DiffractionExperiment &experiment) { void SLSDetectorWrapper::LoadPixelMask(PixelMask &mask) { // Do nothing -} \ No newline at end of file +} diff --git a/detector_control/SLSDetectorWrapper.h b/detector_control/SLSDetectorWrapper.h index 03c44912..05aea96d 100644 --- a/detector_control/SLSDetectorWrapper.h +++ b/detector_control/SLSDetectorWrapper.h @@ -14,6 +14,29 @@ class SLSDetectorWrapper : public DetectorWrapper { Logger logger{"SLSDetectorWrapper"}; DetectorType det_type = DetectorType::JUNGFRAU; sls::Detector det; + + // Mapping between the slsDetectorPackage module index space and the + // Jungfraujoch (logical) module index space. + // + // Inactive half-modules (empty hostname string) are excluded from the + // slsDetectorPackage entirely - they are never added with setHostname() and + // never configured. They DO keep their slot in the logical numbering, so the + // geometry, module count and UDP destination ports of the surviving modules + // are unaffected. + // + // sls_to_logical[s] -> logical index of the s-th module known to SLS + // n_logical_units -> total logical units (2 * modules for EIGER, + // modules for JUNGFRAU), i.e. including placeholders + std::vector sls_to_logical; + int n_logical_units = 0; + + // Scatter an SLS-ordered (active-only) per-module vector back into a + // logical-ordered vector of size n_logical_units, filling inactive slots + // with the given placeholder. Used so that temperature / HV are reported at + // the correct Jungfraujoch module position. + [[nodiscard]] std::vector MapToLogical(const std::vector& sls_values, + int64_t placeholder) const; + void InternalStop(); [[nodiscard]] int64_t GetNumberOfTriggersLeft() const; [[nodiscard]] DetectorPowerState GetPowerState() const;