Merge branch 'developer' into dev/multirxr_proper_cleanup_on_ctrlc

This commit is contained in:
2025-07-11 10:56:00 +02:00
44 changed files with 1555 additions and 945 deletions

View File

@@ -21,6 +21,8 @@ class Caller {
UdpDestination getUdpEntry();
int GetLevelAndInsertIntoArgs(std::string levelSeparatedCommand);
void WrongNumberOfParameters(size_t expected);
std::vector<defs::ROI> parseRoiVector(const std::string &input);
defs::ROI parseRoi(const std::vector<std::string> &args);
template <typename V> std::string OutStringHex(const V &value) {
if (value.equal())

View File

@@ -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() {

View File

@@ -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() {

View File

@@ -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

View File

@@ -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

View File

@@ -119,6 +119,10 @@ class Detector {
Result<defs::xy> getModuleSize(Positions pos = {}) const;
defs::xy getPortPerModuleGeometry() const;
Result<defs::xy> 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<defs::ROI> 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<defs::ROI> 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<defs::ROI> &args);
void clearRxROI();

View File

@@ -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<int, 4> 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<int, 4> rxRoi{{-1, -1, -1, -1}};
};
} // namespace sls

View File

@@ -21,6 +21,8 @@ class Caller {
UdpDestination getUdpEntry();
int GetLevelAndInsertIntoArgs(std::string levelSeparatedCommand);
void WrongNumberOfParameters(size_t expected);
std::vector<defs::ROI> parseRoiVector(const std::string &input);
defs::ROI parseRoi(const std::vector<std::string> &args);
template <typename V> std::string OutStringHex(const V &value) {
if (value.equal())

View File

@@ -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<int>{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<int>(args[0]);
t.xmax = StringTo<int>(args[1]);
}
if (args.size() == 4) {
t.ymin = StringTo<int>(args[2]);
t.ymax = StringTo<int>(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<defs::ROI> 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<defs::ROI> Caller::parseRoiVector(const std::string &input) {
std::vector<defs::ROI> 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<std::string> 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<std::string> &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<int>(parts[0]);
roi.xmax = StringTo<int>(parts[1]);
if (parts.size() == 4) {
roi.ymin = StringTo<int>(parts[2]);
roi.ymax = StringTo<int>(parts[3]);
}
return roi;
}
std::string Caller::ratecorr(int action) {
std::ostringstream os;
if (action == defs::HELP_ACTION) {

View File

@@ -201,6 +201,26 @@ Result<defs::xy> Detector::getModuleSize(Positions pos) const {
return pimpl->Parallel(&Module::getNumberOfChannels, pos);
}
defs::xy Detector::getPortPerModuleGeometry() const {
return pimpl->getPortGeometry();
}
Result<defs::xy> Detector::getPortSize(Positions pos) const {
Result<defs::xy> 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<defs::ROI> Detector::getIndividualRxROIs(Positions pos) const {
return pimpl->Parallel(&Module::getRxROI, pos);
std::vector<defs::ROI> 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<defs::ROI> &args) {
pimpl->setRxROI(args);
}
void Detector::clearRxROI() { pimpl->clearRxROI(); }

View File

@@ -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<int, 4> rxRoi = shm()->rx_roi.getIntArray();
std::vector<bool> runningList(zmqSocket.size());
std::vector<bool> 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<int> positions) const {
// port for given positions
@@ -1696,7 +1666,7 @@ void DetectorImpl::verifyUniqueHost(
}
}
defs::ROI DetectorImpl::getRxROI() const {
std::vector<defs::ROI> 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<defs::ROI> &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<defs::ROI> &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<defs::ROI> &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<defs::ROI> 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<defs::ROI>(nPortsPerModule));
}
modules[0]->setRxROIMetadata(std::vector<defs::ROI>(1));
}
void DetectorImpl::getBadChannels(const std::string &fname,

View File

@@ -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<std::pair<std::string, uint16_t>>
verifyUniqueRxHost(const std::vector<std::string> &names) const;
defs::ROI getRxROI() const;
void setRxROI(const defs::ROI arg);
defs::xy getPortGeometry() const;
std::vector<defs::ROI> getRxROI(int module_id = -1) const;
void setRxROI(const std::vector<defs::ROI> &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<std::pair<std::string, uint16_t>> &hosts) const;
bool roisOverlap(const defs::ROI &a, const defs::ROI &b) const;
void validateROIs(const std::vector<defs::ROI> &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<defs::ROI> &portRois) const;
const int detectorIndex{0};
SharedMemory<sharedDetector> shm{0, -1};
SharedMemory<CtbConfig> ctb_shm{0, -1, CtbConfig::shm_tag()};

View File

@@ -1521,17 +1521,94 @@ void Module::setRxArping(bool enable) {
sendToReceiver(F_SET_RECEIVER_ARPING, static_cast<int>(enable), nullptr);
}
defs::ROI Module::getRxROI() const {
return sendToReceiver<slsDetectorDefs::ROI>(F_RECEIVER_GET_RECEIVER_ROI);
std::vector<defs::ROI> 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<int>();
std::vector<ROI> 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<defs::ROI> &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<int>(portRois.size());
client.Send(size);
if (size > 0)
client.Send(portRois);
if (client.Receive<int>() == 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<slsDetectorDefs::ROI> 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<int>();
std::vector<slsDetectorDefs::ROI> 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<slsDetectorDefs::ROI> &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<int>(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<int>() == FAIL) {
throw ReceiverError("Receiver " + std::to_string(moduleIndex) +
" returned error: " + receiver.readErrorMessage());
}
}
// File

View File

@@ -301,9 +301,10 @@ class Module : public virtual slsDetectorDefs {
std::array<pid_t, NUM_RX_THREAD_IDS> 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<defs::ROI> getRxROI() const;
void setRxROI(const std::vector<slsDetectorDefs::ROI> &portRois);
void setRxROIMetadata(const std::vector<slsDetectorDefs::ROI> &args);
std::vector<slsDetectorDefs::ROI> getRxROIMetadata() const;
/**************************************************
* *

View File

@@ -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() {

View File

@@ -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
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../src>"

View File

@@ -7,6 +7,7 @@
#include "sls/sls_detector_defs.h"
#include "test-Caller-global.h"
#include <filesystem>
#include <sstream>
#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);
}
}
}