From f31234bbc301fe84486a0067c60f9fb3dd856279 Mon Sep 17 00:00:00 2001 From: Leonid Lunin Date: Mon, 1 Jun 2026 12:07:52 +0200 Subject: [PATCH 1/6] Add rx_streamdummyheader command (#1442) * add rx_restream_stop command. This allows to send a ZMQ dummy header any time user wants to do so. For example this allows to pre-configure the ZMQ processing software before the acquisition begins. Therefore, the dummy header was adapted in order to contain the fields stored in the receiver. * renamed command, changed inherit, moved commands to zmq related section * update filename in restreamstop * renamed helper function, sorted header fields alphabetically * fixed fnametostream set * renamed functions, add SetFileName method, format JSON parameters order * added python bindings and formatting (does nothing really) * renamed restream stop functions to stream dummy * checkout .github files from ed8c885 * release notes --------- Co-authored-by: Dhanya Thattil --- .github/workflows/conda_python.yaml | 2 +- RELEASE.md | 3 + python/src/detector.cpp | 4 + slsDetectorGui/src/qDrawPlot.cpp | 1 - .../autocomplete/bash_autocomplete.sh | 6 +- .../autocomplete/zsh_autocomplete.sh | 6 +- slsDetectorSoftware/generator/commands.yaml | 7 ++ .../generator/extended_commands.yaml | 22 +++++ slsDetectorSoftware/include/sls/Detector.h | 1 + slsDetectorSoftware/src/Caller.cpp | 39 ++++++++ slsDetectorSoftware/src/Caller.h | 2 + slsDetectorSoftware/src/Detector.cpp | 4 + slsDetectorSoftware/src/DetectorImpl.cpp | 2 +- slsDetectorSoftware/src/Module.cpp | 6 +- slsDetectorSoftware/src/Module.h | 2 +- slsDetectorSoftware/src/inferAction.cpp | 12 +++ slsDetectorSoftware/src/inferAction.h | 2 + slsReceiverSoftware/src/ClientInterface.cpp | 8 +- slsReceiverSoftware/src/ClientInterface.h | 2 +- slsReceiverSoftware/src/DataStreamer.cpp | 90 +++++++++++-------- slsReceiverSoftware/src/DataStreamer.h | 12 +-- slsReceiverSoftware/src/Implementation.cpp | 17 ++-- slsReceiverSoftware/src/Implementation.h | 2 +- slsSupportLib/include/sls/file_utils.h | 1 - .../include/sls/sls_detector_funcs.h | 4 +- 25 files changed, 188 insertions(+), 69 deletions(-) diff --git a/.github/workflows/conda_python.yaml b/.github/workflows/conda_python.yaml index 86c8b3907..18d18e62e 100644 --- a/.github/workflows/conda_python.yaml +++ b/.github/workflows/conda_python.yaml @@ -33,7 +33,7 @@ jobs: run: conda config --set anaconda_upload no - name: Build - run: conda-build conda-recipes/python-client --output-folder build_output + run: conda-build conda-recipes/python-client --output-folder build_output - name: Upload all Conda packages uses: actions/upload-artifact@v7 diff --git a/RELEASE.md b/RELEASE.md index 7213b631d..c67a67438 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -62,6 +62,9 @@ support for building rpms removed unused function readDataFile/writeDataFile from file_utils.h +added rx_streamdummyheader to send the zmq dummy header any time. Allows pre-configuring zmq processing before acq begins. + + 2 On-board Detector Server Compatibility ========================================== diff --git a/python/src/detector.cpp b/python/src/detector.cpp index ff14eaafa..482976edc 100644 --- a/python/src/detector.cpp +++ b/python/src/detector.cpp @@ -1083,6 +1083,10 @@ void init_det(py::module &m) { CppDetectorApi.def("setRxZmqHwm", (void (Detector::*)(const int)) & Detector::setRxZmqHwm, py::arg()); + CppDetectorApi.def("streamRxDummyHeader", + (void (Detector::*)(sls::Positions) const) & + Detector::streamRxDummyHeader, + py::arg() = Positions{}); CppDetectorApi.def("getSubExptime", (Result(Detector::*)(sls::Positions) const) & Detector::getSubExptime, diff --git a/slsDetectorGui/src/qDrawPlot.cpp b/slsDetectorGui/src/qDrawPlot.cpp index 86d43dc66..54ffffbfd 100644 --- a/slsDetectorGui/src/qDrawPlot.cpp +++ b/slsDetectorGui/src/qDrawPlot.cpp @@ -620,7 +620,6 @@ void qDrawPlot::StartAcquisition() { "qDrawPlot::StartAcquisition"); } - QtConcurrent::run(this, &qDrawPlot::AcquireThread); LOG(logDEBUG) << "End of Starting Acquisition in qDrawPlot"; diff --git a/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh b/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh index 433bd33d4..0fdd93044 100644 --- a/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh +++ b/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh @@ -80,7 +80,7 @@ _sd() { local IS_PATH=0 -local SLS_COMMANDS=" acquire activate adcclk adcenable adcenable10g adcindex adcinvert adclist adcname adcphase adcpipeline adcreg adcvpp apulse asamples autocompdisable badchannels blockingtrigger burstmode burstperiod bursts burstsl bustest cdsgain chipversion clearbit clearbusy clientversion clkdiv clkfreq clkphase collectionmode column compdisabletime confadc config configtransceiver counters currentsource dac dacindex daclist dacname dacvalues datastream dbitclk dbitphase dbitpipeline defaultdac defaultpattern define_bit define_reg definelist_bit definelist_reg delay delayl detectorserverversion detsize diodelay dpulse dr drlist dsamples execcommand exptime exptime1 exptime2 exptime3 extrastoragecells extsampling extsamplingsrc extsig fformat filtercells filterresistor findex firmwaretest firmwareversion fliprows flowcontrol10g fmaster fname foverwrite fpath framecounter frames framesl frametime free fwrite gaincaps gainmode gappixels gatedelay gatedelay1 gatedelay2 gatedelay3 gates getbit hardwareversion highvoltage hostname im_a im_b im_c im_d im_io imagetest include initialchecks inj_ch interpolation interruptsubframe kernelversion lastclient led lock master maxadcphaseshift maxclkphaseshift maxdbitphaseshift measuredperiod measuredsubperiod moduleid nextframenumber nmod numinterfaces overflow packageversion parallel parameters partialreset patfname patioctrl patlimits patloop patloop0 patloop1 patloop2 patmask patnloop patnloop0 patnloop1 patnloop2 patsetbit pattern patternstart patwait patwait0 patwait1 patwait2 patwaittime patwaittime0 patwaittime1 patwaittime2 patword pedestalmode period periodl polarity port power powerchip powerdac powerindex powerlist powername powervalues programfpga pulse pulsechip pulsenmove pumpprobe quad ratecorr readnrows readout readoutspeed readoutspeedlist rebootcontroller reg resetdacs resetfpga romode row runclk runtime rx_arping rx_clearroi rx_dbitlist rx_dbitoffset rx_dbitreorder rx_discardpolicy rx_fifodepth rx_frameindex rx_framescaught rx_framesperfile rx_hostname rx_jsonaddheader rx_jsonpara rx_lastclient rx_lock rx_missingpackets rx_padding rx_printconfig rx_realudpsocksize rx_roi rx_silent rx_start rx_status rx_stop rx_tcpport rx_threads rx_udpsocksize rx_version rx_zmqfreq rx_zmqhwm rx_zmqip rx_zmqport rx_zmqstartfnum rx_zmqstream samples savepattern scan scanerrmsg selinterface serialnumber setbit settings settingslist settingspath signalindex signallist signalname sleep slowadc slowadcindex slowadclist slowadcname slowadcvalues start status stop stopport storagecell_delay storagecell_start subdeadtime subexptime sync syncclk temp_10ge temp_adc temp_control temp_dcdc temp_event temp_fpga temp_fpgaext temp_fpgafl temp_fpgafr temp_slowadc temp_sodl temp_sodr temp_threshold templist tempvalues tengiga threshold thresholdnotb timing timing_info_decoder timinglist timingsource top transceiverenable trigger triggers triggersl trimbits trimen trimval tsamples txdelay txdelay_frame txdelay_left txdelay_right type udp_cleardst udp_dstip udp_dstip2 udp_dstlist udp_dstmac udp_dstmac2 udp_dstport udp_dstport2 udp_firstdst udp_numdst udp_reconfigure udp_srcip udp_srcip2 udp_srcmac udp_srcmac2 udp_validate update updatedetectorserver updatekernel updatemode user v_limit vchip_comp_adc vchip_comp_fe vchip_cs vchip_opa_1st vchip_opa_fd vchip_ref_comp_fe versions veto vetoalg vetofile vetophoton vetoref vetostream virtual vm_a vm_b vm_c vm_d vm_io zmqhwm zmqip zmqport " +local SLS_COMMANDS=" acquire activate adcclk adcenable adcenable10g adcindex adcinvert adclist adcname adcphase adcpipeline adcreg adcvpp apulse asamples autocompdisable badchannels blockingtrigger burstmode burstperiod bursts burstsl bustest cdsgain chipversion clearbit clearbusy clientversion clkdiv clkfreq clkphase collectionmode column compdisabletime confadc config configtransceiver counters currentsource dac dacindex daclist dacname dacvalues datastream dbitclk dbitphase dbitpipeline defaultdac defaultpattern define_bit define_reg definelist_bit definelist_reg delay delayl detectorserverversion detsize diodelay dpulse dr drlist dsamples execcommand exptime exptime1 exptime2 exptime3 extrastoragecells extsampling extsamplingsrc extsig fformat filtercells filterresistor findex firmwaretest firmwareversion fliprows flowcontrol10g fmaster fname foverwrite fpath framecounter frames framesl frametime free fwrite gaincaps gainmode gappixels gatedelay gatedelay1 gatedelay2 gatedelay3 gates getbit hardwareversion highvoltage hostname im_a im_b im_c im_d im_io imagetest include initialchecks inj_ch interpolation interruptsubframe kernelversion lastclient led lock master maxadcphaseshift maxclkphaseshift maxdbitphaseshift measuredperiod measuredsubperiod moduleid nextframenumber nmod numinterfaces overflow packageversion parallel parameters partialreset patfname patioctrl patlimits patloop patloop0 patloop1 patloop2 patmask patnloop patnloop0 patnloop1 patnloop2 patsetbit pattern patternstart patwait patwait0 patwait1 patwait2 patwaittime patwaittime0 patwaittime1 patwaittime2 patword pedestalmode period periodl polarity port power powerchip powerdac powerindex powerlist powername powervalues programfpga pulse pulsechip pulsenmove pumpprobe quad ratecorr readnrows readout readoutspeed readoutspeedlist rebootcontroller reg resetdacs resetfpga romode row runclk runtime rx_arping rx_clearroi rx_dbitlist rx_dbitoffset rx_dbitreorder rx_discardpolicy rx_fifodepth rx_frameindex rx_framescaught rx_framesperfile rx_hostname rx_jsonaddheader rx_jsonpara rx_lastclient rx_lock rx_missingpackets rx_padding rx_printconfig rx_realudpsocksize rx_roi rx_silent rx_start rx_status rx_stop rx_streamdummyheader rx_tcpport rx_threads rx_udpsocksize rx_version rx_zmqfreq rx_zmqhwm rx_zmqip rx_zmqport rx_zmqstartfnum rx_zmqstream samples savepattern scan scanerrmsg selinterface serialnumber setbit settings settingslist settingspath signalindex signallist signalname sleep slowadc slowadcindex slowadclist slowadcname slowadcvalues start status stop stopport storagecell_delay storagecell_start subdeadtime subexptime sync syncclk temp_10ge temp_adc temp_control temp_dcdc temp_event temp_fpga temp_fpgaext temp_fpgafl temp_fpgafr temp_slowadc temp_sodl temp_sodr temp_threshold templist tempvalues tengiga threshold thresholdnotb timing timing_info_decoder timinglist timingsource top transceiverenable trigger triggers triggersl trimbits trimen trimval tsamples txdelay txdelay_frame txdelay_left txdelay_right type udp_cleardst udp_dstip udp_dstip2 udp_dstlist udp_dstmac udp_dstmac2 udp_dstport udp_dstport2 udp_firstdst udp_numdst udp_reconfigure udp_srcip udp_srcip2 udp_srcmac udp_srcmac2 udp_validate update updatedetectorserver updatekernel updatemode user v_limit vchip_comp_adc vchip_comp_fe vchip_cs vchip_opa_1st vchip_opa_fd vchip_ref_comp_fe versions veto vetoalg vetofile vetophoton vetoref vetostream virtual vm_a vm_b vm_c vm_d vm_io zmqhwm zmqip zmqport " __acquire() { FCN_RETURN="" return 0 @@ -2329,6 +2329,10 @@ __rx_stop() { FCN_RETURN="" return 0 } +__rx_streamdummyheader() { +FCN_RETURN="" +return 0 +} __rx_tcpport() { FCN_RETURN="" if [[ ${IS_GET} -eq 0 ]]; then diff --git a/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh b/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh index 953f971d2..3da9f237e 100644 --- a/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh +++ b/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh @@ -4,7 +4,7 @@ _sd() { -local SLS_COMMANDS=" acquire activate adcclk adcenable adcenable10g adcindex adcinvert adclist adcname adcphase adcpipeline adcreg adcvpp apulse asamples autocompdisable badchannels blockingtrigger burstmode burstperiod bursts burstsl bustest cdsgain chipversion clearbit clearbusy clientversion clkdiv clkfreq clkphase collectionmode column compdisabletime confadc config configtransceiver counters currentsource dac dacindex daclist dacname dacvalues datastream dbitclk dbitphase dbitpipeline defaultdac defaultpattern define_bit define_reg definelist_bit definelist_reg delay delayl detectorserverversion detsize diodelay dpulse dr drlist dsamples execcommand exptime exptime1 exptime2 exptime3 extrastoragecells extsampling extsamplingsrc extsig fformat filtercells filterresistor findex firmwaretest firmwareversion fliprows flowcontrol10g fmaster fname foverwrite fpath framecounter frames framesl frametime free fwrite gaincaps gainmode gappixels gatedelay gatedelay1 gatedelay2 gatedelay3 gates getbit hardwareversion highvoltage hostname im_a im_b im_c im_d im_io imagetest include initialchecks inj_ch interpolation interruptsubframe kernelversion lastclient led lock master maxadcphaseshift maxclkphaseshift maxdbitphaseshift measuredperiod measuredsubperiod moduleid nextframenumber nmod numinterfaces overflow packageversion parallel parameters partialreset patfname patioctrl patlimits patloop patloop0 patloop1 patloop2 patmask patnloop patnloop0 patnloop1 patnloop2 patsetbit pattern patternstart patwait patwait0 patwait1 patwait2 patwaittime patwaittime0 patwaittime1 patwaittime2 patword pedestalmode period periodl polarity port power powerchip powerdac powerindex powerlist powername powervalues programfpga pulse pulsechip pulsenmove pumpprobe quad ratecorr readnrows readout readoutspeed readoutspeedlist rebootcontroller reg resetdacs resetfpga romode row runclk runtime rx_arping rx_clearroi rx_dbitlist rx_dbitoffset rx_dbitreorder rx_discardpolicy rx_fifodepth rx_frameindex rx_framescaught rx_framesperfile rx_hostname rx_jsonaddheader rx_jsonpara rx_lastclient rx_lock rx_missingpackets rx_padding rx_printconfig rx_realudpsocksize rx_roi rx_silent rx_start rx_status rx_stop rx_tcpport rx_threads rx_udpsocksize rx_version rx_zmqfreq rx_zmqhwm rx_zmqip rx_zmqport rx_zmqstartfnum rx_zmqstream samples savepattern scan scanerrmsg selinterface serialnumber setbit settings settingslist settingspath signalindex signallist signalname sleep slowadc slowadcindex slowadclist slowadcname slowadcvalues start status stop stopport storagecell_delay storagecell_start subdeadtime subexptime sync syncclk temp_10ge temp_adc temp_control temp_dcdc temp_event temp_fpga temp_fpgaext temp_fpgafl temp_fpgafr temp_slowadc temp_sodl temp_sodr temp_threshold templist tempvalues tengiga threshold thresholdnotb timing timing_info_decoder timinglist timingsource top transceiverenable trigger triggers triggersl trimbits trimen trimval tsamples txdelay txdelay_frame txdelay_left txdelay_right type udp_cleardst udp_dstip udp_dstip2 udp_dstlist udp_dstmac udp_dstmac2 udp_dstport udp_dstport2 udp_firstdst udp_numdst udp_reconfigure udp_srcip udp_srcip2 udp_srcmac udp_srcmac2 udp_validate update updatedetectorserver updatekernel updatemode user v_limit vchip_comp_adc vchip_comp_fe vchip_cs vchip_opa_1st vchip_opa_fd vchip_ref_comp_fe versions veto vetoalg vetofile vetophoton vetoref vetostream virtual vm_a vm_b vm_c vm_d vm_io zmqhwm zmqip zmqport " +local SLS_COMMANDS=" acquire activate adcclk adcenable adcenable10g adcindex adcinvert adclist adcname adcphase adcpipeline adcreg adcvpp apulse asamples autocompdisable badchannels blockingtrigger burstmode burstperiod bursts burstsl bustest cdsgain chipversion clearbit clearbusy clientversion clkdiv clkfreq clkphase collectionmode column compdisabletime confadc config configtransceiver counters currentsource dac dacindex daclist dacname dacvalues datastream dbitclk dbitphase dbitpipeline defaultdac defaultpattern define_bit define_reg definelist_bit definelist_reg delay delayl detectorserverversion detsize diodelay dpulse dr drlist dsamples execcommand exptime exptime1 exptime2 exptime3 extrastoragecells extsampling extsamplingsrc extsig fformat filtercells filterresistor findex firmwaretest firmwareversion fliprows flowcontrol10g fmaster fname foverwrite fpath framecounter frames framesl frametime free fwrite gaincaps gainmode gappixels gatedelay gatedelay1 gatedelay2 gatedelay3 gates getbit hardwareversion highvoltage hostname im_a im_b im_c im_d im_io imagetest include initialchecks inj_ch interpolation interruptsubframe kernelversion lastclient led lock master maxadcphaseshift maxclkphaseshift maxdbitphaseshift measuredperiod measuredsubperiod moduleid nextframenumber nmod numinterfaces overflow packageversion parallel parameters partialreset patfname patioctrl patlimits patloop patloop0 patloop1 patloop2 patmask patnloop patnloop0 patnloop1 patnloop2 patsetbit pattern patternstart patwait patwait0 patwait1 patwait2 patwaittime patwaittime0 patwaittime1 patwaittime2 patword pedestalmode period periodl polarity port power powerchip powerdac powerindex powerlist powername powervalues programfpga pulse pulsechip pulsenmove pumpprobe quad ratecorr readnrows readout readoutspeed readoutspeedlist rebootcontroller reg resetdacs resetfpga romode row runclk runtime rx_arping rx_clearroi rx_dbitlist rx_dbitoffset rx_dbitreorder rx_discardpolicy rx_fifodepth rx_frameindex rx_framescaught rx_framesperfile rx_hostname rx_jsonaddheader rx_jsonpara rx_lastclient rx_lock rx_missingpackets rx_padding rx_printconfig rx_realudpsocksize rx_roi rx_silent rx_start rx_status rx_stop rx_streamdummyheader rx_tcpport rx_threads rx_udpsocksize rx_version rx_zmqfreq rx_zmqhwm rx_zmqip rx_zmqport rx_zmqstartfnum rx_zmqstream samples savepattern scan scanerrmsg selinterface serialnumber setbit settings settingslist settingspath signalindex signallist signalname sleep slowadc slowadcindex slowadclist slowadcname slowadcvalues start status stop stopport storagecell_delay storagecell_start subdeadtime subexptime sync syncclk temp_10ge temp_adc temp_control temp_dcdc temp_event temp_fpga temp_fpgaext temp_fpgafl temp_fpgafr temp_slowadc temp_sodl temp_sodr temp_threshold templist tempvalues tengiga threshold thresholdnotb timing timing_info_decoder timinglist timingsource top transceiverenable trigger triggers triggersl trimbits trimen trimval tsamples txdelay txdelay_frame txdelay_left txdelay_right type udp_cleardst udp_dstip udp_dstip2 udp_dstlist udp_dstmac udp_dstmac2 udp_dstport udp_dstport2 udp_firstdst udp_numdst udp_reconfigure udp_srcip udp_srcip2 udp_srcmac udp_srcmac2 udp_validate update updatedetectorserver updatekernel updatemode user v_limit vchip_comp_adc vchip_comp_fe vchip_cs vchip_opa_1st vchip_opa_fd vchip_ref_comp_fe versions veto vetoalg vetofile vetophoton vetoref vetostream virtual vm_a vm_b vm_c vm_d vm_io zmqhwm zmqip zmqport " __acquire() { FCN_RETURN="" return 0 @@ -2253,6 +2253,10 @@ __rx_stop() { FCN_RETURN="" return 0 } +__rx_streamdummyheader() { +FCN_RETURN="" +return 0 +} __rx_tcpport() { FCN_RETURN="" if [[ ${IS_GET} -eq 0 ]]; then diff --git a/slsDetectorSoftware/generator/commands.yaml b/slsDetectorSoftware/generator/commands.yaml index 9e069f830..03a59ede3 100644 --- a/slsDetectorSoftware/generator/commands.yaml +++ b/slsDetectorSoftware/generator/commands.yaml @@ -1698,6 +1698,13 @@ rx_stop: PUT: function: stopReceiver +rx_streamdummyheader: + inherit_actions: EXECUTE_SET_COMMAND + help: "\n\tSends ZMQ dummy header. The detector related fields are not set, the receiver related fields are set." + actions: + PUT: + function: streamRxDummyHeader + readout: inherit_actions: EXECUTE_SET_COMMAND_NOID help: "\n\t[Mythen3] Starts detector readout. Status changes to TRANSMITTING and automatically returns to idle at the end of readout." diff --git a/slsDetectorSoftware/generator/extended_commands.yaml b/slsDetectorSoftware/generator/extended_commands.yaml index c29c13bc5..f284c0a2e 100644 --- a/slsDetectorSoftware/generator/extended_commands.yaml +++ b/slsDetectorSoftware/generator/extended_commands.yaml @@ -9117,6 +9117,28 @@ rx_stop: \ data file (if file write enabled)." infer_action: true template: true +rx_streamdummyheader: + actions: + PUT: + args: + - arg_types: [] + argc: 0 + cast_input: [] + check_det_id: false + convert_det_id: true + function: streamRxDummyHeader + input: [] + input_types: [] + output: + - '"successful"' + require_det_id: true + store_result_in_t: false + command_name: rx_streamdummyheader + function_alias: rx_streamdummyheader + help: "\n\tSends ZMQ dummy header. The detector related fields are not set, the\ + \ receiver related fields are set." + infer_action: true + template: true rx_tcpport: actions: GET: diff --git a/slsDetectorSoftware/include/sls/Detector.h b/slsDetectorSoftware/include/sls/Detector.h index d107370de..1203b41ad 100644 --- a/slsDetectorSoftware/include/sls/Detector.h +++ b/slsDetectorSoftware/include/sls/Detector.h @@ -1162,6 +1162,7 @@ class Detector { */ void setRxZmqHwm(const int limit); + void streamRxDummyHeader(Positions pos = {}) const; ///@} /** @name Eiger Specific */ diff --git a/slsDetectorSoftware/src/Caller.cpp b/slsDetectorSoftware/src/Caller.cpp index 1e72aba17..b2212d542 100644 --- a/slsDetectorSoftware/src/Caller.cpp +++ b/slsDetectorSoftware/src/Caller.cpp @@ -11131,6 +11131,45 @@ std::string Caller::rx_stop(int action) { return os.str(); } +std::string Caller::rx_streamdummyheader(int action) { + + std::ostringstream os; + // print help + if (action == slsDetectorDefs::HELP_ACTION) { + os << R"V0G0N( + Sends ZMQ dummy header. The detector related fields are not set, the receiver related fields are set. )V0G0N" + << std::endl; + return os.str(); + } + + // check if action and arguments are valid + if (action == slsDetectorDefs::PUT_ACTION) { + if (1 && args.size() != 0) { + throw RuntimeError("Wrong number of arguments for action PUT"); + } + + if (args.size() == 0) { + } + + } + + else { + + throw RuntimeError( + "INTERNAL ERROR: Invalid action: supported actions are ['PUT']"); + } + + // generate code for each action + if (action == slsDetectorDefs::PUT_ACTION) { + if (args.size() == 0) { + det->streamRxDummyHeader(std::vector{det_id}); + os << "successful" << '\n'; + } + } + + return os.str(); +} + std::string Caller::rx_tcpport(int action) { std::ostringstream os; diff --git a/slsDetectorSoftware/src/Caller.h b/slsDetectorSoftware/src/Caller.h index d35ffe569..5b5633297 100644 --- a/slsDetectorSoftware/src/Caller.h +++ b/slsDetectorSoftware/src/Caller.h @@ -269,6 +269,7 @@ class Caller { std::string rx_start(int action); std::string rx_status(int action); std::string rx_stop(int action); + std::string rx_streamdummyheader(int action); std::string rx_tcpport(int action); std::string rx_threads(int action); std::string rx_udpsocksize(int action); @@ -640,6 +641,7 @@ class Caller { {"rx_start", &Caller::rx_start}, {"rx_status", &Caller::rx_status}, {"rx_stop", &Caller::rx_stop}, + {"rx_streamdummyheader", &Caller::rx_streamdummyheader}, {"rx_tcpport", &Caller::rx_tcpport}, {"rx_threads", &Caller::rx_threads}, {"rx_udpsocksize", &Caller::rx_udpsocksize}, diff --git a/slsDetectorSoftware/src/Detector.cpp b/slsDetectorSoftware/src/Detector.cpp index 01e9f5a44..1b15819f6 100644 --- a/slsDetectorSoftware/src/Detector.cpp +++ b/slsDetectorSoftware/src/Detector.cpp @@ -1636,6 +1636,10 @@ void Detector::setRxZmqHwm(const int limit) { } } +void Detector::streamRxDummyHeader(Positions pos) const { + pimpl->Parallel(&Module::streamRxDummyHeader, pos); +} + // Eiger Specific Result Detector::getSubExptime(Positions pos) const { diff --git a/slsDetectorSoftware/src/DetectorImpl.cpp b/slsDetectorSoftware/src/DetectorImpl.cpp index b83e3a52e..101cf6939 100644 --- a/slsDetectorSoftware/src/DetectorImpl.cpp +++ b/slsDetectorSoftware/src/DetectorImpl.cpp @@ -1151,7 +1151,7 @@ int DetectorImpl::acquire() { setJoinThreadFlag(true); } else if (receiver) { while (numZmqRunning != 0) { - Parallel(&Module::restreamStopFromReceiver, {}); + Parallel(&Module::streamRxDummyHeader, {}); std::this_thread::sleep_for(std::chrono::milliseconds(200)); } } diff --git a/slsDetectorSoftware/src/Module.cpp b/slsDetectorSoftware/src/Module.cpp index 17fe8841b..ecf9a4db0 100644 --- a/slsDetectorSoftware/src/Module.cpp +++ b/slsDetectorSoftware/src/Module.cpp @@ -993,15 +993,15 @@ void Module::stopAcquisition() { try { if (shm()->useReceiverFlag && getReceiverStreaming()) { if (detStatus == IDLE && getReceiverStatus() == IDLE) { - restreamStopFromReceiver(); + streamRxDummyHeader(); } } } catch (...) { } } -void Module::restreamStopFromReceiver() { - sendToReceiver(F_RESTREAM_STOP_FROM_RECEIVER); +void Module::streamRxDummyHeader() { + sendToReceiver(F_STREAM_RX_DUMMY_HEADER_FROM_RECEIVER); } void Module::startAndReadAll() { diff --git a/slsDetectorSoftware/src/Module.h b/slsDetectorSoftware/src/Module.h index cf4672c93..55fd6c0b9 100644 --- a/slsDetectorSoftware/src/Module.h +++ b/slsDetectorSoftware/src/Module.h @@ -215,7 +215,7 @@ class Module : public virtual slsDetectorDefs { void startAcquisition(); void startReadout(); void stopAcquisition(); - void restreamStopFromReceiver(); + void streamRxDummyHeader(); void startAndReadAll(); runStatus getRunStatus() const; runStatus getReceiverStatus() const; diff --git a/slsDetectorSoftware/src/inferAction.cpp b/slsDetectorSoftware/src/inferAction.cpp index af2ff6396..d3c8cd23d 100644 --- a/slsDetectorSoftware/src/inferAction.cpp +++ b/slsDetectorSoftware/src/inferAction.cpp @@ -2939,6 +2939,18 @@ int InferAction::rx_stop() { } } +int InferAction::rx_streamdummyheader() { + + if (args.size() == 0) { + return slsDetectorDefs::PUT_ACTION; + } + + else { + + throw RuntimeError("Could not infer action: Wrong number of arguments"); + } +} + int InferAction::rx_tcpport() { if (args.size() == 0) { diff --git a/slsDetectorSoftware/src/inferAction.h b/slsDetectorSoftware/src/inferAction.h index c3be8f075..6baa59d66 100644 --- a/slsDetectorSoftware/src/inferAction.h +++ b/slsDetectorSoftware/src/inferAction.h @@ -220,6 +220,7 @@ class InferAction { int rx_start(); int rx_status(); int rx_stop(); + int rx_streamdummyheader(); int rx_tcpport(); int rx_threads(); int rx_udpsocksize(); @@ -556,6 +557,7 @@ class InferAction { {"rx_start", &InferAction::rx_start}, {"rx_status", &InferAction::rx_status}, {"rx_stop", &InferAction::rx_stop}, + {"rx_streamdummyheader", &InferAction::rx_streamdummyheader}, {"rx_tcpport", &InferAction::rx_tcpport}, {"rx_threads", &InferAction::rx_threads}, {"rx_udpsocksize", &InferAction::rx_udpsocksize}, diff --git a/slsReceiverSoftware/src/ClientInterface.cpp b/slsReceiverSoftware/src/ClientInterface.cpp index 0c08534a8..9ce6b22be 100644 --- a/slsReceiverSoftware/src/ClientInterface.cpp +++ b/slsReceiverSoftware/src/ClientInterface.cpp @@ -166,7 +166,7 @@ int ClientInterface::functionTable(){ flist[F_GET_RECEIVER_STREAMING_PORT] = &ClientInterface::get_streaming_port; flist[F_SET_RECEIVER_SILENT_MODE] = &ClientInterface::set_silent_mode; flist[F_GET_RECEIVER_SILENT_MODE] = &ClientInterface::get_silent_mode; - flist[F_RESTREAM_STOP_FROM_RECEIVER] = &ClientInterface::restream_stop; + flist[F_STREAM_RX_DUMMY_HEADER_FROM_RECEIVER] = &ClientInterface::stream_rx_dummy_header; flist[F_SET_ADDITIONAL_JSON_HEADER] = &ClientInterface::set_additional_json_header; flist[F_GET_ADDITIONAL_JSON_HEADER] = &ClientInterface::get_additional_json_header; flist[F_RECEIVER_UDP_SOCK_BUF_SIZE] = &ClientInterface::set_udp_socket_buffer_size; @@ -1082,14 +1082,14 @@ int ClientInterface::get_silent_mode(Interface &socket) { return socket.sendResult(retval); } -int ClientInterface::restream_stop(Interface &socket) { +int ClientInterface::stream_rx_dummy_header(Interface &socket) { verifyIdle(socket); if (!impl()->getDataStreamEnable()) { throw RuntimeError( - "Could not restream stop packet as data Streaming is disabled"); + "Could not stream rx dummy header as data Streaming is disabled"); } else { LOG(logDEBUG1) << "Restreaming stop"; - impl()->restreamStop(); + impl()->streamRxDummyHeader(); } return socket.Send(OK); } diff --git a/slsReceiverSoftware/src/ClientInterface.h b/slsReceiverSoftware/src/ClientInterface.h index f2dbaa764..0486367e2 100644 --- a/slsReceiverSoftware/src/ClientInterface.h +++ b/slsReceiverSoftware/src/ClientInterface.h @@ -111,7 +111,7 @@ class ClientInterface : private virtual slsDetectorDefs { int get_streaming_port(ServerInterface &socket); int set_silent_mode(ServerInterface &socket); int get_silent_mode(ServerInterface &socket); - int restream_stop(ServerInterface &socket); + int stream_rx_dummy_header(ServerInterface &socket); int set_additional_json_header(ServerInterface &socket); int get_additional_json_header(ServerInterface &socket); int set_udp_socket_buffer_size(ServerInterface &socket); diff --git a/slsReceiverSoftware/src/DataStreamer.cpp b/slsReceiverSoftware/src/DataStreamer.cpp index 8a8e83833..7e2826f4a 100644 --- a/slsReceiverSoftware/src/DataStreamer.cpp +++ b/slsReceiverSoftware/src/DataStreamer.cpp @@ -8,7 +8,6 @@ #include "DataStreamer.h" #include "Fifo.h" #include "GeneralData.h" -#include "sls/ZmqSocket.h" #include "sls/sls_detector_exceptions.h" #include @@ -30,6 +29,10 @@ void DataStreamer::SetGeneralData(GeneralData *g) { generalData = g; } void DataStreamer::SetFileIndex(uint64_t value) { fileIndex = value; } +void DataStreamer::SetFileName(const std::string &fname) { + fileNametoStream = fname; +} + void DataStreamer::SetNumberofPorts(xy np) { numPorts = np; } void DataStreamer::SetFlipRows(bool fd) { @@ -62,11 +65,10 @@ void DataStreamer::SetPortROI(ROI roi) { } } -void DataStreamer::ResetParametersforNewAcquisition(const std::string &fname) { +void DataStreamer::ResetParametersforNewAcquisition() { StopRunning(); startedFlag = false; firstIndex = 0; - fileNametoStream = fname; } void DataStreamer::RecordFirstIndex(uint64_t fnum, size_t firstImageIndex) { @@ -152,8 +154,7 @@ void DataStreamer::ProcessAnImage(sls_detector_header header, size_t size, uint64_t fnum = header.frameNumber; LOG(logDEBUG1) << "DataStreamer " << index << ": fnum:" << fnum; - if (!SendDataHeader(header, size, generalData->nPixelsX, - generalData->nPixelsY)) { + if (!SendDataHeader(header, size)) { LOG(logERROR) << "Could not send zmq header for fnum " << fnum << " and streamer " << index; } @@ -164,34 +165,47 @@ void DataStreamer::ProcessAnImage(sls_detector_header header, size_t size, } } -int DataStreamer::SendDummyHeader() { +zmqHeader DataStreamer::prepareRxZmqHeader() { zmqHeader zHeader; - zHeader.data = false; zHeader.jsonversion = SLS_DETECTOR_JSON_HEADER_VERSION; + + // parameters coming from the receiver + zHeader.dynamicRange = generalData->dynamicRange; + zHeader.fileIndex = fileIndex; + zHeader.flipRows = static_cast(flipRows); + zHeader.fname = fileNametoStream; + zHeader.imageSize = generalData->imageSize; + if (generalData->detType == GOTTHARD2 && index != 0) { + zHeader.imageSize = generalData->vetoImageSize; + } + zHeader.ndetx = numPorts.x; + zHeader.ndety = numPorts.y; + zHeader.npixelsx = generalData->nPixelsX; + zHeader.npixelsy = generalData->nPixelsY; + zHeader.quad = quadEnable; + + // update local copy only if it was updated (to prevent locking each time) + if (isAdditionalJsonUpdated) { + std::lock_guard lock(additionalJsonMutex); + localAdditionalJsonHeader = additionalJsonHeader; + isAdditionalJsonUpdated = false; + } + zHeader.addJsonHeader = localAdditionalJsonHeader; + zHeader.rx_roi = portRoi.getIntArray(); + return zHeader; +} + +int DataStreamer::SendDummyHeader() { + zmqHeader zHeader = prepareRxZmqHeader(); + zHeader.data = false; return zmqSocket->SendHeader(index, zHeader); } -int DataStreamer::SendDataHeader(sls_detector_header header, uint32_t size, - uint32_t nx, uint32_t ny) { - zmqHeader zHeader; +int DataStreamer::SendDataHeader(sls_detector_header header, uint32_t size) { + zmqHeader zHeader = prepareRxZmqHeader(); zHeader.data = true; - zHeader.jsonversion = SLS_DETECTOR_JSON_HEADER_VERSION; - uint64_t frameIndex = header.frameNumber - firstIndex; - uint64_t acquisitionIndex = header.frameNumber; - - zHeader.dynamicRange = generalData->dynamicRange; - zHeader.fileIndex = fileIndex; - zHeader.ndetx = numPorts.x; - zHeader.ndety = numPorts.y; - zHeader.npixelsx = nx; - zHeader.npixelsy = ny; - zHeader.imageSize = size; - zHeader.acqIndex = acquisitionIndex; - zHeader.frameIndex = frameIndex; - zHeader.progress = - 100 * ((double)(frameIndex + 1) / (double)(nTotalFrames)); - zHeader.fname = fileNametoStream; + // parameter from detector header zHeader.frameNumber = header.frameNumber; zHeader.expLength = header.expLength; zHeader.packetNumber = header.packetNumber; @@ -205,26 +219,24 @@ int DataStreamer::SendDataHeader(sls_detector_header header, uint32_t size, zHeader.detSpec4 = header.detSpec4; zHeader.detType = header.detType; zHeader.version = header.version; - zHeader.flipRows = static_cast(flipRows); - zHeader.quad = quadEnable; + + // parameter derived from header and receiver + uint64_t acquisitionIndex = header.frameNumber; + uint64_t frameIndex = header.frameNumber - firstIndex; + zHeader.acqIndex = acquisitionIndex; zHeader.completeImage = (header.packetNumber < generalData->packetsPerFrame ? false : true); - - // update local copy only if it was updated (to prevent locking each time) - if (isAdditionalJsonUpdated) { - std::lock_guard lock(additionalJsonMutex); - localAdditionalJsonHeader = additionalJsonHeader; - isAdditionalJsonUpdated = false; - } - zHeader.addJsonHeader = localAdditionalJsonHeader; - zHeader.rx_roi = portRoi.getIntArray(); + zHeader.frameIndex = frameIndex; + zHeader.imageSize = size; + zHeader.progress = + 100 * ((double)(frameIndex + 1) / (double)(nTotalFrames)); return zmqSocket->SendHeader(index, zHeader); } -void DataStreamer::RestreamStop() { +void DataStreamer::StreamRxDummyHeader() { if (!SendDummyHeader()) { - throw RuntimeError("Could not restream Dummy Header via ZMQ for port " + + throw RuntimeError("Could not stream Dummy Header via ZMQ for port " + std::to_string(zmqSocket->GetPortNumber())); } } diff --git a/slsReceiverSoftware/src/DataStreamer.h b/slsReceiverSoftware/src/DataStreamer.h index d8eaaabcf..f2f8dd1f9 100644 --- a/slsReceiverSoftware/src/DataStreamer.h +++ b/slsReceiverSoftware/src/DataStreamer.h @@ -10,6 +10,7 @@ */ #include "ThreadObject.h" +#include "sls/ZmqSocket.h" #include "sls/network_utils.h" #include @@ -32,6 +33,7 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject { void SetGeneralData(GeneralData *g); void SetFileIndex(uint64_t value); + void SetFileName(const std::string &fname); void SetNumberofPorts(xy np); void SetFlipRows(bool fd); void SetQuadEnable(bool value); @@ -40,7 +42,7 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject { SetAdditionalJsonHeader(const std::map &json); void SetPortROI(ROI roi); - void ResetParametersforNewAcquisition(const std::string &fname); + void ResetParametersforNewAcquisition(); /** * Creates Zmq Sockets * (throws an exception if it couldnt create zmq sockets) @@ -49,7 +51,7 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject { */ void CreateZmqSockets(uint16_t port, int hwm); void CloseZmqSocket(); - void RestreamStop(); + void StreamRxDummyHeader(); private: /** @@ -71,18 +73,16 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject { */ void ProcessAnImage(sls_detector_header header, size_t size, char *data); + zmqHeader prepareRxZmqHeader(); int SendDummyHeader(); /** * Create and send Json Header * @param rheader header of image * @param size data size (could have been modified in call back) - * @param nx number of pixels in x dim - * @param ny number of pixels in y dim * @returns 0 if error, else 1 */ - int SendDataHeader(sls_detector_header header, uint32_t size = 0, - uint32_t nx = 0, uint32_t ny = 0); + int SendDataHeader(sls_detector_header header, uint32_t size = 0); static const std::string TypeName; const GeneralData *generalData{nullptr}; diff --git a/slsReceiverSoftware/src/Implementation.cpp b/slsReceiverSoftware/src/Implementation.cpp index 292c5df29..41cd95a56 100644 --- a/slsReceiverSoftware/src/Implementation.cpp +++ b/slsReceiverSoftware/src/Implementation.cpp @@ -842,10 +842,13 @@ void Implementation::shutDownUDPSockets() { it->ShutDownUDPSocket(); } -void Implementation::restreamStop() { - for (const auto &it : dataStreamer) - it->RestreamStop(); - LOG(logINFO) << "Restreaming Dummy Header via ZMQ successful"; +void Implementation::streamRxDummyHeader() { + std::string fnametostream = (filePath / fileName).string(); + for (const auto &it : dataStreamer) { + it->SetFileName(fnametostream); + it->StreamRxDummyHeader(); + } + LOG(logINFO) << "Streaming Dummy Header via ZMQ successful"; } void Implementation::ResetParametersforNewAcquisition() { @@ -856,8 +859,10 @@ void Implementation::ResetParametersforNewAcquisition() { if (dataStreamEnable) { std::string fnametostream = (filePath / fileName).string(); - for (const auto &it : dataStreamer) - it->ResetParametersforNewAcquisition(fnametostream); + for (const auto &it : dataStreamer) { + it->ResetParametersforNewAcquisition(); + it->SetFileName(fnametostream); + } } } diff --git a/slsReceiverSoftware/src/Implementation.h b/slsReceiverSoftware/src/Implementation.h index 3680cb84e..87b631e79 100644 --- a/slsReceiverSoftware/src/Implementation.h +++ b/slsReceiverSoftware/src/Implementation.h @@ -104,7 +104,7 @@ class Implementation : private virtual slsDetectorDefs { void stopReceiver(); void startReadout(); void shutDownUDPSockets(); - void restreamStop(); + void streamRxDummyHeader(); /************************************************** * * diff --git a/slsSupportLib/include/sls/file_utils.h b/slsSupportLib/include/sls/file_utils.h index 81aa3d19b..cc3f61201 100644 --- a/slsSupportLib/include/sls/file_utils.h +++ b/slsSupportLib/include/sls/file_utils.h @@ -11,7 +11,6 @@ namespace sls { - std::vector readBinaryFile(const std::string &fname, const std::string &errorPrefix); diff --git a/slsSupportLib/include/sls/sls_detector_funcs.h b/slsSupportLib/include/sls/sls_detector_funcs.h index c2fd7eb82..56db44c78 100755 --- a/slsSupportLib/include/sls/sls_detector_funcs.h +++ b/slsSupportLib/include/sls/sls_detector_funcs.h @@ -367,7 +367,7 @@ enum detFuncs { F_GET_RECEIVER_STREAMING_SRC_IP, F_SET_RECEIVER_SILENT_MODE, F_GET_RECEIVER_SILENT_MODE, - F_RESTREAM_STOP_FROM_RECEIVER, + F_STREAM_RX_DUMMY_HEADER_FROM_RECEIVER, F_SET_ADDITIONAL_JSON_HEADER, F_GET_ADDITIONAL_JSON_HEADER, F_RECEIVER_UDP_SOCK_BUF_SIZE, @@ -786,7 +786,7 @@ const char* getFunctionNameFromEnum(enum detFuncs func) { case F_GET_RECEIVER_STREAMING_SRC_IP: return "F_GET_RECEIVER_STREAMING_SRC_IP - obsolete"; case F_SET_RECEIVER_SILENT_MODE: return "F_SET_RECEIVER_SILENT_MODE"; case F_GET_RECEIVER_SILENT_MODE: return "F_GET_RECEIVER_SILENT_MODE"; - case F_RESTREAM_STOP_FROM_RECEIVER: return "F_RESTREAM_STOP_FROM_RECEIVER"; + case F_STREAM_RX_DUMMY_HEADER_FROM_RECEIVER: return "F_STREAM_RX_DUMMY_HEADER_FROM_RECEIVER"; case F_SET_ADDITIONAL_JSON_HEADER: return "F_SET_ADDITIONAL_JSON_HEADER"; case F_GET_ADDITIONAL_JSON_HEADER: return "F_GET_ADDITIONAL_JSON_HEADER"; case F_RECEIVER_UDP_SOCK_BUF_SIZE: return "F_RECEIVER_UDP_SOCK_BUF_SIZE"; From 2e4cd258a832793ac963751ccec6e559b0067ea0 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 10 Jun 2026 17:30:30 +0200 Subject: [PATCH 2/6] Dev/refactor caller tests master attributes (#1462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactoring * wip * refactored, yet to take out checkers * checker added * wip acquire ctb state * wip acquire ctb state * wip, acq test * acquire moved out * fix * wip, To fix masterattribtues * wip, masterfilecheks * wip, at ctbexpectedstate * wip, fixing acquire * wip, acquire done * wip * compiles * refactoring * minor * minor * reduced unnecessary includes * fix in tests * removed ctb state check * ctb fix with ctb state * fixed ctb guard tests * minor debug * work for xilinx ctb --------- Co-authored-by: Erik Fröjdh --- slsDetectorSoftware/tests/CMakeLists.txt | 6 + .../tests/Caller/test-Caller-acquire.cpp | 517 ++++---- .../tests/Caller/test-Caller-global.cpp | 163 +-- .../tests/Caller/test-Caller-global.h | 80 +- .../Caller/test-Caller-master-attributes.cpp | 1086 +---------------- .../tests/Caller/test-Caller-rx.cpp | 38 +- slsDetectorSoftware/tests/acquire/Acquire.cpp | 47 + slsDetectorSoftware/tests/acquire/Acquire.h | 60 + slsDetectorSoftware/tests/acquire/CTBState.h | 138 +++ .../tests/acquire/ExpectedState.cpp | 355 ++++++ .../tests/acquire/ExpectedState.h | 119 ++ slsDetectorSoftware/tests/acquire/FileState.h | 100 ++ .../tests/checks/MasterFileChecks.h | 456 +++++++ .../tests/master_file/Checker.h | 59 + .../tests/master_file/Context.h | 63 + .../tests/master_file/Readers.h | 30 + .../tests/master_file/ReadersH5.h | 295 +++++ .../tests/master_file/ReadersJson.h | 145 +++ 18 files changed, 2193 insertions(+), 1564 deletions(-) create mode 100644 slsDetectorSoftware/tests/acquire/Acquire.cpp create mode 100644 slsDetectorSoftware/tests/acquire/Acquire.h create mode 100644 slsDetectorSoftware/tests/acquire/CTBState.h create mode 100644 slsDetectorSoftware/tests/acquire/ExpectedState.cpp create mode 100644 slsDetectorSoftware/tests/acquire/ExpectedState.h create mode 100644 slsDetectorSoftware/tests/acquire/FileState.h create mode 100644 slsDetectorSoftware/tests/checks/MasterFileChecks.h create mode 100644 slsDetectorSoftware/tests/master_file/Checker.h create mode 100644 slsDetectorSoftware/tests/master_file/Context.h create mode 100644 slsDetectorSoftware/tests/master_file/Readers.h create mode 100644 slsDetectorSoftware/tests/master_file/ReadersH5.h create mode 100644 slsDetectorSoftware/tests/master_file/ReadersJson.h diff --git a/slsDetectorSoftware/tests/CMakeLists.txt b/slsDetectorSoftware/tests/CMakeLists.txt index d25b35718..e39ce339b 100755 --- a/slsDetectorSoftware/tests/CMakeLists.txt +++ b/slsDetectorSoftware/tests/CMakeLists.txt @@ -5,6 +5,9 @@ target_sources(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/test-SharedMemory.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/acquire/Acquire.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/acquire/ExpectedState.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-rx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-rx-running.cpp @@ -18,6 +21,7 @@ target_sources(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-moench.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-global.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-acquire.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller-master-attributes.cpp @@ -44,6 +48,8 @@ target_compile_options(tests target_include_directories(tests PUBLIC + "$" + "$/Caller" "$" "$" "$" diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp index 94e294ab7..8084efd84 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-acquire.cpp @@ -2,289 +2,272 @@ // Copyright (C) 2021 Contributors to the SLS Detector Package #include "Caller.h" #include "catch.hpp" -#include "sls/Detector.h" -#include "sls/sls_detector_defs.h" -#include "sls/versionAPI.h" -#include "test-Caller-global.h" -#include "tests/globals.h" -#include -#include +#include "acquire/Acquire.h" +#include "acquire/CTBState.h" +#include "acquire/ExpectedState.h" +#include "acquire/FileState.h" +#include "checks/MasterFileChecks.h" namespace sls { -using test::GET; -using test::PUT; +namespace acq = sls::test::acquire; +namespace checks = sls::test::checks; + +void acquire_and_check_file_size( + Detector &det, std::optional ctb_state = std::nullopt) { + auto acq_state = acq::default_acquisition_state(); + acq_state.num_frames = 2; + auto file_state = acq::default_file_state(); + acq::run(det, acq_state, file_state); + auto image_size = acq::get_expected_image_size(det, ctb_state); + REQUIRE_NOTHROW( + checks::check_binary_file_size(image_size, acq_state.num_frames)); +} + +void test_ctb_binary_file_size(Detector &det) { + auto detType = det.getDetectorType().squash(defs::GENERIC); + acq::CTBState ctb_state = acq::default_ctb_state(detType); + // default ctb state for tests + // readout mode = defs::ANALOG_AND_DIGITAL + // analog samples = 5000 + // digital samples = 6000 + // trans samples = 288 + // ten_giga = isAltera ? false : true + // adc enable 1g = 0xFFFFFF00 + // adc enable 1g = 0xFF00FFFF + // dbit offset = 0 + // dbit list = {0, 12, 2, 43} + // dbit reorder = false + // trans mask = 0x3 + + acq::CTBStateGuard ctb_guard(det, std::make_optional(ctb_state)); + + { + ctb_state = acq::default_ctb_state(detType); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_offset = 16; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_offset = 16; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_offset = 16; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_offset = 16; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_offset = 16; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_offset = 16; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::TRANSCEIVER_ONLY; + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + ctb_state.dbit_list.clear(); + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } + { + ctb_state = acq::default_ctb_state(detType); + ctb_state.readout_mode = defs::ANALOG_ONLY; + ctb_state.dbit_offset = 16; + ctb_state.dbit_reorder = true; + acq::set_ctb_state(det, ctb_state); + REQUIRE_NOTHROW( + acquire_and_check_file_size(det, std::make_optional(ctb_state))); + } +} // disable for jungfrau as it requires higher maximum receive buffer size // sysctl net.core.rmem_max=$((100*1024*1024)) // sysctl net.core.rmem_default=$((100*1024*1024)) -TEST_CASE("jungfrau_or_moench_acquire_check_file_size", +TEST_CASE("acquire_check_binary_file_size", "[.detectorintegration][.disable_check_data_file]") { Detector det; - Caller caller(&det); auto det_type = det.getDetectorType().tsquash("Inconsistent detector types to test"); - if (det_type == defs::JUNGFRAU || det_type == defs::MOENCH) { - - auto num_udp_interfaces = det.getNumberofUDPInterfaces().tsquash( - "inconsistent number of udp interfaces"); - - int num_frames_to_acquire = 2; - create_files_for_acquire(det, caller, num_frames_to_acquire); - - // check file size (assuming local pc) - { - detParameters par(det_type); - int bytes_per_pixel = det.getDynamicRange().squash() / 8; - // if 2 udp interfaces, data split into half - size_t expected_image_size = (par.nChanX * par.nChanY * par.nChipX * - par.nChipY * bytes_per_pixel) / - num_udp_interfaces; - testFileInfo test_file_info; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - } -} - -TEST_CASE("eiger_acquire_check_file_size", - "[.detectorintegration][.disable_check_data_file]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::EIGER) { - - int dynamic_range = det.getDynamicRange().squash(); - if (dynamic_range != 16) { - throw RuntimeError( - "Eiger detector must have dynamic range 16 to test"); - } - int num_frames_to_acquire = 2; - create_files_for_acquire(det, caller, num_frames_to_acquire); - - // check file size (assuming local pc) - { - detParameters par(det_type); - // data split into half due to 2 udp interfaces per half module - int num_chips = (par.nChipX / 2); - int bytes_per_pixel = (dynamic_range / 8); - size_t expected_image_size = - par.nChanX * par.nChanY * num_chips * bytes_per_pixel; - testFileInfo test_file_info; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - } -} - -TEST_CASE("mythen3_acquire_check_file_size", - "[.detectorintegration][.disable_check_data_file]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::MYTHEN3) { - - int dynamic_range = det.getDynamicRange().squash(); - int counter_mask = det.getCounterMask().squash(); - if (dynamic_range != 16 && counter_mask != 0x3) { - throw RuntimeError("Mythen3 detector must have dynamic range 16 " - "and counter mask 0x3 to test"); - } - int num_counters = __builtin_popcount(counter_mask); - int num_frames_to_acquire = 2; - create_files_for_acquire(det, caller, num_frames_to_acquire); - - // check file size (assuming local pc) - { - detParameters par(det_type); - int bytes_per_pixel = dynamic_range / 8; - int num_channels_per_counter = par.nChanX / 3; - size_t expected_image_size = num_channels_per_counter * - num_counters * par.nChipX * - bytes_per_pixel; - testFileInfo test_file_info; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - } -} - -TEST_CASE("gotthard2_acquire_check_file_size", - "[.detectorintegration][.disable_check_data_file]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - - if (det_type == defs::GOTTHARD2) { - - int num_frames_to_acquire = 2; - create_files_for_acquire(det, caller, num_frames_to_acquire); - - // check file size (assuming local pc) - { - detParameters par(det_type); - int bytes_per_pixel = det.getDynamicRange().squash() / 8; - size_t expected_image_size = - par.nChanX * par.nChipX * bytes_per_pixel; - testFileInfo test_file_info; - test_acquire_binary_file_size(test_file_info, num_frames_to_acquire, - expected_image_size); - } - } -} - -void test_ctb_file_size_with_acquire(Detector &det, Caller &caller, - int64_t num_frames, - const testCtbAcquireInfo &test_info, - bool isXilinxCtb) { - - create_files_for_acquire(det, caller, num_frames, test_info); - - // check file size (assuming local pc) - uint64_t expected_image_size = - calculate_ctb_image_size(test_info, isXilinxCtb).first; - testFileInfo test_file_info; - REQUIRE_NOTHROW(test_acquire_binary_file_size(test_file_info, num_frames, - expected_image_size)); -} - -// disable for xilinx_ctb as it requires higher maximum receive buffer size -// sysctl net.core.rmem_max=$((100*1024*1024)) -// sysctl net.core.rmem_default=$((100*1024*1024)) -TEST_CASE("ctb_acquire_check_file_size", - "[.detectorintegration][.disable_check_data_file]") { - Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); + INFO("Testing acquire and checking binary file size with " + << ToString(det_type)); if (det_type == defs::CHIPTESTBOARD || - det_type == defs::XILINX_CHIPTESTBOARD) { - bool isXilinxCtb = (det_type == defs::XILINX_CHIPTESTBOARD); - int num_frames_to_acquire = 2; - // all the test cases - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_offset = 16; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_AND_DIGITAL; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - test_ctb_config.dbit_offset = 16; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - test_ctb_config.dbit_list.clear(); - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::DIGITAL_AND_TRANSCEIVER; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::TRANSCEIVER_ONLY; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - { - testCtbAcquireInfo test_ctb_config{}; - test_ctb_config.readout_mode = defs::ANALOG_ONLY; - test_ctb_config.dbit_offset = 16; - test_ctb_config.dbit_list.clear(); - test_ctb_config.dbit_reorder = true; - REQUIRE_NOTHROW(test_ctb_file_size_with_acquire( - det, caller, num_frames_to_acquire, test_ctb_config, - isXilinxCtb)); - } - } + det_type == defs::XILINX_CHIPTESTBOARD) + test_ctb_binary_file_size(det); + else + REQUIRE_NOTHROW(acquire_and_check_file_size(det)); } } // namespace sls diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp index b3c2d1f00..5b4ba685f 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-global.cpp @@ -3,13 +3,16 @@ #include "test-Caller-global.h" #include "Caller.h" #include "GeneralData.h" -#include "catch.hpp" #include "sls/Detector.h" #include "sls/logger.h" #include "tests/globals.h" +#include "catch.hpp" + namespace sls { +namespace acq = sls::test::acquire; + using test::GET; using test::PUT; void test_valid_port_caller(const std::string &command, @@ -96,164 +99,10 @@ void test_onchip_dac_caller(defs::dacIndex index, const std::string &dacname, } } -testFileInfo get_file_state(const Detector &det) { - return testFileInfo{ - det.getFilePath().tsquash("Inconsistent file path"), - det.getFileNamePrefix().tsquash("Inconsistent file prefix"), - det.getAcquisitionIndex().tsquash( - "Inconsistent file acquisition index"), - det.getFileWrite().tsquash("Inconsistent file write state"), - det.getFileOverWrite().tsquash("Inconsistent file overwrite state"), - det.getFileFormat().tsquash("Inconsistent file format")}; -} - -void set_file_state(Detector &det, const testFileInfo &file_info) { - if (!file_info.file_path.empty()) - det.setFilePath(file_info.file_path); - det.setFileNamePrefix(file_info.file_prefix); - det.setAcquisitionIndex(file_info.file_acq_index); - det.setFileWrite(file_info.file_write); - det.setFileOverWrite(file_info.file_overwrite); - det.setFileFormat(file_info.file_format); -} - -void test_acquire_binary_file_size(const testFileInfo &file_info, - uint64_t num_frames_to_acquire, - uint64_t expected_image_size) { - assert(file_info.file_format == defs::BINARY); - std::string fname = file_info.file_path + "/" + file_info.file_prefix + - "_d0_f0_" + std::to_string(file_info.file_acq_index) + - ".raw"; - uint64_t expected_file_size = - num_frames_to_acquire * - (expected_image_size + sizeof(defs::sls_receiver_header)); - auto actual_file_size = std::filesystem::file_size(fname); - REQUIRE(actual_file_size == expected_file_size); -} - -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)); - bool idle = false; - while (!idle) { - std::ostringstream oss; - REQUIRE_NOTHROW(caller.call("status", {}, -1, GET)); - auto statusList = det.getDetectorStatus(); - if (statusList.any(defs::ERROR)) { - throw std::runtime_error("error status while acquiring"); - } - if (statusList.contains_only(defs::IDLE, defs::STOPPED)) { - idle = true; - } - } - REQUIRE_NOTHROW(caller.call("rx_stop", {}, -1, PUT)); -} - -void create_files_for_acquire( - Detector &det, Caller &caller, int64_t num_frames, - const std::optional &test_info) { - - // save previous state - testFileInfo prev_file_info = get_file_state(det); - auto prev_num_frames = det.getNumberOfFrames().tsquash( - "Inconsistent number of frames to acquire"); - std::optional prev_ctb_config_info{}; - if (test_info) { - prev_ctb_config_info = get_ctb_config_state(det); - } - - // set state for acquire - testFileInfo test_file_info; - set_file_state(det, test_file_info); - det.setNumberOfFrames(num_frames); - if (test_info) { - set_ctb_config_state(det, *test_info); - } - - // acquire and get num frames caught - REQUIRE_NOTHROW(test_acquire_with_receiver(caller, det)); - // 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 - test_file_info.file_format = defs::HDF5; - test_file_info.file_acq_index = 0; - set_file_state(det, test_file_info); - - // acquire and get num frames caught - test_acquire_with_receiver(caller, det); - { - auto frames_caught = det.getFramesCaught()[0][0]; - REQUIRE(frames_caught == num_frames); - } -#endif - - // restore previous state - // file - set_file_state(det, prev_file_info); - det.setNumberOfFrames(prev_num_frames); - if (test_info) { - set_ctb_config_state(det, *prev_ctb_config_info); - } -} - -testCtbAcquireInfo get_ctb_config_state(const Detector &det) { - testCtbAcquireInfo ctb_config_info{ - det.getReadoutMode().tsquash("inconsistent readout mode to test"), - true, - det.getNumberOfAnalogSamples().tsquash( - "inconsistent number of analog samples to test"), - det.getNumberOfDigitalSamples().tsquash( - "inconsistent number of digital samples to test"), - det.getNumberOfTransceiverSamples().tsquash( - "inconsistent number of transceiver samples to test"), - 0, - det.getTenGigaADCEnableMask().tsquash( - "inconsistent ten giga adc enable mask to test"), - det.getRxDbitOffset().tsquash("inconsistent rx dbit offset to test"), - det.getRxDbitList().tsquash("inconsistent rx dbit list to test"), - det.getRxDbitReorder().tsquash("inconsistent rx dbit reorder to test"), - det.getTransceiverEnableMask().tsquash( - "inconsistent transceiver mask to test")}; - - if (det.getDetectorType().tsquash("inconsistent detector type to test") == - slsDetectorDefs::CHIPTESTBOARD) { - ctb_config_info.ten_giga = - det.getTenGiga().tsquash("inconsistent ten giga enable to test"); - ctb_config_info.adc_enable_1g = det.getADCEnableMask().tsquash( - "inconsistent adc enable mask to test"); - } - return ctb_config_info; -} - -void set_ctb_config_state(Detector &det, - const testCtbAcquireInfo &ctb_config_info) { - det.setReadoutMode(ctb_config_info.readout_mode); - if (det.getDetectorType().tsquash("inconsistent detector type to test") == - slsDetectorDefs::CHIPTESTBOARD) { - det.setTenGiga(ctb_config_info.ten_giga); - det.setADCEnableMask(ctb_config_info.adc_enable_1g); - } - det.setNumberOfAnalogSamples(ctb_config_info.num_adc_samples); - det.setNumberOfDigitalSamples(ctb_config_info.num_dbit_samples); - det.setNumberOfTransceiverSamples(ctb_config_info.num_trans_samples); - det.setTenGigaADCEnableMask(ctb_config_info.adc_enable_10g); - det.setRxDbitOffset(ctb_config_info.dbit_offset); - det.setRxDbitList(ctb_config_info.dbit_list); - det.setRxDbitReorder(ctb_config_info.dbit_reorder); - det.setTransceiverEnableMask(ctb_config_info.transceiver_mask); -} - std::pair -calculate_ctb_image_size(const testCtbAcquireInfo &test_info, - bool isXilinxCtb) { +calculate_ctb_image_size(const acq::CTBState &test_info, bool isXilinxCtb) { - // test_info.print(); // for debugging + LOG(logDEBUG1) << test_info; sls::CtbImageInputs inputs{}; inputs.mode = test_info.readout_mode; inputs.nAnalogSamples = test_info.num_adc_samples; diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-global.h b/slsDetectorSoftware/tests/Caller/test-Caller-global.h index 344dd81e7..47e3d7c19 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-global.h +++ b/slsDetectorSoftware/tests/Caller/test-Caller-global.h @@ -2,10 +2,7 @@ // Copyright (C) 2021 Contributors to the SLS Detector Package #pragma once -#include "Caller.h" -#include "sls/Detector.h" -#include "sls/ToString.h" -#include "sls/logger.h" +#include "acquire/CTBState.h" #include "sls/sls_detector_defs.h" #include @@ -14,64 +11,8 @@ #include namespace sls { -struct testFileInfo { - std::string file_path{"/tmp"}; - std::string file_prefix{"sls_test"}; - int64_t file_acq_index{0}; - bool file_write{true}; - bool file_overwrite{true}; - slsDetectorDefs::fileFormat file_format{slsDetectorDefs::BINARY}; - std::string getMasterFileNamePrefix() const { - return file_path + "/" + file_prefix + "_master_" + - std::to_string(file_acq_index); - } - std::string getVirtualFileName() const { - return file_path + "/" + file_prefix + "_virtual_" + - std::to_string(file_acq_index) + ".h5"; - } - inline void print() const { - LOG(logINFO) << "File Info: " - << "\n\tFile Path: " << file_path - << "\n\tFile Prefix: " << file_prefix - << "\n\tFile Acquisition Index: " << file_acq_index - << "\n\tFile Write: " << file_write - << "\n\tFile Overwrite: " << file_overwrite - << "\n\tFile Format: " << ToString(file_format) - << "\n\tMaster Filename: " << getMasterFileNamePrefix() - << "\n\tVirtual Filename: " << getVirtualFileName(); - } -}; -struct testCtbAcquireInfo { - defs::readoutMode readout_mode{defs::ANALOG_AND_DIGITAL}; - bool ten_giga{false}; - int num_adc_samples{5000}; - int num_dbit_samples{6000}; - int num_trans_samples{288}; - uint32_t adc_enable_1g{0xFFFFFF00}; - uint32_t adc_enable_10g{0xFF00FFFF}; - int dbit_offset{0}; - std::vector dbit_list{0, 12, 2, 43}; - bool dbit_reorder{false}; - uint32_t transceiver_mask{0x3}; - - inline void print() const { - LOG(logINFO) << "CTB Acquire Info: " - << "\n\tReadout Mode: " << ToString(readout_mode) - << "\n\tTen Giga: " << ten_giga - << "\n\tADC Enable 1G: " << std::hex << adc_enable_1g - << std::dec << "\n\tADC Enable 10G: " << std::hex - << adc_enable_10g << std::dec - << "\n\tNumber of Analog Samples: " << num_adc_samples - << "\n\tNumber of Digital Samples: " << num_dbit_samples - << "\n\tNumber of Transceiver Samples: " - << num_trans_samples << "\n\tDBIT Offset: " << dbit_offset - << "\n\tDBIT Reorder: " << dbit_reorder - << "\n\tDBIT List: " << ToString(dbit_list) - << "\n\tTransceiver Mask: " << std::hex << transceiver_mask - << std::dec << std::endl; - } -}; +namespace acq = sls::test::acquire; void test_valid_port_caller(const std::string &command, const std::vector &arguments, @@ -82,22 +23,7 @@ void test_dac_caller(slsDetectorDefs::dacIndex index, void test_onchip_dac_caller(slsDetectorDefs::dacIndex index, const std::string &dacname, int dacvalue); -testFileInfo get_file_state(const Detector &det); -void set_file_state(Detector &det, const testFileInfo &file_info); -void test_acquire_binary_file_size(const testFileInfo &file_info, - uint64_t num_frames_to_acquire, - uint64_t expected_image_size); - -void test_acquire_with_receiver(Caller &caller, const Detector &det); - -void create_files_for_acquire( - Detector &det, Caller &caller, int64_t num_frames = 1, - const std::optional &test_info = std::nullopt); - -testCtbAcquireInfo get_ctb_config_state(const Detector &det); -void set_ctb_config_state(Detector &det, - const testCtbAcquireInfo &ctb_config_info); std::pair -calculate_ctb_image_size(const testCtbAcquireInfo &test_info, bool isXilinxCtb); +calculate_ctb_image_size(const acq::CTBState &test_info, bool isXilinxCtb); } // namespace sls diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp index 4d3c5faef..5a3933ea8 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-master-attributes.cpp @@ -1,16 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-or-other // Copyright (C) 2021 Contributors to the SLS Detector Package -#include "Caller.h" -#include "MasterAttributes.h" -#include "catch.hpp" -#include "receiver_defs.h" +#include "acquire/ExpectedState.h" +#include "checks/MasterFileChecks.h" + #include "sls/Detector.h" #include "sls/ToString.h" #include "sls/logger.h" -#include "sls/sls_detector_defs.h" -#include "test-Caller-global.h" -#include "tests/globals.h" +#include "catch.hpp" #include #include #include @@ -20,1061 +17,58 @@ #ifdef HDF5C #include "H5Cpp.h" -const std::string HDF5_GROUP = "/entry/instrument/detector/"; #endif namespace sls { -using test::GET; -using test::PUT; -using namespace rapidjson; - -inline bool operator==(sls::ns lhs, sls::ns rhs) { - return lhs.count() == rhs.count(); -} - -#ifdef HDF5C -std::optional h5File{}; -#endif - -/** std::string */ -void read_from_json(const Document &doc, const std::string &name, - std::string &retval) { - retval = doc[name.c_str()].GetString(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::string &retval) { - dataset.read(retval, dataset.getStrType()); -} -#endif - -/** int */ -void read_from_json(const Document &doc, const std::string &name, int &retval) { - retval = doc[name.c_str()].GetInt(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - int &retval) { - dataset.read(&retval, H5::PredType::NATIVE_INT); -} -#endif - -/** uint64_t */ -void read_from_json(const Document &doc, const std::string &name, - uint64_t &retval) { - retval = doc[name.c_str()].GetUint64(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - uint64_t &retval) { - dataset.read(&retval, H5::PredType::STD_U64LE); -} -#endif - -/** uint32_t */ -void read_from_json(const Document &doc, const std::string &name, - uint32_t &retval) { - retval = doc[name.c_str()].GetUint(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - uint32_t &retval) { - dataset.read(&retval, H5::PredType::STD_U32LE); -} -#endif - -/** double */ -void read_from_json(const Document &doc, const std::string &name, - double &retval) { - retval = doc[name.c_str()].GetDouble(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - double &retval) { - dataset.read(&retval, H5::PredType::NATIVE_DOUBLE); -} -#endif - -/** std::vector */ -void read_from_json(const Document &doc, const std::string &name, - std::vector &retval) { - for (const auto &item : doc[name.c_str()].GetArray()) { - retval.push_back(item.GetInt64()); - } -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::vector &retval) { - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - retval.resize(dims[0]); - dataset.read(retval.data(), H5::PredType::STD_I64LE); -} -#endif - -/** std::vector */ -void read_from_json(const Document &doc, const std::string &name, - std::vector &retval) { - for (const auto &item : doc[name.c_str()].GetArray()) { - defs::ROI r{}; - r.xmin = item["xmin"].GetInt(); - r.xmax = item["xmax"].GetInt(); - r.ymin = item["ymin"].GetInt(); - r.ymax = item["ymax"].GetInt(); - retval.push_back(r); - } -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::vector &retval) { - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - H5::CompType cType(sizeof(defs::ROI)); - cType.insertMember("xmin", HOFFSET(defs::ROI, xmin), - H5::PredType::NATIVE_INT); - cType.insertMember("xmax", HOFFSET(defs::ROI, xmax), - H5::PredType::NATIVE_INT); - cType.insertMember("ymin", HOFFSET(defs::ROI, ymin), - H5::PredType::NATIVE_INT); - cType.insertMember("ymax", HOFFSET(defs::ROI, ymax), - H5::PredType::NATIVE_INT); - retval.resize(dims[0]); - dataset.read(retval.data(), cType); -} -#endif - -/** std::array */ -void read_from_json(const Document &doc, const std::string &name, - std::array &retval) { - const auto &json_values = doc[name.c_str()].GetArray(); - if (json_values.Size() != retval.size()) { - throw sls::RuntimeError("JSON array " + name + - " does not have num elements as expected"); - } - int index = 0; - for (const auto &item : json_values) { - retval[index++] = item.GetInt(); - } -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::array &retval) { - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - if (dims[0] != retval.size()) { - throw sls::RuntimeError("HDF5 dataset " + name + - " does not have num elements as expected"); - } - dataset.read(retval.data(), H5::PredType::NATIVE_INT); -} -#endif - -/* std::array */ -void read_from_json(const Document &doc, const std::string &name, - std::array &retval) { - const auto &json_values = doc[name.c_str()].GetArray(); - if (json_values.Size() != retval.size()) { - throw sls::RuntimeError("JSON array " + name + - " does not have num elements as expected"); - } - int index = 0; - for (const auto &item : json_values) { - std::string sval = item.GetString(); - retval[index++] = StringTo(sval); - } -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::array &retval) { - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - if (dims[0] != retval.size()) { - throw sls::RuntimeError("HDF5 dataset " + name + - " does not have num elements as expected"); - } - std::vector strValues(dims[0]); - dataset.read(strValues.data(), dataset.getStrType()); - for (size_t i = 0; i < dims[0]; ++i) { - retval[i] = StringTo(strValues[i]); - } -} -#endif - -/** defs::xy */ -void read_from_json(const Document &doc, const std::string &name, - defs::xy &retval) { - retval.x = doc[name.c_str()]["x"].GetInt(); - retval.y = doc[name.c_str()]["y"].GetInt(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - defs::xy &retval) { - H5::CompType cType(sizeof(defs::xy)); - cType.insertMember("x", HOFFSET(defs::xy, x), H5::PredType::NATIVE_INT); - cType.insertMember("y", HOFFSET(defs::xy, y), H5::PredType::NATIVE_INT); - dataset.read(&retval, cType); -} -#endif - -/** defs::scanParameters */ -void read_from_json(const Document &doc, const std::string &name, - defs::scanParameters &retval) { - const auto &s = doc[name.c_str()].GetObject(); - retval.enable = s["enable"].GetInt(); - retval.dacInd = static_cast(s["dacInd"].GetInt()); - retval.startOffset = s["start offset"].GetInt(); - retval.stopOffset = s["stop offset"].GetInt(); - retval.stepSize = s["step size"].GetInt(); - retval.dacSettleTime_ns = s["dac settle time ns"].GetInt64(); -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - defs::scanParameters &retval) { - H5::CompType cType(sizeof(defs::scanParameters)); - cType.insertMember("enable", HOFFSET(defs::scanParameters, enable), - H5::PredType::NATIVE_INT); - cType.insertMember("dacInd", HOFFSET(defs::scanParameters, dacInd), - H5::PredType::NATIVE_INT); - cType.insertMember("startOffset", - HOFFSET(defs::scanParameters, startOffset), - H5::PredType::NATIVE_INT); - cType.insertMember("stopOffset", HOFFSET(defs::scanParameters, stopOffset), - H5::PredType::NATIVE_INT); - cType.insertMember("stepSize", HOFFSET(defs::scanParameters, stepSize), - H5::PredType::NATIVE_INT); - cType.insertMember("dacSettleTime_ns", - HOFFSET(defs::scanParameters, dacSettleTime_ns), - H5::PredType::STD_I64LE); - dataset.read(&retval, cType); -} -#endif - -/** std::map */ -void read_from_json(const Document &doc, const std::string &name, - std::map &retval) { - for (const auto &m : doc[name.c_str()].GetObject()) { - retval[m.name.GetString()] = m.value.GetString(); - } -} -#ifdef HDF5C -void read_from_h5_dataset(const H5::DataSet &dataset, const std::string &name, - std::map &retval) { - H5::DataSpace dataspace = dataset.getSpace(); - hsize_t dims[1]; - dataspace.getSimpleExtentDims(dims); - if (dims[0] == 0) { - return; // empty dataset - } - auto strType = dataset.getStrType(); - H5::CompType mapType(sizeof(char *) * 2); - mapType.insertMember("key", 0, strType); - mapType.insertMember("value", sizeof(char *), strType); - struct KeyValue { - const char *key; - const char *value; - }; - std::vector kv_vector(dims[0]); - dataset.read(kv_vector.data(), mapType); - for (const auto &kv : kv_vector) { - retval[kv.key] = kv.value; - } -} -#endif - -/** test parameter in file */ -template -void test_json_parameter(const Document &doc, const std::string &name, - const T &expected) { - REQUIRE(doc.HasMember(name.c_str())); - T retval{}; - read_from_json(doc, name, retval); - REQUIRE(retval == expected); -} -#ifdef HDF5C -template -void test_h5_dataset(const std::string &name, const T &expected) { - auto dataset = h5File->openDataSet(HDF5_GROUP + name); - T retval{}; - read_from_h5_dataset(dataset, name, retval); - REQUIRE(retval == expected); -} -#endif - -template -void check_master_file(const std::optional &doc, - const std::string &name, const T &expected) { - if (doc.has_value()) { - const auto &d = *doc; - test_json_parameter(d, name, expected); - } else { -#ifdef HDF5C - if (!h5File.has_value()) { - throw sls::RuntimeError("HDF5 file is not opened for testing " + - name); - } - test_h5_dataset(name, expected); -#else - throw sls::RuntimeError("Document is not available for testing " + - name); -#endif - } -} - -void test_master_file_version(const Detector &det, - const std::optional &doc) { - // different values for json and hdf5 - // hdf5 version in atttribute and not dataset - double retval{}; - std::string name = MasterAttributes::N_VERSION.data(); - if (doc.has_value()) { - const auto &d = *doc; - REQUIRE(d.HasMember(MasterAttributes::N_VERSION.data())); - read_from_json(d, name, retval); - REQUIRE(retval == BINARY_WRITER_VERSION); - } else { -#ifdef HDF5C - if (!h5File.has_value()) { - throw sls::RuntimeError( - "HDF5 file is not opened for testing Version"); - } - auto attr = h5File->openAttribute(MasterAttributes::N_VERSION.data()); - attr.read(attr.getDataType(), &retval); - REQUIRE(retval == HDF5_WRITER_VERSION); -#else - throw sls::RuntimeError( - "Document is not available for testing Version"); -#endif - } -} - -void test_master_file_type(const Detector &det, - const std::optional &doc) { - auto det_type = det.getDetectorType().tsquash("Inconsistent detector type"); - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DETECTOR_TYPE.data(), ToString(det_type))); -} - -void test_master_file_timing_mode(const Detector &det, - const std::optional &doc) { - auto timing_mode = det.getTimingMode().tsquash("Inconsistent timing mode"); - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TIMING_MODE.data(), ToString(timing_mode))); -} - -void test_master_file_geometry(const Detector &det, - const std::optional &doc) { - auto modGeometry = det.getModuleGeometry(); - auto portperModGeometry = det.getPortPerModuleGeometry(); - auto geometry = defs::xy{modGeometry.x * portperModGeometry.x, - modGeometry.y * portperModGeometry.y}; - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_GEOMETRY.data(), geometry)); -} - -void test_master_file_image_size(const Detector &det, - const std::optional &doc) { - - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - int bytes_per_pixel = det.getDynamicRange().squash() / 8; - detParameters par(det_type); - - int image_size = 0; - switch (det_type) { - - case defs::EIGER: { - int num_chips = (par.nChipX / 2); - image_size = par.nChanX * par.nChanY * num_chips * bytes_per_pixel; - } break; - - case defs::JUNGFRAU: - case defs::MOENCH: { - auto num_udp_interfaces = det.getNumberofUDPInterfaces().tsquash( - "inconsistent number of udp interfaces"); - image_size = (par.nChanX * par.nChanY * par.nChipX * par.nChipY * - bytes_per_pixel) / - num_udp_interfaces; - } break; - - case defs::MYTHEN3: { - int counter_mask = det.getCounterMask().squash(); - int num_counters = __builtin_popcount(counter_mask); - int num_channels_per_counter = par.nChanX / MAX_NUM_COUNTERS; - image_size = num_channels_per_counter * num_counters * par.nChipX * - bytes_per_pixel; - } break; - - case defs::GOTTHARD2: { - image_size = par.nChanX * par.nChipX * bytes_per_pixel; - } break; - - case defs::CHIPTESTBOARD: - case defs::XILINX_CHIPTESTBOARD: { - testCtbAcquireInfo test_info{}; - image_size = calculate_ctb_image_size( - test_info, (det_type == defs::XILINX_CHIPTESTBOARD)) - .first; - } break; - - default: - throw sls::RuntimeError("Unsupported detector type for this test"); - } - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_IMAGE_SIZE.data(), image_size)); -} - -void test_master_file_det_size(const Detector &det, - const std::optional &doc) { - - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - auto portSize = det.getPortSize()[0]; - - // m3 assumes all counters enabled when getting num channels from client - // TODO: in future, remove assumption - if (det_type == defs::MYTHEN3) { - int nchan = portSize.x / MAX_NUM_COUNTERS; - auto counter_mask = det.getCounterMask().tsquash( - "Inconsistent counter mask for Mythen3 detector"); - int num_counters = __builtin_popcount(counter_mask); - portSize.x = nchan * num_counters; - } else if (det_type == defs::CHIPTESTBOARD || - det_type == defs::XILINX_CHIPTESTBOARD) { - testCtbAcquireInfo test_info{}; - portSize.x = calculate_ctb_image_size( - test_info, det_type == defs::XILINX_CHIPTESTBOARD) - .second; - portSize.y = 1; - } - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_PIXELS.data(), portSize)); -} - -void test_master_file_max_frames_per_file(const Detector &det, - const std::optional &doc) { - auto max_frames_per_file = - det.getFramesPerFile().tsquash("Inconsistent max frames per file"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_MAX_FRAMES_PER_FILE.data(), - max_frames_per_file)); -} - -void test_master_file_frame_discard_policy(const Detector &det, - const std::optional &doc) { - auto policy = det.getRxFrameDiscardPolicy().tsquash( - "Inconsistent frame discard policy"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_FRAME_DISCARD_POLICY.data(), - ToString(policy))); -} - -void test_master_file_frame_padding(const Detector &det, - const std::optional &doc) { - auto padding = static_cast( - det.getPartialFramesPadding().tsquash("Inconsistent frame padding")); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_FRAME_PADDING.data(), padding)); -} - -void test_master_file_scan_parameters(const Detector &det, - const std::optional &doc) { - auto scan_params = det.getScan().tsquash("Inconsistent scan parameters"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_SCAN_PARAMETERS.data(), scan_params)); -} - -void test_master_file_total_frames(const Detector &det, - const std::optional &doc) { - uint64_t repeats = - det.getNumberOfTriggers().tsquash("Inconsistent number of triggers"); - uint64_t numFrames = - det.getNumberOfFrames().tsquash("Inconsistent number of frames"); - int numAdditionalStorageCells = 0; - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types"); - if (det_type == defs::GOTTHARD2) { - auto timing_mode = - det.getTimingMode().tsquash("Inconsistent timing mode"); - auto burst_mode = det.getBurstMode().tsquash("Inconsistent burst mode"); - auto numBursts = - det.getNumberOfBursts().tsquash("Inconsistent number of bursts"); - // auto - if (timing_mode == defs::AUTO_TIMING) { - // burst mode, repeats = #bursts - if (burst_mode == defs::BURST_INTERNAL || - burst_mode == defs::BURST_EXTERNAL) { - repeats = numBursts; - } - // continuous, repeats = 1 (no trigger as well) - else { - repeats = 1; - } - } - // trigger - else { - // continuous, numFrames is limited - if (burst_mode == defs::CONTINUOUS_INTERNAL || - burst_mode == defs::CONTINUOUS_EXTERNAL) { - numFrames = 1; - } - } - } else if (det_type == defs::JUNGFRAU) { - numAdditionalStorageCells = - det.getNumberOfAdditionalStorageCells().tsquash( - "Inconsistent number of additional storage cells"); - } - uint64_t total_frames = - numFrames * repeats * (int64_t)(numAdditionalStorageCells + 1); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TOTAL_FRAMES.data(), total_frames)); -} - -void test_master_file_rois(const Detector &det, - const std::optional &doc) { - auto rois = det.getRxROI(); - auto detsize = det.getDetectorSize(); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); - // compensate for m3 channel size and counter mask mess - if (det_type == defs::MYTHEN3) { - int nchan = detsize.x / MAX_NUM_COUNTERS; - auto counter_mask = det.getCounterMask().tsquash( - "Inconsistent counter mask for Mythen3 detector"); - int num_counters = __builtin_popcount(counter_mask); - detsize.x = nchan * num_counters; - } - // replace -1 for complete ROI - bool is2D = (detsize.y > 1); - for (auto &roi : rois) { - if (roi.completeRoi()) { - roi.xmin = 0; - roi.xmax = detsize.x - 1; - if (is2D) { - roi.ymin = 0; - roi.ymax = detsize.y - 1; - } - } - } - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_RECEIVER_ROIS.data(), rois)); -} - -void test_master_file_exptime(const Detector &det, - const std::optional &doc) { - auto exptime = det.getExptime().tsquash("Inconsistent exposure time"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_EXPOSURE_TIME.data(), ToString(exptime))); -} - -void test_master_file_period(const Detector &det, - const std::optional &doc) { - auto period = det.getPeriod().tsquash("Inconsistent period"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_ACQUISITION_PERIOD.data(), ToString(period))); -} - -void test_master_file_num_udp_interfaces(const Detector &det, - const std::optional &doc) { - auto num_udp_interfaces = det.getNumberofUDPInterfaces().tsquash( - "Inconsistent number of UDP interfaces"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_NUM_UDP_INTERFACES.data(), - num_udp_interfaces)); -} - -void test_master_file_read_n_rows(const Detector &det, - const std::optional &doc) { - auto readnrows = det.getReadNRows().tsquash("Inconsistent number of rows"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_NUMBER_OF_ROWS.data(), readnrows)); -} - -void test_master_file_readout_speed(const Detector &det, - const std::optional &doc) { - auto readout_speed = - det.getReadoutSpeed().tsquash("Inconsistent readout speed"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_READOUT_SPEED.data(), - ToString(readout_speed))); -} - -void test_master_file_frames_in_file(const std::optional &doc, - const int frames_in_file) { - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_FRAMES_IN_FILE.data(), frames_in_file)); -} - -void test_master_file_json_header(const Detector &det, - const std::optional &doc) { - auto json_header = - det.getAdditionalJsonHeader().tsquash("Inconsistent JSON header"); - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_ADDITIONAL_JSON_HEADER.data(), json_header)); -} - -void test_master_file_dynamic_range(const Detector &det, - const std::optional &doc) { - auto dr = det.getDynamicRange().tsquash("Inconsistent dynamic range"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DYNAMIC_RANGE.data(), dr)); -} - -void test_master_file_ten_giga(const Detector &det, - const std::optional &doc) { - auto ten_giga = - static_cast(det.getTenGiga().tsquash("Inconsistent ten giga")); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TEN_GIGA.data(), ten_giga)); -} - -void test_master_file_threshold_energy(const Detector &det, - const std::optional &doc) { - auto threshold = - det.getThresholdEnergy().tsquash("Inconsistent threshold energy"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_THRESHOLD_ENERGY.data(), threshold)); -} - -void test_master_file_sub_exptime(const Detector &det, - const std::optional &doc) { - auto sub_exptime = - det.getSubExptime().tsquash("Inconsistent sub exposure time"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_SUB_EXPOSURE_TIME.data(), - ToString(sub_exptime))); -} - -void test_master_file_sub_period(const Detector &det, - const std::optional &doc) { - auto exptime = det.getSubExptime().tsquash("Inconsistent sub exptime"); - auto deadtime = det.getSubDeadTime().tsquash("Inconsistent sub deadtime"); - auto sub_period = exptime + deadtime; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_SUB_ACQUISITION_PERIOD.data(), - ToString(sub_period))); -} - -void test_master_file_quad(const Detector &det, - const std::optional &doc) { - auto quad = static_cast(det.getQuad().tsquash("Inconsistent quad")); - - REQUIRE_NOTHROW( - check_master_file(doc, MasterAttributes::N_QUAD.data(), quad)); -} - -void test_master_file_rate_corrections(const Detector &det, - const std::optional &doc) { - std::vector dead_times; - for (auto item : det.getRateCorrection()) - dead_times.push_back(item.count()); - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_RATE_CORRECTIONS.data(), dead_times)); -} - -void test_master_file_counter_mask(const Detector &det, - const std::optional &doc) { - auto counter_mask = static_cast( - det.getCounterMask().tsquash("Inconsistent counter mask")); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_COUNTER_MASK.data(), counter_mask)); -} - -void test_master_file_exptimes(const Detector &det, - const std::optional &doc) { - auto exptimes = - det.getExptimeForAllGates().tsquash("Inconsistent exposure times"); - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_EXPOSURE_TIMES.data(), exptimes)); -} - -void test_master_file_gate_delays(const Detector &det, - const std::optional &doc) { - auto gate_delays = - det.getGateDelayForAllGates().tsquash("Inconsistent GateDelay"); - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_GATE_DELAYS.data(), gate_delays)); -} - -void test_master_file_gates(const Detector &det, - const std::optional &doc) { - auto gates = det.getNumberOfGates().tsquash("Inconsistent number of gates"); - - REQUIRE_NOTHROW( - check_master_file(doc, MasterAttributes::N_GATES.data(), gates)); -} - -void test_master_file_threadhold_energies(const Detector &det, - const std::optional &doc) { - auto threshold_energies = - det.getAllThresholdEnergy().tsquash("Inconsistent threshold energies"); - - REQUIRE_NOTHROW(check_master_file>( - doc, MasterAttributes::N_THRESHOLD_ENERGIES.data(), - threshold_energies)); -} - -void test_master_file_burst_mode(const Detector &det, - const std::optional &doc) { - auto burst_mode = det.getBurstMode().tsquash("Inconsistent burst mode"); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_BURST_MODE.data(), ToString(burst_mode))); -} - -void test_master_file_adc_mask(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_ctb_config{}; - auto adc_mask = test_ctb_config.adc_enable_10g; - auto det_type = det.getDetectorType().squash(); - if (det_type == defs::CHIPTESTBOARD) { - auto tengiga = test_ctb_config.ten_giga; - if (!tengiga) - adc_mask = test_ctb_config.adc_enable_1g; - } - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_ADC_MASK.data(), adc_mask)); -} - -void test_master_file_analog_flag(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto romode = test_info.readout_mode; - auto analog = static_cast( - (romode == defs::ANALOG_ONLY || romode == defs::ANALOG_AND_DIGITAL)); - - REQUIRE_NOTHROW( - check_master_file(doc, MasterAttributes::N_ANALOG.data(), analog)); -} - -void test_master_file_analog_samples(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto analog_samples = test_info.num_adc_samples; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_ANALOG_SAMPLES.data(), analog_samples)); -} - -void test_master_file_digital_flag(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto romode = test_info.readout_mode; - auto digital = static_cast(romode == defs::DIGITAL_ONLY || - romode == defs::ANALOG_AND_DIGITAL || - romode == defs::DIGITAL_AND_TRANSCEIVER); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DIGITAL.data(), digital)); -} - -void test_master_file_digital_samples(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto digital_samples = test_info.num_dbit_samples; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DIGITAL_SAMPLES.data(), digital_samples)); -} - -void test_master_file_dbit_offset(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto dbit_offset = test_info.dbit_offset; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DBIT_OFFSET.data(), dbit_offset)); -} - -void test_master_file_dbit_reorder(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto dbit_reorder = test_info.dbit_reorder; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DBIT_REORDER.data(), dbit_reorder)); -} - -void test_master_file_dbit_bitset(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - uint64_t dbit_bitset = 0; - for (auto &i : test_info.dbit_list) { - dbit_bitset |= (static_cast(1) << i); - } - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_DBIT_BITSET.data(), dbit_bitset)); -} - -void test_master_file_transceiver_mask(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto trans_mask = test_info.transceiver_mask; - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TRANSCEIVER_MASK.data(), trans_mask)); -} - -void test_master_file_transceiver_flag(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto romode = test_info.readout_mode; - auto trans = static_cast(romode == defs::DIGITAL_AND_TRANSCEIVER || - romode == defs::TRANSCEIVER_ONLY); - - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TRANSCEIVER.data(), trans)); -} - -void test_master_file_transceiver_samples(const Detector &det, - const std::optional &doc) { - testCtbAcquireInfo test_info{}; - auto trans_samples = test_info.num_trans_samples; - REQUIRE_NOTHROW(check_master_file( - doc, MasterAttributes::N_TRANSCEIVER_SAMPLES.data(), trans_samples)); -} - -void test_master_file_common_metadata(const Detector &det, - const std::optional &doc) { - test_master_file_version(det, doc); - test_master_file_type(det, doc); - test_master_file_timing_mode(det, doc); - test_master_file_geometry(det, doc); - test_master_file_image_size(det, doc); - test_master_file_det_size(det, doc); - test_master_file_max_frames_per_file(det, doc); - test_master_file_frame_discard_policy(det, doc); - test_master_file_frame_padding(det, doc); - test_master_file_scan_parameters(det, doc); - test_master_file_total_frames(det, doc); - test_master_file_json_header(det, doc); - // TODO: test frame header format? -} - -void test_master_file_jungfrau_metadata(const Detector &det, - const std::optional &doc) { - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Jungfrau specific metadata - REQUIRE_NOTHROW(test_master_file_rois(det, doc)); - REQUIRE_NOTHROW(test_master_file_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_num_udp_interfaces(det, doc)); - REQUIRE_NOTHROW(test_master_file_read_n_rows(det, doc)); - REQUIRE_NOTHROW(test_master_file_readout_speed(det, doc)); -} - -void test_master_file_eiger_metadata(const Detector &det, - const std::optional &doc) { - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Eiger specific metadata - REQUIRE_NOTHROW(test_master_file_rois(det, doc)); - REQUIRE_NOTHROW(test_master_file_dynamic_range(det, doc)); - REQUIRE_NOTHROW(test_master_file_ten_giga(det, doc)); - REQUIRE_NOTHROW(test_master_file_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_threshold_energy(det, doc)); - REQUIRE_NOTHROW(test_master_file_sub_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_sub_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_quad(det, doc)); - REQUIRE_NOTHROW(test_master_file_read_n_rows(det, doc)); - REQUIRE_NOTHROW(test_master_file_rate_corrections(det, doc)); - REQUIRE_NOTHROW(test_master_file_readout_speed(det, doc)); -} - -void test_master_file_moench_metadata(const Detector &det, - const std::optional &doc) { - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Moench specific metadata - REQUIRE_NOTHROW(test_master_file_rois(det, doc)); - REQUIRE_NOTHROW(test_master_file_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_num_udp_interfaces(det, doc)); - REQUIRE_NOTHROW(test_master_file_read_n_rows(det, doc)); - REQUIRE_NOTHROW(test_master_file_readout_speed(det, doc)); -} - -void test_master_file_mythen3_metadata(const Detector &det, - const std::optional &doc) { - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Mythen3 specific metadata - REQUIRE_NOTHROW(test_master_file_rois(det, doc)); - REQUIRE_NOTHROW(test_master_file_dynamic_range(det, doc)); - REQUIRE_NOTHROW(test_master_file_ten_giga(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_counter_mask(det, doc)); - REQUIRE_NOTHROW(test_master_file_exptimes(det, doc)); - REQUIRE_NOTHROW(test_master_file_gate_delays(det, doc)); - REQUIRE_NOTHROW(test_master_file_gates(det, doc)); - REQUIRE_NOTHROW(test_master_file_threadhold_energies(det, doc)); - REQUIRE_NOTHROW(test_master_file_readout_speed(det, doc)); -} - -void test_master_file_gotthard2_metadata(const Detector &det, - const std::optional &doc) { - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Gotthard2 specific metadata - REQUIRE_NOTHROW(test_master_file_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - REQUIRE_NOTHROW(test_master_file_burst_mode(det, doc)); - REQUIRE_NOTHROW(test_master_file_readout_speed(det, doc)); -} - -void test_master_file_ctb_metadata(const Detector &det, - const std::optional &doc) { - auto det_type = det.getDetectorType().squash(); - REQUIRE_NOTHROW(test_master_file_common_metadata(det, doc)); - // Ctb specific metadata - REQUIRE_NOTHROW(test_master_file_exptime(det, doc)); - REQUIRE_NOTHROW(test_master_file_period(det, doc)); - if (det_type == defs::CHIPTESTBOARD) - REQUIRE_NOTHROW(test_master_file_ten_giga(det, doc)); - REQUIRE_NOTHROW(test_master_file_adc_mask(det, doc)); - REQUIRE_NOTHROW(test_master_file_analog_flag(det, doc)); - REQUIRE_NOTHROW(test_master_file_analog_samples(det, doc)); - REQUIRE_NOTHROW(test_master_file_digital_flag(det, doc)); - REQUIRE_NOTHROW(test_master_file_digital_samples(det, doc)); - REQUIRE_NOTHROW(test_master_file_dbit_offset(det, doc)); - REQUIRE_NOTHROW(test_master_file_dbit_reorder(det, doc)); - REQUIRE_NOTHROW(test_master_file_dbit_bitset(det, doc)); - REQUIRE_NOTHROW(test_master_file_transceiver_mask(det, doc)); - REQUIRE_NOTHROW(test_master_file_transceiver_flag(det, doc)); - REQUIRE_NOTHROW(test_master_file_transceiver_samples(det, doc)); -} - -void test_master_file_metadata(const Detector &det, - const std::optional &doc) { - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types"); - switch (det_type) { - case defs::JUNGFRAU: - test_master_file_jungfrau_metadata(det, doc); - break; - case defs::EIGER: - test_master_file_eiger_metadata(det, doc); - break; - case defs::MOENCH: - test_master_file_moench_metadata(det, doc); - break; - case defs::MYTHEN3: - test_master_file_mythen3_metadata(det, doc); - break; - case defs::GOTTHARD2: - test_master_file_gotthard2_metadata(det, doc); - break; - case defs::CHIPTESTBOARD: - case defs::XILINX_CHIPTESTBOARD: - test_master_file_ctb_metadata(det, doc); - break; - default: - break; - } -} - -Document parse_binary_master_attributes(std::string file_path) { - REQUIRE(std::filesystem::exists(file_path) == true); - std::ifstream file(file_path); - REQUIRE(file.is_open()); - std::stringstream buffer; - buffer << file.rdbuf(); - std::string json_str = buffer.str(); - - Document doc; - ParseResult result = doc.Parse(json_str.c_str()); - if (!result) { - std::cout << "JSON parse error: " << GetParseError_En(result.Code()) - << " (at offset " << result.Offset() << ")" << std::endl; - - // Optional: Show problematic snippet - size_t offset = result.Offset(); - std::string context = - json_str.substr(std::max(0, (int)offset - 20), 40); - std::cout << "Context around error: \"" << context << "\"" << std::endl; - } - REQUIRE(result); - return doc; -} - -#ifdef HDF5C -void open_hdf5_file(const std::string &file_path) { - REQUIRE(std::filesystem::exists(file_path) == true); - h5File = std::make_optional(file_path, H5F_ACC_RDONLY); - REQUIRE(H5Lexists(h5File->getId(), HDF5_GROUP.c_str(), H5P_DEFAULT) == - true); -} -#endif +namespace mf = sls::test::master_file; +namespace acq = sls::test::acquire; +namespace checks = sls::test::checks; TEST_CASE("check_master_file_attributes", "[.detectorintegration][.disable_check_data_file]") { Detector det; - Caller caller(&det); - auto det_type = - det.getDetectorType().tsquash("Inconsistent detector types to test"); + auto detType = det.getDetectorType().squash(defs::GENERIC); + INFO("Testing master file attributes with " << ToString(detType)); - int64_t num_frames = 1; - switch (det_type) { - case defs::JUNGFRAU: - case defs::EIGER: - case defs::MOENCH: - case defs::MYTHEN3: - case defs::GOTTHARD2: - create_files_for_acquire(det, caller, num_frames); - break; - case defs::CHIPTESTBOARD: - case defs::XILINX_CHIPTESTBOARD: { - testCtbAcquireInfo test_ctb_config{}; - create_files_for_acquire(det, caller, num_frames, test_ctb_config); - } break; - default: - throw sls::RuntimeError("Unsupported detector type for this test"); + // currently num frame = 1 (default) + auto acq_state = acq::default_acquisition_state(); + auto file_state = acq::default_file_state(); + + // if ctb, set to default and restore after test + std::optional ctb_state = std::nullopt; + if (detType == defs::CHIPTESTBOARD || + detType == defs::XILINX_CHIPTESTBOARD) { + ctb_state = std::make_optional(acq::default_ctb_state(detType)); } + acq::CTBStateGuard ctb_guard(det, ctb_state); - testFileInfo file_info; - std::string master_file_prefix = file_info.getMasterFileNamePrefix(); + // binary => /tmp/sls_test_master_0.json + file_state.file_format = defs::BINARY; + acq::run(det, acq_state, file_state); - // binary - std::string fname = - master_file_prefix + ".json"; // /tmp/sls_test_master_0.json - auto doc = std::make_optional(parse_binary_master_attributes(fname)); - test_master_file_metadata(det, doc); - test_master_file_frames_in_file(doc, num_frames); + std::string fname = acq::get_master_file_name(file_state); + mf::Checker checker(fname); + + // get expected state of parameters and check against master file + acq::ExpectedState expected_state = + acq::build_expected_state(det, acq_state, file_state, ctb_state); + checks::check_metadata(checker, expected_state); - // hdf5 #ifdef HDF5C - fname = master_file_prefix + ".h5"; // /tmp/sls_test_master_0.h5 try { - open_hdf5_file(fname); - test_master_file_metadata(det, std::nullopt); - test_master_file_frames_in_file(std::nullopt, num_frames); + // hdf5 => /tmp/sls_test_master_0.h5 + file_state.file_format = defs::HDF5; + acq::run(det, acq_state, file_state); + + std::string fname = acq::get_master_file_name(file_state); + mf::Checker checker(fname); + + // get expected state of parameters and check against master file + acq::ExpectedState expected_state = + acq::build_expected_state(det, acq_state, file_state, ctb_state); + checks::check_metadata(checker, expected_state); } catch (H5::Exception &e) { LOG(logERROR) << "HDF5 error: " << e.getDetailMsg(); throw; diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp index 828f16316..2e8d8a9bd 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp @@ -1,20 +1,21 @@ // SPDX-License-Identifier: LGPL-3.0-or-other // Copyright (C) 2021 Contributors to the SLS Detector Package #include "Caller.h" -#include "catch.hpp" #include "sls/Detector.h" #include "sls/Version.h" -#include "sls/sls_detector_defs.h" -#include "test-Caller-global.h" - -#include -#include - #include "sls/versionAPI.h" +#include "test-Caller-global.h" #include "tests/globals.h" +#include "acquire/Acquire.h" +#include "acquire/FileState.h" + +#include "catch.hpp" + namespace sls { +namespace acq = sls::test::acquire; + using test::GET; using test::PUT; @@ -835,19 +836,22 @@ TEST_CASE("rx_roi", "[.detectorintegration][.disable_check_data_file]") { // check master file creation // TODO: check roi in master file { - REQUIRE_NOTHROW(create_files_for_acquire(det, caller)); - testFileInfo file_info; - std::string master_file_prefix = - file_info.getMasterFileNamePrefix(); + auto acq_state = acq::default_acquisition_state(); + auto file_state = acq::default_file_state(); + + file_state.file_format = defs::BINARY; + acq::run(det, acq_state, file_state); + std::string fname = acq::get_master_file_name(file_state); + REQUIRE(std::filesystem::exists(fname)); - std::string fname = master_file_prefix + ".json"; - REQUIRE(std::filesystem::exists(fname) == true); #ifdef HDF5C - fname = master_file_prefix + ".h5"; - REQUIRE(std::filesystem::exists(fname) == true); + file_state.file_format = defs::HDF5; + acq::run(det, acq_state, file_state); + fname = acq::get_master_file_name(file_state); + REQUIRE(std::filesystem::exists(fname)); if (det.size() > 1 || numinterfaces > 1) { - fname = file_info.getVirtualFileName(); - REQUIRE(std::filesystem::exists(fname) == true); + fname = acq::get_virtual_file_name(file_state); + REQUIRE(std::filesystem::exists(fname)); } #endif } diff --git a/slsDetectorSoftware/tests/acquire/Acquire.cpp b/slsDetectorSoftware/tests/acquire/Acquire.cpp new file mode 100644 index 000000000..bc738174d --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/Acquire.cpp @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package + +#include "Acquire.h" +#include "FileState.h" + +#include "catch.hpp" +#include +#include + +namespace sls::test::acquire { + +void wait_until_idle(const Detector &det) { + bool idle = false; + while (!idle) { + auto statusList = det.getDetectorStatus(); + if (statusList.any(defs::ERROR)) { + throw RuntimeError("error status during acquisition"); + } + if (statusList.contains_only(defs::IDLE, defs::STOPPED)) { + idle = true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } +} + +void run_acquisition(Detector &det) { + det.startReceiver(); + det.startDetector(); + wait_until_idle(det); + det.stopReceiver(); +} + +void run(Detector &det, const AcquisitionState &acq_state, + const FileState &file_state) { + + INFO(ToString(det.getDetectorType().squash(defs::GENERIC)) + << " acquiring with num_frames = " << acq_state.num_frames); + + FileStateGuard file_guard(det, file_state); + AcquisitionStateGuard acq_guard(det, acq_state); + run_acquisition(det); + auto frames_caught = det.getFramesCaught()[0][0]; + REQUIRE(frames_caught == acq_state.num_frames); +} + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/acquire/Acquire.h b/slsDetectorSoftware/tests/acquire/Acquire.h new file mode 100644 index 000000000..47cfc56fb --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/Acquire.h @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package + +#pragma once + +#include "sls/Detector.h" +#include "sls/logger.h" + +namespace sls::test::acquire { + +class FileState; + +// at the moment, only number of frames +struct AcquisitionState { + int64_t num_frames; +}; + +inline AcquisitionState default_acquisition_state() { return {1}; } + +inline AcquisitionState get_acquisition_state(const Detector &det) { + return AcquisitionState{ + det.getNumberOfFrames().tsquash("Inconsistent number of frames")}; +} + +inline void set_acquisition_state(Detector &det, const AcquisitionState &s) { + det.setNumberOfFrames(s.num_frames); +} + +inline std::ostream &operator<<(std::ostream &os, const AcquisitionState &s) { + os << "Acquisition State:" + << "\n Number of Frames: " << s.num_frames; + return os; +} + +/** + * @brief RAII guard for restoring the acquisition state of a detector. + * The constructor saves the current acquisition state and sets a new state, + * while the destructor restores the original state. + * + */ +class AcquisitionStateGuard { + public: + explicit AcquisitionStateGuard(Detector &det, + const AcquisitionState &new_state) + : det(det), saved_(get_acquisition_state(det)) { + set_acquisition_state(det, new_state); + } + ~AcquisitionStateGuard() { set_acquisition_state(det, saved_); } + + private: + Detector &det; + AcquisitionState saved_; +}; + +void wait_until_idle(const Detector &det); +void run_acquisition(Detector &det); +void run(Detector &det, const AcquisitionState &acq_state, + const FileState &file_state); + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/acquire/CTBState.h b/slsDetectorSoftware/tests/acquire/CTBState.h new file mode 100644 index 000000000..dede4d9e4 --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/CTBState.h @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "sls/Detector.h" +#include "sls/logger.h" + +#include +#include +#include + +namespace sls::test::acquire { + +struct CTBState { + defs::readoutMode readout_mode; + int num_adc_samples; + int num_dbit_samples; + int num_trans_samples; + bool ten_giga; + uint32_t adc_enable_1g; + uint32_t adc_enable_10g; + int dbit_offset; + std::vector dbit_list; + bool dbit_reorder; + uint32_t transceiver_mask; +}; + +/** an example of CTB config */ +inline CTBState +default_ctb_state(const defs::detectorType &detType = defs::GENERIC) { + if (detType == defs::GENERIC) { + throw sls::RuntimeError( + "Cannot determine default CTB state for generic detector type."); + } + auto isAltera = (detType == defs::CHIPTESTBOARD); + return {defs::ANALOG_AND_DIGITAL, 5000, 6000, 288, + isAltera ? false : true, 0xFFFFFF00, 0xFF00FFFF, 0, + {0, 12, 2, 43}, false, 0x3}; +} + +inline CTBState get_ctb_state(const Detector &det) { + auto isAltera = + (det.getDetectorType().squash(defs::GENERIC) == defs::CHIPTESTBOARD); + return CTBState{ + det.getReadoutMode().tsquash("Inconsistent readout mode"), + det.getNumberOfAnalogSamples().tsquash( + "Inconsistent number of analog samples"), + det.getNumberOfDigitalSamples().tsquash( + "Inconsistent number of digital samples"), + det.getNumberOfTransceiverSamples().tsquash( + "Inconsistent number of transceiver samples"), + isAltera ? det.getTenGiga().tsquash("Inconsisten ten giga enable") + : true, + isAltera + ? det.getADCEnableMask().tsquash("Inconsistent adc enable mask") + : 0, + det.getTenGigaADCEnableMask().tsquash( + "Inconsistent ten giga adc enable mask"), + det.getRxDbitOffset().tsquash("Inconsistent rx dbit offset"), + det.getRxDbitList().tsquash("Inconsistent rx dbit list"), + det.getRxDbitReorder().tsquash("Inconsistent rx dbit reorder"), + det.getTransceiverEnableMask().tsquash( + "Inconsistent transceiver mask")}; +} + +inline void set_ctb_state(Detector &det, const CTBState &s) { + auto isAltera = + (det.getDetectorType().squash(defs::GENERIC) == defs::CHIPTESTBOARD); + det.setReadoutMode(s.readout_mode); + if (isAltera) { + det.setTenGiga(s.ten_giga); + det.setADCEnableMask(s.adc_enable_1g); + } + det.setNumberOfAnalogSamples(s.num_adc_samples); + det.setNumberOfDigitalSamples(s.num_dbit_samples); + det.setNumberOfTransceiverSamples(s.num_trans_samples); + det.setTenGigaADCEnableMask(s.adc_enable_10g); + det.setRxDbitOffset(s.dbit_offset); + det.setRxDbitList(s.dbit_list); + det.setRxDbitReorder(s.dbit_reorder); + det.setTransceiverEnableMask(s.transceiver_mask); +} + +inline std::ostream &operator<<(std::ostream &os, const CTBState &s) { + os << "CTB State:" + << "\n Readout Mode: " << ToString(s.readout_mode) + << "\n Num ADC Samples: " << s.num_adc_samples + << "\n Num DBIT Samples: " << s.num_dbit_samples + << "\n Num Trans Samples: " << s.num_trans_samples + << "\n Ten Giga: " << s.ten_giga + << "\n ADC Enable 1G: " << ToStringHex(s.adc_enable_1g) + << "\n ADC Enable 10G: " << ToStringHex(s.adc_enable_10g) + << "\n DBIT Offset: " << s.dbit_offset + << "\n DBIT List: " << ToString(s.dbit_list) + << "\n DBIT Reorder: " << s.dbit_reorder + << "\n Transceiver Mask: " << ToStringHex(s.transceiver_mask); + return os; +} + +inline void print_current_ctb_state(const Detector &det) { + auto ctb_state = get_ctb_state(det); + LOG(logINFO) << ctb_state; +} + +/** + * @brief RAII guard for restoring the existing ctb configuration after a test. + * + * The constructor saves the current ctb config and sets a new config, while the + * destructor restores the original config. + */ +class CTBStateGuard { + public: + explicit CTBStateGuard(Detector &det, + std::optional new_state = std::nullopt) + : det(det) { + auto type = det.getDetectorType().squash(defs::GENERIC); + if (type == defs::CHIPTESTBOARD || type == defs::XILINX_CHIPTESTBOARD) { + active = true; + if (!new_state.has_value()) { + throw sls::RuntimeError( + "New CTB state must be provided for CTBStateGuard."); + } + saved_ = get_ctb_state(det); + set_ctb_state(det, new_state.value()); + } + } + ~CTBStateGuard() { + if (active) + set_ctb_state(det, saved_); + } + + private: + Detector &det; + bool active{false}; + CTBState saved_; +}; + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/acquire/ExpectedState.cpp b/slsDetectorSoftware/tests/acquire/ExpectedState.cpp new file mode 100644 index 000000000..17a5edcf0 --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/ExpectedState.cpp @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package + +#include "ExpectedState.h" +#include "Caller/test-Caller-global.h" +#include "receiver_defs.h" + +// unnamed namespace for internal linkage +namespace { +using sls::defs; +using sls::Detector; +using ns = std::chrono::nanoseconds; +namespace acq = sls::test::acquire; + +defs::detectorType get_detector_type(const Detector &det) { + return det.getDetectorType().tsquash("Inconsistent detector type"); +} + +defs::xy get_geometry(const Detector &det) { + auto modGeometry = det.getModuleGeometry(); + auto portperModGeometry = det.getPortPerModuleGeometry(); + return defs::xy{modGeometry.x * portperModGeometry.x, + modGeometry.y * portperModGeometry.y}; +} + +int get_dynamic_range(const Detector &det) { + return det.getDynamicRange().tsquash("Inconsistent dynamic range"); +} + +defs::xy get_port_shape(const Detector &det, + std::optional &ctb_state) { + auto det_type = get_detector_type(det); + auto portSize = det.getPortSize()[0]; + + // m3 assumes all counters enabled when getting num channels from client + // TODO: in future, remove assumption + if (det_type == defs::MYTHEN3) { + int nchan = portSize.x / MAX_NUM_COUNTERS; + auto counter_mask = det.getCounterMask().tsquash( + "Inconsistent counter mask for Mythen3 detector"); + int num_counters = __builtin_popcount(counter_mask); + portSize.x = nchan * num_counters; + } else if (det_type == defs::CHIPTESTBOARD || + det_type == defs::XILINX_CHIPTESTBOARD) { + if (!ctb_state.has_value()) { + throw sls::RuntimeError( + "CTB state must be provided to calculate expected port shape"); + } + portSize.x = + sls::calculate_ctb_image_size( + ctb_state.value(), det_type == defs::XILINX_CHIPTESTBOARD) + .second; + portSize.y = 1; + } + return portSize; +} + +uint64_t get_total_frames(const Detector &det) { + uint64_t repeats = + det.getNumberOfTriggers().tsquash("Inconsistent number of triggers"); + uint64_t numFrames = + det.getNumberOfFrames().tsquash("Inconsistent number of frames"); + int numAdditionalStorageCells = 0; + auto det_type = get_detector_type(det); + if (det_type == defs::GOTTHARD2) { + auto timing_mode = + det.getTimingMode().tsquash("Inconsistent timing mode"); + auto burst_mode = det.getBurstMode().tsquash("Inconsistent burst mode"); + auto numBursts = + det.getNumberOfBursts().tsquash("Inconsistent number of bursts"); + if (timing_mode == defs::AUTO_TIMING) { + // burst mode, repeats = #bursts + if (burst_mode == defs::BURST_INTERNAL || + burst_mode == defs::BURST_EXTERNAL) { + repeats = numBursts; + } + // continuous, repeats = 1 (no trigger as well) + else { + repeats = 1; + } + } else { + // trigger + // continuous, numFrames is limited + if (burst_mode == defs::CONTINUOUS_INTERNAL || + burst_mode == defs::CONTINUOUS_EXTERNAL) { + numFrames = 1; + } + } + } else if (det_type == defs::JUNGFRAU) { + numAdditionalStorageCells = + det.getNumberOfAdditionalStorageCells().tsquash( + "Inconsistent number of additional storage cells"); + } + uint64_t total_frames = + numFrames * repeats * (int64_t)(numAdditionalStorageCells + 1); + return total_frames; +} + +std::vector get_rois(const Detector &det) { + auto rois = det.getRxROI(); + auto detsize = det.getDetectorSize(); + auto det_type = get_detector_type(det); + // compensate for m3 channel size and counter mask mess + if (det_type == defs::MYTHEN3) { + int nchan = detsize.x / MAX_NUM_COUNTERS; + auto counter_mask = det.getCounterMask().tsquash( + "Inconsistent counter mask for Mythen3 detector"); + int num_counters = __builtin_popcount(counter_mask); + detsize.x = nchan * num_counters; + } + // replace -1 for complete ROI + bool is2D = (detsize.y > 1); + for (auto &roi : rois) { + if (roi.completeRoi()) { + roi.xmin = 0; + roi.xmax = detsize.x - 1; + if (is2D) { + roi.ymin = 0; + roi.ymax = detsize.y - 1; + } + } + } + return rois; +} + +ns get_exptime(const Detector &det) { + return ns(det.getExptime().tsquash("Inconsistent exposure time")); +} + +ns get_period(const Detector &det) { + return ns(det.getPeriod().tsquash("Inconsistent exposure time")); +} + +int get_num_udp_interfaces(const Detector &det) { + return det.getNumberofUDPInterfaces().tsquash( + "Inconsistent number of UDP interfaces"); +} + +int get_read_n_rows(const Detector &det) { + return det.getReadNRows().tsquash("Inconsistent number of read rows"); +} + +defs::speedLevel get_readout_speed(const Detector &det) { + return det.getReadoutSpeed().tsquash("Inconsistent readout speed"); +} + +bool get_ten_giga(const Detector &det) { + return det.getTenGiga().tsquash("Inconsistent 10Giga setting"); +} + +std::pair get_sub_exptime_and_sub_period(const Detector &det) { + auto exptime = det.getSubExptime().tsquash("Inconsistent sub exptime"); + auto deadtime = det.getSubDeadTime().tsquash("Inconsistent sub deadtime"); + auto sub_period = exptime + deadtime; + return std::make_pair(ns(exptime), ns(sub_period)); +} + +acq::CommonExpectedState +build_common_state(const Detector &det, + std::optional ctb_state) { + acq::CommonExpectedState e; + e.det_type = get_detector_type(det); + e.timing_mode = det.getTimingMode().tsquash("Inconsistent timing mode"); + e.geometry = get_geometry(det); + e.image_size = acq::get_expected_image_size(det, ctb_state); + e.port_shape = get_port_shape(det, ctb_state); + e.max_frames_per_file = + det.getFramesPerFile().tsquash("Inconsistent frames per file"); + e.frame_discard_policy = det.getRxFrameDiscardPolicy().tsquash( + "Inconsistent frame discard policy"); + e.partial_frames_padding = static_cast( + det.getPartialFramesPadding().tsquash("Inconsistent frame padding")); + e.scan_parameters = det.getScan().tsquash("Inconsistent scan parameters"); + e.total_frames = get_total_frames(det); + e.frames_in_file = det.getFramesCaught()[0][0]; + e.additional_json_header = + det.getAdditionalJsonHeader().tsquash("Inconsistent JSON header"); + return e; +} + +acq::JungfrauExpectedState build_jungfrau_specific_state(const Detector &det) { + acq::JungfrauExpectedState e; + e.rois = get_rois(det); + e.exptime = get_exptime(det); + e.period = get_period(det); + e.num_udp_interfaces = get_num_udp_interfaces(det); + e.read_n_rows = get_read_n_rows(det); + e.readout_speed = get_readout_speed(det); + return e; +} + +acq::MoenchExpectedState build_moench_specific_state(const Detector &det) { + acq::MoenchExpectedState e; + e.rois = get_rois(det); + e.exptime = get_exptime(det); + e.period = get_period(det); + e.num_udp_interfaces = get_num_udp_interfaces(det); + e.read_n_rows = get_read_n_rows(det); + e.readout_speed = get_readout_speed(det); + return e; +} + +acq::EigerExpectedState build_eiger_specific_state(const Detector &det) { + acq::EigerExpectedState e; + e.rois = get_rois(det); + e.dynamic_range = get_dynamic_range(det); + e.ten_giga = get_ten_giga(det); + e.exptime = get_exptime(det); + e.period = get_period(det); + e.threshold_energy = + det.getThresholdEnergy().tsquash("Inconsistent threshold energy"); + auto [sub_exptime, sub_period] = get_sub_exptime_and_sub_period(det); + e.sub_exptime = sub_exptime; + e.sub_period = sub_period; + e.quad = det.getQuad().tsquash("Inconsistent quad setting"); + e.read_n_rows = get_read_n_rows(det); + { + for (auto item : det.getRateCorrection()) + e.rate_corrections.push_back(item.count()); + } + e.readout_speed = get_readout_speed(det); + return e; +} + +acq::Mythen3ExpectedState build_mythen3_specific_state(const Detector &det) { + acq::Mythen3ExpectedState e; + e.rois = get_rois(det); + e.dynamic_range = get_dynamic_range(det); + e.ten_giga = get_ten_giga(det); + e.period = get_period(det); + e.counter_mask = det.getCounterMask().tsquash( + "Inconsistent counter mask for Mythen3 detector"); + e.exp_times = det.getExptimeForAllGates().tsquash( + "Inconsistent exposure times for all gates"); + e.gate_delays = + det.getGateDelayForAllGates().tsquash("Inconsistent gate delays"); + e.num_gates = det.getNumberOfGates().tsquash( + "Inconsistent number of gates for Mythen3 detector"); + e.threshold_energies = + det.getAllThresholdEnergy().tsquash("Inconsistent threshold energies"); + e.readout_speed = get_readout_speed(det); + return e; +} + +acq::Gotthard2ExpectedState +build_gotthard2_specific_state(const Detector &det) { + acq::Gotthard2ExpectedState e; + e.rois = get_rois(det); + e.exptime = get_exptime(det); + e.period = get_period(det); + e.burst_mode = det.getBurstMode().tsquash("Inconsistent burst mode"); + e.readout_speed = get_readout_speed(det); + return e; +} + +acq::CTBExpectedState build_ctb_specific_state(const Detector &det, + const acq::CTBState &ctb_state) { + acq::CTBExpectedState e; + e.exptime = get_exptime(det); + e.period = get_period(det); + e.ctb_acq_state = ctb_state; + return e; +} + +acq::DetectorSpecificState +build_detector_specific_state(const Detector &det, + std::optional ctb_state) { + switch (det.getDetectorType().tsquash("bad type")) { + case defs::JUNGFRAU: + return build_jungfrau_specific_state(det); + case defs::MOENCH: + return build_moench_specific_state(det); + case defs::EIGER: + return build_eiger_specific_state(det); + case defs::MYTHEN3: + return build_mythen3_specific_state(det); + case defs::GOTTHARD2: + return build_gotthard2_specific_state(det); + case defs::CHIPTESTBOARD: + case defs::XILINX_CHIPTESTBOARD: + if (!ctb_state.has_value()) { + throw sls::RuntimeError( + "CTB state must be provided to build expected state"); + } + return build_ctb_specific_state(det, ctb_state.value()); + default: + throw sls::RuntimeError("Unsupported detector type"); + } +} +} // anonymous namespace + +namespace sls::test::acquire { + +ExpectedState build_expected_state(const Detector &det, + const AcquisitionState &acq_state, + const FileState &file_state, + std::optional ctb_state) { + ExpectedState e; + e.common_state = build_common_state(det, ctb_state); + e.file_state = file_state; + e.acquisition_state = acq_state; + e.detector_specific_state = build_detector_specific_state(det, ctb_state); + return e; +} + +int get_expected_image_size(const Detector &det, + std::optional ctb_state) { + auto det_type = get_detector_type(det); + auto dynamic_range = get_dynamic_range(det); + int bytes_per_pixel = dynamic_range / 8; + int image_size = 0; + detParameters par(det_type); + + switch (det_type) { + case defs::EIGER: { + int num_chips = (par.nChipX / 2); + image_size = par.nChanX * par.nChanY * num_chips * bytes_per_pixel; + } break; + case defs::JUNGFRAU: + case defs::MOENCH: { + auto num_udp_interfaces = det.getNumberofUDPInterfaces().tsquash( + "inconsistent number of udp interfaces"); + image_size = (par.nChanX * par.nChanY * par.nChipX * par.nChipY * + bytes_per_pixel) / + num_udp_interfaces; + } break; + case defs::MYTHEN3: { + int counter_mask = det.getCounterMask().squash(); + int num_counters = __builtin_popcount(counter_mask); + int num_channels_per_counter = par.nChanX / MAX_NUM_COUNTERS; + image_size = num_channels_per_counter * num_counters * par.nChipX * + bytes_per_pixel; + } break; + case defs::GOTTHARD2: { + image_size = par.nChanX * par.nChipX * bytes_per_pixel; + } break; + case defs::CHIPTESTBOARD: + case defs::XILINX_CHIPTESTBOARD: + if (!ctb_state.has_value()) { + throw sls::RuntimeError( + "CTB state must be provided to calculate expected image size"); + } + LOG(logINFORED) << ctb_state.value(); + image_size = + sls::calculate_ctb_image_size( + ctb_state.value(), (det_type == defs::XILINX_CHIPTESTBOARD)) + .first; + break; + default: + throw sls::RuntimeError("Unsupported detector type for this test"); + } + return image_size; +} + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/acquire/ExpectedState.h b/slsDetectorSoftware/tests/acquire/ExpectedState.h new file mode 100644 index 000000000..06b457cef --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/ExpectedState.h @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package + +#pragma once + +#include "Acquire.h" +#include "CTBState.h" +#include "FileState.h" + +#include + +namespace sls::test::acquire { + +struct CommonExpectedState { + defs::detectorType det_type{}; + defs::timingMode timing_mode{}; + defs::xy geometry{}; + int image_size{}; + defs::xy port_shape{}; + int max_frames_per_file{}; + defs::frameDiscardPolicy frame_discard_policy{}; + bool partial_frames_padding{}; + defs::scanParameters scan_parameters{}; + uint64_t total_frames{}; + uint64_t frames_in_file{}; + std::map additional_json_header{}; +}; + +struct JungfrauExpectedState { + std::vector rois{}; + ns exptime{}; + ns period{}; + int num_udp_interfaces{}; + int read_n_rows{}; + defs::speedLevel readout_speed{}; +}; + +struct MoenchExpectedState { + std::vector rois{}; + ns exptime{}; + ns period{}; + int num_udp_interfaces{}; + int read_n_rows{}; + defs::speedLevel readout_speed{}; +}; + +struct EigerExpectedState { + std::vector rois{}; + int dynamic_range{}; + bool ten_giga{}; + ns exptime{}; + ns period{}; + int threshold_energy{}; + ns sub_exptime{}; + ns sub_period{}; + bool quad{}; + int read_n_rows{}; + std::vector rate_corrections{}; + defs::speedLevel readout_speed{}; +}; + +struct Mythen3ExpectedState { + std::vector rois{}; + int dynamic_range{}; + bool ten_giga{}; + ns period{}; + int counter_mask{}; + std::array exp_times{}; + std::array gate_delays{}; + int num_gates{}; + std::array threshold_energies{}; + defs::speedLevel readout_speed{}; +}; + +struct Gotthard2ExpectedState { + std::vector rois{}; + ns exptime{}; + ns period{}; + defs::burstMode burst_mode{}; + defs::speedLevel readout_speed{}; +}; + +struct CTBExpectedState { + ns exptime{}; + ns period{}; + CTBState ctb_acq_state{}; +}; + +using DetectorSpecificState = + std::variant; + +struct ExpectedState { + FileState file_state{}; + AcquisitionState acquisition_state{}; + CommonExpectedState common_state{}; + DetectorSpecificState detector_specific_state{}; +}; + +template +const T &get_detector_specific_state(const ExpectedState &expected_state) { + return std::get(expected_state.detector_specific_state); +} + +template bool is_type(const ExpectedState &e) { + return std::holds_alternative(e.detector_specific_state); +} + +ExpectedState build_expected_state( + const Detector &det, + const AcquisitionState &acq_state = default_acquisition_state(), + const FileState &file_state = default_file_state(), + std::optional ctb_state = std::nullopt); + +int get_expected_image_size(const Detector &det, + std::optional ctb_state = std::nullopt); + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/acquire/FileState.h b/slsDetectorSoftware/tests/acquire/FileState.h new file mode 100644 index 000000000..ff33e23b5 --- /dev/null +++ b/slsDetectorSoftware/tests/acquire/FileState.h @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "sls/Detector.h" +#include "sls/logger.h" + +#include + +namespace sls::test::acquire { + +struct FileState { + std::string file_path; + std::string file_prefix; + int64_t file_acq_index; + bool file_write; + bool file_overwrite; + slsDetectorDefs::fileFormat file_format; +}; + +inline FileState default_file_state() { + return {"/tmp", "sls_test", 0, true, true, slsDetectorDefs::BINARY}; +} + +inline FileState get_file_state(const Detector &det) { + return FileState{ + det.getFilePath().tsquash("Inconsistent file path"), + det.getFileNamePrefix().tsquash("Inconsistent file prefix"), + det.getAcquisitionIndex().tsquash( + "Inconsistent file acquisition index"), + det.getFileWrite().tsquash("Inconsistent file write"), + det.getFileOverWrite().tsquash("Inconsistent file overwrite"), + det.getFileFormat().tsquash("Inconsistent file format")}; +} + +inline void set_file_state(Detector &det, const FileState &s) { + if (!s.file_path.empty()) + det.setFilePath(s.file_path); + det.setFileNamePrefix(s.file_prefix); + det.setAcquisitionIndex(s.file_acq_index); + det.setFileWrite(s.file_write); + det.setFileOverWrite(s.file_overwrite); + det.setFileFormat(s.file_format); +} + +inline std::string +get_master_file_name(const FileState &s = default_file_state()) { + auto master_file_prefix = s.file_path + "/" + s.file_prefix + "_master_" + + std::to_string(s.file_acq_index); + if (s.file_format == defs::BINARY) + return master_file_prefix + ".json"; + return master_file_prefix + ".h5"; +} + +inline std::string +get_virtual_file_name(const FileState &s = default_file_state()) { + return s.file_path + "/" + s.file_prefix + "_virtual_" + + std::to_string(s.file_acq_index) + ".h5"; +} + +inline std::string +get_first_port_first_file_name(const FileState &s = default_file_state()) { + auto file_prefix = s.file_path + "/" + s.file_prefix + "_d0_f0_" + + std::to_string(s.file_acq_index); + if (s.file_format == defs::BINARY) + return file_prefix + ".raw"; + return file_prefix + ".h5"; +} + +inline std::ostream &operator<<(std::ostream &os, const FileState &s) { + os << "File State:" + << "\n Path: " << s.file_path << "\n Prefix: " << s.file_prefix + << "\n Acq Index: " << s.file_acq_index << "\n Write: " << s.file_write + << "\n Overwrite: " << s.file_overwrite + << "\n Format: " << ToString(s.file_format) + << "\n Master File: " << get_master_file_name(s) + << "\n Virtual File: " << get_virtual_file_name(s); + return os; +} + +/** + * @brief RAII guard for restoring the file state of a detector. + * + * The constructor saves the current file state and sets a new state, while the + * destructor restores the original state. + */ +class FileStateGuard { + public: + explicit FileStateGuard(Detector &det, const FileState &new_state) + : det(det), saved_(get_file_state(det)) { + set_file_state(det, new_state); + } + ~FileStateGuard() { set_file_state(det, saved_); } + + private: + Detector &det; + FileState saved_; +}; + +} // namespace sls::test::acquire \ No newline at end of file diff --git a/slsDetectorSoftware/tests/checks/MasterFileChecks.h b/slsDetectorSoftware/tests/checks/MasterFileChecks.h new file mode 100644 index 000000000..db5fff20f --- /dev/null +++ b/slsDetectorSoftware/tests/checks/MasterFileChecks.h @@ -0,0 +1,456 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "acquire/ExpectedState.h" +#include "master_file/Checker.h" + +#include "MasterAttributes.h" + +namespace sls::test::checks { + +namespace acq = sls::test::acquire; +namespace mf = sls::test::master_file; + +inline bool operator==(sls::ns lhs, sls::ns rhs) { + return lhs.count() == rhs.count(); +} + +// different values based on file format +template void check_version(CheckerT &checker) { +#ifdef HDF5C + if constexpr (std::is_same_v>) { + checker.template check(MasterAttributes::N_VERSION.data(), + HDF5_WRITER_VERSION, + mf::AccessType::Attribute); + } else +#endif + { + checker.template check(MasterAttributes::N_VERSION.data(), + BINARY_WRITER_VERSION); + } +} + +template +void check_detector_type(CheckerT &checker, const defs::detectorType &value) { + checker.template check( + MasterAttributes::N_DETECTOR_TYPE.data(), ToString(value)); +} + +template +void check_timing_mode(CheckerT &checker, const defs::timingMode &value) { + checker.template check(MasterAttributes::N_TIMING_MODE.data(), + ToString(value)); +} + +template +void check_geometry(CheckerT &checker, const defs::xy &value) { + checker.template check(MasterAttributes::N_GEOMETRY.data(), + value); +} + +template +void check_image_size(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_IMAGE_SIZE.data(), value); +} + +template +void check_port_shape(CheckerT &checker, const defs::xy &value) { + checker.template check(MasterAttributes::N_PIXELS.data(), value); +} + +template +void check_frames_per_file(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_MAX_FRAMES_PER_FILE.data(), + value); +} + +template +void check_frame_discard_policy(CheckerT &checker, + const defs::frameDiscardPolicy &value) { + checker.template check( + MasterAttributes::N_FRAME_DISCARD_POLICY.data(), ToString(value)); +} + +template +void check_partial_frames_padding(CheckerT &checker, const bool &value) { + checker.template check(MasterAttributes::N_FRAME_PADDING.data(), + static_cast(value)); +} + +template +void check_scan_parameters(CheckerT &checker, + const defs::scanParameters &value) { + checker.template check( + MasterAttributes::N_SCAN_PARAMETERS.data(), value); +} + +template +void check_total_frames(CheckerT &checker, const uint64_t &value) { + checker.template check(MasterAttributes::N_TOTAL_FRAMES.data(), + value); +} + +template +void check_frames_in_file(CheckerT &checker, const uint64_t &value) { + checker.template check(MasterAttributes::N_FRAMES_IN_FILE.data(), + value); +} + +template +void check_additional_json_header( + CheckerT &checker, const std::map &value) { + checker.template check>( + MasterAttributes::N_ADDITIONAL_JSON_HEADER.data(), value); +} + +template +void check_rois(CheckerT &checker, const std::vector &value) { + checker.template check>( + MasterAttributes::N_RECEIVER_ROIS.data(), value); +} + +template +void check_exptime(CheckerT &checker, const ns &value) { + checker.template check( + MasterAttributes::N_EXPOSURE_TIME.data(), ToString(value)); +} + +template +void check_period(CheckerT &checker, const ns &value) { + checker.template check( + MasterAttributes::N_ACQUISITION_PERIOD.data(), ToString(value)); +} + +template +void check_num_udp_interfaces(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_NUM_UDP_INTERFACES.data(), + value); +} + +template +void check_read_n_rows(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_NUMBER_OF_ROWS.data(), + value); +} + +template +void check_readout_speed(CheckerT &checker, const defs::speedLevel &value) { + checker.template check( + MasterAttributes::N_READOUT_SPEED.data(), ToString(value)); +} + +template +void check_dynamic_range(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_DYNAMIC_RANGE.data(), + value); +} + +template +void check_ten_giga(CheckerT &checker, const bool &value) { + checker.template check(MasterAttributes::N_TEN_GIGA.data(), + static_cast(value)); +} + +template +void check_threshold_energy(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_THRESHOLD_ENERGY.data(), + value); +} + +template +void check_sub_exptime(CheckerT &checker, const ns &value) { + checker.template check( + MasterAttributes::N_SUB_EXPOSURE_TIME.data(), ToString(value)); +} + +template +void check_sub_period(CheckerT &checker, const ns &value) { + checker.template check( + MasterAttributes::N_SUB_ACQUISITION_PERIOD.data(), ToString(value)); +} + +template +void check_quad(CheckerT &checker, const bool &value) { + checker.template check(MasterAttributes::N_QUAD.data(), + static_cast(value)); +} + +template +void check_rate_corrections(CheckerT &checker, + const std::vector &value) { + checker.template check>( + MasterAttributes::N_RATE_CORRECTIONS.data(), value); +} + +template +void check_counter_mask(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_COUNTER_MASK.data(), value); +} + +template +void check_exptime_array(CheckerT &checker, + const std::array &value) { + checker.template check>( + MasterAttributes::N_EXPOSURE_TIMES.data(), value); +} + +template +void check_gate_delay_array(CheckerT &checker, + const std::array &value) { + checker.template check>( + MasterAttributes::N_GATE_DELAYS.data(), value); +} + +template +void check_gates(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_GATES.data(), value); +} + +template +void check_threshold_energies(CheckerT &checker, + const std::array &value) { + checker.template check>( + MasterAttributes::N_THRESHOLD_ENERGIES.data(), value); +} + +template +void check_burst_mode(CheckerT &checker, const defs::burstMode &value) { + checker.template check(MasterAttributes::N_BURST_MODE.data(), + ToString(value)); +} + +template +void check_readout_mode(CheckerT &checker, const defs::readoutMode &value) { + auto analog = static_cast( + (value == defs::ANALOG_ONLY || value == defs::ANALOG_AND_DIGITAL)); + auto digital = static_cast(value == defs::DIGITAL_ONLY || + value == defs::ANALOG_AND_DIGITAL || + value == defs::DIGITAL_AND_TRANSCEIVER); + auto trans = static_cast(value == defs::DIGITAL_AND_TRANSCEIVER || + value == defs::TRANSCEIVER_ONLY); + + if (analog) + checker.template check(MasterAttributes::N_ANALOG.data(), + static_cast(analog)); + if (digital) + checker.template check(MasterAttributes::N_DIGITAL.data(), + static_cast(digital)); + if (trans) + checker.template check(MasterAttributes::N_TRANSCEIVER.data(), + static_cast(trans)); +} + +template +void check_analog_samples(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_ANALOG_SAMPLES.data(), + value); +} + +template +void check_digital_samples(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_DIGITAL_SAMPLES.data(), + value); +} + +template +void check_transceiver_samples(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_TRANSCEIVER_SAMPLES.data(), + value); +} + +template +void check_adc_mask(CheckerT &checker, const uint32_t &value) { + checker.template check(MasterAttributes::N_ADC_MASK.data(), + value); +} + +template +void check_dbit_offset(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_DBIT_OFFSET.data(), value); +} + +template +void check_dbit_list(CheckerT &checker, const std::vector &value) { + uint64_t dbit_bitset = 0; + for (auto &i : value) { + dbit_bitset |= (static_cast(1) << i); + } + checker.template check(MasterAttributes::N_DBIT_BITSET.data(), + dbit_bitset); +} + +template +void check_dbit_reorder(CheckerT &checker, const int &value) { + checker.template check(MasterAttributes::N_DBIT_REORDER.data(), value); +} + +template +void check_transceiver_mask(CheckerT &checker, const uint32_t &value) { + checker.template check( + MasterAttributes::N_TRANSCEIVER_MASK.data(), value); +} + +template +void check_common_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + check_version(checker); + auto st = expected.common_state; + check_detector_type(checker, st.det_type); + check_geometry(checker, st.geometry); + check_image_size(checker, st.image_size); + check_port_shape(checker, st.port_shape); + check_frames_per_file(checker, st.max_frames_per_file); + check_frame_discard_policy(checker, st.frame_discard_policy); + check_partial_frames_padding(checker, st.partial_frames_padding); + check_scan_parameters(checker, st.scan_parameters); + check_total_frames(checker, st.total_frames); + check_frames_in_file(checker, st.frames_in_file); + check_additional_json_header(checker, st.additional_json_header); +} + +template +void check_jungfrau_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_rois(checker, st.rois); + check_exptime(checker, st.exptime); + check_period(checker, st.period); + check_num_udp_interfaces(checker, st.num_udp_interfaces); + check_read_n_rows(checker, st.read_n_rows); + check_readout_speed(checker, st.readout_speed); +} + +template +void check_moench_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_rois(checker, st.rois); + check_exptime(checker, st.exptime); + check_period(checker, st.period); + check_num_udp_interfaces(checker, st.num_udp_interfaces); + check_read_n_rows(checker, st.read_n_rows); + check_readout_speed(checker, st.readout_speed); +} + +template +void check_eiger_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_rois(checker, st.rois); + check_dynamic_range(checker, st.dynamic_range); + check_ten_giga(checker, st.ten_giga); + check_exptime(checker, st.exptime); + check_period(checker, st.period); + check_threshold_energy(checker, st.threshold_energy); + check_sub_exptime(checker, st.sub_exptime); + check_sub_period(checker, st.sub_period); + check_quad(checker, st.quad); + check_read_n_rows(checker, st.read_n_rows); + check_rate_corrections(checker, st.rate_corrections); + check_readout_speed(checker, st.readout_speed); +} + +template +void check_mythen3_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_rois(checker, st.rois); + check_dynamic_range(checker, st.dynamic_range); + check_ten_giga(checker, st.ten_giga); + check_period(checker, st.period); + check_counter_mask(checker, st.counter_mask); + check_exptime_array(checker, st.exp_times); + check_gate_delay_array(checker, st.gate_delays); + check_gates(checker, st.num_gates); + check_threshold_energies(checker, st.threshold_energies); + check_readout_speed(checker, st.readout_speed); +} + +template +void check_gotthard2_metadata(CheckerT &checker, + const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_rois(checker, st.rois); + check_exptime(checker, st.exptime); + check_period(checker, st.period); + check_burst_mode(checker, st.burst_mode); + check_readout_speed(checker, st.readout_speed); +} + +template +void check_ctb_metadata(CheckerT &checker, const acq::ExpectedState &expected) { + const auto &st = + std::get(expected.detector_specific_state); + check_exptime(checker, st.exptime); + check_period(checker, st.period); + + // 10g for xilinx, 1g/10g for altera ctb + bool ten_giga = true; + if (expected.common_state.det_type == defs::CHIPTESTBOARD) { + check_ten_giga(checker, st.ctb_acq_state.ten_giga); + if (!st.ctb_acq_state.ten_giga) { + ten_giga = false; + } + } + if (ten_giga) + check_adc_mask(checker, st.ctb_acq_state.adc_enable_10g); + else + check_adc_mask(checker, st.ctb_acq_state.adc_enable_1g); + + check_readout_mode(checker, st.ctb_acq_state.readout_mode); + check_analog_samples(checker, st.ctb_acq_state.num_adc_samples); + check_digital_samples(checker, st.ctb_acq_state.num_dbit_samples); + check_transceiver_samples(checker, st.ctb_acq_state.num_trans_samples); + check_dbit_offset(checker, st.ctb_acq_state.dbit_offset); + check_dbit_list(checker, st.ctb_acq_state.dbit_list); + check_dbit_reorder(checker, st.ctb_acq_state.dbit_reorder); + check_transceiver_mask(checker, st.ctb_acq_state.transceiver_mask); +} + +template +void check_metadata(CheckerT &checker, const acq::ExpectedState &expected) { + check_common_metadata(checker, expected); + switch (expected.common_state.det_type) { + case defs::JUNGFRAU: + check_jungfrau_metadata(checker, expected); + break; + case defs::MOENCH: + check_moench_metadata(checker, expected); + break; + case defs::EIGER: + check_eiger_metadata(checker, expected); + break; + case defs::MYTHEN3: + check_mythen3_metadata(checker, expected); + break; + case defs::GOTTHARD2: + check_gotthard2_metadata(checker, expected); + break; + case defs::CHIPTESTBOARD: + case defs::XILINX_CHIPTESTBOARD: + check_ctb_metadata(checker, expected); + break; + default: + throw std::runtime_error( + "Unsupported detector type for metadata checks"); + } +} + +inline void +check_binary_file_size(const int expected_image_size, const int64_t num_frames, + const acq::FileState &st = acq::default_file_state()) { + assert(st.file_format == defs::BINARY); + auto fname = acq::get_first_port_first_file_name(st); + auto expected_file_size = + num_frames * (expected_image_size + sizeof(defs::sls_receiver_header)); + auto actual_file_size = std::filesystem::file_size(fname); + REQUIRE(actual_file_size == expected_file_size); +} + +} // namespace sls::test::checks \ No newline at end of file diff --git a/slsDetectorSoftware/tests/master_file/Checker.h b/slsDetectorSoftware/tests/master_file/Checker.h new file mode 100644 index 000000000..94f1de687 --- /dev/null +++ b/slsDetectorSoftware/tests/master_file/Checker.h @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "Readers.h" +#include "ReadersJson.h" +#ifdef HDF5C +#include "ReadersH5.h" +#endif + +#include "catch.hpp" + +namespace sls::test::master_file { + +template class Checker; + +/** JSON Specialization */ +template <> class Checker { + public: + explicit Checker(const std::string &path) : ctx_(path) {} + explicit Checker(JsonContext ctx) : ctx_(std::move(ctx)) {} + const JsonContext &context() const { return ctx_; } + + template + void check(const std::string &name, const T &expected, + AccessType = AccessType::Dataset) const { + CAPTURE(name); + REQUIRE(ctx_.doc.HasMember(name.c_str())); + auto retval = read(ctx_, name); + REQUIRE(retval == expected); + } + + private: + JsonContext ctx_; +}; + +/** HDF5 Specialization */ +#ifdef HDF5C + +template <> class Checker { + public: + explicit Checker(const std::string &path) : ctx_(path) {} + explicit Checker(H5Context ctx) : ctx_(std::move(ctx)) {} + const H5Context &context() const { return ctx_; } + + template + void check(const std::string &name, const T &expected, + AccessType access = AccessType::Dataset) const { + auto retval = read(ctx_, name, access); + REQUIRE(retval == expected); + } + + private: + H5Context ctx_; +}; + +#endif + +} // namespace sls::test::master_file \ No newline at end of file diff --git a/slsDetectorSoftware/tests/master_file/Context.h b/slsDetectorSoftware/tests/master_file/Context.h new file mode 100644 index 000000000..00f8ad55a --- /dev/null +++ b/slsDetectorSoftware/tests/master_file/Context.h @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "catch.hpp" +#include +#include +#include +#include +#include +#include +#include + +#ifdef HDF5C +#include "H5Cpp.h" +#endif + +namespace sls::test::master_file { + +struct JsonContext { + rapidjson::Document doc; + + explicit JsonContext(const std::string &path) { + parse_binary_master_attributes(path); + } + + private: + void parse_binary_master_attributes(const std::string &file_path) { + REQUIRE(std::filesystem::exists(file_path)); + + std::ifstream file(file_path); + REQUIRE(file.is_open()); + + std::stringstream buffer; + buffer << file.rdbuf(); + + std::string json_str = buffer.str(); + + rapidjson::ParseResult result = doc.Parse(json_str.c_str()); + + if (!result) { + std::cout << "JSON parse error: " << GetParseError_En(result.Code()) + << " (at offset " << result.Offset() << ")" << std::endl; + + size_t offset = result.Offset(); + std::string context = + json_str.substr(std::max(0, (int)offset - 20), 40); + std::cout << "Context around error: \"" << context << "\"" + << std::endl; + } + + REQUIRE(result); + } +}; + +#ifdef HDF5C +struct H5Context { + H5::H5File file; + explicit H5Context(const std::string &path) : file(path, H5F_ACC_RDONLY) {} +}; +#endif + +} // namespace sls::test::master_file diff --git a/slsDetectorSoftware/tests/master_file/Readers.h b/slsDetectorSoftware/tests/master_file/Readers.h new file mode 100644 index 000000000..99675f54a --- /dev/null +++ b/slsDetectorSoftware/tests/master_file/Readers.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "Context.h" +#include "sls/sls_detector_defs.h" + +namespace sls::test::master_file { + +template struct Reader; +template struct AttributeReader; + +enum class AccessType { Dataset, Attribute }; + +template +T read(const Context &ctx, const std::string &name, + AccessType access = AccessType::Dataset) { + return Reader::read(ctx, name, access); +} + +inline void check_size(size_t actual, size_t expected, const std::string &name, + const std::string &doc) { + if (actual != expected) { + throw sls::RuntimeError( + doc + " array " + name + " has " + std::to_string(actual) + + " elements instead of " + std::to_string(expected)); + } +} + +} // namespace sls::test::master_file diff --git a/slsDetectorSoftware/tests/master_file/ReadersH5.h b/slsDetectorSoftware/tests/master_file/ReadersH5.h new file mode 100644 index 000000000..fcd4fa9f7 --- /dev/null +++ b/slsDetectorSoftware/tests/master_file/ReadersH5.h @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "Readers.h" +#include "sls/sls_detector_defs.h" +#include + +#ifdef HDF5C +#include "H5Cpp.h" + +namespace sls::test::master_file { +using ns = std::chrono::nanoseconds; + +static const std::string HDF5_GROUP = "/entry/instrument/detector/"; + +/* --safety checks-- */ +inline void require_dataset(const H5Context &ctx, const std::string &name) { + const std::string full_name = HDF5_GROUP + name; + if (H5Lexists(ctx.file.getId(), full_name.c_str(), H5P_DEFAULT) <= 0) { + throw sls::RuntimeError("Missing HDF5 dataset: " + full_name); + } +} + +inline void require_file_attribute(const H5Context &ctx, + const std::string &name) { + if (!H5Aexists_by_name(ctx.file.getId(), "/", name.c_str(), H5P_DEFAULT)) { + throw sls::RuntimeError("Missing HDF5 attribute: " + name); + } +} + +/* --scalar reads-- */ +template <> struct Reader { + static int read(const H5Context &ctx, const std::string &name, + AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("int attribute access not supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + int out{}; + ds.read(&out, H5::PredType::NATIVE_INT); + return out; + } +}; + +template <> struct Reader { + static uint32_t read(const H5Context &ctx, const std::string &name, + AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError( + "uint32_t attribute access not supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + uint32_t out{}; + ds.read(&out, H5::PredType::STD_U32LE); + return out; + } +}; + +template <> struct Reader { + static uint64_t read(const H5Context &ctx, const std::string &name, + AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError( + "'uint64_t' attribute access not supported for HDF5"); + } + uint64_t out{}; + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + ds.read(&out, H5::PredType::STD_U64LE); + return out; + } +}; + +template <> struct Reader { + static double read(const H5Context &ctx, const std::string &name, + AccessType access) { + double out{}; + if (access == AccessType::Attribute) { + require_file_attribute(ctx, name); + auto attr = ctx.file.openAttribute(name); + attr.read(attr.getDataType(), &out); + return out; + } + // dataset + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + ds.read(&out, H5::PredType::NATIVE_DOUBLE); + return out; + } +}; + +template <> struct Reader { + static std::string read(const H5Context &ctx, const std::string &name, + AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError( + "string attribute access not supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + std::string out{}; + ds.read(out, ds.getStrType()); + return out; + } +}; + +/** complex types */ +template <> struct Reader { + static defs::xy read(const H5Context &ctx, const std::string &name, + AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError( + "'defs::xy' attribute access not supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + // define type + H5::CompType type(sizeof(defs::xy)); + type.insertMember("x", HOFFSET(defs::xy, x), H5::PredType::NATIVE_INT); + type.insertMember("y", HOFFSET(defs::xy, y), H5::PredType::NATIVE_INT); + // read + defs::xy out{}; + ds.read(&out, type); + return out; + } +}; + +inline hsize_t get_1d_size(const H5::DataSet &ds) { + H5::DataSpace space = ds.getSpace(); + hsize_t dims[1]; + space.getSimpleExtentDims(dims); + return dims[0]; +} + +template <> struct Reader> { + static std::vector + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'std::vector' attribute access not " + "supported for HDF5"); + } + require_dataset(ctx, name); + // define type + H5::CompType type(sizeof(defs::ROI)); + type.insertMember("xmin", HOFFSET(defs::ROI, xmin), + H5::PredType::NATIVE_INT); + type.insertMember("xmax", HOFFSET(defs::ROI, xmax), + H5::PredType::NATIVE_INT); + type.insertMember("ymin", HOFFSET(defs::ROI, ymin), + H5::PredType::NATIVE_INT); + type.insertMember("ymax", HOFFSET(defs::ROI, ymax), + H5::PredType::NATIVE_INT); + // read + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + auto len = get_1d_size(ds); + std::vector out{}; + out.resize(len); + ds.read(out.data(), type); + return out; + } +}; + +template <> struct Reader { + static defs::scanParameters + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'defs::scanParameters' attribute access not " + "supported for HDF5"); + } + require_dataset(ctx, name); + // define type + H5::CompType type(sizeof(defs::scanParameters)); + type.insertMember("enable", HOFFSET(defs::scanParameters, enable), + H5::PredType::NATIVE_INT); + type.insertMember("dacInd", HOFFSET(defs::scanParameters, dacInd), + H5::PredType::NATIVE_INT); + type.insertMember("startOffset", + HOFFSET(defs::scanParameters, startOffset), + H5::PredType::NATIVE_INT); + type.insertMember("stopOffset", + HOFFSET(defs::scanParameters, stopOffset), + H5::PredType::NATIVE_INT); + type.insertMember("stepSize", HOFFSET(defs::scanParameters, stepSize), + H5::PredType::NATIVE_INT); + type.insertMember("dacSettleTime_ns", + HOFFSET(defs::scanParameters, dacSettleTime_ns), + H5::PredType::STD_I64LE); + // read + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + defs::scanParameters out{}; + ds.read(&out, type); + return out; + } +}; + +/** arrays/vectors/maps */ +template <> struct Reader> { + static std::array + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'std::array' attribute access not " + "supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + auto len = get_1d_size(ds); + check_size(len, 3, name, "HDF5"); + std::array out{}; + ds.read(out.data(), H5::PredType::NATIVE_INT); + return out; + } +}; + +template <> struct Reader> { + static std::array + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'std::array' attribute access not " + "supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + auto len = get_1d_size(ds); + check_size(len, 3, name, "HDF5"); + + // read int raw char buffer + std::vector raw(len); + ds.read(raw.data(), ds.getStrType()); + + std::array out{}; + for (size_t i = 0; i != len; ++i) { + out[i] = StringTo(raw[i]); + } + return out; + } +}; + +template <> struct Reader> { + static std::vector + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'std::vector' attribute access not " + "supported for HDF5"); + } + require_dataset(ctx, name); + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + auto len = get_1d_size(ds); + std::vector out{}; + out.resize(len); + ds.read(out.data(), H5::PredType::STD_I64LE); + return out; + } +}; + +template <> struct Reader> { + static std::map + read(const H5Context &ctx, const std::string &name, AccessType access) { + if (access == AccessType::Attribute) { + throw RuntimeError("'std::map' attribute " + "access not supported for HDF5"); + } + require_dataset(ctx, name); + std::map out{}; + // dim + auto ds = ctx.file.openDataSet(HDF5_GROUP + name); + auto len = get_1d_size(ds); + // empty ds + if (len == 0) { + return out; + } + // define type + auto strType = ds.getStrType(); + H5::CompType mapType(sizeof(char *) * 2); + mapType.insertMember("key", 0, strType); + mapType.insertMember("value", sizeof(char *), strType); + struct KeyValue { + const char *key; + const char *value; + }; + // read + std::vector kv_vector(len); + ds.read(kv_vector.data(), mapType); + for (const auto &kv : kv_vector) { + out[kv.key] = kv.value; + } + return out; + } +}; + +} // namespace sls::test::master_file + +#endif \ No newline at end of file diff --git a/slsDetectorSoftware/tests/master_file/ReadersJson.h b/slsDetectorSoftware/tests/master_file/ReadersJson.h new file mode 100644 index 000000000..013f8f0fd --- /dev/null +++ b/slsDetectorSoftware/tests/master_file/ReadersJson.h @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +#pragma once + +#include "Readers.h" + +#include "sls/ToString.h" +#include "sls/sls_detector_defs.h" + +#include + +namespace sls::test::master_file { +using ns = std::chrono::nanoseconds; + +/* --scalar reads-- */ +template <> struct Reader { + static int read(const JsonContext &ctx, const std::string &name, + AccessType access) { + return ctx.doc[name.c_str()].GetInt(); + } +}; + +template <> struct Reader { + static uint32_t read(const JsonContext &ctx, const std::string &name, + AccessType access) { + return ctx.doc[name.c_str()].GetUint(); + } +}; + +template <> struct Reader { + static uint64_t read(const JsonContext &ctx, const std::string &name, + AccessType access) { + return ctx.doc[name.c_str()].GetUint64(); + } +}; + +template <> struct Reader { + static double read(const JsonContext &ctx, const std::string &name, + AccessType access) { + return ctx.doc[name.c_str()].GetDouble(); + } +}; + +template <> struct Reader { + static std::string read(const JsonContext &ctx, const std::string &name, + AccessType access) { + return ctx.doc[name.c_str()].GetString(); + } +}; + +/** complex types */ +template <> struct Reader { + static defs::xy read(const JsonContext &ctx, const std::string &name, + AccessType access) { + defs::xy out{}; + out.x = ctx.doc[name.c_str()]["x"].GetInt(); + out.y = ctx.doc[name.c_str()]["y"].GetInt(); + return out; + } +}; + +template <> struct Reader> { + static std::vector + read(const JsonContext &ctx, const std::string &name, AccessType access) { + std::vector out{}; + for (const auto &item : ctx.doc[name.c_str()].GetArray()) { + defs::ROI r{}; + r.xmin = item["xmin"].GetInt(); + r.xmax = item["xmax"].GetInt(); + r.ymin = item["ymin"].GetInt(); + r.ymax = item["ymax"].GetInt(); + out.push_back(r); + } + return out; + } +}; + +template <> struct Reader { + static defs::scanParameters + read(const JsonContext &ctx, const std::string &name, AccessType access) { + defs::scanParameters out{}; + const auto &s = ctx.doc[name.c_str()].GetObject(); + out.enable = s["enable"].GetInt(); + out.dacInd = static_cast(s["dacInd"].GetInt()); + out.startOffset = s["start offset"].GetInt(); + out.stopOffset = s["stop offset"].GetInt(); + out.stepSize = s["step size"].GetInt(); + out.dacSettleTime_ns = s["dac settle time ns"].GetInt64(); + return out; + } +}; + +/** arrays/vectors/maps */ +template <> struct Reader> { + static std::array + read(const JsonContext &ctx, const std::string &name, AccessType access) { + const auto &arr = ctx.doc[name.c_str()].GetArray(); + check_size(arr.Size(), 3, name, "JSON"); + + std::array out{}; + for (size_t i = 0; i < 3; ++i) { + out[i] = arr[i].GetInt(); + } + return out; + } +}; + +template <> struct Reader> { + static std::array + read(const JsonContext &ctx, const std::string &name, AccessType access) { + const auto &arr = ctx.doc[name.c_str()].GetArray(); + check_size(arr.Size(), 3, name, "JSON"); + + std::array out{}; + for (size_t i = 0; i < 3; ++i) { + std::string sval = arr[i].GetString(); + out[i] = StringTo(sval); + } + return out; + } +}; + +template <> struct Reader> { + static std::vector + read(const JsonContext &ctx, const std::string &name, AccessType access) { + std::vector out{}; + for (const auto &item : ctx.doc[name.c_str()].GetArray()) { + out.push_back(item.GetInt64()); + } + return out; + } +}; + +template <> struct Reader> { + static std::map + read(const JsonContext &ctx, const std::string &name, AccessType access) { + std::map out{}; + for (const auto &m : ctx.doc[name.c_str()].GetObject()) { + out[m.name.GetString()] = m.value.GetString(); + } + return out; + } +}; + +} // namespace sls::test::master_file \ No newline at end of file From b4a3e1187a07aa8b30967dc70b8943915c41250c Mon Sep 17 00:00:00 2001 From: AliceMazzoleni99 Date: Fri, 12 Jun 2026 11:49:02 +0200 Subject: [PATCH 3/6] Fix/segmentation fault in virtual detectors (#1472) * fix overflow in copying of image to packet * overflow fixed for xilinx --- .../ctbDetectorServer/slsDetectorFunctionList.c | 4 ++++ .../xilinx_ctbDetectorServer/slsDetectorFunctionList.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.c b/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.c index 895cbed2c..33f95fd42 100644 --- a/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.c +++ b/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.c @@ -2685,6 +2685,10 @@ void *start_timer(void *arg) { imageData + srcOffset, dataSize); srcOffset += dataSize; + dataSize = (srcOffset + dataSize > imageSize) + ? (imageSize - srcOffset) + : dataSize; + sendUDPPacket(0, 0, packetData, packetSize); // LOG(logINFOBLUE, ("packetsize:%d\n", packetSize)); } diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c index 6d4e13a1a..838a8f0ed 100644 --- a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c +++ b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c @@ -1620,6 +1620,10 @@ void *start_timer(void *arg) { srcOffset += dataSize; dataSent += dataSize; + dataSize = (srcOffset + dataSize > imageSize) + ? (imageSize - srcOffset) + : dataSize; + sendUDPPacket(0, 0, packetData, packetSize); } LOG(logINFO, ("Sent frame: %d [%lld]\n", iframes, frameNr + iframes)); From e4a97b1ea976573db4435b62e9e26db913651c17 Mon Sep 17 00:00:00 2001 From: AliceMazzoleni99 Date: Tue, 16 Jun 2026 16:30:14 +0200 Subject: [PATCH 4/6] Dev/ctb/display counter images (#1471) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP * show image coordinates * passing frame and plot values dynamically * WIP * succesfully show each counter as one image * added checkboxes for different images * only display counters for transceiver samples and image plot * fixed layout * dont reshape already done in transform --------- Co-authored-by: Erik Fröjdh --- pyctbgui/pyctbgui/services/Acquisition.py | 5 + pyctbgui/pyctbgui/services/Plot.py | 137 +- pyctbgui/pyctbgui/services/Transceiver.py | 87 +- pyctbgui/pyctbgui/ui/plot.ui | 1767 +++++++++++++-------- pyctbgui/pyctbgui/utils/defines.py | 4 +- 5 files changed, 1277 insertions(+), 723 deletions(-) diff --git a/pyctbgui/pyctbgui/services/Acquisition.py b/pyctbgui/pyctbgui/services/Acquisition.py index b32b8a659..dcf06707d 100644 --- a/pyctbgui/pyctbgui/services/Acquisition.py +++ b/pyctbgui/pyctbgui/services/Acquisition.py @@ -133,6 +133,7 @@ class AcquisitionTab(QtWidgets.QWidget): self.view.labelDigital.setDisabled(True) self.view.labelTransceiver.setDisabled(True) self.view.spinBoxTransceiver.setDisabled(True) + self.plotTab.enableallCounterCheckBox(False) case readoutMode.DIGITAL_ONLY: self.view.spinBoxAnalog.setDisabled(True) self.view.labelAnalog.setDisabled(True) @@ -140,6 +141,7 @@ class AcquisitionTab(QtWidgets.QWidget): self.view.labelDigital.setEnabled(True) self.view.labelTransceiver.setDisabled(True) self.view.spinBoxTransceiver.setDisabled(True) + self.plotTab.enableallCounterCheckBox(False) case readoutMode.ANALOG_AND_DIGITAL: self.view.spinBoxAnalog.setEnabled(True) self.view.labelAnalog.setEnabled(True) @@ -147,6 +149,7 @@ class AcquisitionTab(QtWidgets.QWidget): self.view.labelDigital.setEnabled(True) self.view.labelTransceiver.setDisabled(True) self.view.spinBoxTransceiver.setDisabled(True) + self.plotTab.enableallCounterCheckBox(False) case readoutMode.TRANSCEIVER_ONLY: self.view.spinBoxAnalog.setDisabled(True) self.view.labelAnalog.setDisabled(True) @@ -154,6 +157,7 @@ class AcquisitionTab(QtWidgets.QWidget): self.view.labelDigital.setDisabled(True) self.view.labelTransceiver.setEnabled(True) self.view.spinBoxTransceiver.setEnabled(True) + self.plotTab.enableallCounterCheckBox(self.plotTab.view.radioButtonImage.isChecked()) # enable counter checkboxes for matterhorn case _: self.view.spinBoxAnalog.setDisabled(True) self.view.labelAnalog.setDisabled(True) @@ -161,6 +165,7 @@ class AcquisitionTab(QtWidgets.QWidget): self.view.labelDigital.setEnabled(True) self.view.labelTransceiver.setEnabled(True) self.view.spinBoxTransceiver.setEnabled(True) + self.plotTab.enableallCounterCheckBox(self.plotTab.view.radioButtonImage.isChecked()) # enable counter checkboxes for matterhorn self.view.comboBoxROMode.currentIndexChanged.connect(self.setReadOut) self.view.spinBoxAnalog.editingFinished.connect(self.setAnalog) diff --git a/pyctbgui/pyctbgui/services/Plot.py b/pyctbgui/pyctbgui/services/Plot.py index a14ef1a0a..2b66e71f5 100644 --- a/pyctbgui/pyctbgui/services/Plot.py +++ b/pyctbgui/pyctbgui/services/Plot.py @@ -4,7 +4,7 @@ import random from pathlib import Path import numpy as np -from PyQt5 import QtWidgets, QtGui, uic +from PyQt5 import QtWidgets, QtGui, QtCore, uic from aare import transform, ReadoutMode from aare._aare import Matterhorn10, Matterhorn02, Moench04 @@ -43,6 +43,7 @@ class PlotTab(QtWidgets.QWidget): self.pedestalApply: bool = True self.__acqFrames = None self.logger = logging.getLogger('PlotTab') + self.plotSplitter = None def setup_ui(self): self.signalsTab = self.mainWindow.signalsTab @@ -51,10 +52,17 @@ class PlotTab(QtWidgets.QWidget): self.adcTab = self.mainWindow.adcTab self.initializeColorMaps() + # TODO use list comprehension + self.checkBoxCounters = [self.view.checkBoxCounter0, self.view.checkBoxCounter1, self.view.checkBoxCounter2, self.view.checkBoxCounter3] + + for checkBox in self.checkBoxCounters: + checkBox.setChecked(False) + checkBox.setEnabled(False) + self.imagePlots = ( self.mainWindow.plotAnalogImage, self.mainWindow.plotDigitalImage, - self.mainWindow.plotTransceiverImage, + self.transceiverTab.transceiverImageViews, ) def connect_ui(self): @@ -93,12 +101,54 @@ class PlotTab(QtWidgets.QWidget): self.view.radioButtonFixed.clicked.connect(partial(self.setColorRangeMode, Defines.colorRange.fixed)) self.view.radioButtonCenter.clicked.connect(partial(self.setColorRangeMode, Defines.colorRange.center)) - for plot in self.imagePlots: - plot.scene.sigMouseMoved.connect(partial(self.showPlotValues, plot)) - plot.getHistogramWidget().item.sigLevelChangeFinished.connect(partial(self.handleHistogramChange, plot)) + for index, checkBox in enumerate(self.checkBoxCounters): + checkBox.stateChanged.connect(partial(self.displayCounter, index)) + + # show image Values for analog image + nMaxY = lambda : self.mainWindow.nAnalogRows + nMaxX = lambda : self.mainWindow.nAnalogCols + frame = lambda : self.mainWindow.analog_frame + plot = self.mainWindow.plotAnalogImage + plot.scene.sigMouseMoved.connect(partial(self.showPlotValues, plot, nMaxX, nMaxY, frame)) + plot.getHistogramWidget().item.sigLevelChangeFinished.connect(partial(self.handleHistogramChange, plot)) + + # show image Values for digital image + nMaxY = lambda : self.mainWindow.nDigitalRows + nMaxX = lambda : self.mainWindow.nDigitalCols + frame = lambda : self.mainWindow.digital_frame + plot = self.mainWindow.plotDigitalImage + plot.scene.sigMouseMoved.connect(partial(self.showPlotValues, plot, nMaxX, nMaxY, frame)) + plot.getHistogramWidget().item.sigLevelChangeFinished.connect(partial(self.handleHistogramChange, plot)) + + # show image Values for transceiver image + nMaxY = lambda : self.transceiverTab.nTransceiverRows + nMaxX = lambda : self.transceiverTab.nTransceiverCols + + for index, image_view in enumerate(self.transceiverTab.transceiverImageViews): + frame = lambda : image_view.getImageItem().image + image_view.scene.sigMouseMoved.connect(partial(self.showPlotValues, image_view, nMaxX, nMaxY, frame)) + image_view.getHistogramWidget().item.sigLevelChangeFinished.connect(partial(self.handleHistogramChange, image_view)) self.view.checkBoxShowLegend.stateChanged.connect(self.toggleLegend) + + def displayCounter(self, index : int, state : int): + # toggle the display of the counter i and update the splitter + self.transceiverTab.shownCounters[index] = (state == QtCore.Qt.Checked) + self.transceiverTab.update_ImageSplitter() + + def setCounterCheckBox(self, index : int, check : bool): + self.checkBoxCounters[index].setChecked(check) + + def enableCounterCheckBox(self, index : int, enabled : bool): + is_transceiver = self.mainWindow.romode.value in [3, 4] + is_image = self.view.radioButtonImage.isChecked() + self.checkBoxCounters[index].setEnabled(enabled and is_transceiver and is_image) + + def enableallCounterCheckBox(self, enabled : bool): + for checkBox in self.checkBoxCounters: + checkBox.setEnabled(enabled) + def refresh(self): self.getZMQHWM() @@ -262,7 +312,11 @@ class PlotTab(QtWidgets.QWidget): updates UI views should be called after every change to cmin or cmax """ for plot in self.imagePlots: - plot.getHistogramWidget().item.setLevels(min=self.cmin, max=self.cmax) + if isinstance(plot, list): # hacky for now only transceiver image is a PlotSplitter + for p in plot: + p.getHistogramWidget().item.setLevels(min=self.cmin, max=self.cmax) + else: + plot.getHistogramWidget().item.setLevels(min=self.cmin, max=self.cmax) self.view.cminSpinBox.setValue(self.cmin) self.view.cmaxSpinBox.setValue(self.cmax) @@ -271,7 +325,9 @@ class PlotTab(QtWidgets.QWidget): # print(f'color map:{self.comboBoxColorMap.currentText()}') self.mainWindow.plotAnalogImage.setColorMap(cm) self.mainWindow.plotDigitalImage.setColorMap(cm) - self.mainWindow.plotTransceiverImage.setColorMap(cm) + for i, showncounter in enumerate(self.transceiverTab.shownCounters): + if showncounter: + self.transceiverTab.transceiverImageViews[i].setColorMap(cm) def getZMQHWM(self): @@ -343,11 +399,11 @@ class PlotTab(QtWidgets.QWidget): self.mainWindow.transceiverPlots[i].hide() def addAllSelectedTransceiverPlots(self): - for i in range(Defines.transceiver.count): + for i in range(Defines.transceiver.maxcount): self.addSelectedTransceiverPlots(i) def removeAllTransceiverPlots(self): - for i in range(Defines.transceiver.count): + for i in range(Defines.transceiver.maxcount): self.mainWindow.transceiverPlots[i].hide() def showPlot(self): @@ -356,7 +412,7 @@ class PlotTab(QtWidgets.QWidget): self.mainWindow.plotTransceiverWaveform.hide() self.mainWindow.plotAnalogImage.hide() self.mainWindow.plotDigitalImage.hide() - self.mainWindow.plotTransceiverImage.hide() + self.transceiverTab.transceiverImageSplitter.hide() self.view.labelDigitalWaveformOption.setDisabled(True) self.view.radioButtonOverlay.setDisabled(True) self.view.radioButtonStripe.setDisabled(True) @@ -379,7 +435,10 @@ class PlotTab(QtWidgets.QWidget): if self.view.radioButtonWaveform.isChecked(): self.mainWindow.plotTransceiverWaveform.show() elif self.view.radioButtonImage.isChecked(): - self.mainWindow.plotTransceiverImage.show() + for i, showncounter in enumerate(self.transceiverTab.shownCounters): + self.transceiverTab.transceiverImageViews[i].setVisible(showncounter) + + self.transceiverTab.transceiverImageSplitter.show() def plotOptions(self): @@ -404,14 +463,18 @@ class PlotTab(QtWidgets.QWidget): 'bottom', "Transceiver Sample [#]") self.view.stackedWidgetPlotType.setCurrentIndex(0) + self.enableallCounterCheckBox(False) # disable counter checkboxes for waveform elif self.view.radioButtonImage.isChecked(): self.view.stackedWidgetPlotType.setCurrentIndex(2) + is_transceiver = self.mainWindow.romode.value in [3, 4] + self.enableallCounterCheckBox(is_transceiver) # enable counter checkboxes for matterhorn self.setDecoder() if self.view.radioButtonNoPlot.isChecked(): self.view.labelPlotOptions.hide() self.view.stackedWidgetPlotType.hide() + self.enableallCounterCheckBox(False) # disable counter checkboxes when no plot is selected # enable plotting else: self.view.labelPlotOptions.show() @@ -419,40 +482,48 @@ class PlotTab(QtWidgets.QWidget): self.mainWindow.read_timer.start(Defines.Time_Plot_Refresh_ms) def setDecoder(self): + # TODO: really dont like to set attributes on the fly - hard to understand whats going on if self.view.comboBoxPlot.currentText() == "Matterhorn02": print("Initializing decoder for Matterhorn02") - self.mainWindow.nTransceiverRows = Matterhorn02.nRows - self.mainWindow.nTransceiverCols = Matterhorn02.nCols + self.transceiverTab.nTransceiverRows = Matterhorn02.nRows + self.transceiverTab.nTransceiverCols = Matterhorn02.nCols + self.transceiverTab.update_numCounters(1) self.mainWindow.decoder = transform.Matterhorn02TransceiverTransform() elif self.view.comboBoxPlot.currentText() == "Matterhorn1_16bit_1_counter": print("Initializing decoder for Matterhorn1 with 1 counter 16 bit dynamic range") - self.mainWindow.nTransceiverRows = Matterhorn10.nRows - self.mainWindow.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.nTransceiverRows = Matterhorn10.nRows + self.transceiverTab.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.update_numCounters(1) self.mainWindow.decoder = transform.Matterhorn10Transform(16, 1) elif self.view.comboBoxPlot.currentText() == "Matterhorn1_16bit_4_counters": print("Initializing decoder for Matterhorn1 with 4 counters 16 bit dynamic range") - self.mainWindow.nTransceiverRows = Matterhorn10.nRows*4 - self.mainWindow.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.nTransceiverRows = Matterhorn10.nRows + self.transceiverTab.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.update_numCounters(4) self.mainWindow.decoder = transform.Matterhorn10Transform(16, 4) elif self.view.comboBoxPlot.currentText() == "Matterhorn1_8bit_1_counter": print("Initializing decoder for Matterhorn1 with 1 counter 8 bit dynamic range") - self.mainWindow.nTransceiverRows = Matterhorn10.nRows - self.mainWindow.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.nTransceiverRows = Matterhorn10.nRows + self.transceiverTab.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.update_numCounters(1) self.mainWindow.decoder = transform.Matterhorn10Transform(8, 1) elif self.view.comboBoxPlot.currentText() == "Matterhorn1_8bit_4_counters": print("Initializing decoder for Matterhorn1 with 4 counters 8 bit dynamic range") - self.mainWindow.nTransceiverRows = Matterhorn10.nRows*4 - self.mainWindow.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.nTransceiverRows = Matterhorn10.nRows + self.transceiverTab.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.update_numCounters(4) self.mainWindow.decoder = transform.Matterhorn10Transform(8, 4) elif self.view.comboBoxPlot.currentText() == "Matterhorn1_4bit_4_counters": print("Initializing decoder for Matterhorn1 with 4 counters 4 bit dynamic range") - self.mainWindow.nTransceiverRows = Matterhorn10.nRows*4 - self.mainWindow.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.nTransceiverRows = Matterhorn10.nRows + self.transceiverTab.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.update_numCounters(4) self.mainWindow.decoder = transform.Matterhorn10Transform(4, 4) elif self.view.comboBoxPlot.currentText() == "Matterhorn1_4bit_1_counter": print("Initializing decoder for Matterhorn1 with 1 counter 4 bit dynamic range") - self.mainWindow.nTransceiverRows = Matterhorn10.nRows - self.mainWindow.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.nTransceiverRows = Matterhorn10.nRows + self.transceiverTab.nTransceiverCols = Matterhorn10.nCols + self.transceiverTab.update_numCounters(1) self.mainWindow.decoder = transform.Matterhorn10Transform(4, 1) elif self.view.comboBoxPlot.currentText() == "Moench04": self.mainWindow.nAnalogRows = Moench04.nRows @@ -533,21 +604,13 @@ class PlotTab(QtWidgets.QWidget): # get the RGB Values # print(color.getRgb()) - def showPlotValues(self, sender, pos): + def showPlotValues(self, sender, get_nMaxX, get_nMaxY, get_frame, pos): x = sender.getImageItem().mapFromScene(pos).x() y = sender.getImageItem().mapFromScene(pos).y() val = 0 - nMaxY = self.mainWindow.nAnalogRows - nMaxX = self.mainWindow.nAnalogCols - frame = self.mainWindow.analog_frame - if sender == self.mainWindow.plotDigitalImage: - nMaxY = self.mainWindow.nDigitalRows - nMaxX = self.mainWindow.nDigitalCols - frame = self.mainWindow.digital_frame - elif sender == self.mainWindow.plotTransceiverImage: - nMaxY = self.mainWindow.nTransceiverRows - nMaxX = self.mainWindow.nTransceiverCols - frame = self.mainWindow.transceiver_frame + nMaxX = get_nMaxX() + nMaxY = get_nMaxY() + frame = get_frame() if 0 <= x < nMaxX and 0 <= y < nMaxY and not np.array_equal(frame, []): val = frame[int(y), int(x)] message = f'[row, col]: [{y:.2f}, {x:.2f}] = {val:.2f}' diff --git a/pyctbgui/pyctbgui/services/Transceiver.py b/pyctbgui/pyctbgui/services/Transceiver.py index 13bb72fca..828decd26 100644 --- a/pyctbgui/pyctbgui/services/Transceiver.py +++ b/pyctbgui/pyctbgui/services/Transceiver.py @@ -2,7 +2,7 @@ from functools import partial from pathlib import Path import numpy as np -from PyQt5 import QtWidgets, uic +from PyQt5 import QtWidgets, QtCore, uic import pyqtgraph as pg from pyqtgraph import LegendItem @@ -24,11 +24,18 @@ class TransceiverTab(QtWidgets.QWidget): self.plotTab = None self.legend: LegendItem | None = None self.acquisitionTab = None - + self.nCounters: int = Defines.transceiver.maxcount + self.nTransceiverRows : int = 0 + self.nTransceiverCols : int = 0 + self.shownCounters: list[bool] = [True] * Defines.transceiver.maxcount # per default show all 4 counters + self.transceiverImageSplitter = QtWidgets.QSplitter(QtCore.Qt.Vertical) + self.transceiverImageViews : list = [] # pg image view for each counter + self.firstTransceiverImage : list[bool] = [True] * Defines.transceiver.maxcount # to keep track of first image for each counter to maintain zoom state + def setup_ui(self): self.plotTab = self.mainWindow.plotTab self.acquisitionTab = self.mainWindow.acquisitionTab - for i in range(Defines.transceiver.count): + for i in range(Defines.transceiver.maxcount): self.setTransceiverButtonColor(i, self.plotTab.getRandomColor()) self.initializeAllTransceiverPlots() @@ -39,7 +46,7 @@ class TransceiverTab(QtWidgets.QWidget): self.plotTab.subscribeToggleLegend(self.updateLegend) def connect_ui(self): - for i in range(Defines.transceiver.count): + for i in range(Defines.transceiver.maxcount): getattr(self.view, f"checkBoxTransceiver{i}").stateChanged.connect(partial(self.setTransceiverEnable, i)) getattr(self.view, f"checkBoxTransceiver{i}Plot").stateChanged.connect(partial(self.setTransceiverEnablePlot, i)) @@ -55,7 +62,7 @@ class TransceiverTab(QtWidgets.QWidget): """ enabledPlots = [] self.legend.clear() - for i in range(Defines.transceiver.count): + for i in range(Defines.transceiver.maxcount): if getattr(self.view, f'checkBoxTransceiver{i}Plot').isChecked(): plotName = getattr(self.view, f"labelTransceiver{i}").text() enabledPlots.append((self.mainWindow.transceiverPlots[i], plotName)) @@ -104,7 +111,7 @@ class TransceiverTab(QtWidgets.QWidget): trans_array = self._processWaveformData(data, dSamples, self.mainWindow.romode.value, self.mainWindow.nDBitEnabled, self.nTransceiverEnabled) idx = 0 - for i in range(Defines.transceiver.count): + for i in range(Defines.transceiver.maxcount): checkBoxPlot = getattr(self.view, f"checkBoxTransceiver{i}Plot") checkBoxEn = getattr(self.view, f"checkBoxTransceiver{i}") if checkBoxEn.isChecked() and checkBoxPlot.isChecked(): @@ -133,9 +140,7 @@ class TransceiverTab(QtWidgets.QWidget): transceiverOffset += nDBitEnabled * (nbitsPerDBit // 8) trans_array = np.array(np.frombuffer(data, offset=transceiverOffset, dtype=np.uint8)) - tmp = self.mainWindow.decoder(trans_array) - - return tmp + return self.mainWindow.decoder(trans_array) def processImageData(self, data, dSamples): """ @@ -145,28 +150,47 @@ class TransceiverTab(QtWidgets.QWidget): data: raw image data """ # get zoom state - viewBox = self.mainWindow.plotTransceiverImage.getView() - - state = viewBox.getState() + image_states = [image_view.getView().getState() for image_view in self.transceiverImageViews] + + transceiver_frame : np.ndarray = None try: - self.mainWindow.transceiver_frame = self._processImageData(data, dSamples, self.mainWindow.romode.value, + transceiver_frame = self._processImageData(data, dSamples, self.mainWindow.romode.value, self.mainWindow.nDBitEnabled) self.plotTab.ignoreHistogramSignal = True - self.mainWindow.plotTransceiverImage.setImage(self.mainWindow.transceiver_frame) + + for i in range(transceiver_frame.shape[0]): + self.transceiverImageViews[i].setImage(transceiver_frame[i]) except Exception as e: self.mainWindow.statusbar.setStyleSheet("color:red") self.acquisitionTab.updateCurrentFrame('Invalid Image') self.mainWindow.statusbar.showMessage(str(e)) print("Error: ", str(e)) - self.plotTab.setFrameLimits(self.mainWindow.transceiver_frame) + self.plotTab.setFrameLimits(transceiver_frame) # keep the zoomed in state (not 1st image) - if self.mainWindow.firstTransceiverImage: - self.mainWindow.firstTransceiverImage = False - else: - viewBox.setState(state) - return self.mainWindow.transceiver_frame + for idx, image_view in enumerate(self.transceiverImageViews): + if(self.firstTransceiverImage[idx] and self.shownCounters[idx]): + self.firstTransceiverImage[idx] = False + else: + image_view.getView().setState(image_states[idx]) + + return transceiver_frame + + def update_numCounters(self, num_counters): + # update the number of counters and adjust the image splitter accordingly + self.nCounters = num_counters + for i in range(Defines.transceiver.maxcount): + self.shownCounters[i] = i < self.nCounters + self.plotTab.setCounterCheckBox(i, self.shownCounters[i]) # check the counter checkbox + self.plotTab.enableCounterCheckBox(i, self.shownCounters[i]) # disable counter checkbox + + self.update_ImageSplitter() # update the splitter to show/hide image views based on the number of counters + + + def update_ImageSplitter(self): + for i, showncounter in enumerate(self.shownCounters): + self.transceiverImageViews[i].setVisible(showncounter) def initializeAllTransceiverPlots(self): self.mainWindow.plotTransceiverWaveform = pg.plot() @@ -174,7 +198,7 @@ class TransceiverTab(QtWidgets.QWidget): self.mainWindow.verticalLayoutPlot.addWidget(self.mainWindow.plotTransceiverWaveform, 5) self.mainWindow.transceiverPlots = {} waveform = np.zeros(1000) - for i in range(Defines.transceiver.count): + for i in range(Defines.transceiver.maxcount): pen = pg.mkPen(color=self.getTransceiverButtonColor(i), width=1) legendName = getattr(self.view, f"labelTransceiver{i}").text() self.mainWindow.transceiverPlots[i] = self.mainWindow.plotTransceiverWaveform.plot(waveform, @@ -182,16 +206,19 @@ class TransceiverTab(QtWidgets.QWidget): name=legendName) self.mainWindow.transceiverPlots[i].hide() - self.mainWindow.plotTransceiverImage = pg.ImageView() - self.mainWindow.nTransceiverRows = 0 - self.mainWindow.nTransceiverCols = 0 - self.mainWindow.transceiver_frame = np.zeros( - (self.mainWindow.nTransceiverRows, self.mainWindow.nTransceiverCols)) - self.mainWindow.plotTransceiverImage.setImage(self.mainWindow.transceiver_frame) - self.mainWindow.verticalLayoutPlot.addWidget(self.mainWindow.plotTransceiverImage, 6) - + # initialize image cm = pg.colormap.get('CET-L9') # prepare a linear color map - self.mainWindow.plotTransceiverImage.setColorMap(cm) + + for i in range(self.nCounters): + imageView = pg.ImageView() + imageView.setColorMap(cm) + self.transceiverImageViews.append(imageView) + self.transceiverImageSplitter.addWidget(imageView) + + self.update_ImageSplitter() # update the splitter + self.mainWindow.verticalLayoutPlot.addWidget(self.transceiverImageSplitter, 6) + + def getTransceiverEnableReg(self): retval = self.det.transceiverenable diff --git a/pyctbgui/pyctbgui/ui/plot.ui b/pyctbgui/pyctbgui/ui/plot.ui index 848d48f16..3abb75fb4 100644 --- a/pyctbgui/pyctbgui/ui/plot.ui +++ b/pyctbgui/pyctbgui/ui/plot.ui @@ -6,8 +6,8 @@ 0 0 - 860 - 800 + 1937 + 1104 @@ -25,7 +25,7 @@ 10 50 841 - 141 + 161 @@ -35,13 +35,10 @@ QFrame::Raised - - - - true - + + - Type: + Options: @@ -50,6 +47,12 @@ false + + + 0 + 31 + + Distribution @@ -58,28 +61,193 @@ - - - - Qt::Vertical + + + + true - + - 20 - 40 + 0 + 31 - + + Waveform + + + true + + + buttonGroup + + + + + + + + 0 + 31 + + + + Counter 2 + + + + + + + + 0 + 31 + + + + Counter 3 + + + + + + + true + + + + 0 + 31 + + + + No Plot + + + false + + + buttonGroup + + + + + + + + 0 + 31 + + + + Counter 0 + + + + + + + true + + + + 0 + 31 + + + + <html><head/><body><p>If set to high readout, zmq HWM is set to 2 and buffer size to 1MB to drop zmq packets to catch up.</p><p>If set to low readout (default), zmq HWM is set to zmq default (1000) and buffer size to os default to not drop any zmq packets.</p></body></html> + + + + Low - drop no zmq packet + + + + + High - drop zmq packets to catch up + + + + + + + + true + + + + 0 + 31 + + + + Type: + + + + + + + + 150 + 16777215 + + + + <html><head/><body><p>If set to high readout, zmq HWM is set to 2 and buffer size to 1MB to drop zmq packets to catch up.</p><p>If set to low readout (default), zmq HWM is set to zmq default (1000) and buffer size to os default to not drop any zmq packets.</p></body></html> + + + Readout speed: + + + + + + + true + + + + 0 + 31 + + + + Image + + + buttonGroup + + + + + 16777215 + 31 + + - 2 + 0 + + 0 + + + 0 + + + + 0 + 31 + + Digital plot: @@ -87,6 +255,12 @@ + + + 0 + 31 + + Overlay @@ -110,6 +284,12 @@ + + + 0 + 31 + + show legend @@ -120,6 +300,12 @@ + + + 0 + 31 + + Stripe @@ -149,6 +335,42 @@ + + 0 + + + 0 + + + + + Decoder: + + + + + + + Color Map: + + + + + + + true + + + + 120 + 31 + + + + Color map of the image + + + @@ -210,35 +432,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - true - - - - 120 - 31 - - - - Color map of the image - - - @@ -255,292 +448,661 @@ - - - - Decoder: + + + + Qt::Horizontal - - - - - - Color Map: + + + 40 + 20 + - + - - + + + + + 0 + 31 + + - Options: + Counter 1 - - - - true - - - No Plot - - - false - - - buttonGroup - - - - - - - true - + + 0 31 - - <html><head/><body><p>If set to high readout, zmq HWM is set to 2 and buffer size to 1MB to drop zmq packets to catch up.</p><p>If set to low readout (default), zmq HWM is set to zmq default (1000) and buffer size to os default to not drop any zmq packets.</p></body></html> - - - - Low - drop no zmq packet - - - - - High - drop zmq packets to catch up - - - - - - <html><head/><body><p>If set to high readout, zmq HWM is set to 2 and buffer size to 1MB to drop zmq packets to catch up.</p><p>If set to low readout (default), zmq HWM is set to zmq default (1000) and buffer size to os default to not drop any zmq packets.</p></body></html> - Readout speed: + Counters: - - - - true - - - Image - - - buttonGroup - - - - - - - true - - - Waveform - - - true - - - buttonGroup - - - - 0 - 480 + 10 + 440 841 - 311 + 400 + + + 0 + 400 + + + + + 16777215 + 16777215 + + QFrame::StyledPanel QFrame::Sunken - - - + + + false + + + 0 + 31 + + + + + 16777215 + 16777215 + + Fit Panel ADC: - - + + false - - Max: + + + 0 + 31 + - - - - - - false + + + 16777215 + 16777215 + - - Values - - - - - - - false + + QFrame::NoFrame Dynamic Range: - - + + false + + + 0 + 31 + + + + + 16777215 + 16777215 + + - Y: + Serial Offset: - - + + false + + + 100 + 31 + + + + + 45 + 16777215 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + false + + + 100 + 31 + + + + + 16777215 + 16777215 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + false + + + 0 + 0 + + + + + 16777215 + 16777215 + + Min: - - + + false + + + 245 + 31 + + + + + 200 + 16777215 + + - X: + Pedestal Subtract - + false + + + 100 + 31 + + + + + 45 + 16777215 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + false - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0 + 31 + + + + Raw Data - - + + false - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 0 + 31 + + + + + 16777215 + 16777215 + + + + Min: - + + + + false + + + + 0 + 31 + + + + + 16777215 + 16777215 + + + + N Counters: + + + + + + + false + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Max: + + + + false + + + 100 + 31 + + + + + 45 + 16777215 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + false + + + 100 + 31 + + + + + 45 + 16777215 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + 0 + 31 + + Image Pixels: - + + + + false + + + + 100 + 31 + + + + + 45 + 16777215 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + + 0 + 31 + + + + + 16777215 + 16777215 + + + + X: + + + + false + + + 100 + 31 + + + + + 16777215 + 16777215 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + + + + false + + + + 100 + 31 + + + + + 16777215 + 16777215 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + false + + + + 100 + 31 + + + + + 45 + 16777215 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + false + + + 100 + 31 + + + + + 45 + 16777215 + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - + + + + false + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Plot Bit: + + + + + + + false + + + + 115 + 0 + + + + + 156 + 16777215 + + + + Max: + + + + + + + false + + + + 0 + 31 + + + + + 16777215 + 31 + + + + Y: + + + + + + + false + + + + 100 + 31 + + + + + 45 + 16777215 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + false + + + X + + + + + false @@ -549,77 +1111,17 @@ - - + + false - Min: + Values - - - - false - - - X - - - - - - - false - - - Max: - - - - - - - false - - - Serial offset: - - - - - - - false - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - false - - - Plot Bit: - - - - - - - false - - - Raw Data - - - - + @@ -635,241 +1137,223 @@ - - - - false - - - Pedestal Subtract - - - - - - - false - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - false - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - false - - - N Counters: - - - 10 - 219 + 221 841 - 81 + 91 + + + 0 + 0 + + QFrame::StyledPanel QFrame::Raised - - - - 0 - -7 - 841 - 111 - + + + 9 - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 40 - 20 - - - - - - - - Color Range: - - - - - - - - - 0 - 0 - 121 - 91 - - - - - - - - 16777215 - 15 - - - - All - - - true - - - buttonGroup_4 - - - - - - - true - - - - 16777215 - 15 - - - - 3-97% - - - buttonGroup_4 - - - - - - - - 16777215 - 15 - - - - Fixed - - - buttonGroup_4 - - - - - - - - - - - - 50 - 16777215 - - - - min: - - - - - - - false - - - -1000.000000000000000 - - - 100000.000000000000000 - - - - - - - - 50 - 16777215 - - - - max: - - - - - - - false - - - 100000.000000000000000 - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 40 - 20 - - - - - - + + 9 + + + + + QFrame::NoFrame + + + QFrame::Plain + + + + + + true + + + + 16777215 + 15 + + + + 3-97% + + + buttonGroup_4 + + + + + + + + 16777215 + 15 + + + + All + + + true + + + buttonGroup_4 + + + + + + + + 16777215 + 15 + + + + Fixed + + + buttonGroup_4 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + 0 + 31 + + + + + 16777215 + 31 + + + + Color Range: + + + + + + + false + + + + 0 + 31 + + + + -1000.000000000000000 + + + 100000.000000000000000 + + + + + + + + 50 + 31 + + + + max: + + + + + + + false + + + + 0 + 31 + + + + 100000.000000000000000 + + + + + + + + 50 + 31 + + + + min: + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + 10 - 330 + 322 841 - 101 + 108 @@ -878,208 +1362,183 @@ QFrame::Raised - - - - 0 - 0 - 841 - 51 - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 40 - 20 - - - - - - - - true - - - Pedestal: - - - - - - - - 250 - 0 - - - - recorded frames: 0 - - - - - - - true - - - Record - - - true - - - false - - - buttonGroup_3 - - - - - - - true - - - Apply - - - true - - - buttonGroup_3 - - - - - - - true - - - - 0 - 31 - - - - background-color: rgb(199, 213, 207); - - - Reset - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 40 - 20 - - - - - - - - - - -1 - 49 - 841 - 51 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 120 - 31 - - - - background-color: rgb(199, 213, 207); - - - Load Pedestal - - - - - - - - 120 - 31 - - - - background-color: rgb(199, 213, 207); - - - Save Pedestal - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 40 - 20 - - - - - - + + + + + true + + + + 16777215 + 31 + + + + Apply + + + true + + + buttonGroup_3 + + + + + + + true + + + + 16777215 + 31 + + + + Record + + + true + + + false + + + buttonGroup_3 + + + + + + + + 0 + 0 + + + + + 16777215 + 31 + + + + recorded frames: 0 + + + + + + + true + + + + 0 + 31 + + + + background-color: rgb(199, 213, 207); + + + Reset + + + + + + + true + + + + 150 + 0 + + + + + 16777215 + 31 + + + + Pedestal: + + + + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + 120 + 31 + + + + background-color: rgb(199, 213, 207); + + + Save Pedestal + + + + + + + + 120 + 31 + + + + background-color: rgb(199, 213, 207); + + + Load Pedestal + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 40 + 20 + + + + + + + + + - diff --git a/pyctbgui/pyctbgui/utils/defines.py b/pyctbgui/pyctbgui/utils/defines.py index 2226b5c33..b4ecfc04e 100644 --- a/pyctbgui/pyctbgui/utils/defines.py +++ b/pyctbgui/pyctbgui/utils/defines.py @@ -35,8 +35,8 @@ class Defines: loops_count = 6 class transceiver: - count = 4 - tabIndex = 4 + maxcount = 4 + maxtabIndex = 4 class slowAdc: tabIndex = 2 From 2a24bad2183e349d5a4f5da405a4872fd8c4c7ec Mon Sep 17 00:00:00 2001 From: AliceMazzoleni99 Date: Wed, 17 Jun 2026 13:50:07 +0200 Subject: [PATCH 5/6] treating warnings as errors during workflows (#1473) * treating warnings as errors during workflows * suppress warnings for moench calibration * no format overflow * revert formatting * bettter conditional branching in cmake --- .github/workflows/cmake.yaml | 3 ++- CMakeLists.txt | 15 ++++++++++++--- .../moenchExecutables/CMakeLists.txt | 8 ++++++++ .../moenchExecutables/moench03Interpolation.cpp | 4 ++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/.github/workflows/cmake.yaml b/.github/workflows/cmake.yaml index 1c4d7b0f6..d3eeee0bd 100644 --- a/.github/workflows/cmake.yaml +++ b/.github/workflows/cmake.yaml @@ -37,7 +37,8 @@ jobs: -DSLS_USE_PYTHON=ON \ -DSLS_USE_HDF5=ON \ -DSLS_USE_GUI=ON \ - -DSLS_USE_MOENCH=ON + -DSLS_USE_MOENCH=ON \ + -DSLS_TREAT_WARNINGS_AS_ERRORS=ON - name: Build # Build your program with the given configuration diff --git a/CMakeLists.txt b/CMakeLists.txt index 8830eb86e..d29e4782b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -236,6 +236,7 @@ option(SLS_USE_MOENCH "compile zmq and post processing for Moench" OFF) option(SLS_USE_JUNGFRAU "compile post processing for Jungfrau" OFF) option(SLS_USE_MATTERHORN "compile matterhorn server" OFF) option(SLS_INSTALL_VERSIONED_BINARIES "Add version number to binaries on install" OFF) #Needed for multi version RPM +option(SLS_TREAT_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) #Convenience option to switch off defaults when building Moench binaries only option(SLS_BUILD_ONLY_MOENCH "compile only Moench" OFF) @@ -347,7 +348,9 @@ if (NOT TARGET slsProjectWarnings) -Wno-missing-field-initializers) endif() - + if(SLS_TREAT_WARNINGS_AS_ERRORS) + target_compile_options(slsProjectWarnings INTERFACE -Werror) + endif() endif() @@ -376,6 +379,10 @@ if (NOT TARGET slsProjectCSettings) target_compile_options(slsProjectCSettings INTERFACE -fsanitize=address,undefined -fno-omit-frame-pointer) target_link_libraries(slsProjectCSettings INTERFACE -fsanitize=address,undefined) endif() + + if(SLS_TREAT_WARNINGS_AS_ERRORS) + target_compile_options(slsProjectCSettings INTERFACE -Werror) + endif() endif() @@ -483,13 +490,15 @@ if(SLS_BUILD_DOCS) add_subdirectory(docs) endif(SLS_BUILD_DOCS) -if(SLS_USE_MOENCH) +if(SLS_USE_MOENCH OR SLS_USE_JUNGFRAU) add_subdirectory(slsDetectorCalibration/tiffio) +endif() + +if(SLS_USE_MOENCH) add_subdirectory(slsDetectorCalibration/moenchExecutables) endif(SLS_USE_MOENCH) if(SLS_USE_JUNGFRAU) - add_subdirectory(slsDetectorCalibration/tiffio) add_subdirectory(slsDetectorCalibration/jungfrauExecutables) endif(SLS_USE_JUNGFRAU) diff --git a/slsDetectorCalibration/moenchExecutables/CMakeLists.txt b/slsDetectorCalibration/moenchExecutables/CMakeLists.txt index 44b212df3..55faa558d 100644 --- a/slsDetectorCalibration/moenchExecutables/CMakeLists.txt +++ b/slsDetectorCalibration/moenchExecutables/CMakeLists.txt @@ -76,6 +76,14 @@ foreach(exe ${MOENCH_EXECUTABLES}) slsProjectOptions ) + target_compile_options(${exe} PRIVATE + -Wno-unused-but-set-variable + -Wno-format-nonliteral + -Wno-format-security + -Wno-double-promotion + -Wno-unused-variable + -Wno-format-overflow) + set_target_properties(${exe} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin diff --git a/slsDetectorCalibration/moenchExecutables/moench03Interpolation.cpp b/slsDetectorCalibration/moenchExecutables/moench03Interpolation.cpp index cbbe32746..dc3e71583 100644 --- a/slsDetectorCalibration/moenchExecutables/moench03Interpolation.cpp +++ b/slsDetectorCalibration/moenchExecutables/moench03Interpolation.cpp @@ -204,9 +204,9 @@ int main(int argc, char *argv[]) { */ //#endif - if (totquad > cmin && cl.x >= xmin && cl.x <= xmax && + if (totquad > static_cast(cmin) && cl.x >= xmin && cl.x <= xmax && cl.y >= ymin && cl.y <= ymax && - totquad < cmax) { + totquad < static_cast(cmax)) { // if (sum > cmin && totquad / sum > 0.8 && totquad / sum < 1.2 && // sum < cmax) { From 4395e7b5f6a9251e0ec647288e6649dcc962b69f Mon Sep 17 00:00:00 2001 From: AliceMazzoleni99 Date: Fri, 26 Jun 2026 17:43:27 +0200 Subject: [PATCH 6/6] added moench05 to pyctbgui (#1481) * added moench05 to pyctbgui * use moench05 defs from aare --- pyctbgui/pyctbgui/services/Plot.py | 6 +++++- pyctbgui/pyctbgui/ui/plot.ui | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/pyctbgui/pyctbgui/services/Plot.py b/pyctbgui/pyctbgui/services/Plot.py index 2b66e71f5..688265484 100644 --- a/pyctbgui/pyctbgui/services/Plot.py +++ b/pyctbgui/pyctbgui/services/Plot.py @@ -7,7 +7,7 @@ import numpy as np from PyQt5 import QtWidgets, QtGui, QtCore, uic from aare import transform, ReadoutMode -from aare._aare import Matterhorn10, Matterhorn02, Moench04 +from aare._aare import Matterhorn10, Matterhorn02, Moench04, Moench05 import pyqtgraph as pg from pyctbgui.utils import recordOrApplyPedestal @@ -529,6 +529,10 @@ class PlotTab(QtWidgets.QWidget): self.mainWindow.nAnalogRows = Moench04.nRows self.mainWindow.nAnalogCols = Moench04.nCols self.mainWindow.decoder = transform.Moench04AnalogTransform() + elif self.view.comboBoxPlot.currentText() == "Moench05": + self.mainWindow.nAnalogRows = Moench05.nRows + self.mainWindow.nAnalogCols = Moench05.nCols + self.mainWindow.decoder = transform.Moench05Transform() try: if hasattr(self.mainWindow.decoder, "compatibility") and callable(getattr(self.mainWindow.decoder, "compatibility")): diff --git a/pyctbgui/pyctbgui/ui/plot.ui b/pyctbgui/pyctbgui/ui/plot.ui index 3abb75fb4..01d2b5ec1 100644 --- a/pyctbgui/pyctbgui/ui/plot.ui +++ b/pyctbgui/pyctbgui/ui/plot.ui @@ -424,6 +424,11 @@ Moench04 + + + + Moench05 +