From 3deb528087c2f6fde82d5681e13b61a270c0a780 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 28 Jan 2026 13:42:54 +0100 Subject: [PATCH 01/20] xilinx tsamples test fixed for a value that a receiver can stomach for memory allocation (#1374) --- .../tests/Caller/test-Caller-chiptestboard.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp index e557363aa..795937ee1 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp @@ -934,8 +934,8 @@ TEST_CASE("tsamples", "[.cmdcall]") { } if (det_type == defs::XILINX_CHIPTESTBOARD) { std::ostringstream oss; - caller.call("tsamples", {"2147483647"}, -1, PUT, oss); - REQUIRE(oss.str() == "tsamples 2147483647\n"); + caller.call("tsamples", {"10000"}, -1, PUT, oss); + REQUIRE(oss.str() == "tsamples 10000\n"); } for (int i = 0; i != det.size(); ++i) { det.setNumberOfTransceiverSamples(prev_val[i], {i}); From 55ff22243712b07f72ad3fa443d6cffb681a95c1 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 28 Jan 2026 13:49:46 +0100 Subject: [PATCH 02/20] Dev/server/separate list header (#1373) * xilinx, ctb and eiger server: detangled list.h to its own detector file * detangled list.h from all the detectors servers --- .../bin/ctbDetectorServer_developer | Bin 329860 -> 330060 bytes .../slsDetectorFunctionList.h | 210 +++++ .../eigerDetectorServer/FebControl.c | 6 +- .../eigerDetectorServer/FebControl.h | 1 + .../bin/eigerDetectorServer_developer | Bin 448757 -> 448757 bytes .../slsDetectorFunctionList.h | 209 +++++ .../bin/gotthard2DetectorServer_developer | Bin 295944 -> 295944 bytes .../slsDetectorFunctionList.h | 213 +++++ .../bin/jungfrauDetectorServer_developer | Bin 315340 -> 315340 bytes .../slsDetectorFunctionList.h | 221 ++++++ .../bin/moenchDetectorServer_developer | Bin 296272 -> 296272 bytes .../slsDetectorFunctionList.h | 193 +++++ .../bin/mythen3DetectorServer_developer | Bin 305788 -> 305788 bytes .../slsDetectorFunctionList.h | 214 +++++ .../include/slsDetectorFunctionList.h | 742 ------------------ .../bin/xilinx_ctbDetectorServer_developer | Bin 305832 -> 305832 bytes .../slsDetectorFunctionList.h | 174 ++++ 17 files changed, 1439 insertions(+), 744 deletions(-) create mode 100644 slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.h create mode 100644 slsDetectorServers/eigerDetectorServer/slsDetectorFunctionList.h create mode 100644 slsDetectorServers/gotthard2DetectorServer/slsDetectorFunctionList.h create mode 100644 slsDetectorServers/jungfrauDetectorServer/slsDetectorFunctionList.h create mode 100644 slsDetectorServers/moenchDetectorServer/slsDetectorFunctionList.h create mode 100644 slsDetectorServers/mythen3DetectorServer/slsDetectorFunctionList.h delete mode 100644 slsDetectorServers/slsDetectorServer/include/slsDetectorFunctionList.h create mode 100644 slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h diff --git a/slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer b/slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer index 34fc895ae3da9ea0f6ccd20c56574a87f941d169..e1b0fb940076a28c6c1e5a5e4dd5adb455cfae86 100755 GIT binary patch delta 122532 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

@M;*7 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�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 delta 122491 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⪙e0pTZSf>LgX%EJo^Xgt_iRUV!_XWm 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 C%!UvE diff --git a/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.h b/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.h new file mode 100644 index 000000000..369e71119 --- /dev/null +++ b/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.h @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#include "sls/sls_detector_defs.h" +#include "slsDetectorServer_defs.h" // DAC_INDEX, ADC_INDEX, also include RegisterDefs.h + +#include "AD9257.h" // commonServerFunctions.h, blackfin.h, ansi.h +#include "blackfin.h" +#include "programViaBlackfin.h" + +#include // FILE +#include +#include + +/**************************************************** +This functions are used by the slsDetectroServer_funcs interface. +Here are the definitions, but the actual implementation should be done for each +single detector. + +****************************************************/ + +enum interfaceType { OUTER, INNER }; +typedef struct udpStruct_s { + uint16_t srcport; + uint16_t srcport2; + uint16_t dstport; + uint16_t dstport2; + uint64_t srcmac; + uint64_t srcmac2; + uint64_t dstmac; + uint64_t dstmac2; + uint32_t srcip; + uint32_t srcip2; + uint32_t dstip; + uint32_t dstip2; +} udpStruct; +#define MAC_ADDRESS_SIZE 18 + +// basic tests +int isInitCheckDone(); +int getInitResult(char **mess); +void basictests(); +int checkType(); +int testFpga(); +int testBus(); + +// Ids +void getServerVersion(char *version); +u_int64_t getFirmwareVersion(); +u_int64_t getFirmwareAPIVersion(); +void getHardwareVersion(char *version); +u_int16_t getHardwareVersionNumber(); +u_int16_t getHardwareSerialNumber(); +int isHardwareVersion_1_0(); +u_int32_t getDetectorNumber(); +u_int64_t getDetectorMAC(); +u_int32_t getDetectorIP(); +int enableBlackfinAMCExternalAccessExtension(char *mess); + +// initialization +void initControlServer(); +void initStopServer(); + +// set up detector +void setupDetector(); +int updateDatabytesandAllocateRAM(); +void updateDataBytes(); + +// firmware functions (resets) +void cleanFifos(); +void resetCore(); +void resetPeripheral(); + +// parameters - dr, roi +int setDynamicRange(int dr); +int getDynamicRange(int *retval); + +int setADCEnableMask(uint32_t mask); +uint32_t getADCEnableMask(); +void setADCEnableMask_10G(uint32_t mask); +uint32_t getADCEnableMask_10G(); +int setTransceiverEnableMask(uint32_t mask); +uint32_t getTransceiverEnableMask(); +void setADCInvertRegister(uint32_t val); +uint32_t getADCInvertRegister(); + +int setExternalSamplingSource(int val); +int setExternalSampling(int val); + +// parameters - readout +int setReadoutMode(enum readoutMode mode); +int getReadoutMode(); + +// parameters - timer +int setNextFrameNumber(uint64_t value); +int getNextFrameNumber(uint64_t *value); +void setNumFrames(int64_t val); +int64_t getNumFrames(); +void setNumTriggers(int64_t val); +int64_t getNumTriggers(); +int setExpTime(int64_t val); +int64_t getExpTime(); +int setPeriod(int64_t val); +int64_t getPeriod(); +int setNumAnalogSamples(int val); +int getNumAnalogSamples(); +int setNumDigitalSamples(int val); +int getNumDigitalSamples(); +int setNumTransceiverSamples(int val); +int getNumTransceiverSamples(); + +int64_t getNumFramesLeft(); +int64_t getNumTriggersLeft(); +int setDelayAfterTrigger(int64_t val); +int64_t getDelayAfterTrigger(); +int64_t getDelayAfterTriggerLeft(); +int64_t getPeriodLeft(); +int64_t getFramesFromStart(); +int64_t getActualTime(); +int64_t getMeasurementTime(); + +// parameters - module, settings +enum detectorSettings getSettings(); + +// parameters - threshold +// parameters - dac, adc, hv + +void setDAC(enum DACINDEX ind, int val, int mV); +int getDAC(enum DACINDEX ind, int mV); +int getMaxDacSteps(); +int dacToVoltage(int dac); +int checkVLimitCompliant(int mV); +int checkVLimitDacCompliant(int dac); +int getVLimit(); +void setVLimit(int l); + +int isVchipValid(int val); +int getVchip(); +void setVchip(int val); +int getVChipToSet(enum DACINDEX ind, int val); +int getDACIndexFromADCIndex(enum ADCINDEX ind); +int getADCIndexFromDACIndex(enum DACINDEX ind); +int isPowerValid(enum DACINDEX ind, int val); +int getPower(); +void setPower(enum DACINDEX ind, int val); +void powerOff(); + +int getADC(enum ADCINDEX ind); +int getSlowADC(int ichan); +int getSlowADCTemperature(); +int setHighVoltage(int val); + +// parameters - timing, extsig + +void setTiming(enum timingMode arg); +enum timingMode getTiming(); + +// configure mac +int getNumberofUDPInterfaces(); +void calcChecksum(udp_header *udp); + +int configureMAC(); +int setDetectorPosition(int pos[]); +int *getDetectorPosition(); + +int enableTenGigabitEthernet(int val); + +// very detector specific + +// chip test board specific - configure frequency, phase, pll, +// flashing firmware +int setPhase(enum CLKINDEX ind, int val, int degrees); +int getPhase(enum CLKINDEX ind, int degrees); +int getMaxPhase(enum CLKINDEX ind); +int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); +void configureSyncFrequency(enum CLKINDEX ind); +void setADCPipeline(int val); +int getADCPipeline(); +void setDBITPipeline(int val); +int getDBITPipeline(); +int setLEDEnable(int enable); +void setDigitalIODelay(uint64_t pinMask, int delay); + +int setFrequency(enum CLKINDEX ind, int val); +int getFrequency(enum CLKINDEX ind); + +// aquisition +int startStateMachine(); +#ifdef VIRTUAL +void *start_timer(void *arg); +#endif +int stopStateMachine(); +int startReadOut(); +enum runStatus getRunStatus(); +void waitForAcquisitionEnd(); +int validateUDPSocket(); +void readandSendUDPFrames(); +void unsetFifoReadStrobes(); +int readSample(int ns); +uint32_t checkDataInFifo(); +int checkFifoForEndOfAcquisition(); +int readFrameFromFifo(); +u_int32_t runBusy(); + +// common +int calculateDataBytes(); +int getTotalNumberOfChannels(); +void getNumberOfChannels(int *nchanx, int *nchany); +int getNumberOfChips(); +int getNumberOfDACs(); +int getNumberOfChannelsPerChip(); \ No newline at end of file diff --git a/slsDetectorServers/eigerDetectorServer/FebControl.c b/slsDetectorServers/eigerDetectorServer/FebControl.c index f0b96d487..85a89f2d9 100644 --- a/slsDetectorServers/eigerDetectorServer/FebControl.c +++ b/slsDetectorServers/eigerDetectorServer/FebControl.c @@ -4,6 +4,7 @@ #include "Beb.h" #include "FebRegisterDefs.h" #include "clogger.h" +#include "common.h" #include "sharedMemory.h" #include "slsDetectorServer_defs.h" @@ -403,12 +404,13 @@ int Feb_Control_ReceiveHighVoltage(unsigned int *value) { // normal if (Feb_Control_normal) { + int retval = 0; if (readParameterFromFile(NORMAL_HIGHVOLTAGE_INPUTPORT, "high voltage", - value) == FAIL) { + &retval) == FAIL) { LOG(logERROR, ("Could not get high voltage\n")); return 0; } - *value /= 10; + *value = retval / 10; LOG(logINFO, ("High Voltage: %d\n", (*value))); } diff --git a/slsDetectorServers/eigerDetectorServer/FebControl.h b/slsDetectorServers/eigerDetectorServer/FebControl.h index 63025554f..52908e79f 100644 --- a/slsDetectorServers/eigerDetectorServer/FebControl.h +++ b/slsDetectorServers/eigerDetectorServer/FebControl.h @@ -119,6 +119,7 @@ int Feb_Control_PrintCorrectedValues(); // adcs int Feb_Control_GetLeftFPGATemp(); int Feb_Control_GetRightFPGATemp(); +int Feb_Control_GetFPGAHardwareVersion(int *retval); int64_t Feb_Control_GetFrontLeftFirmwareVersion(); int64_t Feb_Control_GetFrontRightFirmwareVersion(); int64_t Feb_Control_GetMeasuredPeriod(); diff --git a/slsDetectorServers/eigerDetectorServer/bin/eigerDetectorServer_developer b/slsDetectorServers/eigerDetectorServer/bin/eigerDetectorServer_developer index 9ff6d5fa9a976e9a1c00c7bb2da4c9ce90a1592e..19a7c189b2b87a98df3139678751bd9fef0d4810 100755 GIT binary patch delta 63461 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%0D{MHlI+_S=Gu$0RbjoMHgoYlQ`8R;jZw*P8+M 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 delta 63427 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 diff --git a/slsDetectorServers/eigerDetectorServer/slsDetectorFunctionList.h b/slsDetectorServers/eigerDetectorServer/slsDetectorFunctionList.h new file mode 100644 index 000000000..426c8fd7f --- /dev/null +++ b/slsDetectorServers/eigerDetectorServer/slsDetectorFunctionList.h @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#include "sls/sls_detector_defs.h" +#include "slsDetectorServer_defs.h" // DAC_INDEX, ADC_INDEX, also include RegisterDefs.h + +#include // FILE +#include +#include + +/**************************************************** +This functions are used by the slsDetectroServer_funcs interface. +Here are the definitions, but the actual implementation should be done for each +single detector. + +****************************************************/ + +enum interfaceType { OUTER, INNER }; +typedef struct udpStruct_s { + uint16_t srcport; + uint16_t srcport2; + uint16_t dstport; + uint16_t dstport2; + uint64_t srcmac; + uint64_t srcmac2; + uint64_t dstmac; + uint64_t dstmac2; + uint32_t srcip; + uint32_t srcip2; + uint32_t dstip; + uint32_t dstip2; +} udpStruct; +#define MAC_ADDRESS_SIZE 18 + +// basic tests +int isInitCheckDone(); +int getInitResult(char **mess); +void basictests(); + +#if defined(VIRTUAL) +void setTestImageMode(int ival); +int getTestImageMode(); +#endif + +// Ids +void getServerVersion(char *version); +u_int64_t getFirmwareVersion(); +uint64_t getFrontEndFirmwareVersion(enum fpgaPosition fpgaPosition); +u_int64_t getFirmwareAPIVersion(); +void getHardwareVersion(char *version); +int getHardwareVersionNumber(); +int getModuleId(int *ret, char *mess); +int updateModuleId(); +u_int64_t getDetectorMAC(); +u_int32_t getDetectorIP(); + +// initialization +void initControlServer(); +void initStopServer(); +int updateModuleConfiguration(); +int getModuleConfiguration(int *m, int *t, int *n); +#ifdef VIRTUAL +void checkVirtual9MFlag(); +#endif + +// set up detector +#if !defined(VIRTUAL) +void setupFebBeb(); +#endif +int allocateDetectorStructureMemory(); +void setupDetector(); + +int resetToDefaultDacs(int hardReset); +int getDefaultDac(enum DACINDEX index, enum detectorSettings sett, int *retval); +int setDefaultDac(enum DACINDEX index, enum detectorSettings sett, int value); +int readConfigFile(); +int checkCommandLineConfiguration(); +void resetToHardwareSettings(); + +// advanced read/write reg +int writeRegister(uint32_t offset, uint32_t data, int validate); +int readRegister(uint32_t offset, uint32_t *retval); +int setBit(const uint32_t addr, const int nBit, int validate); +int clearBit(const uint32_t addr, const int nBit, int validate); +int getBit(const uint32_t addr, const int nBit, int *retval); + +// parameters - dr, roi +int setDynamicRange(int dr); +int getDynamicRange(int *retval); + +// parameters - readout +int setParallelMode(int mode); +int getParallelMode(); +int setOverFlowMode(int mode); +int getOverFlowMode(); + +// parameters - timer +int setNextFrameNumber(uint64_t value); +int getNextFrameNumber(uint64_t *value); +void setNumFrames(int64_t val); +int64_t getNumFrames(); +void setNumTriggers(int64_t val); +int64_t getNumTriggers(); +int setExpTime(int64_t val); +int64_t getExpTime(); +int setPeriod(int64_t val); +int64_t getPeriod(); +int setSubExpTime(int64_t val); +int64_t getSubExpTime(); +int setSubDeadTime(int64_t val); +int64_t getSubDeadTime(); +int64_t getMeasuredPeriod(); +int64_t getMeasuredSubPeriod(); + +// parameters - module, settings +void getModule(sls_detector_module *myMod); +int setModule(sls_detector_module myMod, char *mess); +int setTrimbits(int *chanregs, char *mess); +enum detectorSettings setSettings(enum detectorSettings sett); +enum detectorSettings getSettings(); + +// parameters - threshold +int getThresholdEnergy(); +int setThresholdEnergy(int ev); + +// parameters - dac, adc, hv +void setDAC(enum DACINDEX ind, int val, int mV); +int getDAC(enum DACINDEX ind, int mV); +int getMaxDacSteps(); + +int getADC(enum ADCINDEX ind); +int setHighVoltage(int val); + +// parameters - timing, extsig +int setMaster(enum MASTERINDEX m); +int setTop(enum TOPINDEX t); +int isTop(int *retval); +int isMaster(int *retval); + +void setTiming(enum timingMode arg); +enum timingMode getTiming(); + +// configure mac +int getNumberofUDPInterfaces(); +int getNumberofDestinations(int *retval); +int setNumberofDestinations(int value); +int configureMAC(); +int setDetectorPosition(int pos[]); +int *getDetectorPosition(); + +int setQuad(int value); +int getQuad(); +int setInterruptSubframe(int value); +int getInterruptSubframe(); +int setReadNRows(int value); +int getReadNRows(); +int enableTenGigabitEthernet(int val); + +// very detector specific + +// eiger specific - iodelay, pulse, rate, temp, activate, delay nw parameter +int setReadoutSpeed(int val); +int getReadoutSpeed(int *retval); +int setIODelay(int val); +int setCounterBit(int val); +int pulsePixel(int n, int x, int y); +int pulsePixelNMove(int n, int x, int y); +int pulseChip(int n); +int updateRateCorrection(char *mess); +int validateAndSetRateCorrection(int64_t tau_ns, char *mess); +int setRateCorrection(int64_t custom_tau_in_nsec); +int getRateCorrectionEnable(); +int getDefaultSettingsTau_in_nsec(); +void setDefaultSettingsTau_in_nsec(int t); +int64_t getCurrentTau(); +void setExternalGating(int enable[]); +int setAllTrimbits(int val); +int getAllTrimbits(); +int getBebFPGATemp(); +int setActivate(int enable); +int getActivate(int *retval); +int getDataStream(enum portPosition port, int *retval); +int setDataStream(enum portPosition port, int enable); + +int getTenGigaFlowControl(); +int setTenGigaFlowControl(int value); +int getTransmissionDelayFrame(); +int setTransmissionDelayFrame(int value); +int getTransmissionDelayLeft(); +int setTransmissionDelayLeft(int value); +int getTransmissionDelayRight(); +int setTransmissionDelayRight(int value); + +// aquisition +int startStateMachine(); +#ifdef VIRTUAL +void *start_timer(void *arg); +#endif +int stopStateMachine(); +int softwareTrigger(int block); +int startReadOut(); +enum runStatus getRunStatus(); +void waitForAcquisitionEnd(int *ret, char *mess); + +// common +int calculateDataBytes(); +int getTotalNumberOfChannels(); +int getNumberOfChips(); +int getNumberOfDACs(); +int getNumberOfChannelsPerChip(); \ No newline at end of file diff --git a/slsDetectorServers/gotthard2DetectorServer/bin/gotthard2DetectorServer_developer b/slsDetectorServers/gotthard2DetectorServer/bin/gotthard2DetectorServer_developer index 98bdbcde08814f97f811219e565da45f1e2874f7..2732f13a7a109218a8f1009f126b7e6bb4d79ff5 100755 GIT binary patch delta 51 zcmeC^5bEd<+Tiwp=L|zj&shmkrUL>kJk3E5+Jhc2ZV!6EWUbC>W?*P!(LRZtdHW=G Hma-ZE1zZw} delta 51 zcmeC^5bEd<+Tiwp=NvjKFvW7+Jhc2ZV!6EWUbC>YG7$#**=M#dHW=G Hma-ZE^lK6l diff --git a/slsDetectorServers/gotthard2DetectorServer/slsDetectorFunctionList.h b/slsDetectorServers/gotthard2DetectorServer/slsDetectorFunctionList.h new file mode 100644 index 000000000..598a4e251 --- /dev/null +++ b/slsDetectorServers/gotthard2DetectorServer/slsDetectorFunctionList.h @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#include "sls/sls_detector_defs.h" +#include "slsDetectorServer_defs.h" // DAC_INDEX, ADC_INDEX, also include RegisterDefs.h + +#include "nios.h" +#include "programViaNios.h" + +#include // FILE +#include +#include + +/**************************************************** +This functions are used by the slsDetectroServer_funcs interface. +Here are the definitions, but the actual implementation should be done for each +single detector. + +****************************************************/ + +enum interfaceType { OUTER, INNER }; +typedef struct udpStruct_s { + uint16_t srcport; + uint16_t srcport2; + uint16_t dstport; + uint16_t dstport2; + uint64_t srcmac; + uint64_t srcmac2; + uint64_t dstmac; + uint64_t dstmac2; + uint32_t srcip; + uint32_t srcip2; + uint32_t dstip; + uint32_t dstip2; +} udpStruct; +#define MAC_ADDRESS_SIZE 18 + +// basic tests +int isInitCheckDone(); +int getInitResult(char **mess); +void basictests(); +int checkType(); +int testFpga(); +int testBus(); + +// Ids +void getServerVersion(char *version); +u_int64_t getFirmwareVersion(); +u_int64_t getFirmwareAPIVersion(); +void getHardwareVersion(char *version); +u_int16_t getHardwareVersionNumber(); +int isHardwareVersion_1_0(); +u_int32_t getDetectorNumber(); +int getModuleId(int *ret, char *mess); +int updateModuleId(); +void setModuleId(int modid); +u_int64_t getDetectorMAC(); +u_int32_t getDetectorIP(); + +// initialization +void initControlServer(); +void initStopServer(); + +// set up detector +void setupDetector(); +int resetToDefaultDacs(int hardReset); +int getDefaultDac(enum DACINDEX index, enum detectorSettings sett, int *retval); +int setDefaultDac(enum DACINDEX index, enum detectorSettings sett, int value); +void setASICDefaults(); +int readConfigFile(); +int checkCommandLineConfiguration(); + +// firmware functions (resets) +void cleanFifos(); +void resetCore(); +void resetPeripheral(); + +// parameters - dr, roi +int setDynamicRange(int dr); +int getDynamicRange(int *retval); + +// parameters - readout +int setParallelMode(int mode); +int getParallelMode(); + +// parameters - timer +int setNextFrameNumber(uint64_t value); +int getNextFrameNumber(uint64_t *value); +void setNumFrames(int64_t val); +int64_t getNumFrames(); +void setNumTriggers(int64_t val); +int64_t getNumTriggers(); +int setExpTime(int64_t val); +int64_t getExpTime(); +int setPeriod(int64_t val); +int64_t getPeriod(); +void setNumBursts(int64_t val); +int64_t getNumBursts(); +int setBurstPeriod(int64_t val); +int64_t getBurstPeriod(); +int64_t getNumFramesLeft(); +int64_t getNumTriggersLeft(); +int setDelayAfterTrigger(int64_t val); +int64_t getDelayAfterTrigger(); +int64_t getDelayAfterTriggerLeft(); +int64_t getPeriodLeft(); +int64_t getNumBurstsLeft(); +int64_t getFramesFromStart(); +int64_t getActualTime(); +int64_t getMeasurementTime(); + +// parameters - module, settings +enum detectorSettings setSettings(enum detectorSettings sett); +enum detectorSettings getSettings(); + +// parameters - dac, adc, hv +int setOnChipDAC(enum ONCHIP_DACINDEX ind, int chipIndex, int val); +int getOnChipDAC(enum ONCHIP_DACINDEX ind, int chipIndex); +void setDAC(enum DACINDEX ind, int val, int mV); +int getDAC(enum DACINDEX ind, int mV); +int getMaxDacSteps(); + +int getADC(enum ADCINDEX ind, int *value); +int setHighVoltage(int val); +int getHighVoltage(int *retval); + +// parameters - timing, extsig +int setMaster(enum MASTERINDEX m); +int isMaster(int *retval); + +void updatingRegisters(); +int updateClockDivs(); +void setTiming(enum timingMode arg); +enum timingMode getTiming(); + +// configure mac +void setNumberofUDPInterfaces(int val); +int getNumberofUDPInterfaces(); +int getNumberofDestinations(int *retval); +int setNumberofDestinations(int value); +int getFirstUDPDestination(); +void setFirstUDPDestination(int value); +void calcChecksum(udp_header *udp); +int configureMAC(); +int setDetectorPosition(int pos[]); +int *getDetectorPosition(); + +// very detector specific +int checkDetectorType(char *mess); +int powerChip(int on, char *mess); +int getPowerChip(); +int isChipConfigured(); +int configureChip(char *mess); +void setDBITPipeline(int val); +int getDBITPipeline(); +int setPhase(enum CLKINDEX ind, int val, int degrees); +int getPhase(enum CLKINDEX ind, int degrees); +int getMaxPhase(enum CLKINDEX ind); +int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); +// void setFrequency(enum CLKINDEX ind, int val); +int getFrequency(enum CLKINDEX ind); +int getVCOFrequency(enum CLKINDEX ind); +int setReadoutSpeed(int val); +int getReadoutSpeed(int *retval); +int getMaxClockDivider(); +int setClockDivider(enum CLKINDEX ind, int val); +int getClockDivider(enum CLKINDEX ind); +int setInjectChannel(int offset, int increment); +void getInjectedChannels(int *offset, int *increment); +int setVetoReference(int gainIndex, int value); +int setVetoPhoton(int chipIndex, int *gainIndices, int *values); +int configureASICVetoReference(int chipIndex, int *gainIndices, int *values); +int getVetoPhoton(int chipIndex, int *retvals, int *gainRetvals); +int setADCConfiguration(int chipIndex, int adcIndex, int value); +int getADCConfiguration(int chipIndex, int adcIndex); +int setBurstModeinFPGA(enum burstMode value); +int setBurstMode(enum burstMode burst); +int configureASICGlobalSettings(); +enum burstMode getBurstMode(); +int setCDSGain(int enable); +int getCDSGain(); +int setFilterResistor(int value); +int getFilterResistor(); +void setCurrentSource(int value); +int getCurrentSource(); +void setTimingSource(enum timingSourceType value); +enum timingSourceType getTimingSource(); +void setVeto(int enable); +int getVeto(); +void setVetoStream(int value); +int getVetoStream(); +enum vetoAlgorithm getVetoAlgorithm(enum streamingInterface interface); +void setVetoAlgorithm(enum vetoAlgorithm alg, + enum streamingInterface interface); + +int setBadChannels(int numChannels, int *channelList); +int *getBadChannels(int *numChannels); + +// aquisition +int startStateMachine(); +#ifdef VIRTUAL +void *start_timer(void *arg); +#endif +int stopStateMachine(); +enum runStatus getRunStatus(); +void waitForAcquisitionEnd(); +u_int32_t runBusy(); + +// common +int calculateDataBytes(); +int getTotalNumberOfChannels(); +int getNumberOfChips(); +int getNumberOfDACs(); +int getNumberOfChannelsPerChip(); \ No newline at end of file diff --git a/slsDetectorServers/jungfrauDetectorServer/bin/jungfrauDetectorServer_developer b/slsDetectorServers/jungfrauDetectorServer/bin/jungfrauDetectorServer_developer index bdcdb373ffc624d3fd12a04d23c39495d77720f1..a992aa229fe270bad374353708bcec8b89eada41 100755 GIT binary patch delta 59 zcmX>zU--;?;RzZnnU(K3rf+=DEYaxF>cZIS!nD~ L^L8d5mQ&3Dju;c3 delta 59 zcmX>zU--;?;RzZn83zoGPv7{SS)$RU)rGOug=wn`^IvUNQv*u_%k6GD%-iK5>~ // FILE +#include +#include + +/**************************************************** +This functions are used by the slsDetectroServer_funcs interface. +Here are the definitions, but the actual implementation should be done for each +single detector. + +****************************************************/ + +enum interfaceType { OUTER, INNER }; +typedef struct udpStruct_s { + uint16_t srcport; + uint16_t srcport2; + uint16_t dstport; + uint16_t dstport2; + uint64_t srcmac; + uint64_t srcmac2; + uint64_t dstmac; + uint64_t dstmac2; + uint32_t srcip; + uint32_t srcip2; + uint32_t dstip; + uint32_t dstip2; +} udpStruct; +#define MAC_ADDRESS_SIZE 18 + +// basic tests +int isInitCheckDone(); +int getInitResult(char **mess); +void basictests(); +int checkType(); +int testFpga(); +int testBus(); + +#if defined(VIRTUAL) +void setTestImageMode(int ival); +int getTestImageMode(); +#endif + +// Ids +void getServerVersion(char *version); +u_int64_t getFirmwareVersion(); +u_int64_t getFirmwareAPIVersion(); +void getHardwareVersion(char *version); +u_int16_t getHardwareVersionNumber(); +u_int16_t getHardwareSerialNumber(); +int isHardwareVersion_1_0(); +int getChipVersion(); +void setChipVersion(int version); +u_int32_t getDetectorNumber(); +int getModuleId(int *ret, char *mess); +int updateModuleId(); +void setModuleId(int modid); +u_int64_t getDetectorMAC(); +u_int32_t getDetectorIP(); + +// initialization +void initControlServer(); +void initStopServer(); + +// set up detector +void setupDetector(); +int resetToDefaultDacs(int hardReset); +int getDefaultDac(enum DACINDEX index, enum detectorSettings sett, int *retval); +int setDefaultDac(enum DACINDEX index, enum detectorSettings sett, int value); +int readConfigFile(); + +// firmware functions (resets) +void cleanFifos(); +void resetCore(); +void resetPeripheral(); + +// parameters - dr, roi +int setDynamicRange(int dr); +int getDynamicRange(int *retval); +void setADCInvertRegister(uint32_t val); +uint32_t getADCInvertRegister(); + +// parameters - timer +int selectStoragecellStart(int pos); +int getMaxStoragecellStart(); +int setNextFrameNumber(uint64_t value); +int getNextFrameNumber(uint64_t *value); +void setNumFrames(int64_t val); +int64_t getNumFrames(); +void setNumTriggers(int64_t val); +int64_t getNumTriggers(); +int setExpTime(int64_t val); +int64_t getExpTime(); +int setPeriod(int64_t val); +int64_t getPeriod(); +void setNumAdditionalStorageCells(int val); +int getNumAdditionalStorageCells(); +int setStorageCellDelay(int64_t val); +int64_t getStorageCellDelay(); +int64_t getNumFramesLeft(); +int64_t getNumTriggersLeft(); +int setDelayAfterTrigger(int64_t val); +int64_t getDelayAfterTrigger(); +int64_t getDelayAfterTriggerLeft(); +int64_t getPeriodLeft(); +int64_t getFramesFromStart(); +int64_t getActualTime(); +int64_t getMeasurementTime(); + +// parameters - module, settings +int setModule(sls_detector_module myMod, char *mess); +enum detectorSettings setSettings(enum detectorSettings sett); +enum detectorSettings getSettings(); +enum gainMode getGainMode(); +void setGainMode(enum gainMode mode); + +// parameters - dac, adc, hv +void setDAC(enum DACINDEX ind, int val, int mV); +int getDAC(enum DACINDEX ind, int mV); +int getMaxDacSteps(); +int getADC(enum ADCINDEX ind); +int setHighVoltage(int val); +int getHighVoltage(int *retval); + +// parameters - timing, extsig +int setMaster(enum MASTERINDEX m); +int isMaster(int *retval); +int getSynchronization(); +void setSynchronization(int enable); +void setTiming(enum timingMode arg); +enum timingMode getTiming(); + +// configure mac +void setNumberofUDPInterfaces(int val); +int getNumberofUDPInterfaces(); +int getNumberofDestinations(int *retval); +int setNumberofDestinations(int value); +int getFirstUDPDestination(); +void setFirstUDPDestination(int value); +void selectPrimaryInterface(int val); +int getPrimaryInterface(); +void setupHeader(int iRxEntry, enum interfaceType type, uint32_t destip, + uint64_t destmac, uint16_t destport, uint64_t sourcemac, + uint32_t sourceip, uint16_t sourceport); +void calcChecksum(udp_header *udp); + +int configureMAC(); +int setDetectorPosition(int pos[]); +int *getDetectorPosition(); + +// very detector specific + +int setReadNRows(int value); +int getReadNRows(); +void initReadoutConfiguration(); +int powerChip(int on); +int isChipConfigured(); +void configureChip(); +int autoCompDisable(int on); +int setComparatorDisableTime(int64_t val); +int64_t getComparatorDisableTime(); +void configureASICTimer(); +int setReadoutSpeed(int val); +int getReadoutSpeed(int *retval); +int setPhase(enum CLKINDEX ind, int val, int degrees); +int getPhase(enum CLKINDEX ind, int degrees); +int getMaxPhase(enum CLKINDEX ind); +int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); +int setThresholdTemperature(int val); +int setTemperatureControl(int val); +int setTemperatureEvent(int val); +void alignDeserializer(); +int getFlipRows(); +void setFlipRows(int arg); +int setFilterResistor(int value); +int getFilterResistor(); +int getNumberOfFilterCells(); +void setNumberOfFilterCells(int iCell); +void disableCurrentSource(); +void enableCurrentSource(int fix, uint64_t select, int normal); +int getCurrentSource(); +int getFixCurrentSource(); +int getNormalCurrentSource(); +uint64_t getSelectCurrentSource(); +int getPedestalMode(); +void getPedestalParameters(uint8_t *frames, uint16_t *loops); +void setPedestalMode(int enable, uint8_t frames, uint16_t loops); +int setTimingInfoDecoder(enum timingInfoDecoder val); +int getTimingInfoDecoder(enum timingInfoDecoder *retval); +int getElectronCollectionMode(); +void setElectronCollectionMode(int enable); + +int getTenGigaFlowControl(); +int setTenGigaFlowControl(int value); +int getTransmissionDelayFrame(); +int setTransmissionDelayFrame(int value); + +// aquisition +int startStateMachine(); +#ifdef VIRTUAL +void *start_timer(void *arg); +#endif +int stopStateMachine(); +int softwareTrigger(int block); +enum runStatus getRunStatus(); +void waitForAcquisitionEnd(); +u_int32_t runBusy(); + +// common +int calculateDataBytes(); +int getTotalNumberOfChannels(); +int getNumberOfChips(); +int getNumberOfDACs(); +int getNumberOfChannelsPerChip(); \ No newline at end of file diff --git a/slsDetectorServers/moenchDetectorServer/bin/moenchDetectorServer_developer b/slsDetectorServers/moenchDetectorServer/bin/moenchDetectorServer_developer index dc962da2b0724a941a25393c1a39ea98ec313613..4b49971ca048f8dacf59a73a4050456cac254e28 100755 GIT binary patch delta 56 zcmcc6C3K-nXo3byX61X%>5S``B^q5?T^P5zFnRuCH8U_Yve-W9AJcs?2)n&ZlzDrZ ID9f#U07fbkj{pDw delta 56 zcmcc6C3K-nXo3by#sPzq(;3$>OEkK)x-f2aVe // FILE +#include +#include + +/**************************************************** +This functions are used by the slsDetectroServer_funcs interface. +Here are the definitions, but the actual implementation should be done for each +single detector. + +****************************************************/ + +enum interfaceType { OUTER, INNER }; +typedef struct udpStruct_s { + uint16_t srcport; + uint16_t srcport2; + uint16_t dstport; + uint16_t dstport2; + uint64_t srcmac; + uint64_t srcmac2; + uint64_t dstmac; + uint64_t dstmac2; + uint32_t srcip; + uint32_t srcip2; + uint32_t dstip; + uint32_t dstip2; +} udpStruct; +#define MAC_ADDRESS_SIZE 18 + +// basic tests +int isInitCheckDone(); +int getInitResult(char **mess); +void basictests(); +int checkType(); +int testFpga(); +int testBus(); + +#if defined(VIRTUAL) +void setTestImageMode(int ival); +int getTestImageMode(); +#endif + +// Ids +void getServerVersion(char *version); +u_int64_t getFirmwareVersion(); +u_int64_t getFirmwareAPIVersion(); +void getHardwareVersion(char *version); +u_int16_t getHardwareVersionNumber(); +u_int16_t getHardwareSerialNumber(); +int isHardwareVersion_1_0(); +u_int32_t getDetectorNumber(); +int getModuleId(int *ret, char *mess); +int updateModuleId(); +void setModuleId(int modid); +u_int64_t getDetectorMAC(); +u_int32_t getDetectorIP(); + +// initialization +void initControlServer(); +void initStopServer(); + +// set up detector +void setupDetector(); +int resetToDefaultDacs(int hardReset); +int getDefaultDac(enum DACINDEX index, enum detectorSettings sett, int *retval); +int setDefaultDac(enum DACINDEX index, enum detectorSettings sett, int value); + +// firmware functions (resets) +void cleanFifos(); +void resetCore(); +void resetPeripheral(); + +// parameters - dr, roi +int setDynamicRange(int dr); +int getDynamicRange(int *retval); +void setADCInvertRegister(uint32_t val); +uint32_t getADCInvertRegister(); + +// parameters - readout +int setParallelMode(int mode); +int getParallelMode(); + +// parameters - timer +int setNextFrameNumber(uint64_t value); +int getNextFrameNumber(uint64_t *value); +void setNumFrames(int64_t val); +int64_t getNumFrames(); +void setNumTriggers(int64_t val); +int64_t getNumTriggers(); +int setExpTime(int64_t val); +int64_t getExpTime(); +int setPeriod(int64_t val); +int64_t getPeriod(); +int64_t getNumFramesLeft(); +int64_t getNumTriggersLeft(); +int setDelayAfterTrigger(int64_t val); +int64_t getDelayAfterTrigger(); +int64_t getDelayAfterTriggerLeft(); +int64_t getPeriodLeft(); +int64_t getFramesFromStart(); +int64_t getActualTime(); +int64_t getMeasurementTime(); + +// parameters - module, settings +int setModule(sls_detector_module myMod, char *mess); +enum detectorSettings setSettings(enum detectorSettings sett); +enum detectorSettings getSettings(); + +// parameters - dac, adc, hv +void setDAC(enum DACINDEX ind, int val, int mV); +int getDAC(enum DACINDEX ind, int mV); +int getMaxDacSteps(); +int getADC(enum ADCINDEX ind); +int setHighVoltage(int val); + +// parameters - timing, extsig +int setMaster(enum MASTERINDEX m); +int isMaster(int *retval); +int getSynchronization(); +void setSynchronization(int enable); +void setTiming(enum timingMode arg); +enum timingMode getTiming(); + +// configure mac +void setNumberofUDPInterfaces(int val); +int getNumberofUDPInterfaces(); +int getNumberofDestinations(int *retval); +int setNumberofDestinations(int value); +int getFirstUDPDestination(); +void setFirstUDPDestination(int value); +void selectPrimaryInterface(int val); +int getPrimaryInterface(); +void setupHeader(int iRxEntry, enum interfaceType type, uint32_t destip, + uint64_t destmac, uint16_t destport, uint64_t sourcemac, + uint32_t sourceip, uint16_t sourceport); +void calcChecksum(udp_header *udp); +int configureMAC(); +int setDetectorPosition(int pos[]); +int *getDetectorPosition(); + +// very detector specific + +// jungfrau/moench specific - powerchip, autocompdisable, clockdiv, asictimer, +// clock, pll, flashing firmware +void setADCPipeline(int val); +int getADCPipeline(); +int setReadNRows(int value); +int getReadNRows(); +void initReadoutConfiguration(); +int powerChip(int on); +int setReadoutSpeed(int val); +int getReadoutSpeed(int *retval); +int setPhase(enum CLKINDEX ind, int val, int degrees); +int getPhase(enum CLKINDEX ind, int degrees); +int getMaxPhase(enum CLKINDEX ind); +int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); +int setThresholdTemperature(int val); +int setTemperatureControl(int val); +int setTemperatureEvent(int val); +void alignDeserializer(); +int getFlipRows(); +void setFlipRows(int arg); + +int getTenGigaFlowControl(); +int setTenGigaFlowControl(int value); +int getTransmissionDelayFrame(); +int setTransmissionDelayFrame(int value); + +// aquisition +int startStateMachine(); +#ifdef VIRTUAL +void *start_timer(void *arg); +#endif +int stopStateMachine(); +int softwareTrigger(int block); +enum runStatus getRunStatus(); +void waitForAcquisitionEnd(); +u_int32_t runBusy(); + +// common +int calculateDataBytes(); +int getTotalNumberOfChannels(); +int getNumberOfChips(); +int getNumberOfDACs(); +int getNumberOfChannelsPerChip(); \ No newline at end of file diff --git a/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServer_developer b/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServer_developer index ffda96dac492d2ca4178225c269a6d8873158a07..e4f56bed22dc01a31af2408905c6f3954e705585 100755 GIT binary patch delta 52 zcmV-40L%aU)DryE60p9j4a@+9jLjGo0>BW249BW2jLd_}tB1?00k_Mm0{VglH843cGKb18 K1Gma91hRXuI2b$t diff --git a/slsDetectorServers/mythen3DetectorServer/slsDetectorFunctionList.h b/slsDetectorServers/mythen3DetectorServer/slsDetectorFunctionList.h new file mode 100644 index 000000000..1032fcc32 --- /dev/null +++ b/slsDetectorServers/mythen3DetectorServer/slsDetectorFunctionList.h @@ -0,0 +1,214 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#include "sls/sls_detector_defs.h" +#include "slsDetectorServer_defs.h" // DAC_INDEX, ADC_INDEX, also include RegisterDefs.h + +#include "mythen3.h" +#include "nios.h" +#include "programViaNios.h" + +#include // FILE +#include +#include + +/**************************************************** +This functions are used by the slsDetectroServer_funcs interface. +Here are the definitions, but the actual implementation should be done for each +single detector. + +****************************************************/ + +enum interfaceType { OUTER, INNER }; +typedef struct udpStruct_s { + uint16_t srcport; + uint16_t srcport2; + uint16_t dstport; + uint16_t dstport2; + uint64_t srcmac; + uint64_t srcmac2; + uint64_t dstmac; + uint64_t dstmac2; + uint32_t srcip; + uint32_t srcip2; + uint32_t dstip; + uint32_t dstip2; +} udpStruct; +#define MAC_ADDRESS_SIZE 18 + +// basic tests +int isInitCheckDone(); +int getInitResult(char **mess); +void basictests(); +int checkType(); +int testFpga(); +int testBus(); + +void setTestImageMode(int ival); +int getTestImageMode(); + +// Ids +void getServerVersion(char *version); +u_int64_t getFirmwareVersion(); +u_int64_t getFirmwareAPIVersion(); +void getHardwareVersion(char *version); +u_int16_t getHardwareVersionNumber(); +int isHardwareVersion_1_0(); +u_int32_t getDetectorNumber(); +int getModuleId(int *ret, char *mess); +int updateModuleId(); +void setModuleId(int modid); +u_int64_t getDetectorMAC(); +u_int32_t getDetectorIP(); + +// initialization +void initControlServer(); +void initStopServer(); + +// set up detector +int allocateDetectorStructureMemory(); +void setupDetector(); +int resetToDefaultDacs(int hardReset); +int getDefaultDac(enum DACINDEX index, enum detectorSettings sett, int *retval); +int setDefaultDac(enum DACINDEX index, enum detectorSettings sett, int value); +void setASICDefaults(); +void setADIFDefaults(); +int checkCommandLineConfiguration(); + +// firmware functions (resets) +void cleanFifos(); +void resetCore(); +void resetPeripheral(); + +// parameters - dr, roi +int setDynamicRange(int dr); +int getDynamicRange(int *retval); + +// parameters - readout +int setParallelMode(int mode); +int getParallelMode(); + +// parameters - timer +void setNumFrames(int64_t val); +int64_t getNumFrames(); +void setNumTriggers(int64_t val); +int64_t getNumTriggers(); +int setPeriod(int64_t val); +int64_t getPeriod(); +void setNumIntGates(int val); +void setNumGates(int val); +int getNumGates(); +void updateGatePeriod(); +int64_t getGatePeriod(); +int setExpTime(int gateIndex, int64_t val); +int64_t getExpTime(int gateIndex); +int setGateDelay(int gateIndex, int64_t val); +int64_t getGateDelay(int gateIndex); + +void setCounterMask(uint32_t arg); +void setCounterMaskWithUpdateFlag(uint32_t arg, int updateMaskFlag); +uint32_t getCounterMask(); +void updatePacketizing(); + +int64_t getNumFramesLeft(); +int64_t getNumTriggersLeft(); +int setDelayAfterTrigger(int64_t val); +int64_t getDelayAfterTrigger(); +int64_t getDelayAfterTriggerLeft(); +int64_t getPeriodLeft(); +int64_t getFramesFromStart(); +int64_t getActualTime(); +int64_t getMeasurementTime(); + +// parameters - module, settings +void getModule(sls_detector_module *myMod); +int setModule(sls_detector_module myMod, char *mess); +int setTrimbits(int *trimbits); +int setAllTrimbits(int val); +int getAllTrimbits(); +enum detectorSettings setSettings(enum detectorSettings sett); +enum detectorSettings getSettings(); + +// parameters - threshold +int getThresholdEnergy(int counterIndex); +void setThresholdEnergy(int counterIndex, int eV); + +// parameters - dac, adc, hv +void setDAC(enum DACINDEX ind, int val, int mV, int counterEnableCheck); +void setGeneralDAC(enum DACINDEX ind, int val, int mV); +void setVthDac(int index, int enable); +int getDAC(enum DACINDEX ind, int mV); +int getMaxDacSteps(); + +int getADC(enum ADCINDEX ind, int *value); +int setHighVoltage(int val); +int getHighVoltage(int *retval); + +// parameters - timing, extsig +int isMaster(int *retval); +void setTiming(enum timingMode arg); +enum timingMode getTiming(); +void setInitialExtSignals(); +int setChipStatusRegister(int csr); +int setGainCaps(int caps); +int setInterpolation(int enable); +int setPumpProbe(int enable); +int setDigitalPulsing(int enable); +int setAnalogPulsing(int enable); +int setNegativePolarity(int enable); +int setDACS(int *dacs); +void setExtSignal(int signalIndex, enum externalSignalFlag mode); +int getExtSignal(int signalIndex); + +// configure mac +int getNumberofUDPInterfaces(); +int getNumberofDestinations(int *retval); +int setNumberofDestinations(int value); +int getFirstUDPDestination(); +void setFirstUDPDestination(int value); +void calcChecksum(udp_header *udp); +int configureMAC(); +int setDetectorPosition(int pos[]); +int *getDetectorPosition(); +int enableTenGigabitEthernet(int val); + +// very detector specific +int checkDetectorType(char *mess); +int powerChip(int on); +int setPhase(enum CLKINDEX ind, int val, int degrees); +int getPhase(enum CLKINDEX ind, int degrees); +int getMaxPhase(enum CLKINDEX ind); +int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); +// void setFrequency(enum CLKINDEX ind, int val); +int getFrequency(enum CLKINDEX ind); +int getVCOFrequency(enum CLKINDEX ind); +int getMaxClockDivider(); +int setClockDivider(enum CLKINDEX ind, int val); +int setClockDividerWithTimeUpdateOption(enum CLKINDEX ind, int val, + int timeUpdate); +int getClockDivider(enum CLKINDEX ind); +int setReadoutSpeed(int val); +int getReadoutSpeed(int *retval); +int setBadChannels(int numChannels, int *channelList); +int *getBadChannels(int *numChannels); + +int getTransmissionDelayFrame(); +int setTransmissionDelayFrame(int value); + +// aquisition +int startStateMachine(); +#ifdef VIRTUAL +void *start_timer(void *arg); +#endif +int stopStateMachine(); +int softwareTrigger(); +int startReadOut(); +enum runStatus getRunStatus(); +void waitForAcquisitionEnd(); +u_int32_t runBusy(); + +// common +int calculateDataBytes(); +int getTotalNumberOfChannels(); +int getNumberOfChips(); +int getNumberOfDACs(); +int getNumberOfChannelsPerChip(); \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer/include/slsDetectorFunctionList.h b/slsDetectorServers/slsDetectorServer/include/slsDetectorFunctionList.h deleted file mode 100644 index 7114c7f4b..000000000 --- a/slsDetectorServers/slsDetectorServer/include/slsDetectorFunctionList.h +++ /dev/null @@ -1,742 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-other -// Copyright (C) 2021 Contributors to the SLS Detector Package -#include "sls/sls_detector_defs.h" -#include "slsDetectorServer_defs.h" // DAC_INDEX, ADC_INDEX, also include RegisterDefs.h - -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(CHIPTESTBOARDD) -#include "AD9257.h" // commonServerFunctions.h, blackfin.h, ansi.h -#endif - -#if defined(MYTHEN3D) || defined(GOTTHARD2D) -#include "nios.h" -#include "programViaNios.h" -#elif defined(CHIPTESTBOARDD) || defined(JUNGFRAUD) || defined(MOENCHD) -#include "blackfin.h" -#include "programViaBlackfin.h" -#endif - -#ifdef ARMPROCESSOR -#include "arm64.h" -#include "programViaArm.h" -#endif - -#ifdef MYTHEN3D -#include "mythen3.h" -#endif - -#include // FILE -#include -#include - -/**************************************************** -This functions are used by the slsDetectroServer_funcs interface. -Here are the definitions, but the actual implementation should be done for each -single detector. - -****************************************************/ - -enum interfaceType { OUTER, INNER }; -typedef struct udpStruct_s { - uint16_t srcport; - uint16_t srcport2; - uint16_t dstport; - uint16_t dstport2; - uint64_t srcmac; - uint64_t srcmac2; - uint64_t dstmac; - uint64_t dstmac2; - uint32_t srcip; - uint32_t srcip2; - uint32_t dstip; - uint32_t dstip2; -} udpStruct; -#define MAC_ADDRESS_SIZE 18 - -// basic tests -int isInitCheckDone(); -int getInitResult(char **mess); -void basictests(); -#if !defined(EIGERD) -int checkType(); -int testFpga(); -#ifdef XILINX_CHIPTESTBOARDD -int testFixedFPGAPattern(); -#else -int testBus(); -#endif -#endif - -#if ((defined(EIGERD) || defined(JUNGFRAUD) || defined(MOENCHD)) && \ - defined(VIRTUAL)) -void setTestImageMode(int ival); -int getTestImageMode(); -#endif - -// Ids -void getServerVersion(char *version); -u_int64_t getFirmwareVersion(); -#ifdef EIGERD -uint64_t getFrontEndFirmwareVersion(enum fpgaPosition fpgaPosition); -#endif -u_int64_t getFirmwareAPIVersion(); -void getHardwareVersion(char *version); -#ifdef EIGERD -int getHardwareVersionNumber(); -#else -#ifndef XILINX_CHIPTESTBOARDD -u_int16_t getHardwareVersionNumber(); -#endif -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(CHIPTESTBOARDD) -u_int16_t getHardwareSerialNumber(); -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(GOTTHARD2D) || \ - defined(MYTHEN3D) -int isHardwareVersion_1_0(); -#endif -#if defined(JUNGFRAUD) -int getChipVersion(); -void setChipVersion(int version); -#endif -#ifndef EIGERD -u_int32_t getDetectorNumber(); -#endif - -#if defined(GOTTHARD2D) || defined(EIGERD) || defined(MYTHEN3D) || \ - defined(JUNGFRAUD) || defined(MOENCHD) -int getModuleId(int *ret, char *mess); -int updateModuleId(); -#ifndef EIGERD -void setModuleId(int modid); -#endif -#endif -u_int64_t getDetectorMAC(); -u_int32_t getDetectorIP(); - -#if defined(CHIPTESTBOARDD) -int enableBlackfinAMCExternalAccessExtension(char *mess); -#endif - -// initialization -void initControlServer(); -void initStopServer(); -#ifdef EIGERD -int updateModuleConfiguration(); -int getModuleConfiguration(int *m, int *t, int *n); -#ifdef VIRTUAL -void checkVirtual9MFlag(); -#endif -#endif - -// set up detector -#if defined(EIGERD) && !defined(VIRTUAL) -void setupFebBeb(); -#endif -#if defined(EIGERD) || defined(MYTHEN3D) -int allocateDetectorStructureMemory(); -#endif -void setupDetector(); -#if defined(CHIPTESTBOARDD) -int updateDatabytesandAllocateRAM(); -void updateDataBytes(); -#endif - -#if !defined(CHIPTESTBOARDD) && !defined(XILINX_CHIPTESTBOARDD) -int resetToDefaultDacs(int hardReset); -int getDefaultDac(enum DACINDEX index, enum detectorSettings sett, int *retval); -int setDefaultDac(enum DACINDEX index, enum detectorSettings sett, int value); -#endif -#if defined(MYTHEN3D) || defined(GOTTHARD2D) -void setASICDefaults(); -#endif -#ifdef MYTHEN3D -void setADIFDefaults(); -#endif -#if defined(GOTTHARD2D) || defined(EIGERD) || defined(JUNGFRAUD) -int readConfigFile(); -#endif -#if defined(GOTTHARD2D) || defined(EIGERD) || defined(MYTHEN3D) -int checkCommandLineConfiguration(); -#endif -#ifdef EIGERD -void resetToHardwareSettings(); -#endif - -// advanced read/write reg -#ifdef EIGERD -int writeRegister(uint32_t offset, uint32_t data, int validate); -int readRegister(uint32_t offset, uint32_t *retval); -int setBit(const uint32_t addr, const int nBit, int validate); -int clearBit(const uint32_t addr, const int nBit, int validate); -int getBit(const uint32_t addr, const int nBit, int *retval); -#endif - -// firmware functions (resets) -#if defined(XILINX_CHIPTESTBOARDD) -void cleanFifos(); -void resetFlow(); -int waitTransceiverReset(char *mess); -#ifdef VIRTUAL -void setTransceiverAlignment(int align); -#endif -int isTransceiverAligned(); -int waitTransceiverAligned(char *mess); -int configureTransceiver(char *mess); -int isChipConfigured(); -int powerChip(int on, char *mess); -int getPowerChip(); -int configureChip(char *mess); -int readConfigFile(char *mess, char *fileName, char *fileType); -int resetChip(char *mess); -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(CHIPTESTBOARDD) || \ - defined(MYTHEN3D) || defined(GOTTHARD2D) -void cleanFifos(); -void resetCore(); -void resetPeripheral(); -#endif - -// parameters - dr, roi -int setDynamicRange(int dr); -int getDynamicRange(int *retval); -#if defined(JUNGFRAUD) || defined(MOENCHD) -void setADCInvertRegister(uint32_t val); -uint32_t getADCInvertRegister(); -#endif -#if defined(CHIPTESTBOARDD) -int setADCEnableMask(uint32_t mask); -uint32_t getADCEnableMask(); -void setADCEnableMask_10G(uint32_t mask); -uint32_t getADCEnableMask_10G(); -int setTransceiverEnableMask(uint32_t mask); -uint32_t getTransceiverEnableMask(); -void setADCInvertRegister(uint32_t val); -uint32_t getADCInvertRegister(); -#endif -#ifdef XILINX_CHIPTESTBOARDD -void setADCEnableMask_10G(uint32_t mask); -uint32_t getADCEnableMask_10G(); -int setTransceiverEnableMask(uint32_t mask); -uint32_t getTransceiverEnableMask(); -#endif -#if defined(CHIPTESTBOARDD) -int setExternalSamplingSource(int val); -int setExternalSampling(int val); -#endif - -// parameters - readout -#if defined(EIGERD) || defined(MYTHEN3D) || defined(GOTTHARD2D) || \ - defined(MOENCHD) -int setParallelMode(int mode); -int getParallelMode(); -#endif -#ifdef EIGERD -int setOverFlowMode(int mode); -int getOverFlowMode(); -#endif -#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) -int setReadoutMode(enum readoutMode mode); -int getReadoutMode(); -#endif - -// parameters - timer -#if defined(JUNGFRAUD) -int selectStoragecellStart(int pos); -int getMaxStoragecellStart(); -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(EIGERD) || \ - defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) || \ - defined(GOTTHARD2D) -int setNextFrameNumber(uint64_t value); -int getNextFrameNumber(uint64_t *value); -#endif -void setNumFrames(int64_t val); -int64_t getNumFrames(); -void setNumTriggers(int64_t val); -int64_t getNumTriggers(); -#ifndef MYTHEN3D -int setExpTime(int64_t val); -int64_t getExpTime(); -#endif -int setPeriod(int64_t val); -int64_t getPeriod(); -#ifdef MYTHEN3D -void setNumIntGates(int val); -void setNumGates(int val); -int getNumGates(); -void updateGatePeriod(); -int64_t getGatePeriod(); -int setExpTime(int gateIndex, int64_t val); -int64_t getExpTime(int gateIndex); -int setGateDelay(int gateIndex, int64_t val); -int64_t getGateDelay(int gateIndex); -#endif -#ifdef GOTTHARD2D -void setNumBursts(int64_t val); -int64_t getNumBursts(); -int setBurstPeriod(int64_t val); -int64_t getBurstPeriod(); -#endif -#ifdef EIGERD -int setSubExpTime(int64_t val); -int64_t getSubExpTime(); -int setSubDeadTime(int64_t val); -int64_t getSubDeadTime(); -int64_t getMeasuredPeriod(); -int64_t getMeasuredSubPeriod(); -#endif -#if defined(JUNGFRAUD) -void setNumAdditionalStorageCells(int val); -int getNumAdditionalStorageCells(); -int setStorageCellDelay(int64_t val); -int64_t getStorageCellDelay(); -#endif -#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) -int setNumAnalogSamples(int val); -int getNumAnalogSamples(); -int setNumDigitalSamples(int val); -int getNumDigitalSamples(); -int setNumTransceiverSamples(int val); -int getNumTransceiverSamples(); -#endif -#ifdef MYTHEN3D -void setCounterMask(uint32_t arg); -void setCounterMaskWithUpdateFlag(uint32_t arg, int updateMaskFlag); -uint32_t getCounterMask(); -void updatePacketizing(); -#endif - -#ifndef EIGERD -int64_t getNumFramesLeft(); -int64_t getNumTriggersLeft(); -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(CHIPTESTBOARDD) || \ - defined(MYTHEN3D) || defined(GOTTHARD2D) || defined(XILINX_CHIPTESTBOARDD) -int setDelayAfterTrigger(int64_t val); -int64_t getDelayAfterTrigger(); -int64_t getDelayAfterTriggerLeft(); -int64_t getPeriodLeft(); -#endif -#ifdef GOTTHARD2D -int64_t getNumBurstsLeft(); -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(CHIPTESTBOARDD) || \ - defined(MYTHEN3D) || defined(GOTTHARD2D) || defined(XILINX_CHIPTESTBOARDD) -int64_t getFramesFromStart(); -int64_t getActualTime(); -int64_t getMeasurementTime(); -#endif - -// parameters - module, settings -#if defined(MYTHEN3D) || defined(EIGERD) -void getModule(sls_detector_module *myMod); -#endif -#if (!defined(CHIPTESTBOARDD)) && (!defined(GOTTHARD2D)) -int setModule(sls_detector_module myMod, char *mess); -#endif - -#ifdef EIGERD -int setTrimbits(int *chanregs, char *mess); -#endif -#ifdef MYTHEN3D -int setTrimbits(int *trimbits); -int setAllTrimbits(int val); -int getAllTrimbits(); -#endif -#ifndef XILINX_CHIPTESTBOARDD -#ifndef CHIPTESTBOARDD -enum detectorSettings setSettings(enum detectorSettings sett); -#endif -enum detectorSettings getSettings(); -#endif -#if defined(JUNGFRAUD) -enum gainMode getGainMode(); -void setGainMode(enum gainMode mode); -#endif - -// parameters - threshold -#ifdef EIGERD -int getThresholdEnergy(); -int setThresholdEnergy(int ev); -#endif -#ifdef MYTHEN3D -int getThresholdEnergy(int counterIndex); -void setThresholdEnergy(int counterIndex, int eV); -#endif -// parameters - dac, adc, hv - -#ifdef GOTTHARD2D -int setOnChipDAC(enum ONCHIP_DACINDEX ind, int chipIndex, int val); -int getOnChipDAC(enum ONCHIP_DACINDEX ind, int chipIndex); -#endif -#ifdef MYTHEN3D -void setDAC(enum DACINDEX ind, int val, int mV, int counterEnableCheck); -void setGeneralDAC(enum DACINDEX ind, int val, int mV); -void setVthDac(int index, int enable); -#else -void setDAC(enum DACINDEX ind, int val, int mV); -#endif -int getDAC(enum DACINDEX ind, int mV); -int getMaxDacSteps(); -#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) -int dacToVoltage(int dac); -int checkVLimitCompliant(int mV); -int checkVLimitDacCompliant(int dac); -int getVLimit(); -void setVLimit(int l); -#endif - -#ifdef CHIPTESTBOARDD -int isVchipValid(int val); -int getVchip(); -void setVchip(int val); -int getVChipToSet(enum DACINDEX ind, int val); -int getDACIndexFromADCIndex(enum ADCINDEX ind); -int getADCIndexFromDACIndex(enum DACINDEX ind); -int isPowerValid(enum DACINDEX ind, int val); -int getPower(); -void setPower(enum DACINDEX ind, int val); -void powerOff(); -#elif XILINX_CHIPTESTBOARDD -int getBitOffsetFromDACIndex(enum DACINDEX ind); -int isPowerValid(enum DACINDEX ind, int val); -int getPower(); -void setPower(enum DACINDEX ind, int val); -#endif - -#if defined(MYTHEN3D) || defined(GOTTHARD2D) || defined(XILINX_CHIPTESTBOARDD) -int getADC(enum ADCINDEX ind, int *value); -#else -int getADC(enum ADCINDEX ind); -#endif -#ifdef CHIPTESTBOARDD -int getSlowADC(int ichan); -int getSlowADCTemperature(); -#endif -#ifdef XILINX_CHIPTESTBOARDD -int getSlowADC(int ichan, int *retval); -int getTemperature(int *retval); -#else -int setHighVoltage(int val); -#if defined(MYTHEN3D) || defined(GOTTHARD2D) -int getHighVoltage(int *retval); -#endif -#endif - -// parameters - timing, extsig -#if defined(EIGERD) || defined(GOTTHARD2D) || defined(JUNGFRAUD) || \ - defined(MOENCHD) -int setMaster(enum MASTERINDEX m); -#endif -#ifdef EIGERD -int setTop(enum TOPINDEX t); -int isTop(int *retval); -#endif -#if defined(MYTHEN3D) || defined(EIGERD) || defined(GOTTHARD2D) || \ - defined(JUNGFRAUD) || defined(MOENCHD) -int isMaster(int *retval); -#endif - -#if defined(JUNGFRAUD) || defined(MOENCHD) -int getSynchronization(); -void setSynchronization(int enable); -#endif - -#ifdef GOTTHARD2D -void updatingRegisters(); -int updateClockDivs(); -#endif -void setTiming(enum timingMode arg); -enum timingMode getTiming(); -#ifdef MYTHEN3D -void setInitialExtSignals(); -int setChipStatusRegister(int csr); -int setGainCaps(int caps); -int setInterpolation(int enable); -int setPumpProbe(int enable); -int setDigitalPulsing(int enable); -int setAnalogPulsing(int enable); -int setNegativePolarity(int enable); -int setDACS(int *dacs); -#endif -#if defined(MYTHEN3D) -void setExtSignal(int signalIndex, enum externalSignalFlag mode); -int getExtSignal(int signalIndex); -#endif - -// configure mac -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(GOTTHARD2D) -void setNumberofUDPInterfaces(int val); -#endif -int getNumberofUDPInterfaces(); - -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(EIGERD) || \ - defined(MYTHEN3D) || defined(GOTTHARD2D) -int getNumberofDestinations(int *retval); -int setNumberofDestinations(int value); -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(MYTHEN3D) || \ - defined(GOTTHARD2D) -int getFirstUDPDestination(); -void setFirstUDPDestination(int value); -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) -void selectPrimaryInterface(int val); -int getPrimaryInterface(); -void setupHeader(int iRxEntry, enum interfaceType type, uint32_t destip, - uint64_t destmac, uint16_t destport, uint64_t sourcemac, - uint32_t sourceip, uint16_t sourceport); -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(GOTTHARD2D) || \ - defined(MYTHEN3D) || defined(CHIPTESTBOARDD) || \ - defined(XILINX_CHIPTESTBOARDD) -void calcChecksum(udp_header *udp); -#endif - -int configureMAC(); -int setDetectorPosition(int pos[]); -int *getDetectorPosition(); - -#ifdef EIGERD -int setQuad(int value); -int getQuad(); -int setInterruptSubframe(int value); -int getInterruptSubframe(); -int setReadNRows(int value); -int getReadNRows(); -#endif -#if defined(CHIPTESTBOARDD) || defined(EIGERD) || defined(MYTHEN3D) -int enableTenGigabitEthernet(int val); -#endif - -// very detector specific - -// chip test board specific - configure frequency, phase, pll, -// flashing firmware -#if defined(CHIPTESTBOARDD) -int setPhase(enum CLKINDEX ind, int val, int degrees); -int getPhase(enum CLKINDEX ind, int degrees); -int getMaxPhase(enum CLKINDEX ind); -int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); -void configureSyncFrequency(enum CLKINDEX ind); -void setADCPipeline(int val); -int getADCPipeline(); -void setDBITPipeline(int val); -int getDBITPipeline(); -int setLEDEnable(int enable); -void setDigitalIODelay(uint64_t pinMask, int delay); -#endif - -#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) -int setFrequency(enum CLKINDEX ind, int val); -int getFrequency(enum CLKINDEX ind); -#endif - -// jungfrau/moench specific - powerchip, autocompdisable, clockdiv, asictimer, -// clock, pll, flashing firmware -#if defined(MOENCHD) -void setADCPipeline(int val); -int getADCPipeline(); -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) -int setReadNRows(int value); -int getReadNRows(); -void initReadoutConfiguration(); -int powerChip(int on); -#ifndef MOENCHD -int isChipConfigured(); -void configureChip(); -int autoCompDisable(int on); -int setComparatorDisableTime(int64_t val); -int64_t getComparatorDisableTime(); -void configureASICTimer(); -#endif -int setReadoutSpeed(int val); -int getReadoutSpeed(int *retval); -int setPhase(enum CLKINDEX ind, int val, int degrees); -int getPhase(enum CLKINDEX ind, int degrees); -int getMaxPhase(enum CLKINDEX ind); -int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); -int setThresholdTemperature(int val); -int setTemperatureControl(int val); -int setTemperatureEvent(int val); -void alignDeserializer(); -int getFlipRows(); -void setFlipRows(int arg); -#ifndef MOENCHD -int setFilterResistor(int value); -int getFilterResistor(); -int getNumberOfFilterCells(); -void setNumberOfFilterCells(int iCell); -void disableCurrentSource(); -void enableCurrentSource(int fix, uint64_t select, int normal); -int getCurrentSource(); -int getFixCurrentSource(); -int getNormalCurrentSource(); -uint64_t getSelectCurrentSource(); -int getPedestalMode(); -void getPedestalParameters(uint8_t *frames, uint16_t *loops); -void setPedestalMode(int enable, uint8_t frames, uint16_t loops); -int setTimingInfoDecoder(enum timingInfoDecoder val); -int getTimingInfoDecoder(enum timingInfoDecoder *retval); -int getElectronCollectionMode(); -void setElectronCollectionMode(int enable); -#endif - -// eiger specific - iodelay, pulse, rate, temp, activate, delay nw parameter -#elif EIGERD -int setReadoutSpeed(int val); -int getReadoutSpeed(int *retval); -int setIODelay(int val); -int setCounterBit(int val); -int pulsePixel(int n, int x, int y); -int pulsePixelNMove(int n, int x, int y); -int pulseChip(int n); -int updateRateCorrection(char *mess); -int validateAndSetRateCorrection(int64_t tau_ns, char *mess); -int setRateCorrection(int64_t custom_tau_in_nsec); -int getRateCorrectionEnable(); -int getDefaultSettingsTau_in_nsec(); -void setDefaultSettingsTau_in_nsec(int t); -int64_t getCurrentTau(); -void setExternalGating(int enable[]); -int setAllTrimbits(int val); -int getAllTrimbits(); -int getBebFPGATemp(); -int setActivate(int enable); -int getActivate(int *retval); -int getDataStream(enum portPosition port, int *retval); -int setDataStream(enum portPosition port, int enable); - -#elif MYTHEN3D -int checkDetectorType(char *mess); -int powerChip(int on); -int setPhase(enum CLKINDEX ind, int val, int degrees); -int getPhase(enum CLKINDEX ind, int degrees); -int getMaxPhase(enum CLKINDEX ind); -int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); -// void setFrequency(enum CLKINDEX ind, int val); -int getFrequency(enum CLKINDEX ind); -int getVCOFrequency(enum CLKINDEX ind); -int getMaxClockDivider(); -int setClockDivider(enum CLKINDEX ind, int val); -int setClockDividerWithTimeUpdateOption(enum CLKINDEX ind, int val, - int timeUpdate); -int getClockDivider(enum CLKINDEX ind); -int setReadoutSpeed(int val); -int getReadoutSpeed(int *retval); -#elif GOTTHARD2D -int checkDetectorType(char *mess); -int powerChip(int on, char *mess); -int getPowerChip(); -int isChipConfigured(); -int configureChip(char *mess); -void setDBITPipeline(int val); -int getDBITPipeline(); -int setPhase(enum CLKINDEX ind, int val, int degrees); -int getPhase(enum CLKINDEX ind, int degrees); -int getMaxPhase(enum CLKINDEX ind); -int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); -// void setFrequency(enum CLKINDEX ind, int val); -int getFrequency(enum CLKINDEX ind); -int getVCOFrequency(enum CLKINDEX ind); -int setReadoutSpeed(int val); -int getReadoutSpeed(int *retval); -int getMaxClockDivider(); -int setClockDivider(enum CLKINDEX ind, int val); -int getClockDivider(enum CLKINDEX ind); -int setInjectChannel(int offset, int increment); -void getInjectedChannels(int *offset, int *increment); -int setVetoReference(int gainIndex, int value); -int setVetoPhoton(int chipIndex, int *gainIndices, int *values); -int configureASICVetoReference(int chipIndex, int *gainIndices, int *values); -int getVetoPhoton(int chipIndex, int *retvals, int *gainRetvals); -int setADCConfiguration(int chipIndex, int adcIndex, int value); -int getADCConfiguration(int chipIndex, int adcIndex); -int setBurstModeinFPGA(enum burstMode value); -int setBurstMode(enum burstMode burst); -int configureASICGlobalSettings(); -enum burstMode getBurstMode(); -int setCDSGain(int enable); -int getCDSGain(); -int setFilterResistor(int value); -int getFilterResistor(); -void setCurrentSource(int value); -int getCurrentSource(); -void setTimingSource(enum timingSourceType value); -enum timingSourceType getTimingSource(); -void setVeto(int enable); -int getVeto(); -void setVetoStream(int value); -int getVetoStream(); -enum vetoAlgorithm getVetoAlgorithm(enum streamingInterface interface); -void setVetoAlgorithm(enum vetoAlgorithm alg, - enum streamingInterface interface); -#endif - -#if defined(GOTTHARD2D) || defined(MYTHEN3D) -int setBadChannels(int numChannels, int *channelList); -int *getBadChannels(int *numChannels); -#endif - -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(EIGERD) -int getTenGigaFlowControl(); -int setTenGigaFlowControl(int value); -#endif -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(EIGERD) || \ - defined(MYTHEN3D) -int getTransmissionDelayFrame(); -int setTransmissionDelayFrame(int value); -#endif -#ifdef EIGERD -int getTransmissionDelayLeft(); -int setTransmissionDelayLeft(int value); -int getTransmissionDelayRight(); -int setTransmissionDelayRight(int value); -#endif - -// aquisition -int startStateMachine(); -#ifdef VIRTUAL -void *start_timer(void *arg); -#endif -int stopStateMachine(); -#if defined(MYTHEN3D) || defined(XILINX_CHIPTESTBOARDD) -int softwareTrigger(); -#endif -#if defined(EIGERD) || defined(JUNGFRAUD) || defined(MOENCHD) -int softwareTrigger(int block); -#endif -#if defined(EIGERD) || defined(MYTHEN3D) || defined(CHIPTESTBOARDD) -int startReadOut(); -#endif -enum runStatus getRunStatus(); -#ifdef EIGERD -void waitForAcquisitionEnd(int *ret, char *mess); -#else -void waitForAcquisitionEnd(); -#endif -#if defined(CHIPTESTBOARDD) -int validateUDPSocket(); -void readandSendUDPFrames(); -void unsetFifoReadStrobes(); -int readSample(int ns); -uint32_t checkDataInFifo(); -int checkFifoForEndOfAcquisition(); -int readFrameFromFifo(); -#endif - -#if defined(JUNGFRAUD) || defined(MOENCHD) || defined(CHIPTESTBOARDD) || \ - defined(MYTHEN3D) || defined(GOTTHARD2D) || defined(XILINX_CHIPTESTBOARDD) -u_int32_t runBusy(); -#endif - -// common -int calculateDataBytes(); -int getTotalNumberOfChannels(); -#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) -void getNumberOfChannels(int *nchanx, int *nchany); -#endif -int getNumberOfChips(); -int getNumberOfDACs(); -int getNumberOfChannelsPerChip(); \ No newline at end of file diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer b/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer index b3a23d9f5a4fcd165a9da295a6f08086676980c8..d3c921dfeba4d78589624362d28a371de4eea199 100755 GIT binary patch delta 37 scmZ2+RcOUkp@tU5Elj>aj27Gdf|!;|Fj};K%VGjz=I!6IST@-M02t&B;s5{u delta 37 scmZ2+RcOUkp@tU5Elj>aj7Ho2f|!;|FdDUg%VGjz=I!6IST@-M02lxc)&Kwi diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h new file mode 100644 index 000000000..1b584109f --- /dev/null +++ b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#include "sls/sls_detector_defs.h" +#include "slsDetectorServer_defs.h" // DAC_INDEX, ADC_INDEX, also include RegisterDefs.h + +#include "arm64.h" +#include "programViaArm.h" + +#include // FILE +#include +#include + +/**************************************************** +This functions are used by the slsDetectroServer_funcs interface. +Here are the definitions, but the actual implementation should be done for each +single detector. + +****************************************************/ + +enum interfaceType { OUTER, INNER }; +typedef struct udpStruct_s { + uint16_t srcport; + uint16_t srcport2; + uint16_t dstport; + uint16_t dstport2; + uint64_t srcmac; + uint64_t srcmac2; + uint64_t dstmac; + uint64_t dstmac2; + uint32_t srcip; + uint32_t srcip2; + uint32_t dstip; + uint32_t dstip2; +} udpStruct; +#define MAC_ADDRESS_SIZE 18 + +// basic tests +int isInitCheckDone(); +int getInitResult(char **mess); +void basictests(); +int checkType(); +int testFpga(); +int testFixedFPGAPattern(); + +// Ids +void getServerVersion(char *version); +u_int64_t getFirmwareVersion(); +u_int64_t getFirmwareAPIVersion(); +void getHardwareVersion(char *version); +u_int32_t getDetectorNumber(); +u_int64_t getDetectorMAC(); +u_int32_t getDetectorIP(); + +// initialization +void initControlServer(); +void initStopServer(); + +// set up detector +void setupDetector(); + +// firmware functions (resets) +void cleanFifos(); +void resetFlow(); +int waitTransceiverReset(char *mess); +#ifdef VIRTUAL +void setTransceiverAlignment(int align); +#endif +int isTransceiverAligned(); +int waitTransceiverAligned(char *mess); +int configureTransceiver(char *mess); +int isChipConfigured(); +int powerChip(int on, char *mess); +int getPowerChip(); +int configureChip(char *mess); +int readConfigFile(char *mess, char *fileName, char *fileType); +int resetChip(char *mess); + +// parameters - dr, roi +int setDynamicRange(int dr); +int getDynamicRange(int *retval); +void setADCEnableMask_10G(uint32_t mask); +uint32_t getADCEnableMask_10G(); +int setTransceiverEnableMask(uint32_t mask); +uint32_t getTransceiverEnableMask(); + +// parameters - readout +int setReadoutMode(enum readoutMode mode); +int getReadoutMode(); + +// parameters - timer +int setNextFrameNumber(uint64_t value); +int getNextFrameNumber(uint64_t *value); +void setNumFrames(int64_t val); +int64_t getNumFrames(); +void setNumTriggers(int64_t val); +int64_t getNumTriggers(); +int setExpTime(int64_t val); +int64_t getExpTime(); +int setPeriod(int64_t val); +int64_t getPeriod(); +int setNumAnalogSamples(int val); +int getNumAnalogSamples(); +int setNumDigitalSamples(int val); +int getNumDigitalSamples(); +int setNumTransceiverSamples(int val); +int getNumTransceiverSamples(); + +int64_t getNumFramesLeft(); +int64_t getNumTriggersLeft(); +int setDelayAfterTrigger(int64_t val); +int64_t getDelayAfterTrigger(); +int64_t getDelayAfterTriggerLeft(); +int64_t getPeriodLeft(); +int64_t getFramesFromStart(); +int64_t getActualTime(); +int64_t getMeasurementTime(); + +// parameters - module, settings +int setModule(sls_detector_module myMod, char *mess); + +// parameters - dac, adc, hv +void setDAC(enum DACINDEX ind, int val, int mV); +int getDAC(enum DACINDEX ind, int mV); +int getMaxDacSteps(); +int dacToVoltage(int dac); +int checkVLimitCompliant(int mV); +int checkVLimitDacCompliant(int dac); +int getVLimit(); +void setVLimit(int l); +int getBitOffsetFromDACIndex(enum DACINDEX ind); +int isPowerValid(enum DACINDEX ind, int val); +int getPower(); +void setPower(enum DACINDEX ind, int val); +int getADC(enum ADCINDEX ind, int *value); +int getSlowADC(int ichan, int *retval); +int getTemperature(int *retval); +int setHighVoltage(int val); + +// parameters - timing, extsig +void setTiming(enum timingMode arg); +enum timingMode getTiming(); + +// configure mac +int getNumberofUDPInterfaces(); +void calcChecksum(udp_header *udp); +int configureMAC(); +int setDetectorPosition(int pos[]); +int *getDetectorPosition(); + +// very detector specific + +// chip test board specific - configure frequency, phase, pll, +// flashing firmware +int setFrequency(enum CLKINDEX ind, int val); +int getFrequency(enum CLKINDEX ind); + +// aquisition +int startStateMachine(); +#ifdef VIRTUAL +void *start_timer(void *arg); +#endif +int stopStateMachine(); +int softwareTrigger(); +enum runStatus getRunStatus(); +void waitForAcquisitionEnd(); +u_int32_t runBusy(); + +// common +int calculateDataBytes(); +int getTotalNumberOfChannels(); +void getNumberOfChannels(int *nchanx, int *nchany); +int getNumberOfChips(); +int getNumberOfDACs(); +int getNumberOfChannelsPerChip(); \ No newline at end of file From 2c6ded89ad3c1b6ea59798c53af072ca57f2b7ca Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 28 Jan 2026 14:59:49 +0100 Subject: [PATCH 03/20] xilinx server: not allowing power down as default dac values for the power regulators and not allowing to be set to these in the future either --- .../slsDetectorFunctionList.c | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c index af5804c82..4b6a2b460 100644 --- a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c +++ b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c @@ -413,9 +413,15 @@ void setupDetector() { } LOG(logINFOBLUE, ("Powering down all dacs\n")); - for (int idac = 0; idac < NDAC; ++idac) { + for (int idac = 0; idac < NDAC_ONLY; ++idac) { setDAC(idac, LTC2620_D_GetPowerDownValue(), 0); } + for (int idac = NDAC_ONLY; idac < NDAC; ++idac) { + if (idac == D_PWR_EMPTY) + continue; + int min = (idac == D_PWR_IO) ? VIO_MIN_MV : POWER_RGLTR_MIN; + setDAC(idac, min, 0); + } resetFlow(); cleanFifos(); @@ -1296,6 +1302,9 @@ int getPower(enum DACINDEX ind) { } void setPower(enum DACINDEX ind, int val) { + if (val < 0) + return; + // validate index and get bit offset in ctrl register int bitOffset = getBitOffsetFromDACIndex(ind); if (bitOffset == -1) { @@ -1303,10 +1312,6 @@ void setPower(enum DACINDEX ind, int val) { } uint32_t addr = CTRL_REG; uint32_t mask = (1 << bitOffset); - - if (val == -1) - return; - char *powerNames[] = {PWR_NAMES}; int pwrIndex = (int)(ind - D_PWR_D); LOG(logINFO, ("Setting Power V%s to %d mV\n", powerNames[pwrIndex], val)); @@ -1322,16 +1327,8 @@ void setPower(enum DACINDEX ind, int val) { LOG(logDEBUG1, ("Switching off power enable\n")); bus_w(addr, bus_r(addr) & ~(mask)); - // power down dac - LOG(logINFO, ("\tPowering down V%d\n", powerNames[pwrIndex])); - setDAC(ind, LTC2620_D_GetPowerDownValue(), 0); - - //(power off is anyway done with power enable) - if (val == 0) - val = LTC2620_D_GetPowerDownValue(); - // convert voltage to dac (power off is anyway done with power enable) - if (val != LTC2620_D_GetPowerDownValue()) { + if (val > 0) { int dacval = -1; if (ConvertToDifferentRange( @@ -1339,9 +1336,8 @@ void setPower(enum DACINDEX ind, int val) { LTC2620_D_GetMinInput(), val, &dacval) == FAIL) { LOG(logERROR, ("\tCannot convert Power V%s to dac value. Invalid value of %d " - "mV. Is not between " - "%d and %d mV\n", - powerNames[pwrIndex], val, POWER_RGLTR_MIN, POWER_RGLTR_MAX)); + "mV.n", + powerNames[pwrIndex], val)); return; } From 9a876075abc54a0ef5bb2f358209dd4253bb8dff Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 28 Jan 2026 15:12:55 +0100 Subject: [PATCH 04/20] updated binary --- .../bin/xilinx_ctbDetectorServer_developer | Bin 305832 -> 305832 bytes slsSupportLib/include/sls/versionAPI.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer b/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer index d3c921dfeba4d78589624362d28a371de4eea199..35a9ed29e5c025148fdcb428002ff0d79b984133 100755 GIT binary patch delta 60542 zcmb4s3tUu1`~R5*6a@qX1Z0t27X<_q6b18k@RF9PcquJ4*Swb(s7%dl*Uao@GLAM` zS&L?-Wv!{Nl9_8-YGz?+X5JRfZsr?y-^}=bXU-0;=hy$%=VSZK_dL%t&&)hC^UUQO zUOU$OwPVdIYnqhY9;S)^O6ZB*@zd)qGBLfrxAD7W1No^b$=R{+9nTNliT#d z|6grW47oFH+L6~-leT@DES{jP@Q>81SF*8L1<>+X+uNFq4`LM(^RX6LJ)86iV#6io zX3qd?6T~`8OlOU<2e%9p*etyo)mky0Xd`Ne=+#D(vYFY%7Jmd=pKT|9vV+-~D8+xX zbBT?$%qhnIhvjs$_#@e}oP4sA?aIj@TiEHGOgwldr!~uLSK7qcgi1|rHnUwWgr9Hc z!2jO^?Pfo;>(KDr!&>L&qVR)rv(o)h)E^eMaoUx7UOPdf{zc*RCD1|-IF{!YqsSlS zuD98u>At_~xj$A5)W+%oU2_jBmuWq)4g510&C4bp_D)`T_B$J{hH3PdD9vz7cMM%F zII|#`r3XBa>?zZ1SiUP~oL1cgZSS;bHJV;XYsa~(Et9E=`t(`wCpCuGR;*n zUV{P8x`$yDNVGiy<_=H*Mwvkey}Bioi)R_Ouww$3nLwdbFh;AGakYNLbWK}<@nFSk z_i8IdxivkG{wa*lKw&bB!n`8Wdf@xN>bVWZmo(N^*fch|L$1{qN6(9=Y2Z#XxR=Sa z9yrQ2bx0EsTXV5h~&^uA4^}q(?;|^awEZ{`F zqge(0HE1nj`LJKbucmbr6)4hYkcNX)`*Zbb8wNOmyMcdc$PN>-#|+t4!!8J7Zbow@ z{Ao~F2z<;A7g$I5;^|V8Rb7KtJq)X~M$ZoWL@3c(85SVDL#E+R!RH^!Owx@^@Z8IK z6lRcj*x14}a*WL@%q1VOb%hgktvU6hPrM!wm7oWDi)?}(q@gnY3w6SSI^kDTy6^&l zPY~HSo*U+3H+8gHea&fmA()H$&o%1*DnKO^V~ z8CtbY+{1dYBOOPi+Y;zMLNEb}67+z`Q547z`lkTv-Kn_5nJDcY614Xl@iZCS$(TsM zywyp0_nmA`Et!50qEUy2ilD~-KqI=I zSJMj7ag8ys7X3iD5pgF=tI~s3Z51-XG6<`jGOY*Jv1vtPy1P^8vmp#3liopwB|-rP z)#KyfPoi@Uyyi~=-_1y8o9<7cE+G)o({p8750nQL_(Bx;!))!~lue~VC|S?OcTOfx zv3Z>{U3x0@V-EiCPpHH1SfM6ZJHqfLGOY&&L#^yD{!}_bV8JT4fk%^RJrd9A z_!F0XqM^uc(MGJzEe)*UGDN8%&UET|Sr4>FRf*pZAm-Vw1**0io>w8$df-2JE)00v zq9tP380LYFmB7TDX*27zwURnhgF3x|`u5zj6H0d)O2-JLpsRvPS3{{RHKt3UIE&E| z+Zd0bg#@)cCxmuG$!E^Lx}^=Z;K`bd z(ts>I(7Od^SZN>}zvsfVja8rp_n*JlbC*S<4ZN|XfpOruOLKoF3+d0Gf^Qh>*FDdr z*<|7b7&4-@3Jq1c1`j2pj%R3+rXnN!irVt*M+NLRDqxUI>w)EXE}G^Uq9uB!lvYcKNu_yvkCerE`UpCX4(4NIjXfW9+m=^e;q%ec~N5B|N*R`tjjM1{Sh zVK0#D>~*%L!-clbq3v^H46cV359q%5&4DHh30ADHwKVq}i|E;4?zDe1S93^npAVW_ zC^9&X42~P-o)#H^ZjE12v8{x;BVjK7&0{>yPVOv>{Ti95A>nNv@*bJtO$mYZFhFH09^t*oUPV zeSJpip?|Z*k7v-mDQJuE9M2V~y<*r>2N@6O&Mc>QTK-)k)u*&tOf2X#Ud@wDI|_+< zNYopKoDn>r9ju~vqSc=*)jtjO(I`ek&qqhz%c^=ObbUY=rD>N-ch!&ZWR(V%gEKfz zVdiZ|6NLJ`P`}quzgwsW9lqsK??^Jv-Hv_)L`_c~G^EfdCXt=cggIiE2>tvB)tZb! zFft}Z21p|P%V-f6>eXSuSPpqvO{#iZrc{8+w<=OI&KicDkbRrVFbzGA)lb>yc_PcZC5dZn-rzw7WT z?SoGCZofoKCXZQSrfe^(|X`MtLTqtus!`+0KsTb zsHl`_J#dU|=wD!UcA!TD5E3CRkZC>eKKrfz{KDVvK-tUVjIv`1aTaMmet*KREIU?L z4Q&JbII9|vM$T>7HQ+m9)e2}Up)CqgAwn7r*?;t_Nb7-Ew&&)|JZB+oCK!S-jIi$= z!2lIN6u@ zZE&(vgW?DR&p~NeGNcdEU0M+>H`!wi+B5#&>~R;-i6(pEg7(}Bez)k9L3`Gk>{%ML zXSLa$M`2HLgb-Z@n;a}}aE!~=nRYeVbpiTCySDo`yL_E#zR9i=f**aMm9Q(Mo~8@C zG}2H{w*~E*9BS8&!Et1eu#5QzhewVQB6GFo%r`Kl3F>mAXt!tGasS1Vhjvz4}^_D-yAuS(}^3NM3$-pWW>HhG*qJhWFNAUB{ktfI^c=$W!^L!8BQ zrOBewphb`Ut3@UB36n*ggBCp)vV5~k$V{(kxYiN4I><60y{i{kv44n ztq#(8%i>$D#Of=d87Q6_L`35r658NwactLZnb?3jcU!MWUsq&g*;vbRMrM%L*-axy zSp9$mXK;8Fl26OD9yr3PM@}G3S^B8VM!s&4B<#@bE!p@{MOJrrx<+tyL&JA7Mzz5* ztp^y}JSvUc%npyrEV1>VPYJje*m@ZY=wg}H0}s{J^RDx?6(W8>rp>`qEz-ZBQZS$2 z-igd&V{gy4V+%v9RdVC8$AhueiXBe>%b1g11|F^zdg9pgx3`Rv)!1NT+k!AcZBw0p zo`bY>cIm7=>5U@u{xGJ$;jBNy2tVjzX1~MM{ojP#J!wZ-uxkZ9BZN?B2Y#^?mgs*$ zufCTpxFfCI?YCjP3L1Pk5{vO*xsE+(lRI+ zdf);SL7}lvhBgqDef6rq*w3TaTKyV*PjCijDPh9hGOY*RVw=Ylj~xVg>C|v;5nK=_ zkxm_@a54D_+-8yZ>AQ@=FElnSyqeQOR|$<#7EF}A~czFtN6VrZBqItlbt)%LQ}DByW{zJuseqKQ_YD zD${yk2rIm^yH$&z0|XEf%={|q48crS_Qai;R%Zk)5ey*{-PeKv17i+5cxSfN7eTW^ zvJiFnxnMviP1zDNu7+4`jp!Q^K?4ExfMZmUV>JsnAF{d|(Z>W3>`*!wI?A*jn8WD! za;v`)ogjc<#}pc3Wm*r6VCi=ylfSlfzAKMdJ&{zjjM`}winjk(r1ikxY{7>JZ}6 zApw{I&<%#QL5A(KvNh9o6gESz-nEWhnhZyb zp&3Fn#8X9E2v3b;`BO5;0XArgb%554HW6$g9=}bn!SVk35hWGp2nE-67yTHeHktG6 zrtSKMZJv^8^)#dZ6~e(Hd0_7IGOY*BvddG@wz2fM0D_Jp7#7Ji7P)ND)LE8@KiGRy zdt-@Crafx4#nDH^(_mDufxEp->w)`N)wDd!-k(g%wff`eBmsATlXfurOT0|$f!kQp z-QBI8csfJ?A+y^Rluc{}>uko|w^-c?w6kCc8P{qB11g~{J9f9zQkT+jR#>TFyZ2^1 zErCbNU|x8&FOl{G7IIctsp&~n2NrTxSgCoEXg6RXXN8ELTF@@QLe2`2M+;gA3@#Ir zM~lfc4`9eyA-pe{W&sO1E3DM8^V=F&T&S==nWh61UfW=_o-cMRJ7Jf;5sVrkg8_Ss=_+Q&dOXi#rNe;7VGZw{1 zU&Pw?d}>3a6Una5SZ_b`1JL0jLcn@Vgq_e~!B%Y5`hEe=%l!hcvJYlvTWmS(rD8CnO|$;A_}by5E{TmQkFjdG^vXZb$RYw&3>x4U>t(uMU?*Ex zo>_v^S8)jU9kLZus|T}76|(VbIK}bh(ys-x3&O5`a)zmt>3)G%*y(bcMbBgP<*7L< zAv_T^BZWM9^pIe>4?_1DInI&keu1Z0X+?@f&u616QoBA3A#@q9R@af*VHUy8@J&jI zFQ0ma&@w1lW+)jW)BOVFY*U5R;%v_jRivifrIhFy6b?`=9LO-eJ*%%sO}G`$ZBV`! zJ6jTWcVM~qrsm%q#CJ5{1=J-ncnTRlWfV16ruzkYv5I>WtvDcD03p$_)aBbU~w0+i}$9cw*=Gf`J~!kUtgzTN#mKX1w4B((b_b+h1u^*$UpPFtm?v2aSA7@ z87tF1BCiR^dxDYoPY5JDpu^bs`yRD=I?^=3&>sx_4Tk-K0rW{m?mvajedhk;9KDDh z7w~Yf4L8^p$#lN}N1kg&8qEssR_WE)xgpRq zq@UbR9w*cN#8@&rp>s=cdmN3Z#b(6lEs&N?>A@*5&Q{jqnTXYeHi9LOaIhon``MY1 zHj%JwY{rrv=wu<^vzs4iNuFer9_T>Mu$2#V!qL{j2W%sBLuA=^*gQIKp&mz5x(~GY z{R+RyFf=qf86ir4EvB~!L+^p%_ZWu%i6d1H=xEksP6wPt&zLi@ovno01Y0k#^)lE# z7HpvXP~T7wo)UItPAd6=CC%N6O8aPT%d9ncZe(o}B@MiTw5&6|D;=$9XIBjZncOfIAM`PpWx3|>T*|Pa9TpmK# zhpWJc&{q_#TWFP{5z)S+Xg!R+plDAReNNF@I9;h|XEDMpYfK+hwBDG`QM9`;y~u^d@wYqS2%7Qnc2Tj#D%uzcGqNkaxSH5zpPGXhdei743|sWs3Gk(?N=*L>jMXgc{8h z?MtFjK^g~CEoh{AfRpA3Mf+P&i=y>pS|1j4QiKkFE83S#1B%vC=DP+Z(kO;Gg9k^gXB6#EqbC)uTj`gI_E_mL zMdQ%)GezSR^ixH9(&@*F)?BUVL6y*3(GL}k!^yph_P3%wMPtIn!YY*m*{?;*7UW82M)SU(H;j~qiDZ_zN}~*Hod55XC_^x zXip}6R?%7;$`p-5q)J7*+t8(;<#>g2A=i^CfrFnViuSank1ATrq6-ym%c2h}+MPw` zD;no7a}}*;)7gr~$;!Qo)^g}9MLToobc1#w4#=U?)C0X8ovdh2J32wpi07P&)^q7t zMZ0t99g4=~xsi&-iNXj)`}62fMeDc^r)ZoR3=DIrs>r7U)C0Xe?Wbr@d)iykS_j%o z(Krm~u4rEe+EvlE0@_8+GR{|ooN&Gz}AH}QZ)8?!xZiB zLN!GrQn`xbcq!{Crhh3KS1bQew6lc%s%UIGURJcNEB&vc-CbSuqDo+g@jFH9H_!`; z_T50wDcaVJo>jEF8$G3He>ZwU(Rz3Kg`z#(>F0{pde9n0JA2T>l6HxBzX$zDJ+Sqp z`xWi(N%tw*-;?f9w6hm|PtpEfbSG#zi(=bshoU_i-KJ=bP;sq7@?f{?Ek*kXeFHZM zj0ZLg-Joc(aKnC~A@v@??4r)Wr`y z)6Xc3Cz=jXwC0MTx2S|3LvK>FCx+grXv7D76m4rp5eOTK+|6hYMQgG221V5jVss8gYXwMkNq9G*z@Gkv3K|f{1WMBZwf1#y-VASP@G9&|1(digvc3 ze=6G7g8rsxTQdDw(avQ0lR>)>d?wQ$)dO1!{a(@T6#9*#{VDXkqQ&JeMI(qft!Ts$ zUn$zzk{(yIuO&UIXg!S{QM4zGenK$*8zr#PL+SwniUW#vTj>Xi_F3usibim;ThV$t zeOJ*4E~*u+wW9w~w6hi6qG(?$>Q%JMmO(eEggb+7RJ1>XiUqec4i|-9QMB7es}$|G z(U%mh+vy96MxgPWq7i7URJ5}-U9M=v9Lqo(@xQ+{eOf(mI_MLM#toRq6s>2{MT+)i zQn#WJTr5zuuMK@r(OO$NN70Ba?pL(CEv-0OG(t&DMs)^q3>MSF7S?TWUwqqiv<@x*XNBc3Rew21$mxpa_v;LD{qD;fbse?=pp z=&NW16xhTF8kA3aD%zhw7UcCsA#Q#c0m6(6d_o^Zlyun3aPmG zBunfoq-_;#>qs4nc6X#UMQfdCx}p&iv{bZKM3WVbH~<&>44rys8Xu%xnx`{urXFZr zXq2M$E;LfnzAiLE(U|)!iuM)LdaT-|p?V4ZThW;Bv27N_yVBnk?dwW^Q8cFbI!%rL znBy-sJm^M$P_(Zb{Z`S=?(}O#V=l+uf{}r{2R)-`ttUOHXiVQ18`!#w{(U`M8RJ5Pay^3~Ps87+DrgtgY8Ajhxv_Fh)SF|UbZdJ51 zf^HTx+TW}BBj}qA4;sGfqrYfb2jigq`ls}zk{^jSr_ zn^LA|EsA3NmDPySN71E<_C?Vr6^*HLiJ~!eKB{P23|**b%$5%;8Z+d4MSGgjxr)XN zIa|?~74J1@7fK&XXQ>Bz9G$LcPaK`5Xj?p;tZ2-H6BKQ0PMwPOG{-H&U}3ZbdWWLj z33Q~QG2`L1Rrn1GoJfai#`y0QyYqw91I%&*6^*6x07dI9Xg@{!ThQK$b|=$biq=wS zcSSo>XjesJ0_&n^XDaQaXqP{g7N`Vft$aoMThexl#;ld4Xv|t-vrCr1W~FvTd#tpT zqA?+*DcYY-QxuI!NbGeujfR+Y35#FmAkA3kWd&Av*&%Uv zI(T#?_L%3$v>v#^7A_k|zT>4CB!T&tWk&WqXPh*#=*l)7;fBzMi+f?TyMaqO9yE3f zHmYZ>alcoXOMEeGLS-qcN)FVjN5|6S=mCsob$Mp%h!!7Li2^WdQ z*$y^->6abhy4R{C_F9$bvDd1^85>e1vF!Ebuev<3A=Mo#t6SEdKlWN}YjM|9!}%L( z+Z}gJH9Eqz+Kz3gZPfY^6tz;biMMSuuiblt_zfrwLkmEE4@$zXff6g|MM>m!SdG1m zP!eszYQiXqGhuopC5a}?7DY*l39E^AQQT^4um%~|*bACbVl!dr+9cD2l{Kd%+l1+f zl;oMP$_|tim@rI+q)1?5YB#E;#$Mi?l9KBf>+BwsxVs6%NKAASR@0c0zGkcmB?C-Y zd0R?u3Bg=q^ScgUSqLaFUk)WBOjtoKC8JE37uXmRR+CSO(}ZElClgIrT@iJWsU{Fp zG?{L~JY6X%H({0CD7oK+Va6qMuVHpgtF+gl1|KqEnEZ%aVwhS`{gP3G(>!Wotc24% zZo&%S3s0Lc4C|!QgjKenWQ7UC$V;9J#nArtx-?2&3qS(NNBVZLlid?u`*9VPormvf>l zUWe}VrwOZUioPA*Fjl~4yml>`lD|!S8vH{eCO))#xCz6mjYPUkpk~JiXTq@2N#abH z8`Ym^!s<}{DJD$Rs?~&vTD1i+m*M?hdnNj3riszp86%ttt1Ct=n6Ls=e}M_JVK6H) zVR1b%z+I2I?By8eyI%*^*fq41Zo)*L?Q6p7&~yV#SQ(n`78B;jNLXgVbad?z#1-03 zHTIeqjBqB#0?b@vOqeqcBb*6yW8Yz-3G<_CPc>m$D-3W7bIHM{#%@DDE>~a>)6tLb zH(}UGCv#007OUhT6NXJ~;x=KG=!%b;Fc;Q((Y%#Z1Ks|hPl z#sJrVxy0Cy<6rd8cN;(hbE55hCJehPWSM)E(n=lPt9cRKs z0Fr3JaDqZoOqd)AO;|x8;y;@S#GwPpG+{MeFcO+DCxVhZ6DB620uxpS?=Lc8UNl{a zz=H7~O6;}k@c!=CF?#JDF%p`v8dPgv6IO|dXn+Z;fv4YM!n|60Z3B)>+ylcX|=$^jouo`+KV`z|F!lOPSQ)(kYZHcj4D#J|n5)L_hxZqRhdK@70C@V3*D==E z3*f09%-`8Pvb{$}42kttLhd)oL?gI5{DiCae-woo&M0sOr4yuo}A; zK~BMSSe?s`lMhmKJt$X1B_<4q7NolgtAj$_gq2~!>TALZFkuZaVU-xTZV7Xlsvjd) znTgShb{}EFur*IcnK10QlQCutgPGHW;TkfTs4&ETvid7Aa!plW5R1boHQj`HF`AW| zFbxyY{U$691JYa*CY<#l6XtTlTiqtm2XB4!8a7@;FJ25tkDIVKIP23UOhd3yX~OE@ zz$;8x865bzuz*XA)iM(u9{i$-(E|_mm@o|<{E7*Sg9E>2!i2NFVZv~dN4yP~i^_}( z;9Of9Km)79xUj>7x#4i{nlQf|qoWBEJ5fnK{0)m3A{M0Fj# zhDo2%GB7$`!%z?`<4`lFOjsR;TfYhOW3>Gm7~(%U14`in47cBz7~?S9{%FF=F)q}Z zFtM}os|mv%GWpYliOr6`uVF5kvDod*q1esP7W!u`*0ZJ33w!E0#P|E?rXe7px&qldEZU$^S+=5#Gyyz~sT1gZw& zZLL{)^$?lv9rzrA7{hRGVB9*si1ZpLl1gyGBd%0WX8qnMh_)p(*qO@~zL89>vQ=+% z=z16eaoFZxGqN|XbVE6;JRp=HEiCf@y9H?v(rfzn4&d;hoxtiD*;HWl7&)}YeP^5% z1nhD8JnV+g^MDa=qHO9=_##BBV2`LG3u;c-rTJlsu)i4PDK=hD`w?$kdO+g}|0b)& znaB=pN+ox(3!5h7|MrzU*1!uH5XTjAy!s$t^|W{rjbhsHhDfks-ej}i#EYD4)tdvu zKY<+oyPGA2>}TYyWU_~)zQxFkY|~rA$r47qOUNYlg!g4jkFWU5(WDg{uz8ZjpTyqU z+}>hq!Om`;6!ZHDd8>c9A8+EEWJBLBusB<=MQ`Vm4s7$=LsIwSap?P~dxPm0S?ZP{ zWD}dVWn?1`Z0;#`$9t{WcU$5u{ub=YmagOz)_H4x@zIs711-0nU@v{rihZ|r4C%6^ z-?m6%(UaMb|9lpWBQvyYo$xm|%ir#ZX$)QB5DjnKRJ+;O?P=+6eTx?bQ61+x=+)w) z8S$kw$V_G{w>#o5eOb@jK&BQl{`q?KX?AdX-)ID)N_9V$y2BCwG1$UG*e0<-JJOo} z4)uPQRviI95bDD<_TY|@7H1ldok3c#?{{Q4RzXl4ya=0a0egS5h||h)tM7=$(VNQg zTSluLNi!ihS_t9;3>lhMQ5B|*_>3K_cGwT%;0c#o+ntBXhJqpdQt}5_%sUQ(7YXtI z_t~xQWZ~uY1@Gh*z6?3>#;Z8A`USFQl^%Qstw9ft^pu{J?8G|`yA^gsK;j(iP?b~! zN%_q$RM=#ezSAA;%V-cA&o=CI5WKgyb70iM0;<2#~@7-^5wY50yZ0~!emX^m^Pj3rm-CY#5<%^&SUu&pHV&%IzoAP!H zOZ3G@`|KiD^zAU(ixv7D_{`a@zW(Bj&WD>E>@QzSd>kTa&p`O&c>LeWHtp$daXZ+> zJv}T}j|IK@eJ451HoQMN`NLz#32&5&+IAW*nAB)K2TR+V+O-+12)^7jUv_dF=-!;*G0b`%{`# z5_fFF^Iur$hYr%4P5KZ+O0LnHHhq{Orj8GjF*;oM&=yyQ00dvofdhCoe@EJuCGF3) zIP;*aGx>+j-d~*59X1NrRVAn? zgH;`Hkd5qv1Kn*}djWejPbV4)e!+rxw*l+M><2$3QS9=;?$J0mQ5tr!t{*u_UpDrm z{&+oS-AD8B843HL3{uQW4^6SG{+yjT+KL@Gv@jamDN3dbyY*uS*~R94oRRj?S@@Uu zCtml&8FFxhsR_3guq_{tkLmq+aOB0igr}MP@DP&ADh`iKdl52k3pw$QAztxpkaHKX z^M}Wi-&wy;MiM_;{mJ~8J)cP(&Ba#>Ionl}#`I5%Q0mp6WUv*VmXNOO@TW!5{(=Sr z2e6nLN2^aE)(o{eOk{%>>G3k88UVc*00-CH6*D?S^K@t)f|`gq(#Mi;mfdl>HM{>v ze6+ZF6g+;%cz48+F*B$(@88sVI{toHNlXit|0#v57E zQ3u{Mq{kB2Ek}!Dra%M-`pZS7#!LOL9W5ed?DWw-WP?=kEnE6UUbLsUK}8*#am+#R zn)I>$G4UbdaYB6g7ey#W;fV}3;)_gtuT&WEjnsfWE@kcW?1L{Hc=1^n@izo^vclsd z@qq>*_S*3=9=IDvFGr02(2Ypxlb&nmu3V@FRE#YBXl=@KpY zswn2apI+n506HY*cnI=4qo>l?cV89ZqYmjON5VU1pBx#pB820(;Tk=+8}(wsQt$VA zFkFw3X}l)JE}flEX0U1gDP%Z1;(r|@%n^SUd!_bia)lM1TThO#6X!& zbmxU&pTvvyLeUhN)&m#Wp7UFx?mmpCSdC-4+v56q8?pTLF?-V6o=yCw&hoZFKKgAP zxqI#G?})|Moz3}v5{YA{zjsI5dWd?c6f@aoHth!oDdDwqac#x(LlOCm9s6OlX#a}{ zYJP|!W7w{X4sxE=UhGd6u$&)9TP7dk{~b-*vNwL5ZYe#)96_GpGwz`q%)bVv`Ec>#xu@7^+=IO--UQUg81e|Rp=R=o^NE+LGc_fLDoS8q{ zt+?NwfJs5U11;)sH>MtOPX^0{p9kWzzMFm?Y5DLVYj(LMv;AVX;uhUe$zhCyLc$%C z2xnt|8Aw*M4Zn;O5$mr5@x7u!zdD-5qTu!DtRna%sE=RUp&d8;>WF{spb^G>h=vkj z(3kA=uNkD4UHvteY-TyXIq+SULBI94v_8mQ^0j5}{D4YUsn?CyNEmA4@FHACT35>JKNG%X<8|#B!Sf{P?Gn6tO{pC6?v} z@OQu&QMbRIKg!1cl@M|7fqMQhoA(!HFpHS(lGu*FvWbJ${*{4nhFr-d1uXxHgEVGC zuk^Q6?Uy0(jw@p`?5T@xP_!Spch2z##vADO8yig>9( zi?6;9`{{Dx%2_0WxL2l=k(RqZ6vnK45ofOhR$e7bNL#*`kTKDCp8z@32=A`pmk4ph zjD(g4#VCyL1l+{aEufn8sve#GCtqjm_PSaXenhPd6s_N8>`LYKS5} zHWCKxW1Apn%5$2K>}Xq51K+QFY!mR!=L?&VERxPQG$ENqKJoeK@Qq*W zg(-_P@p_$XEAf$vTwzYgHxiCxfs60E1V(exl$1sKq9GZ%aRI-*DfyOc=cQ3N#?oW> z3sK}h(e9XrY++usVO}&D(+Z~^up2AfSQ?K!1ueow=_`6HzY-18pXEt0MCYfYNmTwX z@Ba@bIzSi_{A7d;gUGK~{)(?)%;ifV*UA>xkj1h8Da|j&Ad5SAL^Gn7U~^7cv;BXv z(rA%U@Ou~{qa7k6_<47}pc$#OVLwE*!D2&N29!!;0nd*mj%JNfOc7MrQ)=7DrN(KSVY=vx18f#P`ry zqoL04i6h%cFK&+~vxtM!c!>BD_{n&b^iKX;Je=tdUfP_DB()ruSsauFY&gfnH7U``UbIoj4jRH#>sE?@G) zN${F+{BjcE3aE{xmLQFTx9e(9t?$>dAZ$!HQIq)oasEnMp7dVzgN#IRU2nK<<9V=)-BXbv@wIL!tDkRb18b-7BW+NlS9wlb z97gMz{A63wQ5Y1PMTSPb^=>_fpa^7TU@8+V{lMebUu{fqQgj&vaq1CC46W;l-c;|d@{#k z*~we9$CqXRPdkAA%?ST4vlTQ*q&v9BnGWe+uDE2(w zwE)lr0nLx?`wr$^EZRK{yIx7WppbZ@V(}md@{5IJGx77)9myOFfk~anaLcA@-s%q0 zg1bB6*4uJ^tP>eR9^T%jh;$>Q!}jr=h2ilZ@UM!=9q_b;UC}GcN^tS2HSZzl#ICpr zc#EL>pqcQv57{fZw)?xHrf>^BsXM_6wXJyVtxj}`5e-OD07XhX4cnkx9sS zb3bT^*d|*k^G4B1*)&098Ztib{#c~3cHrZ0Btz0qZbcFSi8@j^j$#S zGCpGfw2tFdctT$0mj{qy(qnthO{j~gZ?^CWX(-Aq2SxU>1Vf#|xJ$p@0Gv`7r} zt?*BkNUUt(BQ@CN;X4PCnbE#%(Oxy8y?XM_w-86?51>-K1mfq|s2oEoYBA3Wx61g_tTd9NLxeJcu>$PeF223oL0#YqS+xs8m8IOi4d{hpd+?!66+pYRK}k%3V= zfuhA!Ig@zVNL;q{@YN&Hm7erU@x)Om`aQg26jlkhN-bC>@~;J2%u8==r<1 z>>gF&39*>$&|-GMxOtXWvN{k%K&NCxo`}q2n-gtl)>ok)C|qa1=YkNg^x{zbWnR z>qKtoXHFF5CIdrJyP(|1(zdBZSNbGGLlCL?6NX;VG#+1zl3(#Fymyqdo~A2*q#V=!GX8P^D% z9r#<|B{}@yWZV|?ci>@DNN2=MU8i73Sz4Bs@BxZ=Ab z&(N`|g!h<6y0`P-E*%a;(J5r?^4seOYF>=3SSk)AfQd6gS*3a*+wVx{zG=8CK7rRx zL$A5Q3-5+M!@Rr6ik7yHR14QP=;7Li1PmB!`r!Nf(lt8rA@`6ZuHQpqEFPI?YsCio zeylh=<(@Ea~^!Pa~11oOY~o9-nOn`$?R5?2!E4Sf5(q-|5rwe+|5k}lEy8yfOh z$aC&P9+7;|eQ5D2zUn?W7UHw}(1E7&8TZ4tG5X$*_@XQCIvdZhzMC!L|8D%#*$A-5 zZ@|$AzkEMQc=iDdA6Sz-044IpLmG$j~afxPG0vASrCnzBZ~VH zpSJ+q_w&^Y$g4QZ^7m?Zw4D!q7>|1L*$-n__>%8>n7E_yny?a`$8T~Ghecb@XS&G4 z&7OWWxYoe7+tDpz+l?o>QTem^O>Qzh+DU|B9yhkq)<8hK%X-e4#ZSzKLnxB4zltgQd8W zM$X0!8DT;7C^AC8vsgTcY4g`_KM1O z;$z4bZ=Z&lvK4D2)Ony2uX+sH^(GD4m-7pcL3=81vP3*++VEg5AG8Dyu(n--Y&}i4 zBU}83rNiUo5GfEpX?*Sz#FrdcThDvruZPKVWQ;UoT+sYY`H&|`YFsI7Q@m){C%Rb~0PhqOw3@t|8-TxHkAI6Ue^x+0ne;TNhuYQ_rA)WZprMS%N zjN-Y^kUK2pamEMtjEtk`0}v8TE^4t zSlSnuT#T!NVB@bBU$~4USemajzPLAYjq%03h_&L2d-m`;qQ~=Dm1Jg~-dqO7ZV_U&ClKFql!j{;Og_qA* zP8{*YuvfhGUM2L;(yO1^UbUP=lK&q~*gI!17JKK6Y$qrA<`vki|A`-2L9$1LRA2%s z&?BZyX9i8gwWTvCg@)fz_?7z|VTt&YLQYcPB22TYuioov$wA|NWN$t)fvva}Bh@M1c3;UAzQ_qsr;*MaP z7ZlRsw4oU*NX69V+K7c{a=VVJLZh5Ja6>T8cEI+Ore%B~h21UqNeFjo1;KYwp%TQE zzieT4@U!ru@s3^oJ{M1FTwYK;`1l-wC5oF3Bc(J#HQ+(f_WB%XBone%CsKX2fn)$pEHHuBZS`p zI7)$@6uM6^4}tIyL%3X~v4R5MV8!Q4;X5JR^J6WT)~EWd?Ut zL?(+wCdqnW?@Ra{qE~05N^HP?;T0ZI)ZL`%d>`yG_HW1-8e3CoCbqz8aYimACkTz8 z*Ya8qwh!0yiYoG}72hHpA|4ojA0XggrdMD2OQiKcH@q>o`Lg*ekqq!B){_BN?AiPpfVyBC3w0iuhVlH;dQxKb=g_kP z33ht_TNu7%S{_Nfh836Ij?aG$Dp91@@K%?*9lwI4<=Gc`)3rEDDcC>;S@c})+5m9& zi~Jb@ZUt!d=hCS{RX5aDH)IIai876^_w)3PC@sEM)L$T~#=-H+pCT=T9&lJ}`8?`% zC~p2DZ@CVN_1BS`C!a49;N=(iO9I>n(5mAvBMI5y$I(S@l`^ddYIyz|qW0U>DWRY94q4@1S8{>7GqUKFAku!chno&T|ES(DQ^ZXUeo5 zn8;6W!mD=fLZ1631a9X2-V~>fIs9dj%;(i_l3Sv+j$$s9tH>oh{w?ANZ}tNItc+)1 zBERJ=yj6+`#!GA={oNbVl|Xmo1HI6OBP}o1;&& zv|&XXi`z$hVeq1KzmGC`24Op6E6|AT93oVL0{@x50B*@L=mq{Ivv2 zh&&4Y+4DTp~|0Q<0pJM=`woW%Ua zbObO__yz;<$%!GrR3s<)JSFsIU@DT67+$pL2Tc66T?7`0!i+25DM9S&{P+2iE#y|L z(oS!|0y&BoZpE9VxMehMev9ohw&Fe78*Sa_&Go3;;8f><6a6mcO-y^;QRgBXf<>Z# zFQE;0x+C>!d1$zkzvH*x!2IxjdEVG9WUl>ARe)_ zUM1`uh)zAwIGyb$(|X`3URsS#=g|Ayy`5dT?jg!uW-86kcR z(Ifb$JIM_ey%CRk7xnWQZ}YC$)oaAZi6nNM-^B}+p2ob_E+iN8+jqf*=JHLu#Ljvo zk9rU2Fy7`pY+ID`1@EEHLDO3T#s1)Xc#GNDgkKcNmfItClURb~Z<3FU5r2o|712V$ zzf~cItumRG`}RI;64%5UO{7QBZ=b!ciQwW<^c=8|CPIIXrl*00G?7~0DX+Ip8bTkcr3OE;sZAhc8Dr#|Ae+@zk`OJoA;N8XQd1E-{THtHI zE1&7cv;Xj0KO*B221C3af$_OT#Bg?J2H*b?4l{=F3m+jg>CfX2VgKk>-t`coms0)& z_M;-*88U{L!1o*?$ru7p93q`E9z|v%_7KIyb&JCBIDtF|&ytz?GkEUDq!7pDV?QQo zIj6xZVifUAbm_;%$aWTU(^=#6ut=u$z!qNhF_~!9>~yL?G6)tA_yfnN3c2J3jf--J ziQe5KZc11-ZWcBalYqatRBpyE#Oa940}Gm)ga-UQ=UV(dXQAOYvE~Df5dJVq@eJQ~ z80QFjYku}HT3{>>942{I%|UHKE*En6`_Mw}V<86`ms>u;yosw3vp#`9SN_B&#A@|t z8Vl-95a?7Y4@~5O8Z<8AeL^0O^t7SkveFzr^iz@&spI4wsad?@Q!FO2g8P&_9qBi~ z-n>nXh}g0u3l8RMz~bXGYVabxp3MgyA+5y9;hrN{Za%o&*e$Iz#-U2$$KREm1!r5% zUphjv6u%WG#|0>W2Z2Bp0s&E=Q&>)VK;tIfXC%?8<K@&At!=a(SjP8!hRG~wmdNINA%q2b)es|_l`@SD4W9lvhEZn@^+2u< zFa4a@3fxF_(*qUIT!mk>S)~RY@^6S>jU)aRk1%zv&;c6t^f|s1f)I(x$^?wd=i5K?i-vW)>O2wICaFj#0>`b44)&6c67`oxFy(81kGF>VTC{B^l zH{S%Y-7XxV;hHaK8tS_#b;)j9;{u0xD-*AoGe?i(1z zW6qGdHfIU-^ywR5;1c)gYK_4aUJe|J*YI^`NJg)ALB)4N@j96O3Pc)Ax8a3sX?n!Q z7oRaq-xO-P^9K1+7f(G4+i?}}ENsVn8bU^7F^|tXOEUWW22s&!osq>F)Jhey5H+LW z*m0MzxfjaN%P7M%neH3-4l-+Cj&xO94}S72xglEb(V%(=xBF2ohk~j_wBQum`C3o9 zQmE(#mE8=Lr83<&fT7rrukzS>^4)%t8ov&rLWE$kC&u)A{=FX^$Bz_zqcM-Kg-- z=fWiCI=%{=o%4hC$H4w`7) zHe_;PQ5^j`0cW+bL6`HvC4=p@Ko|j+!^ho6oQn#&99CgsKjUt`_&m)0nXfyKvp;{B zA%q8a@(Xy7_7!AB!%6F)zoBIo0@UI1`31NGc1bRf z}aZbL$ zgW=r%4Q3In-MW2)yl>>UenV`9b{EZt<5U%kkXP9|IpoMGgE`nZ{q5k^ZCJqhy+6v4cST>#V(*RnVuQ`HnK_6G|p4A z!vBD5(lm`~ofQrvn>0-$jalJ(Wc0KT;sq1JfB&^ZAKaHUMNbHS1;fP2hJ6X)2a&~1 zmT`CO0J2HbG%}bNz8~47X&M>4Dts?8ou*A5K69;XT>kc7d9LK~hs`7{OTPZ5F6Pa4 zCuz^W>0OY;L{wiWGMy*Med5R6E=vlZ(uIN9p7-BixWnDo3$nw*-sBn9vrNwmp(NQl z?47UA_cyDRU8~pLTj1`8F7Xb{$9R1YFDDp~7lvuNCqLHY%8QuP1Z2fUZ@~Z1!*olo z2?7!r;gtB`Ti&V8;fH1UhCd7-?V$%JYbMBOj8GzjLmUp(Z!6tCPCh*onlM<+z8=f|) z7Bqop)ty5>RetvtGlO8VMBbvri4u6*d-t;E@YYiqO<3&KC!c8SRgX#QT{Z50=+c{V z{B5oS`Pz7wO0J}RIEJ*Q?{W7-dLX~eQ@g#gY{Yx@>=`zPb(3?*+W>cukUk5dBi^zl zd_Bw!Kk2SOx&~Xo*ovE*B_^%Y-=PJ*g+MyTc_=XF9o|x5#_$fQ{0ym9^bM0!2?gbw zxXjv*Fh~a9zl>-O%8ya-#zOgDxcBkqJKkvve^N&sFjLl%`birtd2%$5%)xlsO&<+b zmSS!fPpS#`b}_ZW6K;R;Ub`qAmuTn*ZRo#}-1|MEGey4t7c#o^qp^bvLKYomTv<(O_zh&yQO1?^rQR7HL>OHfc7@#Idj(l^lyPOuw11B* zI?A{*&yw&<$fBc+D=S+P?nM?IWn5Y3k}&7GODxPiS!b4pC;r|n4L@2Z^H)3i^auC! zR=n%|g`Tx_3GHE-@}74=%uGV{@kh)b8D5znq4#*0vTv#Ec#j)j-a7g0d*0nKHP^Uj zi#>7%dtRby8RrSB$a~&@aOb34Ev3riV`z@bPsY3*?wl0i>vDJ;;r(iUI)<+jXqr5o zF@Z2!zCPj2im6%d3Lld*2%VI_mtRbA=7>MyO$v1W9ZyfNp!vtyp;KBwFCX#IInCe5 zs))BBnfc%W?f#R-zEpQVbZJB$k9eD7Ld=`U#o9+VI}5zeJ$nbAhjBvJ$sw;3;2E^X zyiSl8DSqU2cDs`yEPo~e2h$s6jpN)F=>J>N=fX)p7ee|yUXQsC#Y16^Q=*S=PG^Yo z&XmPIr_Cu_CBM|&etFgBq!aimzjJ#`@On23*UDi(fms{!^N1ER8?A_ovUdtX?p{oB zl4F9|u8?Gr~TrJ`N3M=>HYD@;$QwQq=5=PTT6g+RrL!-lbG=)P zE96oPVIDmZnC7H<-Dv_gKa$OtBi$%>T<#Rcc$ftsY?7a-kdgFq=jOnVze+i{n6Onj z%qN%Ka`3?5bZ2YeA3v(@H0pABdb(4R%#D>2H$NYo<@`2(y)rM>sZB2Pg)=^4aY1LQ zWP~0W>l8YDKDqJ=nuiPXE1djf9t0dy13Mgr828%Tee)8fv*3zW@}%cn9THpQ)D=$9 z>GR9W8iO|quW+)R;1pRg!wCeoVHw%sIpuJvu1lWoDc+M5epAY4IExa$^c6E{@*;AN z)8;AR2TnXT!>MreVmr>6qZJzO9CKo7E~8eQ@7^!xW;w}a%}fdI4_&(HFiQeZyPzjD zHC&=ox!>|RrQe#*Df!*~jhA|{)66n7C$xOg+LuiY7pOjhePpA%GB(N-s+sL>xiP`{ zG4DQ2OLQL5H;NLSMfA|FL}#71^~89hGu@l`vl?u#{JT>vZ*RQBp!v0CSSs^oJ9C%b zi0aAr?1!TeKdYvH*m!9NbeD?RR&z%8+0LxQ@2Z$vy5>NN39IUHd1$tiK0j#oQpvx5 zekH`|^-13pt*ahy8Iu@&H(_wLvp9w~hSlv$l9=QK=kt`J?)lY?FUfq!t!5hEPDzx? zBqt??DU8~EO2SDb#yu+C`y<);ABi_0#4R^ZceG&qaPQ(Gghvgb$H+TO4F+_oOHJD@o?q7TW3@+(^FnHJ;mql^WiZ`N!9=-_9i>upQV>W8P0Bf z?;^vQt1p~qIN#G*aHjJ$P6|KEr2kAk!5et1z1~wN)~w{fagHUrCt=yQ%9(N~7$5F> z;6o>2W<2rX4rG%~!dx}a44*(Y=_Jf${mk$WkWD%Xb6Gz#{0y>5Ct;=zv%+o2CY^*y zV^;Vdk-dLMh7Q)C-zR$l;iQvrgC~>_ehk^9lQ5U{3E@U$lTN~9Ffsg1WRp(9WH2%O zFfzT=FPn7Z{#Gk{|BlS51Lk7CQ0A?6Qk|Ns^v=EPSxxtO`W6e2P zn(Z9SubC6B{>asD(A^JR`rb!9d9`-0>g;JI#2}OC?sG*eZ)7`ZX?=6U58$O&$04$& zS@I!sv&PG|(X7SrR-dzAbFEF_I}ERC;;Zupw{?jgeW8rC>-XHHRytLaOZo@T4zNp zp9_oT^EtU?t+Qb61}wD-_eAq)1L^~D-(2eiW@locE2eHLQDsd~eu{GQVw91H^R%wL zOkNv(;fo*6^3U1-A6X8ixLK}svmAd)T_$DuS=qME$%w7tV~V!XzaW3RjvT**q2^c@ zt3H$|UbOw`I!;}@0kY0nx8N%paM1^K@Hg!Lk$u<4!<(~w;!BEhoVoE;YDrIvR&b>} zm_yQKsqZJfR8HkM3v!>bN$XLgq3vZmN7LePxi(_Ncd0+J$(;jT;qGs`bOawyYU9p6 zPxI%_NMOB_mfW{Ee52dsm(c~bxF@VjdiRR$TDf(-vvU0+&5^Eq+tcj=_6OL1Wo3b0 zrZqTR8@uJ`Gp-Q+7-I24~T-ch&Zl59;MF z*7p@;dQvm>!?iOb?!QX;1}bP!O}-j@KRdj5vVjT;@&QZRHc!c!4bFmDDYk-$VSD|x z^4F`zOr5N8_qSZ) z=48mpxsH24A-zHla*53v&39ij!vs(A7HfBSk|b5{d|)Rpgq->M65{J2XKB_(w-6t+ z@&?_fu?A>)_W|Ywp;K-Gog_e=zWaHmm-@ZM_!VkNb4Od_T48YFs~am!@yX1e<*8h{ z=4Ua~yxgk&gRt5^G^D4KN8xhmn%us`F5df*tnga*Mki~!P51}zC;Z=RO-}d~32$`L zV?*|xGZLoWH&Tfy^6Ey1yPO$ey=&Mv`-8Ei&fU-BL4uS`PJuJLO77d_%sEt+8O|XS z(Mh7oWM+6ZvgrBAm4&VgUxzGuesX2a*M*lMi=Llc8FyOJkVVf=uB>cjcoDMb`N@@a z@~4cEy?=gkg~6@fN3$zlz1fZW>@ZO*1{u zT{OO^V*uOSHQnlLy^3>{{P7dbX`4{dtB6G;5kWE-NZ%^R{N z->ID+TI(*KxfQ^SNRQ@?Eb`fn_x*1{;(NdfJb-Y4@Y2ffH*hf57S{OtcHgUveCgt z6Fd()V6d0~uo8~JoLlG%b7&G6f=6K)9Dp@2eb;#82<(6-;aPYVj==$VdN(yWmzpUV zkEFuNPmV|OVQ1<1p-2@Q`M1)UVDhJFL)Z;_VB2l9A?$^5$pnOHFs6)>z&Kb48_MYm z@WSo%1(;qz9M}kZVDcTrfwgxMXP(E?1T$dzU55z3#y}+jV8UJkz#e!Eo`e_!x7l3`wwt*!Q?NGM-n)RjKM6}d*~ZvfQ`Oy zk^z|1Kn7q9JO*RKlmOPkVc794N)V*aJVI3}8>tCc_gxN8I0_HIito`Ru=QWYBZDyX z{qaal3h|DSVORzW;Gsb_s@SMIPL5&g4>+D->A%v3up17;U^@vfq$Dr{raeo>VJqAR z$6x~-`XL#I2Rg>z{+j{a8Ay6o&c~A7DN9F_`@>T2y0+Fj5&)xtbP%H*aH*z+;ZzL@P}itc<~`} z`bz>J=!K0i?i>MN73_tLzajuU@Sg-&LR0k;0OtIf05JPE_`^on2pfKjKOBU;u!47x zMqmd_xElMH@P`es_z)Yh|Ca!;5;nrqupI_pCICDLM_?~ZNTb(YAONg`#c%*t!_e>W zhk1X%A9nZQ4>S7lhbLhIS2`D97Hs$<$A2*!*{=`)Ho`{OJ3s*4|1Sc-_Wve;$_EK> z4JCh#05I!yG5{N3HLM%L9}d8Fc;F5Eq36%|!v>geEd_fMf7tXU$A2*!Ny7wy`(PtX zxJUq41$$xKTLgfWFd>~9dz%2T0v5xV5&U5pY=lQ)I~;ije^~!l{9(c<{>w-hX2H}; z9RI~^9KA#UnDs6JUivu02`yQS{1Vh0Lys#u^q-G z5&%}h5jc1i0j}euGn)W#7#73)B>Z7k0DpJ{w!@q`_`^mx0&C{tzmk%}td$)9b;$%k z&1fW8n6--Izj7%75DdX;SbYrvp#NF|z(&{$3%GeV0*}Ik>!~rA1@o5S5656NtX_^k z?1t^IY6brAG#r7+8ThZJhG5nqHip?KhE7=_I+X$?)gWg>DYn~h2~N?`0(0>Fa& ziAWRdgq^UC=UV$<-}Z@!XDws-j)_PRrWH;^@?Z_DfT=|jky>~Vw!+}fiO6Yq3=Y81 z%@Yy-Iu1ve3eVp>aVV0<#$Yi4VDBvifa$vk01v>^a0Cv(yxj!IVIYC2a17?b;u8E} z53Gf0d+>+N@H8BS1MuJ{@n6q?^eOyd@uvVDquk=0bn0&h1s_f0G@{fu;$YQ z*g!^MDlEATe|QX5!1OZwVJ&Qho^t$Q2^@gE(0>D+@H6>v5|T^0pLMc3wvQJ zJYPWon0_Y#U@i2A$lzTBfZ3J!!**B!WB1|@>tHKv`0PZa8(x5eF!}C@NK7s@dLK;= z5AEAWo3oMkIXVq&gD2tW{d5{wSWTyaXJOJt+Vt}ik!*MbmcU_n05*OBe>enB!iF#6 z4+r5Wtmoce(k2d9m<{V5z<(3Re;*qM5R`t20I(Z&z=AJNL@vOL2PYz9Fqx)G&O=@^ z5y^pVuoPx}Wg>DA4t&co2@kCV1@Y_`{)Z;1AOq@Q1Ns{I^mwjrha1 z|1%M(Jj6!owZ;aNBa2VlVu z={(yh0ZfBOVLm($D`C!$CL;B)0Jg&VAJchZRR{hs;iq)oPf!z`bY7U#smDJXr3fnE zL0AjZx(EO(pCbUw|91kwrqcu{poxA)0GRP}N&@4$@rPw+@Q0(%PehKwgJ&rK%zkkq zG6YM1IT4Ayk@!6mk#y)kN2e-a<3KMthqb@P5oVmn0d~BM;Y|ei9dqxi!-*a{E8)9@%9fJ2w? z-^uZR_FV$75qysTFb7t^4p< zL|S2vHxfAwyWs#Vbs`aeF~|QtUnG)R%yI0GMDk!atboN+B9U5n47S3a%Lo9=rV;=i zh5lQR$KVfhcsM8zcEbu-cRBvBA{Kv`a0ULl4+mfk^zY*M?_nc#7deJ`uzm&s;1PHL z?u(B^j=<4bk;qASJ~PVym_Q4DAbao`- zDWUK1m{br}=a6108|~{OkxJ;#jYR5U@1{tk9VW2I)(c1A2prfFi6rbHqc9DQz#KRR z3!#528Ha_i7B6`5>mcO0p zTg2x=p2pCLnI+H9^v&lBVy~+EQ7ND0n>FRAmj;!TSxCAi*8s(AJ{R?X?zPHeiZVH+ zXpon{lF9&cTOSGK&M134_KE2mAGzH5Dfdck~6@mUoEexwr;gto&=ArmVZn3&6(1B8PzJ= zl6(t%joIU}Bi|R8k}#D~L>^LE+Zrn?M%E)|knw5cS5?(*Ra4th4cnc|df7=@E87FU z`M!j;#(8Q&40UC85~Vrdi*YK}%69|4g-*gcE#!QsaGm^FnYT`6%z>rrWceK5RZjgn z`F*nQDk+`gi&-(Y?t{kE>i18hTOxD&Jj0Myx}2QpWLek~YiyWLhUr)+ZCBT5eTz$b-O1 z&6g)s$>P3B`2vBWSH=}pGH)K>@dL14*C?Y&XtHC|IXizu7N_}|B-5;vnIa3-p+?OYnW{40& zQjVD~?cBJ$zL{}6eI8eS(xMUQIlu}2N)O^WQ|i$Wdj&yLS=mjWoX2e+7rT<+dP8y{W7MZ2V|LUmqp75P%G;DA*W zN{gaFx)dGKuNV~X^`J&l6!?LZSk{UEw__!Cv3C2zYTYd@3XgbKQ{pNVrP8kw>UA|3 zmJ|iUks>7JiX(DZah7Mr=r2iQ^7|CuRnuq;Pt_6~v>?&3}iQbR&E(h_G3O8S)fDO7k<-^~4&7e^}%5J26+tWovx%X{N<% zNP(+1#i-n~28U+(y5hV%qo|V?Rd+!y>Ry5rt)+M!X{pzYup zE6U`YqFyd44odtwkT02v8Yxm7mHogMGj~G1c9mXZU8Bd!aaC2wImI!#s2G&^9FQlO zii14p1bSt^B36#)kgJ?)wawX7#W~=_@Rs&wj=&}~WfH$0O{ZikvZP2+CHoaa5>`~p zaYdV)Q^ZQ<2BJ2lYfHp$K+>!dt(hW4mh4wlNm$V>#}%m(e*;bjmudT7RLMz|X!~aZ zCoNU(&G*e&JcdE+V%B`k5Yym&eC8AJ&du&{z>wuhUaoVbuzDVl4f}2J4Hi?_8*c*(K)f#$PYulo9KsAaxop2#fnzhtQeF#fG_Df z+nbtm>05>Dxjn5@_l~mnvUaLI_ME=!+#3CdjcBr0TCX5`jD3qY5^5wWEK^~++@Zo_ zS<(0^9F(V2ShdOu`&8H}e^O!E_0gD{NTG~9a*`-Hn|v|84)(sB;+uDw-h=S3md|dY z3s=fRn$yvRg|YHyE7$W zrCwt`q}KVlHbOD7S~;V#*i94s^%5d9BJ;CzV2 zpS-9zC~p8BWS1HFD7$4@KD()R$xXUjz+W4{C_}!k;MIFo`uEB&@_h>#DK4tCP2#tK zO3B>jn;kg9T5}HlSugx=>ZM?-{f0??k6{q zP`CU^g)9Q>z^_oY?Vt@3nzT{*^Bp^w5vMlE!#jLSe5p@N$lRG)<_&rjoWn51-#igr zu`m~LlUFP-tCsjeQZA58#h?@^hGoBks#FZgaRr0BqEap@_DOsZsFh5`5h+qIxGOp( ztmu>Die@>d=#`6#9*N%xG9*(`Aw`NF*{>Ltu%b+kE0}VFl$h_a;H^z=XOg=nT|qVF z>UBo^&8}0Xf`L)7PxhO=u%b|oE2t60pj=dxN_;WsmQ1k7&vJRt49%V1$CI9z?QD5} zXdc_h(f79+%Nir=_6S5-` zzlTVtB~vkcqG*rri>;3|__C#Y+?VeyI}tzOyUkA@+wb_b6~hY7hl(0Gr)ZFi3L^Oc zXK2MSDN>x2{ff>LVV{48e`#eeEywSGWu4^_%QnlCC#J>tulCaMGN<`(@K&AJKh6Ki zDc%Do&d%_U#~^-ww*Pj-%ai<TPYzLEQ}L6~8r z5hPz8N+U?diyt2(BHTUURiSu<%?q)PYzi(L>MUj*~j}ZS5S4+ ze|&sS8YKQ&g6w0GuU2n-d=SoAs2TGfx|W3Z$vO3D7H>K}NqYZw(l1&CNs@_LFEi3H zE|dpAATRmDC5~1IeOayY<)Vg6U-eTIiK8lQnWcBb(zvFcK*J}pHVrp_|_TLYjI$@hBT##O;ztdLTC*D=4%Wev};%MJ}&8p)hQ`PPBOz>9M z$m5weHJZb6RhQb1bJnUiSaq_5QF9^i^eT$k^Ielhh<>BL`>j=hT+|?E-wwIr0{4Gz zn@}BE3q7owpzghB)X^;bll5+sBGj2p8Sp zqSfGew;Jy@*Vf~?+NOP4;#Z^Y+-=kctvbi553uNhI(dp|zo6IX@33mF4pp615_09u zR{b|tJ;3S=S;&xAS5rF)e`m8`t$t&zI0a=BWMrC2)|RRt!s* z8fW#H6e~@BJSEncYh$%KD=BMe7EUB<*gGP7*N_cPBnShQ&zfu$TGy%E7t(Z!rC-$r zy(V&W3|MT{L5W|B`rv!UyWV;iT6LV{qUH>;XDvmoFEUmQ)~eoGl}L*QX+3Y!h#oZ0 zT6HC>K8#XrF{UP>X?VD;q&Z|+Wu1RDQzsMkS}Q&&53HkjLFrmY?8bHzJKB;B){5B% zYL4n@IaEoTtO0@He>V9%Yx~rQHO`mAIT)wOV`_X*&g5{3#j4(V5@?%he4?4Ji#53$ zWI+gZ)7Ol;*cSa6tG>YBB%~KNoiW)9TJ=j-owvovQ?2|hD^HeJ*OSq+*BkX&t4`;% zp;eO2x)O;Neq?+pQ?)aLm8){p%%&dLKslP_X%NWBHr3l{WBrqj)yk?9wSQ!%Nh;r} z-?8emQgj1JogXvhi}sJpn3`)&I4j>k8g(o=kz%%-xxqd7;zRgk*eW|S&MMYSkCyFlxH$V;d=c7Jr@+0YHZM#! z>NGh=qBOQQ51f?DJU74F*b8)aSRcp4eAf4Mu+5*3_>H+Ce_0dd|-wxyK znXu~fa#52?l9bJ~ljpxokm#^{+f0+Mk%;kbx88MDUiVoeKkqk+BUWB5hc^?oi9g&4 zx}~4s+hguD!5eIl*jYCCF(c>OY3@&7PtpV6>~lsQ!xe!3Qls`y%f>DK^pH|71ezmIogh?&KmL#70 zz*_s2tR12bt~Y5F+H=G*tLE;QstYSW>|J8j9@)Q*G&oIU)N zNY;uaQ^B5k7}SPfYHNHhDqt9@@bkm;XD+WuLsd zgIpf96Yx@oHvJ7)*AA9t$zHaU7h;^jU!i3tm){<8jiP;SIXCb%okE+v=rFR$$~|_T zGUR%BJhxbR2`jlojg@IdWFh@(Q}$@LXt7o-VHRO^K=u}q?KswSDQZEQF^+bU32V#) z2&z8#bEA&Vd6w|BfaZ`zU6QtEQ3_8CYd+8x!-m(Wo+m(Ai6&vk- zWmbJqx>W6d*m$3fX2`0mSx`oOKEtSct@`Bq-YLbZw#A7a68~-0ol>Og+BZ$wQS}TS zInb=t%VAX?+-cM}xxWoo-O3s?>dI%0df2M>SaqTFtGe9|zok}vz^VsXtVTW7Z@k;A z`U$IM8g&cpQNZdoR`u1Udxh+?$kVtvX}ehSW=S-?)R;wk;RS1!Cn>whaH_qg zk7hV_fr;KHMZ3uZ2en$2>@Y#{ZCbmm6*Y@`+fbY-^**aUXw^yfV9U1hhpar-Z|vPi zNzi@A$`d5Mgw)P{Kjb<@v;1?brm9hUlVwi{{j~Z8V^!sKjXjqIP0l-5h$l$GXG|K= zs%~UJ(rW9Jb9giH@$SJYbi1*NUR3U|K|CzJbDkgjjcniJ|3YBw4U<;%xPQ@_mB~wc z$V%3iOq5!W{iT?++9c(ZB$D1`yrW~$JgW{#k*Z^-8}AdN)I5K2_*PC#HJgl8nvM0zMJ8WIr2kf}M|*ua z$V6CwpR?*tiT^a}(X5awZt%MB)LV73-Y58LM<_^+#*P^a86u<1bC37pyvB)m%j2&AHC|8BigapTT(8E))zq#`<$hOmTUF z6ZN*!c_yh28%wNutjx#@tXr#yvCl8Rm&uf5vjut$z#73jOpl(yKWxYw$A{!D{p$t8j?-&ig^LNsZPs^n{Idl>v_b#$?%uXKjY{{owV~W-- z`|lz|NLsMsvhI}b@m&60{;ip@MJCx;>+`7f$$Q&my2EDY87pVNt0cujdAgEJr`w?{ zS^+DrH7T-&S4oCS?I=`lgWO@&Ig+xM>*6Xoyca9auS^yOtknzFs$PzxW*9%Sm!vAb zYpklQ)l3c?ZMQUu|13cYE}Hy~+Hz!Cb%o@rx~$!(^R4$*t8SD1sviBKQAfMfm#n%} zT2y_+&S9eJZ(B9vo2tk58t(!dyu+%8#d|mEL3{3wdS9?=Zi%DjhW)m?DPP@3&I-$x znex?2Sc6p7n>5;O8bwykq(;^C?;3ToRe#N@kIF?&TUynpp0WMVKtHYE}$UZ)-km)F-{heT!A6-5hcS(Jbt-^5BHA&$jkovhsF` zzlYQc?lS6V?HsXco-#$vk+$a^Jvu5)@Mw1&v{uKYMXeg;6jn@8U%iK7vc7dMkxM>u zlWgw_Q%t{LL@o$Bw+$Sv8mbsy^_2Q`%_oT~=Ks$5DGzPn@~Wf5R$oP^LZQ zUooxB&aKKM_bLDCnbE1%f$b*vNV`1zlz;iOs>nt+GgZ=s*|hBVP3~^?3GYAo|Jncl E054v3$p8QV delta 60635 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 diff --git a/slsSupportLib/include/sls/versionAPI.h b/slsSupportLib/include/sls/versionAPI.h index 8e7c2d493..245acb823 100644 --- a/slsSupportLib/include/sls/versionAPI.h +++ b/slsSupportLib/include/sls/versionAPI.h @@ -7,6 +7,6 @@ #define APIGOTTHARD2 "0.0.0 0x250909" #define APIMOENCH "0.0.0 0x250909" #define APIEIGER "0.0.0 0x250909" -#define APIXILINXCTB "0.0.0 0x260122" +#define APIXILINXCTB "0.0.0 0x260128" #define APIJUNGFRAU "0.0.0 0x250909" #define APIMYTHEN3 "0.0.0 0x250922" From c3c3970f1952d93352fc16d1aa7eedc8f91e586c Mon Sep 17 00:00:00 2001 From: froejdh_e Date: Wed, 28 Jan 2026 15:13:15 +0100 Subject: [PATCH 05/20] added 16bit 1 counter and 16bit 4 counters --- pyctbgui/pyctbgui/services/Plot.py | 20 ++++++++++++--- pyctbgui/pyctbgui/services/Transceiver.py | 5 +++- pyctbgui/pyctbgui/ui/plot.ui | 12 ++++++++- pyctbgui/pyctbgui/utils/decoder.py | 1 + pyctbgui/pyctbgui/utils/defines.py | 10 +++++++- pyctbgui/pyctbgui/utils/pixelmap.py | 31 +++++++++++++++++++++++ 6 files changed, 73 insertions(+), 6 deletions(-) diff --git a/pyctbgui/pyctbgui/services/Plot.py b/pyctbgui/pyctbgui/services/Plot.py index 0dfedd205..b172f8102 100644 --- a/pyctbgui/pyctbgui/services/Plot.py +++ b/pyctbgui/pyctbgui/services/Plot.py @@ -1,5 +1,6 @@ import logging from functools import partial +import pyctbgui.utils.pixelmap as pm import random from pathlib import Path @@ -415,13 +416,26 @@ class PlotTab(QtWidgets.QWidget): self.mainWindow.read_timer.start(Defines.Time_Plot_Refresh_ms) def setPixelMap(self): - if self.view.comboBoxPlot.currentText() == "Matterhorn": - self.mainWindow.nTransceiverRows = Defines.Matterhorn.nRows - self.mainWindow.nTransceiverCols = Defines.Matterhorn.nCols + if self.view.comboBoxPlot.currentText() == "Matterhorn02": + print("Setting pixel map for Matterhorn02") + self.mainWindow.nTransceiverRows = Defines.Matterhorn02.nRows + self.mainWindow.nTransceiverCols = Defines.Matterhorn02.nCols + self.mainWindow.pixel_map = pm.matterhorn_transceiver() + elif self.view.comboBoxPlot.currentText() == "Matterhorn1_16bit_1_counter": + print("Setting pixel map for Matterhorn1") + self.mainWindow.nTransceiverRows = Defines.Matterhorn1.nRows + self.mainWindow.nTransceiverCols = Defines.Matterhorn1.nCols + self.mainWindow.pixel_map = pm.matterhorn1_transceiver_16bit_1_counter() + elif self.view.comboBoxPlot.currentText() == "Matterhorn1_16bit_4_counters": + print("Setting pixel map for Matterhorn1 with 4 counters") + self.mainWindow.nTransceiverRows = Defines.Matterhorn1.nRows + self.mainWindow.nTransceiverCols = Defines.Matterhorn1.nCols + self.mainWindow.pixel_map = pm.matterhorn1_transceiver_16bit_4_counters() elif self.view.comboBoxPlot.currentText() == "Moench04": self.mainWindow.nAnalogRows = Defines.Moench04.nRows self.mainWindow.nAnalogCols = Defines.Moench04.nCols + def showPatternViewer(self, enable): if enable: self.mainWindow.framePatternViewer.show() diff --git a/pyctbgui/pyctbgui/services/Transceiver.py b/pyctbgui/pyctbgui/services/Transceiver.py index 98c8fc541..124de5d0b 100644 --- a/pyctbgui/pyctbgui/services/Transceiver.py +++ b/pyctbgui/pyctbgui/services/Transceiver.py @@ -133,7 +133,10 @@ class TransceiverTab(QtWidgets.QWidget): nbitsPerDBit += (8 - (dSamples % 8)) transceiverOffset += nDBitEnabled * (nbitsPerDBit // 8) trans_array = np.array(np.frombuffer(data, offset=transceiverOffset, dtype=np.uint16)) - return decoder.decode(trans_array, pm.matterhorn_transceiver()) + print(f'{trans_array.shape=}') + tmp = decoder.decode(trans_array, self.mainWindow.pixel_map) + print(f'{tmp.shape=}') + return tmp def processImageData(self, data, dSamples): """ diff --git a/pyctbgui/pyctbgui/ui/plot.ui b/pyctbgui/pyctbgui/ui/plot.ui index b01bc3e71..edebb4548 100644 --- a/pyctbgui/pyctbgui/ui/plot.ui +++ b/pyctbgui/pyctbgui/ui/plot.ui @@ -165,7 +165,17 @@ - Matterhorn + Matterhorn02 + + + + + Matterhorn1_16bit_4_counters + + + + + Matterhorn1_16bit_1_counter diff --git a/pyctbgui/pyctbgui/utils/decoder.py b/pyctbgui/pyctbgui/utils/decoder.py index 6197f811c..ca254b175 100644 --- a/pyctbgui/pyctbgui/utils/decoder.py +++ b/pyctbgui/pyctbgui/utils/decoder.py @@ -49,3 +49,4 @@ def matterhorn(trans_buffer): offset += nSamples return transceiver_frame + diff --git a/pyctbgui/pyctbgui/utils/defines.py b/pyctbgui/pyctbgui/utils/defines.py index 51c6ac1cb..413285185 100644 --- a/pyctbgui/pyctbgui/utils/defines.py +++ b/pyctbgui/pyctbgui/utils/defines.py @@ -50,7 +50,7 @@ class Defines: Matterhorn = 0 Moench04 = 1 - class Matterhorn: + class Matterhorn02: nRows = 48 nHalfCols = 24 nCols = 48 @@ -58,6 +58,14 @@ class Defines: tranceiverEnable = 0x3 nPixelsPerTransceiver = 4 + class Matterhorn1: + nRows = 256 + nHalfCols = 24 + nCols = 256 + nTransceivers = 2 + tranceiverEnable = 0x3 + nPixelsPerTransceiver = 4 + class Moench04: nRows = 400 nCols = 400 diff --git a/pyctbgui/pyctbgui/utils/pixelmap.py b/pyctbgui/pyctbgui/utils/pixelmap.py index 6cfa28a14..1dd533964 100644 --- a/pyctbgui/pyctbgui/utils/pixelmap.py +++ b/pyctbgui/pyctbgui/utils/pixelmap.py @@ -59,3 +59,34 @@ def matterhorn_transceiver(): offset += nSamples return out + + +def matterhorn1_transceiver_16bit_1_counter(): + pixel_map = np.zeros((256,256), np.uint32) + n_cols = 256 + n_rows = 256 + for row in range(n_rows): + col = 0 + for offset in range(0,64,4): + for pkg in range(offset,256,64): + for pixel in range(4): + pixel_map[row, col] = pixel+pkg+row*n_cols + col += 1 + + return pixel_map + +def matterhorn1_transceiver_16bit_4_counters(): + n_counters = 4 + n_cols = 256 + n_rows = 256 + pixel_map = np.zeros((n_rows*n_counters,n_cols,n_counters), np.uint32) + + for row in range(n_rows): + for counter in range(n_counters): + col = 0 + for offset in range(0,64,4): + for pkg in range(offset,256,64): + for pixel in range(4): + pixel_map[row+n_rows*counter, col] = pixel+pkg+row*n_cols*n_counters+n_cols*counter + col += 1 + return pixel_map \ No newline at end of file From 9e7952048a6317bd407f94bb90f9977adc9522a8 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 28 Jan 2026 15:20:34 +0100 Subject: [PATCH 06/20] calling setPower instead of setDac as the conversion should happen here --- .../bin/xilinx_ctbDetectorServer_developer | Bin 305832 -> 305832 bytes .../slsDetectorFunctionList.c | 7 +++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer b/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer index 35a9ed29e5c025148fdcb428002ff0d79b984133..61496029026f44393148f4b88585f85b351e9e44 100755 GIT binary patch delta 51798 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 Kzu@@~&;J8TT;IR| delta 52371 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 Cg2(y* diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c index 4b6a2b460..7bed0d759 100644 --- a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c +++ b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c @@ -420,7 +420,8 @@ void setupDetector() { if (idac == D_PWR_EMPTY) continue; int min = (idac == D_PWR_IO) ? VIO_MIN_MV : POWER_RGLTR_MIN; - setDAC(idac, min, 0); + // will not enable power at startup (dacValues = -1) + setPower(idac, min); } resetFlow(); @@ -1327,6 +1328,8 @@ void setPower(enum DACINDEX ind, int val) { LOG(logDEBUG1, ("Switching off power enable\n")); bus_w(addr, bus_r(addr) & ~(mask)); + int startup = dacValues[ind] == -1 ? 1 : 0; + // convert voltage to dac (power off is anyway done with power enable) if (val > 0) { @@ -1347,7 +1350,7 @@ void setPower(enum DACINDEX ind, int val) { setDAC(ind, dacval, 0); // if valid, enable power - if (dacval >= 0) { + if (dacval >= 0 && startup == 0) { LOG(logDEBUG1, ("Switching on power enable\n")); bus_w(addr, bus_r(addr) | mask); } From 5811e4e9ab3dd7fa56ae78ee8b42a6cd9003fa56 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 28 Jan 2026 15:30:42 +0100 Subject: [PATCH 07/20] added tests to ensure startup val is not -1 or -100 after powerchip in config for xilinx --- .../tests/Caller/test-Caller-chiptestboard.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp index 795937ee1..a36576d7d 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp @@ -1046,6 +1046,12 @@ TEST_CASE("v_abcd", "[.cmdcall]") { if (det_type == defs::CHIPTESTBOARD || det_type == defs::XILINX_CHIPTESTBOARD) { auto prev_val = det.getPower(indices[i]); + // this is the first command touching power dacs, should not be + // -100 + if (det_type == defs::XILINX_CHIPTESTBOARD) { + REQUIRE(prev_val.any(-100) == false); + REQUIRE(prev_val.any(-1) == false); + } { std::ostringstream oss; caller.call(cmds[i], {"0"}, -1, PUT, oss); From 8a0191e3f6c40e8ad0948b9c98cea32a865d3382 Mon Sep 17 00:00:00 2001 From: froejdh_e Date: Wed, 28 Jan 2026 15:54:27 +0100 Subject: [PATCH 08/20] using np.take and updated pixel map --- pyctbgui/pyctbgui/services/Plot.py | 2 +- pyctbgui/pyctbgui/services/Transceiver.py | 3 ++- pyctbgui/pyctbgui/utils/pixelmap.py | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pyctbgui/pyctbgui/services/Plot.py b/pyctbgui/pyctbgui/services/Plot.py index b172f8102..318a00317 100644 --- a/pyctbgui/pyctbgui/services/Plot.py +++ b/pyctbgui/pyctbgui/services/Plot.py @@ -428,7 +428,7 @@ class PlotTab(QtWidgets.QWidget): self.mainWindow.pixel_map = pm.matterhorn1_transceiver_16bit_1_counter() elif self.view.comboBoxPlot.currentText() == "Matterhorn1_16bit_4_counters": print("Setting pixel map for Matterhorn1 with 4 counters") - self.mainWindow.nTransceiverRows = Defines.Matterhorn1.nRows + self.mainWindow.nTransceiverRows = Defines.Matterhorn1.nRows*4 self.mainWindow.nTransceiverCols = Defines.Matterhorn1.nCols self.mainWindow.pixel_map = pm.matterhorn1_transceiver_16bit_4_counters() elif self.view.comboBoxPlot.currentText() == "Moench04": diff --git a/pyctbgui/pyctbgui/services/Transceiver.py b/pyctbgui/pyctbgui/services/Transceiver.py index 124de5d0b..e478587f5 100644 --- a/pyctbgui/pyctbgui/services/Transceiver.py +++ b/pyctbgui/pyctbgui/services/Transceiver.py @@ -134,7 +134,8 @@ class TransceiverTab(QtWidgets.QWidget): transceiverOffset += nDBitEnabled * (nbitsPerDBit // 8) trans_array = np.array(np.frombuffer(data, offset=transceiverOffset, dtype=np.uint16)) print(f'{trans_array.shape=}') - tmp = decoder.decode(trans_array, self.mainWindow.pixel_map) + # tmp = decoder.decode(trans_array, self.mainWindow.pixel_map) + tmp = np.take(trans_array, self.mainWindow.pixel_map) print(f'{tmp.shape=}') return tmp diff --git a/pyctbgui/pyctbgui/utils/pixelmap.py b/pyctbgui/pyctbgui/utils/pixelmap.py index 1dd533964..72fbcc385 100644 --- a/pyctbgui/pyctbgui/utils/pixelmap.py +++ b/pyctbgui/pyctbgui/utils/pixelmap.py @@ -79,7 +79,7 @@ def matterhorn1_transceiver_16bit_4_counters(): n_counters = 4 n_cols = 256 n_rows = 256 - pixel_map = np.zeros((n_rows*n_counters,n_cols,n_counters), np.uint32) + pixel_map = np.zeros((n_rows*n_counters,n_cols), np.uint32) for row in range(n_rows): for counter in range(n_counters): From c500891eb5e4de332df3aba6787a26aafc342f7f Mon Sep 17 00:00:00 2001 From: froejdh_e Date: Wed, 28 Jan 2026 16:43:43 +0100 Subject: [PATCH 09/20] removed comments --- pyctbgui/pyctbgui/services/Transceiver.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyctbgui/pyctbgui/services/Transceiver.py b/pyctbgui/pyctbgui/services/Transceiver.py index e478587f5..ecaf64282 100644 --- a/pyctbgui/pyctbgui/services/Transceiver.py +++ b/pyctbgui/pyctbgui/services/Transceiver.py @@ -133,10 +133,7 @@ class TransceiverTab(QtWidgets.QWidget): nbitsPerDBit += (8 - (dSamples % 8)) transceiverOffset += nDBitEnabled * (nbitsPerDBit // 8) trans_array = np.array(np.frombuffer(data, offset=transceiverOffset, dtype=np.uint16)) - print(f'{trans_array.shape=}') - # tmp = decoder.decode(trans_array, self.mainWindow.pixel_map) tmp = np.take(trans_array, self.mainWindow.pixel_map) - print(f'{tmp.shape=}') return tmp def processImageData(self, data, dSamples): From 28b2aa9673b99b6c7164ead803b9478b55ea8784 Mon Sep 17 00:00:00 2001 From: AliceMazzoleni99 Date: Tue, 3 Feb 2026 11:45:12 +0100 Subject: [PATCH 10/20] Dev/add simulator tests in GitHub workflows (#1337) * added simulator tests in github workflows * indentation error * typo * debug * Logging for debugging * added more debug lines * more debugging * debug * debug * debug * dont throw if process does not exist * debug * added absolute path to sls_detector commands * some refactoring in test scripts * added absolute path to all slsdet command * typo * ../tests/scripts/test_frame_synchronizer.py * raise exception upon failure for github workflows * removed hidden tags * some refactoring in test scripts * some refactoring * fixed CMakeLists * fixed unsuccesful merge * updated python tests using simulators * debug import error * debug module import * python -m runs module pytest as script - everything in path available * removed integartion tests * enable file write not to log files * run tests without log files * increased sleep time for udp packets * added logg level variable to cmake * added testing policies to documenattion * disabled check for num_frames for jungfrau & xilinx * set log level as cmake cached variable * disable tests for jungfrau and xilinx_ctb * check frames for HDF5 * updated Documentation of Testing * changed withdetectorsimulators to detectorintegration * replaced [.cmdcall] with [.detectorintegration] * check_file_size only disabled for jungfrau - disable for all roi tests * changed time to wait after receive to 5 ms * take into account half modules of eiger * num udp interfaces needs to be consistent across modules * suppressed warning enclosing if * config added 2 udp ports per default for moench and jungfrau * write detector output to console * allow jungfrau to tests num frames, remove unused variable (numinterfaces), add comment for future to handle traceback to know which calling function threw the files unmatched, added documentation for tests (examples for .detectoritnegration and how to disable marked tests, removed addditional argumetns to disable for test_simulator as one can just use ~, removed the check that checks for jungfrau checking number of frames at master attributes and at rx test, removed unused advanced_test_settings in test_simulator script, the num_mods check for multiple modules is removed and default num modules set to 1 for test_simulator (to be increased later), back to raising exception for killprocess * removed integration tests from cmakelists.txt and cmk.sh, modified the tests workflow command to reflect the disable argument and removed xilinx_ctb from test (fix fromdeveloper merge to be done) * filtering by actual name for disable certain tests on github workflow * minor refactor * wip * wip * changes to run on local rh9 runner instead of github workfloa * modified yml to remove some leftover from github workflow * test * fix build_dir in scripts (github workflow) and pytest dir in gitea workflow * making the local machine use python3.13 binary * pythonpath added * changes for build_Dir back * allowing ctb api tests * allowed ctb api tests and set up slsdetname envt variable for shared memory being reserved just for these tests * added rh8 workflow for local runner on gitea * remnants from rh9 local runner * remnants from rh9 local runner * conda env for all shell for local runner * allowing hdf5 to build on local runner * run all tests for both the runners * refactored fixtures a bit and merged some tests that use one session for entire server * test fail * test fix * adding github workflow to test without data file checks and without logs * documentation changes * unnecessary import in conftest * allowing the session_simulator to test for multiple modules and interfaces etc * allow test_simulator script to run for 2 modules for all modules except ctb and xilinx ctb * run upon push * removing the disable file check on github workflow * minor adjustment * testing without synch * reverting to previous * with log file * without the space * summary from file and more error extracts from file to terminal * minor * trying nlf for more details * updated with no log file to print everything to screen also for det and rxr * trying a no throw * stoi was more about indent in yaml * tries * wip * debug * number of frames inconsistent fix=>just take first one, only test xilinx * jungfrau tests without frames caught check * extend the disable file check to everywhere that creates files * specify path for test_simulator * withoutprinting == * wip * back with printing===, but not parsing file for errors anymore * lang? * wip * safe log? * wip2 * wip * dont split error as its streaming live, just raise * with log files * lang? * last resort * wip * test no det with general tests * show tests live * also include hidden integration tests * without extra summary? * revert * last resort again * tsquash on int64_t? * tsquash on int64_t? mroe print * writing to /tmp? * all tests * might be the fix? * write to file * fixed a few quiet mode no log file tests * work on any branch for github tests, work on also release candidates for gitea tests * added frame synchronizer tests to github workflow * moved tests to run_tests.yaml from cmake.yaml * documentation * disabled general tests --------- Co-authored-by: Dhanya Thattil --- .gitea/workflows/rh8-local-tests.yml | 62 +++ .gitea/workflows/rh9-local-tests.yml | 53 ++ .github/workflows/cmake.yaml | 19 +- .github/workflows/run_tests.yaml | 57 ++ CMakeLists.txt | 12 +- cmk.sh | 2 +- docs/src/Testing.rst | 166 ++++++ docs/src/index.rst | 1 + integrationTests/CMakeLists.txt | 40 -- integrationTests/test-eigerIntegration.cpp | 204 ------- .../test-integrationDectector.cpp | 522 ------------------ integrationTests/test-integrationMulti.cpp | 108 ---- integrationTests/test.cpp | 5 - python/tests/conftest.py | 166 ++++-- .../tests/{test_CtbAPI.py => test_det_api.py} | 159 +++--- {tests/scripts => python/tests}/test_free.py | 26 +- python/tests/test_pythonAPI.py | 19 +- .../tests/Caller/test-Caller-acquire.cpp | 20 +- .../Caller/test-Caller-chiptestboard.cpp | 174 +++--- .../tests/Caller/test-Caller-eiger.cpp | 51 +- .../tests/Caller/test-Caller-global.cpp | 22 +- .../tests/Caller/test-Caller-global.h | 2 - .../tests/Caller/test-Caller-gotthard2.cpp | 45 +- .../tests/Caller/test-Caller-jungfrau.cpp | 35 +- .../Caller/test-Caller-master-attributes.cpp | 6 +- .../tests/Caller/test-Caller-moench.cpp | 3 +- .../tests/Caller/test-Caller-mythen3.cpp | 33 +- .../tests/Caller/test-Caller-pattern.cpp | 28 +- .../tests/Caller/test-Caller-rx-running.cpp | 31 +- .../tests/Caller/test-Caller-rx.cpp | 80 +-- .../test-Caller-xilinx-chiptestboard.cpp | 2 +- .../tests/Caller/test-Caller.cpp | 282 +++++----- slsReceiverSoftware/src/Implementation.cpp | 3 +- .../tests/test-ArrangeDataBasedOnBitList.cpp | 12 +- slsSupportLib/tests/test-Timer.cpp | 4 +- slsSupportLib/tests/test-Version.cpp | 2 +- tests/CMakeLists.txt | 4 - tests/scripts/test_commands.py | 100 ---- tests/scripts/test_frame_synchronizer.py | 47 +- tests/scripts/test_roi.py | 82 --- tests/scripts/test_simulators.py | 95 ++-- tests/scripts/utils_for_test.py | 217 +++++--- 42 files changed, 1243 insertions(+), 1758 deletions(-) create mode 100644 .gitea/workflows/rh8-local-tests.yml create mode 100644 .gitea/workflows/rh9-local-tests.yml create mode 100644 .github/workflows/run_tests.yaml create mode 100644 docs/src/Testing.rst delete mode 100755 integrationTests/CMakeLists.txt delete mode 100644 integrationTests/test-eigerIntegration.cpp delete mode 100644 integrationTests/test-integrationDectector.cpp delete mode 100644 integrationTests/test-integrationMulti.cpp delete mode 100755 integrationTests/test.cpp rename python/tests/{test_CtbAPI.py => test_det_api.py} (82%) rename {tests/scripts => python/tests}/test_free.py (94%) delete mode 100644 tests/scripts/test_commands.py delete mode 100644 tests/scripts/test_roi.py diff --git a/.gitea/workflows/rh8-local-tests.yml b/.gitea/workflows/rh8-local-tests.yml new file mode 100644 index 000000000..8865e43ce --- /dev/null +++ b/.gitea/workflows/rh8-local-tests.yml @@ -0,0 +1,62 @@ +name: Run Simulator Tests on local RHEL8 + +on: + push: + branches: + - developer + - main + - 'dev/*' + - '*.*.*.rc' + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Debug + + +jobs: + build: + runs-on: "detectors-software-RH8" + steps: + - uses: actions/checkout@v4 + + - name: Configure CMake + run: | + source /home/gitea_runner/.bashrc + conda activate det + cmake -B build \ + -DCMAKE_BUILD_TYPE=Debug \ + -DSLS_USE_TESTS=ON \ + -DSLS_USE_SIMULATOR=ON \ + -DSLS_USE_PYTHON=On \ + -DSLS_USE_HDF5=ON + + - name: Build + # Build your program with the given configuration + run: | + source /home/gitea_runner/.bashrc + conda activate det + cmake --build build -j4 --config Debug + + - name: Python unit tests with simulators + run: | + source /home/gitea_runner/.bashrc + conda activate det + export PYTHONPATH=$PWD/build/bin:$PYTHONPATH + export SLSDETNAME=gitea_runner_tests + python -m pytest -m detectorintegration python/tests --detector-integration + python -m pytest python/tests + + + - name: Simulator unit tests + run: | + source /home/gitea_runner/.bashrc + conda activate det + cd build/bin + python test_simulators.py -g + + - name: Run frame synchronizer tests + run: | + source /home/gitea_runner/.bashrc + conda activate det + cd build/bin + python test_frame_synchronizer.py diff --git a/.gitea/workflows/rh9-local-tests.yml b/.gitea/workflows/rh9-local-tests.yml new file mode 100644 index 000000000..4aa1a9f98 --- /dev/null +++ b/.gitea/workflows/rh9-local-tests.yml @@ -0,0 +1,53 @@ +name: Run Simulator Tests on local RHEL9 + +on: + push: + branches: + - developer + - main + - 'dev/*' + - '*.*.*.rc' + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Debug + + +jobs: + build: + runs-on: "detectors-software-RH9" + steps: + - uses: actions/checkout@v4 + + - name: Configure CMake + run: | + cmake -B build \ + -DCMAKE_BUILD_TYPE=Debug \ + -DSLS_USE_TESTS=ON \ + -DSLS_USE_SIMULATOR=ON \ + -DSLS_USE_PYTHON=On \ + -DPython_EXECUTABLE=/usr/bin/python3.13 \ + -DPython_INCLUDE_DIR=/usr/include/python3.13 \ + -DPython_LIBRARY=/usr/lib64/libpython3.13.so \ + -DSLS_USE_HDF5=ON + + - name: Build + # Build your program with the given configuration + run: cmake --build build -j4 --config Debug + + - name: Python unit tests with simulators + run: | + export PYTHONPATH=$PWD/build/bin:$PYTHONPATH + export SLSDETNAME=gitea_runner_tests + python3.13 -m pytest -m detectorintegration python/tests --detector-integration + python3.13 -m pytest python/tests + + - name: Simulator unit tests + run: | + cd build/bin + python3.13 test_simulators.py -g + + - name: Run frame synchronizer tests + run: | + cd build/bin + python3.13 test_frame_synchronizer.py diff --git a/.github/workflows/cmake.yaml b/.github/workflows/cmake.yaml index 32aba389c..a7a161c56 100644 --- a/.github/workflows/cmake.yaml +++ b/.github/workflows/cmake.yaml @@ -29,19 +29,20 @@ jobs: - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type - run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DSLS_USE_TESTS=ON -DSLS_USE_HDF5=ON -DSLS_USE_GUI=ON -DSLS_USE_MOENCH=ON -DSLS_USE_PYTHON=ON + run: | + cmake -B ${{github.workspace}}/build \ + -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \ + -DSLS_USE_TESTS=ON \ + -DSLS_USE_SIMULATOR=ON \ + -DSLS_USE_PYTHON=ON \ + -DSLS_USE_HDF5=ON \ + -DSLS_USE_GUI=ON \ + -DSLS_USE_MOENCH=ON - name: Build # Build your program with the given configuration run: cmake --build ${{github.workspace}}/build -j4 --config ${{env.BUILD_TYPE}} - - name: C++ unit tests - working-directory: ${{github.workspace}}/build - run: ctest -C ${{env.BUILD_TYPE}} -j1 --rerun-failed --output-on-failure - - - name: Python unit tests - working-directory: ${{github.workspace}}/build/bin - run: | - python -m pytest ${{github.workspace}}/python/tests + diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml new file mode 100644 index 000000000..215efb8a3 --- /dev/null +++ b/.github/workflows/run_tests.yaml @@ -0,0 +1,57 @@ +name: Run Simulator Tests + +on: + push: + + + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Debug + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: 3.12 + cache: 'pip' + - run: pip install pytest numpy colorama + + - uses: awalsh128/cache-apt-pkgs-action@latest + with: + packages: libhdf5-dev + version: 1.0 + + - name: Configure CMake + run: | + cmake -B ${{github.workspace}}/build \ + -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \ + -DSLS_USE_TESTS=ON \ + -DSLS_USE_SIMULATOR=ON \ + -DSLS_USE_PYTHON=ON \ + -DSLS_USE_HDF5=ON + + - name: Build + # Build your program with the given configuration + run: cmake --build ${{github.workspace}}/build -j4 --config ${{env.BUILD_TYPE}} + + - name: Simulator unit tests + working-directory: ${{github.workspace}}/build/bin + run: | + python test_simulators.py --no-log-file --quiet --tests "[detectorintegration]~[disable_check_data_file]" + #remove --quiet to see more prints from simulator, receiver and tests + python test_frame_synchronizer.py + + + - name: Python unit tests with simulators + working-directory: ${{github.workspace}}/build/bin + run: | + python -m pytest -m detectorintegration ${{github.workspace}}/python/tests --detector-integration + python -m pytest ${{github.workspace}}/python/tests + + - name: C++ unit tests + working-directory: ${{github.workspace}}/build + run: ctest -C ${{env.BUILD_TYPE}} -j1 --rerun-failed --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index 194a2abad..b4ea785fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,8 +40,6 @@ include(FetchContent) option(SLS_FETCH_ZMQ_FROM_GITHUB "Fetch zmq from github" OFF) option(SLS_FETCH_PYBIND11_FROM_GITHUB "Fetch pybind11 from github" OFF) - - # Allow FetchContent_Populate to be called with a single argument # otherwise deprecated warning is issued # Note: From cmake 3.28 we can pass EXCLUDE_FROM_ALL to FetchContent_Declare @@ -200,7 +198,6 @@ option(SLS_USE_RECEIVER_BINARIES "Receiver binaries" ON) option(SLS_USE_GUI "GUI" OFF) option(SLS_USE_SIMULATOR "Simulator" OFF) option(SLS_USE_TESTS "TESTS" OFF) -option(SLS_USE_INTEGRATION_TESTS "Integration Tests" OFF) option(SLS_USE_SANITIZER "Sanitizers for debugging" OFF) option(SLS_USE_PYTHON "Python bindings" OFF) option(SLS_INSTALL_PYTHONEXT "Install the python extension in the install tree under CMAKE_INSTALL_PREFIX/python/" OFF) @@ -292,6 +289,11 @@ if(NOT TARGET slsProjectOptions) target_compile_features(slsProjectOptions INTERFACE cxx_std_17) endif() +set(LOG_MAX_REPORTING_LEVEL sls::logINFO CACHE STRING "set logging level") +target_compile_definitions(slsProjectOptions + INTERFACE LOG_MAX_REPORTING_LEVEL=${LOG_MAX_REPORTING_LEVEL} +) + if (NOT TARGET slsProjectWarnings) add_library(slsProjectWarnings INTERFACE) target_compile_options(slsProjectWarnings INTERFACE @@ -400,10 +402,6 @@ if (SLS_USE_SIMULATOR) add_subdirectory(slsDetectorServers) endif (SLS_USE_SIMULATOR) -if (SLS_USE_INTEGRATION_TESTS) - add_subdirectory(integrationTests) -endif (SLS_USE_INTEGRATION_TESTS) - if (SLS_USE_PYTHON) find_package (Python 3.8 COMPONENTS Interpreter Development.Module REQUIRED) set(PYBIND11_FINDPYTHON ON) # Needed for RH8 diff --git a/cmk.sh b/cmk.sh index dd58a37e1..498d590bd 100755 --- a/cmk.sh +++ b/cmk.sh @@ -250,7 +250,7 @@ fi #Tests if [ $TESTS -eq 1 ]; then - CMAKE_POST+=" -DSLS_USE_TESTS=ON -DSLS_USE_INTEGRATION_TESTS=ON" + CMAKE_POST+=" -DSLS_USE_TESTS=ON " echo "Tests Option enabled" fi diff --git a/docs/src/Testing.rst b/docs/src/Testing.rst new file mode 100644 index 000000000..515571788 --- /dev/null +++ b/docs/src/Testing.rst @@ -0,0 +1,166 @@ +Testing +========== + +We use ``catch2`` and ``pytest`` for unit testing the C++ and Python code. + +CATCH2 Tests +---------------- + +To build and run the catch2 tests use the following commands: + +.. code-block:: console + + cd build + cmake -DSLS_USE_TESTS=ON ../ + bin/tests + +Note that this requires that you have catch2 installed on your system. + +Naming Policy: +----------------- + +Per default all tests should be visible to catch2. + +If a test fails in the github or gitea actions hide the test by adding the tag ``[.]`` to the test name. + +.. code-block:: cpp + + TEST_CASE("This test is hidden from default runs", "[.]") { + REQUIRE(1 == 2); + } + +You can run all tests requiring a detector by running the following command: + +.. code-block:: console + + tests "[.detectorintegration]" + + +.. note :: + + This only works if a configured simulator (or an actual configured detector) and receiver are already set up to run the tests. There is a script that automatically sets up virtual detectors and runs all integration tests. See Section :ref:`Simulator Script. ` below. + + +If you want to disable testing that involves a data file that require pc tuning optimizations, add the hidden tag ``[.disable_check_data_file]`` to the test case. Please note that only some specific disable tests have been implemented so far. + +.. code-block:: console + + tests "[detectorintegration]~[disable_check_data_file]" + +.. note :: + + Ensure that there are no spaces betweent the tags and no '.', else hardly any test will be matched. + +.. _python simulator script: + +Simulator Script: +----------------- + +One can also just run the following script, which will run your tests for all the detector types (simulators only) with a pre-determined configuration. + +.. code-block:: console + + cd build + python bin/test_simulators.py + +This runs all tests marked with the tag ``[.detectorintegration]`` for all detector simulators. +If you want to run them for a specific virtual detector or a specific test use the following command: + +.. code-block:: console + cd build + python bin/test_simulators.py --servers jungfrau --test "[.rx]" + +You can exclude specific tests by adding the option ``~[]``. Again, we assume that this marker is added to the tests that you want to exclude. + +.. code-block:: console + cd build + python bin/test_simulators.py --servers eiger jungfrau moench --test "[detectorintegration]~[disable_check_data_file]" + +You can additionally run all the tests not requiring detectors using the script ``bin/test_simulators.py`` by passing the option ``--general-tests``. + +One can use ``--no-log-file`` if you dont want to create a log files and instead print to console. If you prefer to not print to console either, add ``--quiet``. + + +Pytest Tests +----------------- + +To run the python tests use the following commands: + +.. code-block:: console + + cd build + cmake ../ -DSLS_USE_PYTHON=ON + export PYTHONPATH=$PWD/bin + python -m pytest ../python/tests/ + +If a test requires a detector mark them with the pytest marker ``@pytest.mark.detectorintegration``. + +To run only tests requiring virtual detectors use the following command: + +.. code-block:: console + #in build + python -m pytest -m detectorintegration ../python/tests/ + +There is a helper test fixture in ``slsDetectorSoftware/python/tests/conftest.py`` called ``test_with_simulators`` that sets up virtual detectors and yields the test for all detectors. The set up is done for every test automatically. + +Example usage: + +.. code-block:: python + + import pytest + + @pytest.mark.detectorintegration + def test_example_with_simulator(test_with_simulators): + # your test code here + +If you want to run the test only for a specific test use the parametrized test fixture: + +Example usage: + +.. code-block:: python + + import pytest + + @pytest.mark.detectorintegration + @pytest.mark.parametrize("setup_parameters", [([""], )], indirect=True) + def test_example_with_specific_simulators(test_with_simulators, setup_parameters): + # your test code here + + +There is another helper test fixture in ``slsDetectorSoftware/python/tests/conftest.py`` called ``session_simulator`` that sets up virtual detectors and yields the test for all detectors. The difference with the previous fixture ``test_with_simulators`` is that this fixture will set up one detector at a time and run all the tests using this fixture before cleaning up and moving on to the next detector. It saves time if the setup and cleanup is expensive. + +Example usage: + +.. code-block:: python + + import pytest + + @pytest.mark.detectorintegration + def test_define_reg(session_simulator, request): + """ Test setting define_reg for ctb and xilinx_ctb.""" + det_type, num_interfaces, num_mods, d = session_simulator + assert d is not None + + from slsdet import RegisterAddress + + if det_type in ['ctb', 'xilinx_ctb']: + # your test code here + +For more specific parameters, you can parametrize the fixture like below: + +.. code-block:: python + + import pytest + + @pytest.mark.detectorintegration + @pytest.mark.parametrize( + "session_simulator", + [ + ("ctb", 1, 1), + ("xilinx_ctb", 1, 1), + ], + indirect=True, + ) + def test_define_reg(session_simulator): + det_type, num_interfaces, num_mods, d = session_simulator + # your test code here diff --git a/docs/src/index.rst b/docs/src/index.rst index 4ba406a17..d62597601 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -66,6 +66,7 @@ slsDetectorPackage container_utils type_traits ToString + Testing .. toctree:: :caption: Firmware diff --git a/integrationTests/CMakeLists.txt b/integrationTests/CMakeLists.txt deleted file mode 100755 index a270593fe..000000000 --- a/integrationTests/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -# SPDX-License-Identifier: LGPL-3.0-or-other -# Copyright (C) 2021 Contributors to the SLS Detector Package -# MESSAGE( STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR} ) -# MESSAGE( STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR} ) - - - -# include_directories( -# ${PROJECT_SOURCE_DIR}/catch -# ) - -# target_sources(tests PRIVATE -# ${CMAKE_CURRENT_SOURCE_DIR}/test-integrationMulti.cpp -# ${CMAKE_CURRENT_SOURCE_DIR}/test-integrationDectector.cpp -# ${CMAKE_CURRENT_SOURCE_DIR}/test-eigerIntegration.cpp -# ) - -# if(SLS_USE_TESTS) -# set(TEST_SOURCES -# src/test-slsDetector.cpp -# src/test.cpp -# ) -# add_executable(detector_test ${TEST_SOURCES}) - -# target_link_libraries(detector_test -# slsDetectorShared -# slsProjectOptions -# slsProjectWarnings -# slsSupportLib -# pthread -# rt -# ) -# set_target_properties(detector_test PROPERTIES -# RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin -# ) - - - -# endif() - diff --git a/integrationTests/test-eigerIntegration.cpp b/integrationTests/test-eigerIntegration.cpp deleted file mode 100644 index 6fa4124d2..000000000 --- a/integrationTests/test-eigerIntegration.cpp +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-other -// Copyright (C) 2021 Contributors to the SLS Detector Package -#include "DetectorImpl.h" -#include "catch.hpp" -#include "sls/string_utils.h" -#include "tests/globals.h" -#include - -namespace sls { - -class MultiDetectorFixture { - protected: - DetectorImpl d; - - public: - MultiDetectorFixture() : d(0, true, true) { - d.setHostname(test::hostname.c_str()); - if (test::my_ip != "undefined") - d.setReceiverHostname(test::my_ip); - } - ~MultiDetectorFixture() { d.freeSharedMemory(); } -}; - -TEST_CASE_METHOD(MultiDetectorFixture, "Set and get dacs", - "[.eigerintegration][cli]") { - auto th = 1000; - - // set and read back each individual dac of EIGER - d.setDAC(0, di::SVP, 0); - CHECK(d.setDAC(-1, di::SVP, 0) == 0); - d.setDAC(4000, di::SVN, 0); - CHECK(d.setDAC(-1, di::SVN, 0) == 4000); - d.setDAC(2000, di::VTR, 0); - CHECK(d.setDAC(-1, di::VTR, 0) == 2000); - d.setDAC(3500, di::VRF, 0); - CHECK(d.setDAC(-1, di::VRF, 0) == 3500); - d.setDAC(1400, di::VRS, 0); - CHECK(d.setDAC(-1, di::VRS, 0) == 1400); - d.setDAC(2556, di::VTGSTV, 0); - CHECK(d.setDAC(-1, di::VTGSTV, 0) == 2556); - d.setDAC(1500, di::VCMP_LL, 0); - CHECK(d.setDAC(-1, di::VCMP_LL, 0) == 1500); - d.setDAC(1400, di::VCMP_LR, 0); - CHECK(d.setDAC(-1, di::VCMP_LR, 0) == 1400); - d.setDAC(4000, di::CAL, 0); - CHECK(d.setDAC(-1, di::CAL, 0) == 4000); - d.setDAC(1300, di::VCMP_RL, 0); - CHECK(d.setDAC(-1, di::VCMP_RL, 0) == 1300); - d.setDAC(1200, di::VCMP_RR, 0); - CHECK(d.setDAC(-1, di::VCMP_RR, 0) == 1200); - d.setDAC(1100, di::RXB_RB, 0); - CHECK(d.setDAC(-1, di::RXB_RB, 0) == 1100); - d.setDAC(1100, di::RXB_LB, 0); - CHECK(d.setDAC(-1, di::RXB_LB, 0) == 1100); - d.setDAC(1500, di::VCP, 0); - CHECK(d.setDAC(-1, di::VCP, 0) == 1500); - d.setDAC(2000, di::VCN, 0); - CHECK(d.setDAC(-1, di::VCN, 0) == 2000); - d.setDAC(1550, di::VIS, 0); - CHECK(d.setDAC(-1, di::VIS, 0) == 1550); - d.setDAC(660, di::IO_DELAY, 0); - CHECK(d.setDAC(-1, di::IO_DELAY, 0) == 660); - - // setting threshold sets all individual vcmp - d.setDAC(th, di::THRESHOLD, 0); - CHECK(d.setDAC(-1, di::THRESHOLD, 0) == th); - CHECK(d.setDAC(-1, di::VCMP_LL, 0) == th); - CHECK(d.setDAC(-1, di::VCMP_LR, 0) == th); - CHECK(d.setDAC(-1, di::VCMP_RL, 0) == th); - CHECK(d.setDAC(-1, di::VCMP_RR, 0) == th); - - // different values gives -1 - if (d.getNumberOfDetectors() > 1) { - d.setDAC(1600, di::VCMP_LL, 0, 0); - d.setDAC(1700, di::VCMP_LL, 0, 1); - CHECK(d.setDAC(-1, di::VCMP_LL, 0, 0) == 1600); - CHECK(d.setDAC(-1, di::VCMP_LL, 0, 1) == 1700); - CHECK(d.setDAC(-1, di::VCMP_LL, 0) == -1); - CHECK(d.setDAC(-1, di::THRESHOLD, 0) == -1); - CHECK(d.setDAC(-1, di::THRESHOLD, 0, 0) == -1); - CHECK(d.setDAC(-1, di::THRESHOLD, 0, 1) == -1); - } -} - -TEST_CASE_METHOD(MultiDetectorFixture, "Read temperatures", - "[.eigerintegration][cli]") { - std::vector tempindex{di::TEMPERATURE_FPGA, di::TEMPERATURE_FPGA2, - di::TEMPERATURE_FPGA3}; - for (auto index : tempindex) { - for (int i = 0; i != d.getNumberOfDetectors(); ++i) { - double temp = static_cast(d.getADC(index, 0)) / 1000; - CHECK(temp > 20); - CHECK(temp < 60); - } - } -} - -int to_time(uint32_t reg) { - uint32_t clocks = reg >> 3; - uint32_t exponent = (reg & 0b111) + 1; - return clocks * pow(10, exponent); -} - -TEST_CASE_METHOD(MultiDetectorFixture, "Read/write register", - "[.eigerintegration][cli]") { - d.setNumberOfFrames(1); - d.setExposureTime(10000); - d.acquire(); - CHECK(to_time(d.readRegister(0x4, 0)) == 10000); - - d.writeRegister(0x4, 500); - CHECK(d.readRegister(0x4) == 500); -} - -TEST_CASE_METHOD(MultiDetectorFixture, "Set dynamic range", - "[.eigerintegration][cli][dr]") { - std::vector dynamic_range{4, 8, 16, 32}; - for (auto dr : dynamic_range) { - d.setDynamicRange(dr); - CHECK(d.setDynamicRange() == dr); - } -} - -TEST_CASE_METHOD(MultiDetectorFixture, "Set clock divider", - "[.eigerintegration][cli][this]") { - for (int i = 0; i != 3; ++i) { - d.setSpeed(sv::CLOCK_DIVIDER, i); - CHECK(d.setSpeed(sv::CLOCK_DIVIDER) == i); - } -} - -TEST_CASE_METHOD(MultiDetectorFixture, "Get time left", - "[.eigerintegration][cli]") { - CHECK_THROWS(d.getTimeLeft(ti::PROGRESS)); -} - -TEST_CASE_METHOD(MultiDetectorFixture, "Get ID", "[.eigerintegration][cli]") { - std::string hn = test::hostname; - hn.erase(std::remove(begin(hn), end(hn), 'b'), end(hn)); - hn.erase(std::remove(begin(hn), end(hn), 'e'), end(hn)); - auto hostnames = split(hn, '+'); - CHECK(hostnames.size() == d.getNumberOfDetectors()); - for (int i = 0; i != d.getNumberOfDetectors(); ++i) { - CHECK(d.getId(defs::DETECTOR_SERIAL_NUMBER, 0) == - std::stoi(hostnames[0])); - } -} - -TEST_CASE_METHOD(MultiDetectorFixture, "Lock server", - "[.eigerintegration][cli]") { - - d.lockServer(1); - CHECK(d.lockServer() == 1); - d.lockServer(0); - CHECK(d.lockServer() == 0); -} - -TEST_CASE_METHOD(MultiDetectorFixture, "Settings", "[.eigerintegration][cli]") { - CHECK(d.setSettings(defs::STANDARD) == defs::STANDARD); -} - -TEST_CASE_METHOD(MultiDetectorFixture, "Set readout flags", - "[.eigerintegration][cli]") { - d.setReadOutFlags(defs::PARALLEL); - CHECK((d.setReadOutFlags() & defs::PARALLEL)); - - d.setReadOutFlags(defs::NONPARALLEL); - CHECK_FALSE((d.setReadOutFlags() & defs::PARALLEL)); - CHECK((d.setReadOutFlags() & defs::NONPARALLEL)); -} - -// TEST_CASE_METHOD(MultiDetectorFixture, "Flow control and tengiga", -// "[.eigerintegration][cli]") { -// d.setFlowControl10G(1); -// CHECK(d.setFlowControl10G() == 1); -// d.setFlowControl10G(0); -// CHECK(d.setFlowControl10G() == 0); - -// d.enableTenGigabitEthernet(1); -// CHECK(d.enableTenGigabitEthernet() == 1); -// d.enableTenGigabitEthernet(0); -// CHECK(d.enableTenGigabitEthernet() == 0); -// } - -TEST_CASE_METHOD(MultiDetectorFixture, "activate", "[.eigerintegration][cli]") { - d.activate(0); - CHECK(d.activate() == 0); - d.activate(1); - CHECK(d.activate() == 1); -} - -TEST_CASE_METHOD(MultiDetectorFixture, "all trimbits", - "[.eigerintegration][cli]") { - d.setAllTrimbits(32); - CHECK(d.setAllTrimbits(-1) == 32); -} - -TEST_CASE_METHOD(MultiDetectorFixture, "rate correction", - "[.eigerintegration][cli]") { - d.setRateCorrection(200); - CHECK(d.getRateCorrection() == 200); -} - -} // namespace sls diff --git a/integrationTests/test-integrationDectector.cpp b/integrationTests/test-integrationDectector.cpp deleted file mode 100644 index c02066513..000000000 --- a/integrationTests/test-integrationDectector.cpp +++ /dev/null @@ -1,522 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-other -// Copyright (C) 2021 Contributors to the SLS Detector Package - -#include "catch.hpp" - -#include "DetectorImpl.h" -#include "Module.h" -#include "sls/ClientSocket.h" -#include "sls/logger.h" -#include "sls/sls_detector_defs.h" - -#include "sls/Timer.h" -#include "sls/sls_detector_funcs.h" -#include -#include -#define VERBOSE - -// Header holding all configurations for different detectors -#include "tests/config.h" -#include "tests/globals.h" -// using namespace test; -// using dt = slsDetectorDefs::detectorType; -// extern std::string hostname; -// extern std::string detector_type; -// extern dt type; - -namespace sls { - -TEST_CASE("Single detector no receiver", "[.integration][.single]") { - auto t = Module::getTypeFromDetector(test::hostname); - CHECK(t == test::type); - - Module d(t); - CHECK(d.getDetectorTypeAsEnum() == t); - CHECK(d.getDetectorTypeAsString() == test::detector_type); - - d.setHostname(test::hostname); - CHECK(d.getHostname() == test::hostname); - - CHECK(d.setDetectorType() == test::type); - - d.freeSharedMemory(); -} - -TEST_CASE("Set control port then create a new object with this control port", - "[.integration][.single]") { - /* - TODO! - Standard port but should not be hardcoded - Is this the best way to initialize the detectors - Using braces to make the object go out of scope - */ - int old_cport = DEFAULT_TCP_CNTRL_PORTNO; - int old_sport = DEFAULT_TCP_STOP_PORTNO; - int new_cport = 1993; - int new_sport = 2000; - { - Module d(test::type); - d.setHostname(test::hostname); - CHECK(d.getControlPort() == old_cport); - d.setControlPort(new_cport); - CHECK(d.getStopPort() == old_sport); - d.setStopPort(new_sport); - d.freeSharedMemory(); - } - { - Module d(test::type); - d.setHostname(test::hostname); - d.setControlPort(new_cport); - d.setStopPort(new_sport); - CHECK(d.getControlPort() == new_cport); - CHECK(d.getStopPort() == new_sport); - - // Reset standard ports - d.setControlPort(old_cport); - d.setStopPort(old_sport); - d.freeSharedMemory(); - } - - Module d(test::type); - d.setHostname(test::hostname); - CHECK(d.getStopPort() == DEFAULT_TCP_STOP_PORTNO); - d.freeSharedMemory(); -} - -TEST_CASE("single EIGER detector no receiver basic set and get", - "[.integration][eiger]") { - // TODO! this test should take command line arguments for config - SingleDetectorConfig c; - - // Read type by connecting to the detector - auto type = Module::getTypeFromDetector(c.hostname); - CHECK(type == c.type_enum); - - // Create Module of said type and set hostname and detector online - Module d(type); - CHECK(d.getDetectorTypeAsEnum() == type); - CHECK(d.getDetectorTypeAsString() == c.type_string); - - d.setHostname(c.hostname); - CHECK(d.getHostname() == c.hostname); - - CHECK(d.getUseReceiverFlag() == false); - CHECK_NOTHROW(d.checkDetectorVersionCompatibility()); - - // Setting and reading exposure time - auto t = 1000000000; - d.setExptime(t); - CHECK(d.getExptime() == t); - - // size of an eiger half module with and without gap pixels - CHECK(d.getTotalNumberOfChannels() == 256 * 256 * 4); - CHECK(d.getTotalNumberOfChannels(slsDetectorDefs::dimension::X) == 1024); - CHECK(d.getTotalNumberOfChannels(slsDetectorDefs::dimension::Y) == 256); - // CHECK(d.getTotalNumberOfChannels(slsDetectorDefs::dimension::Z) == 1); - CHECK(d.getTotalNumberOfChannelsInclGapPixels( - slsDetectorDefs::dimension::X) == 1024); - CHECK(d.getTotalNumberOfChannelsInclGapPixels( - slsDetectorDefs::dimension::Y) == 256); - // CHECK(d.getTotalNumberOfChannelsInclGapPixels(slsDetectorDefs::dimension::Z) - // == 1); - - CHECK(d.getNChans() == 256 * 256); - CHECK(d.getNChans(slsDetectorDefs::dimension::X) == 256); - CHECK(d.getNChans(slsDetectorDefs::dimension::Y) == 256); - // CHECK(d.getNChans(slsDetectorDefs::dimension::Z) == 1); - - CHECK(d.getNChips() == 4); - CHECK(d.getNChips(slsDetectorDefs::dimension::X) == 4); - CHECK(d.getNChips(slsDetectorDefs::dimension::Y) == 1); - // CHECK(d.getNChips(slsDetectorDefs::dimension::Z) == 1); - - d.freeSharedMemory(); -} - -TEST_CASE("Locking mechanism and last ip", "[.integration][.single]") { - Module d(test::type); - d.setHostname(test::hostname); - - // Check that detector server is unlocked then lock - CHECK(d.lockServer() == 0); - d.lockServer(1); - CHECK(d.lockServer() == 1); - - // Can we still access the detector while it's locked - auto t = 1300000000; - d.setExptime(t); - CHECK(d.getExptime() == t); - - // unlock again and free - d.lockServer(0); - CHECK(d.lockServer() == 0); - - CHECK(d.getLastClientIP() == test::my_ip); - d.freeSharedMemory(); -} - -TEST_CASE("Set settings", "[.integration][.single]") { - Module d(test::type); - d.setHostname(test::hostname); - CHECK(d.setSettings(defs::STANDARD) == defs::STANDARD); -} - -TEST_CASE("Timer functions", "[.integration][cli]") { - // FRAME_NUMBER, /**< number of real time frames: total number of - // acquisitions is number or frames*number of triggers */ ACQUISITION_TIME, - // /**< exposure time */ FRAME_PERIOD, /**< period between exposures */ - // DELAY_AFTER_TRIGGER, /**< delay between trigger and start of exposure or - // readout (in triggered mode) */ GATES_NUMBER, /**< number of gates per - // frame (in gated mode) */ TRIGGER_NUMBER, /**< number of triggers: total - // number of acquisitions is number or frames*number of triggers */ - // ACTUAL_TIME, /**< Actual time of the detector's internal timer */ - // MEASUREMENT_TIME, /**< Time of the measurement from the detector (fifo) - // */ - - // PROGRESS, /**< fraction of measurement elapsed - only get! */ - // MEASUREMENTS_NUMBER, - // FRAMES_FROM_START, - // FRAMES_FROM_START_PG, - // SAMPLES, - // SUBFRAME_ACQUISITION_TIME, /**< subframe exposure time */ - // STORAGE_CELL_NUMBER, /** list = m.getReceiverDbitList(); - list.clear(); - for (int i = 0; i < 10; ++i) - list.push_back(i); - m.setReceiverDbitList(list); - - CHECK(m.getReceiverDbitList().size() == 10); - - list.push_back(64); - CHECK_THROWS_AS(m.setReceiverDbitList(list), RuntimeError); - CHECK_THROWS_WITH(m.setReceiverDbitList(list), - Catch::Matchers::Contains("be between 0 and 63")); - - list.clear(); - for (int i = 0; i < 65; ++i) - list.push_back(i); - CHECK(list.size() == 65); - CHECK_THROWS_WITH(m.setReceiverDbitList(list), - Catch::Matchers::Contains("be greater than 64")); - - list.clear(); - m.setReceiverDbitList(list); - CHECK(m.getReceiverDbitList().empty()); - - // adcinvert - m.setADCInvert(0); - CHECK(m.getADCInvert() == 0); - m.setADCInvert(5); - CHECK(m.getADCInvert() == 5); - m.setADCInvert(-1); - CHECK(m.getADCInvert() == -1); - - // ext sampling reg - m.setExternalSamplingSource(0); - CHECK(m.getExternalSamplingSource() == 0); - m.setExternalSamplingSource(62); - CHECK(m.getExternalSamplingSource() == 62); - CHECK_THROWS_WITH(m.setExternalSamplingSource(64), - Catch::Matchers::Contains("be 0-63")); - CHECK(m.getExternalSamplingSource() == 62); - m.setExternalSampling(1); - CHECK(m.getExternalSampling() == 1); - m.setExternalSampling(0); - CHECK(m.getExternalSampling() == 0); - m.setExternalSampling(1); - CHECK(m.getExternalSampling() == 1); - CHECK(m.readRegister(0x7b) == 0x1003E); -} - -TEST_CASE("Eiger or Jungfrau nextframenumber", - "[.eigerintegration][.jungfrauintegration][nextframenumber]") { - SingleDetectorConfig c; - - // pick up multi detector from shm id 0 - DetectorImpl m(0); - - // ensure ctb detector type, hostname and online - REQUIRE( - ((m.getDetectorTypeAsEnum() == slsDetectorDefs::detectorType::EIGER) || - (m.getDetectorTypeAsEnum() == - slsDetectorDefs::detectorType::JUNGFRAU))); - REQUIRE(m.getHostname() == c.hostname); - - CHECK(m.setNumberOfFrames(1) == 1); - - // starting fnum - uint64_t val = 8; - - m.setNextFrameNumber(val); - CHECK(m.getNextFrameNumber() == val); - CHECK(m.acquire() == slsDetectorDefs::OK); - CHECK(m.getReceiverCurrentFrameIndex() == val); - - ++val; - CHECK(m.acquire() == slsDetectorDefs::OK); - CHECK(m.getReceiverCurrentFrameIndex() == val); - - CHECK_THROWS_AS(m.setNextFrameNumber(0), RuntimeError); - - if (m.getDetectorTypeAsString() == "Eiger") { - val = 281474976710655; - } else if (m.getDetectorTypeAsString() == "Jungfrau") { - val = 18446744073709551615; - } - m.setNextFrameNumber(val); - CHECK(m.getNextFrameNumber() == val); - CHECK(m.acquire() == slsDetectorDefs::OK); - CHECK(m.getReceiverCurrentFrameIndex() == val); - CHECK(m.getNextFrameNumber() == (val + 1)); -} - -TEST_CASE("Eiger partialread", "[.eigerintegration][partialread]") { - SingleDetectorConfig c; - - // pick up multi detector from shm id 0 - DetectorImpl m(0); - - // ensure detector type, hostname - REQUIRE( - (m.getDetectorTypeAsEnum() == slsDetectorDefs::detectorType::EIGER)); - REQUIRE(m.getHostname() == c.hostname); - - m.setDynamicRange(16); - m.enableTenGigabitEthernet(0); - m.setPartialReadout(256); - CHECK(m.getPartialReadout() == 256); - m.setPartialReadout(1); - CHECK(m.getPartialReadout() == 1); - - m.setDynamicRange(8); - m.setPartialReadout(256); - CHECK(m.getPartialReadout() == 256); - CHECK_THROWS_AS(m.setPartialReadout(1), RuntimeError); - CHECK(m.getPartialReadout() == 256); - CHECK_THROWS_AS(m.setPartialReadout(0), RuntimeError); - m.setPartialReadout(256); -} - -} // namespace sls diff --git a/integrationTests/test-integrationMulti.cpp b/integrationTests/test-integrationMulti.cpp deleted file mode 100644 index 40f77c6dd..000000000 --- a/integrationTests/test-integrationMulti.cpp +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-other -// Copyright (C) 2021 Contributors to the SLS Detector Package -#include "DetectorImpl.h" -#include "catch.hpp" -#include "sls/string_utils.h" -#include "tests/globals.h" -#include - -namespace sls { - -using namespace Catch::literals; - -TEST_CASE("Initialize a multi detector", "[.integration][.multi]") { - auto hostnames = split(test::hostname, '+'); - - DetectorImpl d(0, true, true); - d.setHostname(test::hostname.c_str()); - - CHECK(d.getHostname() == test::hostname); - for (size_t i = 0; i != hostnames.size(); ++i) { - CHECK(d.getHostname(i) == hostnames[i]); - } - - CHECK(d.getDetectorTypeAsEnum() == test::type); - CHECK(d.getDetectorTypeAsString() == test::detector_type); - - CHECK(d.getNumberOfDetectors() == hostnames.size()); - d.freeSharedMemory(); -} - -TEST_CASE("Set and read timers", "[.integration][.multi]") { - - DetectorImpl d(0, true, true); - d.setHostname(test::hostname.c_str()); - - // FRAME_NUMBER - int n_frames = 3; - d.setNumberOfFrames(n_frames); - CHECK(d.setNumberOfFrames() == n_frames); - - // ACQUISITION_TIME - double exptime = 0.3; - d.setExposureTime(exptime, true); - CHECK(d.setExposureTime(-1, true) == Approx(exptime)); - CHECK(d.setExposureTime(-1) == Approx(exptime * 1E9)); - - // FRAME_PERIOD, - double period = 0.5; - d.setExposurePeriod(period, true); - CHECK(d.setExposurePeriod(-1, true) == Approx(period)); - CHECK(d.setExposurePeriod(-1) == Approx(period * 1E9)); - - // DELAY_AFTER_TRIGGER, - // GATES_NUMBER, - // TRIGGER_NUMBER, - // ACTUAL_TIME - // MEASUREMENT_TIME - - // PROGRESS, /**< fraction of measurement elapsed - only get! */ - // MEASUREMENTS_NUMBER, - - // FRAMES_FROM_START, - // FRAMES_FROM_START_PG, - // SAMPLES, - - // SUBFRAME_ACQUISITION_TIME, /**< subframe exposure time */ - double subframe_exposure = 2000000; // ns - if (test::type == dt::EIGER) { - d.setSubFrameExposureTime(subframe_exposure); - CHECK(d.setSubFrameExposureTime(-1) == Approx(subframe_exposure)); - } - - // STORAGE_CELL_NUMBER, /** cmds{"v_a", "v_b", "v_c", "v_d"}; + std::vector indices{defs::V_POWER_A, defs::V_POWER_B, + defs::V_POWER_C, defs::V_POWER_D}; + if (det.isVirtualDetectorServer().tsquash("Inconsistent virtual servers")) { + cmds.push_back("v_io"); + indices.push_back(defs::V_POWER_IO); + } - std::vector cmds{"v_a", "v_b", "v_c", "v_d", "v_io"}; - std::vector indices{defs::V_POWER_A, defs::V_POWER_B, - defs::V_POWER_C, defs::V_POWER_D, - defs::V_POWER_IO}; - - for (size_t i = 0; i < cmds.size(); ++i) { - if (det_type == defs::CHIPTESTBOARD || - det_type == defs::XILINX_CHIPTESTBOARD) { - auto prev_val = det.getPower(indices[i]); - // this is the first command touching power dacs, should not be - // -100 - if (det_type == defs::XILINX_CHIPTESTBOARD) { - REQUIRE(prev_val.any(-100) == false); - REQUIRE(prev_val.any(-1) == false); - } - { - std::ostringstream oss; - caller.call(cmds[i], {"0"}, -1, PUT, oss); - REQUIRE(oss.str() == cmds[i] + " 0\n"); - } - { - std::ostringstream oss1, oss2; - caller.call(cmds[i], {"1200"}, -1, PUT, oss1); - REQUIRE(oss1.str() == cmds[i] + " 1200\n"); - caller.call(cmds[i], {}, -1, GET, oss2); - REQUIRE(oss2.str() == cmds[i] + " 1200\n"); - } - for (int i = 0; i != det.size(); ++i) { - if (det_type == defs::XILINX_CHIPTESTBOARD && - prev_val[i] == -100) { - prev_val[i] = 0; - continue; - } - det.setPower(indices[i], prev_val[i], {i}); - } - - } else { - REQUIRE_THROWS(caller.call(cmds[i], {}, -1, GET)); + for (size_t i = 0; i < cmds.size(); ++i) { + if (det_type == defs::CHIPTESTBOARD || + det_type == defs::XILINX_CHIPTESTBOARD) { + auto prev_val = det.getPower(indices[i]); + // this is the first command touching power dacs, should not be + // -100 + if (det_type == defs::XILINX_CHIPTESTBOARD) { + REQUIRE(prev_val.any(-100) == false); + REQUIRE(prev_val.any(-1) == false); } + { + std::ostringstream oss; + caller.call(cmds[i], {"0"}, -1, PUT, oss); + REQUIRE(oss.str() == cmds[i] + " 0\n"); + } + { + std::ostringstream oss1, oss2; + caller.call(cmds[i], {"1200"}, -1, PUT, oss1); + REQUIRE(oss1.str() == cmds[i] + " 1200\n"); + caller.call(cmds[i], {}, -1, GET, oss2); + REQUIRE(oss2.str() == cmds[i] + " 1200\n"); + } + for (int imod = 0; imod != det.size(); ++imod) { + if (det_type == defs::XILINX_CHIPTESTBOARD && + prev_val[imod] == -100) { + prev_val[imod] = 0; + } + det.setPower(indices[i], prev_val[imod], {imod}); + } + + } else { + REQUIRE_THROWS(caller.call(cmds[i], {}, -1, GET)); } } } -TEST_CASE("v_io", "[.cmdcall]") { +TEST_CASE("v_io", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1093,7 +1093,7 @@ TEST_CASE("v_io", "[.cmdcall]") { } } -TEST_CASE("v_chip", "[.cmdcall]") { +TEST_CASE("v_chip", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1105,7 +1105,7 @@ TEST_CASE("v_chip", "[.cmdcall]") { } } -TEST_CASE("vm_a", "[.cmdcall]") { +TEST_CASE("vm_a", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1116,7 +1116,7 @@ TEST_CASE("vm_a", "[.cmdcall]") { } } -TEST_CASE("vm_b", "[.cmdcall]") { +TEST_CASE("vm_b", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1127,7 +1127,7 @@ TEST_CASE("vm_b", "[.cmdcall]") { } } -TEST_CASE("vm_c", "[.cmdcall]") { +TEST_CASE("vm_c", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1138,7 +1138,7 @@ TEST_CASE("vm_c", "[.cmdcall]") { } } -TEST_CASE("vm_d", "[.cmdcall]") { +TEST_CASE("vm_d", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1149,7 +1149,7 @@ TEST_CASE("vm_d", "[.cmdcall]") { } } -TEST_CASE("vm_io", "[.cmdcall]") { +TEST_CASE("vm_io", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1160,7 +1160,7 @@ TEST_CASE("vm_io", "[.cmdcall]") { } } -TEST_CASE("im_a", "[.cmdcall]") { +TEST_CASE("im_a", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1171,7 +1171,7 @@ TEST_CASE("im_a", "[.cmdcall]") { } } -TEST_CASE("im_b", "[.cmdcall]") { +TEST_CASE("im_b", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1182,7 +1182,7 @@ TEST_CASE("im_b", "[.cmdcall]") { } } -TEST_CASE("im_c", "[.cmdcall]") { +TEST_CASE("im_c", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1193,7 +1193,7 @@ TEST_CASE("im_c", "[.cmdcall]") { } } -TEST_CASE("im_d", "[.cmdcall]") { +TEST_CASE("im_d", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1204,7 +1204,7 @@ TEST_CASE("im_d", "[.cmdcall]") { } } -TEST_CASE("im_io", "[.cmdcall]") { +TEST_CASE("im_io", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1215,7 +1215,7 @@ TEST_CASE("im_io", "[.cmdcall]") { } } -TEST_CASE("slowadc", "[.cmdcall]") { +TEST_CASE("slowadc", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1231,7 +1231,7 @@ TEST_CASE("slowadc", "[.cmdcall]") { } } -TEST_CASE("extsampling", "[.cmdcall]") { +TEST_CASE("extsampling", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1261,7 +1261,7 @@ TEST_CASE("extsampling", "[.cmdcall]") { } } -TEST_CASE("extsamplingsrc", "[.cmdcall]") { +TEST_CASE("extsamplingsrc", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1292,7 +1292,7 @@ TEST_CASE("extsamplingsrc", "[.cmdcall]") { } } -TEST_CASE("diodelay", "[.cmdcall]") { +TEST_CASE("diodelay", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1315,7 +1315,7 @@ TEST_CASE("diodelay", "[.cmdcall]") { } } -TEST_CASE("led", "[.cmdcall]") { +TEST_CASE("led", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-eiger.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-eiger.cpp index ae6a6c824..4f05256fa 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-eiger.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-eiger.cpp @@ -19,7 +19,7 @@ using test::PUT; /** temperature */ -TEST_CASE("temp_fpgaext", "[.cmdcall]") { +TEST_CASE("temp_fpgaext", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -34,7 +34,7 @@ TEST_CASE("temp_fpgaext", "[.cmdcall]") { } } -TEST_CASE("temp_10ge", "[.cmdcall]") { +TEST_CASE("temp_10ge", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -49,7 +49,7 @@ TEST_CASE("temp_10ge", "[.cmdcall]") { } } -TEST_CASE("temp_dcdc", "[.cmdcall]") { +TEST_CASE("temp_dcdc", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -64,7 +64,7 @@ TEST_CASE("temp_dcdc", "[.cmdcall]") { } } -TEST_CASE("temp_sodl", "[.cmdcall]") { +TEST_CASE("temp_sodl", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -79,7 +79,7 @@ TEST_CASE("temp_sodl", "[.cmdcall]") { } } -TEST_CASE("temp_sodr", "[.cmdcall]") { +TEST_CASE("temp_sodr", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -94,7 +94,7 @@ TEST_CASE("temp_sodr", "[.cmdcall]") { } } -TEST_CASE("temp_fpgafl", "[.cmdcall]") { +TEST_CASE("temp_fpgafl", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -109,7 +109,7 @@ TEST_CASE("temp_fpgafl", "[.cmdcall]") { } } -TEST_CASE("temp_fpgafr", "[.cmdcall]") { +TEST_CASE("temp_fpgafr", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -126,7 +126,8 @@ TEST_CASE("temp_fpgafr", "[.cmdcall]") { /* dacs */ -TEST_CASE("Setting and reading back EIGER dacs", "[.cmdcall][.dacs]") { +TEST_CASE("Setting and reading back EIGER dacs", + "[.detectorintegration][.dacs]") { // vsvp, vtr, vrf, vrs, vsvn, vtgstv, vcmp_ll, vcmp_lr, vcal, vcmp_rl, // rxb_rb, rxb_lb, vcmp_rr, vcp, vcn, vis, vthreshold Detector det; @@ -232,7 +233,7 @@ TEST_CASE("Setting and reading back EIGER dacs", "[.cmdcall][.dacs]") { /* Network Configuration (Detector<->Receiver) */ -TEST_CASE("txdelay_left", "[.cmdcall]") { +TEST_CASE("txdelay_left", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -253,7 +254,7 @@ TEST_CASE("txdelay_left", "[.cmdcall]") { } } -TEST_CASE("txdelay_right", "[.cmdcall]") { +TEST_CASE("txdelay_right", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -276,7 +277,7 @@ TEST_CASE("txdelay_right", "[.cmdcall]") { /* Eiger Specific */ -TEST_CASE("subexptime", "[.cmdcall]") { +TEST_CASE("subexptime", "[.detectorintegration]") { Detector det; Caller caller(&det); @@ -297,7 +298,7 @@ TEST_CASE("subexptime", "[.cmdcall]") { } } -TEST_CASE("subdeadtime", "[.cmdcall]") { +TEST_CASE("subdeadtime", "[.detectorintegration]") { Detector det; Caller caller(&det); @@ -318,7 +319,7 @@ TEST_CASE("subdeadtime", "[.cmdcall]") { } } -TEST_CASE("overflow", "[.cmdcall]") { +TEST_CASE("overflow", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -339,7 +340,7 @@ TEST_CASE("overflow", "[.cmdcall]") { } } -TEST_CASE("ratecorr", "[.cmdcall]") { +TEST_CASE("ratecorr", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -373,7 +374,7 @@ TEST_CASE("ratecorr", "[.cmdcall]") { } } -TEST_CASE("interruptsubframe", "[.cmdcall]") { +TEST_CASE("interruptsubframe", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -397,7 +398,7 @@ TEST_CASE("interruptsubframe", "[.cmdcall]") { } } -TEST_CASE("measuredperiod", "[.cmdcall]") { +TEST_CASE("measuredperiod", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -434,7 +435,7 @@ TEST_CASE("measuredperiod", "[.cmdcall]") { } } -TEST_CASE("measuredsubperiod", "[.cmdcall]") { +TEST_CASE("measuredsubperiod", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -474,7 +475,7 @@ TEST_CASE("measuredsubperiod", "[.cmdcall]") { } } -TEST_CASE("activate", "[.cmdcall]") { +TEST_CASE("activate", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -505,7 +506,7 @@ TEST_CASE("activate", "[.cmdcall]") { } } -TEST_CASE("partialreset", "[.cmdcall]") { +TEST_CASE("partialreset", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -527,7 +528,7 @@ TEST_CASE("partialreset", "[.cmdcall]") { } } -TEST_CASE("pulse", "[.cmdcall]") { +TEST_CASE("pulse", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -542,7 +543,7 @@ TEST_CASE("pulse", "[.cmdcall]") { } } -TEST_CASE("pulsenmove", "[.cmdcall]") { +TEST_CASE("pulsenmove", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -557,7 +558,7 @@ TEST_CASE("pulsenmove", "[.cmdcall]") { } } -TEST_CASE("pulsechip", "[.cmdcall]") { +TEST_CASE("pulsechip", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -572,7 +573,7 @@ TEST_CASE("pulsechip", "[.cmdcall]") { } } -TEST_CASE("quad", "[.cmdcall]") { +TEST_CASE("quad", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -588,7 +589,7 @@ TEST_CASE("quad", "[.cmdcall]") { } } -TEST_CASE("datastream", "[.cmdcall]") { +TEST_CASE("datastream", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -628,7 +629,7 @@ TEST_CASE("datastream", "[.cmdcall]") { } } -TEST_CASE("top", "[.cmdcall]") { +TEST_CASE("top", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp index c27ff05d5..0dfc527ac 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp @@ -125,12 +125,6 @@ void test_acquire_binary_file_size(const testFileInfo &file_info, REQUIRE(actual_file_size == expected_file_size); } -void test_frames_caught(const Detector &det, int num_frames_to_acquire) { - auto frames_caught = det.getFramesCaught().tsquash( - "Inconsistent number of frames caught")[0]; - REQUIRE(frames_caught == num_frames_to_acquire); -} - void test_acquire_with_receiver(Caller &caller, const Detector &det) { REQUIRE_NOTHROW(caller.call("rx_start", {}, -1, PUT)); REQUIRE_NOTHROW(caller.call("start", {}, -1, PUT)); @@ -172,9 +166,12 @@ void create_files_for_acquire( // acquire and get num frames caught REQUIRE_NOTHROW(test_acquire_with_receiver(caller, det)); - auto frames_caught = det.getFramesCaught().tsquash( - "Inconsistent number of frames caught")[0]; - REQUIRE(frames_caught == num_frames); + // TODO: maybe there should not be REQUIRE statements in void function at + // all, but traceback should be handled + { + auto frames_caught = det.getFramesCaught()[0][0]; + REQUIRE(frames_caught == num_frames); + } // hdf5 #ifdef HDF5C @@ -184,9 +181,10 @@ void create_files_for_acquire( // acquire and get num frames caught test_acquire_with_receiver(caller, det); - frames_caught = det.getFramesCaught().tsquash( - "Inconsistent number of frames caught")[0]; - REQUIRE(frames_caught == num_frames); + { + auto frames_caught = det.getFramesCaught()[0][0]; + REQUIRE(frames_caught == num_frames); + } #endif // restore previous state diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-global.h b/slsDetectorSoftware/tests/Caller/test-Caller-global.h index f93fc3cdd..3042b91cd 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-global.h +++ b/slsDetectorSoftware/tests/Caller/test-Caller-global.h @@ -88,8 +88,6 @@ void test_acquire_binary_file_size(const testFileInfo &file_info, uint64_t num_frames_to_acquire, uint64_t expected_image_size); -void test_frames_caught(const Detector &det, int num_frames_to_acquire); - void test_acquire_with_receiver(Caller &caller, const Detector &det); void create_files_for_acquire( diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-gotthard2.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-gotthard2.cpp index 54bba5ce2..b5264f5b7 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-gotthard2.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-gotthard2.cpp @@ -18,7 +18,7 @@ using test::GET; using test::PUT; // time specific measurements for gotthard2 -TEST_CASE("timegotthard2", "[.cmdcall]") { +TEST_CASE("timegotthard2", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -106,7 +106,8 @@ TEST_CASE("timegotthard2", "[.cmdcall]") { } /* dacs */ -TEST_CASE("Setting and reading back GOTTHARD2 dacs", "[.cmdcall][.dacs]") { +TEST_CASE("Setting and reading back GOTTHARD2 dacs", + "[.detectorintegration][.dacs]") { // vref_h_adc, vb_comp_fe, vb_comp_adc, vcom_cds, // vref_restore, vb_opa_1st, vref_comp_fe, vcom_adc1, // vref_prech, vref_l_adc, vref_cds, vb_cs, @@ -215,7 +216,7 @@ TEST_CASE("Setting and reading back GOTTHARD2 dacs", "[.cmdcall][.dacs]") { /* on chip dacs */ -TEST_CASE("vchip_comp_fe", "[.cmdcall][.onchipdacs]") { +TEST_CASE("vchip_comp_fe", "[.detectorintegration][.onchipdacs]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -228,7 +229,7 @@ TEST_CASE("vchip_comp_fe", "[.cmdcall][.onchipdacs]") { } } -TEST_CASE("vchip_opa_1st", "[.cmdcall][.onchipdacs]") { +TEST_CASE("vchip_opa_1st", "[.detectorintegration][.onchipdacs]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -241,7 +242,7 @@ TEST_CASE("vchip_opa_1st", "[.cmdcall][.onchipdacs]") { } } -TEST_CASE("vchip_opa_fd", "[.cmdcall][.onchipdacs]") { +TEST_CASE("vchip_opa_fd", "[.detectorintegration][.onchipdacs]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -254,7 +255,7 @@ TEST_CASE("vchip_opa_fd", "[.cmdcall][.onchipdacs]") { } } -TEST_CASE("vchip_comp_adc", "[.cmdcall][.onchipdacs]") { +TEST_CASE("vchip_comp_adc", "[.detectorintegration][.onchipdacs]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -267,7 +268,7 @@ TEST_CASE("vchip_comp_adc", "[.cmdcall][.onchipdacs]") { } } -TEST_CASE("vchip_ref_comp_fe", "[.cmdcall][.onchipdacs]") { +TEST_CASE("vchip_ref_comp_fe", "[.detectorintegration][.onchipdacs]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -281,7 +282,7 @@ TEST_CASE("vchip_ref_comp_fe", "[.cmdcall][.onchipdacs]") { } } -TEST_CASE("vchip_cs", "[.cmdcall][.onchipdacs]") { +TEST_CASE("vchip_cs", "[.detectorintegration][.onchipdacs]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -296,7 +297,7 @@ TEST_CASE("vchip_cs", "[.cmdcall][.onchipdacs]") { /* Gotthard2 Specific */ -TEST_CASE("bursts", "[.cmdcall]") { +TEST_CASE("bursts", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -370,7 +371,7 @@ TEST_CASE("bursts", "[.cmdcall]") { } } -TEST_CASE("burstperiod", "[.cmdcall]") { +TEST_CASE("burstperiod", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -392,7 +393,7 @@ TEST_CASE("burstperiod", "[.cmdcall]") { } } -TEST_CASE("burstsl", "[.cmdcall]") { +TEST_CASE("burstsl", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -403,7 +404,7 @@ TEST_CASE("burstsl", "[.cmdcall]") { } } -TEST_CASE("inj_ch", "[.cmdcall]") { +TEST_CASE("inj_ch", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -428,7 +429,7 @@ TEST_CASE("inj_ch", "[.cmdcall]") { } } -TEST_CASE("vetophoton", "[.cmdcall]") { +TEST_CASE("vetophoton", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -450,7 +451,7 @@ TEST_CASE("vetophoton", "[.cmdcall]") { } } -TEST_CASE("vetoref", "[.cmdcall]") { +TEST_CASE("vetoref", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -465,7 +466,7 @@ TEST_CASE("vetoref", "[.cmdcall]") { } } -TEST_CASE("vetofile", "[.cmdcall]") { +TEST_CASE("vetofile", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -479,7 +480,7 @@ TEST_CASE("vetofile", "[.cmdcall]") { } } -TEST_CASE("burstmode", "[.cmdcall]") { +TEST_CASE("burstmode", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -510,7 +511,7 @@ TEST_CASE("burstmode", "[.cmdcall]") { } } -TEST_CASE("cdsgain", "[.cmdcall]") { +TEST_CASE("cdsgain", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -540,7 +541,7 @@ TEST_CASE("cdsgain", "[.cmdcall]") { } } -TEST_CASE("timingsource", "[.cmdcall]") { +TEST_CASE("timingsource", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -570,7 +571,7 @@ TEST_CASE("timingsource", "[.cmdcall]") { } } -TEST_CASE("veto", "[.cmdcall]") { +TEST_CASE("veto", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -600,7 +601,7 @@ TEST_CASE("veto", "[.cmdcall]") { } } -TEST_CASE("vetostream", "[.cmdcall]") { +TEST_CASE("vetostream", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -646,7 +647,7 @@ TEST_CASE("vetostream", "[.cmdcall]") { REQUIRE_THROWS(caller.call("vetostream", {"dfgd"}, -1, GET)); } -TEST_CASE("vetoalg", "[.cmdcall]") { +TEST_CASE("vetoalg", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -702,7 +703,7 @@ TEST_CASE("vetoalg", "[.cmdcall]") { REQUIRE_THROWS(caller.call("vetoalg", {"dfgd"}, -1, GET)); } -TEST_CASE("confadc", "[.cmdcall]") { +TEST_CASE("confadc", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-jungfrau.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-jungfrau.cpp index 4563cd32f..02334f40f 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-jungfrau.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-jungfrau.cpp @@ -17,7 +17,8 @@ using test::PUT; /* dacs */ -TEST_CASE("Setting and reading back Jungfrau dacs", "[.cmdcall][.dacs]") { +TEST_CASE("Setting and reading back Jungfrau dacs", + "[.detectorintegration][.dacs]") { // vb_comp, vdd_prot, vin_com, vref_prech, vb_pixbuf, vb_ds, vref_ds, // vref_comp Detector det; @@ -95,7 +96,7 @@ TEST_CASE("Setting and reading back Jungfrau dacs", "[.cmdcall][.dacs]") { /* Network Configuration (Detector<->Receiver) */ -TEST_CASE("selinterface", "[.cmdcall]") { +TEST_CASE("selinterface", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -126,7 +127,7 @@ TEST_CASE("selinterface", "[.cmdcall]") { /* Jungfrau/moench Specific */ -TEST_CASE("temp_threshold", "[.cmdcall]") { +TEST_CASE("temp_threshold", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -156,7 +157,7 @@ TEST_CASE("temp_threshold", "[.cmdcall]") { } } -TEST_CASE("chipversion", "[.cmdcall]") { +TEST_CASE("chipversion", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -168,7 +169,7 @@ TEST_CASE("chipversion", "[.cmdcall]") { REQUIRE_THROWS(caller.call("chipversion", {"0"}, -1, PUT)); } -TEST_CASE("temp_control", "[.cmdcall]") { +TEST_CASE("temp_control", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -198,7 +199,7 @@ TEST_CASE("temp_control", "[.cmdcall]") { } } -TEST_CASE("temp_event", "[.cmdcall]") { +TEST_CASE("temp_event", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -219,7 +220,7 @@ TEST_CASE("temp_event", "[.cmdcall]") { } } -TEST_CASE("autocompdisable", "[.cmdcall]") { +TEST_CASE("autocompdisable", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -249,7 +250,7 @@ TEST_CASE("autocompdisable", "[.cmdcall]") { } } -TEST_CASE("compdisabletime", "[.cmdcall]") { +TEST_CASE("compdisabletime", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -279,7 +280,7 @@ TEST_CASE("compdisabletime", "[.cmdcall]") { } } -TEST_CASE("extrastoragecells", "[.cmdcall]") { +TEST_CASE("extrastoragecells", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -322,7 +323,7 @@ TEST_CASE("extrastoragecells", "[.cmdcall]") { } } -TEST_CASE("storagecell_start", "[.cmdcall]") { +TEST_CASE("storagecell_start", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -367,7 +368,7 @@ TEST_CASE("storagecell_start", "[.cmdcall]") { } } -TEST_CASE("storagecell_delay", "[.cmdcall]") { +TEST_CASE("storagecell_delay", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -408,7 +409,7 @@ TEST_CASE("storagecell_delay", "[.cmdcall]") { } } -TEST_CASE("gainmode", "[.cmdcall]") { +TEST_CASE("gainmode", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -457,7 +458,7 @@ TEST_CASE("gainmode", "[.cmdcall]") { } } -TEST_CASE("filtercells", "[.cmdcall]") { +TEST_CASE("filtercells", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -501,7 +502,7 @@ TEST_CASE("filtercells", "[.cmdcall]") { REQUIRE_THROWS(caller.call("filtercells", {"0"}, -1, PUT)); } } -TEST_CASE("pedestalmode", "[.cmdcall]") { +TEST_CASE("pedestalmode", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -657,7 +658,7 @@ TEST_CASE("pedestalmode", "[.cmdcall]") { } } -TEST_CASE("timing_info_decoder", "[.cmdcall]") { +TEST_CASE("timing_info_decoder", "[.detectorintegration]") { Detector det; Caller caller(&det); if (det.getDetectorType().squash() == defs::JUNGFRAU && @@ -686,7 +687,7 @@ TEST_CASE("timing_info_decoder", "[.cmdcall]") { } } -TEST_CASE("collectionmode", "[.cmdcall]") { +TEST_CASE("collectionmode", "[.detectorintegration]") { Detector det; Caller caller(&det); if (det.getDetectorType().squash() == defs::JUNGFRAU) { @@ -714,7 +715,7 @@ TEST_CASE("collectionmode", "[.cmdcall]") { } } -TEST_CASE("sync", "[.cmdcall]") { +TEST_CASE("sync", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp index bf4cf9bac..e2ef3c9d0 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp @@ -1032,7 +1032,9 @@ void open_hdf5_file(const std::string &file_path) { } #endif -TEST_CASE("check_master_file_attributes", "[.cmdcall][.cmdacquire][.cmdattr]") { +TEST_CASE( + "check_master_file_attributes", + "[.detectorintegration][.cmdacquire][.cmdattr][.disable_check_data_file]") { Detector det; Caller caller(&det); @@ -1041,8 +1043,8 @@ TEST_CASE("check_master_file_attributes", "[.cmdcall][.cmdacquire][.cmdattr]") { int64_t num_frames = 1; switch (det_type) { - case defs::EIGER: case defs::JUNGFRAU: + case defs::EIGER: case defs::MOENCH: case defs::MYTHEN3: case defs::GOTTHARD2: diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-moench.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-moench.cpp index 56353f44b..17ee3be1a 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-moench.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-moench.cpp @@ -17,7 +17,8 @@ using test::PUT; /* dacs */ -TEST_CASE("Setting and reading back moench dacs", "[.cmdcall][.dacs]") { +TEST_CASE("Setting and reading back moench dacs", + "[.detectorintegration][.dacs]") { // vbp_colbuf, vipre, vin_cm, vb_sda, vcasc_sfp, vout_cm, vipre_cds, // ibias_sfp Detector det; diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-mythen3.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-mythen3.cpp index 16295a3e1..602d0b2db 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-mythen3.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-mythen3.cpp @@ -19,7 +19,8 @@ using test::PUT; /* dacs */ -TEST_CASE("Setting and reading back MYTHEN3 dacs", "[.cmdcall][.dacs]") { +TEST_CASE("Setting and reading back MYTHEN3 dacs", + "[.detectorintegration][.dacs]") { // vcassh, vth2, vshaper, vshaperneg, vipre_out, vth3, vth1, // vicin, vcas, vpreamp, vpl, vipre, viinsh, vph, vtrim, vdcsh, @@ -184,7 +185,7 @@ TEST_CASE("Setting and reading back MYTHEN3 dacs", "[.cmdcall][.dacs]") { /* acquisition */ -TEST_CASE("readout", "[.cmdcall]") { +TEST_CASE("readout", "[.detectorintegration]") { Detector det; Caller caller(&det); // PUT only command @@ -201,7 +202,7 @@ TEST_CASE("readout", "[.cmdcall]") { /* Mythen3 Specific */ -TEST_CASE("counters", "[.cmdcall]") { +TEST_CASE("counters", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -233,7 +234,7 @@ TEST_CASE("counters", "[.cmdcall]") { } } -TEST_CASE("gates", "[.cmdcall]") { +TEST_CASE("gates", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -263,7 +264,7 @@ TEST_CASE("gates", "[.cmdcall]") { } } -TEST_CASE("exptime1", "[.cmdcall]") { +TEST_CASE("exptime1", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -292,7 +293,7 @@ TEST_CASE("exptime1", "[.cmdcall]") { } } -TEST_CASE("exptime2", "[.cmdcall]") { +TEST_CASE("exptime2", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -321,7 +322,7 @@ TEST_CASE("exptime2", "[.cmdcall]") { } } -TEST_CASE("exptime3", "[.cmdcall]") { +TEST_CASE("exptime3", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -350,7 +351,7 @@ TEST_CASE("exptime3", "[.cmdcall]") { } } -TEST_CASE("gatedelay", "[.cmdcall]") { +TEST_CASE("gatedelay", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -386,7 +387,7 @@ TEST_CASE("gatedelay", "[.cmdcall]") { } } -TEST_CASE("gatedelay1", "[.cmdcall]") { +TEST_CASE("gatedelay1", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -415,7 +416,7 @@ TEST_CASE("gatedelay1", "[.cmdcall]") { } } -TEST_CASE("gatedelay2", "[.cmdcall]") { +TEST_CASE("gatedelay2", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -444,7 +445,7 @@ TEST_CASE("gatedelay2", "[.cmdcall]") { } } -TEST_CASE("gatedelay3", "[.cmdcall]") { +TEST_CASE("gatedelay3", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -473,7 +474,7 @@ TEST_CASE("gatedelay3", "[.cmdcall]") { } } -TEST_CASE("polarity", "[.cmdcall]") { +TEST_CASE("polarity", "[.detectorintegration]") { Detector det; Caller caller(&det); if (det.getDetectorType().squash() == defs::MYTHEN3) { @@ -501,7 +502,7 @@ TEST_CASE("polarity", "[.cmdcall]") { } } -TEST_CASE("interpolation", "[.cmdcall]") { +TEST_CASE("interpolation", "[.detectorintegration]") { Detector det; Caller caller(&det); if (det.getDetectorType().squash() == defs::MYTHEN3) { @@ -555,7 +556,7 @@ TEST_CASE("interpolation", "[.cmdcall]") { } } -TEST_CASE("pumpprobe", "[.cmdcall]") { +TEST_CASE("pumpprobe", "[.detectorintegration]") { Detector det; Caller caller(&det); if (det.getDetectorType().squash() == defs::MYTHEN3) { @@ -632,7 +633,7 @@ TEST_CASE("pumpprobe", "[.cmdcall]") { } } -TEST_CASE("apulse", "[.cmdcall]") { +TEST_CASE("apulse", "[.detectorintegration]") { Detector det; Caller caller(&det); if (det.getDetectorType().squash() == defs::MYTHEN3) { @@ -660,7 +661,7 @@ TEST_CASE("apulse", "[.cmdcall]") { } } -TEST_CASE("dpulse", "[.cmdcall]") { +TEST_CASE("dpulse", "[.detectorintegration]") { Detector det; Caller caller(&det); if (det.getDetectorType().squash() == defs::MYTHEN3) { diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-pattern.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-pattern.cpp index 60dafd4f4..cd49c5ae4 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-pattern.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-pattern.cpp @@ -19,7 +19,7 @@ using test::PUT; /* Pattern */ -TEST_CASE("patfname", "[.cmdcall]") { +TEST_CASE("patfname", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -32,7 +32,7 @@ TEST_CASE("patfname", "[.cmdcall]") { } } -TEST_CASE("pattern", "[.cmdcall]") { +TEST_CASE("pattern", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -45,7 +45,7 @@ TEST_CASE("pattern", "[.cmdcall]") { } } -TEST_CASE("savepattern", "[.cmdcall]") { +TEST_CASE("savepattern", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -63,7 +63,7 @@ TEST_CASE("savepattern", "[.cmdcall]") { } } -TEST_CASE("defaultpattern", "[.cmdcall]") { +TEST_CASE("defaultpattern", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -76,7 +76,7 @@ TEST_CASE("defaultpattern", "[.cmdcall]") { } } -TEST_CASE("patioctrl", "[.cmdcall]") { +TEST_CASE("patioctrl", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -107,7 +107,7 @@ TEST_CASE("patioctrl", "[.cmdcall]") { } } -TEST_CASE("patword", "[.cmdcall]") { +TEST_CASE("patword", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -149,7 +149,7 @@ TEST_CASE("patword", "[.cmdcall]") { } } -TEST_CASE("patlimits", "[.cmdcall]") { +TEST_CASE("patlimits", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -176,7 +176,7 @@ TEST_CASE("patlimits", "[.cmdcall]") { } } -TEST_CASE("patloop", "[.cmdcall]") { +TEST_CASE("patloop", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -225,7 +225,7 @@ TEST_CASE("patloop", "[.cmdcall]") { } } -TEST_CASE("patnloop", "[.cmdcall]") { +TEST_CASE("patnloop", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -271,7 +271,7 @@ TEST_CASE("patnloop", "[.cmdcall]") { } } -TEST_CASE("patwait", "[.cmdcall]") { +TEST_CASE("patwait", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -317,7 +317,7 @@ TEST_CASE("patwait", "[.cmdcall]") { } } -TEST_CASE("patwaittime", "[.cmdcall]") { +TEST_CASE("patwaittime", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -379,7 +379,7 @@ TEST_CASE("patwaittime", "[.cmdcall]") { } } -TEST_CASE("patmask", "[.cmdcall]") { +TEST_CASE("patmask", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -405,7 +405,7 @@ TEST_CASE("patmask", "[.cmdcall]") { } } -TEST_CASE("patsetbit", "[.cmdcall]") { +TEST_CASE("patsetbit", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -431,7 +431,7 @@ TEST_CASE("patsetbit", "[.cmdcall]") { } } -TEST_CASE("patternstart", "[.cmdcall]") { +TEST_CASE("patternstart", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("patternstart", {}, -1, GET)); diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-rx-running.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-rx-running.cpp index 844df6419..dc736e27a 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-rx-running.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-rx-running.cpp @@ -12,7 +12,7 @@ namespace sls { using test::PUT; TEST_CASE("Ctb and xilinx - cant put if receiver is not idle", - "[.cmdcall][.rx]") { + "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); @@ -60,7 +60,8 @@ TEST_CASE("Ctb and xilinx - cant put if receiver is not idle", } } -TEST_CASE("adcenable - cant put if receiver is not idle", "[.cmdcall][.rx]") { +TEST_CASE("adcenable - cant put if receiver is not idle", + "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); @@ -83,7 +84,8 @@ TEST_CASE("adcenable - cant put if receiver is not idle", "[.cmdcall][.rx]") { } } -TEST_CASE("bursts - cant put if receiver is not idle", "[.cmdcall][.rx]") { +TEST_CASE("bursts - cant put if receiver is not idle", + "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); @@ -105,7 +107,8 @@ TEST_CASE("bursts - cant put if receiver is not idle", "[.cmdcall][.rx]") { } } -TEST_CASE("counters - cant put if receiver is not idle", "[.cmdcall][.rx]") { +TEST_CASE("counters - cant put if receiver is not idle", + "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); @@ -129,30 +132,30 @@ TEST_CASE("counters - cant put if receiver is not idle", "[.cmdcall][.rx]") { } TEST_CASE("numinterfaces - cant put if receiver is not idle", - "[.cmdcall][.rx]") { + "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); if (det_type == defs::JUNGFRAU || det_type == defs::MOENCH) { - auto prev_numinterfaces = det.getNumberofUDPInterfaces(); + auto prev_numinterfaces = det.getNumberofUDPInterfaces().tsquash( + "Number of UDP Interfaces is not consistent among modules"); // start receiver REQUIRE_NOTHROW(caller.call("rx_start", {}, -1, PUT)); - REQUIRE_THROWS(caller.call("numinterafaces", {"2"}, -1, PUT)); + REQUIRE_THROWS(caller.call("numinterfaces", {"2"}, -1, PUT)); // stop receiver REQUIRE_NOTHROW(caller.call("rx_stop", {}, -1, PUT)); - for (int i = 0; i != det.size(); ++i) { - det.setNumberofUDPInterfaces(prev_numinterfaces[i], {i}); - } + det.setNumberofUDPInterfaces(prev_numinterfaces); } } -TEST_CASE("dr - cant put if receiver is not idle", "[.cmdcall][.rx]") { +TEST_CASE("dr - cant put if receiver is not idle", + "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); @@ -174,7 +177,8 @@ TEST_CASE("dr - cant put if receiver is not idle", "[.cmdcall][.rx]") { } } -TEST_CASE("tengiga - cant put if receiver is not idle", "[.cmdcall][.rx]") { +TEST_CASE("tengiga - cant put if receiver is not idle", + "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); @@ -198,7 +202,8 @@ TEST_CASE("tengiga - cant put if receiver is not idle", "[.cmdcall][.rx]") { } } -TEST_CASE("general - cant put if receiver is not idle", "[.cmdcall][.rx]") { +TEST_CASE("general - cant put if receiver is not idle", + "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp index 74fa821d8..f00c239e3 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp @@ -25,7 +25,7 @@ python/scripts/list_tested_cmd.py to check if all commands are covered /* configuration */ -TEST_CASE("rx_version", "[.cmdcall][.rx]") { +TEST_CASE("rx_version", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); std::ostringstream oss; @@ -39,7 +39,7 @@ TEST_CASE("rx_version", "[.cmdcall][.rx]") { } /* acquisition */ -TEST_CASE("rx_start", "[.cmdcall][.rx]") { +TEST_CASE("rx_start", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); det.setFileWrite(false); // avoid writing or error on file creation @@ -57,7 +57,7 @@ TEST_CASE("rx_start", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_stop", "[.cmdcall][.rx]") { +TEST_CASE("rx_stop", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); // PUT only command @@ -74,7 +74,7 @@ TEST_CASE("rx_stop", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_status", "[.cmdcall][.rx]") { +TEST_CASE("rx_status", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); det.setFileWrite(false); // avoid writing or error on file creation @@ -92,7 +92,7 @@ TEST_CASE("rx_status", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_framescaught", "[.cmdcall][.rx]") { +TEST_CASE("rx_framescaught", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); // This ensures 0 caught frames @@ -126,7 +126,7 @@ TEST_CASE("rx_framescaught", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_missingpackets", "[.cmdcall][.rx]") { +TEST_CASE("rx_missingpackets", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getFileWrite(); @@ -171,7 +171,7 @@ TEST_CASE("rx_missingpackets", "[.cmdcall][.rx]") { det.setNumberOfFrames(prev_frames); } -TEST_CASE("rx_frameindex", "[.cmdcall][.rx]") { +TEST_CASE("rx_frameindex", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); caller.call("rx_frameindex", {}, -1, GET); @@ -182,7 +182,7 @@ TEST_CASE("rx_frameindex", "[.cmdcall][.rx]") { /* Network Configuration (Detector<->Receiver) */ -TEST_CASE("rx_printconfig", "[.cmdcall][.rx]") { +TEST_CASE("rx_printconfig", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("rx_printconfig", {}, -1, GET)); @@ -190,7 +190,7 @@ TEST_CASE("rx_printconfig", "[.cmdcall][.rx]") { /* Receiver Config */ -TEST_CASE("rx_hostname", "[.cmdcall][.rx]") { +TEST_CASE("rx_hostname", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxHostname(); @@ -222,7 +222,7 @@ TEST_CASE("rx_hostname", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_tcpport", "[.cmdcall][.rx]") { +TEST_CASE("rx_tcpport", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxPort(); @@ -261,7 +261,7 @@ TEST_CASE("rx_tcpport", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_fifodepth", "[.cmdcall][.rx]") { +TEST_CASE("rx_fifodepth", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxFifoDepth(); @@ -285,7 +285,7 @@ TEST_CASE("rx_fifodepth", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_silent", "[.cmdcall][.rx]") { +TEST_CASE("rx_silent", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxSilentMode(); @@ -309,7 +309,7 @@ TEST_CASE("rx_silent", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_discardpolicy", "[.cmdcall][.rx]") { +TEST_CASE("rx_discardpolicy", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxFrameDiscardPolicy(); @@ -338,7 +338,7 @@ TEST_CASE("rx_discardpolicy", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_padding", "[.cmdcall][.rx]") { +TEST_CASE("rx_padding", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getPartialFramesPadding(); @@ -362,7 +362,7 @@ TEST_CASE("rx_padding", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_udpsocksize", "[.cmdcall][.rx]") { +TEST_CASE("rx_udpsocksize", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); int64_t prev_val = det.getRxUDPSocketBufferSize().tsquash( @@ -382,7 +382,7 @@ TEST_CASE("rx_udpsocksize", "[.cmdcall][.rx]") { det.setRxUDPSocketBufferSize(prev_val); } -TEST_CASE("rx_realudpsocksize", "[.cmdcall][.rx]") { +TEST_CASE("rx_realudpsocksize", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); uint64_t val = 0; @@ -401,7 +401,7 @@ TEST_CASE("rx_realudpsocksize", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_lock", "[.cmdcall][.rx]") { +TEST_CASE("rx_lock", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxLock(); @@ -425,7 +425,7 @@ TEST_CASE("rx_lock", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_lastclient", "[.cmdcall][.rx]") { +TEST_CASE("rx_lastclient", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); std::ostringstream oss; @@ -435,14 +435,14 @@ TEST_CASE("rx_lastclient", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_threads", "[.cmdcall][.rx]") { +TEST_CASE("rx_threads", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); std::ostringstream oss; REQUIRE_NOTHROW(caller.call("rx_threads", {}, -1, GET, oss)); } -TEST_CASE("rx_arping", "[.cmdcall][.rx]") { +TEST_CASE("rx_arping", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxArping(); @@ -467,7 +467,7 @@ TEST_CASE("rx_arping", "[.cmdcall][.rx]") { } } } -TEST_CASE("rx_roi", "[.cmdcall]") { +TEST_CASE("rx_roi", "[.detectorintegration][.disable_check_data_file]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -782,7 +782,7 @@ TEST_CASE("rx_roi", "[.cmdcall]") { } } -TEST_CASE("rx_clearroi", "[.cmdcall]") { +TEST_CASE("rx_clearroi", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -808,7 +808,7 @@ TEST_CASE("rx_clearroi", "[.cmdcall]") { /* File */ -TEST_CASE("fformat", "[.cmdcall]") { +TEST_CASE("fformat", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getFileFormat(); @@ -827,7 +827,7 @@ TEST_CASE("fformat", "[.cmdcall]") { } } -TEST_CASE("fpath", "[.cmdcall]") { +TEST_CASE("fpath", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getFilePath(); @@ -849,7 +849,7 @@ TEST_CASE("fpath", "[.cmdcall]") { } } -TEST_CASE("fname", "[.cmdcall]") { +TEST_CASE("fname", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getFileNamePrefix(); @@ -876,7 +876,7 @@ TEST_CASE("fname", "[.cmdcall]") { } } -TEST_CASE("findex", "[.cmdcall]") { +TEST_CASE("findex", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getAcquisitionIndex(); @@ -900,7 +900,7 @@ TEST_CASE("findex", "[.cmdcall]") { } } -TEST_CASE("fwrite", "[.cmdcall]") { +TEST_CASE("fwrite", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getFileWrite(); @@ -924,7 +924,7 @@ TEST_CASE("fwrite", "[.cmdcall]") { } } -TEST_CASE("fmaster", "[.cmdcall]") { +TEST_CASE("fmaster", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getMasterFileWrite(); @@ -946,7 +946,7 @@ TEST_CASE("fmaster", "[.cmdcall]") { det.setMasterFileWrite(prev_val); } -TEST_CASE("foverwrite", "[.cmdcall]") { +TEST_CASE("foverwrite", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getFileOverWrite(); @@ -970,7 +970,7 @@ TEST_CASE("foverwrite", "[.cmdcall]") { } } -TEST_CASE("rx_framesperfile", "[.cmdcall][.rx]") { +TEST_CASE("rx_framesperfile", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getFramesPerFile(); @@ -1001,7 +1001,7 @@ TEST_CASE("rx_framesperfile", "[.cmdcall][.rx]") { /* ZMQ Streaming Parameters (Receiver<->Client) */ -TEST_CASE("rx_zmqstream", "[.cmdcall][.rx]") { +TEST_CASE("rx_zmqstream", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxZmqDataStream(); @@ -1027,7 +1027,7 @@ TEST_CASE("rx_zmqstream", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_zmqfreq", "[.cmdcall][.rx]") { +TEST_CASE("rx_zmqfreq", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxZmqFrequency(); @@ -1051,7 +1051,7 @@ TEST_CASE("rx_zmqfreq", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_zmqstartfnum", "[.cmdcall][.rx]") { +TEST_CASE("rx_zmqstartfnum", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxZmqStartingFrame(); @@ -1075,7 +1075,7 @@ TEST_CASE("rx_zmqstartfnum", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_zmqport", "[.cmdcall][.rx]") { +TEST_CASE("rx_zmqport", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val_zmqport = det.getRxZmqPort(); @@ -1123,7 +1123,7 @@ TEST_CASE("rx_zmqport", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_zmqhwm", "[.cmdcall]") { +TEST_CASE("rx_zmqhwm", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = @@ -1153,7 +1153,7 @@ TEST_CASE("rx_zmqhwm", "[.cmdcall]") { /* CTB Specific */ -TEST_CASE("rx_dbitlist", "[.cmdcall][.rx]") { +TEST_CASE("rx_dbitlist", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1183,7 +1183,7 @@ TEST_CASE("rx_dbitlist", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_dbitoffset", "[.cmdcall][.rx]") { +TEST_CASE("rx_dbitoffset", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1218,7 +1218,7 @@ TEST_CASE("rx_dbitoffset", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_dbitreorder", "[.cmdcall][.rx]") { +TEST_CASE("rx_dbitreorder", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1249,7 +1249,7 @@ TEST_CASE("rx_dbitreorder", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_jsonaddheader", "[.cmdcall][.rx]") { +TEST_CASE("rx_jsonaddheader", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getAdditionalJsonHeader(); @@ -1275,7 +1275,7 @@ TEST_CASE("rx_jsonaddheader", "[.cmdcall][.rx]") { } } -TEST_CASE("rx_jsonpara", "[.cmdcall][.rx]") { +TEST_CASE("rx_jsonpara", "[.detectorintegration][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getAdditionalJsonHeader(); diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-xilinx-chiptestboard.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-xilinx-chiptestboard.cpp index 6133962f4..3849be6fb 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-xilinx-chiptestboard.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-xilinx-chiptestboard.cpp @@ -19,7 +19,7 @@ using test::PUT; /* dacs */ -TEST_CASE("configtransceiver", "[.cmdcall]") { +TEST_CASE("configtransceiver", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); diff --git a/slsDetectorSoftware/tests/Caller/test-Caller.cpp b/slsDetectorSoftware/tests/Caller/test-Caller.cpp index 65fd764fa..93c88713e 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller.cpp @@ -21,7 +21,7 @@ using test::GET; using test::PUT; TEST_CASE("Calling help doesn't throw or cause segfault") { - // Dont add [.cmdcall] tag this should run with normal tests + // Dont add [.detectorintegration] tag this should run with normal tests Caller caller(nullptr); std::ostringstream os; for (std::string cmd : caller.getAllCommands()) @@ -29,7 +29,7 @@ TEST_CASE("Calling help doesn't throw or cause segfault") { caller.call(cmd, {}, -1, slsDetectorDefs::HELP_ACTION, os)); } -TEST_CASE("Unknown command", "[.cmdcall]") { +TEST_CASE("Unknown command", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("vsaevrreavv", {}, -1, PUT)); @@ -37,7 +37,7 @@ TEST_CASE("Unknown command", "[.cmdcall]") { /* configuration */ -TEST_CASE("config", "[.cmdcall]") { +TEST_CASE("config", "[.detectorintegration]") { Detector det; Caller caller(&det); // put only @@ -59,7 +59,11 @@ void test_include_file(const std::string &cmd) { det.getFileWrite().tsquash("File write enable has to be same to test"); { - system("echo -e 'frames 2\nfwrite 1' > /tmp/tempsetup.det "); + std::ofstream f("/tmp/tempsetup.det", std::ios::trunc); + f << "frames 2\n"; + f << "fwrite 1\n"; + } + { std::ostringstream oss; caller.call(cmd, {"/tmp/tempsetup.det"}, -1, PUT, oss); REQUIRE(oss.str() == cmd + " /tmp/tempsetup.det\n"); @@ -69,7 +73,11 @@ void test_include_file(const std::string &cmd) { 1); } { - system("echo -e 'frames 3\nfwrite 0' > /tmp/tempsetup.det "); + std::ofstream f("/tmp/tempsetup.det", std::ios::trunc); + f << "frames 3\n"; + f << "fwrite 0\n"; + } + { std::ostringstream oss; caller.call(cmd, {"/tmp/tempsetup.det"}, -1, PUT, oss); REQUIRE(oss.str() == cmd + " /tmp/tempsetup.det\n"); @@ -82,17 +90,19 @@ void test_include_file(const std::string &cmd) { det.setFileWrite(prev_fwrite); } -TEST_CASE("parameters", "[.cmdcall]") { test_include_file("parameters"); } +TEST_CASE("parameters", "[.detectorintegration]") { + test_include_file("parameters"); +} -TEST_CASE("include", "[.cmdcall]") { test_include_file("include"); } +TEST_CASE("include", "[.detectorintegration]") { test_include_file("include"); } -TEST_CASE("hostname", "[.cmdcall]") { +TEST_CASE("hostname", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("hostname", {}, -1, GET)); } -TEST_CASE("virtual", "[.cmdcall]") { +TEST_CASE("virtual", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("virtual", {}, -1, GET)); @@ -100,56 +110,56 @@ TEST_CASE("virtual", "[.cmdcall]") { REQUIRE_THROWS(caller.call("virtual", {"3", "65534"}, -1, PUT)); } -TEST_CASE("versions", "[.cmdcall]") { +TEST_CASE("versions", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("versions", {}, -1, GET)); REQUIRE_THROWS(caller.call("versions", {"0"}, -1, PUT)); } -TEST_CASE("packageversion", "[.cmdcall]") { +TEST_CASE("packageversion", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("packageversion", {}, -1, GET)); REQUIRE_THROWS(caller.call("packageversion", {"0"}, -1, PUT)); } -TEST_CASE("clientversion", "[.cmdcall]") { +TEST_CASE("clientversion", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("clientversion", {}, -1, GET)); REQUIRE_THROWS(caller.call("clientversion", {"0"}, -1, PUT)); } -TEST_CASE("firmwareversion", "[.cmdcall]") { +TEST_CASE("firmwareversion", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("firmwareversion", {}, -1, GET)); REQUIRE_THROWS(caller.call("firmwareversion", {"0"}, -1, PUT)); } -TEST_CASE("detectorserverversion", "[.cmdcall]") { +TEST_CASE("detectorserverversion", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("detectorserverversion", {}, -1, GET)); REQUIRE_THROWS(caller.call("detectorserverversion", {"0"}, -1, PUT)); } -TEST_CASE("hardwareversion", "[.cmdcall]") { +TEST_CASE("hardwareversion", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("hardwareversion", {}, -1, GET)); REQUIRE_THROWS(caller.call("hardwareversion", {"0"}, -1, PUT)); } -TEST_CASE("kernelversion", "[.cmdcall]") { +TEST_CASE("kernelversion", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("kernelversion", {}, -1, GET)); REQUIRE_THROWS(caller.call("kernelversion", {"0"}, -1, PUT)); } -TEST_CASE("serialnumber", "[.cmdcall]") { +TEST_CASE("serialnumber", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -160,7 +170,7 @@ TEST_CASE("serialnumber", "[.cmdcall]") { } } -TEST_CASE("moduleid", "[.cmdcall]") { +TEST_CASE("moduleid", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -173,7 +183,7 @@ TEST_CASE("moduleid", "[.cmdcall]") { } } -TEST_CASE("type", "[.cmdcall]") { +TEST_CASE("type", "[.detectorintegration]") { Detector det; Caller caller(&det); auto dt = det.getDetectorType().squash(); @@ -185,13 +195,13 @@ TEST_CASE("type", "[.cmdcall]") { // REQUIRE(dt == test::type); } -TEST_CASE("detsize", "[.cmdcall]") { +TEST_CASE("detsize", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("detsize", {}, -1, GET)); } -TEST_CASE("settingslist", "[.cmdcall]") { +TEST_CASE("settingslist", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -204,7 +214,7 @@ TEST_CASE("settingslist", "[.cmdcall]") { } } -TEST_CASE("settings", "[.cmdcall]") { +TEST_CASE("settings", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -295,7 +305,7 @@ TEST_CASE("settings", "[.cmdcall]") { } } -TEST_CASE("threshold", "[.cmdcall]") { +TEST_CASE("threshold", "[.detectorintegration]") { Detector det; Caller caller(&det); @@ -374,7 +384,7 @@ TEST_CASE("threshold", "[.cmdcall]") { } } -TEST_CASE("thresholdnotb", "[.cmdcall]") { +TEST_CASE("thresholdnotb", "[.detectorintegration]") { Detector det; Caller caller(&det); @@ -454,7 +464,7 @@ TEST_CASE("thresholdnotb", "[.cmdcall]") { } } -TEST_CASE("settingspath", "[.cmdcall]") { +TEST_CASE("settingspath", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getSettingsPath(); @@ -470,13 +480,13 @@ TEST_CASE("settingspath", "[.cmdcall]") { } } -TEST_CASE("trimbits", "[.cmdcall]") { +TEST_CASE("trimbits", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("trimbits", {}, -1, GET)); } -TEST_CASE("trimval", "[.cmdcall]") { +TEST_CASE("trimval", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -510,7 +520,7 @@ TEST_CASE("trimval", "[.cmdcall]") { } } -TEST_CASE("trimen", "[.cmdcall][.this]") { +TEST_CASE("trimen", "[.detectorintegration][.this]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -532,7 +542,7 @@ TEST_CASE("trimen", "[.cmdcall][.this]") { } } -TEST_CASE("gappixels", "[.cmdcall]") { +TEST_CASE("gappixels", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -582,7 +592,7 @@ TEST_CASE("gappixels", "[.cmdcall]") { } } -TEST_CASE("fliprows", "[.cmdcall]") { +TEST_CASE("fliprows", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -617,7 +627,7 @@ TEST_CASE("fliprows", "[.cmdcall]") { } } -TEST_CASE("master", "[.cmdcall]") { +TEST_CASE("master", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -662,7 +672,7 @@ TEST_CASE("master", "[.cmdcall]") { } } -TEST_CASE("badchannels", "[.cmdcall]") { +TEST_CASE("badchannels", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -732,7 +742,7 @@ TEST_CASE("badchannels", "[.cmdcall]") { } } -TEST_CASE("row", "[.cmdcall]") { +TEST_CASE("row", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getRow()[0]; @@ -755,7 +765,7 @@ TEST_CASE("row", "[.cmdcall]") { det.setRow(prev_val, {0}); } -TEST_CASE("column", "[.cmdcall]") { +TEST_CASE("column", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getColumn()[0]; @@ -782,7 +792,7 @@ TEST_CASE("column", "[.cmdcall]") { // acquire: not testing -TEST_CASE("frames", "[.cmdcall]") { +TEST_CASE("frames", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = @@ -806,7 +816,7 @@ TEST_CASE("frames", "[.cmdcall]") { det.setNumberOfFrames(prev_val); } -TEST_CASE("triggers", "[.cmdcall]") { +TEST_CASE("triggers", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = @@ -830,7 +840,7 @@ TEST_CASE("triggers", "[.cmdcall]") { det.setNumberOfTriggers(prev_val); } -TEST_CASE("exptime", "[.cmdcall][.time]") { +TEST_CASE("exptime", "[.detectorintegration][.time]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -880,7 +890,7 @@ TEST_CASE("exptime", "[.cmdcall][.time]") { det.setExptime(-1, prev_val); } -TEST_CASE("period", "[.cmdcall]") { +TEST_CASE("period", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getPeriod(); @@ -904,7 +914,7 @@ TEST_CASE("period", "[.cmdcall]") { } } -TEST_CASE("delay", "[.cmdcall]") { +TEST_CASE("delay", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -934,7 +944,7 @@ TEST_CASE("delay", "[.cmdcall]") { } } -TEST_CASE("framesl", "[.cmdcall]") { +TEST_CASE("framesl", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -945,7 +955,7 @@ TEST_CASE("framesl", "[.cmdcall]") { } } -TEST_CASE("triggersl", "[.cmdcall]") { +TEST_CASE("triggersl", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -956,7 +966,7 @@ TEST_CASE("triggersl", "[.cmdcall]") { } } -TEST_CASE("delayl", "[.cmdcall]") { +TEST_CASE("delayl", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -970,7 +980,7 @@ TEST_CASE("delayl", "[.cmdcall]") { } } -TEST_CASE("periodl", "[.cmdcall]") { +TEST_CASE("periodl", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -984,7 +994,7 @@ TEST_CASE("periodl", "[.cmdcall]") { } } -TEST_CASE("dr", "[.cmdcall]") { +TEST_CASE("dr", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1026,14 +1036,14 @@ TEST_CASE("dr", "[.cmdcall]") { } } -TEST_CASE("drlist", "[.cmdcall]") { +TEST_CASE("drlist", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("drlist", {}, -1, GET)); REQUIRE_THROWS(caller.call("drlist", {}, -1, PUT)); } -TEST_CASE("timing", "[.cmdcall]") { +TEST_CASE("timing", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1095,14 +1105,14 @@ TEST_CASE("timing", "[.cmdcall]") { } } -TEST_CASE("timinglist", "[.cmdcall]") { +TEST_CASE("timinglist", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("timinglist", {}, -1, GET)); REQUIRE_THROWS(caller.call("timinglist", {}, -1, PUT)); } -TEST_CASE("readoutspeed", "[.cmdcall]") { +TEST_CASE("readoutspeed", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1192,7 +1202,7 @@ TEST_CASE("readoutspeed", "[.cmdcall]") { } } -TEST_CASE("readoutspeedlist", "[.cmdcall]") { +TEST_CASE("readoutspeedlist", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1206,7 +1216,7 @@ TEST_CASE("readoutspeedlist", "[.cmdcall]") { } } -TEST_CASE("adcphase", "[.cmdcall]") { +TEST_CASE("adcphase", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1236,7 +1246,7 @@ TEST_CASE("adcphase", "[.cmdcall]") { } } -TEST_CASE("maxadcphaseshift", "[.cmdcall]") { +TEST_CASE("maxadcphaseshift", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1250,7 +1260,7 @@ TEST_CASE("maxadcphaseshift", "[.cmdcall]") { } } -TEST_CASE("dbitphase", "[.cmdcall]") { +TEST_CASE("dbitphase", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1279,7 +1289,7 @@ TEST_CASE("dbitphase", "[.cmdcall]") { } } -TEST_CASE("maxdbitphaseshift", "[.cmdcall]") { +TEST_CASE("maxdbitphaseshift", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1292,7 +1302,7 @@ TEST_CASE("maxdbitphaseshift", "[.cmdcall]") { } } -TEST_CASE("clkfreq", "[.cmdcall]") { +TEST_CASE("clkfreq", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1312,7 +1322,7 @@ TEST_CASE("clkfreq", "[.cmdcall]") { } } -TEST_CASE("clkphase", "[.cmdcall]") { +TEST_CASE("clkphase", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1362,7 +1372,7 @@ TEST_CASE("clkphase", "[.cmdcall]") { } } -TEST_CASE("clkdiv", "[.cmdcall]") { +TEST_CASE("clkdiv", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1426,7 +1436,7 @@ TEST_CASE("clkdiv", "[.cmdcall]") { } } -TEST_CASE("maxclkphaseshift", "[.cmdcall]") { +TEST_CASE("maxclkphaseshift", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1446,7 +1456,7 @@ TEST_CASE("maxclkphaseshift", "[.cmdcall]") { } } -TEST_CASE("highvoltage", "[.cmdcall]") { +TEST_CASE("highvoltage", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1531,7 +1541,7 @@ TEST_CASE("highvoltage", "[.cmdcall]") { } } -TEST_CASE("powerchip", "[.cmdcall]") { +TEST_CASE("powerchip", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1583,7 +1593,7 @@ TEST_CASE("powerchip", "[.cmdcall]") { } } -TEST_CASE("imagetest", "[.cmdcall]") { +TEST_CASE("imagetest", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1595,7 +1605,7 @@ TEST_CASE("imagetest", "[.cmdcall]") { } } -TEST_CASE("extsig", "[.cmdcall]") { +TEST_CASE("extsig", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1649,7 +1659,7 @@ TEST_CASE("extsig", "[.cmdcall]") { } } -TEST_CASE("parallel", "[.cmdcall]") { +TEST_CASE("parallel", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1680,7 +1690,7 @@ TEST_CASE("parallel", "[.cmdcall]") { } } -TEST_CASE("filterresistor", "[.cmdcall]") { +TEST_CASE("filterresistor", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1724,7 +1734,7 @@ TEST_CASE("filterresistor", "[.cmdcall]") { } } -TEST_CASE("dbitpipeline", "[.cmdcall]") { +TEST_CASE("dbitpipeline", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1774,7 +1784,7 @@ TEST_CASE("dbitpipeline", "[.cmdcall]") { } } -TEST_CASE("readnrows", "[.cmdcall]") { +TEST_CASE("readnrows", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1826,7 +1836,7 @@ TEST_CASE("readnrows", "[.cmdcall]") { } } -TEST_CASE("currentsource", "[.cmdcall]") { +TEST_CASE("currentsource", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1968,21 +1978,21 @@ TEST_CASE("currentsource", "[.cmdcall]") { /** temperature */ -TEST_CASE("templist", "[.cmdcall]") { +TEST_CASE("templist", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("templist", {}, -1, GET)); REQUIRE_THROWS(caller.call("templist", {}, -1, PUT)); } -TEST_CASE("tempvalues", "[.cmdcall]") { +TEST_CASE("tempvalues", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("tempvalues", {}, -1, GET)); REQUIRE_THROWS(caller.call("tempvalues", {}, -1, PUT)); } -TEST_CASE("temp_adc", "[.cmdcall]") { +TEST_CASE("temp_adc", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -1997,7 +2007,7 @@ TEST_CASE("temp_adc", "[.cmdcall]") { } } -TEST_CASE("temp_fpga", "[.cmdcall]") { +TEST_CASE("temp_fpga", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2014,7 +2024,7 @@ TEST_CASE("temp_fpga", "[.cmdcall]") { /* list */ -TEST_CASE("daclist", "[.cmdcall]") { +TEST_CASE("daclist", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2050,14 +2060,14 @@ TEST_CASE("daclist", "[.cmdcall]") { /* dacs */ -TEST_CASE("dacvalues", "[.cmdcall]") { +TEST_CASE("dacvalues", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("dacvalues", {}, -1, GET)); REQUIRE_THROWS(caller.call("dacvalues", {}, -1, PUT)); } -TEST_CASE("defaultdac", "[.cmdcall]") { +TEST_CASE("defaultdac", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2117,7 +2127,7 @@ TEST_CASE("defaultdac", "[.cmdcall]") { } } -TEST_CASE("resetdacs", "[.cmdcall]") { +TEST_CASE("resetdacs", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2142,7 +2152,7 @@ TEST_CASE("resetdacs", "[.cmdcall]") { /* acquisition */ -TEST_CASE("trigger", "[.cmdcall]") { +TEST_CASE("trigger", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("trigger", {}, -1, GET)); @@ -2185,7 +2195,7 @@ TEST_CASE("trigger", "[.cmdcall]") { } } -TEST_CASE("blockingtrigger", "[.cmdcall]") { +TEST_CASE("blockingtrigger", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("blockingtrigger", {}, -1, GET)); @@ -2229,7 +2239,7 @@ TEST_CASE("blockingtrigger", "[.cmdcall]") { } } -TEST_CASE("clearbusy", "[.cmdcall]") { +TEST_CASE("clearbusy", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("clearbusy", {}, -1, PUT)); @@ -2237,7 +2247,7 @@ TEST_CASE("clearbusy", "[.cmdcall]") { REQUIRE_THROWS(caller.call("clearbusy", {}, -1, GET)); } -TEST_CASE("start", "[.cmdcall]") { +TEST_CASE("start", "[.detectorintegration]") { Detector det; Caller caller(&det); // PUT only command @@ -2276,7 +2286,7 @@ TEST_CASE("start", "[.cmdcall]") { det.setNumberOfFrames(prev_frames); } -TEST_CASE("stop", "[.cmdcall]") { +TEST_CASE("stop", "[.detectorintegration]") { Detector det; Caller caller(&det); // PUT only command @@ -2321,7 +2331,7 @@ TEST_CASE("stop", "[.cmdcall]") { det.setNumberOfFrames(prev_frames); } -TEST_CASE("status", "[.cmdcall]") { +TEST_CASE("status", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2360,7 +2370,7 @@ TEST_CASE("status", "[.cmdcall]") { det.setNumberOfFrames(prev_frames); } -TEST_CASE("nextframenumber", "[.cmdcall]") { +TEST_CASE("nextframenumber", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2467,7 +2477,7 @@ TEST_CASE("nextframenumber", "[.cmdcall]") { } } -TEST_CASE("scan", "[.cmdcall]") { +TEST_CASE("scan", "[.detectorintegration]") { Detector det; Caller caller(&det); defs::dacIndex ind = defs::DAC_0; @@ -2593,7 +2603,7 @@ TEST_CASE("scan", "[.cmdcall]") { } } -TEST_CASE("scanerrmsg", "[.cmdcall]") { +TEST_CASE("scanerrmsg", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("scanerrmsg", {}, -1, GET)); @@ -2602,17 +2612,17 @@ TEST_CASE("scanerrmsg", "[.cmdcall]") { /* Network Configuration (Detector<->Receiver) */ -TEST_CASE("numinterfaces", "[.cmdcall]") { +TEST_CASE("numinterfaces", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); if (det_type == defs::JUNGFRAU || det_type == defs::MOENCH) { auto prev_val = det.getNumberofUDPInterfaces().tsquash( "inconsistent numinterfaces to test"); - UdpDestination prev_udp_dest{}; + Result prev_udp_dest; IpAddr prev_src_ip2{}; if (prev_val == 2 && det_type != defs::EIGER) { - prev_udp_dest = det.getDestinationUDPList(0)[0]; + prev_udp_dest = det.getDestinationUDPList(0); prev_src_ip2 = det.getSourceUDPIP2()[0]; } { @@ -2631,8 +2641,10 @@ TEST_CASE("numinterfaces", "[.cmdcall]") { REQUIRE(oss.str() == "numinterfaces 1\n"); } if (prev_val == 2 && det_type != defs::EIGER) { - det.setDestinationUDPList({prev_udp_dest}, 0); - det.setSourceUDPIP2({prev_src_ip2}, {0}); + for (int i = 0; i != det.size(); ++i) { + det.setDestinationUDPList({prev_udp_dest[i]}, {i}); + } + det.setSourceUDPIP2({prev_src_ip2}); } det.setNumberofUDPInterfaces(prev_val); } else if (det_type == defs::EIGER) { @@ -2652,7 +2664,7 @@ TEST_CASE("numinterfaces", "[.cmdcall]") { REQUIRE_THROWS(caller.call("numinterfaces", {"0"}, -1, PUT)); } -TEST_CASE("udp_srcip", "[.cmdcall]") { +TEST_CASE("udp_srcip", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getSourceUDPIP(); @@ -2667,7 +2679,7 @@ TEST_CASE("udp_srcip", "[.cmdcall]") { } } -TEST_CASE("udp_dstlist", "[.cmdcall]") { +TEST_CASE("udp_dstlist", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2683,7 +2695,7 @@ TEST_CASE("udp_dstlist", "[.cmdcall]") { } } -TEST_CASE("udp_numdst", "[.cmdcall]") { +TEST_CASE("udp_numdst", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2696,7 +2708,7 @@ TEST_CASE("udp_numdst", "[.cmdcall]") { } } -TEST_CASE("udp_cleardst", "[.cmdcall]") { +TEST_CASE("udp_cleardst", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("udp_cleardst", {}, -1, GET)); @@ -2704,7 +2716,7 @@ TEST_CASE("udp_cleardst", "[.cmdcall]") { /*REQUIRE_NOTHROW(caller.call("udp_cleardst", {}, -1, PUT));*/ } -TEST_CASE("udp_firstdst", "[.cmdcall]") { +TEST_CASE("udp_firstdst", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2738,13 +2750,13 @@ TEST_CASE("udp_firstdst", "[.cmdcall]") { } } -TEST_CASE("udp_dstip", "[.cmdcall]") { +TEST_CASE("udp_dstip", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("udp_dstip", {"0.0.0.0"}, -1, PUT)); } -TEST_CASE("udp_srcmac", "[.cmdcall]") { +TEST_CASE("udp_srcmac", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getSourceUDPMAC(); @@ -2761,13 +2773,13 @@ TEST_CASE("udp_srcmac", "[.cmdcall]") { } } -TEST_CASE("udp_dstmac", "[.cmdcall]") { +TEST_CASE("udp_dstmac", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("udp_dstmac", {"00:00:00:00:00:00"}, -1, PUT)); } -TEST_CASE("udp_dstport", "[.cmdcall]") { +TEST_CASE("udp_dstport", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getDestinationUDPPort(); @@ -2788,7 +2800,7 @@ TEST_CASE("udp_dstport", "[.cmdcall]") { } } -TEST_CASE("udp_srcip2", "[.cmdcall]") { +TEST_CASE("udp_srcip2", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2810,7 +2822,7 @@ TEST_CASE("udp_srcip2", "[.cmdcall]") { } } -TEST_CASE("udp_dstip2", "[.cmdcall]") { +TEST_CASE("udp_dstip2", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2822,7 +2834,7 @@ TEST_CASE("udp_dstip2", "[.cmdcall]") { } } -TEST_CASE("udp_srcmac2", "[.cmdcall]") { +TEST_CASE("udp_srcmac2", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2846,7 +2858,7 @@ TEST_CASE("udp_srcmac2", "[.cmdcall]") { } } -TEST_CASE("udp_dstmac2", "[.cmdcall]") { +TEST_CASE("udp_dstmac2", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2859,7 +2871,7 @@ TEST_CASE("udp_dstmac2", "[.cmdcall]") { } } -TEST_CASE("udp_dstport2", "[.cmdcall]") { +TEST_CASE("udp_dstport2", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2888,21 +2900,21 @@ TEST_CASE("udp_dstport2", "[.cmdcall]") { } } -TEST_CASE("udp_reconfigure", "[.cmdcall]") { +TEST_CASE("udp_reconfigure", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("udp_reconfigure", {}, -1, GET)); REQUIRE_NOTHROW(caller.call("udp_reconfigure", {}, -1, PUT)); } -TEST_CASE("udp_validate", "[.cmdcall]") { +TEST_CASE("udp_validate", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_THROWS(caller.call("udp_validate", {}, -1, GET)); REQUIRE_NOTHROW(caller.call("udp_validate", {}, -1, PUT)); } -TEST_CASE("tengiga", "[.cmdcall]") { +TEST_CASE("tengiga", "[.detectorintegration]") { Detector det; Caller caller(&det); @@ -2926,7 +2938,7 @@ TEST_CASE("tengiga", "[.cmdcall]") { } } -TEST_CASE("flowcontrol10g", "[.cmdcall]") { +TEST_CASE("flowcontrol10g", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2957,7 +2969,7 @@ TEST_CASE("flowcontrol10g", "[.cmdcall]") { } } -TEST_CASE("txdelay_frame", "[.cmdcall]") { +TEST_CASE("txdelay_frame", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -2985,7 +2997,7 @@ TEST_CASE("txdelay_frame", "[.cmdcall]") { } } -TEST_CASE("txdelay", "[.cmdcall]") { +TEST_CASE("txdelay", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3061,7 +3073,7 @@ TEST_CASE("txdelay", "[.cmdcall]") { /* ZMQ Streaming Parameters (Receiver<->Client) */ -TEST_CASE("zmqport", "[.cmdcall]") { +TEST_CASE("zmqport", "[.detectorintegration]") { Detector det; Caller caller(&det); @@ -3116,7 +3128,7 @@ TEST_CASE("zmqport", "[.cmdcall]") { } } -TEST_CASE("zmqip", "[.cmdcall]") { +TEST_CASE("zmqip", "[.detectorintegration]") { Detector det; Caller caller(&det); std::ostringstream oss1, oss2; @@ -3132,7 +3144,7 @@ TEST_CASE("zmqip", "[.cmdcall]") { } } -TEST_CASE("zmqhwm", "[.cmdcall]") { +TEST_CASE("zmqhwm", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getClientZmqHwm(); @@ -3161,7 +3173,7 @@ TEST_CASE("zmqhwm", "[.cmdcall]") { /* Advanced */ -TEST_CASE("adcpipeline", "[.cmdcall]") { +TEST_CASE("adcpipeline", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3196,7 +3208,7 @@ TEST_CASE("adcpipeline", "[.cmdcall]") { } } -TEST_CASE("programfpga", "[.cmdcall]") { +TEST_CASE("programfpga", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3212,7 +3224,7 @@ TEST_CASE("programfpga", "[.cmdcall]") { } } -TEST_CASE("resetfpga", "[.cmdcall]") { +TEST_CASE("resetfpga", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3228,7 +3240,7 @@ TEST_CASE("resetfpga", "[.cmdcall]") { } } -TEST_CASE("updatekernel", "[.cmdcall]") { +TEST_CASE("updatekernel", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3247,7 +3259,7 @@ TEST_CASE("updatekernel", "[.cmdcall]") { } } -TEST_CASE("rebootcontroller", "[.cmdcall]") { +TEST_CASE("rebootcontroller", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3263,7 +3275,7 @@ TEST_CASE("rebootcontroller", "[.cmdcall]") { } } -TEST_CASE("update", "[.cmdcall]") { +TEST_CASE("update", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3281,7 +3293,7 @@ TEST_CASE("update", "[.cmdcall]") { } } -TEST_CASE("reg", "[.cmdcall][.definecmds]") { +TEST_CASE("reg", "[.detectorintegration][.definecmds]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3319,7 +3331,7 @@ TEST_CASE("reg", "[.cmdcall][.definecmds]") { } } -TEST_CASE("adcreg", "[.cmdcall]") { +TEST_CASE("adcreg", "[.detectorintegration]") { // TODO! what is a safe value to use? Detector det; Caller caller(&det); @@ -3340,7 +3352,7 @@ TEST_CASE("adcreg", "[.cmdcall]") { } } -TEST_CASE("setbit", "[.cmdcall][.definecmds]") { +TEST_CASE("setbit", "[.detectorintegration][.definecmds]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3370,7 +3382,7 @@ TEST_CASE("setbit", "[.cmdcall][.definecmds]") { } } -TEST_CASE("clearbit", "[.cmdcall][.definecmds]") { +TEST_CASE("clearbit", "[.detectorintegration][.definecmds]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3400,7 +3412,7 @@ TEST_CASE("clearbit", "[.cmdcall][.definecmds]") { } } -TEST_CASE("getbit", "[.cmdcall][.definecmds]") { +TEST_CASE("getbit", "[.detectorintegration][.definecmds]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3430,7 +3442,7 @@ TEST_CASE("getbit", "[.cmdcall][.definecmds]") { } } -TEST_CASE("firmwaretest", "[.cmdcall]") { +TEST_CASE("firmwaretest", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3447,7 +3459,7 @@ TEST_CASE("firmwaretest", "[.cmdcall]") { } } -TEST_CASE("bustest", "[.cmdcall]") { +TEST_CASE("bustest", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3464,7 +3476,7 @@ TEST_CASE("bustest", "[.cmdcall]") { } } -TEST_CASE("initialchecks", "[.cmdcall]") { +TEST_CASE("initialchecks", "[.detectorintegration]") { Detector det; Caller caller(&det); auto check = det.getInitialChecks(); @@ -3486,7 +3498,7 @@ TEST_CASE("initialchecks", "[.cmdcall]") { det.setInitialChecks(check); } -TEST_CASE("adcinvert", "[.cmdcall]") { +TEST_CASE("adcinvert", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3514,7 +3526,7 @@ TEST_CASE("adcinvert", "[.cmdcall]") { /* Insignificant */ -TEST_CASE("port", "[.cmdcall]") { +TEST_CASE("port", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getControlPort({0}).squash(); @@ -3538,7 +3550,7 @@ TEST_CASE("port", "[.cmdcall]") { det.setControlPort(prev_val, {0}); } -TEST_CASE("stopport", "[.cmdcall]") { +TEST_CASE("stopport", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getStopPort({0}).squash(); @@ -3562,7 +3574,7 @@ TEST_CASE("stopport", "[.cmdcall]") { det.setStopPort(prev_val, {0}); } -TEST_CASE("lock", "[.cmdcall]") { +TEST_CASE("lock", "[.detectorintegration]") { Detector det; Caller caller(&det); auto prev_val = det.getDetectorLock(); @@ -3586,13 +3598,13 @@ TEST_CASE("lock", "[.cmdcall]") { } } -TEST_CASE("execcommand", "[.cmdcall]") { +TEST_CASE("execcommand", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("execcommand", {"ls *.txt"}, -1, PUT)); } -TEST_CASE("framecounter", "[.cmdcall]") { +TEST_CASE("framecounter", "[.detectorintegration]") { Detector det; Caller caller(&det); auto det_type = det.getDetectorType().squash(); @@ -3610,7 +3622,7 @@ TEST_CASE("framecounter", "[.cmdcall]") { } } -TEST_CASE("runtime", "[.cmdcall]") { +TEST_CASE("runtime", "[.detectorintegration]") { // TODO! can we test this? Detector det; Caller caller(&det); @@ -3628,7 +3640,7 @@ TEST_CASE("runtime", "[.cmdcall]") { } } -TEST_CASE("frametime", "[.cmdcall]") { +TEST_CASE("frametime", "[.detectorintegration]") { // TODO! can we test this? Detector det; Caller caller(&det); @@ -3646,14 +3658,14 @@ TEST_CASE("frametime", "[.cmdcall]") { } } -TEST_CASE("user", "[.cmdcall]") { +TEST_CASE("user", "[.detectorintegration]") { Detector det; Caller caller(&det); // caller only has help. cmdApp takes care of put and get REQUIRE_NOTHROW(caller.call("user", {}, -1, defs::HELP_ACTION)); } -TEST_CASE("sleep", "[.cmdcall]") { +TEST_CASE("sleep", "[.detectorintegration]") { Detector det; Caller caller(&det); REQUIRE_NOTHROW(caller.call("sleep", {"1"}, -1, PUT)); diff --git a/slsReceiverSoftware/src/Implementation.cpp b/slsReceiverSoftware/src/Implementation.cpp index afffb317d..84b248835 100644 --- a/slsReceiverSoftware/src/Implementation.cpp +++ b/slsReceiverSoftware/src/Implementation.cpp @@ -813,7 +813,8 @@ void Implementation::startReadout() { << "waiting for all packets, previousValue:" << previousValue << " totalPacketsReceived: " << totalPacketsReceived; - /* TODO! Need to find optimal time **/ + /* TODO! Need to find optimal time - xilinx and jungfrau need + * more time */ std::this_thread::sleep_for(std::chrono::milliseconds(5)); previousValue = totalPacketsReceived; totalPacketsReceived = 0; diff --git a/slsReceiverSoftware/tests/test-ArrangeDataBasedOnBitList.cpp b/slsReceiverSoftware/tests/test-ArrangeDataBasedOnBitList.cpp index db278658e..54f60d0cb 100644 --- a/slsReceiverSoftware/tests/test-ArrangeDataBasedOnBitList.cpp +++ b/slsReceiverSoftware/tests/test-ArrangeDataBasedOnBitList.cpp @@ -140,7 +140,7 @@ class DataProcessorTestFixture { }; TEST_CASE_METHOD(DataProcessorTestFixture, "Remove Trailing Bits", - "[.dataprocessor][.bitoffset]") { + "[dataprocessor][bitoffset]") { const size_t num_random_offset_bytes = 3; set_random_offset_bytes(num_random_offset_bytes); @@ -170,7 +170,7 @@ TEST_CASE_METHOD(DataProcessorTestFixture, "Remove Trailing Bits", // parametric test tested with num_samples = 5, num_samples = 10, num_samples = // 8 TEST_CASE_METHOD(DataProcessorTestFixture, "Reorder all", - "[.dataprocessor][.reorder]") { + "[dataprocessor][reorder]") { // parameters: num_samples, expected_num_digital_bytes, // expected_digital_part_for_each_bit auto parameters = GENERATE( @@ -219,7 +219,7 @@ TEST_CASE_METHOD(DataProcessorTestFixture, "Reorder all", TEST_CASE_METHOD(DataProcessorTestFixture, "Reorder all and remove trailing bits", - "[.dataprocessor][.reorder]") { + "[dataprocessor][reorder]") { // set number of samples for test fixture -> create data const size_t num_random_offset_bytes = 3; @@ -261,7 +261,7 @@ TEST_CASE_METHOD(DataProcessorTestFixture, } TEST_CASE_METHOD(DataProcessorTestFixture, "Arrange bitlist with reorder false", - "[.dataprocessor][.retrievebitlist]") { + "[dataprocessor][retrievebitlist]") { // parameters: num_samples, bitlist, expected_num_digital_bytes, // expected_digital_part auto parameters = GENERATE( @@ -315,7 +315,7 @@ TEST_CASE_METHOD(DataProcessorTestFixture, "Arrange bitlist with reorder false", } TEST_CASE_METHOD(DataProcessorTestFixture, "Arrange bitlist with reorder true", - "[.dataprocessor][.retrievebitlist]") { + "[dataprocessor][retrievebitlist]") { // parameters: num_samples, bitlist, expected_num_digital_bytes, // expected_digital_part auto parameters = GENERATE( @@ -369,7 +369,7 @@ TEST_CASE_METHOD(DataProcessorTestFixture, "Arrange bitlist with reorder true", TEST_CASE_METHOD(DataProcessorTestFixture, "Arrange bitlist and remove trailing bits", - "[.dataprocessor][.retrievebitlist]") { + "[dataprocessor][retrievebitlist]") { size_t num_random_offset_bytes = 3; std::vector bitlist{1, 4, 5}; diff --git a/slsSupportLib/tests/test-Timer.cpp b/slsSupportLib/tests/test-Timer.cpp index 6d94f1f7b..a4e768334 100644 --- a/slsSupportLib/tests/test-Timer.cpp +++ b/slsSupportLib/tests/test-Timer.cpp @@ -8,7 +8,7 @@ namespace sls { -TEST_CASE("Time 1s restart then time 2s", "[.timer]") { +TEST_CASE("Time 1s restart then time 2s", "[timer]") { auto sleep_duration = std::chrono::seconds(1); auto t = Timer(); std::this_thread::sleep_for(sleep_duration); @@ -19,7 +19,7 @@ TEST_CASE("Time 1s restart then time 2s", "[.timer]") { REQUIRE(t.elapsed_s() == Approx(2).epsilon(0.01)); } -TEST_CASE("Return ms", "[.timer]") { +TEST_CASE("Return ms", "[timer]") { auto sleep_duration = std::chrono::milliseconds(1300); auto t = Timer(); std::this_thread::sleep_for(sleep_duration); diff --git a/slsSupportLib/tests/test-Version.cpp b/slsSupportLib/tests/test-Version.cpp index 9c1f1d3f8..5870c9b56 100644 --- a/slsSupportLib/tests/test-Version.cpp +++ b/slsSupportLib/tests/test-Version.cpp @@ -5,7 +5,7 @@ namespace sls { -TEST_CASE("check if version is semantic", "[.version]") { +TEST_CASE("check if version is semantic", "[version]") { auto [version_string, has_semantic_version] = GENERATE(std::make_tuple("developer 0x250512", false), diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9f3d1b7c9..84e727629 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -60,7 +60,3 @@ catch_discover_tests(tests) configure_file(scripts/test_simulators.py ${CMAKE_BINARY_DIR}/bin/test_simulators.py COPYONLY) configure_file(scripts/test_frame_synchronizer.py ${CMAKE_BINARY_DIR}/bin/test_frame_synchronizer.py COPYONLY) configure_file(scripts/utils_for_test.py ${CMAKE_BINARY_DIR}/bin/utils_for_test.py COPYONLY) -configure_file(scripts/test_roi.py ${CMAKE_BINARY_DIR}/bin/test_roi.py COPYONLY) -configure_file(scripts/test_free.py ${CMAKE_BINARY_DIR}/bin/test_free.py COPYONLY) -configure_file(scripts/test_commands.py ${CMAKE_BINARY_DIR}/bin/test_commands.py COPYONLY) - diff --git a/tests/scripts/test_commands.py b/tests/scripts/test_commands.py deleted file mode 100644 index 73d28442e..000000000 --- a/tests/scripts/test_commands.py +++ /dev/null @@ -1,100 +0,0 @@ -# SPDX-License-Identifier: LGPL-3.0-or-other -# Copyright (C) 2021 Contributors to the SLS Detector Package -''' -This file is used to start up simulators and test for freeing shm and accessing it from python. -Run this using: pytest -s test_free.py -''' - -from time import time -import pytest, sys, time - -from slsdet import Detector, Ctb, freeSharedMemory -from slsdet.defines import DEFAULT_TCP_RX_PORTNO -from utils_for_test import ( - Log, - LogLevel, - cleanup, - startDetectorVirtualServer, - startProcessInBackground, - loadConfig, - loadBasicSettings -) - -def startReceiver(num_mods, fp): - if num_mods == 1: - cmd = ['slsReceiver'] - else: - cmd = ['slsMultiReceiver', str(DEFAULT_TCP_RX_PORTNO), str(num_mods)] - # in 10.0.0 - #cmd = ['slsMultiReceiver', '-p', str(DEFAULT_TCP_RX_PORTNO), '-n', str(num_mods)] - startProcessInBackground(cmd, fp) - time.sleep(1) - -''' -scope = module =>Once per test file/module -to share expensive setup like startDetectorVirtualServer -''' -@pytest.fixture(scope="module") -def det_config(): - return { - "name": "ctb", - "num_mods": 1 - } - -# autouse is false to pass explictly -@pytest.fixture(scope="module", autouse=False) -def setup_simulator(det_config): - """Fixture to start the detector server once and clean up at the end.""" - fp = sys.stdout - - cleanup(fp) - # server - startDetectorVirtualServer(det_config["name"], det_config["num_mods"], fp) - # receiver - startReceiver(det_config["num_mods"], fp) - # config and basic settings - d = loadConfig(name=det_config["name"], rx_hostname="localhost", settingsdir="", fp=fp, num_mods=det_config["num_mods"]) - loadBasicSettings(name=det_config["name"], d=d, fp=fp) - - yield d # tests run here - - cleanup(fp) - - - -def test_parameters_file(setup_simulator): - d = setup_simulator - Log(LogLevel.INFOBLUE, f'\nRunning test_parameters_file') - - assert isinstance(d, Detector) - - with open("/tmp/params.det", "w") as f: - f.write("frames 2\n") - f.write("fwrite 1\n") - - # this should not throw - d.parameters = "/tmp/params.det" - - assert d.frames == 2 - assert d.fwrite == 1 - - Log(LogLevel.INFOGREEN, f"✅ Test passed. Command: parameters") - - -def test_include_file(setup_simulator): - d = setup_simulator - Log(LogLevel.INFOBLUE, f'\test_include_file test_parameters_file') - - assert isinstance(d, Detector) - - with open("/tmp/params.det", "w") as f: - f.write("frames 3\n") - f.write("fwrite 0\n") - - # this should not throw - d.include = "/tmp/params.det" - - assert d.frames == 3 - assert d.fwrite == 0 - - Log(LogLevel.INFOGREEN, f"✅ Test passed. Command: include") diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py index 6653c96cb..0f25b02a4 100644 --- a/tests/scripts/test_frame_synchronizer.py +++ b/tests/scripts/test_frame_synchronizer.py @@ -14,44 +14,43 @@ from utils_for_test import ( Log, LogLevel, RuntimeException, - checkIfProcessRunning, - killProcess, cleanup, - cleanSharedmemory, startProcessInBackground, - startProcessInBackgroundWithLogFile, checkLogForErrors, startDetectorVirtualServer, loadConfig, loadBasicSettings, - ParseArguments + ParseArguments, + build_dir, + optional_file ) LOG_PREFIX_FNAME = '/tmp/slsFrameSynchronizer_test' MAIN_LOG_FNAME = LOG_PREFIX_FNAME + '_log.txt' PULL_SOCKET_PREFIX_FNAME = LOG_PREFIX_FNAME + '_pull_socket_' +SYNCHRONIZER_SUFFIX_FNAME = LOG_PREFIX_FNAME + '_synchronizer.txt' -def startFrameSynchronizerPullSocket(name, fp): - fname = PULL_SOCKET_PREFIX_FNAME + name + '.txt' +def startFrameSynchronizerPullSocket(name, fp, quiet_mode=False): cmd = ['python', '-u', 'frameSynchronizerPullSocket.py'] - startProcessInBackgroundWithLogFile(cmd, fp, fname) + fname = PULL_SOCKET_PREFIX_FNAME + name + '.txt' + startProcessInBackground(cmd, fp, fname, quiet_mode) time.sleep(1) checkLogForErrors(fp, fname) -def startFrameSynchronizer(num_mods, fp): - cmd = ['slsFrameSynchronizer', str(DEFAULT_TCP_RX_PORTNO), str(num_mods)] +def startFrameSynchronizer(num_mods, fp, quiet_mode=False): + cmd = [str(build_dir / 'slsFrameSynchronizer'), str(DEFAULT_TCP_RX_PORTNO), str(num_mods)] # in 10.0.0 #cmd = ['slsFrameSynchronizer', '-p', str(DEFAULT_TCP_RX_PORTNO), '-n', str(num_mods)] - startProcessInBackground(cmd, fp) + fname = SYNCHRONIZER_SUFFIX_FNAME + startProcessInBackground(cmd, fp, fname, quiet_mode) time.sleep(1) def acquire(fp, det): - Log(LogLevel.INFO, 'Acquiring') - Log(LogLevel.INFO, 'Acquiring', fp) + Log(LogLevel.INFO, 'Acquiring', fp, True) det.acquire() @@ -60,14 +59,12 @@ def testFramesCaught(name, det, num_frames): if fnum != num_frames: raise RuntimeException(f"{name} caught only {fnum}. Expected {num_frames}") - Log(LogLevel.INFOGREEN, f'Frames caught test passed for {name}') - Log(LogLevel.INFOGREEN, f'Frames caught test passed for {name}', fp) + Log(LogLevel.INFOGREEN, f'Frames caught test passed for {name}', fp, True) def testZmqHeadetTypeCount(name, det, num_mods, num_frames, fp): - Log(LogLevel.INFO, f"Testing Zmq Header type count for {name}") - Log(LogLevel.INFO, f"Testing Zmq Header type count for {name}", fp) + Log(LogLevel.INFO, f"Testing Zmq Header type count for {name}", fp, True) htype_counts = { "header": 0, "series_end": 0, @@ -89,7 +86,6 @@ def testZmqHeadetTypeCount(name, det, num_mods, num_frames, fp): htype_counts[htype] += 1 except json.JSONDecodeError: continue - # test if file contents matches expected counts num_ports_per_module = 1 if name == "gotthard2" else det.numinterfaces total_num_frame_parts = num_ports_per_module * num_mods * num_frames @@ -100,19 +96,17 @@ def testZmqHeadetTypeCount(name, det, num_mods, num_frames, fp): except Exception as e: raise RuntimeException(f'Failed to get zmq header count type. Error:{str(e)}') from e - Log(LogLevel.INFOGREEN, f"Zmq Header type count test passed for {name}") - Log(LogLevel.INFOGREEN, f"Zmq Header type count test passed for {name}", fp) + Log(LogLevel.INFOGREEN, f"Zmq Header type count test passed for {name}", fp, True) def startTestsForAll(args, fp): for server in args.servers: try: - Log(LogLevel.INFOBLUE, f'Synchronizer Tests for {server}') - Log(LogLevel.INFOBLUE, f'Synchronizer Tests for {server}', fp) + Log(LogLevel.INFOBLUE, f'Synchronizer Tests for {server}', fp, True) cleanup(fp) - startDetectorVirtualServer(server, args.num_mods, fp) - startFrameSynchronizerPullSocket(server, fp) - startFrameSynchronizer(args.num_mods, fp) + startDetectorVirtualServer(server, args.num_mods, fp, args.quiet) + startFrameSynchronizerPullSocket(server, fp, args.quiet) + startFrameSynchronizer(args.num_mods, f, args.quiet_modep) d = loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, log_file_fp=fp, num_mods=args.num_mods, num_frames=args.num_frames) loadBasicSettings(name=server, d=d, fp=fp) acquire(fp, d) @@ -128,6 +122,9 @@ def startTestsForAll(args, fp): if __name__ == '__main__': args = ParseArguments(description='Automated tests to test frame synchronizer', default_num_mods=2) + if args.no_log_file: + raise RuntimeException("Cannot run frame synchronizer test without files") + Log(LogLevel.INFOBLUE, '\nLog File: ' + MAIN_LOG_FNAME + '\n') with open(MAIN_LOG_FNAME, 'w') as fp: diff --git a/tests/scripts/test_roi.py b/tests/scripts/test_roi.py deleted file mode 100644 index 17d35c56d..000000000 --- a/tests/scripts/test_roi.py +++ /dev/null @@ -1,82 +0,0 @@ -# SPDX-License-Identifier: LGPL-3.0-or-other -# Copyright (C) 2021 Contributors to the SLS Detector Package -''' -This file is used to start up simulators, receivers and test roi for every detector in many configurations. -''' - -import sys, time -import traceback - -from slsdet import Detector, burstMode -from slsdet.defines import DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO -from datetime import timedelta - - -from utils_for_test import ( - Log, - LogLevel, - RuntimeException, - cleanup, - startProcessInBackground, - startReceiver, - startDetectorVirtualServer, - connectToVirtualServers, - loadBasicSettings, - loadConfig, - runProcessWithLogFile -) - -LOG_PREFIX_FNAME = '/tmp/slsDetectorPackage_virtual_roi_test' -MAIN_LOG_FNAME = LOG_PREFIX_FNAME + '_log.txt' -ROI_TEST_FNAME = LOG_PREFIX_FNAME + '_results_' - -def startTestsForAll(fp): - servers = [ - 'eiger', - 'jungfrau', - 'mythen3', - 'gotthard2', - 'moench', - ] - nmods = 2 - for server in servers: - for ninterfaces in range(1, 2): - if ninterfaces == 2 and server != 'jungfrau' and server != 'moench': - continue - try: - msg = f'Starting Roi Tests for {server}' - if server == 'jungfrau' or server == 'moench': - msg += f' with {ninterfaces} interfaces' - Log(LogLevel.INFOBLUE, msg) - Log(LogLevel.INFOBLUE, msg, fp) - cleanup(fp) - startDetectorVirtualServer(server, nmods, fp) - startReceiver(nmods, fp) - d = loadConfig(name=server, log_file_fp = fp, num_mods=nmods, num_frames=5, num_interfaces=ninterfaces) - loadBasicSettings(name=server, d=d, fp=fp) - - fname = ROI_TEST_FNAME + server + '.txt' - cmd = ['tests', 'rx_roi', '--abort', '-s'] - runProcessWithLogFile('Roi Tests for ' + server, cmd, fp, fname) - Log(LogLevel.INFO, '\n') - except Exception as e: - raise RuntimeException(f'Roi Tests failed') from e - - Log(LogLevel.INFOGREEN, 'Passed all Roi tests for all detectors \n' + str(servers)) - - -if __name__ == '__main__': - Log(LogLevel.INFOBLUE, '\nLog File: ' + MAIN_LOG_FNAME + '\n') - - with open(MAIN_LOG_FNAME, 'w') as fp: - try: - startTestsForAll(fp) - #TODO: check master file as well for both json and hdf5 as well - cleanup(fp) - except Exception as e: - with open(MAIN_LOG_FNAME, 'a') as fp_error: - traceback.print_exc(file=fp_error) - cleanup(fp) - Log(LogLevel.ERROR, f'Tests Failed.') - - diff --git a/tests/scripts/test_simulators.py b/tests/scripts/test_simulators.py index 7aab1d958..a7f211d12 100644 --- a/tests/scripts/test_simulators.py +++ b/tests/scripts/test_simulators.py @@ -2,91 +2,98 @@ # Copyright (C) 2021 Contributors to the SLS Detector Package ''' This file is used to start up simulators, receivers and run all the tests on them and finally kill the simulators and receivers. + +It can be used to run all catch tests with tag [.detectorintegration]. + +Pass --tests to run specific tests only or --tests to run all tests with that specific tag. + +Pass --servers ... to run tests only for specific detector servers. ''' import argparse import sys, subprocess, time, traceback +from contextlib import contextmanager from slsdet import Detector from slsdet.defines import DEFAULT_TCP_RX_PORTNO + from utils_for_test import ( Log, LogLevel, RuntimeException, - checkIfProcessRunning, - killProcess, cleanup, - cleanSharedmemory, - startProcessInBackground, - runProcessWithLogFile, + runProcess, + startReceiver, + runProcess, startDetectorVirtualServer, loadConfig, loadBasicSettings, - ParseArguments + ParseArguments, + build_dir, + optional_file ) - LOG_PREFIX_FNAME = '/tmp/slsDetectorPackage_virtual_test' MAIN_LOG_FNAME = LOG_PREFIX_FNAME + '_log.txt' GENERAL_TESTS_LOG_FNAME = LOG_PREFIX_FNAME + '_results_general.txt' -CMD_TEST_LOG_PREFIX_FNAME = LOG_PREFIX_FNAME + '_results_cmd_' - - -def startReceiver(num_mods, fp): - if num_mods == 1: - cmd = ['slsReceiver'] - else: - cmd = ['slsMultiReceiver', str(DEFAULT_TCP_RX_PORTNO), str(num_mods)] - # in 10.0.0 - #cmd = ['slsMultiReceiver', '-p', str(DEFAULT_TCP_RX_PORTNO), '-n', str(num_mods)] - startProcessInBackground(cmd, fp) - time.sleep(1) def startGeneralTests(fp): fname = GENERAL_TESTS_LOG_FNAME - cmd = ['tests', '--abort', '-s'] + cmd = [str(build_dir / 'tests'), '--abort', '-s'] try: cleanup(fp) - runProcessWithLogFile('General Tests', cmd, fp, fname) + runProcess('General Tests', cmd, fp, fname) except Exception as e: raise RuntimeException(f'General tests failed.') from e +def startTestsForAll(args, fp): + + fname_template = LOG_PREFIX_FNAME + "_{}_{}.txt" + + + test_filter = args.tests + cmd = [str(build_dir / 'tests'), '--abort', test_filter, '-s'] + + num_mods = args.num_mods -def startCmdTestsForAll(args, fp): for server in args.servers: - try: - num_mods = 2 if server == 'eiger' else 1 - fname = CMD_TEST_LOG_PREFIX_FNAME + server + '.txt' - cmd = ['tests', '--abort', args.markers, '-s'] + for curMods in range(1, num_mods + 1): + if curMods == 2 and server in ['ctb', 'xilinx_ctb']: + continue + for ninterfaces in [1,2]: # always test both + if ninterfaces == 2 and server != 'jungfrau' and server != 'moench': + continue - Log(LogLevel.INFOBLUE, f'Starting Cmd Tests for {server}') - cleanup(fp) - startDetectorVirtualServer(name=server, num_mods=num_mods, fp=fp) - startReceiver(num_mods, fp) - d = loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, log_file_fp=fp, num_mods=num_mods) - loadBasicSettings(name=server, d=d, fp=fp) - runProcessWithLogFile('Cmd Tests (' + args.markers + ') for ' + server, cmd, fp, fname) - except Exception as e: - raise RuntimeException(f'Cmd Tests failed for {server}.') from e + if server == "eiger": + curMods *= 2 # top and bottom half module + try: + fname = fname_template.format(args.tests, server) if not args.no_log_file else None + + Log(LogLevel.INFOBLUE, f'Starting {args.tests} Tests for {server}, {ninterfaces} interfaces, {curMods} modules', fp, True) + cleanup(fp) + startDetectorVirtualServer(name=server, num_mods=curMods, fp=fp, no_log_file=args.no_log_file, quiet_mode=args.quiet) + startReceiver(curMods, fp, args.no_log_file, args.quiet) + d = loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, log_file_fp=fp, num_mods=curMods, num_interfaces=ninterfaces) + loadBasicSettings(name=server, d=d, fp=fp) + runProcess('Tests (' + args.tests + ') for ' + server, cmd, fp, fname, args.quiet) + except Exception as e: + raise RuntimeException(f'Tests (' + args.tests + ') failed for ' + server + '.') from e Log(LogLevel.INFOGREEN, 'Passed all tests for all detectors \n' + str(args.servers)) if __name__ == '__main__': - args = ParseArguments(description='Automated tests with the virtual detector servers', default_num_mods=1, markers=True, general_tests_option=True) - if args.num_mods > 1: - raise RuntimeException(f'Cannot support multiple modules at the moment (except Eiger).') + args = ParseArguments(description='Automated tests with the virtual detector servers', default_num_mods=2, specific_tests=True, general_tests_option=True) - Log(LogLevel.INFOBLUE, '\nLog File: ' + MAIN_LOG_FNAME + '\n') - - with open(MAIN_LOG_FNAME, 'w') as fp: + with optional_file(MAIN_LOG_FNAME if not args.no_log_file else None, 'w', args.quiet) as fp: try: if args.general_tests: startGeneralTests(fp) - startCmdTestsForAll(args, fp) + startTestsForAll(args, fp) cleanup(fp) except Exception as e: - with open(MAIN_LOG_FNAME, 'a') as fp_error: - traceback.print_exc(file=fp_error) + traceback.print_exc(file=fp) cleanup(fp) Log(LogLevel.ERROR, f'Tests Failed.') + raise e + diff --git a/tests/scripts/utils_for_test.py b/tests/scripts/utils_for_test.py index bff86f886..66c43fb38 100644 --- a/tests/scripts/utils_for_test.py +++ b/tests/scripts/utils_for_test.py @@ -3,19 +3,24 @@ ''' This file is used for common utils used for integration tests between simulators and receivers. ''' - +import os from pathlib import Path import sys, subprocess, time, argparse from enum import Enum from colorama import Fore, Style, init from datetime import timedelta +from contextlib import contextmanager from slsdet import Detector, Ctb, detectorSettings, burstMode from slsdet.defines import DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO SERVER_START_PORTNO=1900 +LOG_PREFIX_FNAME = "/tmp/slsDetectorPackage_" + init(autoreset=True) +build_dir = Path(__file__).resolve().parents[2] / "build" / "bin" + class LogLevel(Enum): INFO = 0 INFORED = 1 @@ -44,10 +49,13 @@ LOG_COLORS = { } -def Log(level: LogLevel, message: str, stream=sys.stdout): +def Log(level: LogLevel, message: str, stream=sys.stdout, both=False): color = LOG_COLORS.get(level, Fore.WHITE) label = LOG_LABELS.get(level, "") print(f"{color}{label}{message}{Style.RESET_ALL}", file=stream, flush=True) + if both and stream != sys.stdout: + print(f"{color}{label}{message}{Style.RESET_ALL}", file=sys.stdout, flush=True) + class RuntimeException (Exception): @@ -56,9 +64,27 @@ class RuntimeException (Exception): super().__init__(message) +@contextmanager +def optional_file(file_path=None, mode='w', quiet_mode=False): + if file_path: + f = open(file_path, mode) + try: + yield f + finally: + f.close() + else: + if quiet_mode: + f = open(os.devnull, mode) + try: + yield f + finally: + f.close() + else: + yield sys.stdout + + def checkIfProcessRunning(processName): - cmd = f"pgrep -f {processName}" - res = subprocess.getoutput(cmd) + res = subprocess.getoutput(f"pgrep -f {processName}") return res.strip().splitlines() @@ -80,18 +106,13 @@ def killProcess(name, fp): def cleanSharedmemory(fp): Log(LogLevel.INFO, 'Cleaning up shared memory', fp) try: - p = subprocess.run(['sls_detector_get', 'free'], stdout=fp, stderr=fp, check = False) - except FileNotFoundError: - # Binary not available (e.g. on CI) → ignore - Log(LogLevel.INFO, 'sls_detector_get not found, skipping shared memory cleanup', fp) + p = subprocess.run([build_dir / 'sls_detector_get', 'free'], stdout=fp, stderr=fp) except Exception as e: - # Any other cleanup failure should NEVER fail tests - Log(LogLevel.WARN, f'Ignoring shared memory cleanup error: {e}', fp) + raise RuntimeException(f'Could not free shared memory: {str(e)}') def cleanup(fp): - Log(LogLevel.INFO, 'Cleaning up') - Log(LogLevel.INFO, 'Cleaning up', fp) + Log(LogLevel.INFO, 'Cleaning up', fp, True) killProcess('DetectorServer_virtual', fp) killProcess('slsReceiver', fp) killProcess('slsMultiReceiver', fp) @@ -100,20 +121,15 @@ def cleanup(fp): cleanSharedmemory(fp) -def startProcessInBackground(cmd, fp): - Log(LogLevel.INFO, 'Starting up ' + ' '.join(cmd)) - Log(LogLevel.INFO, 'Starting up ' + ' '.join(cmd), fp) - try: - p = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) - except Exception as e: - raise RuntimeException(f'Failed to start {cmd}:{str(e)}') from e +def startProcessInBackground(cmd, fp, log_file_name: str, quiet_mode=False): + info_text = 'Starting up ' + ' '.join(cmd) + if log_file_name: + info_text += '. Log: ' + log_file_name + Log(LogLevel.INFO, f'{info_text}', fp, True) -def startProcessInBackgroundWithLogFile(cmd, fp, log_file_name: str): - Log(LogLevel.INFOBLUE, 'Starting up ' + ' '.join(cmd) + '. Log: ' + log_file_name) - Log(LogLevel.INFOBLUE, 'Starting up ' + ' '.join(cmd) + '. Log: ' + log_file_name, fp) try: - with open(log_file_name, 'w') as log_fp: + with optional_file(log_file_name, 'w', quiet_mode) as log_fp: subprocess.Popen(cmd, stdout=log_fp, stderr=log_fp, text=True) except Exception as e: raise RuntimeException(f'Failed to start {cmd}:{str(e)}') from e @@ -124,8 +140,7 @@ def checkLogForErrors(fp, log_file_path: str): with open(log_file_path, 'r') as log_file: for line in log_file: if 'Error' in line: - Log(LogLevel.ERROR, f"Error found in log: {line.strip()}") - Log(LogLevel.ERROR, f"Error found in log: {line.strip()}", fp) + Log(LogLevel.ERROR, f"Error found in log: {line.strip()}", fp, True) raise RuntimeException("Error found in log file") except FileNotFoundError: print(f"Log file not found: {log_file_path}") @@ -135,33 +150,92 @@ def checkLogForErrors(fp, log_file_path: str): raise -def runProcessWithLogFile(name, cmd, fp, log_file_name): - Log(LogLevel.INFOBLUE, 'Running ' + name + '. Log: ' + log_file_name) - Log(LogLevel.INFOBLUE, 'Running ' + name + '. Log: ' + log_file_name, fp) - Log(LogLevel.INFOBLUE, 'Cmd: ' + ' '.join(cmd), fp) +def checkLogForErrorsOrSummary(fp, lines, source_name=""): + failed = False # if it found "failed" or "FAILED" in file + failed_msg = "" + printing_error = False # print every line in file after failure + printing_summary = False # print summary if no failure + + + for line in lines: + line_stripped = line.rstrip() + + # Detect failure (case-insensitive) + if not failed and (": FAILED:" in line or " failed\nassertions" in line): + failed = True + failed_msg = line_stripped + printing_error = True + Log(LogLevel.ERROR, line_stripped, fp) + if source_name: + Log(LogLevel.ERROR, f"Error log from file: {source_name}") + Log(LogLevel.ERROR, "="*79) + + # After failure, log everything as ERROR + if printing_error: + print(f"{line_stripped}") + continue + + # Summary delimiter + if line_stripped.startswith("====="): + printing_summary = True + + # No failure - print summary lines + if printing_summary: + print(f"{line_stripped}") + + + if failed: + Log(LogLevel.ERROR, "="*79) + raise RuntimeException(f'Test failed: {failed_msg}') + else: + print("="*79) + + + +def runProcess(name, cmd, fp, log_file_name = None, quiet_mode=False): + + info_text = 'Running ' + name + '.' + if log_file_name: + info_text += ' Log: ' + log_file_name + Log(LogLevel.INFOBLUE, info_text, fp, True) + Log(LogLevel.INFOBLUE, 'Cmd: ' + ' '.join(cmd), fp, True) + + error_log = None + try: - with open(log_file_name, 'w') as log_fp: - subprocess.run(cmd, stdout=log_fp, stderr=log_fp, check=True, text=True) + if log_file_name: + with optional_file(log_file_name, 'w', quiet_mode) as log_fp: + subprocess.run(cmd, stdout=log_fp, stderr=log_fp, check=True, text=True) + else: + capture = subprocess.run(cmd, check=True, text=True, capture_output=True) + captured_log = capture.stdout.splitlines() + except subprocess.CalledProcessError as e: - pass + print("error: ", str(e)) + captured_log = e.stdout.splitlines() + pass except Exception as e: + print("something else failed") Log(LogLevel.ERROR, f'Failed to run {name}:{str(e)}', fp) raise RuntimeException(f'Failed to run {name}:{str(e)}') - with open (log_file_name, 'r') as f: - for line in f: - if "FAILED" in line: - raise RuntimeException(f'{line}') + if log_file_name: + with optional_file(log_file_name, 'r') as log_fp: + checkLogForErrorsOrSummary(fp, log_fp, log_file_name) + else: + checkLogForErrorsOrSummary(fp, captured_log) - Log(LogLevel.INFOGREEN, name + ' successful!\n') - Log(LogLevel.INFOGREEN, name + ' successful!\n', fp) + Log(LogLevel.INFOGREEN, name + ' successful!\n', fp, True) -def startDetectorVirtualServer(name :str, num_mods, fp): +def startDetectorVirtualServer(name :str, num_mods, fp, no_log_file = False, quiet_mode=False): for i in range(num_mods): port_no = SERVER_START_PORTNO + (i * 2) - cmd = [name + 'DetectorServer_virtual', '-p', str(port_no)] - startProcessInBackgroundWithLogFile(cmd, fp, "/tmp/virtual_det_" + name + "_" + str(i) + ".txt") + cmd = [str(build_dir / (name + 'DetectorServer_virtual')), '-p', str(port_no)] + fname = LOG_PREFIX_FNAME + "virtual_det_" + name + "_" + str(SERVER_START_PORTNO) + ".txt" + if no_log_file: + fname = None + startProcessInBackground(cmd, fp, fname, quiet_mode) match name: case 'jungfrau': time.sleep(7) @@ -196,20 +270,23 @@ def connectToVirtualServers(name, num_mods, ctb_object=False): return d -def startReceiver(num_mods, fp): +def startReceiver(num_mods, fp, no_log_file = False, quiet_mode=False): if num_mods == 1: - cmd = ['slsReceiver'] + cmd = [str(build_dir / 'slsReceiver')] + fname = LOG_PREFIX_FNAME + "slsReceiver.txt" else: - cmd = ['slsMultiReceiver', str(DEFAULT_TCP_RX_PORTNO), str(num_mods)] + cmd = [str(build_dir / 'slsMultiReceiver'), str(DEFAULT_TCP_RX_PORTNO), str(num_mods)] + fname = LOG_PREFIX_FNAME + "slsMultiReceiver.txt" # in 10.0.0 #cmd = ['slsMultiReceiver', '-p', str(DEFAULT_TCP_RX_PORTNO), '-n', str(num_mods)] - startProcessInBackground(cmd, fp) + + if no_log_file: + fname = None + startProcessInBackground(cmd, fp, fname, quiet_mode) time.sleep(1) - def loadConfig(name, rx_hostname = 'localhost', settingsdir = None, log_file_fp = None, num_mods = 1, num_frames = 1, num_interfaces = 1): - Log(LogLevel.INFO, 'Loading config') - Log(LogLevel.INFO, 'Loading config', log_file_fp) + Log(LogLevel.INFO, 'Loading config', log_file_fp, True) try: d = connectToVirtualServers(name, num_mods) @@ -217,17 +294,15 @@ def loadConfig(name, rx_hostname = 'localhost', settingsdir = None, log_file_fp d.numinterfaces = num_interfaces d.udp_dstport = DEFAULT_UDP_DST_PORTNO - if name == 'eiger' or name == 'jungfrau' or name == 'moench': + if name == 'eiger' or num_interfaces == 2: d.udp_dstport2 = DEFAULT_UDP_DST_PORTNO + 1 d.rx_hostname = rx_hostname - d.udp_dstip = 'auto' - if name != "eiger": d.udp_srcip = 'auto' - if name == "jungfrau" or name == "moench": + if num_interfaces == 2: d.udp_dstip2 = 'auto' if name == "jungfrau" or name == "moench" or name == "xilinx_ctb": @@ -241,6 +316,7 @@ def loadConfig(name, rx_hostname = 'localhost', settingsdir = None, log_file_fp d.trimen = [4500, 5400, 6400] if name == 'eiger' else [4000, 6000, 8000, 12000] d.setThresholdEnergy(4500, detectorSettings.STANDARD) + d.frames = num_frames except Exception as e: @@ -248,10 +324,11 @@ def loadConfig(name, rx_hostname = 'localhost', settingsdir = None, log_file_fp return d + + # for easy acquire def loadBasicSettings(name, d, fp): - Log(LogLevel.INFO, 'Loading basic settings for ' + name) - Log(LogLevel.INFO, 'Loading basic settings for ' + name, fp) + Log(LogLevel.INFO, 'Loading basic settings for ' + name, fp, True) try: # basic settings for easy acquire if name == "jungfrau": @@ -278,7 +355,7 @@ def loadBasicSettings(name, d, fp): except Exception as e: raise RuntimeException(f'Could not load config for {name}. Error: {str(e)}') from e -def ParseArguments(description, default_num_mods=1, markers=False, general_tests_option=False): +def ParseArguments(description, default_num_mods=2, specific_tests=False, general_tests_option=False): parser = argparse.ArgumentParser(description) default_settings_path = Path(__file__).resolve().parents[2] / "settingsdir" @@ -293,16 +370,21 @@ def ParseArguments(description, default_num_mods=1, markers=False, general_tests help='Number of frames to test with') parser.add_argument('-s', '--servers', nargs='*', help='Detector servers to run') - if markers: - parser.add_argument('-m', '--markers', nargs='?', default ='[.cmdcall]', - help = 'Markers to use for cmd tests, default: [.cmdcall]') - + parser.add_argument('-nlf', '--no-log-file', action='store_true', + help='Dont write output to log file') + parser.add_argument('-q', '--quiet', action='store_true', + help='Dont write to stdout when possible.') + if specific_tests: + parser.add_argument('-t', '--tests', nargs='?', default ='[.detectorintegration]', + help = 'Test markers or specific test name to use for tests, default: [.detectorintegration]') + if general_tests_option: - parser.add_argument('-g', '--general_tests', action='store_true', + parser.add_argument('-g', '--general-tests', action='store_true', help = 'Enable general tests (no value needed)') args = parser.parse_args() + # Set default server list if not provided if args.servers is None: args.servers = [ @@ -323,8 +405,19 @@ def ParseArguments(description, default_num_mods=1, markers=False, general_tests f"num_mods: '{args.num_mods}'\n" f"num_frames: '{args.num_frames}'" ) - if markers: - msg += f"\nmarkers: '{args.markers}'" + + if args.no_log_file: + msg += f"\nLog File: Disabled" + else: + msg += f"\nLog File: Enabled" + + if args.quiet: + msg += f"\nQuiet mode: Enabled" + else: + msg += f"\nQuiet mode: Disabled" + + if specific_tests: + msg += f"\ntests: '{args.tests}'" if general_tests_option: msg += f"\ngeneral_tests: '{args.general_tests}'" From fb58fefe57c9441f619dee0dcb08caa50f59a709 Mon Sep 17 00:00:00 2001 From: Martin Mueller Date: Thu, 5 Feb 2026 15:11:31 +0100 Subject: [PATCH 11/20] added RegDefs for 1G support on XCTB --- .../xilinx_ctbDetectorServer/RegisterDefs.h | 50 ++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/RegisterDefs.h b/slsDetectorServers/xilinx_ctbDetectorServer/RegisterDefs.h index 438355c46..d0a6e8bdb 100644 --- a/slsDetectorServers/xilinx_ctbDetectorServer/RegisterDefs.h +++ b/slsDetectorServers/xilinx_ctbDetectorServer/RegisterDefs.h @@ -131,7 +131,7 @@ /* REG no_Samples_X_Reg */ #define NO_SAMPLES_X_REG 0xa00cUL -#define NO_SAMPLES_X_MSK 0x7FFFFFFFUL +#define NO_SAMPLES_X_MSK 0xffffUL #define NO_SAMPLES_X_OFST 0 /* REG count_Frames_From_Reg_1 */ @@ -150,6 +150,39 @@ #define LOCAL_FRAME_NUMBER_REG_2 0xa01cUL #define LOCAL_FRAME_NUMBER_REG_2_PRESET 0x0UL +/* REG X0_FIFO_read */ +#define X0_FIFO_READ 0xa020UL +#define X0_FIFO_READ_PRESET 0x0UL + +/* REG X1_FIFO_read */ +#define X1_FIFO_READ 0xa024UL +#define X1_FIFO_READ_PRESET 0x0UL + +/* REG X2_FIFO_read */ +#define X2_FIFO_READ 0xa028UL +#define X2_FIFO_READ_PRESET 0x0UL + +/* REG X3_FIFO_read */ +#define X3_FIFO_READ 0xa02cUL +#define X3_FIFO_READ_PRESET 0x0UL + +/* REG D_FIFO_read */ +#define D_FIFO_READ 0xa030UL +#define D_FIFO_READ_PRESET 0x0UL + +/* REG A_FIFO_select */ +#define A_FIFO_SELECT 0xa034UL +#define A_FIFO_SELECT_PRESET 0x0UL + +/* REG A_FIFO_read */ +#define A_FIFO_READ 0xa038UL +#define A_FIFO_READ_PRESET 0x0UL + +/* REG enable_1G */ +#define ENABLE_1G 0xa03cUL +#define ENABLE_1G_MSK 0x1UL +#define ENABLE_1G_OFST 0 + /* REG TransceiverRXCTRL0Reg1 */ #define TRANSCEIVERRXCTRL0REG1 0xc100UL #define TRANSCEIVERRXCTRL0REG1_PRESET 0x0UL @@ -301,21 +334,21 @@ #define RXDATACH3_OFST 0 /* REG PktPacketLengthReg */ -#define PKTPACKETLENGTHREG 0xa020UL +#define PKTPACKETLENGTHREG 0xa100UL #define PACKETLENGTH1G_MSK 0xffffUL #define PACKETLENGTH1G_OFST 0 #define PACKETLENGTH10G_MSK 0xffff0000UL #define PACKETLENGTH10G_OFST 16 /* REG PktNoPacketsReg */ -#define PKTNOPACKETSREG 0xa024UL +#define PKTNOPACKETSREG 0xa104UL #define NOPACKETS1G_MSK 0x3fUL #define NOPACKETS1G_OFST 0 #define NOPACKETS10G_MSK 0x3f0000UL #define NOPACKETS10G_OFST 16 /* REG PktCtrlReg */ -#define PKTCTRLREG 0xa028UL +#define PKTCTRLREG 0xa108UL #define NOSERVERS_MSK 0x3fUL #define NOSERVERS_OFST 0 #define SERVERSTART_MSK 0x1f00UL @@ -325,14 +358,14 @@ #define ETHINTERF_OFST 16 /* REG PktCoordReg1 */ -#define PKTCOORDREG1 0xa02cUL +#define PKTCOORDREG1 0xa10cUL #define COORDX_MSK 0xffffUL #define COORDX_OFST 0 #define COORDY_MSK 0xffff0000UL #define COORDY_OFST 16 /* REG PktCoordReg2 */ -#define PKTCOORDREG2 0xa030UL +#define PKTCOORDREG2 0xa110UL #define COORDZ_MSK 0xffffUL #define COORDZ_OFST 0 @@ -639,6 +672,11 @@ #define MISO_SELECT 0xc018UL #define MISO_SELECT_PRESET 0x0UL +/* REG OBUFDTS_ena */ +#define OBUFDTS_ENA 0xc01cUL +#define OBUFDTS_ENA_MSK 0xffffUL +#define OBUFDTS_ENA_OFST 0 + /* REG A_FIFO_Overflow_Status_Reg */ #define A_FIFO_OVERFLOW_STATUS_REG 0x9000UL #define A_FIFO_OVERFLOW_STATUS_REG_PRESET 0x0UL From 0992c7ae4c904b68c9034104b206e35917e61989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Thu, 5 Feb 2026 17:20:47 +0100 Subject: [PATCH 12/20] Read and write SPI for Xilinx CTB (#1381) -readSpi and writeSpi in C++ and Python API --- CMakeLists.txt | 6 + python/src/detector.cpp | 12 + .../include/slsDetectorServer_funcs.h | 2 + .../src/slsDetectorServer_funcs.c | 309 ++++++++++++++++++ .../slsDetectorFunctionList.c | 2 +- .../slsDetectorFunctionList.h | 2 +- slsDetectorSoftware/include/sls/Detector.h | 6 + slsDetectorSoftware/src/Detector.cpp | 15 + slsDetectorSoftware/src/Module.cpp | 41 +++ slsDetectorSoftware/src/Module.h | 4 + .../include/sls/sls_detector_funcs.h | 4 + 11 files changed, 401 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b4ea785fe..daffa4b67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -199,6 +199,7 @@ option(SLS_USE_GUI "GUI" OFF) option(SLS_USE_SIMULATOR "Simulator" OFF) option(SLS_USE_TESTS "TESTS" OFF) option(SLS_USE_SANITIZER "Sanitizers for debugging" OFF) +option(SLS_USE_SANITIZER_IN_SERVER "Sanitizers for debugging" OFF) option(SLS_USE_PYTHON "Python bindings" OFF) option(SLS_INSTALL_PYTHONEXT "Install the python extension in the install tree under CMAKE_INSTALL_PREFIX/python/" OFF) option(SLS_USE_CTBGUI "ctb GUI" OFF) @@ -346,6 +347,11 @@ if (NOT TARGET slsProjectCSettings) target_link_libraries(slsProjectCSettings INTERFACE Threads::Threads ) + + if(SLS_USE_SANITIZER_IN_SERVER) + target_compile_options(slsProjectCSettings INTERFACE -fsanitize=address,undefined -fno-omit-frame-pointer) + target_link_libraries(slsProjectCSettings INTERFACE -fsanitize=address,undefined) + endif() endif() diff --git a/python/src/detector.cpp b/python/src/detector.cpp index 070dac493..d3e6be8b3 100644 --- a/python/src/detector.cpp +++ b/python/src/detector.cpp @@ -2198,5 +2198,17 @@ void init_det(py::module &m) { (Result(Detector::*)(sls::Positions) const) & Detector::getMeasurementTime, py::arg() = Positions{}); + CppDetectorApi.def("readSpi", + (Result>(Detector::*)( + int, int, int, sls::Positions) const) & + Detector::readSpi, + py::arg(), py::arg(), py::arg(), + py::arg() = Positions{}); + CppDetectorApi.def( + "writeSpi", + (void (Detector::*)(int, int, const std::vector &, + sls::Positions)) & + Detector::writeSpi, + py::arg(), py::arg(), py::arg(), py::arg() = Positions{}); ; } diff --git a/slsDetectorServers/slsDetectorServer/include/slsDetectorServer_funcs.h b/slsDetectorServers/slsDetectorServer/include/slsDetectorServer_funcs.h index 2c7e60ce9..e43148b83 100644 --- a/slsDetectorServers/slsDetectorServer/include/slsDetectorServer_funcs.h +++ b/slsDetectorServers/slsDetectorServer/include/slsDetectorServer_funcs.h @@ -338,3 +338,5 @@ int get_collection_mode(int); int set_collection_mode(int); int get_pattern_wait_interval(int); int set_pattern_wait_interval(int); +int spi_read(int); +int spi_write(int); diff --git a/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c b/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c index 157daa377..174af28a8 100644 --- a/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c +++ b/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c @@ -7,6 +7,10 @@ #include "sls/sls_detector_funcs.h" #include "slsDetectorFunctionList.h" +#include +#include +#include + #if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) || \ defined(MYTHEN3D) #include "Pattern.h" @@ -19,6 +23,8 @@ #include #include +#include + // defined in the detector specific Makefile #ifdef EIGERD const enum detectorType myDetectorType = EIGER; @@ -515,6 +521,8 @@ void function_table() { flist[F_SET_COLLECTION_MODE] = &set_collection_mode; flist[F_GET_PATTERN_WAIT_INTERVAL] = &get_pattern_wait_interval; flist[F_SET_PATTERN_WAIT_INTERVAL] = &set_pattern_wait_interval; + flist[F_SPI_READ] = &spi_read; + flist[F_SPI_WRITE] = &spi_write; // check if (NUM_DET_FUNCTIONS >= RECEIVER_ENUM_START) { LOG(logERROR, ("The last detector function enum has reached its " @@ -11095,4 +11103,305 @@ int set_pattern_wait_interval(int file_des) { #endif return Server_SendResult(file_des, INT64, NULL, 0); +} + +/** + * Non destructive read from SPI register. Read n_bytes by shifting in dummy + * data while keeping csn 0 after the operation. Shift the read out data back + * in to restore the register. + */ + +int spi_read(int file_des){ +#if !defined(XILINX_CHIPTESTBOARDD) + functionNotImplemented(); + return sendError(file_des); +#endif + + int chip_id = 0; + if (receiveData(file_des, &chip_id, sizeof(chip_id), INT32) < 0){ + return printSocketReadError(); + } + if(chip_id < 0 || chip_id > 15){ + sprintf(mess, "Invalid chip_id %d. Must be 0-15\n", chip_id); + return sendError(file_des); + } + + int register_id = 0; + if (receiveData(file_des, ®ister_id, sizeof(register_id), INT32) < 0){ + return printSocketReadError(); + } + if(register_id < 0 || register_id > 15){ + sprintf(mess, "Invalid register_id %d. Must be 0-15\n", register_id); + return sendError(file_des); + } + + int n_bytes = 0; + if (receiveData(file_des, &n_bytes, sizeof(n_bytes), INT32) < 0){ + return printSocketReadError(); + } + if(n_bytes < 1 ){ + sprintf(mess, "Invalid n_bytes %d. Must ask for a read of at least 1 byte\n", n_bytes); + return sendError(file_des); + } + + LOG(logINFO, ("SPI Read Requested: chip_id=%d, register_id=%d, n_bytes=%d\n", + chip_id, register_id, n_bytes)); + + +#ifdef VIRTUAL + // For the virtual detector we create a fake register to read from + // and fill it with 0,2,4,6,... This way we can check that copying + // of the data works as expected + uint8_t *fake_register = malloc(n_bytes); + if(fake_register == NULL){ + LOG(logERROR, ("Could not allocate memory for fake register\n")); + exit(EXIT_FAILURE); + } + for (int i = 0; i < n_bytes; i++) { + fake_register[i] = (uint8_t)( (i*2) % 256 ); + } +#else + int spifd = open("/dev/spidev2.0", O_RDWR); + LOG(logINFO, ("SPI Read: opened spidev2.0 with fd=%d\n", spifd)); + if(spifd < 0){ + sprintf(mess, "Could not open /dev/spidev2.0\n"); + return sendError(file_des); + } +#endif + + // Allocate dummy data to shif in, we keep a copy of this + // to double check that we access a register of the correct size + uint8_t *dummy_data = malloc(n_bytes); + if(dummy_data == NULL){ + LOG(logERROR, ("Could not allocate memory for dummy data\n")); + exit(EXIT_FAILURE); + } + for(int i=0; i 15){ + ret = FAIL; + sprintf(mess, "Invalid chip_id %d. Must be 0-15\n", chip_id); + LOG(logERROR, (mess)); + return Server_SendResult(file_des, INT32, NULL, 0); + } + + int register_id = 0; + if (receiveData(file_des, ®ister_id, sizeof(register_id), INT32) < 0){ + return printSocketReadError(); + } + if(register_id < 0 || register_id > 15){ + ret = FAIL; + sprintf(mess, "Invalid register_id %d. Must be 0-15\n", register_id); + LOG(logERROR, (mess)); + return Server_SendResult(file_des, INT32, NULL, 0); + } + + int n_bytes = 0; + if (receiveData(file_des, &n_bytes, sizeof(n_bytes), INT32) < 0){ + return printSocketReadError(); + } + if(n_bytes < 1 ){ + sprintf(mess, "Invalid n_bytes %d. Must ask for a write of at least 1 byte\n", n_bytes); + return sendError(file_des); + } + + LOG(logINFO, ("SPI Write Requested: chip_id=%d, register_id=%d, n_bytes=%d\n", + chip_id, register_id, n_bytes)); + + uint8_t *data = malloc(n_bytes); + if(data == NULL){ + LOG(logERROR, ("Could not allocate memory for SPI write data\n")); + exit(EXIT_FAILURE); + } + memset(data, 0, n_bytes); + if (receiveData(file_des, data, n_bytes, OTHER) < 0){ + free(data); + return printSocketReadError(); + } + + uint8_t* local_tx = malloc(n_bytes+1); + if(local_tx == NULL){ + LOG(logERROR, ("Could not allocate memory for local_tx\n")); + exit(EXIT_FAILURE); + } + uint8_t* local_rx = malloc(n_bytes+1); + if(local_rx == NULL){ + LOG(logERROR, ("Could not allocate memory for local_rx\n")); + exit(EXIT_FAILURE); + } + + struct spi_ioc_transfer send_cmd[1]; + memset(send_cmd, 0, sizeof(send_cmd)); + send_cmd[0].len = n_bytes+1; + send_cmd[0].tx_buf = (unsigned long) local_tx; + send_cmd[0].rx_buf = (unsigned long) local_rx; + + // 0 - Normal operation, 1 - CSn remains zero after operation + send_cmd[0].cs_change = 0; + local_tx[0] = ((chip_id & 0xF) << 4) | (register_id & 0xF); + for (int i=0; i < n_bytes; i++) + local_tx[i+1] = data[i]; + +#ifdef VIRTUAL + // For the virtual detector we have nothing to do +#else + int spifd = open("/dev/spidev2.0", O_RDWR); + LOG(logINFO, ("SPI Read: opened spidev2.0 with fd=%d\n", spifd)); + if(spifd < 0){ + free(data); + free(local_tx); + free(local_rx); + sprintf(mess, "Could not open /dev/spidev2.0\n"); + return sendError(file_des); + } + if(ioctl(spifd, SPI_IOC_MESSAGE(1), &send_cmd)<0){ + close(spifd); + free(data); + free(local_tx); + free(local_rx); + sprintf(mess, "SPI write failed with %d:%s\n", errno, strerror(errno)); + return sendError(file_des); + } + close(spifd); +#endif + + free(data); + free(local_tx); + free(local_rx); + + ret = OK; + LOG(logDEBUG1, ("SPI Write Complete\n")); + return Server_SendResult(file_des, INT32, NULL, 0); } \ No newline at end of file diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c index 7bed0d759..4c98ea216 100644 --- a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c +++ b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c @@ -1884,4 +1884,4 @@ int getFrequency(enum CLKINDEX ind) { clkFrequency[ind] = XILINX_PLL_getFrequency(ind); #endif return clkFrequency[ind]; -} \ No newline at end of file +} diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h index 1b584109f..8d76bf57b 100644 --- a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h +++ b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h @@ -171,4 +171,4 @@ int getTotalNumberOfChannels(); void getNumberOfChannels(int *nchanx, int *nchany); int getNumberOfChips(); int getNumberOfDACs(); -int getNumberOfChannelsPerChip(); \ No newline at end of file +int getNumberOfChannelsPerChip(); diff --git a/slsDetectorSoftware/include/sls/Detector.h b/slsDetectorSoftware/include/sls/Detector.h index a200c99c7..958e64dc5 100644 --- a/slsDetectorSoftware/include/sls/Detector.h +++ b/slsDetectorSoftware/include/sls/Detector.h @@ -2247,6 +2247,12 @@ class Detector { ///@} + Result> readSpi(int chip_id, int register_id, + int n_bytes, Positions pos = {}) const; + + void writeSpi(int chip_id, int register_id, + const std::vector &data, Positions pos = {}); + private: std::vector getValidPortNumbers(uint16_t start_port); void updateRxRateCorrections(); diff --git a/slsDetectorSoftware/src/Detector.cpp b/slsDetectorSoftware/src/Detector.cpp index 30d387f92..1960fd858 100644 --- a/slsDetectorSoftware/src/Detector.cpp +++ b/slsDetectorSoftware/src/Detector.cpp @@ -2954,4 +2954,19 @@ std::vector Detector::getValidPortNumbers(uint16_t start_port) { return res; } +Result> Detector::readSpi(int chip_id, int register_id, + int n_bytes, + Positions pos) const { + return pimpl->Parallel(&Module::readSpi, pos, chip_id, register_id, + n_bytes); +} + +void Detector::writeSpi(int chip_id, int register_id, + const std::vector &data, Positions pos){ + pimpl->Parallel(&Module::writeSpi, pos, chip_id, register_id, data); + } + + + + } // namespace sls diff --git a/slsDetectorSoftware/src/Module.cpp b/slsDetectorSoftware/src/Module.cpp index 6167f585d..b0dc43211 100644 --- a/slsDetectorSoftware/src/Module.cpp +++ b/slsDetectorSoftware/src/Module.cpp @@ -4074,4 +4074,45 @@ void Module::simulatingActivityinDetector(const std::string &functionType, } printf("\n"); } + +std::vector Module::readSpi(int chip_id, int register_id, + int n_bytes) const{ + auto client = DetectorSocket(shm()->hostname, shm()->controlPort); + client.Send(F_SPI_READ); + client.setFnum(F_SPI_READ); + client.Send(chip_id); + client.Send(register_id); + client.Send(n_bytes); + + if (client.Receive() == FAIL) { + std::ostringstream os; + os << "Module " << moduleIndex << " (" << shm()->hostname << ")" + << " returned error: " << client.readErrorMessage(); + throw DetectorError(os.str()); + } + + std::vector data(n_bytes); + client.Receive(data); + return data; + +} + +void Module::writeSpi(int chip_id, int register_id, + const std::vector &data){ + auto client = DetectorSocket(shm()->hostname, shm()->controlPort); + client.Send(F_SPI_WRITE); + client.setFnum(F_SPI_WRITE); + client.Send(chip_id); + client.Send(register_id); + client.Send(static_cast(data.size())); + client.Send(data); + + if (client.Receive() == FAIL) { + std::ostringstream os; + os << "Module " << moduleIndex << " (" << shm()->hostname << ")" + << " returned error: " << client.readErrorMessage(); + throw DetectorError(os.str()); + } +} + } // namespace sls diff --git a/slsDetectorSoftware/src/Module.h b/slsDetectorSoftware/src/Module.h index d23cc095e..2e4fb5684 100644 --- a/slsDetectorSoftware/src/Module.h +++ b/slsDetectorSoftware/src/Module.h @@ -607,6 +607,10 @@ class Module : public virtual slsDetectorDefs { int64_t getNumberOfFramesFromStart() const; int64_t getActualTime() const; int64_t getMeasurementTime() const; + std::vector readSpi(int chip_id, int register_id, + int n_bytes) const; + + void writeSpi(int chip_id, int register_id, const std::vector &data); private: std::string getReceiverLongVersion() const; diff --git a/slsSupportLib/include/sls/sls_detector_funcs.h b/slsSupportLib/include/sls/sls_detector_funcs.h index 6d49fee7e..9296547de 100755 --- a/slsSupportLib/include/sls/sls_detector_funcs.h +++ b/slsSupportLib/include/sls/sls_detector_funcs.h @@ -299,6 +299,8 @@ enum detFuncs { F_SET_COLLECTION_MODE, F_GET_PATTERN_WAIT_INTERVAL, F_SET_PATTERN_WAIT_INTERVAL, + F_SPI_READ, + F_SPI_WRITE, NUM_DET_FUNCTIONS, RECEIVER_ENUM_START = 512, /**< detector function should not exceed this @@ -709,6 +711,8 @@ const char* getFunctionNameFromEnum(enum detFuncs func) { case F_SET_COLLECTION_MODE: return "F_SET_COLLECTION_MODE"; case F_GET_PATTERN_WAIT_INTERVAL: return "F_GET_PATTERN_WAIT_INTERVAL"; case F_SET_PATTERN_WAIT_INTERVAL: return "F_SET_PATTERN_WAIT_INTERVAL"; + case F_SPI_READ: return "F_SPI_READ"; + case F_SPI_WRITE: return "F_SPI_WRITE"; case NUM_DET_FUNCTIONS: return "NUM_DET_FUNCTIONS"; case RECEIVER_ENUM_START: return "RECEIVER_ENUM_START"; From 92da709bda88548b6602d1050aafde0e87df48b3 Mon Sep 17 00:00:00 2001 From: Alice Date: Fri, 6 Feb 2026 14:39:11 +0100 Subject: [PATCH 13/20] skip if not plotted --- pyctbgui/pyctbgui/services/Signals.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyctbgui/pyctbgui/services/Signals.py b/pyctbgui/pyctbgui/services/Signals.py index 856571e22..107eaa628 100644 --- a/pyctbgui/pyctbgui/services/Signals.py +++ b/pyctbgui/pyctbgui/services/Signals.py @@ -112,7 +112,7 @@ class SignalsTab(QtWidgets.QWidget): bit_index += (8 - (bit_index % 8)) if not isPlottedArray[i]: bit_index += nbitsPerDBit - samples_per_bit[idx, :] = np.nan + samples_per_bit[idx, :] = 0.0 continue for iSample in range(dSamples): # all samples for digital bit together from slsReceiver @@ -130,7 +130,7 @@ class SignalsTab(QtWidgets.QWidget): for idx, i in enumerate(rx_dbitlist): if not isPlottedArray[i]: bit_index += 1 - bits_per_sample[iSample, idx] = np.nan + bits_per_sample[iSample, idx] = 0.0 index = int(bit_index/8) iBit = idx % 8 @@ -161,9 +161,9 @@ class SignalsTab(QtWidgets.QWidget): irow = 0 for idx, i in enumerate(self.rx_dbitlist): # bits enabled but not plotting - waveform = digital_array[idx, :] - if np.isnan(waveform[0]): + if not isPlottedArray[i]: continue + waveform = digital_array[idx, :] self.mainWindow.digitalPlots[i].setData(waveform) plotName = getattr(self.view, f"labelBIT{i}").text() waveforms[plotName] = waveform From 7f5b26743f112a4d24af07999a520fe49fafb523 Mon Sep 17 00:00:00 2001 From: Alice Date: Fri, 6 Feb 2026 14:55:18 +0100 Subject: [PATCH 14/20] uff introduced another bug --- pyctbgui/pyctbgui/services/Signals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyctbgui/pyctbgui/services/Signals.py b/pyctbgui/pyctbgui/services/Signals.py index 107eaa628..a836a41c6 100644 --- a/pyctbgui/pyctbgui/services/Signals.py +++ b/pyctbgui/pyctbgui/services/Signals.py @@ -163,7 +163,7 @@ class SignalsTab(QtWidgets.QWidget): # bits enabled but not plotting if not isPlottedArray[i]: continue - waveform = digital_array[idx, :] + waveform = digital_array[i, :] self.mainWindow.digitalPlots[i].setData(waveform) plotName = getattr(self.view, f"labelBIT{i}").text() waveforms[plotName] = waveform From 78044b2783864d3011fd6db398a95bb5f07ce6d1 Mon Sep 17 00:00:00 2001 From: Alice Date: Fri, 6 Feb 2026 16:41:04 +0100 Subject: [PATCH 15/20] always write all wave data & uncheck plot if digital bit unchecked --- pyctbgui/pyctbgui/services/Signals.py | 48 ++++++++++++--------------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/pyctbgui/pyctbgui/services/Signals.py b/pyctbgui/pyctbgui/services/Signals.py index a836a41c6..c1eec01cb 100644 --- a/pyctbgui/pyctbgui/services/Signals.py +++ b/pyctbgui/pyctbgui/services/Signals.py @@ -36,16 +36,16 @@ class SignalsTab(QtWidgets.QWidget): for i in range(Defines.signals.count): getattr(self.view, f"checkBoxBIT{i}DB").stateChanged.connect(partial(self.setDigitalBitEnable, i)) getattr(self.view, f"checkBoxBIT{i}Out").stateChanged.connect(partial(self.setIOOut, i)) - getattr(self.view, f"checkBoxBIT{i}Plot").stateChanged.connect(partial(self.setEnableBitPlot, i)) + getattr(self.view, f"checkBoxBIT{i}Plot").stateChanged.connect(partial(self.setBitPlot, i)) getattr(self.view, f"pushButtonBIT{i}").clicked.connect(partial(self.selectBitColor, i)) self.view.checkBoxBIT0_31DB.stateChanged.connect( partial(self.setDigitalBitEnableRange, 0, Defines.signals.half)) self.view.checkBoxBIT32_63DB.stateChanged.connect( partial(self.setDigitalBitEnableRange, Defines.signals.half, Defines.signals.count)) - self.view.checkBoxBIT0_31Plot.stateChanged.connect(partial(self.setEnableBitPlotRange, 0, + self.view.checkBoxBIT0_31Plot.stateChanged.connect(partial(self.setBitPlotRange, 0, Defines.signals.half)) self.view.checkBoxBIT32_63Plot.stateChanged.connect( - partial(self.setEnableBitPlotRange, Defines.signals.half, Defines.signals.count)) + partial(self.setBitPlotRange, Defines.signals.half, Defines.signals.count)) self.view.checkBoxBIT0_31Out.stateChanged.connect(partial(self.setIOOutRange, 0, Defines.signals.half)) self.view.checkBoxBIT32_63Out.stateChanged.connect( partial(self.setIOOutRange, Defines.signals.half, Defines.signals.count)) @@ -90,7 +90,7 @@ class SignalsTab(QtWidgets.QWidget): self.legend.addItem(plot, name) @recordOrApplyPedestal - def _processWaveformData(self, data, aSamples, dSamples, rx_dbitreorder, rx_dbitlist, isPlottedArray, romode, + def _processWaveformData(self, data, aSamples, dSamples, rx_dbitreorder, rx_dbitlist, romode, nADCEnabled): #transform raw waveform data into a processed numpy array @@ -110,10 +110,7 @@ class SignalsTab(QtWidgets.QWidget): # where numbits * numsamples is not a multiple of 8 if bit_index % 8 != 0: bit_index += (8 - (bit_index % 8)) - if not isPlottedArray[i]: - bit_index += nbitsPerDBit - samples_per_bit[idx, :] = 0.0 - continue + for iSample in range(dSamples): # all samples for digital bit together from slsReceiver index = int(bit_index / 8) @@ -128,10 +125,6 @@ class SignalsTab(QtWidgets.QWidget): for iSample in range(dSamples): bit_index = nbitsPerSample * iSample for idx, i in enumerate(rx_dbitlist): - if not isPlottedArray[i]: - bit_index += 1 - bits_per_sample[iSample, idx] = 0.0 - index = int(bit_index/8) iBit = idx % 8 bit = (digital_array[index] >> iBit) & 1 @@ -154,16 +147,13 @@ class SignalsTab(QtWidgets.QWidget): waveforms = {} isPlottedArray = {i: getattr(self.view, f"checkBoxBIT{i}Plot").isChecked() for i in self.rx_dbitlist} - digital_array = self._processWaveformData(data, aSamples, dSamples, self.rx_dbitreorder, self.rx_dbitlist, isPlottedArray, + digital_array = self._processWaveformData(data, aSamples, dSamples, self.rx_dbitreorder, self.rx_dbitlist, self.mainWindow.romode.value, self.mainWindow.nADCEnabled) irow = 0 for idx, i in enumerate(self.rx_dbitlist): - # bits enabled but not plotting - if not isPlottedArray[i]: - continue - waveform = digital_array[i, :] + waveform = digital_array[idx, :] self.mainWindow.digitalPlots[i].setData(waveform) plotName = getattr(self.view, f"labelBIT{i}").text() waveforms[plotName] = waveform @@ -215,11 +205,11 @@ class SignalsTab(QtWidgets.QWidget): self.mainWindow.nDBitEnabled = len(list(retval)) for i in range(Defines.signals.count): self.getDigitalBitEnable(i, retval) - self.getEnableBitPlot(i) + self.EnableBitPlot(i) self.getEnableBitColor(i) self.plotTab.addSelectedDigitalPlots(i) self.getDigitalBitEnableRange(retval) - self.getEnableBitPlotRange() + self.EnableBitPlotRange() def setDigitalBitEnable(self, i): bitList = self.det.rx_dbitlist @@ -257,21 +247,27 @@ class SignalsTab(QtWidgets.QWidget): self.updateDigitalBitEnable() - def getEnableBitPlot(self, i): + def EnableBitPlot(self, i): + """ enables plot check box if bit is enabled, otherwise unchecks and disables plot check box """ + checkBox = getattr(self.view, f"checkBoxBIT{i}DB") checkBoxPlot = getattr(self.view, f"checkBoxBIT{i}Plot") + if(checkBoxPlot.isChecked()): + checkBoxPlot.setChecked(checkBox.isChecked()) + checkBoxPlot.setEnabled(checkBox.isChecked()) - def setEnableBitPlot(self, i): + def setBitPlot(self, i): + """ sets plot check box e.g. adds plots to plot tab """ pushButton = getattr(self.view, f"pushButtonBIT{i}") checkBox = getattr(self.view, f"checkBoxBIT{i}Plot") pushButton.setEnabled(checkBox.isChecked()) - self.getEnableBitPlotRange() + self.EnableBitPlotRange() self.plotTab.addSelectedDigitalPlots(i) self.updateLegend() - def getEnableBitPlotRange(self): + def EnableBitPlotRange(self): self.view.checkBoxBIT0_31Plot.stateChanged.disconnect() self.view.checkBoxBIT32_63Plot.stateChanged.disconnect() self.view.checkBoxBIT0_31Plot.setEnabled( @@ -286,12 +282,12 @@ class SignalsTab(QtWidgets.QWidget): all( getattr(self.view, f"checkBoxBIT{i}Plot").isChecked() for i in range(Defines.signals.half, Defines.signals.count))) - self.view.checkBoxBIT0_31Plot.stateChanged.connect(partial(self.setEnableBitPlotRange, 0, + self.view.checkBoxBIT0_31Plot.stateChanged.connect(partial(self.setBitPlotRange, 0, Defines.signals.half)) self.view.checkBoxBIT32_63Plot.stateChanged.connect( - partial(self.setEnableBitPlotRange, Defines.signals.half, Defines.signals.count)) + partial(self.setBitPlotRange, Defines.signals.half, Defines.signals.count)) - def setEnableBitPlotRange(self, start_nr, end_nr): + def setBitPlotRange(self, start_nr, end_nr): checkBox = getattr(self.view, f"checkBoxBIT{start_nr}_{end_nr - 1}Plot") enable = checkBox.isChecked() for i in range(start_nr, end_nr): From ec6a8b6d66e7498f942dbda210ff318b96a7ce4f Mon Sep 17 00:00:00 2001 From: Alice Date: Mon, 9 Feb 2026 12:42:35 +0100 Subject: [PATCH 16/20] added range update --- pyctbgui/pyctbgui/services/Signals.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pyctbgui/pyctbgui/services/Signals.py b/pyctbgui/pyctbgui/services/Signals.py index c1eec01cb..459a4bddc 100644 --- a/pyctbgui/pyctbgui/services/Signals.py +++ b/pyctbgui/pyctbgui/services/Signals.py @@ -89,6 +89,11 @@ class SignalsTab(QtWidgets.QWidget): for plot, name in self.getEnabledPlots(): self.legend.addItem(plot, name) + def updatePlotRange(self): + vb = self.mainWindow.plotDigitalWaveform.getViewBox() + vb.enableAutoRange(enable=True) # Enable auto-range + vb.updateAutoRange() # Force immediate update + @recordOrApplyPedestal def _processWaveformData(self, data, aSamples, dSamples, rx_dbitreorder, rx_dbitlist, romode, nADCEnabled): @@ -145,7 +150,6 @@ class SignalsTab(QtWidgets.QWidget): self.refresh() waveforms = {} - isPlottedArray = {i: getattr(self.view, f"checkBoxBIT{i}Plot").isChecked() for i in self.rx_dbitlist} digital_array = self._processWaveformData(data, aSamples, dSamples, self.rx_dbitreorder, self.rx_dbitlist, self.mainWindow.romode.value, @@ -163,7 +167,9 @@ class SignalsTab(QtWidgets.QWidget): irow += 1 else: self.mainWindow.digitalPlots[i].setY(0) - + + self.updatePlotRange() # Call after all data is set + return waveforms @@ -266,6 +272,8 @@ class SignalsTab(QtWidgets.QWidget): self.EnableBitPlotRange() self.plotTab.addSelectedDigitalPlots(i) self.updateLegend() + self.updatePlotRange() + def EnableBitPlotRange(self): self.view.checkBoxBIT0_31Plot.stateChanged.disconnect() From 3f4df445f1ad4f62b15ea3a41055cbfe92d91099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Erik=20Fr=C3=B6jdh?= Date: Mon, 9 Feb 2026 13:50:35 +0100 Subject: [PATCH 17/20] send back the result of the SPI write (#1387) --- python/src/detector.cpp | 4 ++-- .../src/slsDetectorServer_funcs.c | 15 ++++++++++----- slsDetectorSoftware/include/sls/Detector.h | 2 +- slsDetectorSoftware/src/Detector.cpp | 4 ++-- slsDetectorSoftware/src/Module.cpp | 7 ++++++- slsDetectorSoftware/src/Module.h | 2 +- 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/python/src/detector.cpp b/python/src/detector.cpp index d3e6be8b3..7bad555c0 100644 --- a/python/src/detector.cpp +++ b/python/src/detector.cpp @@ -2206,8 +2206,8 @@ void init_det(py::module &m) { py::arg() = Positions{}); CppDetectorApi.def( "writeSpi", - (void (Detector::*)(int, int, const std::vector &, - sls::Positions)) & + (Result>(Detector::*)( + int, int, const std::vector &, sls::Positions)) & Detector::writeSpi, py::arg(), py::arg(), py::arg(), py::arg() = Positions{}); ; diff --git a/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c b/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c index 174af28a8..49427f02f 100644 --- a/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c +++ b/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c @@ -11375,7 +11375,10 @@ int spi_write(int file_des){ local_tx[i+1] = data[i]; #ifdef VIRTUAL - // For the virtual detector we have nothing to do + // For the virtual detector copy the data from local_tx to local_rx + for (int i=0; i < n_bytes+1; i++){ + local_rx[i] = local_tx[i]; + } #else int spifd = open("/dev/spidev2.0", O_RDWR); LOG(logINFO, ("SPI Read: opened spidev2.0 with fd=%d\n", spifd)); @@ -11397,11 +11400,13 @@ int spi_write(int file_des){ close(spifd); #endif + ret = OK; + LOG(logDEBUG1, ("SPI Write Complete\n")); + Server_SendResult(file_des, INT32, NULL, 0); + sendData(file_des, local_rx+1, n_bytes, OTHER); + free(data); free(local_tx); free(local_rx); - - ret = OK; - LOG(logDEBUG1, ("SPI Write Complete\n")); - return Server_SendResult(file_des, INT32, NULL, 0); + return ret; } \ No newline at end of file diff --git a/slsDetectorSoftware/include/sls/Detector.h b/slsDetectorSoftware/include/sls/Detector.h index 958e64dc5..fb7c32b29 100644 --- a/slsDetectorSoftware/include/sls/Detector.h +++ b/slsDetectorSoftware/include/sls/Detector.h @@ -2250,7 +2250,7 @@ class Detector { Result> readSpi(int chip_id, int register_id, int n_bytes, Positions pos = {}) const; - void writeSpi(int chip_id, int register_id, + Result> writeSpi(int chip_id, int register_id, const std::vector &data, Positions pos = {}); private: diff --git a/slsDetectorSoftware/src/Detector.cpp b/slsDetectorSoftware/src/Detector.cpp index 1960fd858..619619e09 100644 --- a/slsDetectorSoftware/src/Detector.cpp +++ b/slsDetectorSoftware/src/Detector.cpp @@ -2961,9 +2961,9 @@ Result> Detector::readSpi(int chip_id, int register_id, n_bytes); } -void Detector::writeSpi(int chip_id, int register_id, +Result> Detector::writeSpi(int chip_id, int register_id, const std::vector &data, Positions pos){ - pimpl->Parallel(&Module::writeSpi, pos, chip_id, register_id, data); + return pimpl->Parallel(&Module::writeSpi, pos, chip_id, register_id, data); } diff --git a/slsDetectorSoftware/src/Module.cpp b/slsDetectorSoftware/src/Module.cpp index b0dc43211..216a491fd 100644 --- a/slsDetectorSoftware/src/Module.cpp +++ b/slsDetectorSoftware/src/Module.cpp @@ -4097,7 +4097,7 @@ std::vector Module::readSpi(int chip_id, int register_id, } -void Module::writeSpi(int chip_id, int register_id, +std::vector Module::writeSpi(int chip_id, int register_id, const std::vector &data){ auto client = DetectorSocket(shm()->hostname, shm()->controlPort); client.Send(F_SPI_WRITE); @@ -4113,6 +4113,11 @@ void Module::writeSpi(int chip_id, int register_id, << " returned error: " << client.readErrorMessage(); throw DetectorError(os.str()); } + + // Read the output from the SPI write. This contains the data before the write. + std::vector ret(data.size()); + client.Receive(ret); + return ret; } } // namespace sls diff --git a/slsDetectorSoftware/src/Module.h b/slsDetectorSoftware/src/Module.h index 2e4fb5684..cc692cdef 100644 --- a/slsDetectorSoftware/src/Module.h +++ b/slsDetectorSoftware/src/Module.h @@ -610,7 +610,7 @@ class Module : public virtual slsDetectorDefs { std::vector readSpi(int chip_id, int register_id, int n_bytes) const; - void writeSpi(int chip_id, int register_id, const std::vector &data); + std::vector writeSpi(int chip_id, int register_id, const std::vector &data); private: std::string getReceiverLongVersion() const; From 1c44a66964bd0d129f531ac793e60ce817a5c113 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Tue, 10 Feb 2026 16:10:51 +0100 Subject: [PATCH 18/20] formatted --- .../src/slsDetectorServer_funcs.c | 173 +++++++++--------- slsDetectorSoftware/include/sls/Detector.h | 5 +- slsDetectorSoftware/src/Detector.cpp | 10 +- slsDetectorSoftware/src/Module.cpp | 10 +- slsDetectorSoftware/src/Module.h | 5 +- 5 files changed, 104 insertions(+), 99 deletions(-) diff --git a/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c b/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c index 49427f02f..af0f205ea 100644 --- a/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c +++ b/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c @@ -7,9 +7,9 @@ #include "sls/sls_detector_funcs.h" #include "slsDetectorFunctionList.h" +#include #include #include -#include #if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) || \ defined(MYTHEN3D) @@ -11108,175 +11108,180 @@ int set_pattern_wait_interval(int file_des) { /** * Non destructive read from SPI register. Read n_bytes by shifting in dummy * data while keeping csn 0 after the operation. Shift the read out data back - * in to restore the register. + * in to restore the register. */ -int spi_read(int file_des){ +int spi_read(int file_des) { #if !defined(XILINX_CHIPTESTBOARDD) functionNotImplemented(); return sendError(file_des); #endif int chip_id = 0; - if (receiveData(file_des, &chip_id, sizeof(chip_id), INT32) < 0){ + if (receiveData(file_des, &chip_id, sizeof(chip_id), INT32) < 0) { return printSocketReadError(); } - if(chip_id < 0 || chip_id > 15){ + if (chip_id < 0 || chip_id > 15) { sprintf(mess, "Invalid chip_id %d. Must be 0-15\n", chip_id); return sendError(file_des); } int register_id = 0; - if (receiveData(file_des, ®ister_id, sizeof(register_id), INT32) < 0){ + if (receiveData(file_des, ®ister_id, sizeof(register_id), INT32) < 0) { return printSocketReadError(); } - if(register_id < 0 || register_id > 15){ + if (register_id < 0 || register_id > 15) { sprintf(mess, "Invalid register_id %d. Must be 0-15\n", register_id); return sendError(file_des); } int n_bytes = 0; - if (receiveData(file_des, &n_bytes, sizeof(n_bytes), INT32) < 0){ + if (receiveData(file_des, &n_bytes, sizeof(n_bytes), INT32) < 0) { return printSocketReadError(); } - if(n_bytes < 1 ){ - sprintf(mess, "Invalid n_bytes %d. Must ask for a read of at least 1 byte\n", n_bytes); + if (n_bytes < 1) { + sprintf(mess, + "Invalid n_bytes %d. Must ask for a read of at least 1 byte\n", + n_bytes); return sendError(file_des); } - LOG(logINFO, ("SPI Read Requested: chip_id=%d, register_id=%d, n_bytes=%d\n", - chip_id, register_id, n_bytes)); - + LOG(logINFO, + ("SPI Read Requested: chip_id=%d, register_id=%d, n_bytes=%d\n", + chip_id, register_id, n_bytes)); #ifdef VIRTUAL // For the virtual detector we create a fake register to read from // and fill it with 0,2,4,6,... This way we can check that copying // of the data works as expected uint8_t *fake_register = malloc(n_bytes); - if(fake_register == NULL){ + if (fake_register == NULL) { LOG(logERROR, ("Could not allocate memory for fake register\n")); exit(EXIT_FAILURE); } for (int i = 0; i < n_bytes; i++) { - fake_register[i] = (uint8_t)( (i*2) % 256 ); + fake_register[i] = (uint8_t)((i * 2) % 256); } #else int spifd = open("/dev/spidev2.0", O_RDWR); LOG(logINFO, ("SPI Read: opened spidev2.0 with fd=%d\n", spifd)); - if(spifd < 0){ + if (spifd < 0) { sprintf(mess, "Could not open /dev/spidev2.0\n"); return sendError(file_des); } #endif - // Allocate dummy data to shif in, we keep a copy of this + // Allocate dummy data to shif in, we keep a copy of this // to double check that we access a register of the correct size uint8_t *dummy_data = malloc(n_bytes); - if(dummy_data == NULL){ + if (dummy_data == NULL) { LOG(logERROR, ("Could not allocate memory for dummy data\n")); exit(EXIT_FAILURE); } - for(int i=0; i 15){ + if (chip_id < 0 || chip_id > 15) { ret = FAIL; sprintf(mess, "Invalid chip_id %d. Must be 0-15\n", chip_id); LOG(logERROR, (mess)); @@ -11318,10 +11320,10 @@ int spi_write(int file_des){ } int register_id = 0; - if (receiveData(file_des, ®ister_id, sizeof(register_id), INT32) < 0){ + if (receiveData(file_des, ®ister_id, sizeof(register_id), INT32) < 0) { return printSocketReadError(); } - if(register_id < 0 || register_id > 15){ + if (register_id < 0 || register_id > 15) { ret = FAIL; sprintf(mess, "Invalid register_id %d. Must be 0-15\n", register_id); LOG(logERROR, (mess)); @@ -11329,67 +11331,70 @@ int spi_write(int file_des){ } int n_bytes = 0; - if (receiveData(file_des, &n_bytes, sizeof(n_bytes), INT32) < 0){ + if (receiveData(file_des, &n_bytes, sizeof(n_bytes), INT32) < 0) { return printSocketReadError(); } - if(n_bytes < 1 ){ - sprintf(mess, "Invalid n_bytes %d. Must ask for a write of at least 1 byte\n", n_bytes); + if (n_bytes < 1) { + sprintf(mess, + "Invalid n_bytes %d. Must ask for a write of at least 1 byte\n", + n_bytes); return sendError(file_des); } - LOG(logINFO, ("SPI Write Requested: chip_id=%d, register_id=%d, n_bytes=%d\n", - chip_id, register_id, n_bytes)); - + LOG(logINFO, + ("SPI Write Requested: chip_id=%d, register_id=%d, n_bytes=%d\n", + chip_id, register_id, n_bytes)); + uint8_t *data = malloc(n_bytes); - if(data == NULL){ + if (data == NULL) { LOG(logERROR, ("Could not allocate memory for SPI write data\n")); exit(EXIT_FAILURE); } memset(data, 0, n_bytes); - if (receiveData(file_des, data, n_bytes, OTHER) < 0){ + if (receiveData(file_des, data, n_bytes, OTHER) < 0) { free(data); return printSocketReadError(); } - uint8_t* local_tx = malloc(n_bytes+1); - if(local_tx == NULL){ + uint8_t *local_tx = malloc(n_bytes + 1); + if (local_tx == NULL) { LOG(logERROR, ("Could not allocate memory for local_tx\n")); exit(EXIT_FAILURE); } - uint8_t* local_rx = malloc(n_bytes+1); - if(local_rx == NULL){ + uint8_t *local_rx = malloc(n_bytes + 1); + if (local_rx == NULL) { LOG(logERROR, ("Could not allocate memory for local_rx\n")); exit(EXIT_FAILURE); } struct spi_ioc_transfer send_cmd[1]; memset(send_cmd, 0, sizeof(send_cmd)); - send_cmd[0].len = n_bytes+1; - send_cmd[0].tx_buf = (unsigned long) local_tx; - send_cmd[0].rx_buf = (unsigned long) local_rx; + send_cmd[0].len = n_bytes + 1; + send_cmd[0].tx_buf = (unsigned long)local_tx; + send_cmd[0].rx_buf = (unsigned long)local_rx; // 0 - Normal operation, 1 - CSn remains zero after operation - send_cmd[0].cs_change = 0; + send_cmd[0].cs_change = 0; local_tx[0] = ((chip_id & 0xF) << 4) | (register_id & 0xF); - for (int i=0; i < n_bytes; i++) - local_tx[i+1] = data[i]; + for (int i = 0; i < n_bytes; i++) + local_tx[i + 1] = data[i]; #ifdef VIRTUAL // For the virtual detector copy the data from local_tx to local_rx - for (int i=0; i < n_bytes+1; i++){ + for (int i = 0; i < n_bytes + 1; i++) { local_rx[i] = local_tx[i]; } #else int spifd = open("/dev/spidev2.0", O_RDWR); LOG(logINFO, ("SPI Read: opened spidev2.0 with fd=%d\n", spifd)); - if(spifd < 0){ + if (spifd < 0) { free(data); free(local_tx); free(local_rx); sprintf(mess, "Could not open /dev/spidev2.0\n"); return sendError(file_des); } - if(ioctl(spifd, SPI_IOC_MESSAGE(1), &send_cmd)<0){ + if (ioctl(spifd, SPI_IOC_MESSAGE(1), &send_cmd) < 0) { close(spifd); free(data); free(local_tx); @@ -11403,7 +11408,7 @@ int spi_write(int file_des){ ret = OK; LOG(logDEBUG1, ("SPI Write Complete\n")); Server_SendResult(file_des, INT32, NULL, 0); - sendData(file_des, local_rx+1, n_bytes, OTHER); + sendData(file_des, local_rx + 1, n_bytes, OTHER); free(data); free(local_tx); diff --git a/slsDetectorSoftware/include/sls/Detector.h b/slsDetectorSoftware/include/sls/Detector.h index fb7c32b29..c4d5b670f 100644 --- a/slsDetectorSoftware/include/sls/Detector.h +++ b/slsDetectorSoftware/include/sls/Detector.h @@ -2248,10 +2248,11 @@ class Detector { ///@} Result> readSpi(int chip_id, int register_id, - int n_bytes, Positions pos = {}) const; + int n_bytes, Positions pos = {}) const; Result> writeSpi(int chip_id, int register_id, - const std::vector &data, Positions pos = {}); + const std::vector &data, + Positions pos = {}); private: std::vector getValidPortNumbers(uint16_t start_port); diff --git a/slsDetectorSoftware/src/Detector.cpp b/slsDetectorSoftware/src/Detector.cpp index 619619e09..39706ec56 100644 --- a/slsDetectorSoftware/src/Detector.cpp +++ b/slsDetectorSoftware/src/Detector.cpp @@ -2961,12 +2961,10 @@ Result> Detector::readSpi(int chip_id, int register_id, n_bytes); } -Result> Detector::writeSpi(int chip_id, int register_id, - const std::vector &data, Positions pos){ +Result> +Detector::writeSpi(int chip_id, int register_id, + const std::vector &data, Positions pos) { return pimpl->Parallel(&Module::writeSpi, pos, chip_id, register_id, data); - } - - - +} } // namespace sls diff --git a/slsDetectorSoftware/src/Module.cpp b/slsDetectorSoftware/src/Module.cpp index 216a491fd..8a97afb89 100644 --- a/slsDetectorSoftware/src/Module.cpp +++ b/slsDetectorSoftware/src/Module.cpp @@ -4076,7 +4076,7 @@ void Module::simulatingActivityinDetector(const std::string &functionType, } std::vector Module::readSpi(int chip_id, int register_id, - int n_bytes) const{ + int n_bytes) const { auto client = DetectorSocket(shm()->hostname, shm()->controlPort); client.Send(F_SPI_READ); client.setFnum(F_SPI_READ); @@ -4084,7 +4084,7 @@ std::vector Module::readSpi(int chip_id, int register_id, client.Send(register_id); client.Send(n_bytes); - if (client.Receive() == FAIL) { + if (client.Receive() == FAIL) { std::ostringstream os; os << "Module " << moduleIndex << " (" << shm()->hostname << ")" << " returned error: " << client.readErrorMessage(); @@ -4094,11 +4094,10 @@ std::vector Module::readSpi(int chip_id, int register_id, std::vector data(n_bytes); client.Receive(data); return data; - } std::vector Module::writeSpi(int chip_id, int register_id, - const std::vector &data){ + const std::vector &data) { auto client = DetectorSocket(shm()->hostname, shm()->controlPort); client.Send(F_SPI_WRITE); client.setFnum(F_SPI_WRITE); @@ -4114,7 +4113,8 @@ std::vector Module::writeSpi(int chip_id, int register_id, throw DetectorError(os.str()); } - // Read the output from the SPI write. This contains the data before the write. + // Read the output from the SPI write. This contains the data before the + // write. std::vector ret(data.size()); client.Receive(ret); return ret; diff --git a/slsDetectorSoftware/src/Module.h b/slsDetectorSoftware/src/Module.h index cc692cdef..9f75f1765 100644 --- a/slsDetectorSoftware/src/Module.h +++ b/slsDetectorSoftware/src/Module.h @@ -608,9 +608,10 @@ class Module : public virtual slsDetectorDefs { int64_t getActualTime() const; int64_t getMeasurementTime() const; std::vector readSpi(int chip_id, int register_id, - int n_bytes) const; + int n_bytes) const; - std::vector writeSpi(int chip_id, int register_id, const std::vector &data); + std::vector writeSpi(int chip_id, int register_id, + const std::vector &data); private: std::string getReceiverLongVersion() const; From 26729b06cbd7f36e2a3923de5015db35e4ace9b0 Mon Sep 17 00:00:00 2001 From: Martin Mueller Date: Tue, 10 Feb 2026 16:14:01 +0100 Subject: [PATCH 19/20] added copy of ctbDetectorServer to auto-deploy --- .gitea/workflows/rh8-local.yml | 3 ++- .gitea/workflows/rh9-local.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitea/workflows/rh8-local.yml b/.gitea/workflows/rh8-local.yml index 02067f5bb..b129e2768 100644 --- a/.gitea/workflows/rh8-local.yml +++ b/.gitea/workflows/rh8-local.yml @@ -30,4 +30,5 @@ jobs: if: gitea.ref == 'refs/heads/developer' run: | sftp -r gitea_runner@mpc2935:/slsDetectorSoftware/RH8 <<< $'put build/bin' - sftp -r gitea_runner@mpc2935:/slsDetectorSoftware/RH8 <<< $'put pyctbgui' \ No newline at end of file + sftp -r gitea_runner@mpc2935:/slsDetectorSoftware/RH8 <<< $'put pyctbgui' + sftp -r gitea_runner@mpc2935:/slsDetectorSoftware/RH8 <<< $'put slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer' \ No newline at end of file diff --git a/.gitea/workflows/rh9-local.yml b/.gitea/workflows/rh9-local.yml index c282e4228..817fa11af 100644 --- a/.gitea/workflows/rh9-local.yml +++ b/.gitea/workflows/rh9-local.yml @@ -27,4 +27,5 @@ jobs: if: gitea.ref == 'refs/heads/developer' run: | sftp -r gitea_runner@mpc2935:/slsDetectorSoftware/RH9 <<< $'put build/bin' - sftp -r gitea_runner@mpc2935:/slsDetectorSoftware/RH9 <<< $'put pyctbgui' \ No newline at end of file + sftp -r gitea_runner@mpc2935:/slsDetectorSoftware/RH9 <<< $'put pyctbgui' + sftp -r gitea_runner@mpc2935:/slsDetectorSoftware/RH9 <<< $'put slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer' \ No newline at end of file From 65c8f2c7d8840319c372739f3776820fe319016f Mon Sep 17 00:00:00 2001 From: AliceMazzoleni99 Date: Tue, 17 Feb 2026 17:11:33 +0100 Subject: [PATCH 20/20] dev/handes trailing / in filepath (#1391) * member filePath in Implementation is std::filesystem::path, some refactoring * PR Review * adapted function signature - compiled with hdf5 on --- slsReceiverSoftware/src/BinaryDataFile.cpp | 18 ++++---- slsReceiverSoftware/src/BinaryDataFile.h | 4 +- slsReceiverSoftware/src/DataProcessor.cpp | 11 +++-- slsReceiverSoftware/src/DataProcessor.h | 8 ++-- slsReceiverSoftware/src/File.h | 13 +++--- slsReceiverSoftware/src/HDF5DataFile.cpp | 13 ++++-- slsReceiverSoftware/src/HDF5DataFile.h | 7 +-- slsReceiverSoftware/src/Implementation.cpp | 45 ++++++++++-------- slsReceiverSoftware/src/Implementation.h | 3 +- slsReceiverSoftware/src/MasterFileUtility.cpp | 46 ++++++++++--------- slsReceiverSoftware/src/MasterFileUtility.h | 7 +-- 11 files changed, 100 insertions(+), 75 deletions(-) diff --git a/slsReceiverSoftware/src/BinaryDataFile.cpp b/slsReceiverSoftware/src/BinaryDataFile.cpp index 4f5b90f4d..bc8438277 100644 --- a/slsReceiverSoftware/src/BinaryDataFile.cpp +++ b/slsReceiverSoftware/src/BinaryDataFile.cpp @@ -19,16 +19,15 @@ void BinaryDataFile::CloseFile() { fd = nullptr; } -void BinaryDataFile::CreateFirstBinaryDataFile(const std::string &fNamePrefix, - const uint64_t fIndex, - const bool ovEnable, - const bool sMode, - const uint16_t uPortNumber, - const uint32_t mFramesPerFile) { +void BinaryDataFile::CreateFirstBinaryDataFile( + const std::filesystem::path &filePath, const std::string &fNamePrefix, + const uint64_t fIndex, const bool ovEnable, const bool sMode, + const uint16_t uPortNumber, const uint32_t mFramesPerFile) { subFileIndex = 0; numFramesInFile = 0; + m_filePath = filePath; fileNamePrefix = fNamePrefix; fileIndex = fIndex; overWriteEnable = ovEnable; @@ -42,9 +41,10 @@ void BinaryDataFile::CreateFirstBinaryDataFile(const std::string &fNamePrefix, void BinaryDataFile::CreateFile() { numFramesInFile = 0; - std::ostringstream os; - os << fileNamePrefix << "_f" << subFileIndex << '_' << fileIndex << ".raw"; - fileName = os.str(); + std::filesystem::path p = + m_filePath / (fileNamePrefix + "_f" + std::to_string(subFileIndex) + + '_' + std::to_string(fileIndex) + ".raw"); + fileName = p.string(); if (!overWriteEnable) { if (nullptr == (fd = fopen(fileName.c_str(), "wx"))) { diff --git a/slsReceiverSoftware/src/BinaryDataFile.h b/slsReceiverSoftware/src/BinaryDataFile.h index 79daffa61..5fc43f4cb 100644 --- a/slsReceiverSoftware/src/BinaryDataFile.h +++ b/slsReceiverSoftware/src/BinaryDataFile.h @@ -14,7 +14,8 @@ class BinaryDataFile : private virtual slsDetectorDefs, public File { fileFormat GetFileFormat() const override; void CloseFile() override; - void CreateFirstBinaryDataFile(const std::string &fNamePrefix, + void CreateFirstBinaryDataFile(const std::filesystem::path &filePath, + const std::string &fNamePrefix, const uint64_t fIndex, const bool ovEnable, const bool sMode, const uint16_t uPortNumber, const uint32_t mFramesPerFile) override; @@ -29,6 +30,7 @@ class BinaryDataFile : private virtual slsDetectorDefs, public File { uint32_t index; FILE *fd{nullptr}; std::string fileName; + std::filesystem::path m_filePath; uint32_t numFramesInFile{0}; uint32_t subFileIndex{0}; diff --git a/slsReceiverSoftware/src/DataProcessor.cpp b/slsReceiverSoftware/src/DataProcessor.cpp index 8631af36c..e6303c188 100644 --- a/slsReceiverSoftware/src/DataProcessor.cpp +++ b/slsReceiverSoftware/src/DataProcessor.cpp @@ -148,7 +148,8 @@ void DataProcessor::SetupFileWriter(const bool filewriteEnable, } } -void DataProcessor::CreateFirstFiles(const std::string &fileNamePrefix, +void DataProcessor::CreateFirstFiles(const std::filesystem::path &filePath, + const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, @@ -178,14 +179,14 @@ void DataProcessor::CreateFirstFiles(const std::string &fileNamePrefix, #ifdef HDF5C case HDF5: dataFile->CreateFirstHDF5DataFile( - fileNamePrefix, fileIndex, overWriteEnable, silentMode, + filePath, fileNamePrefix, fileIndex, overWriteEnable, silentMode, udpPortNumber, generalData->framesPerFile, nTotalFrames, nx, ny, generalData->dynamicRange); break; #endif case BINARY: dataFile->CreateFirstBinaryDataFile( - fileNamePrefix, fileIndex, overWriteEnable, silentMode, + filePath, fileNamePrefix, fileIndex, overWriteEnable, silentMode, udpPortNumber, generalData->framesPerFile); break; default: @@ -203,7 +204,7 @@ uint32_t DataProcessor::GetFilesInAcquisition() const { } std::string DataProcessor::CreateVirtualFile( - const std::string &filePath, const std::string &fileNamePrefix, + const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, const int modulePos, const int numModX, const int numModY, std::mutex *hdf5LibMutex, bool gotthard25um) { @@ -247,7 +248,7 @@ void DataProcessor::LinkFileInMaster(const std::string &masterFileName, #endif std::string DataProcessor::CreateMasterFile( - const std::string &filePath, const std::string &fileNamePrefix, + const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, bool silentMode, const fileFormat fileFormatType, MasterAttributes *attr, std::mutex *hdf5LibMutex) { diff --git a/slsReceiverSoftware/src/DataProcessor.h b/slsReceiverSoftware/src/DataProcessor.h index dfed87819..9aef85445 100644 --- a/slsReceiverSoftware/src/DataProcessor.h +++ b/slsReceiverSoftware/src/DataProcessor.h @@ -15,6 +15,7 @@ #include "receiver_defs.h" #include +#include #include #include @@ -59,12 +60,13 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { const fileFormat fileFormatType, std::mutex *hdf5LibMutex); - void CreateFirstFiles(const std::string &fileNamePrefix, + void CreateFirstFiles(const std::filesystem::path &filePath, + const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, const bool detectorDataStream); #ifdef HDF5C uint32_t GetFilesInAcquisition() const; - std::string CreateVirtualFile(const std::string &filePath, + std::string CreateVirtualFile(const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, @@ -76,7 +78,7 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { const bool silentMode, std::mutex *hdf5LibMutex); #endif - std::string CreateMasterFile(const std::string &filePath, + std::string CreateMasterFile(const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, bool silentMode, diff --git a/slsReceiverSoftware/src/File.h b/slsReceiverSoftware/src/File.h index c30a86238..18bc32496 100644 --- a/slsReceiverSoftware/src/File.h +++ b/slsReceiverSoftware/src/File.h @@ -6,6 +6,7 @@ #include "sls/sls_detector_defs.h" #include +#include #ifdef HDF5C #include "H5Cpp.h" @@ -56,6 +57,7 @@ class File : private virtual slsDetectorDefs { }; virtual void CreateFirstHDF5DataFile( + const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, const uint16_t udpPortNumber, const uint32_t maxFramesPerFile, @@ -66,12 +68,11 @@ class File : private virtual slsDetectorDefs { "should be overloaded by a derived class"; }; #endif - virtual void CreateFirstBinaryDataFile(const std::string &fileNamePrefix, - const uint64_t fileIndex, - const bool overWriteEnable, - const bool silentMode, - const uint16_t udpPortNumber, - const uint32_t maxFramesPerFile) { + virtual void CreateFirstBinaryDataFile( + const std::filesystem::path &filePath, + const std::string &fileNamePrefix, const uint64_t fileIndex, + const bool overWriteEnable, const bool silentMode, + const uint16_t udpPortNumber, const uint32_t maxFramesPerFile) { LOG(logERROR) << "This is a generic function CreateFirstBinaryDataFile that " "should be overloaded by a derived class"; diff --git a/slsReceiverSoftware/src/HDF5DataFile.cpp b/slsReceiverSoftware/src/HDF5DataFile.cpp index 2b267f474..8dc2f4598 100644 --- a/slsReceiverSoftware/src/HDF5DataFile.cpp +++ b/slsReceiverSoftware/src/HDF5DataFile.cpp @@ -87,8 +87,9 @@ void HDF5DataFile::CloseFile() { } void HDF5DataFile::CreateFirstHDF5DataFile( - const std::string &fNamePrefix, const uint64_t fIndex, const bool owEnable, - const bool sMode, const uint16_t uPortNumber, const uint32_t mFramesPerFile, + const std::filesystem::path &filePath, const std::string &fNamePrefix, + const uint64_t fIndex, const bool owEnable, const bool sMode, + const uint16_t uPortNumber, const uint32_t mFramesPerFile, const uint64_t nImages, const uint32_t nX, const uint32_t nY, const uint32_t dr) { @@ -103,6 +104,7 @@ void HDF5DataFile::CreateFirstHDF5DataFile( nPixelsY = nY; dynamicRange = dr; + m_filePath = filePath; fileNamePrefix = fNamePrefix; fileIndex = fIndex; overWriteEnable = owEnable; @@ -129,9 +131,10 @@ void HDF5DataFile::CreateFile() { numFramesInFile = 0; numFilesInAcquisition++; - std::ostringstream os; - os << fileNamePrefix << "_f" << subFileIndex << '_' << fileIndex << ".h5"; - fileName = os.str(); + std::filesystem::path p = + m_filePath / (fileNamePrefix + "_f" + std::to_string(subFileIndex) + + '_' + std::to_string(fileIndex) + ".h5"); + fileName = p.string(); std::lock_guard lock(*hdf5Lib); diff --git a/slsReceiverSoftware/src/HDF5DataFile.h b/slsReceiverSoftware/src/HDF5DataFile.h index e3a7b19b3..06dabe2d2 100644 --- a/slsReceiverSoftware/src/HDF5DataFile.h +++ b/slsReceiverSoftware/src/HDF5DataFile.h @@ -3,7 +3,6 @@ #pragma once #include "File.h" - #include namespace sls { @@ -23,7 +22,8 @@ class HDF5DataFile : private virtual slsDetectorDefs, public File { void CloseFile() override; - void CreateFirstHDF5DataFile(const std::string &fNamePrefix, + void CreateFirstHDF5DataFile(const std::filesystem::path &filePath, + const std::string &fNamePrefix, const uint64_t fIndex, const bool owEnable, const bool sMode, const uint16_t uPortNumber, const uint32_t mFramesPerFile, @@ -65,7 +65,8 @@ class HDF5DataFile : private virtual slsDetectorDefs, public File { uint32_t nPixelsY{0}; uint32_t dynamicRange{0}; - std::string fileNamePrefix; + std::filesystem::path m_filePath{}; + std::string fileNamePrefix{}; uint64_t fileIndex{0}; bool overWriteEnable{false}; bool silentMode{false}; diff --git a/slsReceiverSoftware/src/Implementation.cpp b/slsReceiverSoftware/src/Implementation.cpp index 84b248835..292c5df29 100644 --- a/slsReceiverSoftware/src/Implementation.cpp +++ b/slsReceiverSoftware/src/Implementation.cpp @@ -483,10 +483,14 @@ void Implementation::setFileFormat(const fileFormat f) { LOG(logINFO) << "File Format: " << ToString(fileFormatType); } -std::string Implementation::getFilePath() const { return filePath; } +std::string Implementation::getFilePath() const { return filePath.string(); } void Implementation::setFilePath(const std::string &c) { - filePath = c; + // check if filePath empty and throw error + if (c.empty()) { + throw ReceiverError("File path cannot be empty"); + } + filePath = std::filesystem::path(c); LOG(logINFO) << "File path: " << filePath; } @@ -651,7 +655,7 @@ void Implementation::startReceiver() { generalData->dynamicRange, numPorts, static_cast(generalData->imageSize), - filePath, + filePath.string(), fileName, fileIndex, quadEnable, @@ -851,9 +855,7 @@ void Implementation::ResetParametersforNewAcquisition() { it->ResetParametersforNewAcquisition(); if (dataStreamEnable) { - std::ostringstream os; - os << filePath << '/' << fileName; - std::string fnametostream = os.str(); + std::string fnametostream = (filePath / fileName).string(); for (const auto &it : dataStreamer) it->ResetParametersforNewAcquisition(fnametostream); } @@ -874,21 +876,28 @@ void Implementation::CreateUDPSockets() { void Implementation::SetupWriter() { try { - // check if filePath empty and throw error - if (filePath.empty()) { - throw ReceiverError("File path cannot be empty"); - } // check if folder exists and throw if it cant create - mkdir_p(filePath); + if (filePath.empty()) { + throw RuntimeError("File path cannot be empty. Please set file " + "path before starting acquisition."); + } + if (!std::filesystem::exists(filePath)) { + try { + std::filesystem::create_directories(filePath); + } catch (const std::filesystem::filesystem_error &e) { + throw RuntimeError("Could not create directory: " + + filePath.string() + ". Error: " + e.what()); + } + } // create first files for (unsigned int i = 0; i < dataProcessor.size(); ++i) { - std::ostringstream os; - os << filePath << "/" << fileName << "_d" - << (modulePos * generalData->numUDPInterfaces + i); - std::string fileNamePrefix = os.str(); - dataProcessor[i]->CreateFirstFiles(fileNamePrefix, fileIndex, - overwriteEnable, silentMode, - detectorDataStream[i]); + + std::string fileNamePrefix = + fileName + "_d" + + std::to_string(modulePos * generalData->numUDPInterfaces + i); + dataProcessor[i]->CreateFirstFiles( + filePath, fileNamePrefix, fileIndex, overwriteEnable, + silentMode, detectorDataStream[i]); } } catch (const RuntimeError &e) { shutDownUDPSockets(); diff --git a/slsReceiverSoftware/src/Implementation.h b/slsReceiverSoftware/src/Implementation.h index 2699bbfc4..3680cb84e 100644 --- a/slsReceiverSoftware/src/Implementation.h +++ b/slsReceiverSoftware/src/Implementation.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -316,7 +317,7 @@ class Implementation : private virtual slsDetectorDefs { // file parameters fileFormat fileFormatType{BINARY}; - std::string filePath{}; + std::filesystem::path filePath{}; std::string fileName{"run"}; uint64_t fileIndex{0}; bool fileWriteEnable{false}; diff --git a/slsReceiverSoftware/src/MasterFileUtility.cpp b/slsReceiverSoftware/src/MasterFileUtility.cpp index a4e782744..ffead18a9 100644 --- a/slsReceiverSoftware/src/MasterFileUtility.cpp +++ b/slsReceiverSoftware/src/MasterFileUtility.cpp @@ -9,16 +9,17 @@ namespace sls { namespace masterFileUtility { -std::string CreateMasterBinaryFile(const std::string &filePath, +std::string CreateMasterBinaryFile(const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, MasterAttributes *attr) { - std::ostringstream os; - os << filePath << "/" << fileNamePrefix << "_master" - << "_" << fileIndex << ".json"; - std::string fileName = os.str(); + + std::filesystem::path p = filePath / (fileNamePrefix + "_master_" + + std::to_string(fileIndex) + ".json"); + + std::string fileName = p.string(); std::string mode = "w"; if (!overWriteEnable) @@ -112,17 +113,16 @@ void LinkHDF5FileInMaster(std::string &masterFileName, } } -std::string CreateMasterHDF5File(const std::string &filePath, +std::string CreateMasterHDF5File(const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, MasterAttributes *attr, std::mutex *hdf5LibMutex) { - std::ostringstream os; - os << filePath << "/" << fileNamePrefix << "_master" - << "_" << fileIndex << ".h5"; - std::string fileName = os.str(); + std::filesystem::path p = filePath / (fileNamePrefix + "_master_" + + std::to_string(fileIndex) + ".json"); + std::string fileName = p.string(); std::lock_guard lock(*hdf5LibMutex); @@ -185,7 +185,7 @@ int GetNumPortsInRoi(const defs::ROI roi, const defs::xy portSize) { /** Will not be called if dynamic range is 4 and roi enabled */ std::string CreateVirtualHDF5File( - const std::string &filePath, const std::string &fileNamePrefix, + const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, const int modulePos, const int numUnitsPerReadout, const uint32_t maxFramesPerFile, const int nPixelsX, const int nPixelsY, @@ -202,10 +202,11 @@ std::string CreateVirtualHDF5File( } // virtual file name - std::ostringstream osfn; - osfn << filePath << "/" << fileNamePrefix << "_virtual" - << "_" << fileIndex << ".h5"; - std::string fileName = osfn.str(); + std::filesystem::path p = filePath / (fileNamePrefix + "_virtual_" + + std::to_string(fileIndex) + ".h5"); + + std::string fileName = p.string(); + unsigned int paraSize = parameterNames.size(); std::lock_guard lock(*hdf5LibMutex); std::unique_ptr fd{nullptr}; @@ -331,12 +332,15 @@ std::string CreateVirtualHDF5File( H5S_SELECT_SET, numBlocksPara, startLocationPara, strideBetweenBlocksPara, blockSizePara); - // source file name - std::ostringstream os; - os << filePath << "/" << fileNamePrefix << "_d" - << (modulePos * numUnitsPerReadout + iReadout) << "_f" - << iFile << '_' << fileIndex << ".h5"; - std::string srcFileName = os.str(); + std::filesystem::path p = + filePath / + (fileNamePrefix + "_d" + + std::to_string(modulePos * numUnitsPerReadout + + iReadout) + + "_f" + std::to_string(iFile) + '_' + + std::to_string(fileIndex) + ".h5"); + + std::string srcFileName = p.string(); LOG(logDEBUG1) << srcFileName; // find relative path diff --git a/slsReceiverSoftware/src/MasterFileUtility.h b/slsReceiverSoftware/src/MasterFileUtility.h index cbf68be39..795607079 100644 --- a/slsReceiverSoftware/src/MasterFileUtility.h +++ b/slsReceiverSoftware/src/MasterFileUtility.h @@ -4,13 +4,14 @@ #include "MasterAttributes.h" +#include #include namespace sls { namespace masterFileUtility { -std::string CreateMasterBinaryFile(const std::string &filePath, +std::string CreateMasterBinaryFile(const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, @@ -24,7 +25,7 @@ void LinkHDF5FileInMaster(std::string &masterFileName, const bool silentMode, std::mutex *hdf5LibMutex, size_t multiRoiSize); -std::string CreateMasterHDF5File(const std::string &filePath, +std::string CreateMasterHDF5File(const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, @@ -35,7 +36,7 @@ defs::ROI GetGlobalPortRoi(const int iPort, const defs::xy portSize, int GetNumPortsInRoi(const defs::ROI roi, const defs::xy portSize); std::string CreateVirtualHDF5File( - const std::string &filePath, const std::string &fileNamePrefix, + const std::filesystem::path &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, const int modulePos, const int numUnitsPerReadout, const uint32_t maxFramesPerFile, const int nPixelsX, const int nPixelsY,