// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute // SPDX-License-Identifier: GPL-3.0-only #include #include "../common/JFJochException.h" #include "../common/Definitions.h" #include "SLSDetectorWrapper.h" void SLSDetectorWrapper::Initialize(DiffractionExperiment& experiment, const std::vector& net_config) { if (experiment.GetDetectorType() == DetectorType::DECTRIS) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "DectrisDetectorWrapper needs PSI detector"); logger.Info("Initialize detector {}", experiment.GetDetectorSetup().GetDescription()); det_type = experiment.GetDetectorSetup().GetDetectorType(); try { auto module_hostname = experiment.GetDetectorModuleHostname(); if (!module_hostname.empty()) det.setHostname(module_hostname); else throw JFJochException(JFJochExceptionCategory::Detector, "Detector hostname not provided"); auto mod_cfg = experiment.GetDetectorModuleConfig(net_config); if (det_type == DetectorType::JUNGFRAU) { if (det.size() != experiment.GetModulesNum()) { logger.Error("Discrepancy in module number between DAQ and detector"); throw JFJochException(JFJochExceptionCategory::Detector, "Discrepancy in module number between DAQ and detector"); } // always try to stop new detector first det.stopDetector(); det.setNumberofUDPInterfaces(experiment.GetUDPInterfaceCount()); for (int i = 0; i < mod_cfg.size(); i++) { logger.Info("Configure network for module {}", i); auto &cfg = mod_cfg[i]; det.setSourceUDPIP(sls::IpAddr(cfg.ipv4_src_addr_1), {i}); det.setSourceUDPMAC(sls::MacAddr(BASE_DETECTOR_MAC + i * 2), {i}); det.setDestinationUDPPort(16384 + (cfg.data_stream<<8) + cfg.module_id_in_data_stream * 2, i); det.setDestinationUDPIP(sls::IpAddr(cfg.ipv4_dest_addr_1), {i}); det.setDestinationUDPMAC(sls::MacAddr(cfg.mac_addr_dest_1), {i}); if (experiment.GetUDPInterfaceCount() == 2) { det.setSourceUDPIP2(sls::IpAddr(cfg.ipv4_src_addr_2), {i}); det.setSourceUDPMAC2(sls::MacAddr(BASE_DETECTOR_MAC + i * 2 + 1), {i}); det.setDestinationUDPPort2(16384 + (cfg.data_stream<<8) + cfg.module_id_in_data_stream * 2 + 1, i); det.setDestinationUDPIP2(sls::IpAddr(cfg.ipv4_dest_addr_2), {i}); det.setDestinationUDPMAC2(sls::MacAddr(cfg.mac_addr_dest_2), {i}); } uint32_t tmp = (cfg.module_id_in_data_stream * 2) % UINT16_MAX; uint32_t column_id_register = ((tmp + 1) << 16) | tmp; det.writeRegister(0x7C, column_id_register, {i}); } det.setTemperatureControl(true); det.setThresholdTemperature(experiment.GetDetectorSetup().GetTempThreshold_degC(), {0});; auto tx_delay = experiment.GetDetectorSetup().GetTxDelay(); if (tx_delay.size() == experiment.GetModulesNum()) { for (int i = 0 ; i < tx_delay.size(); i++) det.setTransmissionDelayFrame(tx_delay[i], {i}); } if (experiment.GetUDPInterfaceCount() == 2) det.setReadoutSpeed(slsDetectorDefs::speedLevel::FULL_SPEED); else det.setReadoutSpeed(slsDetectorDefs::speedLevel::HALF_SPEED); det.setAutoComparatorDisable(true); if (!det.getPowerChip().squash(false)) { det.setPowerChip(true); std::this_thread::sleep_for(std::chrono::seconds(5)); } } else if (det_type == DetectorType::EIGER) { if (det.size() != 2 * experiment.GetModulesNum()) { logger.Error("Discrepancy in module number between DAQ and detector"); throw JFJochException(JFJochExceptionCategory::Detector, "Discrepancy in module number between DAQ and detector"); } det.setDynamicRange(experiment.GetEigerBitDepth()); det.setTenGiga(true); auto trim_directory = experiment.GetDetectorSetup().GetTrimFileDirectory(); if (!trim_directory.empty()) { // Hardcoded for now - need to make it nicer std::vector trim_en = {4500, 5400, 6400, 8000, 9900, 15800}; det.setTrimEnergies(trim_en); det.setSettingsPath(trim_directory); } 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); 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}); if (!trim_files.empty()) { det.loadTrimbits(trim_files[2 * i], {2 * i}); det.loadTrimbits(trim_files[2 * i + 1], {2 * i + 1}); } } } det.setHighVoltage(experiment.GetDetectorSetup().GetHighVoltage()); } catch (const std::exception &e) { logger.ErrorException(e); throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } logger.Info(" ... done"); } 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"); auto energy_threshold_ev = experiment.GetEigerThreshold_keV() * 1000.0f; if (det.getThresholdEnergy().squash(0) != energy_threshold_ev) det.setThresholdEnergy(energy_threshold_ev); det.setPeriod(experiment.GetDetectorPeriod()); } try { InternalStop(); det.setNextFrameNumber(1); det.setNumberOfFrames(experiment.GetFrameNumPerTrigger() / experiment.GetStorageCellNumber()); det.setNumberOfTriggers(experiment.GetNumTriggers()); det.startDetector(); } catch (std::exception &e) { logger.ErrorException(e); throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } logger.Info(" ... done"); } void SLSDetectorWrapper::InternalStop() { // Assume it is executed in try-catch! auto state = GetState(); if (state == DetectorState::ERROR) throw JFJochException(JFJochExceptionCategory::Detector, "Detector in error state"); else if ((state == DetectorState::BUSY) || (state == DetectorState::WAITING)) { try { det.stopDetector(); } catch (...) { // Sometimes stop gives problem - ignore these logger.Warning("Problem with stopping the detector - ignored."); } std::this_thread::sleep_for(std::chrono::milliseconds(10)); state = GetState(); if (state != DetectorState::IDLE) throw JFJochException(JFJochExceptionCategory::Detector, "Detector busy and cannot be stopped"); } } void SLSDetectorWrapper::Deactivate() { logger.Info("Deactivate"); try { InternalStop(); if (det_type == DetectorType::JUNGFRAU) { det.setHighVoltage(0); std::this_thread::sleep_for(std::chrono::seconds(5)); det.setPowerChip(false); det.setMaster(false, 0); det.setSynchronization(false); } } catch (std::exception &e) { logger.ErrorException(e); throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } logger.Info(" ... done"); } void SLSDetectorWrapper::Stop() { logger.Info("Stop"); try { InternalStop(); } catch (std::exception &e) { logger.ErrorException(e); throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } logger.Info(" ... done"); } void SLSDetectorWrapper::Trigger() { logger.Info("Trigger"); try { det.sendSoftwareTrigger(); } catch (std::exception &e) { logger.ErrorException(e); throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } logger.Info(" ... done"); } DetectorState SLSDetectorWrapper::GetState() const { if (det.empty()) return DetectorState::NOT_CONNECTED; try { bool is_idle = true; bool is_waiting = true; for (const auto & i : det.getDetectorStatus()) { if (i == slsDetectorDefs::runStatus::ERROR) return DetectorState::ERROR; if ((i != slsDetectorDefs::runStatus::IDLE) && (i != slsDetectorDefs::runStatus::STOPPED) && (i != slsDetectorDefs::runStatus::RUN_FINISHED)) is_idle = false; if (i != slsDetectorDefs::WAITING) is_waiting = false; } if (is_idle) return DetectorState::IDLE; else if (is_waiting) return DetectorState::WAITING; else return DetectorState::BUSY; } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } } int64_t SLSDetectorWrapper::GetNumberOfTriggersLeft() const { int64_t ret = 0; for (const auto & i : det.getNumberOfTriggersLeft()) { if (i > ret) ret = i; } return ret; } DetectorPowerState SLSDetectorWrapper::GetPowerState() const { uint64_t count_on = 0; for (const auto &i : det.getPowerChip()) { if (i) count_on++; } if (count_on == 0) return DetectorPowerState::OFF; else if (count_on == det.size()) return DetectorPowerState::ON; else return DetectorPowerState::PARTIAL; } int64_t SLSDetectorWrapper::GetFirmwareVersion() const { try { auto result = det.getFirmwareVersion(); return result.squash(0x0); } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } } std::string SLSDetectorWrapper::GetDetectorServerVersion() const { try { auto result = det.getDetectorServerVersion(); return result.squash("mixed"); } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } } std::vector SLSDetectorWrapper::GetFPGATemperatures() const { try { auto result = det.getTemperature(slsDetectorDefs::TEMPERATURE_FPGA); std::vector ret; for (int i = 0; i < result.size(); i++) ret.push_back(result[i]); return ret; } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } } std::vector SLSDetectorWrapper::GetHighVoltage() const { try { auto result = det.getHighVoltage(); std::vector ret; for (int i : result) ret.push_back(i); return ret; } catch (std::exception &e) { throw JFJochException(JFJochExceptionCategory::Detector, e.what()); } } DetectorStatus SLSDetectorWrapper::GetStatus() const { DetectorStatus status{}; if (det.empty()) return DetectorStatus{ .detector_state = DetectorState::NOT_CONNECTED, .power_state = DetectorPowerState::OFF, .detector_server_version = "N/A" }; status.detector_server_version = GetDetectorServerVersion(); status.detector_state = GetState(); if (det_type == DetectorType::JUNGFRAU) { status.power_state = GetPowerState(); status.remaining_triggers = GetNumberOfTriggersLeft(); } else { status.power_state = DetectorPowerState::ON; status.remaining_triggers = -1; } status.temperature_fpga_degC = GetFPGATemperatures(); status.high_voltage_V = GetHighVoltage(); return status; } void SLSDetectorWrapper::Configure(const DiffractionExperiment &experiment) { if (det_type == DetectorType::JUNGFRAU) { if (experiment.IsFixedGainG1()) { if ((experiment.GetDetectorMode() == DetectorMode::PedestalG0) || (experiment.GetDetectorMode() == DetectorMode::PedestalG2)) throw JFJochException(JFJochExceptionCategory::Detector, "Pedestal G0/G2 doesn't make sense for fixed G1 mode"); det.setGainMode(slsDetectorDefs::FIX_G1); } else { switch (experiment.GetDetectorMode()) { case DetectorMode::PedestalG1: det.setGainMode(slsDetectorDefs::gainMode::FORCE_SWITCH_G1); break; case DetectorMode::PedestalG2: det.setGainMode(slsDetectorDefs::gainMode::FORCE_SWITCH_G2); break; default: det.setGainMode(slsDetectorDefs::gainMode::DYNAMIC); break; } } det.setStorageCellStart(experiment.GetStorageCellStart()); det.setNumberOfAdditionalStorageCells(experiment.GetStorageCellNumber() - 1); det.setStorageCellDelay( experiment.GetStorageCellDelay() - std::chrono::nanoseconds(MIN_STORAGE_CELL_DELAY_IN_NS)); if (experiment.IsUsingGainHG0()) det.setSettings(slsDetectorDefs::HIGHGAIN0); else det.setSettings(slsDetectorDefs::GAIN0); if (experiment.IsDetectorModuleSync()) { det.setMaster(true, 0); det.setSynchronization(true); } det.setDelayAfterTrigger(experiment.GetDetectorDelay()); } else if (det_type == DetectorType::EIGER) { auto energy_threshold = experiment.GetDetectorSettings().GetEigerThreshold_keV(); if (energy_threshold.has_value()) det.setThresholdEnergy(std::lround(energy_threshold.value() * 1000.0)); } switch (experiment.GetDetectorTiming()) { case DetectorTiming::Auto: det.setTimingMode(slsDetectorDefs::timingMode::AUTO_TIMING); break; case DetectorTiming::Trigger: det.setTimingMode(slsDetectorDefs::timingMode::TRIGGER_EXPOSURE); break; case DetectorTiming::Burst: if (det_type == DetectorType::JUNGFRAU) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Burst timing mode not supported with JUNGFRAU"); det.setTimingMode(slsDetectorDefs::timingMode::BURST_TRIGGER); break; case DetectorTiming::Gated: if (det_type == DetectorType::JUNGFRAU) throw JFJochException(JFJochExceptionCategory::InputParameterInvalid, "Gated timing mode not supported with JUNGFRAU"); det.setTimingMode(slsDetectorDefs::timingMode::GATED); break; } det.setPeriod(experiment.GetDetectorPeriod()); det.setExptime(std::chrono::microseconds(experiment.GetFrameCountTime())); } void SLSDetectorWrapper::LoadPixelMask(PixelMask &mask) { // Do nothing }