diff --git a/python/src/detector.cpp b/python/src/detector.cpp index 5393cbf6e..f9e3821d7 100644 --- a/python/src/detector.cpp +++ b/python/src/detector.cpp @@ -109,6 +109,13 @@ void init_det(py::module &m) { (Result(Detector::*)(sls::Positions) const) & Detector::getModuleSize, py::arg() = Positions{}); + CppDetectorApi.def("getPortPerModuleGeometry", + (defs::xy(Detector::*)() const) & + Detector::getPortPerModuleGeometry); + CppDetectorApi.def("getPortSize", + (Result(Detector::*)(sls::Positions) const) & + Detector::getPortSize, + py::arg() = Positions{}); CppDetectorApi.def("getDetectorSize", (defs::xy(Detector::*)() const) & Detector::getDetectorSize); CppDetectorApi.def("setDetectorSize", @@ -924,15 +931,14 @@ void init_det(py::module &m) { (void (Detector::*)(bool, sls::Positions)) & Detector::setRxArping, py::arg(), py::arg() = Positions{}); - CppDetectorApi.def("getIndividualRxROIs", - (Result(Detector::*)(sls::Positions) const) & - Detector::getIndividualRxROIs, - py::arg()); CppDetectorApi.def("getRxROI", - (defs::ROI(Detector::*)() const) & Detector::getRxROI); - CppDetectorApi.def( - "setRxROI", (void (Detector::*)(const defs::ROI)) & Detector::setRxROI, - py::arg()); + (std::vector (Detector::*)(int) const) & + Detector::getRxROI, + py::arg()); + CppDetectorApi.def("setRxROI", + (void (Detector::*)(const std::vector &)) & + Detector::setRxROI, + py::arg()); CppDetectorApi.def("clearRxROI", (void (Detector::*)()) & Detector::clearRxROI); CppDetectorApi.def( diff --git a/slsDetectorGui/include/qDrawPlot.h b/slsDetectorGui/include/qDrawPlot.h index 92f660b50..c4b3b2a9d 100644 --- a/slsDetectorGui/include/qDrawPlot.h +++ b/slsDetectorGui/include/qDrawPlot.h @@ -40,6 +40,7 @@ class qDrawPlot : public QWidget, private Ui::PlotObject { void SetDataCallBack(bool enable); void SetBinary(bool enable, int from = 0, int to = 0); void StartAcquisition(); + void UpdateROI(); public slots: void SetPersistency(int val); @@ -166,8 +167,8 @@ class qDrawPlot : public QWidget, private Ui::PlotObject { int64_t currentFrame{0}; mutable std::mutex mPlots; int64_t currentAcqIndex{0}; - slsDetectorDefs::ROI rxRoi{}; - bool isRxRoiDisplayed{false}; + bool hasRoi{false}; + bool roiDisplayInitialized{false}; bool isGapPixels{false}; unsigned int nPixelsX{0}; @@ -176,6 +177,7 @@ class qDrawPlot : public QWidget, private Ui::PlotObject { uint32_t gainMask{0}; int gainOffset{0}; bool gotthard25; + std::vector roi{1}; }; } // namespace sls diff --git a/slsDetectorGui/slsDetectorPlotting/include/SlsQt1DPlot.h b/slsDetectorGui/slsDetectorPlotting/include/SlsQt1DPlot.h index 9dfdee151..878a43b81 100644 --- a/slsDetectorGui/slsDetectorPlotting/include/SlsQt1DPlot.h +++ b/slsDetectorGui/slsDetectorPlotting/include/SlsQt1DPlot.h @@ -5,6 +5,8 @@ #include "SlsQt1DZoomer.h" #include "sls/ansi.h" +#include "sls/sls_detector_defs.h" + #include #include #include @@ -141,8 +143,9 @@ class SlsQt1DPlot : public QwtPlot { void SetLogX(bool yes = 1); void SetLogY(bool yes = 1); - void EnableRoiBox(std::array roi); - void DisableRoiBox(); + void EnableRoiBoxes(std::vector roi, int ymin, + int ymax); + void DisableRoiBoxes(); private: bool gainPlot{false}; @@ -169,6 +172,7 @@ class SlsQt1DPlot : public QwtPlot { friend void SlsQtH1D::Attach(SlsQt1DPlot *p); friend void SlsQtH1D::Detach(SlsQt1DPlot *p); + std::vector> roiBoxes{}; QwtPlotShapeItem *roiBox{nullptr}; signals: diff --git a/slsDetectorGui/slsDetectorPlotting/include/SlsQt2DPlot.h b/slsDetectorGui/slsDetectorPlotting/include/SlsQt2DPlot.h index 4c55db6af..f7142b6eb 100644 --- a/slsDetectorGui/slsDetectorPlotting/include/SlsQt2DPlot.h +++ b/slsDetectorGui/slsDetectorPlotting/include/SlsQt2DPlot.h @@ -3,6 +3,8 @@ #pragma once #include "SlsQt2DHist.h" #include "SlsQt2DZoomer.h" +#include "sls/sls_detector_defs.h" + #include #include #include @@ -71,8 +73,8 @@ class SlsQt2DPlot : public QwtPlot { void SetLogz(bool enable, bool isMin, bool isMax, double min, double max); void SetZRange(bool isMin, bool isMax, double min, double max); void LogZ(bool on = 1); - void EnableRoiBox(std::array roi); - void DisableRoiBox(); + void EnableRoiBoxes(std::vector roi); + void DisableRoiBoxes(); public slots: void showSpectrogram(bool on); @@ -101,7 +103,7 @@ class SlsQt2DPlot : public QwtPlot { QList contourLevelsLog; bool disableZoom{false}; int isLog; - QwtPlotShapeItem *roiBox{nullptr}; + std::vector> roiBoxes{}; }; } // namespace sls diff --git a/slsDetectorGui/slsDetectorPlotting/src/SlsQt1DPlot.cpp b/slsDetectorGui/slsDetectorPlotting/src/SlsQt1DPlot.cpp index 55716cc33..8ae58d4b2 100644 --- a/slsDetectorGui/slsDetectorPlotting/src/SlsQt1DPlot.cpp +++ b/slsDetectorGui/slsDetectorPlotting/src/SlsQt1DPlot.cpp @@ -462,24 +462,26 @@ void SlsQt1DPlot::SetLog(int axisId, bool yes) { Update(); } -void SlsQt1DPlot::EnableRoiBox(std::array roi) { - if (roiBox == nullptr) { - roiBox = new QwtPlotShapeItem(); - roiBox->attach(this); - roiBox->setPen(QColor(Qt::yellow), 2.0, Qt::SolidLine); +void SlsQt1DPlot::EnableRoiBoxes(std::vector roi, + int ymin, int ymax) { + roiBoxes.clear(); + for (auto &r : roi) { + auto box = std::make_unique(); + box->setPen(QColor(Qt::yellow), 2.0, Qt::SolidLine); + // TopLeft - BottomRight (max points are +1 on graph) + QRectF myRect(QPointF(r.xmin, ymin), QPointF(r.xmax - 1, ymax - 1)); + box->setRect(myRect); + box->attach(this); + roiBoxes.push_back(std::move(box)); } - - // TopLeft - BottomRight (max points are +1 on graph) - QRect myRect(QPoint(roi[0], roi[2]), QPoint(roi[1] - 1, roi[3] - 1)); - roiBox->setRect(QRectF(myRect)); replot(); } -void SlsQt1DPlot::DisableRoiBox() { - if (roiBox != nullptr) { - roiBox->detach(); - replot(); +void SlsQt1DPlot::DisableRoiBoxes() { + for (auto &r : roiBoxes) { + r->detach(); } + replot(); } void SlsQt1DPlot::SetZoomX(const QRectF &rect) { diff --git a/slsDetectorGui/slsDetectorPlotting/src/SlsQt2DPlot.cpp b/slsDetectorGui/slsDetectorPlotting/src/SlsQt2DPlot.cpp index 3418abf08..ecd7b6eb3 100644 --- a/slsDetectorGui/slsDetectorPlotting/src/SlsQt2DPlot.cpp +++ b/slsDetectorGui/slsDetectorPlotting/src/SlsQt2DPlot.cpp @@ -350,25 +350,25 @@ void SlsQt2DPlot::showSpectrogram(bool on) { Update(); } -void SlsQt2DPlot::EnableRoiBox(std::array roi) { - if (roiBox == nullptr) { - roiBox = new QwtPlotShapeItem(); +void SlsQt2DPlot::EnableRoiBoxes(std::vector roi) { + roiBoxes.clear(); + for (auto &r : roi) { + auto box = std::make_unique(); + box->setPen(QColor(Qt::yellow), 2.0, Qt::SolidLine); + // TopLeft - BottomRight (max points are +1 on graph) + QRectF myRect(QPointF(r.xmin, r.ymin), QPointF(r.xmax - 1, r.ymax - 1)); + box->setRect(myRect); + box->attach(this); + roiBoxes.push_back(std::move(box)); } - roiBox->setPen(QColor(Qt::yellow), 2.0, Qt::SolidLine); - - // TopLeft - BottomRight (max points are +1 on graph) - QRect myRect(QPoint(roi[0], roi[2]), QPoint(roi[1] - 1, roi[3] - 1)); - roiBox->setRect(QRectF(myRect)); - - roiBox->attach(this); replot(); } -void SlsQt2DPlot::DisableRoiBox() { - if (roiBox != nullptr) { - roiBox->detach(); - replot(); +void SlsQt2DPlot::DisableRoiBoxes() { + for (auto &r : roiBoxes) { + r->detach(); } + replot(); } } // namespace sls diff --git a/slsDetectorGui/src/qDetectorMain.cpp b/slsDetectorGui/src/qDetectorMain.cpp index c5ddc8618..438707c36 100644 --- a/slsDetectorGui/src/qDetectorMain.cpp +++ b/slsDetectorGui/src/qDetectorMain.cpp @@ -539,6 +539,7 @@ void qDetectorMain::EnableTabs(bool enable) { tabDeveloper->Refresh(); tabPlot->Refresh(); + plot->UpdateROI(); plot->StartAcquisition(); } else { // to enable scan box tabPlot->Refresh(); diff --git a/slsDetectorGui/src/qDrawPlot.cpp b/slsDetectorGui/src/qDrawPlot.cpp index 829a3c81a..e967862a3 100644 --- a/slsDetectorGui/src/qDrawPlot.cpp +++ b/slsDetectorGui/src/qDrawPlot.cpp @@ -608,7 +608,6 @@ void qDrawPlot::StartAcquisition() { currentFrame = 0; boxPlot->setTitle("Old Plot"); det->clearAcquiringFlag(); // (from previous exit) or if running - isRxRoiDisplayed = false; // ensure data streaming in receiver (if plot enabled) if (isPlot) { @@ -632,6 +631,34 @@ void qDrawPlot::StartAcquisition() { LOG(logDEBUG) << "End of Starting Acquisition in qDrawPlot"; } +void qDrawPlot::UpdateROI() { + try { + std::lock_guard lock(mPlots); + roi = det->getRxROI(); + roiDisplayInitialized = false; + // roi enabled + if (roi.size() > 1 || !roi[0].completeRoi()) { + hasRoi = true; + // update gap pixels + // hardcoded gap pixels because only for eiger and jungfrau + if (isGapPixels) { + for (auto &r : roi) { + r.xmin += ((r.xmin / 1024) * 6 + (r.xmin / 256) * 2); + r.xmax += ((r.xmax / 1024) * 6 + (r.xmax / 256) * 2); + r.ymin += ((r.ymin / 512) * 34 + (r.ymin / 256) * 2); + r.ymax += ((r.ymax / 512) * 34 + (r.ymax / 256) * 2); + } + LOG(logINFO) + << "Roi recalculated with gap pixels: " << ToString(roi); + } + } else { + hasRoi = false; + } + LOG(logDEBUG) << "Roi: " << ToString(roi); + } + CATCH_DISPLAY("Could not get reciver ROI.", "qDrawPlot::UpdateRoi") +} + void qDrawPlot::AcquireThread() { LOG(logDEBUG) << "Acquire Thread"; std::string mess; @@ -709,7 +736,6 @@ void qDrawPlot::GetData(detectorData *data, uint64_t frameIndex, << " \t dynamic range: " << data->dynamicRange << std::endl << " \t file index: " << data->fileIndex << std::endl << " \t complete image: " << data->completeImage << std::endl - << " \t rx Roi: " << ToString(data->rxRoi) << std::endl << " ]"; progress = data->progressIndex; @@ -717,22 +743,6 @@ void qDrawPlot::GetData(detectorData *data, uint64_t frameIndex, currentFrame = frameIndex; LOG(logDEBUG) << "[ Progress:" << progress << "%, Frame:" << currentFrame << " ]"; - if (!isRxRoiDisplayed) { - rxRoi.xmin = data->rxRoi[0]; - rxRoi.xmax = data->rxRoi[1]; - rxRoi.ymin = data->rxRoi[2]; - rxRoi.ymax = data->rxRoi[3]; - // only for 2d anyway - if (isGapPixels) { - rxRoi.xmin += ((rxRoi.xmin / 1024) * 6 + (rxRoi.xmin / 256) * 2); - rxRoi.xmax += ((rxRoi.xmax / 1024) * 6 + (rxRoi.xmax / 256) * 2); - rxRoi.ymin += ((rxRoi.ymin / 512) * 34 + (rxRoi.ymin / 256) * 2); - rxRoi.ymax += ((rxRoi.ymax / 512) * 34 + (rxRoi.ymax / 256) * 2); - LOG(logINFO) << "Rx_roi recalculated with gap pixels: " - << ToString(rxRoi); - } - LOG(logDEBUG) << "Rx_roi: " << ToString(rxRoi); - } // 1d check if npixelX has changed (m3 for different counters enabled) if (is1d && static_cast(nPixelsX) != data->nx) { @@ -971,30 +981,27 @@ void qDrawPlot::Update1dPlot() { xyRangeChanged = false; } plot1d->DisableZoom(disableZoom); - if (!isRxRoiDisplayed) { - isRxRoiDisplayed = true; - if (rxRoi.completeRoi()) { - plot1d->DisableRoiBox(); + if (!roiDisplayInitialized) { + roiDisplayInitialized = true; + if (!hasRoi) { + plot1d->DisableRoiBoxes(); if (isGainDataExtracted) { - gainplot1d->DisableRoiBox(); + gainplot1d->DisableRoiBoxes(); } lblRxRoiEnabled->hide(); } else { - plot1d->EnableRoiBox(std::array{ - rxRoi.xmin, rxRoi.xmax, (int)plot1d->GetYMinimum(), - (int)plot1d->GetYMaximum()}); + plot1d->EnableRoiBoxes(roi, (int)plot1d->GetYMinimum(), + (int)plot1d->GetYMaximum()); if (isGainDataExtracted) { - gainplot1d->EnableRoiBox( - std::array{rxRoi.xmin, rxRoi.xmax, 0, 3}); + gainplot1d->EnableRoiBoxes(roi, 0, 3); } lblRxRoiEnabled->show(); } } // ymin and ymax could change (so replot roi every time) - if (!rxRoi.completeRoi()) { - plot1d->EnableRoiBox(std::array{rxRoi.xmin, rxRoi.xmax, - (int)plot1d->GetYMinimum(), - (int)plot1d->GetYMaximum()}); + if (hasRoi) { + plot1d->EnableRoiBoxes(roi, (int)plot1d->GetYMinimum(), + (int)plot1d->GetYMaximum()); } } @@ -1025,18 +1032,18 @@ void qDrawPlot::Update2dPlot() { } plot2d->DisableZoom(disableZoom); plot2d->SetZRange(isZRange[0], isZRange[1], zRange[0], zRange[1]); - if (!isRxRoiDisplayed) { - isRxRoiDisplayed = true; - if (rxRoi.completeRoi()) { - plot2d->DisableRoiBox(); + if (!roiDisplayInitialized) { + roiDisplayInitialized = true; + if (!hasRoi) { + plot2d->DisableRoiBoxes(); if (isGainDataExtracted) { - gainplot2d->DisableRoiBox(); + gainplot2d->DisableRoiBoxes(); } lblRxRoiEnabled->hide(); } else { - plot2d->EnableRoiBox(rxRoi.getIntArray()); + plot2d->EnableRoiBoxes(roi); if (isGainDataExtracted) { - gainplot2d->EnableRoiBox(rxRoi.getIntArray()); + gainplot2d->EnableRoiBoxes(roi); } lblRxRoiEnabled->show(); } diff --git a/slsDetectorSoftware/generator/Caller.in.h b/slsDetectorSoftware/generator/Caller.in.h index f1a3a2821..715922dfb 100644 --- a/slsDetectorSoftware/generator/Caller.in.h +++ b/slsDetectorSoftware/generator/Caller.in.h @@ -21,6 +21,8 @@ class Caller { UdpDestination getUdpEntry(); int GetLevelAndInsertIntoArgs(std::string levelSeparatedCommand); void WrongNumberOfParameters(size_t expected); + std::vector parseRoiVector(const std::string &input); + defs::ROI parseRoi(const std::vector &args); template std::string OutStringHex(const V &value) { if (value.equal()) diff --git a/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh b/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh index 3ce858ee8..5fe099a7d 100644 --- a/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh +++ b/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh @@ -2193,20 +2193,6 @@ return 0 } __rx_roi() { FCN_RETURN="" -if [[ ${IS_GET} -eq 0 ]]; then -if [[ "${cword}" == "2" ]]; then -FCN_RETURN="" -fi -if [[ "${cword}" == "3" ]]; then -FCN_RETURN="" -fi -if [[ "${cword}" == "4" ]]; then -FCN_RETURN="" -fi -if [[ "${cword}" == "5" ]]; then -FCN_RETURN="" -fi -fi return 0 } __rx_silent() { diff --git a/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh b/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh index c59c1e4a3..64598dd2b 100644 --- a/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh +++ b/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh @@ -2117,20 +2117,6 @@ return 0 } __rx_roi() { FCN_RETURN="" -if [[ ${IS_GET} -eq 0 ]]; then -if [[ "${cword}" == "2" ]]; then -FCN_RETURN="" -fi -if [[ "${cword}" == "3" ]]; then -FCN_RETURN="" -fi -if [[ "${cword}" == "4" ]]; then -FCN_RETURN="" -fi -if [[ "${cword}" == "5" ]]; then -FCN_RETURN="" -fi -fi return 0 } __rx_silent() { diff --git a/slsDetectorSoftware/generator/commands.yaml b/slsDetectorSoftware/generator/commands.yaml index f33cf7be2..1ae493055 100644 --- a/slsDetectorSoftware/generator/commands.yaml +++ b/slsDetectorSoftware/generator/commands.yaml @@ -2598,11 +2598,7 @@ rx_roi: GET: argc: 0 PUT: - args: - - argc: 2 - arg_types: [ int, int ] - - argc: 4 - arg_types: [ int, int, int, int ] + argc: -1 ratecorr: is_description: true diff --git a/slsDetectorSoftware/generator/extended_commands.yaml b/slsDetectorSoftware/generator/extended_commands.yaml index 6b9f72577..3a698f537 100644 --- a/slsDetectorSoftware/generator/extended_commands.yaml +++ b/slsDetectorSoftware/generator/extended_commands.yaml @@ -8831,25 +8831,8 @@ rx_roi: store_result_in_t: true PUT: args: - - arg_types: - - int - - int - argc: 2 - cast_input: [] - check_det_id: false - convert_det_id: true - function: '' - input: [] - input_types: [] - output: [] - require_det_id: false - store_result_in_t: false - - arg_types: - - int - - int - - int - - int - argc: 4 + - arg_types: [] + argc: -1 cast_input: [] check_det_id: false convert_det_id: true diff --git a/slsDetectorSoftware/include/sls/Detector.h b/slsDetectorSoftware/include/sls/Detector.h index eb679a578..743c80b77 100644 --- a/slsDetectorSoftware/include/sls/Detector.h +++ b/slsDetectorSoftware/include/sls/Detector.h @@ -119,6 +119,10 @@ class Detector { Result getModuleSize(Positions pos = {}) const; + defs::xy getPortPerModuleGeometry() const; + + Result getPortSize(Positions pos = {}) const; + /** Gets the actual full detector size. It is the same even if ROI changes */ defs::xy getDetectorSize() const; @@ -710,7 +714,7 @@ class Detector { * restarts client and receiver zmq sockets if zmq streaming enabled. \n * [Gotthard2] second interface enabled to send veto information via 10Gbps * for debugging. By default, if veto enabled, it is sent via 2.5 gbps - * interface. */ + * interface. \nSetting this resets the receiver roi */ void setNumberofUDPInterfaces(int n, Positions pos = {}); /** [Jungfrau][Moench] */ @@ -985,13 +989,14 @@ class Detector { * every minute. Useful in 10G mode. */ void setRxArping(bool value, Positions pos = {}); - /** at module level */ - Result getIndividualRxROIs(Positions pos) const; + /** If module_id is -1, returns multi level ROIs. Else it returns port + * level ROIs. Max 2 ports and hence max 2 elements per readout */ + std::vector getRxROI(int module_id = -1) const; - defs::ROI getRxROI() const; - - /** only at multi module level without gap pixels */ - void setRxROI(const defs::ROI value); + /** only at multi module level without gap pixels. If more than 1 ROI per + * UDP port, it will throw. Setting number of udp interfaces will clear the + * roi. Cannot be set for CTB or Xilinx CTB */ + void setRxROI(const std::vector &args); void clearRxROI(); diff --git a/slsDetectorSoftware/include/sls/detectorData.h b/slsDetectorSoftware/include/sls/detectorData.h index 33f983a66..c55906482 100644 --- a/slsDetectorSoftware/include/sls/detectorData.h +++ b/slsDetectorSoftware/include/sls/detectorData.h @@ -19,14 +19,6 @@ class detectorData { databytes(databytes), dynamicRange(dynamicRange), completeImage(completeImage){}; - detectorData(double progressIndex, std::string fileName, int nx, int ny, - char *data, int databytes, int dynamicRange, - uint64_t fileIndex, bool completeImage, - std::array rxRoi) - : progressIndex(progressIndex), fileName(fileName), - fileIndex(fileIndex), nx(nx), ny(ny), data(data), - databytes(databytes), dynamicRange(dynamicRange), - completeImage(completeImage), rxRoi(rxRoi){}; /** * data has to be deleted by caller */ @@ -62,7 +54,6 @@ class detectorData { int databytes; int dynamicRange; bool completeImage; - std::array rxRoi{{-1, -1, -1, -1}}; }; } // namespace sls diff --git a/slsDetectorSoftware/src/Caller.h b/slsDetectorSoftware/src/Caller.h index 3e1be13ac..068aca94c 100644 --- a/slsDetectorSoftware/src/Caller.h +++ b/slsDetectorSoftware/src/Caller.h @@ -21,6 +21,8 @@ class Caller { UdpDestination getUdpEntry(); int GetLevelAndInsertIntoArgs(std::string levelSeparatedCommand); void WrongNumberOfParameters(size_t expected); + std::vector parseRoiVector(const std::string &input); + defs::ROI parseRoi(const std::vector &args); template std::string OutStringHex(const V &value) { if (value.equal()) diff --git a/slsDetectorSoftware/src/CallerSpecial.cpp b/slsDetectorSoftware/src/CallerSpecial.cpp index bdea93207..a4ddf69ae 100644 --- a/slsDetectorSoftware/src/CallerSpecial.cpp +++ b/slsDetectorSoftware/src/CallerSpecial.cpp @@ -719,49 +719,133 @@ std::string Caller::rx_zmqip(int action) { } return os.str(); } + std::string Caller::rx_roi(int action) { std::ostringstream os; + std::string helpMessage = + std::string("[xmin] [xmax] [ymin] [ymax]\n") + + "\tDefines a single region of interest (ROI) in the receiver.\n" + "\tFor example, to set a single ROI: 0 100 20 30\n\n" + + "\tTo specify multiple ROIs, use square brackets between ROIs and " + "commas inside for each ROI. \n" + "\tInside each bracket, no spaces allowed.\n\n" + + "\tIf you use semicolon (along with '['and ']' to separate rois), \n" + "\tenclose the entire list in quotes.\n" + "\tExamples:\n" + "\t [0,100,0,100] [200,300,0,100]\n" + "\t \"[0,100,0,100];[200,300,0,100]\"\n\n" + + "\tNotes:\n" + "\t- ROIs can only be set at the multi-module level.\n" + "\t- If multi module ROIs ends up with more then 1 ROI per UDP " + "port or if they overlap each other, it will throw an error.\n" + "\t- ROIs coordinates assume no gap pixels, even if they are enabled " + "in gui.\n" + "\t- To retrieve ROIs per port, specify the module ID when using the " + "get command.\n" + "\t- Use the command 'rx_clearroi' to clear all ROIs.\n" + "\t- Changing the number of UDP interfaces will automatically clear " + "the current ROIs.\n\n" + "\t- Cannot be set for CTB or Xilinx CTB.\n"; + if (action == defs::HELP_ACTION) { - os << "[xmin] [xmax] [ymin] [ymax]\n\tRegion of interest in " - "receiver.\n\tOnly allowed at multi module level and without gap " - "pixels." - << '\n'; + os << helpMessage; } else if (action == defs::GET_ACTION) { if (!args.empty()) { WrongNumberOfParameters(0); } - if (det_id == -1) { - auto t = det->getRxROI(); - os << t << '\n'; - } else { - auto t = det->getIndividualRxROIs(std::vector{det_id}); - os << t << '\n'; - } + auto t = det->getRxROI(det_id); + os << ToString(t) << '\n'; } else if (action == defs::PUT_ACTION) { - defs::ROI t; - // 2 or 4 arguments - if (args.size() != 2 && args.size() != 4) { - WrongNumberOfParameters(2); - } - if (args.size() == 2 || args.size() == 4) { - t.xmin = StringTo(args[0]); - t.xmax = StringTo(args[1]); - } - if (args.size() == 4) { - t.ymin = StringTo(args[2]); - t.ymax = StringTo(args[3]); - } - // only multi level if (det_id != -1) { - throw RuntimeError("Cannot execute receiver ROI at module level"); + throw RuntimeError("Cannot set receiver ROI at module level"); } - det->setRxROI(t); - os << t << '\n'; + // Support multiple args with bracketed ROIs, or single arg with + // semicolon-separated vector in quotes + bool isVectorInput = + std::all_of(args.begin(), args.end(), [](const std::string &a) { + return a.find('[') != std::string::npos && + a.find(']') != std::string::npos; + }); + std::vector rois; + try { + // single roi in previous format: [xmin,xmax,ymin,ymax] + if (!isVectorInput) { + auto t = parseRoi(args); + rois.emplace_back(t); + } + // multiple roi or single roi with brackets + // multiple roi: multiple args with bracketed ROIs, or single arg + // with semicolon-bracketed Rois in quotes + else { + for (const auto &arg : args) { + auto subRois = parseRoiVector(arg); + rois.insert(rois.end(), subRois.begin(), subRois.end()); + } + } + } catch (const std::exception &e) { + throw RuntimeError("Could not parse ROI: Did you use spaces inside " + "the brackets? Use sls_detector_help " + + cmd + " to get the right syntax expected."); + } + + det->setRxROI(rois); + os << ToString(rois) << '\n'; } else { throw RuntimeError("Unknown action"); } return os.str(); } + +std::vector Caller::parseRoiVector(const std::string &input) { + std::vector rois; + std::stringstream ss(input); + std::string token; + + while (std::getline(ss, token, ']')) { + // remove spaces and semicolons + token.erase( + std::remove_if(token.begin(), token.end(), + [](char c) { return std::isspace(c) || c == ';'; }), + token.end()); + if (token.empty()) + continue; + if (token.front() != '[') { + throw RuntimeError("Each ROI must be enclosed in square brackets: " + "[xmin,xmax,ymin,ymax]"); + } + token = token.substr(1, token.size() - 1); // remove brackets + std::vector parts; + std::stringstream inner(token); + std::string num; + while (std::getline(inner, num, ',')) { + parts.push_back(num); + } + + auto roi = parseRoi(parts); + rois.emplace_back(roi); + } + return rois; +} + +defs::ROI Caller::parseRoi(const std::vector &parts) { + if (parts.size() != 2 && parts.size() != 4) { + throw RuntimeError( + "Could not parse ROI. A ROI must have 2 or 4 integers"); + } + + defs::ROI roi; + roi.xmin = StringTo(parts[0]); + roi.xmax = StringTo(parts[1]); + if (parts.size() == 4) { + roi.ymin = StringTo(parts[2]); + roi.ymax = StringTo(parts[3]); + } + return roi; +} + std::string Caller::ratecorr(int action) { std::ostringstream os; if (action == defs::HELP_ACTION) { diff --git a/slsDetectorSoftware/src/Detector.cpp b/slsDetectorSoftware/src/Detector.cpp index f84e5fbb3..8c95fbf63 100644 --- a/slsDetectorSoftware/src/Detector.cpp +++ b/slsDetectorSoftware/src/Detector.cpp @@ -201,6 +201,26 @@ Result Detector::getModuleSize(Positions pos) const { return pimpl->Parallel(&Module::getNumberOfChannels, pos); } +defs::xy Detector::getPortPerModuleGeometry() const { + return pimpl->getPortGeometry(); +} + +Result Detector::getPortSize(Positions pos) const { + Result res = pimpl->Parallel(&Module::getNumberOfChannels, pos); + defs::xy portGeometry = getPortPerModuleGeometry(); + if ((portGeometry.x != 1 && portGeometry.x != 2) || + (portGeometry.y != 1 && portGeometry.y != 2)) { + throw RuntimeError( + "Port size is not 1 or 2 in either dimension. Port geometry:" + + ToString(portGeometry)); + } + for (auto &it : res) { + it.x /= portGeometry.x; + it.y /= portGeometry.y; + } + return res; +} + defs::xy Detector::getDetectorSize() const { return pimpl->getNumberOfChannels(); } @@ -1367,13 +1387,13 @@ void Detector::setRxArping(bool value, Positions pos) { pimpl->Parallel(&Module::setRxArping, pos, value); } -Result Detector::getIndividualRxROIs(Positions pos) const { - return pimpl->Parallel(&Module::getRxROI, pos); +std::vector Detector::getRxROI(int module_id) const { + return pimpl->getRxROI(module_id); } -defs::ROI Detector::getRxROI() const { return pimpl->getRxROI(); } - -void Detector::setRxROI(const defs::ROI value) { pimpl->setRxROI(value); } +void Detector::setRxROI(const std::vector &args) { + pimpl->setRxROI(args); +} void Detector::clearRxROI() { pimpl->clearRxROI(); } diff --git a/slsDetectorSoftware/src/DetectorImpl.cpp b/slsDetectorSoftware/src/DetectorImpl.cpp index 3e3efb8b5..d51c3d802 100644 --- a/slsDetectorSoftware/src/DetectorImpl.cpp +++ b/slsDetectorSoftware/src/DetectorImpl.cpp @@ -130,10 +130,6 @@ void DetectorImpl::initializeDetectorStructure() { shm()->gapPixels = false; // zmqlib default shm()->zmqHwm = -1; - shm()->rx_roi.xmin = -1; - shm()->rx_roi.xmax = -1; - shm()->rx_roi.ymin = -1; - shm()->rx_roi.ymax = -1; } void DetectorImpl::initializeMembers(bool verify) { @@ -539,7 +535,6 @@ void DetectorImpl::readFrameFromReceiver() { bool quadEnable = false; // to flip image bool eiger = false; - std::array rxRoi = shm()->rx_roi.getIntArray(); std::vector runningList(zmqSocket.size()); std::vector connectList(zmqSocket.size()); @@ -736,7 +731,7 @@ void DetectorImpl::readFrameFromReceiver() { thisData = new detectorData(currentProgress, currentFileName, nDetActualPixelsX, nDetActualPixelsY, callbackImage, imagesize, dynamicRange, - currentFileIndex, completeImage, rxRoi); + currentFileIndex, completeImage); try { dataReady( thisData, currentFrameIndex, @@ -1548,31 +1543,6 @@ void DetectorImpl::setDefaultDac(defs::dacIndex index, int defaultValue, Parallel(&Module::setDefaultDac, pos, index, defaultValue, sett); } -defs::xy DetectorImpl::getPortGeometry() const { - defs::xy portGeometry(1, 1); - switch (shm()->detType) { - case EIGER: - portGeometry.x = modules[0]->getNumberofUDPInterfacesFromShm(); - break; - case JUNGFRAU: - case MOENCH: - portGeometry.y = modules[0]->getNumberofUDPInterfacesFromShm(); - break; - default: - break; - } - return portGeometry; -} - -defs::xy DetectorImpl::calculatePosition(int moduleIndex, - defs::xy geometry) const { - defs::xy pos{}; - int maxYMods = shm()->numberOfModules.y; - pos.y = (moduleIndex % maxYMods) * geometry.y; - pos.x = (moduleIndex / maxYMods) * geometry.x; - return pos; -} - void DetectorImpl::verifyUniqueDetHost(const uint16_t port, std::vector positions) const { // port for given positions @@ -1696,7 +1666,7 @@ void DetectorImpl::verifyUniqueHost( } } -defs::ROI DetectorImpl::getRxROI() const { +std::vector DetectorImpl::getRxROI(int module_id) const { if (shm()->detType == CHIPTESTBOARD || shm()->detType == defs::XILINX_CHIPTESTBOARD) { throw RuntimeError("RxRoi not implemented for this Detector"); @@ -1704,75 +1674,164 @@ defs::ROI DetectorImpl::getRxROI() const { if (modules.size() == 0) { throw RuntimeError("No Modules added"); } - // complete detector in roi - auto t = Parallel(&Module::getRxROI, {}); - if (t.equal() && t.front().completeRoi()) { - LOG(logDEBUG) << "no roi"; - return defs::ROI(0, shm()->numberOfChannels.x - 1, 0, - shm()->numberOfChannels.y - 1); + if (module_id >= (int)modules.size()) { + throw RuntimeError("Invalid module id: " + std::to_string(module_id)); + } + if (module_id >= 0) { + return modules[module_id]->getRxROI(); } - defs::xy numChansPerMod = modules[0]->getNumberOfChannels(); - bool is2D = (numChansPerMod.y > 1 ? true : false); - defs::xy geometry = getPortGeometry(); - - defs::ROI retval{}; - for (size_t iModule = 0; iModule != modules.size(); ++iModule) { - - defs::ROI moduleRoi = modules[iModule]->getRxROI(); - if (moduleRoi.noRoi()) { - LOG(logDEBUG) << iModule << ": no roi"; - } else { - // expand complete roi - if (moduleRoi.completeRoi()) { - moduleRoi.xmin = 0; - moduleRoi.xmax = numChansPerMod.x; - if (is2D) { - moduleRoi.ymin = 0; - moduleRoi.ymax = numChansPerMod.y; - } - } - LOG(logDEBUG) << iModule << ": " << moduleRoi; - - // get roi at detector level - defs::xy pos = calculatePosition(iModule, geometry); - defs::ROI moduleFullRoi{}; - moduleFullRoi.xmin = numChansPerMod.x * pos.x + moduleRoi.xmin; - moduleFullRoi.xmax = numChansPerMod.x * pos.x + moduleRoi.xmax; - if (is2D) { - moduleFullRoi.ymin = numChansPerMod.y * pos.y + moduleRoi.ymin; - moduleFullRoi.ymax = numChansPerMod.y * pos.y + moduleRoi.ymax; - } - LOG(logDEBUG) << iModule << ": (full roi)" << moduleFullRoi; - - // get min and max - if (retval.xmin == -1 || moduleFullRoi.xmin < retval.xmin) { - LOG(logDEBUG) << iModule << ": xmin updated"; - retval.xmin = moduleFullRoi.xmin; - } - if (retval.xmax == -1 || moduleFullRoi.xmax > retval.xmax) { - LOG(logDEBUG) << iModule << ": xmax updated"; - retval.xmax = moduleFullRoi.xmax; - } - if (retval.ymin == -1 || moduleFullRoi.ymin < retval.ymin) { - LOG(logDEBUG) << iModule << ": ymin updated"; - retval.ymin = moduleFullRoi.ymin; - } - if (retval.ymax == -1 || moduleFullRoi.ymax > retval.ymax) { - LOG(logDEBUG) << iModule << ": ymax updated"; - retval.ymax = moduleFullRoi.ymax; - } - } - LOG(logDEBUG) << iModule << ": (retval): " << retval; - } - if (retval.ymin == -1) { - retval.ymin = 0; - retval.ymax = 0; - } - return retval; + return modules[0]->getRxROIMetadata(); } -void DetectorImpl::setRxROI(const defs::ROI arg) { +void DetectorImpl::validateROIs(const std::vector &rois) { + for (size_t i = 0; i < rois.size(); ++i) { + const auto &roi = rois[i]; + + if (roi.noRoi()) { + throw RuntimeError("Invalid Roi of size 0. Roi: " + ToString(roi)); + } + bool is2D = (modules[0]->getNumberOfChannels().y > 1 ? true : false); + if (roi.completeRoi()) { + std::ostringstream oss; + oss << "Did you mean the clear roi command (API: clearRxROI, cmd: " + "rx_clearroi) Roi: [ -1, -1 "; + oss << (is2D ? ", -1, -1 ]?" : "]?"); + throw RuntimeError(oss.str()); + } + if (roi.xmin > roi.xmax || roi.ymin > roi.ymax) { + throw RuntimeError( + "Invalid Roi. xmin/ymin exceeds xmax/ymax. Roi: " + + ToString(roi)); + } + + if (roi.xmin < 0 || roi.xmax >= shm()->numberOfChannels.x) { + throw RuntimeError( + "ROI x-dimension outside detector bounds. Roi: " + + ToString(roi)); + } + + if (is2D) { + if (roi.ymin < 0 || roi.ymax >= shm()->numberOfChannels.y) { + throw RuntimeError( + "ROI y-dimension outside detector bounds. Roi: " + + ToString(roi)); + } + } else { + if ((roi.ymin != -1 && roi.ymin != 0) || + (roi.ymax != -1 && roi.ymax != 0)) { + throw RuntimeError( + "Invalid Y range for 1D detector: should be -1. Roi: " + + ToString(roi)); + } + } + + for (size_t j = i + 1; j < rois.size(); ++j) { + if (rois[i].overlap(rois[j])) { + throw RuntimeError("Invalid Overlapping Rois."); + } + } + } +} + +defs::xy DetectorImpl::getPortGeometry() const { + defs::xy portGeometry(1, 1); + switch (shm()->detType) { + case EIGER: + portGeometry.x = modules[0]->getNumberofUDPInterfacesFromShm(); + break; + case JUNGFRAU: + case MOENCH: + portGeometry.y = modules[0]->getNumberofUDPInterfacesFromShm(); + break; + case GOTTHARD2: // 2nd port if used is for veto, not data + default: + break; + } + return portGeometry; +} + +defs::xy DetectorImpl::calculatePosition(int moduleIndex) const { + int maxYMods = shm()->numberOfModules.y; + int y = (moduleIndex % maxYMods); + int x = (moduleIndex / maxYMods); + return defs::xy{x, y}; +} + +defs::ROI DetectorImpl::getModuleROI(int moduleIndex) const { + const defs::xy modSize = modules[0]->getNumberOfChannels(); + // calculate module position (not taking into account port geometry) + const defs::xy modPos = calculatePosition(moduleIndex); + const int xmin = modSize.x * modPos.x; + const int xmax = xmin + modSize.x - 1; + int ymin = -1, ymax = -1; + if (modSize.y > 1) { + ymin = modSize.y * modPos.y; + ymax = ymin + modSize.y - 1; + } + return defs::ROI{xmin, xmax, ymin, ymax}; +} + +void DetectorImpl::convertGlobalRoiToPortLevel( + const defs::ROI &userRoi, const defs::ROI &moduleRoi, + std::vector &portRois) const { + const defs::xy modSize = modules[0]->getNumberOfChannels(); + const defs::xy portGeometry = getPortGeometry(); + const int numPortsPerModule = portGeometry.x * portGeometry.y; + + if (numPortsPerModule > 2) { + throw RuntimeError("Only up to 2 ports per module supported."); + } + if (numPortsPerModule != (int)portRois.size()) { + throw RuntimeError("Number of port ROIs does not match number of ports " + "in module. Expected: " + + std::to_string(numPortsPerModule) + + ", got: " + std::to_string(portRois.size())); + } + + for (int port = 0; port != numPortsPerModule; ++port) { + defs::ROI portRoi = moduleRoi; + // Recalculate port ROI boundaries (split vertically or horizontally) + if (portGeometry.x == 2) { + int midX = (moduleRoi.xmin + moduleRoi.xmax) / 2; + if (port == 0) + portRoi.xmax = midX; + else + portRoi.xmin = midX + 1; + } else if (portGeometry.y == 2) { + int midY = (moduleRoi.ymin + moduleRoi.ymax) / 2; + if (port == 0) + portRoi.ymax = midY; + else + portRoi.ymin = midY + 1; + } + + // find overlapped roi (port vs user roi) + if (userRoi.overlap(portRoi)) { + defs::ROI clipped{}; + // Clip user ROI to port ROI + clipped.xmin = std::max(userRoi.xmin, portRoi.xmin) - portRoi.xmin; + clipped.xmax = std::min(userRoi.xmax, portRoi.xmax) - portRoi.xmin; + if (modSize.y > 1) { + clipped.ymin = + std::max(userRoi.ymin, portRoi.ymin) - portRoi.ymin; + clipped.ymax = + std::min(userRoi.ymax, portRoi.ymax) - portRoi.ymin; + } + + // Check if port ROI already exists for this port (from another user + // roi) + if (!portRois[port].completeRoi() && !portRois[port].noRoi()) { + throw RuntimeError( + "Multiple ROIs specified for the same port " + + std::to_string(port) + " with ROI: " + ToString(userRoi)); + } + portRois[port] = clipped; + } + } +} + +void DetectorImpl::setRxROI(const std::vector &args) { if (shm()->detType == CHIPTESTBOARD || shm()->detType == defs::XILINX_CHIPTESTBOARD) { throw RuntimeError("RxRoi not implemented for this Detector"); @@ -1780,118 +1839,41 @@ void DetectorImpl::setRxROI(const defs::ROI arg) { if (modules.size() == 0) { throw RuntimeError("No Modules added"); } - if (arg.noRoi()) { - throw RuntimeError("Invalid Roi of size 0."); - } - if (arg.completeRoi()) { - throw RuntimeError("Did you mean the clear roi command (API: " - "clearRxROI, cmd: rx_clearroi)?"); - } - if (arg.xmin > arg.xmax || arg.ymin > arg.ymax) { - throw RuntimeError( - "Invalid Receiver Roi. xmin/ymin exceeds xmax/ymax."); + + if (args.empty()) { + return clearRxROI(); } - defs::xy numChansPerMod = modules[0]->getNumberOfChannels(); - bool is2D = (numChansPerMod.y > 1 ? true : false); - defs::xy geometry = getPortGeometry(); + validateROIs(args); + int nPortsPerModule = + Parallel(&Module::getNumberofUDPInterfacesFromShm, {}) + .tsquash("Inconsistent number of udp ports set up per module"); - if (!is2D && ((arg.ymin != -1 && arg.ymin != 0) || - (arg.ymax != -1 && arg.ymax != 0))) { - throw RuntimeError( - "Invalid Receiver roi. Cannot set 2d roi for a 1d detector."); - } + for (size_t iModule = 0; iModule < modules.size(); ++iModule) { + auto moduleGlobalRoi = getModuleROI(iModule); + // at most 2 rois per module (for each port) + std::vector portRois(nPortsPerModule); - if (arg.xmin < 0 || arg.xmax >= shm()->numberOfChannels.x || - (is2D && (arg.ymin < 0 || arg.ymax >= shm()->numberOfChannels.y))) { - throw RuntimeError("Invalid Receiver Roi. Outside detector range."); - } - - for (size_t iModule = 0; iModule != modules.size(); ++iModule) { - // default init = complete roi - defs::ROI moduleRoi{}; - - // incomplete roi - if (!arg.completeRoi()) { - // multi module Gotthard2 - if (shm()->detType == GOTTHARD2 && size() > 1) { - moduleRoi.xmin = arg.xmin / 2; - moduleRoi.xmax = arg.xmax / 2; - if (iModule == 0) { - // all should be even - if (arg.xmin % 2 != 0) { - ++moduleRoi.xmin; - } - } else if (iModule == 1) { - // all should be odd - if (arg.xmax % 2 == 0) { - --moduleRoi.xmax; - } - } else { - throw RuntimeError("Cannot have more than 2 modules for a " - "Gotthard2 detector"); - } - } else { - // get module limits - defs::xy pos = calculatePosition(iModule, geometry); - defs::ROI moduleFullRoi{}; - moduleFullRoi.xmin = numChansPerMod.x * pos.x; - moduleFullRoi.xmax = numChansPerMod.x * (pos.x + 1) - 1; - if (is2D) { - moduleFullRoi.ymin = numChansPerMod.y * pos.y; - moduleFullRoi.ymax = numChansPerMod.y * (pos.y + 1) - 1; - } - - // no roi - if (arg.xmin > moduleFullRoi.xmax || - arg.xmax < moduleFullRoi.xmin || - (is2D && (arg.ymin > moduleFullRoi.ymax || - arg.ymax < moduleFullRoi.ymin))) { - moduleRoi.setNoRoi(); - } - // incomplete module roi - else if (arg.xmin > moduleFullRoi.xmin || - arg.xmax < moduleFullRoi.xmax || - (is2D && (arg.ymin > moduleFullRoi.ymin || - arg.ymax < moduleFullRoi.ymax))) { - moduleRoi.xmin = (arg.xmin <= moduleFullRoi.xmin) - ? 0 - : (arg.xmin % numChansPerMod.x); - moduleRoi.xmax = (arg.xmax >= moduleFullRoi.xmax) - ? numChansPerMod.x - 1 - : (arg.xmax % numChansPerMod.x); - if (is2D) { - moduleRoi.ymin = (arg.ymin <= moduleFullRoi.ymin) - ? 0 - : (arg.ymin % numChansPerMod.y); - moduleRoi.ymax = (arg.ymax >= moduleFullRoi.ymax) - ? numChansPerMod.y - 1 - : (arg.ymax % numChansPerMod.y); - } - } + // check overlap with module + for (const auto &arg : args) { + if (arg.overlap(moduleGlobalRoi)) { + convertGlobalRoiToPortLevel(arg, moduleGlobalRoi, portRois); } } - modules[iModule]->setRxROI(moduleRoi); + modules[iModule]->setRxROI(portRois); } - // updating shm rx_roi for gui purposes - shm()->rx_roi = arg; - // metadata - if (arg.completeRoi()) { - modules[0]->setRxROIMetadata(defs::ROI(0, shm()->numberOfChannels.x - 1, - 0, - shm()->numberOfChannels.y - 1)); - } else { - modules[0]->setRxROIMetadata(arg); - } + modules[0]->setRxROIMetadata(args); } void DetectorImpl::clearRxROI() { - Parallel(&Module::setRxROI, {}, defs::ROI{}); - shm()->rx_roi.xmin = -1; - shm()->rx_roi.ymin = -1; - shm()->rx_roi.xmax = -1; - shm()->rx_roi.ymax = -1; + int nPortsPerModule = + Parallel(&Module::getNumberofUDPInterfacesFromShm, {}) + .tsquash("Inconsistent number of udp ports set up per module"); + for (size_t iModule = 0; iModule < modules.size(); ++iModule) { + modules[iModule]->setRxROI(std::vector(nPortsPerModule)); + } + modules[0]->setRxROIMetadata(std::vector(1)); } void DetectorImpl::getBadChannels(const std::string &fname, diff --git a/slsDetectorSoftware/src/DetectorImpl.h b/slsDetectorSoftware/src/DetectorImpl.h index da56e5a22..5b2789e0b 100644 --- a/slsDetectorSoftware/src/DetectorImpl.h +++ b/slsDetectorSoftware/src/DetectorImpl.h @@ -24,7 +24,7 @@ class detectorData; class Module; #define DETECTOR_SHMAPIVERSION 0x190809 -#define DETECTOR_SHMVERSION 0x220505 +#define DETECTOR_SHMVERSION 0x250616 #define SHORT_STRING_LENGTH 50 /** @@ -65,8 +65,6 @@ struct sharedDetector { bool gapPixels; /** high water mark of listening tcp port (only data) */ int zmqHwm; - /** in shm for gui purposes */ - defs::ROI rx_roi{}; }; class DetectorImpl : public virtual slsDetectorDefs { @@ -303,8 +301,9 @@ class DetectorImpl : public virtual slsDetectorDefs { std::vector> verifyUniqueRxHost(const std::vector &names) const; - defs::ROI getRxROI() const; - void setRxROI(const defs::ROI arg); + defs::xy getPortGeometry() const; + std::vector getRxROI(int module_id = -1) const; + void setRxROI(const std::vector &args); void clearRxROI(); void getBadChannels(const std::string &fname, Positions pos) const; @@ -422,12 +421,17 @@ class DetectorImpl : public virtual slsDetectorDefs { */ int kbhit(); - defs::xy getPortGeometry() const; - defs::xy calculatePosition(int moduleIndex, defs::xy geometry) const; - void verifyUniqueHost( bool isDet, std::vector> &hosts) const; + bool roisOverlap(const defs::ROI &a, const defs::ROI &b) const; + void validateROIs(const std::vector &rois); + defs::xy calculatePosition(int moduleIndex) const; + defs::ROI getModuleROI(int moduleIndex) const; + void convertGlobalRoiToPortLevel(const defs::ROI &userRoi, + const defs::ROI &moduleRoi, + std::vector &portRois) const; + const int detectorIndex{0}; SharedMemory shm{0, -1}; SharedMemory ctb_shm{0, -1, CtbConfig::shm_tag()}; diff --git a/slsDetectorSoftware/src/Module.cpp b/slsDetectorSoftware/src/Module.cpp index d1356c30f..440386080 100644 --- a/slsDetectorSoftware/src/Module.cpp +++ b/slsDetectorSoftware/src/Module.cpp @@ -1521,17 +1521,94 @@ void Module::setRxArping(bool enable) { sendToReceiver(F_SET_RECEIVER_ARPING, static_cast(enable), nullptr); } -defs::ROI Module::getRxROI() const { - return sendToReceiver(F_RECEIVER_GET_RECEIVER_ROI); +std::vector Module::getRxROI() const { + LOG(logDEBUG1) << "Getting receiver ROI for Module " << moduleIndex; + // check number of ports + if (!shm()->useReceiverFlag) { + throw RuntimeError("No receiver to get ROI."); + } + auto client = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort); + client.Send(F_RECEIVER_GET_RECEIVER_ROI); + client.setFnum(F_RECEIVER_GET_RECEIVER_ROI); + auto nPorts = client.Receive(); + std::vector retval(nPorts); + if (nPorts > 0) + client.Receive(retval); + if (nPorts != shm()->numUDPInterfaces) { + throw RuntimeError( + "Invalid number of rois: " + std::to_string(nPorts) + + ". Expected: " + std::to_string(shm()->numUDPInterfaces)); + } + LOG(logDEBUG1) << "ROI of Receiver" << moduleIndex << ": " + << ToString(retval); + return retval; } -void Module::setRxROI(const slsDetectorDefs::ROI arg) { - LOG(logDEBUG) << moduleIndex << ": " << arg; - sendToReceiver(F_RECEIVER_SET_RECEIVER_ROI, arg, nullptr); +void Module::setRxROI(const std::vector &portRois) { + LOG(logDEBUG) << "Sending to receiver " << moduleIndex + << " [roi: " << ToString(portRois) << ']'; + if (!shm()->useReceiverFlag) { + throw RuntimeError("No receiver to set ROI."); + } + if ((int)portRois.size() != shm()->numUDPInterfaces) { + throw RuntimeError( + "Invalid number of ROIs: " + std::to_string(portRois.size()) + + ". Expected: " + std::to_string(shm()->numUDPInterfaces)); + } + // check number of ports + auto client = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort); + client.Send(F_RECEIVER_SET_RECEIVER_ROI); + client.setFnum(F_RECEIVER_SET_RECEIVER_ROI); + int size = static_cast(portRois.size()); + client.Send(size); + if (size > 0) + client.Send(portRois); + if (client.Receive() == FAIL) { + throw ReceiverError("Receiver " + std::to_string(moduleIndex) + + " returned error: " + client.readErrorMessage()); + } } -void Module::setRxROIMetadata(const slsDetectorDefs::ROI arg) { - sendToReceiver(F_RECEIVER_SET_RECEIVER_ROI_METADATA, arg, nullptr); +std::vector Module::getRxROIMetadata() const { + LOG(logDEBUG1) << "Getting receiver ROI metadata for Module " + << moduleIndex; + // check number of ports + if (!shm()->useReceiverFlag) { + throw RuntimeError("No receiver to get ROI metadata."); + } + auto client = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort); + client.Send(F_RECEIVER_GET_ROI_METADATA); + client.setFnum(F_RECEIVER_GET_ROI_METADATA); + auto size = client.Receive(); + std::vector retval(size); + if (size > 0) + client.Receive(retval); + if (size == 0) { + throw RuntimeError("Invalid number of ROI metadata: " + + std::to_string(size) + ". Min: 1."); + } + LOG(logDEBUG1) << "ROI metadata of Receiver: " << ToString(retval); + return retval; +} + +void Module::setRxROIMetadata(const std::vector &args) { + LOG(logDEBUG) << "Sending to receiver " << moduleIndex + << " [roi metadata: " << ToString(args) << ']'; + auto receiver = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort); + receiver.Send(F_RECEIVER_SET_RECEIVER_ROI_METADATA); + receiver.setFnum(F_RECEIVER_SET_RECEIVER_ROI_METADATA); + int size = static_cast(args.size()); + receiver.Send(size); + if (size > 0) + receiver.Send(args); + if (size < 1) { + throw RuntimeError("Invalid number of ROI metadata: " + + std::to_string(size) + ". Min: 1."); + } + if (receiver.Receive() == FAIL) { + throw ReceiverError("Receiver " + std::to_string(moduleIndex) + + " returned error: " + receiver.readErrorMessage()); + } } // File diff --git a/slsDetectorSoftware/src/Module.h b/slsDetectorSoftware/src/Module.h index 86c9e3d5f..4494383b0 100644 --- a/slsDetectorSoftware/src/Module.h +++ b/slsDetectorSoftware/src/Module.h @@ -301,9 +301,10 @@ class Module : public virtual slsDetectorDefs { std::array getReceiverThreadIds() const; bool getRxArping() const; void setRxArping(bool enable); - defs::ROI getRxROI() const; - void setRxROI(const slsDetectorDefs::ROI arg); - void setRxROIMetadata(const slsDetectorDefs::ROI arg); + std::vector getRxROI() const; + void setRxROI(const std::vector &portRois); + void setRxROIMetadata(const std::vector &args); + std::vector getRxROIMetadata() const; /************************************************** * * diff --git a/slsDetectorSoftware/src/inferAction.cpp b/slsDetectorSoftware/src/inferAction.cpp index 68c8638f1..7865cd692 100644 --- a/slsDetectorSoftware/src/inferAction.cpp +++ b/slsDetectorSoftware/src/inferAction.cpp @@ -2769,22 +2769,8 @@ int InferAction::rx_realudpsocksize() { int InferAction::rx_roi() { - if (args.size() == 0) { - return slsDetectorDefs::GET_ACTION; - } - - if (args.size() == 2) { - return slsDetectorDefs::PUT_ACTION; - } - - if (args.size() == 4) { - return slsDetectorDefs::PUT_ACTION; - } - - else { - - throw RuntimeError("Could not infer action: Wrong number of arguments"); - } + throw RuntimeError("sls_detector is disabled for command: rx_roi. Use " + "sls_detector_get or sls_detector_put"); } int InferAction::rx_silent() { diff --git a/slsDetectorSoftware/tests/CMakeLists.txt b/slsDetectorSoftware/tests/CMakeLists.txt index d87f17ed0..6a4c03855 100755 --- a/slsDetectorSoftware/tests/CMakeLists.txt +++ b/slsDetectorSoftware/tests/CMakeLists.txt @@ -24,11 +24,16 @@ target_sources(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/test-Module.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test-Pattern.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test-CtbConfig.cpp - - - ) +# HDF5 file +if (SLS_USE_HDF5) + target_compile_definitions(tests + PUBLIC + -DHDF5C ${HDF5_DEFINITIONS} + ) +endif (SLS_USE_HDF5) + target_include_directories(tests PUBLIC "$" diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp index fcf0de97d..51fe04899 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp @@ -7,6 +7,7 @@ #include "sls/sls_detector_defs.h" #include "test-Caller-global.h" +#include #include #include "sls/versionAPI.h" @@ -466,7 +467,6 @@ TEST_CASE("rx_arping", "[.cmdcall][.rx]") { } } } - TEST_CASE("rx_roi", "[.cmdcall]") { Detector det; Caller caller(&det); @@ -477,35 +477,95 @@ TEST_CASE("rx_roi", "[.cmdcall]") { } else { auto prev_val = det.getRxROI(); defs::xy detsize = det.getDetectorSize(); + auto portSize = det.getPortSize()[0]; + int delta = 50; // 1d - if (det_type == defs::GOTTHARD || det_type == defs::GOTTHARD2 || - det_type == defs::MYTHEN3) { + if (det_type == defs::GOTTHARD2 || det_type == defs::MYTHEN3) { { std::ostringstream oss; caller.call("rx_roi", {"5", "10"}, -1, PUT, oss); - REQUIRE(oss.str() == "rx_roi [5, 10]\n"); + REQUIRE(oss.str() == "rx_roi [[5, 10]]\n"); } { std::ostringstream oss; caller.call("rx_roi", {"10", "15"}, -1, PUT, oss); - REQUIRE(oss.str() == "rx_roi [10, 15]\n"); + REQUIRE(oss.str() == "rx_roi [[10, 15]]\n"); } + REQUIRE_THROWS(caller.call("rx_roi", {"0", "0"}, -1, PUT)); REQUIRE_THROWS(caller.call("rx_roi", {"-1", "-1"}, -1, PUT)); - REQUIRE_THROWS( - caller.call("rx_roi", {"10", "15", "25", "30"}, -1, PUT)); + // xmin > xmax + REQUIRE_THROWS(caller.call("rx_roi", {"[12, 8, -1, -1]"}, -1, PUT)); + // outside detector bounds + REQUIRE_THROWS(caller.call( + "rx_roi", + {"[95," + std::to_string(detsize.x + 5) + ", -1, -1]"}, -1, + PUT)); + // module level not allowed + REQUIRE_THROWS(caller.call("rx_roi", {"[5, 10, -1, -1]"}, 0, PUT)); + + // vector of rois + // square brackets missing + REQUIRE_THROWS(caller.call( + "rx_roi", {"[5, 20, -1, -1]; 25, 30, -1, -1]"}, -1, PUT)); + REQUIRE_THROWS(caller.call( + "rx_roi", {"[5, 20, -1, -1]; [25, 30, -1, -1"}, -1, PUT)); + // invalid roi, 4 parts expected + REQUIRE_THROWS(caller.call( + "rx_roi", {"[5, 20, -1] [25, 30, -1, -1]"}, -1, PUT)); + // overlapping rois + REQUIRE_THROWS(caller.call( + "rx_roi", {"[0, 10,-1, -1] [5, 15, -1, -1]"}, -1, PUT)); + + if (det.size() == 2) { + auto moduleSize = det.getModuleSize()[0]; + std::string stringMin = std::to_string(moduleSize.x); + std::string stringMax = std::to_string(moduleSize.x + 1); + + // separated by space is allowed + REQUIRE_NOTHROW(caller.call( + "rx_roi", + {"[5, 10, -1, -1]", + "[" + stringMin + ", " + stringMax + ", -1, -1]"}, + -1, PUT)); + std::ostringstream oss; + // separated by semicolon with quotes is allowed (skips + // cmdParser) + REQUIRE_NOTHROW(caller.call("rx_roi", + {"[5, 10, -1, -1];[" + stringMin + + ", " + stringMax + ", -1, -1]"}, + -1, PUT, oss)); + REQUIRE(oss.str() == "rx_roi [[5, 10], [" + stringMin + ", " + + stringMax + "]]\n"); + + // verify individual roi + { + stringMin = std::to_string(moduleSize.x - 50); + stringMax = std::to_string(moduleSize.x + 50); + std::ostringstream oss, oss1; + REQUIRE_NOTHROW(caller.call( + "rx_roi", {"[" + stringMin + ", " + stringMax + "]"}, + -1, PUT, oss)); + REQUIRE(oss.str() == "rx_roi [[" + stringMin + ", " + + stringMax + "]]\n"); + REQUIRE_NOTHROW(caller.call("rx_roi", {}, 0, GET, oss1)); + REQUIRE(oss1.str() == "rx_roi [[" + stringMin + ", " + + std::to_string(moduleSize.x - 1) + + "]]\n"); + } + } } - // 2d + // 2d eiger, jungfrau, moench else { { std::ostringstream oss; caller.call("rx_roi", {"10", "15", "1", "5"}, -1, PUT, oss); - REQUIRE(oss.str() == "rx_roi [10, 15, 1, 5]\n"); + REQUIRE(oss.str() == "rx_roi [[10, 15, 1, 5]]\n"); } { std::ostringstream oss; caller.call("rx_roi", {"10", "22", "18", "19"}, -1, PUT, oss); - REQUIRE(oss.str() == "rx_roi [10, 22, 18, 19]\n"); + REQUIRE(oss.str() == "rx_roi [[10, 22, 18, 19]]\n"); } { std::ostringstream oss; @@ -513,18 +573,215 @@ TEST_CASE("rx_roi", "[.cmdcall]") { {"1", std::to_string(detsize.x - 5), "1", std::to_string(detsize.y - 5)}, -1, PUT, oss); - REQUIRE(oss.str() == std::string("rx_roi [1, ") + + REQUIRE(oss.str() == std::string("rx_roi [[1, ") + std::to_string(detsize.x - 5) + std::string(", 1, ") + std::to_string(detsize.y - 5) + - std::string("]\n")); + std::string("]]\n")); } + REQUIRE_THROWS( + caller.call("rx_roi", {"0", "0", "0", "0"}, -1, PUT)); REQUIRE_THROWS( caller.call("rx_roi", {"-1", "-1", "-1", "-1"}, -1, PUT)); + // xmin > xmax + REQUIRE_THROWS(caller.call("rx_roi", {"[12, 8, 0, 10]"}, -1, PUT)); + // ymin > ymax + REQUIRE_THROWS(caller.call("rx_roi", {"[0, 10, 20, 5]"}, -1, PUT)); + // outside detector bounds + REQUIRE_THROWS(caller.call( + "rx_roi", {"[95," + std::to_string(detsize.x + 5) + ", 0, 10]"}, + -1, PUT)); + REQUIRE_THROWS(caller.call( + "rx_roi", + {"[95, 100, 0, " + std::to_string(detsize.y + 5) + "]"}, -1, + PUT)); + // module level not allowed + REQUIRE_THROWS(caller.call("rx_roi", {"[5, 10, 20, 30]"}, 0, PUT)); + + // vector of rois + // square brackets missing + REQUIRE_THROWS(caller.call( + "rx_roi", {"[5, 20, 20, 30]; 25, 30, 14, 15]"}, -1, PUT)); + REQUIRE_THROWS(caller.call( + "rx_roi", {"[5, 20, 20, 30]; [25, 30, 14, 15"}, -1, PUT)); + // invalid roi, 4 parts expected + REQUIRE_THROWS(caller.call( + "rx_roi", {"[5, 20, 20] [25, 30, 14, 15]"}, -1, PUT)); + // overlapping rois + REQUIRE_THROWS(caller.call( + "rx_roi", {"[0, 10, 0, 10] [5, 15, 0, 10]"}, -1, PUT)); + REQUIRE_THROWS(caller.call( + "rx_roi", {"[0, 10, 0, 10] [0, 10, 9, 11]"}, -1, PUT)); + + int numinterfaces = det.getNumberofUDPInterfaces().tsquash( + "inconsistent number of interfaces"); + + // multiple ports horizontally + if (det_type == defs::EIGER || + (det.size() == 2 && det.getModuleGeometry().x > 1)) { + std::string stringMin = std::to_string(portSize.x); + std::string stringMax = std::to_string(portSize.x + 1); + + // separated by space is allowed + REQUIRE_NOTHROW(caller.call( + "rx_roi", + {"[5, 10, 20, 30]", + "[" + stringMin + ", " + stringMax + ", 20, 30]"}, + -1, PUT)); + std::ostringstream oss; + // separated by semicolon with quotes is allowed (skips + // cmdParser) + REQUIRE_NOTHROW(caller.call("rx_roi", + {"[5, 10, 20, 30];[" + stringMin + + ", " + stringMax + ", 20, 30]"}, + -1, PUT, oss)); + REQUIRE(oss.str() == "rx_roi [[5, 10, 20, 30], [" + stringMin + + ", " + stringMax + ", 20, 30]]\n"); + + // verify individual roi + { + stringMin = std::to_string(portSize.x - delta); + stringMax = std::to_string(portSize.x + delta); + std::ostringstream oss, oss1; + REQUIRE_NOTHROW(caller.call( + "rx_roi", + {"[" + stringMin + ", " + stringMax + ", 20, 30]"}, -1, + PUT, oss)); + REQUIRE(oss.str() == "rx_roi [[" + stringMin + ", " + + stringMax + ", 20, 30]]\n"); + REQUIRE_NOTHROW(caller.call("rx_roi", {}, 0, GET, oss1)); + // eiger returns 2 values for 2 ports per module + if (det_type == defs::EIGER) { + REQUIRE(oss1.str() == + "rx_roi [[" + stringMin + ", " + + std::to_string(portSize.x - 1) + + ", 20, 30], [0, " + std::to_string(delta) + + ", 20, 30]]\n"); + } + // others return only 1 roi per module (1 port per module) + else { + REQUIRE(oss1.str() == + "rx_roi [[" + stringMin + ", " + + std::to_string(portSize.x - 1) + + ", 20, 30]]\n"); + } + } + } + + // multiple ports vertically + if (((det_type == defs::JUNGFRAU || det_type == defs::MOENCH) && + (numinterfaces == 2)) || + (det.size() == 2 && det.getModuleGeometry().y > 1)) { + std::string stringMin = std::to_string(portSize.y); + std::string stringMax = std::to_string(portSize.y + 1); + + // separated by space is allowed + REQUIRE_NOTHROW( + caller.call("rx_roi", + {"[5, 10, 20, 30]", "[25, 28, " + stringMin + + ", " + stringMax + "]"}, + -1, PUT)); + std::ostringstream oss; + // separated by semicolon is allowed with quotes (skips + // cmdParser) + REQUIRE_NOTHROW( + caller.call("rx_roi", + {"[5, 10, 20, 30];[25, 28, " + stringMin + + ", " + stringMax + "]"}, + -1, PUT, oss)); + REQUIRE(oss.str() == "rx_roi [[5, 10, 20, 30], [25, 28, " + + stringMin + ", " + stringMax + "]]\n"); + + // verify individual roi + { + stringMin = std::to_string(portSize.y - delta); + stringMax = std::to_string(portSize.y + delta); + std::ostringstream oss, oss1; + REQUIRE_NOTHROW(caller.call( + "rx_roi", + {"[ 20, 30, " + stringMin + ", " + stringMax + "]"}, -1, + PUT, oss)); + REQUIRE(oss.str() == "rx_roi [[20, 30, " + stringMin + + ", " + stringMax + "]]\n"); + REQUIRE_NOTHROW(caller.call("rx_roi", {}, 0, GET, oss1)); + + // non-eiger with 2 interfaces returns 2 values for 2 ports + // per module + if ((det_type == defs::JUNGFRAU || + det_type == defs::MOENCH) && + (numinterfaces == 2)) { + REQUIRE(oss1.str() == + "rx_roi [[20, 30, " + stringMin + ", " + + std::to_string(portSize.y - 1) + + "], [20, 30, 0, " + std::to_string(delta) + + "]]\n"); + } + // others return only 1 roi per module (1 port per module) + else { + // (eiger 2 ports) + if (det_type == defs::EIGER) { + REQUIRE(oss1.str() == + "rx_roi [[20, 30, " + stringMin + ", " + + std::to_string(portSize.y - 1) + + "], [-1, -1]]\n"); + } else { + REQUIRE(oss1.str() == + "rx_roi [[20, 30, " + stringMin + ", " + + std::to_string(portSize.y - 1) + + "]]\n"); + } + } + } + } + } + + // check master file creation + // TODO: check roi in master file + { + auto prev_write = det.getFileWrite().tsquash( + "inconsistent file write values in test"); + auto prev_path = det.getFilePath().tsquash( + "inconsistent file path values in test"); + auto prev_format = det.getFileFormat().tsquash( + "inconsistent file format values in test"); + auto prev_index = det.getAcquisitionIndex().tsquash( + "inconsistent file index values in test"); + auto prev_fname = det.getFileNamePrefix().tsquash( + "inconsistent file name prefix values in test"); + + det.setFileWrite(true); + det.setFilePath("/tmp"); + det.setFileNamePrefix("test"); + + det.setAcquisitionIndex(0); + det.setFileFormat(defs::BINARY); + REQUIRE_NOTHROW(caller.call("acquire", {}, -1, PUT)); + std::string file_path = "/tmp/test_master_0.json"; + REQUIRE(std::filesystem::exists(file_path) == true); + +#ifdef HDF5C + det.setAcquisitionIndex(0); + det.setFileFormat(defs::HDF5); + REQUIRE_NOTHROW(caller.call("acquire", {}, -1, PUT)); + file_path = "/tmp/test_master_0.h5"; + REQUIRE(std::filesystem::exists(file_path) == true); + file_path = "/tmp/test_virtual_0.h5"; + REQUIRE(std::filesystem::exists(file_path) == true); +#endif + + det.setFileWrite(prev_write); + if (!prev_path.empty()) + det.setFilePath(prev_path); + det.setFileFormat(prev_format); + det.setAcquisitionIndex(prev_index); + det.setFileNamePrefix(prev_fname); } for (int i = 0; i != det.size(); ++i) { - det.setRxROI(prev_val); + if (prev_val.size() == 1 && prev_val[0].completeRoi()) { + det.clearRxROI(); + } else + det.setRxROI(prev_val); } } } diff --git a/slsReceiverSoftware/src/ClientInterface.cpp b/slsReceiverSoftware/src/ClientInterface.cpp index f3de129b5..902870a8f 100644 --- a/slsReceiverSoftware/src/ClientInterface.cpp +++ b/slsReceiverSoftware/src/ClientInterface.cpp @@ -220,6 +220,7 @@ int ClientInterface::functionTable(){ flist[F_RECEIVER_SET_COLUMN] = &ClientInterface::set_column; flist[F_GET_RECEIVER_DBIT_REORDER] = &ClientInterface::get_dbit_reorder; flist[F_SET_RECEIVER_DBIT_REORDER] = &ClientInterface::set_dbit_reorder; + flist[F_RECEIVER_GET_ROI_METADATA] = &ClientInterface::get_roi_metadata; for (int i = NUM_DET_FUNCTIONS + 1; i < NUM_REC_FUNCTIONS ; i++) { @@ -1693,19 +1694,37 @@ int ClientInterface::set_arping(Interface &socket) { } int ClientInterface::get_receiver_roi(Interface &socket) { - auto retval = impl()->getReceiverROI(); - LOG(logDEBUG1) << "Receiver roi retval:" << ToString(retval); - return socket.sendResult(retval); + auto retvals = impl()->getPortROIs(); + LOG(logDEBUG1) << "Receiver roi retval:" << ToString(retvals); + auto size = static_cast(retvals.size()); + if (size != impl()->getNumberofUDPInterfaces()) { + throw RuntimeError("Invalid number of ROIs received: " + + std::to_string(size) + ". Expected: " + + std::to_string(impl()->getNumberofUDPInterfaces())); + } + socket.Send(size); + if (size > 0) + socket.Send(retvals); + return OK; } int ClientInterface::set_receiver_roi(Interface &socket) { - auto arg = socket.Receive(); + auto roiSize = socket.Receive(); + std::vector args(roiSize); + if (roiSize > 0) { + socket.Receive(args); + } + if (roiSize != impl()->getNumberofUDPInterfaces()) { + throw RuntimeError("Invalid number of ROIs received: " + + std::to_string(roiSize) + ". Expected: " + + std::to_string(impl()->getNumberofUDPInterfaces())); + } if (detType == CHIPTESTBOARD || detType == XILINX_CHIPTESTBOARD) functionNotImplemented(); - LOG(logDEBUG1) << "Set Receiver ROI: " << ToString(arg); + LOG(logDEBUG1) << "Set Receiver ROI: " << ToString(args); verifyIdle(socket); try { - impl()->setReceiverROI(arg); + impl()->setPortROIs(args); } catch (const std::exception &e) { throw RuntimeError("Could not set Receiver ROI [" + std::string(e.what()) + ']'); @@ -1715,18 +1734,26 @@ int ClientInterface::set_receiver_roi(Interface &socket) { } int ClientInterface::set_receiver_roi_metadata(Interface &socket) { - auto arg = socket.Receive(); + auto roiSize = socket.Receive(); + LOG(logDEBUG1) << "Number of ReceiverROI metadata: " << roiSize; + if (roiSize < 1) { + throw RuntimeError("Invalid number of ROIs received: " + + std::to_string(roiSize) + ". Min: 1."); + } + std::vector rois(roiSize); + if (roiSize > 0) { + socket.Receive(rois); + } if (detType == CHIPTESTBOARD || detType == XILINX_CHIPTESTBOARD) functionNotImplemented(); - LOG(logDEBUG1) << "Set Receiver ROI Metadata: " << ToString(arg); verifyIdle(socket); + LOG(logINFO) << "Setting ReceiverROI metadata[" << roiSize << ']'; try { - impl()->setReceiverROIMetadata(arg); + impl()->setMultiROIMetadata(rois); } catch (const std::exception &e) { throw RuntimeError("Could not set ReceiverROI metadata [" + std::string(e.what()) + ']'); } - return socket.Send(OK); } @@ -1812,4 +1839,16 @@ int ClientInterface::set_dbit_reorder(Interface &socket) { return socket.Send(OK); } +int ClientInterface::get_roi_metadata(Interface &socket) { + if (detType == CHIPTESTBOARD || detType == XILINX_CHIPTESTBOARD) + functionNotImplemented(); + auto retvals = impl()->getMultiROIMetadata(); + LOG(logDEBUG1) << "Receiver ROI metadata retval:" << ToString(retvals); + auto size = static_cast(retvals.size()); + socket.Send(size); + if (size > 0) + socket.Send(retvals); + return OK; +} + } // namespace sls diff --git a/slsReceiverSoftware/src/ClientInterface.h b/slsReceiverSoftware/src/ClientInterface.h index 665e0f396..e17a3ab85 100644 --- a/slsReceiverSoftware/src/ClientInterface.h +++ b/slsReceiverSoftware/src/ClientInterface.h @@ -166,6 +166,7 @@ class ClientInterface : private virtual slsDetectorDefs { int set_column(ServerInterface &socket); int get_dbit_reorder(ServerInterface &socket); int set_dbit_reorder(ServerInterface &socket); + int get_roi_metadata(ServerInterface &socket); Implementation *impl() { if (receiver != nullptr) { diff --git a/slsReceiverSoftware/src/DataProcessor.cpp b/slsReceiverSoftware/src/DataProcessor.cpp index 9cedcd522..8631af36c 100644 --- a/slsReceiverSoftware/src/DataProcessor.cpp +++ b/slsReceiverSoftware/src/DataProcessor.cpp @@ -48,10 +48,15 @@ void DataProcessor::SetUdpPortNumber(const uint16_t portNumber) { void DataProcessor::SetActivate(bool enable) { activated = enable; } -void DataProcessor::SetReceiverROI(ROI roi) { - receiverRoi = roi; - receiverRoiEnabled = receiverRoi.completeRoi() ? false : true; - receiverNoRoi = receiverRoi.noRoi(); +void DataProcessor::SetPortROI(ROI roi) { + portRoi = roi; + isPartiallyInRoi = portRoi.completeRoi() ? false : true; + isOutsideRoi = portRoi.noRoi(); +} + +void DataProcessor::setMultiROIMetadata( + const std::vector &args) { + multiRoiMetadata = args; } void DataProcessor::SetDataStreamEnable(bool enable) { @@ -154,17 +159,17 @@ void DataProcessor::CreateFirstFiles(const std::string &fileNamePrefix, CloseFiles(); // deactivated (half module/ single port or no roi), dont write file - if (!activated || !detectorDataStream || receiverNoRoi) { + if (!activated || !detectorDataStream || isOutsideRoi) { return; } #ifdef HDF5C int nx = generalData->nPixelsX; int ny = generalData->nPixelsY; - if (receiverRoiEnabled) { - nx = receiverRoi.xmax - receiverRoi.xmin + 1; - ny = receiverRoi.ymax - receiverRoi.ymin + 1; - if (receiverRoi.ymax == -1 || receiverRoi.ymin == -1) { + if (isPartiallyInRoi) { + nx = portRoi.xmax - portRoi.xmin + 1; + ny = portRoi.ymax - portRoi.ymin + 1; + if (portRoi.ymax == -1 || portRoi.ymin == -1) { ny = 1; } } @@ -201,16 +206,11 @@ std::string DataProcessor::CreateVirtualFile( const std::string &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, const int modulePos, const int numModX, const int numModY, - std::mutex *hdf5LibMutex) { + std::mutex *hdf5LibMutex, bool gotthard25um) { - if (receiverRoiEnabled) { - throw std::runtime_error( - "Skipping virtual hdf5 file since rx_roi is enabled."); - } - - bool gotthard25um = ((generalData->detType == GOTTHARD || - generalData->detType == GOTTHARD2) && - (numModX * numModY) == 2); + int ny = generalData->nPixelsY; + if (generalData->dynamicRange == 4) + ny /= 2; // 0 for infinite files uint32_t framesPerFile = @@ -224,10 +224,10 @@ std::string DataProcessor::CreateVirtualFile( return masterFileUtility::CreateVirtualHDF5File( filePath, fileNamePrefix, fileIndex, overWriteEnable, silentMode, modulePos, generalData->numUDPInterfaces, framesPerFile, - generalData->nPixelsX, generalData->nPixelsY, generalData->dynamicRange, - numFramesCaught, numModX, numModY, dataFile->GetPDataType(), + generalData->nPixelsX, ny, generalData->dynamicRange, numFramesCaught, + numModX, numModY, dataFile->GetPDataType(), dataFile->GetParameterNames(), dataFile->GetParameterDataTypes(), - hdf5LibMutex, gotthard25um); + hdf5LibMutex, gotthard25um, multiRoiMetadata); } void DataProcessor::LinkFileInMaster(const std::string &masterFileName, @@ -235,18 +235,14 @@ void DataProcessor::LinkFileInMaster(const std::string &masterFileName, const bool silentMode, std::mutex *hdf5LibMutex) { - if (receiverRoiEnabled) { - throw std::runtime_error( - "Should not be here, roi with hdf5 virtual should throw."); - } std::string fname{virtualFileName}, masterfname{masterFileName}; // if no virtual file, link data file if (virtualFileName.empty()) { fname = dataFile->GetFileName(); } - masterFileUtility::LinkHDF5FileInMaster(masterfname, fname, - dataFile->GetParameterNames(), - silentMode, hdf5LibMutex); + masterFileUtility::LinkHDF5FileInMaster( + masterfname, fname, dataFile->GetParameterNames(), silentMode, + hdf5LibMutex, multiRoiMetadata.size()); } #endif @@ -301,7 +297,7 @@ void DataProcessor::ThreadExecution() { // stream (if time/freq to stream) or free if (streamCurrentFrame) { // copy the complete image back if roi enabled - if (receiverRoiEnabled) { + if (isPartiallyInRoi) { memImage->size = generalData->imageSize; memcpy(memImage->data, &completeImageToStreamBeforeCropping[0], generalData->imageSize); @@ -381,7 +377,7 @@ void DataProcessor::ProcessAnImage(sls_receiver_header &header, size_t &size, streamCurrentFrame = false; } - if (receiverRoiEnabled) { + if (isPartiallyInRoi) { // copy the complete image to stream before cropping if (streamCurrentFrame) { memcpy(&completeImageToStreamBeforeCropping[0], data, @@ -687,12 +683,12 @@ void DataProcessor::ArrangeDbitData(size_t &size, char *data) { } void DataProcessor::CropImage(size_t &size, char *data) { - LOG(logDEBUG) << "Cropping Image to ROI " << ToString(receiverRoi); + LOG(logDEBUG1) << "Cropping Image to ROI " << ToString(portRoi); int nPixelsX = generalData->nPixelsX; - int xmin = receiverRoi.xmin; - int xmax = receiverRoi.xmax; - int ymin = receiverRoi.ymin; - int ymax = receiverRoi.ymax; + int xmin = portRoi.xmin; + int xmax = portRoi.xmax; + int ymin = portRoi.ymin; + int ymax = portRoi.ymax; int xwidth = xmax - xmin + 1; int ywidth = ymax - ymin + 1; if (ymin == -1 || ymax == -1) { diff --git a/slsReceiverSoftware/src/DataProcessor.h b/slsReceiverSoftware/src/DataProcessor.h index 2f1c8f4ba..dfed87819 100644 --- a/slsReceiverSoftware/src/DataProcessor.h +++ b/slsReceiverSoftware/src/DataProcessor.h @@ -39,7 +39,8 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { void SetUdpPortNumber(const uint16_t portNumber); void SetActivate(bool enable); - void SetReceiverROI(ROI roi); + void SetPortROI(const ROI arg); + void setMultiROIMetadata(const std::vector &args); void SetDataStreamEnable(bool enable); void SetStreamingFrequency(uint32_t value); void SetStreamingTimerInMs(uint32_t value); @@ -69,7 +70,7 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { const bool overWriteEnable, const bool silentMode, const int modulePos, const int numModX, const int numModY, - std::mutex *hdf5LibMutex); + std::mutex *hdf5LibMutex, bool gotthard25um); void LinkFileInMaster(const std::string &masterFileName, const std::string &virtualFileName, const bool silentMode, std::mutex *hdf5LibMutex); @@ -159,9 +160,11 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject { uint16_t udpPortNumber{0}; bool dataStreamEnable; bool activated{false}; - ROI receiverRoi{}; - bool receiverRoiEnabled{false}; - bool receiverNoRoi{false}; + ROI portRoi{}; + bool isPartiallyInRoi{false}; + bool isOutsideRoi{false}; + std::vector multiRoiMetadata{}; + std::unique_ptr completeImageToStreamBeforeCropping; /** if 0, sending random images with a timer */ uint32_t streamingFrequency; diff --git a/slsReceiverSoftware/src/DataStreamer.cpp b/slsReceiverSoftware/src/DataStreamer.cpp index c1b31b75b..8a8e83833 100644 --- a/slsReceiverSoftware/src/DataStreamer.cpp +++ b/slsReceiverSoftware/src/DataStreamer.cpp @@ -53,7 +53,14 @@ void DataStreamer::SetAdditionalJsonHeader( isAdditionalJsonUpdated = true; } -void DataStreamer::SetReceiverROI(ROI roi) { receiverRoi = roi; } +void DataStreamer::SetPortROI(ROI roi) { + if (roi.completeRoi()) { // TODO: just not send zmq if not in roi? + portRoi = + ROI(0, generalData->nPixelsX - 1, 0, generalData->nPixelsY - 1); + } else { + portRoi = roi; + } +} void DataStreamer::ResetParametersforNewAcquisition(const std::string &fname) { StopRunning(); @@ -210,7 +217,7 @@ int DataStreamer::SendDataHeader(sls_detector_header header, uint32_t size, isAdditionalJsonUpdated = false; } zHeader.addJsonHeader = localAdditionalJsonHeader; - zHeader.rx_roi = receiverRoi.getIntArray(); + zHeader.rx_roi = portRoi.getIntArray(); return zmqSocket->SendHeader(index, zHeader); } diff --git a/slsReceiverSoftware/src/DataStreamer.h b/slsReceiverSoftware/src/DataStreamer.h index dc28e35ac..d8eaaabcf 100644 --- a/slsReceiverSoftware/src/DataStreamer.h +++ b/slsReceiverSoftware/src/DataStreamer.h @@ -38,7 +38,7 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject { void SetNumberofTotalFrames(uint64_t value); void SetAdditionalJsonHeader(const std::map &json); - void SetReceiverROI(ROI roi); + void SetPortROI(ROI roi); void ResetParametersforNewAcquisition(const std::string &fname); /** @@ -91,7 +91,7 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject { uint64_t fileIndex{0}; bool flipRows{false}; std::map additionalJsonHeader; - ROI receiverRoi{}; + ROI portRoi{}; /** Used by streamer thread to update local copy (reduce number of locks * during streaming) */ diff --git a/slsReceiverSoftware/src/Implementation.cpp b/slsReceiverSoftware/src/Implementation.cpp index f9480c5d4..faf3bf031 100644 --- a/slsReceiverSoftware/src/Implementation.cpp +++ b/slsReceiverSoftware/src/Implementation.cpp @@ -155,6 +155,9 @@ void Implementation::setDetectorType(const detectorType d) { break; } + // number of portrois should be equal to number of interfaces + ResetRois(); + SetLocalNetworkParameters(); SetupFifoStructure(); @@ -184,7 +187,7 @@ void Implementation::SetupListener(int i) { listener[i]->SetUdpPortNumber(udpPortNum[i]); listener[i]->SetEthernetInterface(eth[i]); listener[i]->SetActivate(activated); - listener[i]->SetNoRoi(portRois[i].noRoi()); + listener[i]->SetIsOutsideRoi(portRois[i].noRoi()); listener[i]->SetDetectorDatastream(detectorDataStream[i]); listener[i]->SetSilentMode(silentMode); } @@ -194,7 +197,9 @@ void Implementation::SetupDataProcessor(int i) { dataProcessor[i]->SetGeneralData(generalData); dataProcessor[i]->SetUdpPortNumber(udpPortNum[i]); dataProcessor[i]->SetActivate(activated); - dataProcessor[i]->SetReceiverROI(portRois[i]); + dataProcessor[i]->SetPortROI(portRois[i]); + if (i == 0) + dataProcessor[0]->setMultiROIMetadata(multiRoiMetadata); dataProcessor[i]->SetDataStreamEnable(dataStreamEnable); dataProcessor[i]->SetStreamingFrequency(streamingFrequency); dataProcessor[i]->SetStreamingTimerInMs(streamingTimerInMs); @@ -216,8 +221,7 @@ void Implementation::SetupDataStreamer(int i) { dataStreamer[i]->SetFlipRows(flipRows); dataStreamer[i]->SetNumberofPorts(numPorts); dataStreamer[i]->SetNumberofTotalFrames(numberOfTotalFrames); - dataStreamer[i]->SetReceiverROI( - portRois[i].completeRoi() ? GetMaxROIPerPort() : portRois[i]); + dataStreamer[i]->SetPortROI(portRois[i]); } slsDetectorDefs::xy Implementation::getDetectorSize() const { @@ -233,18 +237,13 @@ const slsDetectorDefs::xy Implementation::GetPortGeometry() const { return portGeometry; } -const slsDetectorDefs::ROI Implementation::GetMaxROIPerPort() const { - return slsDetectorDefs::ROI{0, (int)generalData->nPixelsX - 1, 0, - (int)generalData->nPixelsY - 1}; -} - void Implementation::setDetectorSize(const slsDetectorDefs::xy size) { xy portGeometry = GetPortGeometry(); std::string log_message = "Detector Size (ports): ("; numModules = size; - numPorts.x = portGeometry.x * size.x; - numPorts.y = portGeometry.y * size.y; + numPorts.x = portGeometry.x * numModules.x; + numPorts.y = portGeometry.y * numModules.y; if (quadEnable) { numPorts.x = 1; numPorts.y = 2; @@ -401,97 +400,57 @@ void Implementation::setArping(const bool i, } } -slsDetectorDefs::ROI Implementation::getReceiverROI() const { - return receiverRoi; +std::vector Implementation::getPortROIs() const { + return portRois; } -void Implementation::setReceiverROI(const slsDetectorDefs::ROI arg) { - receiverRoi = arg; +void Implementation::ResetRois() { + int numports = generalData->numUDPInterfaces; + std::vector rois(numports); + std::vector multiRoi(1); + setPortROIs(rois); + setMultiROIMetadata(multiRoi); +} - if (generalData->numUDPInterfaces == 1 || - generalData->detType == slsDetectorDefs::GOTTHARD2) { - portRois[0] = arg; - } else { - slsDetectorDefs::xy nPortDim(generalData->nPixelsX, - generalData->nPixelsY); - - for (int iPort = 0; iPort != generalData->numUDPInterfaces; ++iPort) { - // default init = complete roi - slsDetectorDefs::ROI portRoi{}; - - // no roi - if (arg.noRoi()) { - portRoi.setNoRoi(); - } - - // incomplete roi - else if (!arg.completeRoi()) { - // get port limits - slsDetectorDefs::ROI portFullRoi{0, nPortDim.x - 1, 0, - nPortDim.y - 1}; - if (iPort == 1) { - // left right (eiger) - if (GetPortGeometry().x == 2) { - portFullRoi.xmin += nPortDim.x; - portFullRoi.xmax += nPortDim.x; - } - // top bottom (jungfrau or moench) - else { - portFullRoi.ymin += nPortDim.y; - portFullRoi.ymax += nPortDim.y; - } - } - LOG(logDEBUG) - << iPort << ": portfullroi:" << ToString(portFullRoi); - - // no roi - if (arg.xmin > portFullRoi.xmax || - arg.xmax < portFullRoi.xmin || - arg.ymin > portFullRoi.ymax || - arg.ymax < portFullRoi.ymin) { - portRoi.setNoRoi(); - } - - // incomplete module roi - else if (arg.xmin > portFullRoi.xmin || - arg.xmax < portFullRoi.xmax || - arg.ymin > portFullRoi.ymin || - arg.ymax < portFullRoi.ymax) { - portRoi.xmin = (arg.xmin <= portFullRoi.xmin) - ? 0 - : (arg.xmin % nPortDim.x); - portRoi.xmax = (arg.xmax >= portFullRoi.xmax) - ? nPortDim.x - 1 - : (arg.xmax % nPortDim.x); - portRoi.ymin = (arg.ymin <= portFullRoi.ymin) - ? 0 - : (arg.ymin % nPortDim.y); - portRoi.ymax = (arg.ymax >= portFullRoi.ymax) - ? nPortDim.y - 1 - : (arg.ymax % nPortDim.y); - } - } - portRois[iPort] = portRoi; +void Implementation::setPortROIs(const std::vector &args) { + int nx = static_cast(generalData->nPixelsX); + int ny = static_cast(generalData->nPixelsY); + // validate rois + for (auto &it : args) { + if (it.completeRoi() || it.noRoi()) { + continue; // valid + } + if (it.xmin < 0 || it.xmax < 0 || it.xmin >= nx || it.xmax >= nx) { + throw RuntimeError("Invalid ROIvx coordinates: " + ToString(it)); + } + if (ny > 1 && + (it.ymin < 0 || it.ymax < 0 || it.ymin >= ny || it.ymax >= ny)) { + throw RuntimeError("Invalid ROI y coordinates: " + ToString(it)); } } + portRois = args; + for (size_t i = 0; i != listener.size(); ++i) - listener[i]->SetNoRoi(portRois[i].noRoi()); - for (size_t i = 0; i != dataProcessor.size(); ++i) - dataProcessor[i]->SetReceiverROI(portRois[i]); + listener[i]->SetIsOutsideRoi(portRois[i].noRoi()); + for (size_t i = 0; i != dataProcessor.size(); ++i) { + dataProcessor[i]->SetPortROI(portRois[i]); + } for (size_t i = 0; i != dataStreamer.size(); ++i) { - dataStreamer[i]->SetReceiverROI( - portRois[i].completeRoi() ? GetMaxROIPerPort() : portRois[i]); - } - LOG(logINFO) << "receiver roi: " << ToString(receiverRoi); - if (generalData->numUDPInterfaces == 2 && - generalData->detType != slsDetectorDefs::GOTTHARD2) { - LOG(logINFO) << "port rois: " << ToString(portRois); + dataStreamer[i]->SetPortROI(portRois[i]); } + LOG(logINFO) << "Rois (per port): " << ToString(portRois); } -void Implementation::setReceiverROIMetadata(const ROI arg) { - receiverRoiMetadata = arg; - LOG(logINFO) << "receiver roi Metadata: " << ToString(receiverRoiMetadata); +void Implementation::setMultiROIMetadata( + const std::vector &args) { + multiRoiMetadata = args; + if (dataProcessor.size() > 0) + dataProcessor[0]->setMultiROIMetadata(multiRoiMetadata); + LOG(logINFO) << "Multi ROI Metadata: " << ToString(multiRoiMetadata); +} + +std::vector Implementation::getMultiROIMetadata() const { + return multiRoiMetadata; } /************************************************** @@ -787,8 +746,7 @@ void Implementation::stopReceiver() { summary = (i == 0 ? "\n\tDeactivated Left Port" : "\n\tDeactivated Right Port"); } else if (portRois[i].noRoi()) { - summary = (i == 0 ? "\n\tNo Roi on Left Port" - : "\n\tNo Roi on Right Port"); + summary = "\n\tNo Roi on Port[" + std::to_string(i) + ']'; } else { std::ostringstream os; os << "\n\tMissing Packets\t\t: " << mpMessage @@ -958,7 +916,20 @@ void Implementation::StartMasterWriter() { masterAttributes.framePadding = framePadding; masterAttributes.scanParams = scanParams; masterAttributes.totalFrames = numberOfTotalFrames; - masterAttributes.receiverRoi = receiverRoiMetadata; + // complete ROI (for each port TODO?) + if (multiRoiMetadata.size() == 1 && + multiRoiMetadata[0].completeRoi()) { + int nTotalPixelsX = (generalData->nPixelsX * numPorts.x); + int nTotalPixelsY = (generalData->nPixelsY * numPorts.y); + if (nTotalPixelsY == 1) { + masterAttributes.rois.push_back(ROI{0, nTotalPixelsX - 1}); + } else { + masterAttributes.rois.push_back( + ROI{0, nTotalPixelsX - 1, 0, nTotalPixelsY - 1}); + } + } else { + masterAttributes.rois = multiRoiMetadata; + } masterAttributes.exptime = acquisitionTime; masterAttributes.period = acquisitionPeriod; masterAttributes.burstMode = burstMode; @@ -1019,14 +990,36 @@ void Implementation::StartMasterWriter() { fileFormatType, &masterAttributes, &hdf5LibMutex); } #ifdef HDF5C + // create virtual and master file if (fileFormatType == HDF5) { + + bool gotthard25um = ((generalData->detType == GOTTHARD || + generalData->detType == GOTTHARD2) && + (numPorts.x * numPorts.y) == 2); + + // virtual hdf5 not allowed with roi for the following cases in hdf5 + if (multiRoiMetadata.size() > 1 || + (!multiRoiMetadata[0].completeRoi())) { + if (generalData->dynamicRange == 4) { + throw std::runtime_error( + "Skipping virtual hdf5 file since rx_roi is enabled " + "and it is in 4 bit mode."); + } + if (gotthard25um && (numPorts.x * numPorts.y) == 2) { + throw std::runtime_error( + "Skipping virtual hdf5 file since rx_roi is " + "enabled and there are 2 Gotthard 25um modules."); + } + } + std::string virtualFileName; // create virtual hdf5 file (if multiple files) if (dataProcessor[0]->GetFilesInAcquisition() > 1 || (numPorts.x * numPorts.y) > 1) { virtualFileName = dataProcessor[0]->CreateVirtualFile( filePath, fileName, fileIndex, overwriteEnable, silentMode, - modulePos, numPorts.x, numPorts.y, &hdf5LibMutex); + modulePos, numPorts.x, numPorts.y, &hdf5LibMutex, + gotthard25um); } // link file in master if (masterFileWriteEnable) { @@ -1087,8 +1080,9 @@ void Implementation::setNumberofUDPInterfaces(const int n) { // fifo SetupFifoStructure(); - // recalculate port rois - setReceiverROI(receiverRoi); + + // number of portrois should be equal to number of interfaces + ResetRois(); // create threads for (int i = 0; i < generalData->numUDPInterfaces; ++i) { diff --git a/slsReceiverSoftware/src/Implementation.h b/slsReceiverSoftware/src/Implementation.h index 9fbf1fd28..31359930f 100644 --- a/slsReceiverSoftware/src/Implementation.h +++ b/slsReceiverSoftware/src/Implementation.h @@ -58,9 +58,10 @@ class Implementation : private virtual slsDetectorDefs { bool getArping() const; pid_t getArpingProcessId() const; void setArping(const bool i, const std::vector ips); - ROI getReceiverROI() const; - void setReceiverROI(const ROI arg); - void setReceiverROIMetadata(const ROI arg); + std::vector getPortROIs() const; + void setPortROIs(const std::vector &args); + void setMultiROIMetadata(const std::vector &args); + std::vector getMultiROIMetadata() const; /************************************************** * * @@ -283,7 +284,7 @@ class Implementation : private virtual slsDetectorDefs { void SetupFifoStructure(); const xy GetPortGeometry() const; - const ROI GetMaxROIPerPort() const; + void ResetRois(); void ResetParametersforNewAcquisition(); void CreateUDPSockets(); void SetupWriter(); @@ -308,10 +309,8 @@ class Implementation : private virtual slsDetectorDefs { bool framePadding{true}; pid_t parentThreadId; pid_t tcpThreadId; - ROI receiverRoi{}; - std::array portRois{}; - // receiver roi for complete detector for metadata - ROI receiverRoiMetadata{}; + std::vector portRois; + std::vector multiRoiMetadata; // file parameters fileFormat fileFormatType{BINARY}; diff --git a/slsReceiverSoftware/src/Listener.cpp b/slsReceiverSoftware/src/Listener.cpp index 9c465883b..f1450e847 100644 --- a/slsReceiverSoftware/src/Listener.cpp +++ b/slsReceiverSoftware/src/Listener.cpp @@ -85,17 +85,17 @@ void Listener::SetEthernetInterface(const std::string e) { void Listener::SetActivate(bool enable) { activated = enable; - disabledPort = (!activated || !detectorDataStream || noRoi); + disabledPort = (!activated || !detectorDataStream || isOutsideRoi); } void Listener::SetDetectorDatastream(bool enable) { detectorDataStream = enable; - disabledPort = (!activated || !detectorDataStream || noRoi); + disabledPort = (!activated || !detectorDataStream || isOutsideRoi); } -void Listener::SetNoRoi(bool enable) { - noRoi = enable; - disabledPort = (!activated || !detectorDataStream || noRoi); +void Listener::SetIsOutsideRoi(bool enable) { + isOutsideRoi = enable; + disabledPort = (!activated || !detectorDataStream || isOutsideRoi); } void Listener::SetSilentMode(bool enable) { silentMode = enable; } diff --git a/slsReceiverSoftware/src/Listener.h b/slsReceiverSoftware/src/Listener.h index b5d437e64..628c8e349 100644 --- a/slsReceiverSoftware/src/Listener.h +++ b/slsReceiverSoftware/src/Listener.h @@ -43,7 +43,7 @@ class Listener : private virtual slsDetectorDefs, public ThreadObject { void SetEthernetInterface(const std::string e); void SetActivate(bool enable); void SetDetectorDatastream(bool enable); - void SetNoRoi(bool enable); + void SetIsOutsideRoi(bool enable); void SetSilentMode(bool enable); void ResetParametersforNewAcquisition(); @@ -116,7 +116,7 @@ class Listener : private virtual slsDetectorDefs, public ThreadObject { std::string eth; bool activated{false}; bool detectorDataStream{true}; - bool noRoi{false}; + bool isOutsideRoi{false}; bool silentMode; bool disabledPort{false}; diff --git a/slsReceiverSoftware/src/MasterAttributes.cpp b/slsReceiverSoftware/src/MasterAttributes.cpp index 8420e31ad..211b6f439 100644 --- a/slsReceiverSoftware/src/MasterAttributes.cpp +++ b/slsReceiverSoftware/src/MasterAttributes.cpp @@ -43,30 +43,30 @@ void MasterAttributes::WriteHDF5Attributes(H5::H5File *fd, H5::Group *group) { WriteCommonHDF5Attributes(fd, group); switch (detType) { case slsDetectorDefs::JUNGFRAU: - WriteJungfrauHDF5Attributes(fd, group); + WriteJungfrauHDF5Attributes(group); break; case slsDetectorDefs::MOENCH: - WriteMoenchHDF5Attributes(fd, group); + WriteMoenchHDF5Attributes(group); break; case slsDetectorDefs::EIGER: - WriteEigerHDF5Attributes(fd, group); + WriteEigerHDF5Attributes(group); break; case slsDetectorDefs::MYTHEN3: - WriteMythen3HDF5Attributes(fd, group); + WriteMythen3HDF5Attributes(group); break; case slsDetectorDefs::GOTTHARD2: - WriteGotthard2HDF5Attributes(fd, group); + WriteGotthard2HDF5Attributes(group); break; case slsDetectorDefs::CHIPTESTBOARD: - WriteCtbHDF5Attributes(fd, group); + WriteCtbHDF5Attributes(group); break; case slsDetectorDefs::XILINX_CHIPTESTBOARD: - WriteXilinxCtbHDF5Attributes(fd, group); + WriteXilinxCtbHDF5Attributes(group); break; default: throw RuntimeError("Unknown Detector type to get master attributes"); } - WriteFinalHDF5Attributes(fd, group); + WriteFinalHDF5Attributes(group); } #endif @@ -110,17 +110,6 @@ void MasterAttributes::GetCommonBinaryAttributes( w->String(ToString(scanParams).c_str()); w->Key("Total Frames"); w->Uint64(totalFrames); - w->Key("Receiver Roi"); - w->StartObject(); - w->Key("xmin"); - w->Uint(receiverRoi.xmin); - w->Key("xmax"); - w->Uint(receiverRoi.xmax); - w->Key("ymin"); - w->Uint(receiverRoi.ymin); - w->Key("ymax"); - w->Uint(receiverRoi.ymax); - w->EndObject(); } void MasterAttributes::GetFinalBinaryAttributes( @@ -165,6 +154,17 @@ void MasterAttributes::GetFinalBinaryAttributes( w->EndObject(); } +void MasterAttributes::GetBinaryRois( + rapidjson::PrettyWriter *w) { + w->Key("Receiver Rois"); + w->StartArray(); + for (const slsDetectorDefs::ROI &roi : rois) { + std::string roi_str = ToString(roi); + w->String(roi_str.c_str()); + } + w->EndArray(); +} + #ifdef HDF5C void MasterAttributes::WriteCommonHDF5Attributes(H5::H5File *fd, H5::Group *group) { @@ -287,38 +287,9 @@ void MasterAttributes::WriteCommonHDF5Attributes(H5::H5File *fd, "Total Frames", H5::PredType::STD_U64LE, dataspace); dataset.write(&totalFrames, H5::PredType::STD_U64LE); } - // Receiver Roi xmin - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "receiver roi xmin", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&receiverRoi.xmin, H5::PredType::NATIVE_INT); - } - // Receiver Roi xmax - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "receiver roi xmax", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&receiverRoi.xmax, H5::PredType::NATIVE_INT); - } - // Receiver Roi ymin - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "receiver roi ymin", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&receiverRoi.ymin, H5::PredType::NATIVE_INT); - } - // Receiver Roi ymax - { - H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); - H5::DataSet dataset = group->createDataSet( - "receiver roi ymax", H5::PredType::NATIVE_INT, dataspace); - dataset.write(&receiverRoi.ymax, H5::PredType::NATIVE_INT); - } } -void MasterAttributes::WriteFinalHDF5Attributes(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteFinalHDF5Attributes(H5::Group *group) { char c[1024]{}; // Total Frames in file { @@ -339,7 +310,20 @@ void MasterAttributes::WriteFinalHDF5Attributes(H5::H5File *fd, } } -void MasterAttributes::WriteHDF5Exptime(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5ROIs(H5::Group *group) { + hsize_t dims[1] = {rois.size()}; + H5::DataSpace dataspace(1, dims); + H5::StrType strdatatype(H5::PredType::C_S1, 1024); + H5::DataSet dataset = + group->createDataSet("Receiver Rois", strdatatype, dataspace); + std::vector cRois(rois.size()); + for (size_t i = 0; i < rois.size(); ++i) { + strcpy_safe(cRois[i], ToString(rois[i])); + } + dataset.write(cRois.data(), strdatatype); +} + +void MasterAttributes::WriteHDF5Exptime(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::StrType strdatatype(H5::PredType::C_S1, 256); H5::DataSet dataset = @@ -349,7 +333,7 @@ void MasterAttributes::WriteHDF5Exptime(H5::H5File *fd, H5::Group *group) { dataset.write(c, strdatatype); } -void MasterAttributes::WriteHDF5Period(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5Period(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::StrType strdatatype(H5::PredType::C_S1, 256); H5::DataSet dataset = @@ -359,7 +343,7 @@ void MasterAttributes::WriteHDF5Period(H5::H5File *fd, H5::Group *group) { dataset.write(c, strdatatype); } -void MasterAttributes::WriteHDF5DynamicRange(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5DynamicRange(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Dynamic Range", H5::PredType::NATIVE_INT, dataspace); @@ -372,30 +356,28 @@ void MasterAttributes::WriteHDF5DynamicRange(H5::H5File *fd, H5::Group *group) { attribute.write(strdatatype, c); } -void MasterAttributes::WriteHDF5TenGiga(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5TenGiga(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Ten Giga Enable", H5::PredType::NATIVE_INT, dataspace); dataset.write(&tenGiga, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5NumUDPInterfaces(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteHDF5NumUDPInterfaces(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Number of UDP Interfaces", H5::PredType::NATIVE_INT, dataspace); dataset.write(&numUDPInterfaces, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5ReadNRows(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5ReadNRows(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Number of rows", H5::PredType::NATIVE_INT, dataspace); dataset.write(&readNRows, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5ThresholdEnergy(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteHDF5ThresholdEnergy(H5::Group *group) { char c[1024]{}; H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( @@ -409,8 +391,7 @@ void MasterAttributes::WriteHDF5ThresholdEnergy(H5::H5File *fd, attribute.write(strdatatype, c); } -void MasterAttributes::WriteHDF5ThresholdEnergies(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteHDF5ThresholdEnergies(H5::Group *group) { char c[1024]{}; H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::StrType strdatatype(H5::PredType::C_S1, 1024); @@ -420,7 +401,7 @@ void MasterAttributes::WriteHDF5ThresholdEnergies(H5::H5File *fd, dataset.write(c, strdatatype); } -void MasterAttributes::WriteHDF5SubExpTime(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5SubExpTime(H5::Group *group) { char c[1024]{}; H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::StrType strdatatype(H5::PredType::C_S1, 256); @@ -430,7 +411,7 @@ void MasterAttributes::WriteHDF5SubExpTime(H5::H5File *fd, H5::Group *group) { dataset.write(c, strdatatype); } -void MasterAttributes::WriteHDF5SubPeriod(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5SubPeriod(H5::Group *group) { char c[1024]{}; H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::StrType strdatatype(H5::PredType::C_S1, 256); @@ -440,15 +421,14 @@ void MasterAttributes::WriteHDF5SubPeriod(H5::H5File *fd, H5::Group *group) { dataset.write(c, strdatatype); } -void MasterAttributes::WriteHDF5SubQuad(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5SubQuad(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet("Quad", H5::PredType::NATIVE_INT, dataspace); dataset.write(&quad, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5RateCorrections(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteHDF5RateCorrections(H5::Group *group) { char c[1024]{}; H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::StrType strdatatype(H5::PredType::C_S1, 1024); @@ -458,14 +438,14 @@ void MasterAttributes::WriteHDF5RateCorrections(H5::H5File *fd, dataset.write(c, strdatatype); } -void MasterAttributes::WriteHDF5CounterMask(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5CounterMask(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Counter Mask", H5::PredType::STD_U32LE, dataspace); dataset.write(&counterMask, H5::PredType::STD_U32LE); } -void MasterAttributes::WriteHDF5ExptimeArray(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5ExptimeArray(H5::Group *group) { for (int i = 0; i != 3; ++i) { char c[1024]{}; H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); @@ -477,8 +457,7 @@ void MasterAttributes::WriteHDF5ExptimeArray(H5::H5File *fd, H5::Group *group) { } } -void MasterAttributes::WriteHDF5GateDelayArray(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteHDF5GateDelayArray(H5::Group *group) { for (int i = 0; i != 3; ++i) { char c[1024]{}; H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); @@ -490,14 +469,14 @@ void MasterAttributes::WriteHDF5GateDelayArray(H5::H5File *fd, } } -void MasterAttributes::WriteHDF5Gates(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5Gates(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet("Gates", H5::PredType::STD_U32LE, dataspace); dataset.write(&gates, H5::PredType::STD_U32LE); } -void MasterAttributes::WriteHDF5BurstMode(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5BurstMode(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::StrType strdatatype(H5::PredType::C_S1, 256); H5::DataSet dataset = @@ -507,82 +486,77 @@ void MasterAttributes::WriteHDF5BurstMode(H5::H5File *fd, H5::Group *group) { dataset.write(c, strdatatype); } -void MasterAttributes::WriteHDF5AdcMask(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5AdcMask(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet("ADC Mask", H5::PredType::NATIVE_INT, dataspace); dataset.write(&adcmask, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5AnalogFlag(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5AnalogFlag(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Analog Flag", H5::PredType::NATIVE_INT, dataspace); dataset.write(&analog, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5AnalogSamples(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteHDF5AnalogSamples(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Analog Samples", H5::PredType::NATIVE_INT, dataspace); dataset.write(&analogSamples, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5DigitalFlag(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5DigitalFlag(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Digital Flag", H5::PredType::NATIVE_INT, dataspace); dataset.write(&digital, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5DigitalSamples(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteHDF5DigitalSamples(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Digital Samples", H5::PredType::NATIVE_INT, dataspace); dataset.write(&digitalSamples, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5DbitOffset(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5DbitOffset(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Dbit Offset", H5::PredType::NATIVE_INT, dataspace); dataset.write(&dbitoffset, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5DbitReorder(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5DbitReorder(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Dbit Reorder", H5::PredType::NATIVE_INT, dataspace); dataset.write(&dbitreorder, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5DbitList(H5::H5File *fd, H5::Group *group) { +void MasterAttributes::WriteHDF5DbitList(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Dbit Bitset List", H5::PredType::STD_U64LE, dataspace); dataset.write(&dbitlist, H5::PredType::STD_U64LE); } -void MasterAttributes::WriteHDF5TransceiverMask(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteHDF5TransceiverMask(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Transceiver Mask", H5::PredType::NATIVE_INT, dataspace); dataset.write(&transceiverMask, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5TransceiverFlag(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteHDF5TransceiverFlag(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Transceiver Flag", H5::PredType::NATIVE_INT, dataspace); dataset.write(&transceiver, H5::PredType::NATIVE_INT); } -void MasterAttributes::WriteHDF5TransceiverSamples(H5::H5File *fd, - H5::Group *group) { +void MasterAttributes::WriteHDF5TransceiverSamples(H5::Group *group) { H5::DataSpace dataspace = H5::DataSpace(H5S_SCALAR); H5::DataSet dataset = group->createDataSet( "Transceiver Samples", H5::PredType::NATIVE_INT, dataspace); @@ -592,6 +566,7 @@ void MasterAttributes::WriteHDF5TransceiverSamples(H5::H5File *fd, void MasterAttributes::GetJungfrauBinaryAttributes( rapidjson::PrettyWriter *w) { + GetBinaryRois(w); w->Key("Exptime"); w->String(ToString(exptime).c_str()); w->Key("Period"); @@ -603,17 +578,18 @@ void MasterAttributes::GetJungfrauBinaryAttributes( } #ifdef HDF5C -void MasterAttributes::WriteJungfrauHDF5Attributes(H5::H5File *fd, - H5::Group *group) { - MasterAttributes::WriteHDF5Exptime(fd, group); - MasterAttributes::WriteHDF5Period(fd, group); - MasterAttributes::WriteHDF5NumUDPInterfaces(fd, group); - MasterAttributes::WriteHDF5ReadNRows(fd, group); +void MasterAttributes::WriteJungfrauHDF5Attributes(H5::Group *group) { + MasterAttributes::WriteHDF5ROIs(group); + MasterAttributes::WriteHDF5Exptime(group); + MasterAttributes::WriteHDF5Period(group); + MasterAttributes::WriteHDF5NumUDPInterfaces(group); + MasterAttributes::WriteHDF5ReadNRows(group); } #endif void MasterAttributes::GetMoenchBinaryAttributes( rapidjson::PrettyWriter *w) { + GetBinaryRois(w); w->Key("Exptime"); w->String(ToString(exptime).c_str()); w->Key("Period"); @@ -625,17 +601,18 @@ void MasterAttributes::GetMoenchBinaryAttributes( } #ifdef HDF5C -void MasterAttributes::WriteMoenchHDF5Attributes(H5::H5File *fd, - H5::Group *group) { - MasterAttributes::WriteHDF5Exptime(fd, group); - MasterAttributes::WriteHDF5Period(fd, group); - MasterAttributes::WriteHDF5NumUDPInterfaces(fd, group); - MasterAttributes::WriteHDF5ReadNRows(fd, group); +void MasterAttributes::WriteMoenchHDF5Attributes(H5::Group *group) { + MasterAttributes::WriteHDF5ROIs(group); + MasterAttributes::WriteHDF5Exptime(group); + MasterAttributes::WriteHDF5Period(group); + MasterAttributes::WriteHDF5NumUDPInterfaces(group); + MasterAttributes::WriteHDF5ReadNRows(group); } #endif void MasterAttributes::GetEigerBinaryAttributes( rapidjson::PrettyWriter *w) { + GetBinaryRois(w); w->Key("Dynamic Range"); w->Uint(dynamicRange); w->Key("Ten Giga"); @@ -659,23 +636,24 @@ void MasterAttributes::GetEigerBinaryAttributes( } #ifdef HDF5C -void MasterAttributes::WriteEigerHDF5Attributes(H5::H5File *fd, - H5::Group *group) { - MasterAttributes::WriteHDF5DynamicRange(fd, group); - MasterAttributes::WriteHDF5TenGiga(fd, group); - MasterAttributes::WriteHDF5Exptime(fd, group); - MasterAttributes::WriteHDF5Period(fd, group); - MasterAttributes::WriteHDF5ThresholdEnergy(fd, group); - MasterAttributes::WriteHDF5SubExpTime(fd, group); - MasterAttributes::WriteHDF5SubPeriod(fd, group); - MasterAttributes::WriteHDF5SubQuad(fd, group); - MasterAttributes::WriteHDF5ReadNRows(fd, group); - MasterAttributes::WriteHDF5RateCorrections(fd, group); +void MasterAttributes::WriteEigerHDF5Attributes(H5::Group *group) { + MasterAttributes::WriteHDF5ROIs(group); + MasterAttributes::WriteHDF5DynamicRange(group); + MasterAttributes::WriteHDF5TenGiga(group); + MasterAttributes::WriteHDF5Exptime(group); + MasterAttributes::WriteHDF5Period(group); + MasterAttributes::WriteHDF5ThresholdEnergy(group); + MasterAttributes::WriteHDF5SubExpTime(group); + MasterAttributes::WriteHDF5SubPeriod(group); + MasterAttributes::WriteHDF5SubQuad(group); + MasterAttributes::WriteHDF5ReadNRows(group); + MasterAttributes::WriteHDF5RateCorrections(group); } #endif void MasterAttributes::GetMythen3BinaryAttributes( rapidjson::PrettyWriter *w) { + GetBinaryRois(w); w->Key("Dynamic Range"); w->Uint(dynamicRange); w->Key("Ten Giga"); @@ -699,21 +677,22 @@ void MasterAttributes::GetMythen3BinaryAttributes( } #ifdef HDF5C -void MasterAttributes::WriteMythen3HDF5Attributes(H5::H5File *fd, - H5::Group *group) { - MasterAttributes::WriteHDF5DynamicRange(fd, group); - MasterAttributes::WriteHDF5TenGiga(fd, group); - MasterAttributes::WriteHDF5Period(fd, group); - MasterAttributes::WriteHDF5CounterMask(fd, group); - MasterAttributes::WriteHDF5ExptimeArray(fd, group); - MasterAttributes::WriteHDF5GateDelayArray(fd, group); - MasterAttributes::WriteHDF5Gates(fd, group); - MasterAttributes::WriteHDF5ThresholdEnergies(fd, group); +void MasterAttributes::WriteMythen3HDF5Attributes(H5::Group *group) { + MasterAttributes::WriteHDF5ROIs(group); + MasterAttributes::WriteHDF5DynamicRange(group); + MasterAttributes::WriteHDF5TenGiga(group); + MasterAttributes::WriteHDF5Period(group); + MasterAttributes::WriteHDF5CounterMask(group); + MasterAttributes::WriteHDF5ExptimeArray(group); + MasterAttributes::WriteHDF5GateDelayArray(group); + MasterAttributes::WriteHDF5Gates(group); + MasterAttributes::WriteHDF5ThresholdEnergies(group); } #endif void MasterAttributes::GetGotthard2BinaryAttributes( rapidjson::PrettyWriter *w) { + GetBinaryRois(w); w->Key("Exptime"); w->String(ToString(exptime).c_str()); w->Key("Period"); @@ -723,11 +702,11 @@ void MasterAttributes::GetGotthard2BinaryAttributes( } #ifdef HDF5C -void MasterAttributes::WriteGotthard2HDF5Attributes(H5::H5File *fd, - H5::Group *group) { - MasterAttributes::WriteHDF5Exptime(fd, group); - MasterAttributes::WriteHDF5Period(fd, group); - MasterAttributes::WriteHDF5BurstMode(fd, group); +void MasterAttributes::WriteGotthard2HDF5Attributes(H5::Group *group) { + MasterAttributes::WriteHDF5ROIs(group); + MasterAttributes::WriteHDF5Exptime(group); + MasterAttributes::WriteHDF5Period(group); + MasterAttributes::WriteHDF5BurstMode(group); } #endif @@ -764,22 +743,21 @@ void MasterAttributes::GetCtbBinaryAttributes( } #ifdef HDF5C -void MasterAttributes::WriteCtbHDF5Attributes(H5::H5File *fd, - H5::Group *group) { - MasterAttributes::WriteHDF5Exptime(fd, group); - MasterAttributes::WriteHDF5Period(fd, group); - MasterAttributes::WriteHDF5TenGiga(fd, group); - MasterAttributes::WriteHDF5AdcMask(fd, group); - MasterAttributes::WriteHDF5AnalogFlag(fd, group); - MasterAttributes::WriteHDF5AnalogSamples(fd, group); - MasterAttributes::WriteHDF5DigitalFlag(fd, group); - MasterAttributes::WriteHDF5DigitalSamples(fd, group); - MasterAttributes::WriteHDF5DbitOffset(fd, group); - MasterAttributes::WriteHDF5DbitReorder(fd, group); - MasterAttributes::WriteHDF5DbitList(fd, group); - MasterAttributes::WriteHDF5TransceiverMask(fd, group); - MasterAttributes::WriteHDF5TransceiverFlag(fd, group); - MasterAttributes::WriteHDF5TransceiverSamples(fd, group); +void MasterAttributes::WriteCtbHDF5Attributes(H5::Group *group) { + MasterAttributes::WriteHDF5Exptime(group); + MasterAttributes::WriteHDF5Period(group); + MasterAttributes::WriteHDF5TenGiga(group); + MasterAttributes::WriteHDF5AdcMask(group); + MasterAttributes::WriteHDF5AnalogFlag(group); + MasterAttributes::WriteHDF5AnalogSamples(group); + MasterAttributes::WriteHDF5DigitalFlag(group); + MasterAttributes::WriteHDF5DigitalSamples(group); + MasterAttributes::WriteHDF5DbitOffset(group); + MasterAttributes::WriteHDF5DbitReorder(group); + MasterAttributes::WriteHDF5DbitList(group); + MasterAttributes::WriteHDF5TransceiverMask(group); + MasterAttributes::WriteHDF5TransceiverFlag(group); + MasterAttributes::WriteHDF5TransceiverSamples(group); } #endif @@ -814,21 +792,20 @@ void MasterAttributes::GetXilinxCtbBinaryAttributes( } #ifdef HDF5C -void MasterAttributes::WriteXilinxCtbHDF5Attributes(H5::H5File *fd, - H5::Group *group) { - MasterAttributes::WriteHDF5Exptime(fd, group); - MasterAttributes::WriteHDF5Period(fd, group); - MasterAttributes::WriteHDF5AdcMask(fd, group); - MasterAttributes::WriteHDF5AnalogFlag(fd, group); - MasterAttributes::WriteHDF5AnalogSamples(fd, group); - MasterAttributes::WriteHDF5DigitalFlag(fd, group); - MasterAttributes::WriteHDF5DigitalSamples(fd, group); - MasterAttributes::WriteHDF5DbitOffset(fd, group); - MasterAttributes::WriteHDF5DbitReorder(fd, group); - MasterAttributes::WriteHDF5DbitList(fd, group); - MasterAttributes::WriteHDF5TransceiverMask(fd, group); - MasterAttributes::WriteHDF5TransceiverFlag(fd, group); - MasterAttributes::WriteHDF5TransceiverSamples(fd, group); +void MasterAttributes::WriteXilinxCtbHDF5Attributes(H5::Group *group) { + MasterAttributes::WriteHDF5Exptime(group); + MasterAttributes::WriteHDF5Period(group); + MasterAttributes::WriteHDF5AdcMask(group); + MasterAttributes::WriteHDF5AnalogFlag(group); + MasterAttributes::WriteHDF5AnalogSamples(group); + MasterAttributes::WriteHDF5DigitalFlag(group); + MasterAttributes::WriteHDF5DigitalSamples(group); + MasterAttributes::WriteHDF5DbitOffset(group); + MasterAttributes::WriteHDF5DbitReorder(group); + MasterAttributes::WriteHDF5DbitList(group); + MasterAttributes::WriteHDF5TransceiverMask(group); + MasterAttributes::WriteHDF5TransceiverFlag(group); + MasterAttributes::WriteHDF5TransceiverSamples(group); } #endif } // namespace sls diff --git a/slsReceiverSoftware/src/MasterAttributes.h b/slsReceiverSoftware/src/MasterAttributes.h index 0e3c168ae..f195ee70a 100644 --- a/slsReceiverSoftware/src/MasterAttributes.h +++ b/slsReceiverSoftware/src/MasterAttributes.h @@ -57,7 +57,7 @@ class MasterAttributes { uint32_t transceiverMask{0}; uint32_t transceiver{0}; uint32_t transceiverSamples{0}; - slsDetectorDefs::ROI receiverRoi{}; + std::vector rois{}; uint32_t counterMask{0}; std::array exptimeArray{}; std::array gateDelayArray{}; @@ -78,79 +78,81 @@ class MasterAttributes { rapidjson::PrettyWriter *w); void GetFinalBinaryAttributes( rapidjson::PrettyWriter *w); + void GetBinaryRois(rapidjson::PrettyWriter *w); #ifdef HDF5C void WriteCommonHDF5Attributes(H5::H5File *fd, H5::Group *group); - void WriteFinalHDF5Attributes(H5::H5File *fd, H5::Group *group); - void WriteHDF5Exptime(H5::H5File *fd, H5::Group *group); - void WriteHDF5Period(H5::H5File *fd, H5::Group *group); - void WriteHDF5DynamicRange(H5::H5File *fd, H5::Group *group); - void WriteHDF5TenGiga(H5::H5File *fd, H5::Group *group); - void WriteHDF5NumUDPInterfaces(H5::H5File *fd, H5::Group *group); - void WriteHDF5ReadNRows(H5::H5File *fd, H5::Group *group); - void WriteHDF5ThresholdEnergy(H5::H5File *fd, H5::Group *group); - void WriteHDF5ThresholdEnergies(H5::H5File *fd, H5::Group *group); - void WriteHDF5SubExpTime(H5::H5File *fd, H5::Group *group); - void WriteHDF5SubPeriod(H5::H5File *fd, H5::Group *group); - void WriteHDF5SubQuad(H5::H5File *fd, H5::Group *group); - void WriteHDF5RateCorrections(H5::H5File *fd, H5::Group *group); - void WriteHDF5CounterMask(H5::H5File *fd, H5::Group *group); - void WriteHDF5ExptimeArray(H5::H5File *fd, H5::Group *group); - void WriteHDF5GateDelayArray(H5::H5File *fd, H5::Group *group); - void WriteHDF5Gates(H5::H5File *fd, H5::Group *group); - void WriteHDF5BurstMode(H5::H5File *fd, H5::Group *group); - void WriteHDF5AdcMask(H5::H5File *fd, H5::Group *group); - void WriteHDF5AnalogFlag(H5::H5File *fd, H5::Group *group); - void WriteHDF5AnalogSamples(H5::H5File *fd, H5::Group *group); - void WriteHDF5DigitalFlag(H5::H5File *fd, H5::Group *group); - void WriteHDF5DigitalSamples(H5::H5File *fd, H5::Group *group); - void WriteHDF5DbitOffset(H5::H5File *fd, H5::Group *group); - void WriteHDF5DbitList(H5::H5File *fd, H5::Group *group); - void WriteHDF5DbitReorder(H5::H5File *fd, H5::Group *group); - void WriteHDF5TransceiverMask(H5::H5File *fd, H5::Group *group); - void WriteHDF5TransceiverFlag(H5::H5File *fd, H5::Group *group); - void WriteHDF5TransceiverSamples(H5::H5File *fd, H5::Group *group); + void WriteFinalHDF5Attributes(H5::Group *group); + void WriteHDF5ROIs(H5::Group *group); + void WriteHDF5Exptime(H5::Group *group); + void WriteHDF5Period(H5::Group *group); + void WriteHDF5DynamicRange(H5::Group *group); + void WriteHDF5TenGiga(H5::Group *group); + void WriteHDF5NumUDPInterfaces(H5::Group *group); + void WriteHDF5ReadNRows(H5::Group *group); + void WriteHDF5ThresholdEnergy(H5::Group *group); + void WriteHDF5ThresholdEnergies(H5::Group *group); + void WriteHDF5SubExpTime(H5::Group *group); + void WriteHDF5SubPeriod(H5::Group *group); + void WriteHDF5SubQuad(H5::Group *group); + void WriteHDF5RateCorrections(H5::Group *group); + void WriteHDF5CounterMask(H5::Group *group); + void WriteHDF5ExptimeArray(H5::Group *group); + void WriteHDF5GateDelayArray(H5::Group *group); + void WriteHDF5Gates(H5::Group *group); + void WriteHDF5BurstMode(H5::Group *group); + void WriteHDF5AdcMask(H5::Group *group); + void WriteHDF5AnalogFlag(H5::Group *group); + void WriteHDF5AnalogSamples(H5::Group *group); + void WriteHDF5DigitalFlag(H5::Group *group); + void WriteHDF5DigitalSamples(H5::Group *group); + void WriteHDF5DbitOffset(H5::Group *group); + void WriteHDF5DbitList(H5::Group *group); + void WriteHDF5DbitReorder(H5::Group *group); + void WriteHDF5TransceiverMask(H5::Group *group); + void WriteHDF5TransceiverFlag(H5::Group *group); + void WriteHDF5TransceiverSamples(H5::Group *group); #endif void GetJungfrauBinaryAttributes( rapidjson::PrettyWriter *w); #ifdef HDF5C - void WriteJungfrauHDF5Attributes(H5::H5File *fd, H5::Group *group); + void WriteJungfrauHDF5Attributes(H5::Group *group); #endif void GetEigerBinaryAttributes( rapidjson::PrettyWriter *w); #ifdef HDF5C - void WriteEigerHDF5Attributes(H5::H5File *fd, H5::Group *group); + void WriteEigerHDF5Attributes(H5::Group *group); #endif void GetMythen3BinaryAttributes( rapidjson::PrettyWriter *w); #ifdef HDF5C - void WriteMythen3HDF5Attributes(H5::H5File *fd, H5::Group *group); + void WriteMythen3HDF5Attributes(H5::Group *group); #endif void GetGotthard2BinaryAttributes( rapidjson::PrettyWriter *w); #ifdef HDF5C - void WriteGotthard2HDF5Attributes(H5::H5File *fd, H5::Group *group); + void WriteGotthard2HDF5Attributes(H5::Group *group); #endif void GetMoenchBinaryAttributes( rapidjson::PrettyWriter *w); #ifdef HDF5C - void WriteMoenchHDF5Attributes(H5::H5File *fd, H5::Group *group); + void WriteMoenchHDF5Attributes(H5::Group *group); #endif void GetCtbBinaryAttributes(rapidjson::PrettyWriter *w); #ifdef HDF5C - void WriteCtbHDF5Attributes(H5::H5File *fd, H5::Group *group); + void WriteCtbHDF5Attributes(H5::Group *group); #endif void GetXilinxCtbBinaryAttributes( rapidjson::PrettyWriter *w); #ifdef HDF5C - void WriteXilinxCtbHDF5Attributes(H5::H5File *fd, H5::Group *group); + void WriteXilinxCtbHDF5Attributes(H5::Group *group); #endif }; diff --git a/slsReceiverSoftware/src/MasterFileUtility.cpp b/slsReceiverSoftware/src/MasterFileUtility.cpp index 793dc777e..08412ace9 100644 --- a/slsReceiverSoftware/src/MasterFileUtility.cpp +++ b/slsReceiverSoftware/src/MasterFileUtility.cpp @@ -49,7 +49,8 @@ std::string CreateMasterBinaryFile(const std::string &filePath, void LinkHDF5FileInMaster(std::string &masterFileName, std::string &dataFilename, std::vector parameterNames, - const bool silentMode, std::mutex *hdf5LibMutex) { + const bool silentMode, std::mutex *hdf5LibMutex, + size_t multiRoiSize) { std::lock_guard lock(*hdf5LibMutex); std::unique_ptr fd{nullptr}; @@ -67,27 +68,35 @@ void LinkHDF5FileInMaster(std::string &masterFileName, fd = make_unique(dataFilename.c_str(), H5F_ACC_RDONLY, H5::FileCreatPropList::DEFAULT, flist); - // create link for data dataset - H5::DataSet dset = fd->openDataSet(DATASET_NAME); - std::string linkname = - std::string("/entry/data/") + std::string(DATASET_NAME); - if (H5Lcreate_external(dataFilename.c_str(), DATASET_NAME, - masterfd.getLocId(), linkname.c_str(), - H5P_DEFAULT, H5P_DEFAULT) < 0) { - throw RuntimeError( - "Could not create link to data dataset in master"); - } + for (size_t iRoi = 0; iRoi != multiRoiSize; ++iRoi) { - // create link for parameter datasets - for (unsigned int i = 0; i < parameterNames.size(); ++i) { - H5::DataSet pDset = fd->openDataSet(parameterNames[i].c_str()); - linkname = std::string("/entry/data/") + parameterNames[i]; - if (H5Lcreate_external(dataFilename.c_str(), - parameterNames[i].c_str(), + // create link for data dataset + std::string datasetname = std::string(DATASET_NAME); + if (multiRoiSize > 1) + datasetname += ('_' + std::to_string(iRoi)); + H5::DataSet dset = fd->openDataSet(datasetname); + std::string linkname = std::string("/entry/data/") + datasetname; + if (H5Lcreate_external(dataFilename.c_str(), datasetname.c_str(), masterfd.getLocId(), linkname.c_str(), H5P_DEFAULT, H5P_DEFAULT) < 0) { throw RuntimeError( - "Could not create link to parameter dataset in master"); + "Could not create link to data dataset in master"); + } + + // create link for parameter datasets + for (unsigned int i = 0; i < parameterNames.size(); ++i) { + std::string parameterDsetName = parameterNames[i]; + if (multiRoiSize > 1) + parameterDsetName += ('_' + std::to_string(iRoi)); + H5::DataSet pDset = fd->openDataSet(parameterDsetName.c_str()); + linkname = std::string("/entry/data/") + parameterDsetName; + if (H5Lcreate_external(dataFilename.c_str(), + parameterDsetName.c_str(), + masterfd.getLocId(), linkname.c_str(), + H5P_DEFAULT, H5P_DEFAULT) < 0) { + throw RuntimeError( + "Could not create link to parameter dataset in master"); + } } } fd->close(); @@ -160,33 +169,56 @@ std::string CreateMasterHDF5File(const std::string &filePath, return fileName; } +defs::ROI GetGlobalPortRoi(const int iPort, const defs::xy portSize, + const int numPortsY) { + defs::xy portPos = {(iPort / numPortsY), (iPort % numPortsY)}; + const int xmin = portSize.x * portPos.x; + const int xmax = xmin + portSize.x - 1; + const int ymin = portSize.y * portPos.y; + const int ymax = ymin + portSize.y - 1; + return defs::ROI{xmin, xmax, ymin, ymax}; +} + +int GetNumPortsInRoi(const defs::ROI roi, const defs::xy portSize) { + if (portSize.x == 0 || portSize.y == 0) { + throw RuntimeError("Port width or height cannot be zero"); + } + int iPortXMin = roi.xmin / portSize.x; + int iPortXMax = roi.xmax / portSize.x; + int iPortYMin = roi.ymin / portSize.y; + int iPortYMax = roi.ymax / portSize.y; + return ((iPortXMax - iPortXMin + 1) * (iPortYMax - iPortYMin + 1)); +} + +/** Will not be called if dynamic range is 4 and roi enabled */ std::string CreateVirtualHDF5File( const std::string &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, const int modulePos, const int numUnitsPerReadout, - const uint32_t maxFramesPerFile, const uint32_t nPixelsX, - const uint32_t nPixelsY, const uint32_t dynamicRange, - const uint64_t numImagesCaught, const int numModX, const int numModY, - const H5::DataType dataType, const std::vector parameterNames, + const uint32_t maxFramesPerFile, const int nPixelsX, const int nPixelsY, + const uint32_t dynamicRange, const uint64_t numImagesCaught, + const int numModX, const int numModY, const H5::DataType dataType, + const std::vector parameterNames, const std::vector parameterDataTypes, - std::mutex *hdf5LibMutex, bool gotthard25um) { + std::mutex *hdf5LibMutex, bool gotthard25um, + std::vector multiRoi) { + + bool completeRoi = false; + if (multiRoi.size() == 1 && multiRoi[0].completeRoi()) { + completeRoi = true; + } // virtual file name std::ostringstream osfn; osfn << filePath << "/" << fileNamePrefix << "_virtual" << "_" << fileIndex << ".h5"; std::string fileName = osfn.str(); - unsigned int paraSize = parameterNames.size(); - uint64_t numModZ = numModX; - uint32_t nDimy = nPixelsY; - uint32_t nDimz = ((dynamicRange == 4) ? (nPixelsX / 2) : nPixelsX); - std::lock_guard lock(*hdf5LibMutex); - std::unique_ptr fd{nullptr}; try { H5::Exception::dontPrint(); // to handle errors + H5Eset_auto(H5E_DEFAULT, (H5E_auto2_t)H5Eprint, stderr); // file H5::FileAccPropList fapl; @@ -205,134 +237,172 @@ std::string CreateVirtualHDF5File( "version", H5::PredType::NATIVE_DOUBLE, dataspace_attr); attribute.write(H5::PredType::NATIVE_DOUBLE, &dValue); - // dataspace - hsize_t vdsDims[DATA_RANK] = {numImagesCaught, numModY * nDimy, - numModZ * nDimz}; - hsize_t vdsDimsPara[VDS_PARA_RANK] = {numImagesCaught, - numModY * numModZ}; - H5::DataSpace vdsDataSpace(DATA_RANK, vdsDims, nullptr); - H5::DataSpace vdsDataSpacePara(VDS_PARA_RANK, vdsDimsPara, nullptr); + for (size_t iRoi = 0; iRoi != multiRoi.size(); ++iRoi) { - // property list - H5::DSetCreatPropList plist; - uint64_t fill_value = -1; - plist.setFillValue(dataType, &fill_value); - std::vector plistPara(paraSize); - // ignoring last fill (string) - for (unsigned int i = 0; i != plistPara.size() - 1; ++i) { - plistPara[i].setFillValue(parameterDataTypes[i], &fill_value); - } + auto currentRoi = multiRoi[iRoi]; + defs::xy detectorSize = {nPixelsX * numModX, nPixelsY * numModY}; + if (completeRoi) { + currentRoi = + defs::ROI{0, detectorSize.x - 1, 0, detectorSize.y - 1}; + } + if (multiRoi[iRoi].completeRoi() && iRoi != 0) + throw RuntimeError( + "Cannot have complete roi and multiple rois"); - // hyperslab (files) - int numFiles = numImagesCaught / maxFramesPerFile; - if (numImagesCaught % maxFramesPerFile) - ++numFiles; - uint64_t framesSaved = 0; - for (int iFile = 0; iFile < numFiles; ++iFile) { - - uint64_t nDimx = - ((numImagesCaught - framesSaved) > maxFramesPerFile) - ? maxFramesPerFile - : (numImagesCaught - framesSaved); - - hsize_t startLocation[DATA_RANK] = {framesSaved, 0, 0}; - hsize_t strideBetweenBlocks[DATA_RANK] = {1, 1, 1}; - hsize_t numBlocks[DATA_RANK] = {nDimx, nDimy, nDimz}; - hsize_t blockSize[DATA_RANK] = {1, 1, 1}; - - hsize_t startLocationPara[VDS_PARA_RANK] = {framesSaved, 0}; - hsize_t strideBetweenBlocksPara[VDS_PARA_RANK] = {1, 1}; - hsize_t numBlocksPara[VDS_PARA_RANK] = {nDimx, 1}; - hsize_t blockSizePara[VDS_PARA_RANK] = {1, 1}; - - // interleaving for g2 - if (gotthard25um) { - strideBetweenBlocks[2] = 2; + // get detector shape and number of ports in roi + defs::xy portSize{nPixelsX, nPixelsY}; + uint32_t nTotalPorts = numModX * numModY; + hsize_t roiWidth = detectorSize.x; + hsize_t roiHeight = detectorSize.y; + hsize_t nPortsInRoi = nTotalPorts; + if (!completeRoi) { + roiWidth = multiRoi[iRoi].width(); + roiHeight = multiRoi[iRoi].height(); + nPortsInRoi = GetNumPortsInRoi(multiRoi[iRoi], portSize); } - for (unsigned int iReadout = 0; iReadout < numModY * numModZ; - ++iReadout) { + // dataspace + uint64_t nImages = numImagesCaught; + int numFiles = numImagesCaught / maxFramesPerFile; + if (numImagesCaught % maxFramesPerFile) + ++numFiles; - // interleaving for g2 (startLocation is 0 and 1) + hsize_t vdsDims[DATA_RANK] = {nImages, roiHeight, roiWidth}; + hsize_t vdsDimsPara[VDS_PARA_RANK] = {nImages, nPortsInRoi}; + H5::DataSpace vdsDataSpace(DATA_RANK, vdsDims, nullptr); + H5::DataSpace vdsDataSpacePara(VDS_PARA_RANK, vdsDimsPara, nullptr); + + // property list + H5::DSetCreatPropList plist; + uint64_t fill_value = -1; + plist.setFillValue(dataType, &fill_value); + std::vector plistPara(paraSize); + // ignoring last fill (string) + for (unsigned int i = 0; i != plistPara.size() - 1; ++i) { + plistPara[i].setFillValue(parameterDataTypes[i], &fill_value); + } + + // hyperslab (files) + uint64_t framesSaved = 0; + for (int iFile = 0; iFile != numFiles; ++iFile) { + + // images in src file + uint64_t nSrcFileImages = numImagesCaught - framesSaved; + if ((numImagesCaught - framesSaved) > maxFramesPerFile) + nSrcFileImages = maxFramesPerFile; + + hsize_t strideBetweenBlocks[DATA_RANK] = {1, 1, 1}; + hsize_t numBlocks[DATA_RANK] = {1, 1, 1}; + hsize_t strideBetweenBlocksPara[VDS_PARA_RANK] = {1, 1}; + hsize_t numBlocksPara[VDS_PARA_RANK] = {1, 1}; + hsize_t blockSizePara[VDS_PARA_RANK] = {nSrcFileImages, 1}; + + // following recalculated for every readout + hsize_t blockSize[DATA_RANK] = {nSrcFileImages, + static_cast(nPixelsY), + static_cast(nPixelsX)}; + hsize_t startLocation[DATA_RANK] = {framesSaved, 0, 0}; + hsize_t startLocationPara[VDS_PARA_RANK] = {framesSaved, 0}; + + // interleaving for g2 if (gotthard25um) { - startLocation[2] = iReadout; + strideBetweenBlocks[2] = 2; } - vdsDataSpace.selectHyperslab(H5S_SELECT_SET, numBlocks, - startLocation, strideBetweenBlocks, - blockSize); + for (unsigned int iReadout = 0; iReadout < nTotalPorts; + ++iReadout) { + auto globalPortRoi = + GetGlobalPortRoi(iReadout, portSize, numModY); + if (!globalPortRoi.overlap(currentRoi)) + continue; - vdsDataSpacePara.selectHyperslab( - H5S_SELECT_SET, numBlocksPara, startLocationPara, - strideBetweenBlocksPara, blockSizePara); + // calculate start location (special for roi) + int xmin = std::max(currentRoi.xmin, globalPortRoi.xmin); + int xmax = std::min(currentRoi.xmax, globalPortRoi.xmax); + int ymin = std::max(currentRoi.ymin, globalPortRoi.ymin); + int ymax = std::min(currentRoi.ymax, globalPortRoi.ymax); + hsize_t portRoiHeight = ymax - ymin + 1; + hsize_t portRoiWidth = xmax - xmin + 1; - // source file name - std::ostringstream os; - os << filePath << "/" << fileNamePrefix << "_d" - << (modulePos * numUnitsPerReadout + iReadout) << "_f" - << iFile << '_' << fileIndex << ".h5"; - std::string srcFileName = os.str(); - LOG(logDEBUG1) << srcFileName; - - // find relative path - std::string relative_srcFileName = srcFileName; - { - size_t p = srcFileName.rfind('/', srcFileName.length()); - if (p != std::string::npos) - relative_srcFileName = (srcFileName.substr( - p + 1, srcFileName.length() - p)); - } - - // source dataspace - hsize_t srcDims[DATA_RANK] = {nDimx, nDimy, nDimz}; - hsize_t srcDimsMax[DATA_RANK] = {H5S_UNLIMITED, nDimy, nDimz}; - H5::DataSpace srcDataSpace(DATA_RANK, srcDims, srcDimsMax); - hsize_t srcDimsPara[PARA_RANK] = {nDimx}; - hsize_t srcDimsMaxPara[PARA_RANK] = {H5S_UNLIMITED}; - H5::DataSpace srcDataSpacePara(PARA_RANK, srcDimsPara, - srcDimsMaxPara); - // temporary fixfor corner case bug: - // (framescaught not multiple of framesperfile, - // virtual parameter datasets error loading (bad scalar value)) - if (nDimx != maxFramesPerFile) { - hsize_t count[1] = {nDimx}; - hsize_t start[1] = {0}; - srcDataSpacePara.selectHyperslab( - H5S_SELECT_SET, count, start, strideBetweenBlocksPara, - blockSizePara); - } - - // mapping of property list - plist.setVirtual(vdsDataSpace, relative_srcFileName.c_str(), - DATASET_NAME, srcDataSpace); - for (unsigned int p = 0; p < paraSize; ++p) { - plistPara[p].setVirtual( - vdsDataSpacePara, relative_srcFileName.c_str(), - parameterNames[p].c_str(), srcDataSpacePara); - } - - // H5Sclose(srcDataspace); - // H5Sclose(srcDataspace_para); - - if (!gotthard25um) { - startLocation[2] += nDimz; - if (startLocation[2] >= (numModZ * nDimz)) { - startLocation[2] = 0; - startLocation[1] += nDimy; + // recalculating start location and block size + if (!gotthard25um) { + startLocation[1] = ymin - currentRoi.ymin; + startLocation[2] = xmin - currentRoi.xmin; + blockSize[1] = portRoiHeight; + blockSize[2] = portRoiWidth; + } + // interleaving for g2 (startLocation is 0 and 1) (g2 had no + // roi) + else { + ++startLocation[2]; } - } - ++startLocationPara[1]; - } - framesSaved += nDimx; - } - // datasets - H5::DataSet vdsDataSet( - fd->createDataSet(DATASET_NAME, dataType, vdsDataSpace, plist)); - for (unsigned int p = 0; p < paraSize; ++p) { - H5::DataSet vdsDataSetPara(fd->createDataSet( - parameterNames[p].c_str(), parameterDataTypes[p], - vdsDataSpacePara, plistPara[p])); + vdsDataSpace.selectHyperslab( + H5S_SELECT_SET, numBlocks, startLocation, + strideBetweenBlocks, blockSize); + + vdsDataSpacePara.selectHyperslab( + H5S_SELECT_SET, numBlocksPara, startLocationPara, + strideBetweenBlocksPara, blockSizePara); + + // source file name + std::ostringstream os; + os << filePath << "/" << fileNamePrefix << "_d" + << (modulePos * numUnitsPerReadout + iReadout) << "_f" + << iFile << '_' << fileIndex << ".h5"; + std::string srcFileName = os.str(); + LOG(logDEBUG1) << srcFileName; + + // find relative path + std::string relative_srcFileName = srcFileName; + { + size_t p = srcFileName.rfind('/', srcFileName.length()); + if (p != std::string::npos) + relative_srcFileName = (srcFileName.substr( + p + 1, srcFileName.length() - p)); + } + + // source dataspace + hsize_t srcDims[DATA_RANK] = {nSrcFileImages, portRoiHeight, + portRoiWidth}; + hsize_t srcDimsMax[DATA_RANK] = { + H5S_UNLIMITED, portRoiHeight, portRoiWidth}; + H5::DataSpace srcDataSpace(DATA_RANK, srcDims, srcDimsMax); + hsize_t srcDimsPara[PARA_RANK] = {nSrcFileImages}; + hsize_t srcDimsMaxPara[PARA_RANK] = {H5S_UNLIMITED}; + H5::DataSpace srcDataSpacePara(PARA_RANK, srcDimsPara, + srcDimsMaxPara); + + // mapping of property list + plist.setVirtual(vdsDataSpace, relative_srcFileName.c_str(), + DATASET_NAME, srcDataSpace); + for (unsigned int p = 0; p < paraSize; ++p) { + plistPara[p].setVirtual( + vdsDataSpacePara, relative_srcFileName.c_str(), + parameterNames[p].c_str(), srcDataSpacePara); + } + + // map next readout + ++startLocationPara[1]; + } + framesSaved += nSrcFileImages; + } + // datasets + std::string datasetname = std::string(DATASET_NAME); + // suffix '_[iRoi]' for multiple rois + if (multiRoi.size() > 1) + datasetname += ('_' + std::to_string(iRoi)); + H5::DataSet vdsDataSet( + fd->createDataSet(datasetname, dataType, vdsDataSpace, plist)); + for (unsigned int p = 0; p < paraSize; ++p) { + std::string parameterDsetName = parameterNames[p]; + // suffix '_[iRoi]' for multiple rois + if (multiRoi.size() > 1) + parameterDsetName += ('_' + std::to_string(iRoi)); + H5::DataSet vdsDataSetPara(fd->createDataSet( + parameterDsetName.c_str(), parameterDataTypes[p], + vdsDataSpacePara, plistPara[p])); + } } fd->close(); diff --git a/slsReceiverSoftware/src/MasterFileUtility.h b/slsReceiverSoftware/src/MasterFileUtility.h index ec7494c1d..cbf68be39 100644 --- a/slsReceiverSoftware/src/MasterFileUtility.h +++ b/slsReceiverSoftware/src/MasterFileUtility.h @@ -21,7 +21,8 @@ std::string CreateMasterBinaryFile(const std::string &filePath, void LinkHDF5FileInMaster(std::string &masterFileName, std::string &dataFilename, std::vector parameterNames, - const bool silentMode, std::mutex *hdf5LibMutex); + const bool silentMode, std::mutex *hdf5LibMutex, + size_t multiRoiSize); std::string CreateMasterHDF5File(const std::string &filePath, const std::string &fileNamePrefix, @@ -29,17 +30,21 @@ std::string CreateMasterHDF5File(const std::string &filePath, const bool overWriteEnable, const bool silentMode, MasterAttributes *attr, std::mutex *hdf5LibMutex); +defs::ROI GetGlobalPortRoi(const int iPort, const defs::xy portSize, + const int numPortsY); +int GetNumPortsInRoi(const defs::ROI roi, const defs::xy portSize); std::string CreateVirtualHDF5File( const std::string &filePath, const std::string &fileNamePrefix, const uint64_t fileIndex, const bool overWriteEnable, const bool silentMode, const int modulePos, const int numUnitsPerReadout, - const uint32_t maxFramesPerFile, const uint32_t nPixelsX, - const uint32_t nPixelsY, const uint32_t dynamicRange, - const uint64_t numImagesCaught, const int numModX, const int numModY, - const H5::DataType dataType, const std::vector parameterNames, + const uint32_t maxFramesPerFile, const int nPixelsX, const int nPixelsY, + const uint32_t dynamicRange, const uint64_t numImagesCaught, + const int numModX, const int numModY, const H5::DataType dataType, + const std::vector parameterNames, const std::vector parameterDataTypes, - std::mutex *hdf5LibMutex, bool gotthard25um); + std::mutex *hdf5LibMutex, bool gotthard25um, + std::vector multiRoi); #endif } // namespace masterFileUtility diff --git a/slsReceiverSoftware/src/receiver_defs.h b/slsReceiverSoftware/src/receiver_defs.h index ade363e79..0d38ba749 100644 --- a/slsReceiverSoftware/src/receiver_defs.h +++ b/slsReceiverSoftware/src/receiver_defs.h @@ -19,8 +19,8 @@ namespace sls { // files // versions -#define HDF5_WRITER_VERSION (6.7) // 1 decimal places -#define BINARY_WRITER_VERSION (7.3) // 1 decimal places +#define HDF5_WRITER_VERSION (7.0) // 1 decimal places +#define BINARY_WRITER_VERSION (8.0) // 1 decimal places #define MAX_FRAMES_PER_FILE 20000 #define SHORT_MAX_FRAMES_PER_FILE 100000 diff --git a/slsSupportLib/include/sls/sls_detector_defs.h b/slsSupportLib/include/sls/sls_detector_defs.h index 9a010ba4c..942774bbf 100644 --- a/slsSupportLib/include/sls/sls_detector_defs.h +++ b/slsSupportLib/include/sls/sls_detector_defs.h @@ -230,6 +230,8 @@ class slsDetectorDefs { ROI(int xmin, int xmax) : xmin(xmin), xmax(xmax){}; ROI(int xmin, int xmax, int ymin, int ymax) : xmin(xmin), xmax(xmax), ymin(ymin), ymax(ymax){}; + constexpr int width() const { return (xmax - xmin + 1); } + constexpr int height() const { return (ymax - ymin + 1); } constexpr std::array getIntArray() const { return std::array({xmin, xmax, ymin, ymax}); } @@ -237,7 +239,8 @@ class slsDetectorDefs { return (xmin == -1 && xmax == -1 && ymin == -1 && ymax == -1); } constexpr bool noRoi() const { - return (xmin == 0 && xmax == 0 && ymin == 0 && ymax == 0); + return ((xmin == 0 && xmax == 0) && + ((ymin == 0 && ymax == 0) || (ymin == -1 && ymax == -1))); } void setNoRoi() { xmin = 0; @@ -245,6 +248,10 @@ class slsDetectorDefs { ymin = 0; ymax = 0; } + constexpr bool overlap(const ROI &other) const { + return ((xmin <= other.xmax && xmax >= other.xmin) && + (ymin <= other.ymax && ymax >= other.ymin)); + } constexpr bool operator==(const ROI &other) const { return ((xmin == other.xmin) && (xmax == other.xmax) && (ymin == other.ymin) && (ymax == other.ymax)); diff --git a/slsSupportLib/include/sls/sls_detector_funcs.h b/slsSupportLib/include/sls/sls_detector_funcs.h index ec1c6d2f1..20688941c 100755 --- a/slsSupportLib/include/sls/sls_detector_funcs.h +++ b/slsSupportLib/include/sls/sls_detector_funcs.h @@ -412,6 +412,7 @@ enum detFuncs { F_RECEIVER_SET_COLUMN, F_GET_RECEIVER_DBIT_REORDER, F_SET_RECEIVER_DBIT_REORDER, + F_RECEIVER_GET_ROI_METADATA, NUM_REC_FUNCTIONS }; @@ -820,6 +821,7 @@ const char* getFunctionNameFromEnum(enum detFuncs func) { case F_RECEIVER_SET_COLUMN: return "F_RECEIVER_SET_COLUMN"; case F_GET_RECEIVER_DBIT_REORDER: return "F_GET_RECEIVER_DBIT_REORDER"; case F_SET_RECEIVER_DBIT_REORDER: return "F_SET_RECEIVER_DBIT_REORDER"; + case F_RECEIVER_GET_ROI_METADATA: return "F_RECEIVER_GET_ROI_METADATA"; case NUM_REC_FUNCTIONS: return "NUM_REC_FUNCTIONS"; default: return "Unknown Function"; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4811f1798..e585c90ab 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -62,3 +62,4 @@ catch_discover_tests(tests) configure_file(scripts/test_simulators.py ${CMAKE_BINARY_DIR}/bin/test_simulators.py COPYONLY) configure_file(scripts/test_frame_synchronizer.py ${CMAKE_BINARY_DIR}/bin/test_frame_synchronizer.py COPYONLY) configure_file(scripts/utils_for_test.py ${CMAKE_BINARY_DIR}/bin/utils_for_test.py COPYONLY) +configure_file(scripts/test_roi.py ${CMAKE_BINARY_DIR}/bin/test_roi.py COPYONLY) diff --git a/tests/scripts/test_roi.py b/tests/scripts/test_roi.py new file mode 100644 index 000000000..2f56f31b1 --- /dev/null +++ b/tests/scripts/test_roi.py @@ -0,0 +1,116 @@ +# SPDX-License-Identifier: LGPL-3.0-or-other +# Copyright (C) 2021 Contributors to the SLS Detector Package +''' +This file is used to start up simulators, receivers and test roi for every detector in many configurations. +''' + +import sys, time +import traceback + +from slsdet import Detector +from slsdet.defines import DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO + + +from utils_for_test import ( + Log, + LogLevel, + RuntimeException, + cleanup, + startProcessInBackground, + startDetectorVirtualServer, + connectToVirtualServers, + runProcessWithLogFile +) + +LOG_PREFIX_FNAME = '/tmp/slsDetectorPackage_virtual_roi_test' +MAIN_LOG_FNAME = LOG_PREFIX_FNAME + '_log.txt' +ROI_TEST_FNAME = LOG_PREFIX_FNAME + '_results_' + +def startReceiver(num_mods, fp): + if num_mods == 1: + cmd = ['slsReceiver'] + else: + cmd = ['slsMultiReceiver', str(DEFAULT_TCP_RX_PORTNO), str(num_mods)] + # in 10.0.0 + #cmd = ['slsMultiReceiver', '-p', str(DEFAULT_TCP_RX_PORTNO), '-n', str(num_mods)] + startProcessInBackground(cmd, fp) + time.sleep(1) + + +def loadConfigForRoi(name, fp, num_mods = 1, num_interfaces = 1): + Log(LogLevel.INFO, 'Loading config') + Log(LogLevel.INFO, 'Loading config', fp) + try: + d = connectToVirtualServers(name, num_mods) + + if name == 'jungfrau' or name == 'moench': + d.numinterfaces = num_interfaces + + d.udp_dstport = DEFAULT_UDP_DST_PORTNO + if name == 'eiger' or name == 'jungfrau' or name == 'moench': + d.udp_dstport2 = DEFAULT_UDP_DST_PORTNO + 1 + + d.rx_hostname = 'localhost' + d.udp_dstip = 'auto' + if name != "eiger": + d.udp_srcip = 'auto' + if name == 'jungfrau' or name == 'moench': + d.udp_dstip2 = 'auto' + d.powerchip = 1 + + d.frames = 5 + + except Exception as e: + raise RuntimeException(f'Could not load config for {name}. Error: {str(e)}') from e + + return d + +def startTestsForAll(fp): + servers = [ + 'eiger', + 'jungfrau', + 'mythen3', + 'gotthard2', + 'moench', + ] + nmods = 2 + for server in servers: + for ninterfaces in range(1, 2): + if ninterfaces == 2 and server != 'jungfrau' and server != 'moench': + continue + try: + msg = f'Starting Roi Tests for {server}' + if server == 'jungfrau' or server == 'moench': + msg += f' with {ninterfaces} interfaces' + Log(LogLevel.INFOBLUE, msg) + Log(LogLevel.INFOBLUE, msg, fp) + cleanup(fp) + startDetectorVirtualServer(server, nmods, fp) + startReceiver(nmods, fp) + d = loadConfigForRoi(name=server, fp=fp, num_mods=nmods, num_interfaces=ninterfaces) + + fname = ROI_TEST_FNAME + server + '.txt' + cmd = ['tests', 'rx_roi', '--abort', '-s'] + runProcessWithLogFile('Roi Tests for ' + server, cmd, fp, fname) + Log(LogLevel.INFO, '\n') + except Exception as e: + raise RuntimeException(f'Roi Tests failed') from e + + Log(LogLevel.INFOGREEN, 'Passed all Roi tests for all detectors \n' + str(servers)) + + +if __name__ == '__main__': + Log(LogLevel.INFOBLUE, '\nLog File: ' + MAIN_LOG_FNAME + '\n') + + with open(MAIN_LOG_FNAME, 'w') as fp: + try: + startTestsForAll(fp) + #TODO: check master file as well for both json and hdf5 as well + cleanup(fp) + except Exception as e: + with open(MAIN_LOG_FNAME, 'a') as fp_error: + traceback.print_exc(file=fp_error) + cleanup(fp) + Log(LogLevel.ERROR, f'Tests Failed.') + +