436 lines
17 KiB
C++
436 lines
17 KiB
C++
// SPDX-FileCopyrightText: 2024 Filip Leonarski, Paul Scherrer Institute <filip.leonarski@psi.ch>
|
|
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
#include <thread>
|
|
|
|
#include "../common/JFJochException.h"
|
|
#include "../common/Definitions.h"
|
|
#include "SLSDetectorWrapper.h"
|
|
|
|
void SLSDetectorWrapper::Initialize(DiffractionExperiment& experiment,
|
|
const std::vector<AcquisitionDeviceNetConfig>& net_config) {
|
|
if (experiment.GetDetectorType() == DetectorType::DECTRIS)
|
|
throw JFJochException(JFJochExceptionCategory::InputParameterInvalid,
|
|
"DectrisDetectorWrapper needs PSI detector");
|
|
logger.Info("Initialize detector");
|
|
det_type = experiment.GetDetectorSetup().GetDetectorType();
|
|
try {
|
|
auto module_hostname = experiment.GetDetectorModuleHostname();
|
|
|
|
if (det.empty()) {
|
|
if (!module_hostname.empty())
|
|
det.setHostname(module_hostname);
|
|
else
|
|
throw JFJochException(JFJochExceptionCategory::Detector,
|
|
"Detector not configured and hostname not provided");
|
|
} else {
|
|
// Stop the detector
|
|
InternalStop();
|
|
|
|
if (det_type == DetectorType::JUNGFRAU) {
|
|
// Clear synchronization prior to reconfiguring the detector
|
|
det.setMaster(false, 0);
|
|
det.setSynchronization(false);
|
|
}
|
|
|
|
if (!module_hostname.empty()) {
|
|
bool reset = true;
|
|
auto det_module_hostname = det.getHostname();
|
|
|
|
if (module_hostname.size() == det_module_hostname.size()) {
|
|
reset = false;
|
|
for (int i = 0; i < module_hostname.size(); i++) {
|
|
if (module_hostname[i] != det_module_hostname[i])
|
|
reset = true;
|
|
}
|
|
}
|
|
if (reset) {
|
|
logger.Info("Resetting detector module host names");
|
|
det.setHostname(module_hostname);
|
|
}
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
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<int> 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 + 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<uint32_t>(cfg.module_id_in_data_stream * 2), {2 * i});
|
|
//det.setRow(static_cast<uint32_t>(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();
|
|
det.setHighVoltage(0);
|
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|
if (det_type == DetectorType::JUNGFRAU) {
|
|
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<int64_t> SLSDetectorWrapper::GetFPGATemperatures() const {
|
|
try {
|
|
auto result = det.getTemperature(slsDetectorDefs::TEMPERATURE_FPGA);
|
|
std::vector<int64_t> 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<int64_t> SLSDetectorWrapper::GetHighVoltage() const {
|
|
try {
|
|
auto result = det.getHighVoltage();
|
|
std::vector<int64_t> 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
|
|
} |