Compare commits

..

2 Commits

Author SHA1 Message Date
5093472aa1 WIP 2025-05-30 16:57:16 +02:00
e14d307a42 tests for bool in ToString/StringTo 2025-05-30 15:58:26 +02:00
32 changed files with 337 additions and 875 deletions

View File

@ -1,64 +0,0 @@
name: Build wheel
on:
workflow_dispatch:
pull_request:
push:
branches:
- main
release:
types:
- published
jobs:
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest,]
steps:
- uses: actions/checkout@v4
- name: Build wheels
run: pipx run cibuildwheel==2.23.0
- uses: actions/upload-artifact@v4
with:
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
path: ./wheelhouse/*.whl
build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build sdist
run: pipx run build --sdist
- uses: actions/upload-artifact@v4
with:
name: cibw-sdist
path: dist/*.tar.gz
upload_pypi:
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
if: github.event_name == 'release' && github.event.action == 'published'
# or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this)
# if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
steps:
- uses: actions/download-artifact@v4
with:
# unpacks all CIBW artifacts into dist/
pattern: cibw-*
path: dist
merge-multiple: true
- uses: pypa/gh-action-pypi-publish@release/v1

View File

@ -42,7 +42,6 @@ set(SPHINX_SOURCE_FILES
src/pyexamples.rst
src/pyPatternGenerator.rst
src/servers.rst
src/multidet.rst
src/receiver_api.rst
src/result.rst
src/type_traits.rst

View File

@ -6,8 +6,6 @@ Usage
The syntax is *'[detector index]-[module index]:[command]'*, where the indices are by default '0', when not specified.
.. _cl-module-index-label:
Module index
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Modules are indexed based on their order in the hostname command. They are used to configure a specific module within a detector and are followed by a ':' in syntax.

View File

@ -28,12 +28,6 @@ Welcome to slsDetectorPackage's documentation!
receiver_api
examples
.. toctree::
:caption: how to
:maxdepth: 2
multidet
.. toctree::
:caption: Python API
:maxdepth: 2

View File

@ -1,228 +0,0 @@
Using multiple detectors
==========================
The slsDetectorPackage supports using several detectors on the same computer.
This can either be two users, that need to use the same computer without interfering
with each other, or the same user that wants to use multiple detectors at the same time.
The detectors in turn can consist of multiple modules. For example, a 9M Jungfrau detector
consists of 18 modules which typically are addressed at once as a single detector.
.. note ::
To address a single module of a multi-module detector you can use the module index.
- Command line: :ref:`cl-module-index-label`
- Python: :ref:`py-module-index-label`
Coming back to multiple detectors we have two tools to our disposal:
#. Detector index
#. The SLSDETNAME environment variable
They can be used together or separately depending on the use case.
Detector index
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When configuring a detector you can specify a detector index. The default is 0.
**Command line**
.. code-block:: bash
# Given that we have two detectors (my-det and my-det2) that we want to use,
# we can configure them with different indices.
# Configure the first detector with index 0
$ sls_detector_put hostname my-det
# Set number of frames for detector 0 to 10
$ sls_detector_put frames 10
#
#Configure the second detector with index 1 (notice the 1- before hostname)
$ sls_detector_put 1-hostname my-det2
# Further configuration
...
# Set number of frames for detector 1 to 19
$ sls_detector_put 1-frames 19
# Note that if we call sls_detector_get without specifying the index,
# it will return the configuration of detector 0
$ sls_detector_get frames
10
The detector index is added to the name of the shared memory segment, so in this case
the shared memory segments would be:
.. code-block:: bash
#First detector
/dev/shm/slsDetectorPackage_detector_0
/dev/shm/slsDetectorPackage_detector_0_module_0
#Second detector
/dev/shm/slsDetectorPackage_detector_1
/dev/shm/slsDetectorPackage_detector_1_module_0
**Python**
The main difference between the command line and the Python API is that you set the index
when you create the detector object and you don't have to repeat it for every call.
The C++ API works int the same way.
.. code-block:: python
from slsdet import Detector
# The same can be achieved in Python by creating a detector object with an index.
# Again we have two detectors (my-det and my-det2) that we want to use:
# Configure detector with index 0
d = Detector()
# If the detector has already been configured and has a shared memory
# segment, you can omit setting the hostname again
d.hostname = 'my-det'
#Further configuration
...
# Configure a second detector with index 1
d2 = Detector(1)
d2.hostname = 'my-det2'
d.frames = 10
d2.frames = 19
$SLSDETNAME
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To avoid interfering with other users on shared PCs it is best to always set the SLSDETNAME environmental variable.
Imagining a fictive user: Anna, we can set SLSDETNAME from the shell before configuring the detector:
**Command line**
.. code-block:: bash
# Set the SLSDETNAME variable
$ export SLSDETNAME=Anna
# You can check that it is set
$ echo $SLSDETNAME
Anna
# Now configures a detector with index 0 and prefixed with the name Anna
# /dev/shm/slsDetectorPackage_detector_0_Anna
$ sls_detector_put hostname my-det
.. tip ::
Set SLSDETNAME in your .bashrc in order to not forget it when opening a new terminal.
**Python**
With python the best way is to set the SLSDETNAME from the command line before starting the python interpreter.
Bash:
.. code-block:: bash
$ export SLSDETNAME=Anna
Python:
.. code-block:: python
from slsdet import Detector
# Now configures a detector with index 0 and prefixed with the name Anna
# /dev/shm/slsDetectorPackage_detector_0_Anna
d = Detector()
d.hostname = 'my-det'
You can also set SLSDETNAME from within the Python interpreter, but you have to be aware that it will only
affect the current process and not the whole shell session.
.. code-block:: python
import os
os.environ['SLSDETNAME'] = 'Anna'
# You can check that it is set
print(os.environ['SLSDETNAME']) # Output: Anna
#Now SLSDETNAME is set to Anna but as soon as you exit the python interpreter
# it will not be set anymore
.. note ::
Python has two ways of reading environment variables: `**os.environ**` as shown above which throws a
KeyError if the variable is not set and `os.getenv('SLSDETNAME')` which returns None if the variable is not set.
For more details see the official python documentation on: https://docs.python.org/3/library/os.html#os.environ
Checking for other detectors
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If using shared accounts on a shared computer (which you anyway should not do), it is good practice to check
if there are other detectors configured by other users before configuring your own detector.
You can do this by listing the files in the shared memory directory `/dev/shm/` that start with `sls`. In this
example we can see that two single module detectors are configured one with index 0 and one with index 1.
SLSDETNAME is set to `Anna` so it makes sense to assume that she is the user that configured these detectors.
.. code-block:: bash
# List the files in /dev/shm that starts with sls
$ ls /dev/shm/sls*
/dev/shm/slsDetectorPackage_detector_0_Anna
/dev/shm/slsDetectorPackage_detector_0_module_0_Anna
/dev/shm/slsDetectorPackage_detector_1_Anna
/dev/shm/slsDetectorPackage_detector_1_module_0_Anna
We also provide a command: user, which gets information about the shared memory segment that
the client points to without doing any changes.
.. code-block:: bash
#in this case 3 simulated Mythen3 modules
$ sls_detector_get user
user
Hostname: localhost+localhost+localhost+
Type: Mythen3
PID: 1226078
User: l_msdetect
Date: Mon Jun 2 05:46:20 PM CEST 2025
Other considerations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The shared memory is not the only way to interfere with other users. You also need to make sure that you are not
using the same:
* rx_tcpport
* Unique combination of udp_dstip and udp_dstport
* rx_zmqport
* zmqport
.. attention ::
The computer that you are using need to have enough resources to run multiple detectors at the same time.
This includes CPU and network bandwidth. Please coordinate with the other users!

View File

@ -123,47 +123,6 @@ in a large detector.
# Set exposure time for module 1, 5 and 7
d.setExptime(0.1, [1,5,7])
.. _py-module-index-label:
----------------------------------
Accessing individual modules
----------------------------------
Using the C++ like API you can access individual modules in a large detector
by passing in the module index as an argument to the function.
::
# Read the UDP destination port for all modules
>>> d.getDestinationUDPPort()
[50001, 50002, 50003]
# Read it for module 0 and 1
>>> d.getDestinationUDPPort([0, 1])
[50001, 50002]
>>> d.setDestinationUDPPort(50010, 1)
>>> d.getDestinationUDPPort()
[50001, 50010, 50003]
From the more pythonic API there is no way to read from only one module but you can read
and then use list slicing to get the values for the modules you are interested in.
::
>>> d.udp_dstport
[50001, 50010, 50003]
>>> d.udp_dstport[0]
50001
#For some but not all properties you can also pass in a dictionary with module index as key
>>> ip = IpAddr('127.0.0.1')
>>> d.udp_dstip = {1:ip}
--------------------
Finding functions
--------------------

View File

@ -14,4 +14,4 @@ slsDetectorPackage/8.0.2_rh8 stable cmake/3.15.5 Qt/5.12.10
slsDetectorPackage/9.0.0_rh8 stable cmake/3.15.5 Qt/5.12.10
slsDetectorPackage/9.1.0_rh8 stable cmake/3.15.5 Qt/5.12.10
slsDetectorPackage/9.1.1_rh8 stable cmake/3.15.5 Qt/5.12.10
slsDetectorPackage/9.2.0_rh8 stable cmake/3.15.5 Qt/5.12.10

View File

@ -924,6 +924,17 @@ void init_det(py::module &m) {
(void (Detector::*)(bool, sls::Positions)) &
Detector::setRxArping,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def("getIndividualRxROIs",
(Result<defs::ROI>(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());
CppDetectorApi.def("clearRxROI",
(void (Detector::*)()) & Detector::clearRxROI);
CppDetectorApi.def(
"getFileFormat",
(Result<defs::fileFormat>(Detector::*)(sls::Positions) const) &

View File

@ -29,7 +29,6 @@ target_link_libraries(slsDetectorObject
slsProjectOptions
slsSupportStatic
pthread
rt
PRIVATE
slsProjectWarnings
)
@ -98,8 +97,7 @@ if(SLS_USE_TEXTCLIENT)
add_executable(${val1} src/CmdApp.cpp)
target_link_libraries(${val1}
slsDetectorStatic
pthread
slsDetectorStatic
)
SET_SOURCE_FILES_PROPERTIES( src/Caller.cpp PROPERTIES COMPILE_FLAGS "-Wno-unused-variable -Wno-unused-but-set-variable")

View File

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

View File

@ -2598,7 +2598,11 @@ rx_roi:
GET:
argc: 0
PUT:
argc: -1
args:
- argc: 2
arg_types: [ int, int ]
- argc: 4
arg_types: [ int, int, int, int ]
ratecorr:
is_description: true

View File

@ -119,10 +119,6 @@ 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;
@ -989,14 +985,13 @@ class Detector {
* every minute. Useful in 10G mode. */
void setRxArping(bool value, Positions pos = {});
/** Returns multi level ROIs */
std::vector<defs::ROI> getRxROI() const;
/** at module level */
Result<defs::ROI> getIndividualRxROIs(Positions pos) const;
/** Returns port level ROIs. Max 2 ports and hence max 2 elements per readout */
Result<std::array<defs::ROI, 2>> getRxROI(int module_id) const;
defs::ROI getRxROI() const;
/** only at multi module level without gap pixels. At most, 1 ROI per UDP port */
void setRxROI(const std::vector<defs::ROI> &args);
/** only at multi module level without gap pixels */
void setRxROI(const defs::ROI value);
void clearRxROI();

View File

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

View File

@ -719,112 +719,49 @@ 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\tRegion of interest in receiver.\n\t")
+ "For a list of rois, use '[' and ']; ' to distinguish between "
"rois and use comma inside the square brackets.\n\t If one fails to use space after semicolon, please use quotes"
+ "For example: [0,100,0,100]; [200,300,0,100] will set two "
"rois.or '[0,100,0,100];[200,300,0,100]' when the vector is a single string\n\t"
+ "Only allowed at multi module level and without gap "
"pixels.\n";
if (action == defs::HELP_ACTION) {
os << helpMessage;
os << "[xmin] [xmax] [ymin] [ymax]\n\tRegion of interest in "
"receiver.\n\tOnly allowed at multi module level and without gap "
"pixels."
<< '\n';
} else if (action == defs::GET_ACTION) {
if (!args.empty()) {
WrongNumberOfParameters(0);
}
if (det_id != -1) {
throw RuntimeError("Cannot execute receiver ROI at module level");
} else {
if (det_id == -1) {
auto t = det->getRxROI();
os << ToString(t) << '\n';
os << t << '\n';
} else {
auto t = det->getIndividualRxROIs(std::vector<int>{det_id});
os << t << '\n';
}
} else if (action == defs::PUT_ACTION) {
std::vector<defs::ROI> rois;
// Support multiple args with bracketed ROIs, or single arg with
// semicolon-separated vector
bool isVectorInput =
std::all_of(args.begin(), args.end(), [](const std::string &a) {
return a.find('[') != std::string::npos &&
a.find(']') != std::string::npos;
});
try {
// previous format: 2 or 4 separate args
if ((args.size() == 2 || args.size() == 4) && !isVectorInput) {
defs::ROI t;
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]);
}
rois.emplace_back(t);
} else {
if (!isVectorInput)
WrongNumberOfParameters(2);
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: " + helpMessage);
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");
}
det->setRxROI(rois);
os << ToString(rois) << '\n';
det->setRxROI(t);
os << t << '\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, ';')) {
token.erase(std::remove_if(token.begin(), token.end(), ::isspace),
token.end());
if (token.empty())
continue;
if (token.front() != '[' || token.back() != ']') {
throw RuntimeError("Each ROI must be enclosed in square brackets: "
"[xmin,xmax,ymin,ymax]");
}
token = token.substr(1, token.size() - 2); // remove brackets
std::vector<std::string> parts;
std::stringstream inner(token);
std::string num;
while (std::getline(inner, num, ',')) {
parts.push_back(num);
}
if (parts.size() != 4) {
throw RuntimeError("ROI must have 4 comma-separated integers");
}
defs::ROI roi;
roi.xmin = StringTo<int>(parts[0]);
roi.xmax = StringTo<int>(parts[1]);
roi.ymin = StringTo<int>(parts[2]);
roi.ymax = StringTo<int>(parts[3]);
rois.emplace_back(roi);
}
return rois;
}
std::string Caller::ratecorr(int action) {
std::ostringstream os;
if (action == defs::HELP_ACTION) {

View File

@ -201,22 +201,6 @@ 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();
for (auto &it : res) {
if (portGeometry.x == 2)
it.x /= 2;
if (portGeometry.y == 2)
it.y /= 2;
}
return res;
}
defs::xy Detector::getDetectorSize() const {
return pimpl->getNumberOfChannels();
}
@ -1383,22 +1367,16 @@ void Detector::setRxArping(bool value, Positions pos) {
pimpl->Parallel(&Module::setRxArping, pos, value);
}
std::vector<defs::ROI> Detector::getRxROI() const {
return pimpl->getRxROI();
Result<defs::ROI> Detector::getIndividualRxROIs(Positions pos) const {
return pimpl->Parallel(&Module::getRxROI, pos);
}
Result<std::array<defs::ROI, 2>> Detector::getRxROI(int module_id) const {
return pimpl->Parallel(&Module::getRxROI, {module_id});
}
defs::ROI Detector::getRxROI() const { return pimpl->getRxROI(); }
// RxROIs can be set for all types except CTB. At multi level without gap pixels
void Detector::setRxROI(const std::vector<defs::ROI> &args) {
pimpl->setRxROI(args);
}
void Detector::setRxROI(const defs::ROI value) { pimpl->setRxROI(value); }
void Detector::clearRxROI() { pimpl->clearRxROI(); }
// File
Result<defs::fileFormat> Detector::getFileFormat(Positions pos) const {

View File

@ -130,6 +130,10 @@ 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) {
@ -535,7 +539,7 @@ void DetectorImpl::readFrameFromReceiver() {
bool quadEnable = false;
// to flip image
bool eiger = false;
std::array<int, 4> rxRoi{}; // TODO: get roi from json header
std::array<int, 4> rxRoi = shm()->rx_roi.getIntArray();
std::vector<bool> runningList(zmqSocket.size());
std::vector<bool> connectList(zmqSocket.size());
@ -1544,6 +1548,31 @@ 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
@ -1667,8 +1696,7 @@ void DetectorImpl::verifyUniqueHost(
}
}
std::vector<defs::ROI> DetectorImpl::getRxROI() const {
defs::ROI DetectorImpl::getRxROI() const {
if (shm()->detType == CHIPTESTBOARD ||
shm()->detType == defs::XILINX_CHIPTESTBOARD) {
throw RuntimeError("RxRoi not implemented for this Detector");
@ -1676,169 +1704,75 @@ std::vector<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);
}
// return std::vector<defs::ROI>{};
return rxRoiTemp;
// TODO
}
defs::xy numChansPerMod = modules[0]->getNumberOfChannels();
bool is2D = (numChansPerMod.y > 1 ? true : false);
defs::xy geometry = getPortGeometry();
bool DetectorImpl::roisOverlap(const defs::ROI &a, const defs::ROI &b) const {
bool xOverlap = !(a.xmax < b.xmin || a.xmin > b.xmax);
bool yOverlap = !(a.ymax < b.ymin || a.ymin > b.ymax);
return xOverlap && yOverlap;
}
defs::ROI retval{};
for (size_t iModule = 0; iModule != modules.size(); ++iModule) {
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));
}
defs::ROI moduleRoi = modules[iModule]->getRxROI();
if (moduleRoi.noRoi()) {
LOG(logDEBUG) << iModule << ": no 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 (roisOverlap(rois[i], rois[j])) {
throw RuntimeError("Invalid Overlapping Rois.");
// 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;
}
defs::xy DetectorImpl::calculatePosition(size_t moduleIndex,
const defs::xy &geometry) const {
if ((geometry.x != 0 && geometry.x != 1) ||
(geometry.y != 0 && geometry.y != 1)) {
throw RuntimeError("Invalid geometry configuration. Geometry: " +
ToString(geometry));
}
if (moduleIndex >= static_cast<size_t>(geometry.x * geometry.y)) {
throw RuntimeError("Module index " + std::to_string(moduleIndex) +
" out of bounds.");
}
int x = moduleIndex % geometry.x;
int y = moduleIndex / geometry.x;
return defs::xy{x, y};
}
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 {
int maxYMods = shm()->numberOfModules.y;
int y = (moduleIndex % maxYMods) * geometry.y;
int x = (moduleIndex / maxYMods) * geometry.x;
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, defs::xy{1, 1});
return defs::ROI{modSize.x * modPos.x, modSize.x * (modPos.x + 1) - 1,
modSize.y * modPos.y,
modSize.y * (modPos.y + 1) - 1}; // convert y for 1d?
}
void DetectorImpl::convertGlobalRoiToPortLevel(
const defs::ROI &userRoi, const defs::ROI &moduleRoi,
std::array<defs::ROI, 2> &portRois) const {
const defs::xy modSize = modules[0]->getNumberOfChannels();
const defs::xy geometry = getPortGeometry();
const int numPorts = geometry.x * geometry.y;
if (numPorts > 2) {
throw RuntimeError("Only up to 2 ports per module supported.");
}
for (int port = 0; port < numPorts; ++port) {
defs::ROI portRoi = moduleRoi;
// Calculate port ROI boundaries (split vertically or horizontally)
if (geometry.x == 2) {
int midX = (moduleRoi.xmin + moduleRoi.xmax) / 2;
if (port == 0)
portRoi.xmax = midX;
else
portRoi.xmin = midX + 1;
} else if (geometry.y == 2) {
int midY = (moduleRoi.ymin + moduleRoi.ymax) / 2;
if (port == 0)
portRoi.ymax = midY;
else
portRoi.ymin = midY + 1;
}
// Check if user ROI overlaps with this port ROI
if (roisOverlap(userRoi, portRoi)) {
defs::ROI clipped{};
clipped.xmin = std::max(userRoi.xmin, portRoi.xmin);
clipped.xmax = std::min(userRoi.xmax, portRoi.xmax);
if (modSize.y > 1) {
clipped.ymin = std::max(userRoi.ymin, portRoi.ymin);
clipped.ymax = std::min(userRoi.ymax, portRoi.ymax);
}
// Check if port ROI already exists for this port
if (!portRois[port].completeRoi()) {
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) {
void DetectorImpl::setRxROI(const defs::ROI arg) {
if (shm()->detType == CHIPTESTBOARD ||
shm()->detType == defs::XILINX_CHIPTESTBOARD) {
throw RuntimeError("RxRoi not implemented for this Detector");
@ -1846,42 +1780,118 @@ void DetectorImpl::setRxROI(const std::vector<defs::ROI> &args) {
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.");
}
validateROIs(args);
defs::xy numChansPerMod = modules[0]->getNumberOfChannels();
bool is2D = (numChansPerMod.y > 1 ? true : false);
defs::xy geometry = getPortGeometry();
for (size_t iModule = 0; iModule < modules.size(); ++iModule) {
auto moduleGlobalRoi = getModuleROI(iModule);
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.");
}
// at most 2 rois per module (for each port)
std::array<defs::ROI, 2> portRois{};
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 (const auto &arg : args) {
if (roisOverlap(arg, moduleGlobalRoi)) {
convertGlobalRoiToPortLevel(arg, moduleGlobalRoi, portRois);
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);
}
}
}
}
// print the rois for debugging
LOG(logINFOBLUE) << "Module " << iModule << " RxROIs:";
for (size_t iPort = 0; iPort != 2; iPort++) {
LOG(logINFOBLUE)
<< " Port " << iPort << ": " << ToString(portRois[iPort]);
}
modules[iModule]->setRxROI(portRois);
modules[iModule]->setRxROI(moduleRoi);
}
rxRoiTemp = args;
// updating shm rx_roi for gui purposes
shm()->rx_roi = arg;
// metadata
modules[0]->setRxROIMetadata(args);
}
void DetectorImpl::clearRxROI() {
rxRoiTemp.clear();
for (size_t iModule = 0; iModule < modules.size(); ++iModule) {
modules[iModule]->setRxROI(std::array<defs::ROI, 2>{});
if (arg.completeRoi()) {
modules[0]->setRxROIMetadata(defs::ROI(0, shm()->numberOfChannels.x - 1,
0,
shm()->numberOfChannels.y - 1));
} else {
modules[0]->setRxROIMetadata(arg);
}
}
int DetectorImpl::getNumberOfUdpPortsInRxROI() const {
return 0; // TODO
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;
}
void DetectorImpl::getBadChannels(const std::string &fname,

View File

@ -24,7 +24,7 @@ class detectorData;
class Module;
#define DETECTOR_SHMAPIVERSION 0x190809
#define DETECTOR_SHMVERSION 0x250616
#define DETECTOR_SHMVERSION 0x220505
#define SHORT_STRING_LENGTH 50
/**
@ -65,6 +65,8 @@ 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 {
@ -301,11 +303,9 @@ class DetectorImpl : public virtual slsDetectorDefs {
std::vector<std::pair<std::string, uint16_t>>
verifyUniqueRxHost(const std::vector<std::string> &names) const;
defs::xy getPortGeometry() const;
std::vector<defs::ROI> getRxROI() const;
void setRxROI(const std::vector<defs::ROI> &args);
defs::ROI getRxROI() const;
void setRxROI(const defs::ROI arg);
void clearRxROI();
int getNumberOfUdpPortsInRxROI() const;
void getBadChannels(const std::string &fname, Positions pos) const;
void setBadChannels(const std::string &fname, Positions pos);
@ -422,19 +422,12 @@ 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(size_t moduleIndex,
const defs::xy &geometry) const;
defs::xy calculatePosition(int moduleIndex, defs::xy geometry) const;
defs::ROI getModuleROI(int moduleIndex) const;
void convertGlobalRoiToPortLevel(
const defs::ROI &userRoi, const defs::ROI &moduleRoi,
std::array<defs::ROI, 2> &portRois) const;
const int detectorIndex{0};
SharedMemory<sharedDetector> shm{0, -1};
SharedMemory<CtbConfig> ctb_shm{0, -1, CtbConfig::shm_tag()};
@ -462,8 +455,6 @@ class DetectorImpl : public virtual slsDetectorDefs {
void (*dataReady)(detectorData *, uint64_t, uint32_t, void *){nullptr};
void *pCallbackArg{nullptr};
std::vector<defs::ROI> rxRoiTemp;
};
} // namespace sls

View File

@ -1203,7 +1203,7 @@ void Module::setDestinationUDPIP(const IpAddr ip) {
}
sendToDetector(F_SET_DEST_UDP_IP, ip, nullptr);
if (shm()->useReceiverFlag) {
MacAddr retval(0LU);
MacAddr retval{uint64_t(0)};
sendToReceiver(F_SET_RECEIVER_UDP_IP, ip, retval);
LOG(logINFO) << "Setting destination udp mac of Module " << moduleIndex
<< " to " << retval;
@ -1226,7 +1226,7 @@ void Module::setDestinationUDPIP2(const IpAddr ip) {
}
sendToDetector(F_SET_DEST_UDP_IP2, ip, nullptr);
if (shm()->useReceiverFlag) {
MacAddr retval(0LU);
MacAddr retval{uint64_t(0)};
sendToReceiver(F_SET_RECEIVER_UDP_IP2, ip, retval);
LOG(logINFO) << "Setting destination udp mac2 of Module " << moduleIndex
<< " to " << retval;
@ -1521,25 +1521,16 @@ void Module::setRxArping(bool enable) {
sendToReceiver(F_SET_RECEIVER_ARPING, static_cast<int>(enable), nullptr);
}
std::array<defs::ROI, 2> Module::getRxROI() const {
return sendToReceiver<std::array<slsDetectorDefs::ROI, 2>>(F_RECEIVER_GET_RECEIVER_ROI);
defs::ROI Module::getRxROI() const {
return sendToReceiver<slsDetectorDefs::ROI>(F_RECEIVER_GET_RECEIVER_ROI);
}
void Module::setRxROI(std::array<defs::ROI, 2> portRois) {
/*LOG(logDEBUG) << "Sending to receiver " << moduleIndex << " [rx roi: " << ToString(portRois)
<< ']';
auto receiver = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort);
receiver.Send(F_RECEIVER_SET_RECEIVER_ROI);
receiver.setFnum(F_RECEIVER_SET_RECEIVER_ROI);
receiver.Send(portRois);
if (receiver.Receive<int>() == FAIL) {
throw ReceiverError("Receiver " + std::to_string(moduleIndex) +
" returned error: " + receiver.readErrorMessage());
}*/
sendToReceiver(F_RECEIVER_SET_RECEIVER_ROI, portRois, nullptr);
void Module::setRxROI(const slsDetectorDefs::ROI arg) {
LOG(logDEBUG) << moduleIndex << ": " << arg;
sendToReceiver(F_RECEIVER_SET_RECEIVER_ROI, arg, nullptr);
}
void Module::setRxROIMetadata(const std::vector<slsDetectorDefs::ROI> &arg) {
void Module::setRxROIMetadata(const slsDetectorDefs::ROI arg) {
sendToReceiver(F_RECEIVER_SET_RECEIVER_ROI_METADATA, arg, nullptr);
}

View File

@ -301,9 +301,9 @@ class Module : public virtual slsDetectorDefs {
std::array<pid_t, NUM_RX_THREAD_IDS> getReceiverThreadIds() const;
bool getRxArping() const;
void setRxArping(bool enable);
std::array<defs::ROI, 2> getRxROI() const;
void setRxROI(const std::array<slsDetectorDefs::ROI, 2> portRois);
void setRxROIMetadata(const std::vector<slsDetectorDefs::ROI> &args);
defs::ROI getRxROI() const;
void setRxROI(const slsDetectorDefs::ROI arg);
void setRxROIMetadata(const slsDetectorDefs::ROI arg);
/**************************************************
* *

View File

@ -25,8 +25,12 @@
#include <unistd.h>
namespace sls {
#define SHM_DETECTOR_PREFIX "/slsDetectorPackage_detector_"
#ifdef __APPLE__
// On macOS SHM_NAME_MAX is 31 so we need the shortest possible prefix
#define SHM_DETECTOR_PREFIX "/sls_"
#else
#define SHM_DETECTOR_PREFIX "/slsDetectorPackage_detector_"
#endif
#define SHM_MODULE_PREFIX "_module_"
#define SHM_ENV_NAME "SLSDETNAME"
@ -203,6 +207,11 @@ template <typename T> class SharedMemory {
throw SharedMemoryError(msg);
}
#ifdef __APPLE__
// On macOS, fstat returns the allocated size and not the requested size.
// This means we can't check for size since we always get for example 16384 bytes.
return;
#endif
auto actual_size = static_cast<size_t>(sb.st_size);
auto expected_size = sizeof(T);
if (actual_size != expected_size) {

View File

@ -466,6 +466,7 @@ TEST_CASE("rx_arping", "[.cmdcall][.rx]") {
}
}
}
TEST_CASE("rx_roi", "[.cmdcall]") {
Detector det;
Caller caller(&det);
@ -478,69 +479,33 @@ TEST_CASE("rx_roi", "[.cmdcall]") {
defs::xy detsize = det.getDetectorSize();
// 1d
if (det_type == defs::GOTTHARD2 || det_type == defs::MYTHEN3) {
if (det_type == defs::GOTTHARD || 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));
// 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));
// 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 is allowed
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");
}
REQUIRE_THROWS(
caller.call("rx_roi", {"10", "15", "25", "30"}, -1, PUT));
}
// 2d eiger, jungfrau, moench
// 2d
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;
@ -548,83 +513,14 @@ 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));
// 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));
auto portSize = det.getPortSize()[0];
if (det_type == defs::EIGER) {
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 is allowed
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");
}
if (det_type == defs::JUNGFRAU || det_type == defs::MOENCH) {
// 2 interfaces or 2 modules
if ((det.getNumberofUDPInterfaces().tsquash(
"inconsistent number of interfaces") == 2) || (det.size() == 2)) {
std::string stringMin = std::to_string(portSize.y);
std::string stringMax = std::to_string(portSize.y + 1);
if (det.size() == 2) {
auto moduleSize = det.getModuleSize()[0];
stringMin = std::to_string(moduleSize.y);
stringMax = std::to_string(moduleSize.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
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");
}
}
}
for (int i = 0; i != det.size(); ++i) {
@ -633,7 +529,6 @@ TEST_CASE("rx_roi", "[.cmdcall]") {
}
}
TEST_CASE("rx_clearroi", "[.cmdcall]") {
Detector det;
Caller caller(&det);

View File

@ -113,12 +113,10 @@ template <typename T, size_t Capacity> class StaticVector {
// auto begin() noexcept -> decltype(data_.begin()) { return data_.begin();
// }
const_iterator begin() const noexcept { return data_.begin(); }
iterator end() noexcept { return data_.begin() + current_size; }
const_iterator end() const noexcept { return data_.begin() + current_size; }
iterator end() noexcept { return data_.begin()+current_size; }
const_iterator end() const noexcept { return data_.begin()+current_size; }
const_iterator cbegin() const noexcept { return data_.cbegin(); }
const_iterator cend() const noexcept {
return data_.cbegin() + current_size;
}
const_iterator cend() const noexcept { return data_.cbegin()+current_size; }
void size_check(size_type s) const {
if (s > Capacity) {

View File

@ -349,5 +349,4 @@ std::vector<T> StringTo(const std::vector<std::string> &strings) {
return result;
}
} // namespace sls

View File

@ -237,8 +237,7 @@ class slsDetectorDefs {
return (xmin == -1 && xmax == -1 && ymin == -1 && ymax == -1);
}
constexpr bool noRoi() const {
return ((xmin == 0 && xmax == 0) &&
((ymin == 0 && ymax == 0) || (ymin == -1 && ymax == -1)));
return (xmin == 0 && xmax == 0 && ymin == 0 && ymax == 0);
}
void setNoRoi() {
xmin = 0;

View File

@ -10,3 +10,4 @@
#define APIXILINXCTB "0.0.0 0x250523"
#define APIJUNGFRAU "0.0.0 0x250523"
#define APIMYTHEN3 "0.0.0 0x250523"

View File

@ -5,7 +5,12 @@
namespace sls {
std::string ToString(bool value) { return value ? "1" : "0"; }
std::string ToString(bool value) {
return value ? "1" : "0";
}
std::string ToString(const slsDetectorDefs::xy &coord) {
std::ostringstream oss;

View File

@ -15,7 +15,7 @@
#include <netdb.h>
#include <sstream>
#include <sys/ioctl.h>
#include <sys/prctl.h>
// #include <sys/prctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
@ -178,30 +178,30 @@ IpAddr InterfaceNameToIp(const std::string &ifn) {
MacAddr InterfaceNameToMac(const std::string &inf) {
// TODO! Copied from genericSocket needs to be refactored!
struct ifreq ifr;
char mac[32];
const int mac_len = sizeof(mac);
memset(mac, 0, mac_len);
// struct ifreq ifr;
// char mac[32];
// const int mac_len = sizeof(mac);
// memset(mac, 0, mac_len);
int sock = socket(PF_INET, SOCK_STREAM, 0);
strncpy(ifr.ifr_name, inf.c_str(), sizeof(ifr.ifr_name) - 1);
ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
// int sock = socket(PF_INET, SOCK_STREAM, 0);
// strncpy(ifr.ifr_name, inf.c_str(), sizeof(ifr.ifr_name) - 1);
// ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = '\0';
if (-1 == ioctl(sock, SIOCGIFHWADDR, &ifr)) {
perror("ioctl(SIOCGIFHWADDR) ");
return MacAddr{};
}
for (int j = 0, k = 0; j < 6; j++) {
k += snprintf(
mac + k, mac_len - k - 1, j ? ":%02X" : "%02X",
(int)(unsigned int)(unsigned char)ifr.ifr_hwaddr.sa_data[j]);
}
mac[mac_len - 1] = '\0';
// if (-1 == ioctl(sock, SIOCGIFHWADDR, &ifr)) {
// perror("ioctl(SIOCGIFHWADDR) ");
// return MacAddr{};
// }
// for (int j = 0, k = 0; j < 6; j++) {
// k += snprintf(
// mac + k, mac_len - k - 1, j ? ":%02X" : "%02X",
// (int)(unsigned int)(unsigned char)ifr.ifr_hwaddr.sa_data[j]);
// }
// mac[mac_len - 1] = '\0';
if (sock != 1) {
close(sock);
}
return MacAddr(mac);
// if (sock != 1) {
// close(sock);
// }
return MacAddr();
}
void validatePortNumber(uint16_t port) {

View File

@ -8,12 +8,15 @@
#include <sstream>
#include <vector>
using sls::StaticVector;
TEST_CASE("StaticVector is a container") {
REQUIRE(sls::is_container<StaticVector<int, 7>>::value == true);
}
TEST_CASE("Comparing StaticVector containers") {
StaticVector<int, 5> a{0, 1, 2};
StaticVector<int, 5> b{0, 1, 2};
@ -341,3 +344,4 @@ TEST_CASE("StaticVector stream") {
oss << vec;
REQUIRE(oss.str() == "[33, 85667, 2]");
}

View File

@ -26,6 +26,7 @@ TEST_CASE("Convert string to bool", "[support]") {
REQUIRE(StringTo<bool>("0") == false);
}
TEST_CASE("Integer conversions", "[support]") {
REQUIRE(ToString(0) == "0");
REQUIRE(ToString(1) == "1");

View File

@ -25,7 +25,6 @@ target_link_libraries(tests
slsProjectOptions
slsSupportStatic
pthread
rt
PRIVATE
slsProjectWarnings
)

View File

@ -20,7 +20,6 @@ from utils_for_test import (
cleanSharedmemory,
startProcessInBackground,
startProcessInBackgroundWithLogFile,
checkLogForErrors,
startDetectorVirtualServer,
loadConfig,
ParseArguments
@ -35,9 +34,6 @@ def startFrameSynchronizerPullSocket(name, fp):
fname = PULL_SOCKET_PREFIX_FNAME + name + '.txt'
cmd = ['python', '-u', 'frameSynchronizerPullSocket.py']
startProcessInBackgroundWithLogFile(cmd, fp, fname)
time.sleep(1)
checkLogForErrors(fp, fname)
def startFrameSynchronizer(num_mods, fp):
@ -48,14 +44,16 @@ def startFrameSynchronizer(num_mods, fp):
time.sleep(1)
def acquire(fp, det):
def acquire(fp):
Log(LogLevel.INFO, 'Acquiring')
Log(LogLevel.INFO, 'Acquiring', fp)
det.acquire()
d = Detector()
d.acquire()
def testFramesCaught(name, det, num_frames):
fnum = det.rx_framescaught[0]
def testFramesCaught(name, num_frames):
d = Detector()
fnum = d.rx_framescaught[0]
if fnum != num_frames:
raise RuntimeException(f"{name} caught only {fnum}. Expected {num_frames}")
@ -63,7 +61,7 @@ def testFramesCaught(name, det, num_frames):
Log(LogLevel.INFOGREEN, f'Frames caught test passed for {name}', fp)
def testZmqHeadetTypeCount(name, det, num_mods, num_frames, fp):
def testZmqHeadetTypeCount(name, num_mods, num_frames, fp):
Log(LogLevel.INFO, f"Testing Zmq Header type count for {name}")
Log(LogLevel.INFO, f"Testing Zmq Header type count for {name}", fp)
@ -90,7 +88,8 @@ def testZmqHeadetTypeCount(name, det, num_mods, num_frames, fp):
continue
# test if file contents matches expected counts
num_ports_per_module = 1 if name == "gotthard2" else det.numinterfaces
d = Detector()
num_ports_per_module = 1 if name == "gotthard2" else d.numinterfaces
total_num_frame_parts = num_ports_per_module * num_mods * num_frames
for htype, expected_count in [("header", num_mods), ("series_end", num_mods), ("module", total_num_frame_parts)]:
if htype_counts[htype] != expected_count:
@ -112,10 +111,10 @@ def startTestsForAll(args, fp):
startDetectorVirtualServer(server, args.num_mods, fp)
startFrameSynchronizerPullSocket(server, fp)
startFrameSynchronizer(args.num_mods, fp)
d = loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, fp=fp, num_mods=args.num_mods, num_frames=args.num_frames)
acquire(fp, d)
testFramesCaught(server, d, args.num_frames)
testZmqHeadetTypeCount(server, d, args.num_mods, args.num_frames, fp)
loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, fp=fp, num_mods=args.num_mods, num_frames=args.num_frames)
acquire(fp)
testFramesCaught(server, args.num_frames)
testZmqHeadetTypeCount(server, args.num_mods, args.num_frames, fp)
Log(LogLevel.INFO, '\n')
except Exception as e:
raise RuntimeException(f'Synchronizer Tests failed') from e

View File

@ -104,7 +104,7 @@ def startProcessInBackground(cmd, fp):
raise RuntimeException(f'Failed to start {cmd}:{str(e)}') from e
def startProcessInBackgroundWithLogFile(cmd, fp, log_file_name: str):
def startProcessInBackgroundWithLogFile(cmd, fp, log_file_name):
Log(LogLevel.INFOBLUE, 'Starting up ' + ' '.join(cmd) + '. Log: ' + log_file_name)
Log(LogLevel.INFOBLUE, 'Starting up ' + ' '.join(cmd) + '. Log: ' + log_file_name, fp)
try:
@ -114,22 +114,6 @@ def startProcessInBackgroundWithLogFile(cmd, fp, log_file_name: str):
raise RuntimeException(f'Failed to start {cmd}:{str(e)}') from e
def checkLogForErrors(fp, log_file_path: str):
try:
with open(log_file_path, 'r') as log_file:
for line in log_file:
if 'Error' in line:
Log(LogLevel.ERROR, f"Error found in log: {line.strip()}")
Log(LogLevel.ERROR, f"Error found in log: {line.strip()}", fp)
raise RuntimeException("Error found in log file")
except FileNotFoundError:
print(f"Log file not found: {log_file_path}")
raise
except Exception as e:
print(f"Exception while reading log: {e}")
raise
def runProcessWithLogFile(name, cmd, fp, log_file_name):
Log(LogLevel.INFOBLUE, 'Running ' + name + '. Log: ' + log_file_name)
Log(LogLevel.INFOBLUE, 'Running ' + name + '. Log: ' + log_file_name, fp)
@ -156,7 +140,7 @@ def startDetectorVirtualServer(name :str, num_mods, fp):
for i in range(num_mods):
port_no = SERVER_START_PORTNO + (i * 2)
cmd = [name + 'DetectorServer_virtual', '-p', str(port_no)]
startProcessInBackgroundWithLogFile(cmd, fp, "/tmp/virtual_det_" + name + str(i) + ".txt")
startProcessInBackground(cmd, fp)
match name:
case 'jungfrau':
time.sleep(7)
@ -217,8 +201,6 @@ def loadConfig(name, rx_hostname, settingsdir, fp, num_mods = 1, num_frames = 1)
d.frames = num_frames
except Exception as e:
raise RuntimeException(f'Could not load config for {name}. Error: {str(e)}') from e
return d
def ParseArguments(description, default_num_mods=1):