10.0.0.rc (#1260)
Some checks failed
Build on RHEL9 / build (push) Failing after 4m23s
Build on RHEL8 / build (push) Failing after 4m58s

* alignedData now uses std::align_alloc

* imagedata is now allocated on the heap

* m3 server fix for trimbits and badchannels that are shifted by 1

* formatting

* binary in

* added check for proper memory allocation

* commenting out the example in receiver data call back changing size as it affects users using debugging mode to print out headers

* fixed warnings

* commenting out the example in receiver data call back changing size as it affects users using debugging mode to print out headers

* got rid of cast to uint64

* got rid of Reorder function

* added sanity check to only enable for chipttestboard and xilinx

* removed Gotthard stuff

* update the comment about how to modify data on a data call back from the receiver

* autogenerated commands and make format

* changed font size in GUI

* clang-format with clang-format version 17

* updated update_image_size in xilinx

* version number automated for python build

* mistakenly set version back to 0.0.0

* updated github workflow scripts to support automatic version numbering with environment variable

* managed to load VERSION file in yaml file - simplifies things

* saving changes in git workflow failed

* got typo in github workflow

* updatet regex pattern to support postfix

* normalized version to PEP 440 specification in update_version.py

* bug did not support version 0.0.0

* upgrading to c++17 from c++11 and patch command has to be found before applying patch on libzmq (#1195)

* Dev/allow localhost for virtual tests (#1190)

* remove the check for localhost being used in rx_hostname for python test for simulators, run rx_arping test only if hostname is not 'localhost'

* fix tests for fpath: cannot set back to empty anymore (empty is default)

* default rx_hostname arg = localhost, and default settings path =../../settingsdir

* changed virtual tests script for better printout on exceptions

* fix for catching generaltests exceptions and exiting instead of continuing

* fix minor

* fixed shared memeory tests to include current env and fixed prints for errors

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@gmail.com>

* added regex pattern matching to version in toml file

* Dev/gitea docker (#1194)

* gitea workflows for RH8 and RH9
* using our docker images

* version now supports . before postfix

* rough draft of test acquire of all detectors for frames caught and file size. ctb not included yet

* moved dbitoffset, dbitreorder and dbitlist to GeneralData

* added error message on receiver side, throw error

* removed log as error already printed

* added tests to check file size and frames caught with an acquire (virtual) for every detector

* minor printout removed

* typo fixed

* removed minor printout

* incorrect counter mask tested

* fix 10g adc enable mask, switched with 1g

* fixed hardcoded values of nchip nchan etc from detPArameters

* fixed ctb tests, need to fix in develoepr (if digital modfe not enabled, should not take into accoutn dbitlist or dbitoffset or dbitreorder

* only reorder bits if some sort of digital readout mode enabled

* trying to fix acquire for xilinx

* fix for xilinx ctb virtual

* alloweing all tests

* typo

* fix for slsreceiver killed but complaining for virtual tests with script

* fixed bug found by @AliceMazzoleni99 that for ctb server is still shown in pgrep -f if xilinx server running, so now the pid is killed and looking for any DetectorServer_virtual instead. also reset color coding after Log

* check if process running for kill -9 slsReceiver fail

* removed -9 to kill with cleanup

* frame synchonrizer fixes: typo of iterator for loop and zmg_msg_t list cleaned up before sending multi part zmq; test written for the frame synchronizer, test_simulator.py rewritten for more robustness and refactoring commonality between both scripts

* better error messageS

* minor

* typo

* moving the erasure of the fnum to after sending the zmg packets and also deleteing all old frames when end of acquisition

* fix bug in blackfin read access to firmware registers

* updates api version based on version file & converted shell script files to python

* updated all makefiles

* refactoring code and compiling binary

* formatting

* rewrote end() for StaticVector

* rearranged receiver topics, differentiated btween receiver variants and added info about slsFrameSynchronizer

* typo

* minor aesthetics

* minor

* added extra fs link and fixed execute_program warning

* and now with link

* updating pmods

* adresses review comments

* dummy commit for versionAPI

* formatted and updated versionAPI.h

* added expat to host section

* updated documentation for pip installation as well

* Dev/add numpy  (#1227)

* added numpy dependency
* added build specifications for python version and platform

* updates files/variants for pmods for 9.2.0 (#1233)

* tests for bool in ToString/StringTo (#1230)

- Added tests for ToString/StringTo<bool>
- Added overload for ToString of bool (previously went through int)

* added docs for SLSDETNAME (#1228)

* added docs for SLSDETNAME

* clarification on hostname

* added examples on module index

* fixes

* fixed typo

* Dev/update test framesynchronizer (#1221)

* raise an exception if the pull socket python script had errors at startup (for eg if pyzmq was not installed)

* minor changes that got lost in the merge of automate_version_part 2 PR

---------

Co-authored-by: Erik Fröjdh <erik.frojdh@gmail.com>

* added workflow for python wheels

* wip

* formatting

* wip

* wip to parse vector of rois at command line

* wip

* first level test

* can get individual rois, but not connected to command yet

* rois shoudl work. left to implement tests for individual rois, create multiple datasets (1 for each roi) in the virutal data file. currently virutal dataset with roi is not implemented and a warning is given instead. wonder why since the inviduviaual roi files are clipped

* all tests pased

* minor

* fixed rx_roi for multi modules jungfrau , tests for eiger, multi modules jungfrau in x and 2 interfaces

* works for eiger as well

* switched to vector instead of std::array<ROI, 2>>, which prints extra [-1, -1] when theres only 1 udp interface

* wip

* fix for empty roi vectors (which shouldnt be) as you cant know if its all or not in roi

* wip: to map roi to virutal

* fix for eiger, added python test for testig roi in different module and detector type configurations

* wip, fails with master and virtual

* works for complete roi

* wip, works for a single roi

* works for all rois

* wip to fix tests

* 1d fixes

* rois test work on 1d as well

* check master file creation as well in rx_roi tests

* get rx_roi from metadata from rxr, cant reconstruct. fixed clear roi should give 1 roi min

* gui shows roi now

* format

* updated python bindings

* updated master file versions

* cmd generation and formatting

* minor fixes in command line and help

* minor

* doesnt happen anymore

* comment

* minor

* redundant getRxROI in Detector class for multi level and module level

* refactor cmd parsing (detid can be parsed directly)

* refactor cmd line

* refactor command line parsing of roi

* modified comments about ctb and xilinx not using roi

* refactoring

* refactorign

* refactoring wip

* wip refactoring

* formattin

* to avoid confusion, moved default initialized, single sized declared vector of roi to be created at setDetectorType

* pybind only 1 function for getRxROI

* command line help

* specified number of receiver error message

* minor comment

* refactored to take out repetitive code, need to adjust for slsMulti and slsFrameSync

* wip

* works, need to add tests

* made Commadnlineoptions into a class

* wip test

* fixed tests

* cleaning up properly , semaphore leaks, child process/thread throwing handled

* getuid issue on github workflow

* constexpr and checking if options object type is same

* unnecessary capture

* remove testing code, minor

* fixed help, -t for multi should not be supported as it never had it

* Formatting

* hdf5 definitions in test when not compiled with hdf5

* typo

* moved optstring and long options to the constructor

* raising a SIGINT when the child thread has an exception so that the parent thread can exit all the threads and clean up gracefully

* minor test typo

* check status of child exiting and use that to send sigint to all the child processes from the parent

* fixed validation in network_utils, added a tests to throw for port 65535 in test mode (option on for sls_use_tests), multi:parent process checks child process exit status to send sigint to others

* moving set signal handler to network utils

* readoutspeed in rx master file and  other master file inconsistencies (#1245)

readout speed added to json and h5 master files.
Also fixed master file inconsistencies

Sserver binaries
- update server binaries because readoutspeed needs to be sent to receiver with rx_hostname command

API
- added const to Detector class set/getburstmode

Python
- updated python bindings (burstmode const and roi arguments)

Cmd generation
- added pragma once in Caller.in.h as Caller is included in test files

m3: num channels due to #counters < 3
* workaround for m3 for messed up num channels (client always assumes all counters enabled and adds them to num channels), fix for hdf5

g2: exptime master file inconsistency
- exptime didnt match because of round of when setting burst mode (sets to a different clk divider)
- so updating actual time for all timers (exptime, period, subexptime etc, )  in Module class, get timer values from detector when setting it and then send to receiver to write in master file

ctb image size incorrect:
-  write actual size into master file and not the reserved size (digital reduces depending on dbit list and dbit offset)
- added a calculate ctb image size free function in generalData.h that is used there as well as for the tests.


master file inconsistencies
- refactored master attributes writing using templates
-    names changed to keep it consistent between json and hdf5 master file (Version, Pixels, Exposure Times, GateDelays, Acquisition Period, etc.)
-  datatypes changed to keep it simple where possible: imageSize, dynamicRange, tengiga, quad, readnrows, analog, analogsamples, digital, digitalsamples, dbitreorder, dbitoffset, transceivermask, transeiver, transceiversamples, countermask, gates =>int
- replacing "toString" with arrays, objects etc for eg for scan, rois, etc.
- json header always written (empty dataset or empty brackets)
- hdf5 needs const char* so have to convert strings to it, but taking care that strings exist prior to push_back
- master attributes (redundant string literals->error prone

tests for master file
- suppressed deprecated functions in rapidjson warnings just for the tests
- added slsREceiverSoftware/src to allow access to receiver_defs.h to test binary/hdf5 version
- refactored acquire tests by moving all the acquire tests from individual detector type files to a single one=test-Caller-acquire.cpp
- set some default settings (loadBasicSettings) for a basic acquire at load config part for the test_simulator python scripts. so minimum number of settings for detector to be set for any acquire tests.
- added tests to test master files for json and hdf5= test-Caller-master-attributes.cpp
- added option to add '-m' markers for tests using test_simulator python script

* doc: added inst on how to set persistentn NIC changes after reboot for each ethernet interface such as rx 4096, rx-usecs, adaptive-rx and gro etc.

* added permanent ethtool settings also for fedora or modern rhel

* troubleshooting doc: permanent changes for 10g pc tuning (#1247)

* doc: added inst on how to set persistentn NIC changes after reboot for each ethernet interface such as rx 4096, rx-usecs, adaptive-rx and gro etc.

* added permanent ethtool settings also for fedora or modern rhel

* ifcfg scripts still work on rhel8, just not preferred

* added dataformat for jungfrau

* eiger basic mod

* eiger doc done

* added moench

* done

* free shm exposed in python as free function and detector function

* minimum change

* added quad and updated about 1gbe/10gbe

* more info

* remove arguments info

* replacing commands with links

* minor

* detail explanation of eiger

* fixed imagesize ctb issue (out values not transferred, setting any dbit values was not recalculatign image size in generaldata)

* fixed ctb dbit clock changing period in tests as it was setting run clock instead

* python accessing freed shared memory object (#1253)

* added a 'isValid' member in shared memory (also updated shm version) with default true, any access to shared memory() checks also for validity. any free will set this to false and then unmap shm. Any access to shm will then check validity in python.

* fixed tests for shm

* added tests in python as well

---------

Co-authored-by: Alice <alice.mazzoleni@psi.ch>

* updated error message

* made markers argument in ParseArguments a boolean instead of an int

* removed relative path compared to where executable run in test script for settingsdir

* fix roi test

* updating versions (#1258)

* updating package version, client version, server versions. Renaming server versions, using hardlinks in serverBin. Removing ctb servers in serverBin. (#1259)

* fixed no interpolation mode for moench (#1262)

Co-authored-by: Anna Bergamaschi <anna.bergamaschi@psi.ch>

* fixed multi receiver and frames sync help throw of bad variant access (#1265)

* 1000/doc c standard (#1267)

* updated c++11 to c++17

* more about c++11 and updating readme

* updated documentation for receiver arguments and also making receiver constructor explicit

* minor fix for rxr err message

* fixed doc about gcc version

* 1000/release notes (#1269)

* updated firmware and server version in release notes

* release notes wip

* updated notes(prs done)

* updated release notes. wip

* Release notes

* minor

* minor fix

* 1000/doc architecture commands (#1271)

* sw architecture and setup commands

* 1000/shm free obsolete (#1273)

* freeing obsolete shm withoua a 'isValid' should access raw pointers. Need to move this all into the shm class

* fixed obsolete shm free issue

* minor

* ensuring the test works platform independent for size of int

* removed verify, update, fixed getUser to be a free function, generated commands, python bindings yet to do

* python bindings

* fixed tests

* minor

* minor

* format

* userdetails refinedg

* fixed caller test

* updated client api version (#1277)

* one doesnt need to open shared memory to call removesharedmemory, and calling hasMemoryvalid without opening will cause segfault (not used now, but could in the future)

* fix test on shm

* minor

* added image source files from draw.io to create the images (#1280)

* 1000/fix_actual_tests (#1282)

- fix acquire fail in tests (adcreg test)
- roi tests fail after overlapping invalid test and acquire after
- print udp dest mac in server properly
- fixed udp dst list get (server was not sending entry proper size to match proper struct size in client)
- updated server binaries and updated hard links in serverBin
- added documentation regarding gui:  zmqport and zmqip in terms of gui, rx_zmqstream
- removed print - probably ended there for debuggung

---------

Co-authored-by: Alice <alice.mazzoleni@psi.ch>

* 1000/fix_m3_tests (#1286)

* testing clkdiv one must ensure the exptime delay etc all are reset to the exact values for tests

* change dac max values for vth values for m3 in client side (set module

* 1000/doc_cmake (#1289)

* more detail documentation in installation

* more detail documentation in installation

* added links to api examples

* reverted back that vthreshold dacs in m3 have min and max as 200 and 2400 (#1294)

* update release notes and date (#1298)

---------

Co-authored-by: Mazzoleni Alice Francesca <mazzol_a@pc17378.psi.ch>
Co-authored-by: Erik Fröjdh <erik.frojdh@gmail.com>
Co-authored-by: AliceMazzoleni99 <alice.mazzoleni@psi.ch>
Co-authored-by: Martin Mueller <martin.mueller@psi.ch>
Co-authored-by: froejdh_e <erik.frojdh@psi.ch>
Co-authored-by: Anna Bergamaschi <anna.bergamaschi@psi.ch>
This commit is contained in:
2025-09-10 11:13:36 +02:00
committed by GitHub
parent c10bc5e530
commit 54e4c46f02
253 changed files with 35062 additions and 32286 deletions

View File

@@ -13,6 +13,7 @@ set(SOURCES
src/Arping.cpp
src/MasterAttributes.cpp
src/MasterFileUtility.cpp
src/CommandLineOptions.cpp
)
set(PUBLICHEADERS
@@ -51,6 +52,10 @@ target_link_libraries(slsReceiverObject
slsProjectWarnings #don't propagate warnigns
)
target_compile_definitions(slsReceiverObject
PRIVATE $<$<BOOL:${SLS_USE_TESTS}>:SLS_USE_TESTS>
)
# HDF5
if (SLS_USE_HDF5)
if (HDF5_FOUND)
@@ -86,6 +91,7 @@ set_target_properties(slsReceiverStatic PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
PUBLIC_HEADER "${PUBLICHEADERS}"
)
list(APPEND RECEIVER_LIBRARY_TARGETS slsReceiverStatic)

View File

@@ -13,22 +13,12 @@ class Receiver : private virtual slsDetectorDefs {
public:
/**
* Constructor
* Starts up a Receiver server. Reads configuration file, options, and
* assembles a Receiver using TCP and UDP detector interfaces
* Starts up a Receiver server.
* Assembles a Receiver using TCP and UDP detector interfaces
* throws an exception in case of failure
* @param argc from command line
* @param argv from command line
* @param port TCP/IP port number
*/
Receiver(int argc, char *argv[]);
/**
* Constructor
* Starts up a Receiver server. Reads configuration file, options, and
* assembles a Receiver using TCP and UDP detector interfaces
* throws an exception in case of failure
* @param tcpip_port_no TCP/IP port number
*/
Receiver(uint16_t tcpip_port_no = 1954);
explicit Receiver(uint16_t port = 1954);
~Receiver();
@@ -45,9 +35,8 @@ class Receiver : private virtual slsDetectorDefs {
* Call back arguments are:
* - startCallbackHeader metadata
*/
void registerCallBackStartAcquisition(int (*func)(const startCallbackHeader,
void *),
void *arg);
void registerCallBackStartAcquisition(
void (*func)(const startCallbackHeader, void *), void *arg);
/**
* Call back for acquisition finished

View File

@@ -44,7 +44,7 @@ ClientInterface::~ClientInterface() {
}
ClientInterface::ClientInterface(uint16_t portNumber)
: detType(GOTTHARD), portNumber(portNumber), server(portNumber) {
: detType(GENERIC), portNumber(portNumber), server(portNumber) {
validatePortNumber(portNumber);
functionTable();
parentThreadId = gettid();
@@ -56,7 +56,7 @@ std::string ClientInterface::getReceiverVersion() { return APIRECEIVER; }
/***callback functions***/
void ClientInterface::registerCallBackStartAcquisition(
int (*func)(const startCallbackHeader, void *), void *arg) {
void (*func)(const startCallbackHeader, void *), void *arg) {
std::lock_guard<std::mutex> lock(callbackMutex);
startAcquisitionCallBack = func;
pStartAcquisition = arg;
@@ -120,7 +120,6 @@ int ClientInterface::functionTable(){
flist[F_GET_LAST_RECEIVER_CLIENT_IP] = &ClientInterface::get_last_client_ip;
flist[F_GET_RECEIVER_VERSION] = &ClientInterface::get_version;
flist[F_SETUP_RECEIVER] = &ClientInterface::setup_receiver;
flist[F_RECEIVER_SET_DETECTOR_ROI] = &ClientInterface::set_detector_roi;
flist[F_RECEIVER_SET_NUM_FRAMES] = &ClientInterface::set_num_frames;
flist[F_SET_RECEIVER_NUM_TRIGGERS] = &ClientInterface::set_num_triggers;
flist[F_SET_RECEIVER_NUM_BURSTS] = &ClientInterface::set_num_bursts;
@@ -219,7 +218,10 @@ int ClientInterface::functionTable(){
flist[F_RECEIVER_SET_TRANSCEIVER_MASK] = &ClientInterface::set_transceiver_mask;
flist[F_RECEIVER_SET_ROW] = &ClientInterface::set_row;
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;
flist[F_SET_RECEIVER_READOUT_SPEED] = &ClientInterface::set_readout_speed;
for (int i = NUM_DET_FUNCTIONS + 1; i < NUM_REC_FUNCTIONS ; i++) {
LOG(logDEBUG1) << "function fnum: " << i << " (" <<
@@ -409,13 +411,12 @@ int ClientInterface::setup_receiver(Interface &socket) {
impl()->setReadoutMode(arg.roMode);
impl()->setTenGigaADCEnableMask(arg.adc10gMask);
impl()->setTransceiverEnableMask(arg.transceiverMask);
} else {
impl()->setReadoutSpeed(arg.readoutSpeed);
}
if (detType == CHIPTESTBOARD) {
impl()->setADCEnableMask(arg.adcMask);
}
if (detType == GOTTHARD) {
impl()->setDetectorROI(arg.roi);
}
if (detType == MYTHEN3) {
impl()->setCounterMask(arg.countermask);
impl()->setAcquisitionTime1(
@@ -443,7 +444,6 @@ int ClientInterface::setup_receiver(Interface &socket) {
void ClientInterface::setDetectorType(detectorType arg) {
switch (arg) {
case GOTTHARD:
case EIGER:
case CHIPTESTBOARD:
case XILINX_CHIPTESTBOARD:
@@ -482,22 +482,6 @@ void ClientInterface::setDetectorType(detectorType arg) {
impl()->setThreadIds(parentThreadId, tcpThreadId);
}
int ClientInterface::set_detector_roi(Interface &socket) {
auto arg = socket.Receive<ROI>();
LOG(logDEBUG1) << "Set Detector ROI: " << ToString(arg);
if (detType != GOTTHARD)
functionNotImplemented();
verifyIdle(socket);
try {
impl()->setDetectorROI(arg);
} catch (const std::exception &e) {
throw RuntimeError("Could not set ROI [" + std::string(e.what()) + ']');
}
return socket.Send(OK);
}
int ClientInterface::set_num_frames(Interface &socket) {
auto value = socket.Receive<int64_t>();
if (value <= 0) {
@@ -1712,19 +1696,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<int>(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<ROI>();
auto roiSize = socket.Receive<int>();
std::vector<ROI> 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()) + ']');
@@ -1734,18 +1736,26 @@ int ClientInterface::set_receiver_roi(Interface &socket) {
}
int ClientInterface::set_receiver_roi_metadata(Interface &socket) {
auto arg = socket.Receive<ROI>();
auto roiSize = socket.Receive<int>();
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<ROI> 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);
}
@@ -1810,4 +1820,66 @@ int ClientInterface::set_column(Interface &socket) {
return socket.Send(OK);
}
int ClientInterface::get_dbit_reorder(Interface &socket) {
if (detType != CHIPTESTBOARD && detType != XILINX_CHIPTESTBOARD)
functionNotImplemented();
int retval = impl()->getDbitReorder();
LOG(logDEBUG1) << "Dbit reorder retval: " << retval;
return socket.sendResult(retval);
}
int ClientInterface::set_dbit_reorder(Interface &socket) {
auto arg = socket.Receive<int>();
if (detType != CHIPTESTBOARD && detType != XILINX_CHIPTESTBOARD)
functionNotImplemented();
if (arg < 0) {
throw RuntimeError("Invalid dbit reorder: " + std::to_string(arg));
}
verifyIdle(socket);
LOG(logDEBUG1) << "Setting Dbit reorder: " << arg;
impl()->setDbitReorder(arg);
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<int>(retvals.size());
socket.Send(size);
if (size > 0)
socket.Send(retvals);
return OK;
}
int ClientInterface::set_readout_speed(Interface &socket) {
auto value = socket.Receive<int>();
verifyIdle(socket);
switch (detType) {
case GOTTHARD2:
if (value != G2_108MHZ && value != G2_144MHZ)
throw RuntimeError("Invalid readout speed for GOTTHARD2: " +
std::to_string(value));
break;
case EIGER:
case JUNGFRAU:
case MYTHEN3:
case MOENCH:
if (value < 0 || value > QUARTER_SPEED) {
throw RuntimeError("Invalid readout speed: " +
std::to_string(value));
}
break;
default:
functionNotImplemented();
}
LOG(logDEBUG1) << "Setting readout speed to " << value;
impl()->setReadoutSpeed(static_cast<speedLevel>(value));
return socket.Send(OK);
}
} // namespace sls

View File

@@ -34,9 +34,8 @@ class ClientInterface : private virtual slsDetectorDefs {
//***callback functions***
/** params: file path, file name, file index, image size */
void registerCallBackStartAcquisition(int (*func)(const startCallbackHeader,
void *),
void *arg);
void registerCallBackStartAcquisition(
void (*func)(const startCallbackHeader, void *), void *arg);
/** params: total frames caught */
void registerCallBackAcquisitionFinished(
@@ -64,7 +63,6 @@ class ClientInterface : private virtual slsDetectorDefs {
int get_version(ServerInterface &socket);
int setup_receiver(ServerInterface &socket);
void setDetectorType(detectorType arg);
int set_detector_roi(ServerInterface &socket);
int set_num_frames(ServerInterface &socket);
int set_num_triggers(ServerInterface &socket);
int set_num_bursts(ServerInterface &socket);
@@ -166,6 +164,10 @@ class ClientInterface : private virtual slsDetectorDefs {
int set_transceiver_mask(ServerInterface &socket);
int set_row(ServerInterface &socket);
int set_column(ServerInterface &socket);
int get_dbit_reorder(ServerInterface &socket);
int set_dbit_reorder(ServerInterface &socket);
int get_roi_metadata(ServerInterface &socket);
int set_readout_speed(ServerInterface &socket);
Implementation *impl() {
if (receiver != nullptr) {
@@ -180,8 +182,8 @@ class ClientInterface : private virtual slsDetectorDefs {
//***callback parameters***
int (*startAcquisitionCallBack)(const startCallbackHeader,
void *) = nullptr;
void (*startAcquisitionCallBack)(const startCallbackHeader,
void *) = nullptr;
void *pStartAcquisition{nullptr};
void (*acquisitionFinishedCallBack)(const endCallbackHeader,
void *) = nullptr;

View File

@@ -0,0 +1,352 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "CommandLineOptions.h"
#include "sls/ToString.h"
#include "sls/logger.h"
#include "sls/sls_detector_defs.h"
#include "sls/versionAPI.h"
#include <cstring>
#include <unistd.h>
CommandLineOptions::CommandLineOptions(AppType app)
: appType_(app), optString_(buildOptString()),
longOptions_(buildOptionList()) {}
/** for testing */
ParsedOptions CommandLineOptions::parse(const std::vector<std::string> &args) {
std::vector<char *> argv;
argv.reserve(args.size());
for (const auto &arg : args) {
argv.push_back(const_cast<char *>(arg.c_str()));
}
int argc = static_cast<int>(argv.size());
return parse(argc, argv.data());
}
ParsedOptions CommandLineOptions::parse(int argc, char *argv[]) {
CommonOptions base;
MultiReceiverOptions multi;
FrameSyncOptions frame;
base.port = DEFAULT_TCP_RX_PORTNO;
optind = 0; // reset getopt
int opt, option_index = 0;
bool help_or_version_requested = false;
while ((opt = getopt_long(argc, argv, optString_.c_str(),
longOptions_.data(), &option_index)) != -1) {
switch (opt) {
case 'v':
case 'h':
handleCommonOption(opt, optarg, base);
help_or_version_requested = true;
break;
case 'p':
case 'u':
handleCommonOption(opt, optarg, base);
break;
case 'c':
case 'n':
case 't':
handleAppSpecificOption(opt, optarg, base, multi, frame);
break;
default:
throw sls::RuntimeError("Invalid arguments." + getHelpMessage());
}
}
// remaining arguments
if (!help_or_version_requested && optind < argc) {
// deprecated and current options => invalid
if (base.port != DEFAULT_TCP_RX_PORTNO || multi.numReceivers != 1 ||
frame.numReceivers != 1 || multi.callbackEnabled != false ||
frame.printHeaders != false) {
LOG(sls::logWARNING) << "Cannot use both deprecated options and "
"the valid options simultaneously. Please "
"move away from the deprecated options.\n";
}
// unsupported deprecated arguments
if (appType_ == AppType::SingleReceiver) {
throw sls::RuntimeError("Invalid arguments." + getHelpMessage());
}
// parse deprecated arguments
std::vector<std::string> args(argv, argv + argc);
auto [p, n, o] = ParseDeprecated(args);
// set options
base.port = p;
if (appType_ == AppType::MultiReceiver) {
multi.numReceivers = n;
multi.callbackEnabled = o;
} else if (appType_ == AppType::FrameSynchronizer) {
frame.numReceivers = n;
frame.printHeaders = o;
}
}
// Logging
if (!help_or_version_requested) {
LOG(sls::logINFO) << "TCP Port: " << base.port;
if (appType_ == AppType::MultiReceiver) {
LOG(sls::logINFO) << "Number of receivers: " << multi.numReceivers;
LOG(sls::logINFO) << "Callback enabled: " << multi.callbackEnabled;
} else if (appType_ == AppType::FrameSynchronizer) {
LOG(sls::logINFO) << "Number of receivers: " << frame.numReceivers;
LOG(sls::logINFO) << "Print headers: " << frame.printHeaders;
}
}
switch (appType_) {
case AppType::SingleReceiver:
return base;
case AppType::MultiReceiver:
static_cast<CommonOptions &>(multi) = base;
return multi;
case AppType::FrameSynchronizer:
static_cast<CommonOptions &>(frame) = base;
return frame;
default:
throw sls::RuntimeError("Unknown AppType in CommandLineOptions::parse");
}
}
std::vector<option> CommandLineOptions::buildOptionList() const {
std::vector<option> opts = {
{"version", no_argument, nullptr, 'v'},
{"help", no_argument, nullptr, 'h'},
{"port", required_argument, nullptr, 'p'},
{"uid", required_argument, nullptr, 'u'},
};
switch (appType_) {
case AppType::SingleReceiver:
opts.push_back({"rx_tcpport", required_argument, nullptr, 't'});
break;
case AppType::MultiReceiver:
opts.push_back({"num-receivers", required_argument, nullptr, 'n'});
opts.push_back({"callback", no_argument, nullptr, 'c'});
break;
case AppType::FrameSynchronizer:
opts.push_back({"num-receivers", required_argument, nullptr, 'n'});
opts.push_back({"print-headers", no_argument, nullptr, 'c'});
break;
}
opts.push_back({nullptr, 0, nullptr, 0}); // null-terminator for getopt
return opts;
}
std::string CommandLineOptions::buildOptString() const {
std::string optstr = "vhp:u:";
if (appType_ == AppType::MultiReceiver ||
appType_ == AppType::FrameSynchronizer)
optstr += "cn:";
if (appType_ == AppType::SingleReceiver)
optstr += "t:";
return optstr;
}
uint16_t CommandLineOptions::parsePort(const char *optarg) {
uint16_t val = 0;
try {
val = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Could not parse port number " +
std::string(optarg));
}
if (val < 1024) {
throw sls::RuntimeError(
"Invalid/ privileged port number parsed. Min: 1024.");
}
return val;
}
uint16_t CommandLineOptions::parseNumReceivers(const char *optarg) {
uint16_t val = 0;
try {
val = sls::StringTo<uint16_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Could not parse number of receivers " +
std::string(optarg));
}
if (val == 0 || val > MAX_RECEIVERS) {
throw sls::RuntimeError(
"Invalid number of receivers parsed. Options: 1 - " +
std::to_string(MAX_RECEIVERS));
}
return val;
}
uid_t CommandLineOptions::parseUID(const char *optarg) {
uid_t val = -1;
try {
val = sls::StringTo<uid_t>(optarg);
} catch (...) {
throw sls::RuntimeError("Could not parse UID " + std::string(optarg));
}
if (val == static_cast<uid_t>(-1)) {
throw sls::RuntimeError(
"Could not parse UID. Expected a valid user ID." +
std::string(optarg));
}
return val;
}
void CommandLineOptions::handleCommonOption(int opt, const char *optarg,
CommonOptions &base) {
switch (opt) {
case 'v':
base.versionRequested = true;
std::cout << getVersion() << std::endl;
break;
case 'h':
base.helpRequested = true;
std::cout << getHelpMessage() << std::endl;
break;
case 'p':
base.port = parsePort(optarg);
break;
case 'u':
base.userid = parseUID(optarg);
setEffectiveUID(base.userid);
break;
}
}
void CommandLineOptions::handleAppSpecificOption(int opt, const char *optarg,
CommonOptions &base,
MultiReceiverOptions &multi,
FrameSyncOptions &frame) {
switch (opt) {
case 'c':
if (appType_ == AppType::MultiReceiver)
multi.callbackEnabled = true;
else if (appType_ == AppType::FrameSynchronizer)
frame.printHeaders = true;
break;
case 'n': {
auto val = parseNumReceivers(optarg);
if (appType_ == AppType::MultiReceiver)
multi.numReceivers = val;
else if (appType_ == AppType::FrameSynchronizer)
frame.numReceivers = val;
break;
}
case 't':
LOG(sls::logWARNING) << "Deprecated option '-t' and '--rx_tcport'. Use "
"'--p' or '--port' instead.";
base.port = parsePort(optarg);
break;
}
}
/* maintain backward compatibility of [start port] [num receivers] [optional
* arg] */
std::tuple<uint16_t, uint16_t, bool>
CommandLineOptions::ParseDeprecated(const std::vector<std::string> &args) {
size_t nargs = args.size();
if (nargs != 1 && nargs != 3 && nargs != 4) {
throw sls::RuntimeError("Invalid number of arguments.");
}
LOG(sls::logWARNING)
<< "Deprecated options will be removed in future versions. "
"Please use the new options.\n";
// default deprecated values
if (nargs == 1) {
return std::make_tuple(DEFAULT_TCP_RX_PORTNO, 1, false);
}
// parse deprecated arguments
uint16_t p = parsePort(args[1].c_str());
uint16_t n = parseNumReceivers(args[2].c_str());
bool o = false;
if (nargs == 4) {
try {
o = sls::StringTo<bool>(args[3].c_str());
} catch (...) {
throw sls::RuntimeError("Invalid optional argument "
"parsed. Expected 1 (true) or "
"0 (false).");
}
}
return std::make_tuple(p, n, o);
}
std::string CommandLineOptions::getTypeString() const {
switch (appType_) {
case AppType::SingleReceiver:
return "slsReceiver";
case AppType::MultiReceiver:
return "slsMultiReceiver";
case AppType::FrameSynchronizer:
return "slsFrameSynchronizer";
default:
return "Unknown";
}
}
std::string CommandLineOptions::getVersion() const {
return getTypeString() + " Version: " + APIRECEIVER;
}
std::string CommandLineOptions::getHelpMessage() const {
switch (appType_) {
case AppType::SingleReceiver:
return std::string("\nUsage: ") + getTypeString() + " Options:\n" +
"\t-v, --version : Version.\n" +
"\t-p, --port : TCP port to communicate with client "
"for "
"configuration. Non-zero and 16 bit.\n" +
"\t-u, --uid : Set effective user id if receiver "
"started "
"with privileges. \n\n";
case AppType::MultiReceiver:
return std::string("\nUsage: " + getTypeString() + " Options:\n") +
"\t-v, --version : Version.\n" +
"\t-n, --num-receivers : Number of receivers.\n" +
"\t-p, --port : TCP port to communicate with client "
"for "
"configuration. Non-zero and 16 bit.\n" +
"\t-c, --callback : Enable dummy callbacks for debugging. "
"Disabled by default. \n" +
"\t-u, --uid : Set effective user id if receiver "
"started "
"with privileges. \n\n";
case AppType::FrameSynchronizer:
return std::string("\nUsage: " + getTypeString() + " Options:\n") +
"\t-v, --version : Version.\n" +
"\t-n, --num-receivers : Number of receivers.\n" +
"\t-p, --port : TCP port to communicate with client "
"for "
"configuration. Non-zero and 16 bit.\n" +
"\t-c, --print-headers : Print callback headers for debugging. "
"Disabled by default.\n" +
"\t-u, --uid : Set effective user id if receiver "
"started "
"with privileges. \n\n";
}
throw sls::RuntimeError("Unknown AppType for help message");
}
void CommandLineOptions::setEffectiveUID(uid_t uid) {
if (geteuid() == uid) {
LOG(sls::logINFO) << "Process already has the same Effective UID "
<< uid;
} else {
if (seteuid(uid) != 0 || geteuid() != uid) {
throw sls::RuntimeError("Could not set Effective UID");
}
LOG(sls::logINFO) << "Process Effective UID changed to " << uid;
}
}

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#pragma once
#include <cstdint>
#include <getopt.h>
#include <string>
#include <tuple>
#include <variant>
#include <vector>
enum class AppType { MultiReceiver, SingleReceiver, FrameSynchronizer };
struct CommonOptions {
uint16_t port = -1;
uid_t userid = -1;
bool versionRequested = false;
bool helpRequested = false;
};
struct MultiReceiverOptions : CommonOptions {
uint16_t numReceivers = 1;
bool callbackEnabled = false;
};
struct FrameSyncOptions : CommonOptions {
uint16_t numReceivers = 1;
bool printHeaders = false;
};
using ParsedOptions =
std::variant<CommonOptions, MultiReceiverOptions, FrameSyncOptions>;
class CommandLineOptions {
public:
explicit CommandLineOptions(AppType app);
ParsedOptions parse(const std::vector<std::string> &args); // for testing
ParsedOptions parse(int argc, char *argv[]);
std::string getTypeString() const;
std::string getVersion() const;
std::string getHelpMessage() const;
static void setEffectiveUID(uid_t uid);
static std::tuple<uint16_t, uint16_t, bool>
ParseDeprecated(const std::vector<std::string> &args);
private:
AppType appType_;
std::string optString_;
std::vector<option> longOptions_;
std::vector<option> buildOptionList() const;
std::string buildOptString() const;
static uint16_t parsePort(const char *optarg);
static uint16_t parseNumReceivers(const char *optarg);
static uid_t parseUID(const char *optarg);
void handleCommonOption(int opt, const char *optarg, CommonOptions &base);
void handleAppSpecificOption(int opt, const char *optarg,
CommonOptions &base,
MultiReceiverOptions &multi,
FrameSyncOptions &frame);
static constexpr uint16_t MAX_RECEIVERS = 1000;
};

View File

@@ -3,7 +3,7 @@
/************************************************
* @file DataProcessor.cpp
* @short creates data processor thread that
* pulls pointers to memory addresses from fifos
* pulls pointers to memory addresses from fifos
* and processes data stored in them & writes them to file
***********************************************/
@@ -23,6 +23,7 @@
#include <cerrno>
#include <cstring>
#include <iostream>
#include <numeric>
namespace sls {
@@ -47,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<slsDetectorDefs::ROI> &args) {
multiRoiMetadata = args;
}
void DataProcessor::SetDataStreamEnable(bool enable) {
@@ -71,12 +77,6 @@ void DataProcessor::SetStreamingStartFnum(uint32_t value) {
void DataProcessor::SetFramePadding(bool enable) { framePadding = enable; }
void DataProcessor::SetCtbDbitList(std::vector<int> value) {
ctbDbitList = value;
}
void DataProcessor::SetCtbDbitOffset(int value) { ctbDbitOffset = value; }
void DataProcessor::SetQuadEnable(bool value) { quadEnable = value; }
void DataProcessor::SetFlipRows(bool fd) {
@@ -159,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;
}
}
@@ -206,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 =
@@ -229,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,
@@ -240,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
@@ -300,13 +291,13 @@ void DataProcessor::ThreadExecution() {
memImage->data);
} catch (const std::exception &e) {
fifo->FreeAddress(buffer);
return;
throw RuntimeError(e.what());
}
// 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);
@@ -333,6 +324,7 @@ void DataProcessor::StopProcessing(char *buf) {
void DataProcessor::ProcessAnImage(sls_receiver_header &header, size_t &size,
size_t &firstImageIndex, char *data) {
uint64_t fnum = header.detHeader.frameNumber;
LOG(logDEBUG1) << "DataProcessing " << index << ": fnum:" << fnum;
currentFrameIndex = fnum;
@@ -356,9 +348,20 @@ void DataProcessor::ProcessAnImage(sls_receiver_header &header, size_t &size,
if (framePadding && nump < generalData->packetsPerFrame)
PadMissingPackets(header, data);
// rearrange ctb digital bits (if ctbDbitlist is not empty)
if (!ctbDbitList.empty()) {
RearrangeDbitData(size, data);
if (generalData->readoutType == slsDetectorDefs::DIGITAL_ONLY ||
generalData->readoutType == slsDetectorDefs::ANALOG_AND_DIGITAL ||
generalData->readoutType == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) {
// rearrange ctb digital bits
if (!generalData->ctbDbitList.empty()) {
ArrangeDbitData(size, data);
} else if (generalData->ctbDbitReorder) {
std::vector<int> ctbDbitList(64);
std::iota(ctbDbitList.begin(), ctbDbitList.end(), 0);
generalData->SetctbDbitList(ctbDbitList);
ArrangeDbitData(size, data);
} else if (generalData->ctbDbitOffset > 0) {
RemoveTrailingBits(size, data);
}
}
// 'stream Image' check has to be done here before crop image
@@ -374,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,
@@ -505,16 +508,6 @@ void DataProcessor::PadMissingPackets(sls_receiver_header header, char *data) {
// missing packet
switch (generalData->detType) {
// for gotthard, 1st packet: 4 bytes fnum, CACA + CACA, 639*2 bytes
// data
// 2nd packet: 4 bytes fnum, previous 1*2 bytes data +
// 640*2 bytes data !!
case GOTTHARD:
if (pnum == 0u)
memset(data + (pnum * dsize), 0xFF, dsize - 2);
else
memset(data + (pnum * dsize), 0xFF, dsize + 2);
break;
case CHIPTESTBOARD:
case XILINX_CHIPTESTBOARD:
if (pnum == (pperFrame - 1))
@@ -530,11 +523,53 @@ void DataProcessor::PadMissingPackets(sls_receiver_header header, char *data) {
}
}
void DataProcessor::RemoveTrailingBits(size_t &size, char *data) {
if (!(generalData->detType == slsDetectorDefs::CHIPTESTBOARD ||
generalData->detType == slsDetectorDefs::XILINX_CHIPTESTBOARD)) {
throw std::runtime_error("behavior undefined for detector " +
std::to_string(generalData->detType));
}
const size_t nAnalogDataBytes = generalData->GetNumberOfAnalogDatabytes();
const size_t nDigitalDataBytes = generalData->GetNumberOfDigitalDatabytes();
const size_t nTransceiverDataBytes =
generalData->GetNumberOfTransceiverDatabytes();
const size_t ctbDbitOffset = generalData->ctbDbitOffset;
const size_t ctbDigitalDataBytes = nDigitalDataBytes - ctbDbitOffset;
// no digital data
if (ctbDigitalDataBytes == 0) {
LOG(logWARNING)
<< "No digital data for call back, yet ctbDbitOffset is non zero.";
return;
}
// update size and copy data
memmove(data + nAnalogDataBytes, data + nAnalogDataBytes + ctbDbitOffset,
ctbDigitalDataBytes + nTransceiverDataBytes);
size = nAnalogDataBytes + ctbDigitalDataBytes + nTransceiverDataBytes;
}
/** ctb specific */
void DataProcessor::RearrangeDbitData(size_t &size, char *data) {
int nAnalogDataBytes = generalData->GetNumberOfAnalogDatabytes();
int nDigitalDataBytes = generalData->GetNumberOfDigitalDatabytes();
int nTransceiverDataBytes = generalData->GetNumberOfTransceiverDatabytes();
void DataProcessor::ArrangeDbitData(size_t &size, char *data) {
if (!(generalData->detType == slsDetectorDefs::CHIPTESTBOARD ||
generalData->detType == slsDetectorDefs::XILINX_CHIPTESTBOARD)) {
throw std::runtime_error("behavior undefined for detector " +
std::to_string(generalData->detType));
}
const size_t nAnalogDataBytes = generalData->GetNumberOfAnalogDatabytes();
const size_t nDigitalDataBytes = generalData->GetNumberOfDigitalDatabytes();
const size_t nTransceiverDataBytes =
generalData->GetNumberOfTransceiverDatabytes();
const size_t ctbDbitOffset = generalData->ctbDbitOffset;
const bool ctbDbitReorder = generalData->ctbDbitReorder;
const auto ctbDbitList = generalData->ctbDbitList;
// TODO! (Erik) Refactor and add tests
int ctbDigitalDataBytes = nDigitalDataBytes - ctbDbitOffset;
@@ -545,61 +580,115 @@ void DataProcessor::RearrangeDbitData(size_t &size, char *data) {
return;
}
char *source = (data + nAnalogDataBytes + ctbDbitOffset);
const int numDigitalSamples = (ctbDigitalDataBytes / sizeof(uint64_t));
// const int numResult8Bits = ceil((numDigitalSamples * ctbDbitList.size())
// / 8.00);
int numBitsPerDbit = numDigitalSamples;
if ((numBitsPerDbit % 8) != 0)
numBitsPerDbit += (8 - (numDigitalSamples % 8));
const int totalNumBytes = (numBitsPerDbit / 8) * ctbDbitList.size();
std::vector<uint8_t> result(totalNumBytes);
int totalNumBytes =
0; // number of bytes for selected digital data given by dtbDbitList
// store each selected bit from all samples consecutively
if (ctbDbitReorder) {
size_t numBitsPerDbit =
numDigitalSamples; // num bits per selected digital
// Bit for all samples
if ((numBitsPerDbit % 8) != 0)
numBitsPerDbit += (8 - (numDigitalSamples % 8));
totalNumBytes = (numBitsPerDbit / 8) * ctbDbitList.size();
}
// store all selected bits from one sample consecutively
else {
size_t numBitsPerSample =
ctbDbitList.size(); // num bits for all selected bits per sample
if ((numBitsPerSample % 8) != 0)
numBitsPerSample += (8 - (numBitsPerSample % 8));
totalNumBytes = (numBitsPerSample / 8) * numDigitalSamples;
}
std::vector<uint8_t> result(totalNumBytes, 0);
uint8_t *dest = &result[0];
auto *source = (uint64_t *)(data + nAnalogDataBytes + ctbDbitOffset);
// loop through digital bit enable vector
int bitoffset = 0;
for (auto bi : ctbDbitList) {
// where numbits * numDigitalSamples is not a multiple of 8
if (bitoffset != 0) {
bitoffset = 0;
++dest;
}
// loop through the frame digital data
for (auto *ptr = source; ptr < (source + numDigitalSamples);) {
// get selected bit from each 8 bit
uint8_t bit = (*ptr++ >> bi) & 1;
*dest |= bit << bitoffset;
++bitoffset;
// extract destination in 8 bit batches
if (bitoffset == 8) {
if (ctbDbitReorder) {
// loop through digital bit enable vector
int bitoffset = 0;
for (auto bi : ctbDbitList) {
// where numbits * numDigitalSamples is not a multiple of 8
if (bitoffset != 0) {
bitoffset = 0;
++dest;
}
uint8_t byte_index = bi / 8;
// loop through the frame digital data
for (auto *ptr = source + byte_index;
ptr < (source + 8 * numDigitalSamples); ptr += 8) {
// get selected bit from each 8 bit
uint8_t bit = (*ptr >> bi % 8) & 1;
*dest |= bit << bitoffset; // stored as least significant
++bitoffset;
// extract destination in 8 bit batches
if (bitoffset == 8) {
bitoffset = 0;
++dest;
}
}
}
} else {
// loop through the digital data
int bitoffset = 0;
for (auto *ptr = source; ptr < (source + 8 * numDigitalSamples);
ptr += 8) {
// where bit enable vector size is not a multiple of 8
if (bitoffset != 0) {
bitoffset = 0;
++dest;
}
// loop through digital bit enable vector
for (auto bi : ctbDbitList) {
// get selected bit from each 64 bit
uint8_t byte_index = bi / 8;
uint8_t bit = (*(ptr + byte_index) >> (bi % 8)) & 1;
*dest |= bit << bitoffset;
++bitoffset;
// extract destination in 8 bit batches
if (bitoffset == 8) {
bitoffset = 0;
++dest;
}
}
}
}
size = totalNumBytes * sizeof(uint8_t) + nAnalogDataBytes +
nTransceiverDataBytes;
// check if size changed, if so move transceiver data to avoid gap in memory
if (size != nAnalogDataBytes + nDigitalDataBytes + nTransceiverDataBytes)
memmove(data + nAnalogDataBytes + totalNumBytes * sizeof(uint8_t),
data + nAnalogDataBytes + nDigitalDataBytes,
nTransceiverDataBytes);
// copy back to memory and update size
memcpy(data + nAnalogDataBytes, result.data(),
totalNumBytes * sizeof(uint8_t));
size = totalNumBytes * sizeof(uint8_t) + nAnalogDataBytes + ctbDbitOffset +
nTransceiverDataBytes;
LOG(logDEBUG1) << "totalNumBytes: " << totalNumBytes
LOG(logDEBUG1) << "nDigitalDataBytes: " << totalNumBytes
<< " nAnalogDataBytes:" << nAnalogDataBytes
<< " ctbDbitOffset:" << ctbDbitOffset
<< " nTransceiverDataBytes:" << nTransceiverDataBytes
<< " size:" << size;
<< " toal size:" << size;
}
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) {

View File

@@ -39,14 +39,13 @@ 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<slsDetectorDefs::ROI> &args);
void SetDataStreamEnable(bool enable);
void SetStreamingFrequency(uint32_t value);
void SetStreamingTimerInMs(uint32_t value);
void SetStreamingStartFnum(uint32_t value);
void SetFramePadding(bool enable);
void SetCtbDbitList(std::vector<int> value);
void SetCtbDbitOffset(int value);
void SetQuadEnable(bool value);
void SetFlipRows(bool fd);
void SetNumberofTotalFrames(uint64_t value);
@@ -71,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);
@@ -91,6 +90,20 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject {
size_t &, void *),
void *arg);
protected:
/**
* Align corresponding digital bits together (CTB only if ctbDbitlist is not
* empty)
* set variable reorder to true if data should be rearranged such that
* it groups each signal (0-63) from all the different samples together
*/
void ArrangeDbitData(size_t &size, char *data);
/**
* remove trailing bits in digital data stream
*/
void RemoveTrailingBits(size_t &size, char *data);
private:
void RecordFirstIndex(uint64_t fnum);
@@ -137,12 +150,6 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject {
void PadMissingPackets(sls_receiver_header header, char *data);
/**
* Align corresponding digital bits together (CTB only if ctbDbitlist is not
* empty)
*/
void RearrangeDbitData(size_t &size, char *data);
void CropImage(size_t &size, char *data);
static const std::string typeName;
@@ -153,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<ROI> multiRoiMetadata{};
std::unique_ptr<char[]> completeImageToStreamBeforeCropping;
/** if 0, sending random images with a timer */
uint32_t streamingFrequency;
@@ -164,8 +173,6 @@ class DataProcessor : private virtual slsDetectorDefs, public ThreadObject {
uint32_t currentFreqCount{0};
struct timespec timerbegin {};
bool framePadding;
std::vector<int> ctbDbitList;
int ctbDbitOffset;
std::atomic<bool> startedFlag{false};
std::atomic<uint64_t> firstIndex{0};
bool quadEnable{false};

View File

@@ -22,10 +22,7 @@ DataStreamer::DataStreamer(int index) : ThreadObject(index, TypeName) {
LOG(logDEBUG) << "DataStreamer " << index << " created";
}
DataStreamer::~DataStreamer() {
CloseZmqSocket();
delete[] completeBuffer;
}
DataStreamer::~DataStreamer() { CloseZmqSocket(); }
void DataStreamer::SetFifo(Fifo *f) { fifo = f; }
@@ -56,25 +53,20 @@ 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();
startedFlag = false;
firstIndex = 0;
fileNametoStream = fname;
if (completeBuffer) {
delete[] completeBuffer;
completeBuffer = nullptr;
}
if (generalData->detType == GOTTHARD &&
generalData->detectorRoi.xmin != -1) {
adcConfigured =
generalData->GetAdcConfigured(index, generalData->detectorRoi);
completeBuffer = new char[generalData->imageSizeComplete];
memset(completeBuffer, 0, generalData->imageSizeComplete);
}
}
void DataStreamer::RecordFirstIndex(uint64_t fnum, size_t firstImageIndex) {
@@ -160,40 +152,15 @@ void DataStreamer::ProcessAnImage(sls_detector_header header, size_t size,
uint64_t fnum = header.frameNumber;
LOG(logDEBUG1) << "DataStreamer " << index << ": fnum:" << fnum;
// shortframe gotthard
if (completeBuffer) {
// disregarding the size modified from callback (always using
// imageSizeComplete instead of size because gui needs
// imagesizecomplete and listener writes imagesize to size
if (!SendDataHeader(header, generalData->imageSizeComplete,
generalData->nPixelsXComplete,
generalData->nPixelsYComplete)) {
LOG(logERROR) << "Could not send zmq header for fnum " << fnum
<< " and streamer " << index;
}
memcpy(completeBuffer + ((generalData->imageSize) * adcConfigured),
data, size);
if (!zmqSocket->SendData(completeBuffer,
generalData->imageSizeComplete)) {
LOG(logERROR) << "Could not send zmq data for fnum " << fnum
<< " and streamer " << index;
}
if (!SendDataHeader(header, size, generalData->nPixelsX,
generalData->nPixelsY)) {
LOG(logERROR) << "Could not send zmq header for fnum " << fnum
<< " and streamer " << index;
}
// normal
else {
if (!SendDataHeader(header, size, generalData->nPixelsX,
generalData->nPixelsY)) {
LOG(logERROR) << "Could not send zmq header for fnum " << fnum
<< " and streamer " << index;
}
if (!zmqSocket->SendData(data, size)) {
LOG(logERROR) << "Could not send zmq data for fnum " << fnum
<< " and streamer " << index;
}
if (!zmqSocket->SendData(data, size)) {
LOG(logERROR) << "Could not send zmq data for fnum " << fnum
<< " and streamer " << index;
}
}
@@ -250,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);
}

View File

@@ -38,7 +38,7 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject {
void SetNumberofTotalFrames(uint64_t value);
void
SetAdditionalJsonHeader(const std::map<std::string, std::string> &json);
void SetReceiverROI(ROI roi);
void SetPortROI(ROI roi);
void ResetParametersforNewAcquisition(const std::string &fname);
/**
@@ -88,11 +88,10 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject {
const GeneralData *generalData{nullptr};
Fifo *fifo{nullptr};
ZmqSocket *zmqSocket{nullptr};
int adcConfigured{-1};
uint64_t fileIndex{0};
bool flipRows{false};
std::map<std::string, std::string> additionalJsonHeader;
ROI receiverRoi{};
ROI portRoi{};
/** Used by streamer thread to update local copy (reduce number of locks
* during streaming) */
@@ -107,8 +106,6 @@ class DataStreamer : private virtual slsDetectorDefs, public ThreadObject {
bool startedFlag{false};
uint64_t firstIndex{0};
std::string fileNametoStream;
/** Complete buffer used for detectorRoi, eg. shortGotthard */
char *completeBuffer{nullptr};
xy numPorts{1, 1};
bool quadEnable{false};

View File

@@ -5,17 +5,17 @@
* reconstructing image. Sample python script for pull socket for this combiner
* in python/scripts folder. TODO: Not handling empty frames from one socket
*/
#include "CommandLineOptions.h"
#include "sls/Receiver.h"
#include "sls/ToString.h"
#include "sls/container_utils.h"
#include "sls/logger.h"
#include "sls/network_utils.h"
#include "sls/sls_detector_defs.h"
#include "sls/versionAPI.h"
#include <csignal> //SIGINT
#include <cstdio>
#include <cstring>
#include <iostream>
#include <mutex>
#include <ostream>
#include <semaphore.h>
@@ -29,9 +29,14 @@
#include <zmq.h>
std::vector<std::thread> threads;
std::vector<sem_t *> semaphores;
sls::TLogLevel printHeadersLevel = sls::logDEBUG;
// gettid added in glibc 2.30
#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)
#endif
/** Define Colors to print data call back in different colors for different
* recievers */
#define PRINT_IN_COLOR(c, f, ...) \
@@ -58,16 +63,6 @@ struct FrameStatus {
};
FrameStatus *global_frame_status = nullptr;
/**
* Control+C Interrupt Handler
* to let all the processes know to exit properly
*/
void sigInterruptHandler(int p) {
for (size_t i = 0; i != semaphores.size(); ++i) {
sem_post(semaphores[i]);
}
}
void cleanup() {
if (global_frame_status) {
std::lock_guard<std::mutex> lock(global_frame_status->mtx);
@@ -87,21 +82,6 @@ void cleanup() {
}
}
/**
* prints usage of this example program
*/
std::string getHelpMessage() {
std::ostringstream os;
os << "\nUsage:\n"
<< "./slsFrameSynchronizer --version or -v\n"
<< "\t - Gets the slsFrameSynchronizer version\n\n"
<< "./slsFrameSynchronizer [start tcp port] [num recevers] [print "
"callback headers (optional)]\n"
<< "\t - tcp port has to be non-zero and 16 bit\n"
<< "\t - print callback headers option is 0 (disabled) by default\n";
return os.str();
}
void zmq_free(void *data, void *hint) { delete[] static_cast<char *>(data); }
void print_frames(const PortFrameMap &frame_port_map) {
@@ -282,7 +262,7 @@ void Correlate(FrameStatus *stat) {
zmq_ctx_destroy(context);
}
int StartAcquisitionCallback(
void StartAcquisitionCallback(
const slsDetectorDefs::startCallbackHeader callbackHeader,
void *objectPointer) {
LOG(printHeadersLevel)
@@ -354,7 +334,6 @@ int StartAcquisitionCallback(
}
}
sem_post(&stat->available);
return slsDetectorDefs::OK; // TODO: change return to void
}
void AcquisitionFinishedCallback(
@@ -516,93 +495,46 @@ void GetDataCallback(slsDetectorDefs::sls_receiver_header &header,
sem_post(&stat->available);
}
std::vector<sem_t> semaphores;
/**
* Example of main program using the Receiver class
*
* - Defines in file for:
* - Default Number of receivers is 1
* - Default Start TCP port is 1954
* Control+C Interrupt Handler
* to let all the processes know to exit properly
* Only the main thread will call this handler
*/
void sigInterruptHandler(int p) {
(void)signal; // suppress unused warning if needed
for (auto &s : semaphores) {
sem_post(&s);
}
}
int main(int argc, char *argv[]) {
// version
if (argc == 2) {
std::string sargv1 = std::string(argv[1]);
if (sargv1 == "--version" || sargv1 == "-v") {
std::cout << "slsFrameSynchronizer Version: " << APIRECEIVER
<< std::endl;
exit(EXIT_SUCCESS);
}
CommandLineOptions cli(AppType::FrameSynchronizer);
ParsedOptions opts;
try {
opts = cli.parse(argc, argv);
} catch (sls::RuntimeError &e) {
return EXIT_FAILURE;
}
auto &f = std::get<FrameSyncOptions>(opts);
if (f.versionRequested || f.helpRequested) {
return EXIT_SUCCESS;
}
/** - set default values */
int numReceivers = 1;
uint16_t startTCPPort = DEFAULT_TCP_RX_PORTNO;
bool printHeaders = false;
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << ']';
/** - get number of receivers and start tcp port from command line
* arguments */
if (argc > 1) {
try {
if (argc == 3 || argc == 4) {
startTCPPort = sls::StringTo<uint16_t>(argv[1]);
if (startTCPPort == 0) {
throw std::runtime_error("Invalid start tcp port");
}
numReceivers = std::stoi(argv[2]);
if (numReceivers > 1024) {
cprintf(RED,
"Did you mix up the order of the arguments?\n%s\n",
getHelpMessage().c_str());
return EXIT_FAILURE;
}
if (numReceivers == 0) {
cprintf(RED, "Invalid number of receivers.\n%s\n",
getHelpMessage().c_str());
return EXIT_FAILURE;
}
if (argc == 4) {
printHeaders = sls::StringTo<bool>(argv[3]);
if (printHeaders) {
printHeadersLevel = sls::logINFOBLUE;
}
}
} else
throw std::runtime_error("Invalid number of arguments");
} catch (const std::exception &e) {
cprintf(RED, "Error: %s\n%s\n", e.what(), getHelpMessage().c_str());
return EXIT_FAILURE;
}
// close files on ctrl+c
sls::setupSignalHandler(SIGINT, sigInterruptHandler);
// handle locally on socket crash
sls::setupSignalHandler(SIGPIPE, SIG_IGN);
semaphores.resize(f.numReceivers);
for (auto &s : semaphores) {
sem_init(&s, 0, 0);
}
cprintf(RESET, "Number of Receivers: %d\n", numReceivers);
cprintf(RESET, "Start TCP Port: %hu\n", startTCPPort);
cprintf(RESET, "Print Callback Headers: %s\n\n",
(printHeaders ? "Enabled" : "Disabled"));
/** - Catch signal SIGINT to close files and call destructors properly */
struct sigaction sa;
sa.sa_flags = 0; // no flags
sa.sa_handler = sigInterruptHandler; // handler function
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
// of handler
if (sigaction(SIGINT, &sa, nullptr) == -1) {
cprintf(RED, "Could not set handler function for SIGINT\n");
}
/** - Ignore SIG_PIPE, prevents global signal handler, handle locally,
instead of a server crashing due to client crash when writing, it just
gives error */
struct sigaction asa;
asa.sa_flags = 0; // no flags
asa.sa_handler = SIG_IGN; // handler function
sigemptyset(&asa.sa_mask); // dont block additional signals during
// invocation of handler
if (sigaction(SIGPIPE, &asa, nullptr) == -1) {
cprintf(RED, "Could not set handler function for SIGPIPE\n");
}
FrameStatus stat{true, false, numReceivers};
FrameStatus stat{true, false, f.numReceivers};
// store pointer for signal handler
global_frame_status = &stat;
@@ -610,34 +542,44 @@ int main(int argc, char *argv[]) {
void *user_data = static_cast<void *>(&stat);
std::thread combinerThread(Correlate, &stat);
for (int i = 0; i != numReceivers; ++i) {
sem_t *semaphore = new sem_t;
sem_init(semaphore, 1, 0);
semaphores.push_back(semaphore);
std::exception_ptr threadException = nullptr;
for (int i = 0; i != f.numReceivers; ++i) {
uint16_t port = f.port + i;
sem_t *semaphore = &semaphores[i];
threads.emplace_back(
[i, semaphore, port, user_data, &threadException]() {
LOG(sls::logINFOBLUE)
<< "Thread " << i << " [ Tid: " << gettid() << ']';
try {
sls::Receiver receiver(port);
receiver.registerCallBackStartAcquisition(
StartAcquisitionCallback, user_data);
receiver.registerCallBackAcquisitionFinished(
AcquisitionFinishedCallback, user_data);
receiver.registerCallBackRawDataReady(GetDataCallback,
user_data);
uint16_t port = startTCPPort + i;
threads.emplace_back([i, semaphore, port, user_data]() {
sls::Receiver receiver(port);
receiver.registerCallBackStartAcquisition(StartAcquisitionCallback,
user_data);
receiver.registerCallBackAcquisitionFinished(
AcquisitionFinishedCallback, user_data);
receiver.registerCallBackRawDataReady(GetDataCallback, user_data);
/** - as long as no Ctrl+C */
sem_wait(semaphore);
sem_destroy(semaphore);
delete semaphore;
// clean up frames
if (i == 0)
cleanup();
});
/** - as long as no Ctrl+C */
// each child shares the common semaphore
sem_wait(semaphore);
} catch (...) {
// capture exception and raise SIGINT to exit gracefully
threadException = std::current_exception();
raise(SIGINT);
}
LOG(sls::logINFOBLUE)
<< "Exiting Thread " << i << " [ Tid: " << gettid() << " ]";
});
}
for (auto &thread : threads) {
thread.join();
for (auto &t : threads) {
t.join();
}
for (auto &s : semaphores)
sem_destroy(&s);
cleanup();
{
std::lock_guard<std::mutex> lock(stat.mtx);
stat.terminate = true;
@@ -646,6 +588,19 @@ int main(int argc, char *argv[]) {
combinerThread.join();
sem_destroy(&stat.available);
if (threadException) {
try {
std::rethrow_exception(threadException);
} catch (const std::exception &e) {
LOG(sls::logERROR)
<< "Unhandled exception from thread: " << e.what();
return EXIT_FAILURE;
} catch (...) {
LOG(sls::logERROR) << "Unknown exception occurred in thread";
return EXIT_FAILURE;
}
}
LOG(sls::logINFOBLUE) << "Goodbye!";
return EXIT_SUCCESS;
}

View File

@@ -18,6 +18,135 @@
namespace sls {
struct CtbImageInputs {
slsDetectorDefs::readoutMode mode{slsDetectorDefs::ANALOG_ONLY};
int nAnalogSamples{};
uint32_t adcMask{};
int nTransceiverSamples{};
uint32_t transceiverMask{};
int nDigitalSamples{};
int dbitOffset{};
bool dbitReorder{};
std::vector<int> dbitList{};
inline void print() const {
LOG(logINFO) << "CTB Image Inputs: "
<< "Readout Mode:" << ToString(mode)
<< "\n\tNumber of Analog Samples:" << nAnalogSamples
<< "\n\tADC Enable 1G:" << std::hex << adcMask << std::dec
<< "\n\tNumber of Transceiver Samples:"
<< nTransceiverSamples
<< "\n\tTransceiver Mask:" << std::hex << transceiverMask
<< std::dec
<< "\n\tNumber of Digital Samples:" << nDigitalSamples
<< "\n\tDBIT Offset:" << dbitOffset
<< "\n\tDBIT Reorder:" << dbitReorder
<< "\n\tDBIT List:" << ToString(dbitList);
}
};
struct CtbImageOutputs {
int nAnalogBytes{};
int nDigitalBytes{};
int nDigitalBytesReserved{}; // including dbit offset and for 64 bits
int nTransceiverBytes{};
int nPixelsX{};
inline void print() const {
LOG(logINFO) << "CTB Image Outputs: "
<< "\n\tNumber of Analog Bytes:" << nAnalogBytes
<< "\n\tNumber of Actual Digital Bytes:" << nDigitalBytes
<< "\n\tNumber of Digital Bytes Reserved:"
<< nDigitalBytesReserved
<< "\n\tNumber of Transceiver Bytes:" << nTransceiverBytes
<< "\n\tNumber of Pixels in X:" << nPixelsX;
}
};
inline CtbImageOutputs computeCtbImageSize(const CtbImageInputs &in) {
CtbImageOutputs out{};
constexpr int num_bytes_per_analog_channel = 2;
constexpr int num_bytes_per_transceiver_channel = 8;
constexpr int max_digital_channels = 64;
// in.print(); // for debugging
// analog channels (normal, analog/digital readout)
if (in.mode == slsDetectorDefs::ANALOG_ONLY ||
in.mode == slsDetectorDefs::ANALOG_AND_DIGITAL) {
int nAnalogChans = __builtin_popcount(in.adcMask);
out.nPixelsX += nAnalogChans;
out.nAnalogBytes =
nAnalogChans * num_bytes_per_analog_channel * in.nAnalogSamples;
LOG(logDEBUG1) << " Number of Analog Channels:" << nAnalogChans
<< " Databytes: " << out.nAnalogBytes;
}
// digital channels
if (in.mode == slsDetectorDefs::DIGITAL_ONLY ||
in.mode == slsDetectorDefs::ANALOG_AND_DIGITAL ||
in.mode == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) {
int nSamples = in.nDigitalSamples;
{
// allocate enought for 64 bits and dbit offset for now
// TODO: to be replaced in the future with the actual reserved and
// used
int32_t num_bytes_per_bit =
(nSamples % 8 == 0) ? (nSamples / 8) : (nSamples / 8 + 1);
out.nDigitalBytesReserved =
max_digital_channels * num_bytes_per_bit;
LOG(logDEBUG1) << "Number of Digital Channels:"
<< max_digital_channels << " Databytes reserved: "
<< out.nDigitalBytesReserved;
}
// remove offset
if (in.dbitOffset > 0) {
int nBytesReserved = out.nDigitalBytesReserved - in.dbitOffset;
nSamples = nBytesReserved / sizeof(uint64_t);
}
// calculate channels
int nChans = in.dbitList.size();
if (nChans == 0) {
nChans = max_digital_channels;
}
out.nPixelsX += nChans;
// calculate actual bytes
if (!in.dbitReorder) {
uint32_t nBitsPerSample = nChans;
if (nBitsPerSample % 8 != 0) {
nBitsPerSample += (8 - (nBitsPerSample % 8));
}
out.nDigitalBytes = (nBitsPerSample / 8) * nSamples;
} else {
uint32_t nBitsPerSignal = nSamples;
if (nBitsPerSignal % 8 != 0) {
nBitsPerSignal += (8 - (nBitsPerSignal % 8));
}
out.nDigitalBytes = nChans * (nBitsPerSignal / 8);
}
LOG(logDEBUG1) << "Number of Actual Digital Channels:" << nChans
<< " Databytes: " << out.nDigitalBytes;
}
// transceiver channels
if (in.mode == slsDetectorDefs::TRANSCEIVER_ONLY ||
in.mode == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) {
int nTransceiverChans = __builtin_popcount(in.transceiverMask);
out.nPixelsX += nTransceiverChans;
out.nTransceiverBytes = nTransceiverChans *
num_bytes_per_transceiver_channel *
in.nTransceiverSamples;
LOG(logDEBUG1) << "Number of Transceiver Channels:" << nTransceiverChans
<< " Databytes: " << out.nTransceiverBytes;
}
return out;
}
class GeneralData {
public:
@@ -40,13 +169,6 @@ class GeneralData {
uint32_t fifoDepth{0};
int numUDPInterfaces{1};
uint32_t headerPacketSize{0};
/** Streaming (for ROI - mainly short Gotthard) */
uint32_t nPixelsXComplete{0};
/** Streaming (for ROI - mainly short Gotthard) */
uint32_t nPixelsYComplete{0};
/** Streaming (for ROI - mainly short Gotthard) - Image size (in bytes) */
uint32_t imageSizeComplete{0};
/** if standard header implemented in firmware */
bool standardheader{false};
uint32_t udpSocketBufferSize{RECEIVE_SOCKET_BUFFER_SIZE};
uint32_t vetoDataSize{0};
@@ -59,15 +181,18 @@ class GeneralData {
uint32_t nAnalogSamples{0};
uint32_t nDigitalSamples{0};
uint32_t nTransceiverSamples{0};
std::vector<int> ctbDbitList{};
int ctbDbitOffset{0};
bool ctbDbitReorder{false};
slsDetectorDefs::readoutMode readoutType{slsDetectorDefs::ANALOG_ONLY};
uint32_t adcEnableMaskOneGiga{BIT32_MASK};
uint32_t adcEnableMaskTenGiga{BIT32_MASK};
slsDetectorDefs::ROI detectorRoi{};
uint32_t counterMask{0};
uint32_t transceiverMask{0};
slsDetectorDefs::frameDiscardPolicy frameDiscardMode{
slsDetectorDefs::NO_DISCARD};
/* actual image size after ctboffset and ctbreorder */
uint32_t actualImageSize{0};
GeneralData(){};
virtual ~GeneralData(){};
@@ -84,14 +209,12 @@ class GeneralData {
* Get Header Infomation (frame number, packet number)
* @param index thread index for debugging purposes
* @param packetData pointer to data
* @param oddStartingPacket odd starting packet (gotthard)
* @param frameNumber frame number
* @param packetNumber packet number
* @param bunchId bunch Id
*/
virtual void GetHeaderInfo(int index, char *packetData,
bool oddStartingPacket, uint64_t &frameNumber,
uint32_t &packetNumber,
uint64_t &frameNumber, uint32_t &packetNumber,
uint64_t &bunchId) const {
frameNumber = ((uint32_t)(*((uint32_t *)(packetData))));
frameNumber++;
@@ -100,16 +223,6 @@ class GeneralData {
bunchId = -1;
}
virtual void SetDetectorROI(slsDetectorDefs::ROI i) {
ThrowGenericError("SetDetectorROI");
};
/**@returns adc configured */
virtual int GetAdcConfigured(int index, slsDetectorDefs::ROI i) const {
ThrowGenericError("GetAdcConfigured");
return 0;
};
virtual void SetDynamicRange(int dr) {
ThrowGenericError("SetDynamicRange");
};
@@ -118,11 +231,6 @@ class GeneralData {
ThrowGenericError("SetTenGigaEnable");
};
virtual bool SetOddStartingPacket(int index, char *packetData) {
ThrowGenericError("SetOddStartingPacket");
return false;
};
virtual void SetNumberofInterfaces(const int n) {
ThrowGenericError("SetNumberofInterfaces");
};
@@ -173,138 +281,17 @@ class GeneralData {
virtual void SetTransceiverEnableMask(int n) {
ThrowGenericError("SetTransceiverEnableMask");
};
};
class GotthardData : public GeneralData {
private:
const int nChan = 128;
const int nChipsPerAdc = 2;
public:
GotthardData() {
detType = slsDetectorDefs::GOTTHARD;
nPixelsY = 1;
headerSizeinPacket = 6;
framesPerFile = MAX_FRAMES_PER_FILE;
UpdateImageSize();
virtual void SetctbDbitOffset(const int n) {
ThrowGenericError("SetctbDbitOffset");
};
/**
* Get Header Infomation (frame number, packet number)
* @param index thread index for debugging purposes
* @param packetData pointer to data
* @param oddStartingPacket odd starting packet (gotthard)
* @param frameNumber frame number
* @param packetNumber packet number
* @param bunchId bunch Id
*/
void GetHeaderInfo(int index, char *packetData, bool oddStartingPacket,
uint64_t &frameNumber, uint32_t &packetNumber,
uint64_t &bunchId) const {
if (nPixelsX == 1280) {
frameNumber = *reinterpret_cast<uint32_t *>(packetData);
if (oddStartingPacket)
frameNumber++;
packetNumber = frameNumber & packetIndexMask;
frameNumber = (frameNumber & frameIndexMask) >> frameIndexOffset;
} else {
frameNumber = *reinterpret_cast<uint32_t *>(packetData);
packetNumber = 0;
}
bunchId = -1;
}
/** @returns adc configured */
int GetAdcConfigured(int index, slsDetectorDefs::ROI i) const {
int adc = -1;
// single adc
if (i.xmin != -1) {
// gotthard can have only one adc per detector enabled (or all)
// adc = mid value/numchans also for only 1 roi
adc = ((((i.xmax) + (i.xmin)) / 2) / (nChan * nChipsPerAdc));
if ((adc < 0) || (adc > 4)) {
LOG(logWARNING) << index
<< ": Deleting ROI. "
"Adc value should be between 0 and 4";
adc = -1;
}
}
LOG(logINFO) << "Adc Configured: " << adc;
return adc;
virtual void SetctbDbitList(const std::vector<int> &value) {
ThrowGenericError("SetctbDbitList");
};
/**
* Set odd starting packet (gotthard)
* @param index thread index for debugging purposes
* @param packetData pointer to data
* @returns true or false for odd starting packet number
*/
bool SetOddStartingPacket(int index, char *packetData) {
bool oddStartingPacket = true;
// care only if no roi
if (nPixelsX == 1280) {
uint32_t fnum = ((uint32_t)(*((uint32_t *)(packetData))));
uint32_t firstData = ((uint32_t)(*((uint32_t *)(packetData + 4))));
// first packet
if (firstData == 0xCACACACA) {
// packet number should be 0, but is 1 => so odd starting packet
if (fnum & packetIndexMask) {
oddStartingPacket = true;
} else {
oddStartingPacket = false;
}
}
// second packet
else {
// packet number should be 1, but is 0 => so odd starting packet
if (!(fnum & packetIndexMask)) {
oddStartingPacket = true;
} else {
oddStartingPacket = false;
}
}
}
return oddStartingPacket;
};
void SetDetectorROI(slsDetectorDefs::ROI i) {
detectorRoi = i;
UpdateImageSize();
};
private:
void UpdateImageSize() {
// all adcs
if (detectorRoi.xmin == -1) {
nPixelsX = 1280;
dataSize = 1280;
packetsPerFrame = 2;
frameIndexMask = 0xFFFFFFFE;
frameIndexOffset = 1;
packetIndexMask = 1;
framesPerFile = MAX_FRAMES_PER_FILE;
nPixelsXComplete = 0;
nPixelsYComplete = 0;
imageSizeComplete = 0;
fifoDepth = 50000;
} else {
nPixelsX = 256;
dataSize = 512;
packetsPerFrame = 1;
frameIndexMask = 0xFFFFFFFF;
frameIndexOffset = 0;
packetIndexMask = 0;
framesPerFile = SHORT_MAX_FRAMES_PER_FILE;
nPixelsXComplete = 1280;
nPixelsYComplete = 1;
imageSizeComplete = 1280 * 2;
fifoDepth = 75000;
}
imageSize = int(nPixelsX * nPixelsY * GetPixelDepth());
packetSize = headerSizeinPacket + dataSize;
packetsPerFrame = imageSize / dataSize;
virtual void SetctbDbitReorder(const bool reorder) {
ThrowGenericError("SetctbDbitReorder");
};
};
@@ -339,6 +326,7 @@ class EigerData : public GeneralData {
dataSize = (tengigaEnable ? 4096 : 1024);
packetSize = headerSizeinPacket + dataSize;
imageSize = int(nPixelsX * nPixelsY * GetPixelDepth());
actualImageSize = imageSize;
packetsPerFrame = imageSize / dataSize;
fifoDepth = (dynamicRange == 32 ? 100 : 1000);
};
@@ -369,6 +357,7 @@ class JungfrauData : public GeneralData {
nPixelsX = (256 * 4);
nPixelsY = (256 * 2) / numUDPInterfaces;
imageSize = int(nPixelsX * nPixelsY * GetPixelDepth());
actualImageSize = imageSize;
packetsPerFrame = imageSize / dataSize;
udpSocketBufferSize = (1000 * 1024 * 1024) / numUDPInterfaces;
};
@@ -400,6 +389,7 @@ class MoenchData : public GeneralData {
nPixelsX = (400);
nPixelsY = (400) / numUDPInterfaces;
imageSize = int(nPixelsX * nPixelsY * GetPixelDepth());
actualImageSize = imageSize;
packetsPerFrame = imageSize / dataSize;
udpSocketBufferSize = (1000 * 1024 * 1024) / numUDPInterfaces;
};
@@ -451,6 +441,7 @@ class Mythen3Data : public GeneralData {
nPixelsX = (NCHAN * ncounters); // max 1280 channels x 3 counters
LOG(logINFO) << "nPixelsX: " << nPixelsX;
imageSize = nPixelsX * nPixelsY * GetPixelDepth();
actualImageSize = imageSize;
// 10g
if (tengigaEnable) {
@@ -502,14 +493,12 @@ class Gotthard2Data : public GeneralData {
* Get Header Infomation (frame number, packet number) for veto packets
* @param index thread index for debugging purposes
* @param packetData pointer to data
* @param oddStartingPacket odd starting packet (gotthard)
* @param frameNumber frame number
* @param packetNumber packet number
* @param bunchId bunch Id
*/
void GetHeaderInfo(int index, char *packetData, bool oddStartingPacket,
uint64_t &frameNumber, uint32_t &packetNumber,
uint64_t &bunchId) const {
void GetHeaderInfo(int index, char *packetData, uint64_t &frameNumber,
uint32_t &packetNumber, uint64_t &bunchId) const {
frameNumber = *reinterpret_cast<uint64_t *>(packetData);
bunchId = *reinterpret_cast<uint64_t *>(packetData + 8);
packetNumber = 0;
@@ -519,6 +508,7 @@ class Gotthard2Data : public GeneralData {
void UpdateImageSize() {
packetSize = headerSizeinPacket + dataSize;
imageSize = int(nPixelsX * nPixelsY * GetPixelDepth());
actualImageSize = imageSize;
packetsPerFrame = imageSize / dataSize;
vetoPacketSize = vetoHsize + vetoDataSize;
vetoImageSize = vetoDataSize * packetsPerFrame;
@@ -529,8 +519,6 @@ class Gotthard2Data : public GeneralData {
class ChipTestBoardData : public GeneralData {
private:
const int NCHAN_DIGITAL = 64;
const int NUM_BYTES_PER_ANALOG_CHANNEL = 2;
const int NUM_BYTES_PER_TRANSCEIVER_CHANNEL = 8;
int nAnalogBytes = 0;
int nDigitalBytes = 0;
int nTransceiverBytes = 0;
@@ -539,7 +527,7 @@ class ChipTestBoardData : public GeneralData {
/** Constructor */
ChipTestBoardData() {
detType = slsDetectorDefs::CHIPTESTBOARD;
nPixelsY = 1; // number of samples
nPixelsY = 1;
headerSizeinPacket = sizeof(slsDetectorDefs::sls_detector_header);
frameIndexMask = 0xFFFFFF; // 10g
frameIndexOffset = 8; // 10g
@@ -547,105 +535,110 @@ class ChipTestBoardData : public GeneralData {
framesPerFile = CTB_MAX_FRAMES_PER_FILE;
fifoDepth = 2500;
standardheader = true;
ctbDbitReorder = true;
UpdateImageSize();
};
}
public:
int GetNumberOfAnalogDatabytes() { return nAnalogBytes; };
int GetNumberOfAnalogDatabytes() { return nAnalogBytes; }
int GetNumberOfDigitalDatabytes() { return nDigitalBytes; };
int GetNumberOfDigitalDatabytes() { return nDigitalBytes; }
int GetNumberOfTransceiverDatabytes() { return nTransceiverBytes; };
int GetNumberOfTransceiverDatabytes() { return nTransceiverBytes; }
void SetNumberOfAnalogSamples(int n) {
nAnalogSamples = n;
UpdateImageSize();
};
}
void SetNumberOfDigitalSamples(int n) {
nDigitalSamples = n;
UpdateImageSize();
};
}
void SetNumberOfTransceiverSamples(int n) {
nTransceiverSamples = n;
UpdateImageSize();
};
}
void SetctbDbitOffset(const int value) {
ctbDbitOffset = value;
UpdateImageSize();
}
void SetctbDbitList(const std::vector<int> &value) {
ctbDbitList = std::move(value);
UpdateImageSize();
}
void SetctbDbitReorder(const bool value) {
ctbDbitReorder = value;
UpdateImageSize();
}
void SetOneGigaAdcEnableMask(int n) {
adcEnableMaskOneGiga = n;
UpdateImageSize();
};
}
void SetTenGigaAdcEnableMask(int n) {
adcEnableMaskTenGiga = n;
UpdateImageSize();
};
}
void SetTransceiverEnableMask(int n) {
transceiverMask = n;
UpdateImageSize();
};
}
void SetReadoutMode(slsDetectorDefs::readoutMode r) {
readoutType = r;
UpdateImageSize();
};
}
void SetTenGigaEnable(bool tg) {
tengigaEnable = tg;
UpdateImageSize();
};
}
private:
void UpdateImageSize() {
nAnalogBytes = 0;
nDigitalBytes = 0;
nTransceiverBytes = 0;
int nAnalogChans = 0, nDigitalChans = 0, nTransceiverChans = 0;
// used in calculations so cant remove now - TODO: remove later
nDigitalBytes = sizeof(uint64_t) * nDigitalSamples;
// analog channels (normal, analog/digital readout)
if (readoutType == slsDetectorDefs::ANALOG_ONLY ||
readoutType == slsDetectorDefs::ANALOG_AND_DIGITAL) {
uint32_t adcEnableMask =
(tengigaEnable ? adcEnableMaskTenGiga : adcEnableMaskOneGiga);
nAnalogChans = __builtin_popcount(adcEnableMask);
// calculate image size
CtbImageInputs inputs{};
inputs.mode = readoutType;
inputs.nAnalogSamples = nAnalogSamples;
inputs.adcMask =
tengigaEnable ? adcEnableMaskTenGiga : adcEnableMaskOneGiga;
inputs.nTransceiverSamples = nTransceiverSamples;
inputs.transceiverMask = transceiverMask;
inputs.nDigitalSamples = nDigitalSamples;
inputs.dbitOffset = ctbDbitOffset;
inputs.dbitList = ctbDbitList;
inputs.dbitReorder = ctbDbitReorder;
nAnalogBytes =
nAnalogChans * NUM_BYTES_PER_ANALOG_CHANNEL * nAnalogSamples;
LOG(logDEBUG1) << " Number of Analog Channels:" << nAnalogChans
<< " Databytes: " << nAnalogBytes;
}
// digital channels
if (readoutType == slsDetectorDefs::DIGITAL_ONLY ||
readoutType == slsDetectorDefs::ANALOG_AND_DIGITAL ||
readoutType == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) {
nDigitalChans = NCHAN_DIGITAL;
nDigitalBytes = (sizeof(uint64_t) * nDigitalSamples);
LOG(logDEBUG1) << "Number of Digital Channels:" << nDigitalChans
<< " Databytes: " << nDigitalBytes;
}
// transceiver channels
if (readoutType == slsDetectorDefs::TRANSCEIVER_ONLY ||
readoutType == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) {
nTransceiverChans = __builtin_popcount(transceiverMask);
;
nTransceiverBytes = nTransceiverChans *
NUM_BYTES_PER_TRANSCEIVER_CHANNEL *
nTransceiverSamples;
LOG(logDEBUG1) << "Number of Transceiver Channels:"
<< nTransceiverChans
<< " Databytes: " << nTransceiverBytes;
}
nPixelsX = nAnalogChans + nDigitalChans + nTransceiverChans;
auto out = computeCtbImageSize(inputs);
nPixelsX = out.nPixelsX;
nAnalogBytes = out.nAnalogBytes;
nTransceiverBytes = out.nTransceiverBytes;
imageSize = out.nAnalogBytes + out.nDigitalBytesReserved +
out.nTransceiverBytes;
// to write to file: after ctb offset and reorder
actualImageSize =
out.nAnalogBytes + out.nDigitalBytes + out.nTransceiverBytes;
LOG(logDEBUG1) << "Actual image size: " << actualImageSize;
// calculate network parameters
dataSize = tengigaEnable ? 8144 : UDP_PACKET_DATA_BYTES;
packetSize = headerSizeinPacket + dataSize;
imageSize = nAnalogBytes + nDigitalBytes + nTransceiverBytes;
packetsPerFrame = ceil((double)imageSize / (double)dataSize);
LOG(logDEBUG1) << "Total Number of Channels:" << nPixelsX
<< " Databytes: " << imageSize;
};
}
};
class XilinxChipTestBoardData : public GeneralData {
@@ -672,6 +665,7 @@ class XilinxChipTestBoardData : public GeneralData {
dataSize = 8144;
packetSize = headerSizeinPacket + dataSize;
tengigaEnable = true;
ctbDbitReorder = true;
UpdateImageSize();
};
@@ -697,10 +691,20 @@ class XilinxChipTestBoardData : public GeneralData {
UpdateImageSize();
};
void SetOneGigaAdcEnableMask(int n) {
adcEnableMaskOneGiga = n;
void SetctbDbitOffset(const int value) {
ctbDbitOffset = value;
UpdateImageSize();
};
}
void SetctbDbitList(const std::vector<int> &value) {
ctbDbitList = std::move(value);
UpdateImageSize();
}
void SetctbDbitReorder(const bool value) {
ctbDbitReorder = value;
UpdateImageSize();
}
void SetTenGigaAdcEnableMask(int n) {
adcEnableMaskTenGiga = n;
@@ -719,48 +723,36 @@ class XilinxChipTestBoardData : public GeneralData {
private:
void UpdateImageSize() {
nAnalogBytes = 0;
nDigitalBytes = 0;
nTransceiverBytes = 0;
int nAnalogChans = 0, nDigitalChans = 0, nTransceiverChans = 0;
// used in calculations so cant remove now - TODO: remove later
nDigitalBytes = sizeof(uint64_t) * nDigitalSamples;
// analog channels (normal, analog/digital readout)
if (readoutType == slsDetectorDefs::ANALOG_ONLY ||
readoutType == slsDetectorDefs::ANALOG_AND_DIGITAL) {
uint32_t adcEnableMask = adcEnableMaskTenGiga;
nAnalogChans = __builtin_popcount(adcEnableMask);
// calculate image size
CtbImageInputs inputs{};
inputs.mode = readoutType;
inputs.nAnalogSamples = nAnalogSamples;
inputs.adcMask = adcEnableMaskTenGiga;
inputs.nTransceiverSamples = nTransceiverSamples;
inputs.transceiverMask = transceiverMask;
inputs.nDigitalSamples = nDigitalSamples;
inputs.dbitOffset = ctbDbitOffset;
inputs.dbitList = ctbDbitList;
inputs.dbitReorder = ctbDbitReorder;
nAnalogBytes =
nAnalogChans * NUM_BYTES_PER_ANALOG_CHANNEL * nAnalogSamples;
LOG(logDEBUG1) << " Number of Analog Channels:" << nAnalogChans
<< " Databytes: " << nAnalogBytes;
}
// digital channels
if (readoutType == slsDetectorDefs::DIGITAL_ONLY ||
readoutType == slsDetectorDefs::ANALOG_AND_DIGITAL ||
readoutType == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) {
nDigitalChans = NCHAN_DIGITAL;
nDigitalBytes = (sizeof(uint64_t) * nDigitalSamples);
LOG(logDEBUG1) << "Number of Digital Channels:" << nDigitalChans
<< " Databytes: " << nDigitalBytes;
}
// transceiver channels
if (readoutType == slsDetectorDefs::TRANSCEIVER_ONLY ||
readoutType == slsDetectorDefs::DIGITAL_AND_TRANSCEIVER) {
nTransceiverChans = __builtin_popcount(transceiverMask);
;
nTransceiverBytes = nTransceiverChans *
NUM_BYTES_PER_TRANSCEIVER_CHANNEL *
nTransceiverSamples;
LOG(logDEBUG1) << "Number of Transceiver Channels:"
<< nTransceiverChans
<< " Databytes: " << nTransceiverBytes;
}
nPixelsX = nAnalogChans + nDigitalChans + nTransceiverChans;
auto out = computeCtbImageSize(inputs);
imageSize = nAnalogBytes + nDigitalBytes + nTransceiverBytes;
nPixelsX = out.nPixelsX;
nAnalogBytes = out.nAnalogBytes;
nTransceiverBytes = out.nTransceiverBytes;
imageSize = out.nAnalogBytes + out.nDigitalBytesReserved +
out.nTransceiverBytes;
// to write to file: after ctb offset and reorder
actualImageSize =
out.nAnalogBytes + out.nDigitalBytes + out.nTransceiverBytes;
LOG(logDEBUG1) << "Actual image size: " << actualImageSize;
// calculate network parameters
packetsPerFrame = ceil((double)imageSize / (double)dataSize);
LOG(logDEBUG1) << "Total Number of Channels:" << nPixelsX
<< " Databytes: " << imageSize;
};

View File

@@ -111,7 +111,6 @@ void Implementation::SetupFifoStructure() {
void Implementation::setDetectorType(const detectorType d) {
switch (d) {
case GOTTHARD:
case EIGER:
case JUNGFRAU:
case MOENCH:
@@ -131,9 +130,6 @@ void Implementation::setDetectorType(const detectorType d) {
// set detector specific variables
switch (d) {
case GOTTHARD:
generalData = new GotthardData();
break;
case EIGER:
generalData = new EigerData();
break;
@@ -159,6 +155,9 @@ void Implementation::setDetectorType(const detectorType d) {
break;
}
// number of portrois should be equal to number of interfaces
ResetRois();
SetLocalNetworkParameters();
SetupFifoStructure();
@@ -188,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);
}
@@ -198,14 +197,14 @@ 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);
dataProcessor[i]->SetStreamingStartFnum(streamingStartFnum);
dataProcessor[i]->SetFramePadding(framePadding);
dataProcessor[i]->SetCtbDbitList(ctbDbitList);
dataProcessor[i]->SetCtbDbitOffset(ctbDbitOffset);
dataProcessor[i]->SetQuadEnable(quadEnable);
dataProcessor[i]->SetFlipRows(flipRows);
dataProcessor[i]->SetNumberofTotalFrames(numberOfTotalFrames);
@@ -222,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 {
@@ -239,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;
@@ -407,97 +400,57 @@ void Implementation::setArping(const bool i,
}
}
slsDetectorDefs::ROI Implementation::getReceiverROI() const {
return receiverRoi;
std::vector<slsDetectorDefs::ROI> Implementation::getPortROIs() const {
return portRois;
}
void Implementation::setReceiverROI(const slsDetectorDefs::ROI arg) {
receiverRoi = arg;
void Implementation::ResetRois() {
int numports = generalData->numUDPInterfaces;
std::vector<ROI> rois(numports);
std::vector<ROI> 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<defs::ROI> &args) {
int nx = static_cast<int>(generalData->nPixelsX);
int ny = static_cast<int>(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 ROI x 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<slsDetectorDefs::ROI> &args) {
multiRoiMetadata = args;
if (dataProcessor.size() > 0)
dataProcessor[0]->setMultiROIMetadata(multiRoiMetadata);
LOG(logINFO) << "Multi ROI Metadata: " << ToString(multiRoiMetadata);
}
std::vector<slsDetectorDefs::ROI> Implementation::getMultiROIMetadata() const {
return multiRoiMetadata;
}
/**************************************************
@@ -793,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
@@ -919,7 +871,12 @@ void Implementation::CreateUDPSockets() {
}
void Implementation::SetupWriter() {
try {
// check if filePath empty and throw error
if (filePath.empty()) {
throw ReceiverError("File path cannot be empty");
}
// check if folder exists and throw if it cant create
mkdir_p(filePath);
// create first files
@@ -951,7 +908,7 @@ void Implementation::StartMasterWriter() {
masterAttributes.detType = generalData->detType;
masterAttributes.timingMode = timingMode;
masterAttributes.geometry = numPorts;
masterAttributes.imageSize = generalData->imageSize;
masterAttributes.imageSize = generalData->actualImageSize;
masterAttributes.nPixels =
xy(generalData->nPixelsX, generalData->nPixelsY);
masterAttributes.maxFramesPerFile = generalData->framesPerFile;
@@ -959,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;
@@ -973,7 +943,7 @@ void Implementation::StartMasterWriter() {
masterAttributes.quad = quadEnable;
masterAttributes.readNRows = readNRows;
masterAttributes.ratecorr = rateCorrections;
masterAttributes.adcmask = generalData->tengigaEnable
masterAttributes.adcMask = generalData->tengigaEnable
? generalData->adcEnableMaskTenGiga
: generalData->adcEnableMaskOneGiga;
masterAttributes.analog =
@@ -989,10 +959,12 @@ void Implementation::StartMasterWriter() {
? 1
: 0;
masterAttributes.digitalSamples = generalData->nDigitalSamples;
masterAttributes.dbitoffset = ctbDbitOffset;
masterAttributes.dbitlist = 0;
for (auto &i : ctbDbitList) {
masterAttributes.dbitlist |= (static_cast<uint64_t>(1) << i);
masterAttributes.dbitOffset = generalData->ctbDbitOffset;
masterAttributes.dbitReorder = generalData->ctbDbitReorder;
masterAttributes.dbitList = 0;
for (auto &i : generalData->ctbDbitList) {
masterAttributes.dbitList |= (static_cast<uint64_t>(1) << i);
}
masterAttributes.transceiverSamples =
generalData->nTransceiverSamples;
@@ -1002,7 +974,6 @@ void Implementation::StartMasterWriter() {
generalData->readoutType == DIGITAL_AND_TRANSCEIVER)
? 1
: 0;
masterAttributes.detectorRoi = generalData->detectorRoi;
masterAttributes.counterMask = generalData->counterMask;
masterAttributes.exptimeArray[0] = acquisitionTime1;
masterAttributes.exptimeArray[1] = acquisitionTime2;
@@ -1012,6 +983,7 @@ void Implementation::StartMasterWriter() {
masterAttributes.gateDelayArray[2] = gateDelay3;
masterAttributes.gates = numberOfGates;
masterAttributes.additionalJsonHeader = additionalJsonHeader;
masterAttributes.readoutSpeed = readoutSpeed;
// create master file
masterFileName = dataProcessor[0]->CreateMasterFile(
@@ -1019,14 +991,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 +1081,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) {
@@ -1595,22 +1590,6 @@ void Implementation::setDynamicRange(const uint32_t i) {
LOG(logINFO) << "Dynamic Range: " << generalData->dynamicRange;
}
slsDetectorDefs::ROI Implementation::getROI() const {
return generalData->detectorRoi;
}
void Implementation::setDetectorROI(slsDetectorDefs::ROI arg) {
if (generalData->detectorRoi.xmin != arg.xmin ||
generalData->detectorRoi.xmax != arg.xmax) {
// only for gotthard
generalData->SetDetectorROI(arg);
SetupFifoStructure();
}
LOG(logINFO) << "Detector ROI: " << ToString(generalData->detectorRoi);
LOG(logINFO) << "Packets per Frame: " << (generalData->packetsPerFrame);
}
bool Implementation::getTenGigaEnable() const {
return generalData->tengigaEnable;
}
@@ -1764,22 +1743,29 @@ void Implementation::setTenGigaADCEnableMask(uint32_t mask) {
LOG(logINFO) << "Packets per Frame: " << (generalData->packetsPerFrame);
}
std::vector<int> Implementation::getDbitList() const { return ctbDbitList; }
void Implementation::setDbitList(const std::vector<int> &v) {
ctbDbitList = v;
for (const auto &it : dataProcessor)
it->SetCtbDbitList(ctbDbitList);
LOG(logINFO) << "Dbit list: " << ToString(ctbDbitList);
std::vector<int> Implementation::getDbitList() const {
return generalData->ctbDbitList;
}
int Implementation::getDbitOffset() const { return ctbDbitOffset; }
void Implementation::setDbitList(const std::vector<int> &v) {
generalData->SetctbDbitList(v);
LOG(logINFO) << "Dbit list: " << ToString(v);
}
int Implementation::getDbitOffset() const { return generalData->ctbDbitOffset; }
void Implementation::setDbitOffset(const int s) {
ctbDbitOffset = s;
for (const auto &it : dataProcessor)
it->SetCtbDbitOffset(ctbDbitOffset);
LOG(logINFO) << "Dbit offset: " << ctbDbitOffset;
generalData->SetctbDbitOffset(s);
LOG(logINFO) << "Dbit offset: " << s;
}
bool Implementation::getDbitReorder() const {
return generalData->ctbDbitReorder;
}
void Implementation::setDbitReorder(const bool reorder) {
generalData->SetctbDbitReorder(reorder);
LOG(logINFO) << "Dbit reorder: " << reorder;
}
uint32_t Implementation::getTransceiverEnableMask() const {
@@ -1796,13 +1782,22 @@ void Implementation::setTransceiverEnableMask(uint32_t mask) {
LOG(logINFO) << "Packets per Frame: " << (generalData->packetsPerFrame);
}
slsDetectorDefs::speedLevel Implementation::getReadoutSpeed() const {
return readoutSpeed;
}
void Implementation::setReadoutSpeed(const slsDetectorDefs::speedLevel i) {
readoutSpeed = i;
LOG(logINFO) << "Readout Speed: " << ToString(readoutSpeed);
}
/**************************************************
* *
* Callbacks *
* *
* ************************************************/
void Implementation::registerCallBackStartAcquisition(
int (*func)(const startCallbackHeader, void *), void *arg) {
void (*func)(const startCallbackHeader, void *), void *arg) {
startAcquisitionCallBack = func;
pStartAcquisition = arg;
}

View File

@@ -58,9 +58,10 @@ class Implementation : private virtual slsDetectorDefs {
bool getArping() const;
pid_t getArpingProcessId() const;
void setArping(const bool i, const std::vector<std::string> ips);
ROI getReceiverROI() const;
void setReceiverROI(const ROI arg);
void setReceiverROIMetadata(const ROI arg);
std::vector<defs::ROI> getPortROIs() const;
void setPortROIs(const std::vector<defs::ROI> &args);
void setMultiROIMetadata(const std::vector<slsDetectorDefs::ROI> &args);
std::vector<slsDetectorDefs::ROI> getMultiROIMetadata() const;
/**************************************************
* *
@@ -211,9 +212,6 @@ class Implementation : private virtual slsDetectorDefs {
void setCounterMask(const uint32_t i);
uint32_t getDynamicRange() const;
void setDynamicRange(const uint32_t i);
ROI getROI() const;
/* [Gotthard] */
void setDetectorROI(ROI arg);
bool getTenGigaEnable() const;
/* [Eiger][Ctb] */
void setTenGigaEnable(const bool b);
@@ -255,9 +253,15 @@ class Implementation : private virtual slsDetectorDefs {
int getDbitOffset() const;
/* [Ctb] */
void setDbitOffset(const int s);
bool getDbitReorder() const;
/* [Ctb] */
void setDbitReorder(const bool reorder);
uint32_t getTransceiverEnableMask() const;
/* [Ctb] */
void setTransceiverEnableMask(const uint32_t mask);
speedLevel getReadoutSpeed() const;
/* [Eiger][Jungfrau][Moench][Mythen3][Gotthard2]*/
void setReadoutSpeed(const speedLevel i);
/**************************************************
* *
@@ -265,9 +269,8 @@ class Implementation : private virtual slsDetectorDefs {
* *
* ************************************************/
/** params: file path, file name, file index, image size */
void registerCallBackStartAcquisition(int (*func)(const startCallbackHeader,
void *),
void *arg);
void registerCallBackStartAcquisition(
void (*func)(const startCallbackHeader, void *), void *arg);
/** params: total frames caught */
void registerCallBackAcquisitionFinished(
void (*func)(const endCallbackHeader, void *), void *arg);
@@ -283,7 +286,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,14 +311,12 @@ class Implementation : private virtual slsDetectorDefs {
bool framePadding{true};
pid_t parentThreadId;
pid_t tcpThreadId;
ROI receiverRoi{};
std::array<ROI, 2> portRois{};
// receiver roi for complete detector for metadata
ROI receiverRoiMetadata{};
std::vector<slsDetectorDefs::ROI> portRois;
std::vector<slsDetectorDefs::ROI> multiRoiMetadata;
// file parameters
fileFormat fileFormatType{BINARY};
std::string filePath{"/"};
std::string filePath{};
std::string fileName{"run"};
uint64_t fileIndex{0};
bool fileWriteEnable{false};
@@ -370,11 +371,11 @@ class Implementation : private virtual slsDetectorDefs {
int thresholdEnergyeV{-1};
std::array<int, 3> thresholdAllEnergyeV = {{-1, -1, -1}};
std::vector<int64_t> rateCorrections;
std::vector<int> ctbDbitList;
int ctbDbitOffset{0};
speedLevel readoutSpeed{FULL_SPEED};
// callbacks
int (*startAcquisitionCallBack)(const startCallbackHeader, void *){nullptr};
void (*startAcquisitionCallBack)(const startCallbackHeader,
void *){nullptr};
void *pStartAcquisition{nullptr};
void (*acquisitionFinishedCallBack)(const endCallbackHeader,
void *){nullptr};

View File

@@ -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; }
@@ -350,8 +350,6 @@ uint32_t Listener::ListenToAnImage(sls_receiver_header &dstHeader,
carryOverFlag = false;
}
// until last packet isHeaderEmpty to account for gotthard short frame, else
// never entering this loop)
while (numpackets < pperFrame) {
// listen to new packet
if (!udpSocketAlive || !udpSocket->ReceivePacket(&listeningPacket[0])) {
@@ -463,15 +461,6 @@ void Listener::CopyPacket(char *dst, char *src, uint32_t dataSize,
// copy packet data
switch (generalData->detType) {
// for gotthard,
// 1st packet: 4 bytes fnum, CACA + CACA, 639*2 bytes data
// 2nd packet: 4 bytes fnum, previous 1*2 bytes data + 640*2 bytes data
case GOTTHARD:
if (!pnum)
memcpy(dst, &src[detHeaderSize + 2], dataSize - 2);
else
memcpy(dst + dataSize - 2, &src[detHeaderSize - 2], dataSize + 2);
break;
case CHIPTESTBOARD:
case XILINX_CHIPTESTBOARD:
if (pnum == (generalData->packetsPerFrame - 1))
@@ -517,14 +506,7 @@ void Listener::GetPacketIndices(uint64_t &fnum, uint32_t &pnum, uint64_t &bnum,
fnum = header->frameNumber;
pnum = header->packetNumber;
} else {
// set first packet to be odd or even (check required when switching
// from roi to no roi)
if (generalData->detType == GOTTHARD && !startedFlag) {
oddStartingPacket =
generalData->SetOddStartingPacket(index, &packet[0]);
}
generalData->GetHeaderInfo(index, &packet[0], oddStartingPacket, fnum,
pnum, bnum);
generalData->GetHeaderInfo(index, &packet[0], fnum, pnum, bnum);
}
}

View File

@@ -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};
@@ -156,12 +156,6 @@ class Listener : private virtual slsDetectorDefs, public ThreadObject {
// for print progress during acquisition*/
uint32_t numPacketsStatistic{0};
uint32_t numFramesStatistic{0};
/**
* starting packet number is odd or even, accordingly increment frame number
* to get first packet number as 0
* (pecific to gotthard, can vary between modules, hence defined here) */
bool oddStartingPacket{true};
};
} // namespace sls

File diff suppressed because it is too large Load Diff

View File

@@ -4,12 +4,14 @@
#include "receiver_defs.h"
#include "sls/ToString.h"
#include "sls/TypeTraits.h"
#include "sls/logger.h"
#include "sls/sls_detector_defs.h"
#include <chrono>
#include <rapidjson/prettywriter.h>
#include <rapidjson/stringbuffer.h>
#include <string_view>
#ifdef HDF5C
#include "H5Cpp.h"
@@ -18,6 +20,7 @@
namespace sls {
using ns = std::chrono::nanoseconds;
using writer = rapidjson::PrettyWriter<rapidjson::StringBuffer>;
class MasterAttributes {
public:
@@ -25,7 +28,7 @@ class MasterAttributes {
slsDetectorDefs::detectorType detType{slsDetectorDefs::GENERIC};
slsDetectorDefs::timingMode timingMode{slsDetectorDefs::AUTO_TIMING};
slsDetectorDefs::xy geometry{};
uint32_t imageSize{0};
int imageSize{0};
slsDetectorDefs::xy nPixels{};
uint32_t maxFramesPerFile{0};
slsDetectorDefs::frameDiscardPolicy frameDiscardMode{
@@ -37,127 +40,413 @@ class MasterAttributes {
ns period{0};
slsDetectorDefs::burstMode burstMode{slsDetectorDefs::BURST_INTERNAL};
int numUDPInterfaces{0};
uint32_t dynamicRange{0};
uint32_t tenGiga{0};
int dynamicRange{0};
int tenGiga{0};
int thresholdEnergyeV{0};
std::array<int, 3> thresholdAllEnergyeV = {{0, 0, 0}};
ns subExptime{0};
ns subPeriod{0};
uint32_t quad{0};
uint32_t readNRows;
int quad{0};
int readNRows;
std::vector<int64_t> ratecorr;
uint32_t adcmask{0};
uint32_t analog{0};
uint32_t analogSamples{0};
uint32_t digital{0};
uint32_t digitalSamples{0};
uint32_t dbitoffset{0};
uint64_t dbitlist{0};
uint32_t transceiverMask{0};
uint32_t transceiver{0};
uint32_t transceiverSamples{0};
slsDetectorDefs::ROI detectorRoi{};
slsDetectorDefs::ROI receiverRoi{};
uint32_t counterMask{0};
uint32_t adcMask{0};
int analog{0};
int analogSamples{0};
int digital{0};
int digitalSamples{0};
int dbitReorder{1};
int dbitOffset{0};
uint64_t dbitList{0};
int transceiverMask{0};
int transceiver{0};
int transceiverSamples{0};
std::vector<slsDetectorDefs::ROI> rois{};
int counterMask{0};
std::array<ns, 3> exptimeArray{};
std::array<ns, 3> gateDelayArray{};
uint32_t gates;
int gates;
std::map<std::string, std::string> additionalJsonHeader;
uint64_t framesInFile{0};
slsDetectorDefs::speedLevel readoutSpeed{slsDetectorDefs::FULL_SPEED};
inline static const std::string_view N_DETECTOR_TYPE = "Detector Type";
inline static const std::string_view N_TIMING_MODE = "Timing Mode";
inline static const std::string_view N_GEOMETRY = "Geometry";
inline static const std::string_view N_IMAGE_SIZE = "Image Size";
inline static const std::string_view N_PIXELS = "Pixels";
inline static const std::string_view N_MAX_FRAMES_PER_FILE =
"Max Frames Per File";
inline static const std::string_view N_FRAME_DISCARD_POLICY =
"Frame Discard Policy";
inline static const std::string_view N_FRAME_PADDING = "Frame Padding";
inline static const std::string_view N_TOTAL_FRAMES = "Total Frames";
inline static const std::string_view N_FRAMES_IN_FILE = "Frames in File";
inline static const std::string_view N_EXPOSURE_TIME = "Exposure Time";
inline static const std::string_view N_ACQUISITION_PERIOD =
"Acquisition Period";
inline static const std::string_view N_NUM_UDP_INTERFACES =
"Number of UDP Interfaces";
inline static const std::string_view N_NUMBER_OF_ROWS = "Number of Rows";
inline static const std::string_view N_READOUT_SPEED = "Readout Speed";
inline static const std::string_view N_DYNAMIC_RANGE = "Dynamic Range";
inline static const std::string_view N_TEN_GIGA = "Ten Giga";
inline static const std::string_view N_THRESHOLD_ENERGY =
"Threshold Energy";
inline static const std::string_view N_SUB_EXPOSURE_TIME =
"Sub Exposure Time";
inline static const std::string_view N_SUB_ACQUISITION_PERIOD =
"Sub Acquisition Period";
inline static const std::string_view N_QUAD = "Quad";
inline static const std::string_view N_RATE_CORRECTIONS =
"Rate Corrections";
inline static const std::string_view N_COUNTER_MASK = "Counter Mask";
inline static const std::string_view N_EXPOSURE_TIMES = "Exposure Times";
inline static const std::string_view N_GATE_DELAYS = "Gate Delays";
inline static const std::string_view N_GATES = "Gates";
inline static const std::string_view N_THRESHOLD_ENERGIES =
"Threshold Energies";
inline static const std::string_view N_BURST_MODE = "Burst Mode";
inline static const std::string_view N_ADC_MASK = "ADC Mask";
inline static const std::string_view N_ANALOG = "Analog Flag";
inline static const std::string_view N_ANALOG_SAMPLES = "Analog Samples";
inline static const std::string_view N_DIGITAL = "Digital Flag";
inline static const std::string_view N_DIGITAL_SAMPLES = "Digital Samples";
inline static const std::string_view N_DBIT_REORDER = "Dbit Reorder";
inline static const std::string_view N_DBIT_OFFSET = "Dbit Offset";
inline static const std::string_view N_DBIT_BITSET = "Dbit Bitset";
inline static const std::string_view N_TRANSCEIVER_MASK =
"Transceiver Mask";
inline static const std::string_view N_TRANSCEIVER = "Transceiver Flag";
inline static const std::string_view N_TRANSCEIVER_SAMPLES =
"Transceiver Samples";
inline static const std::string_view N_VERSION = "Version";
inline static const std::string_view N_TIMESTAMP = "Timestamp";
inline static const std::string_view N_RECEIVER_ROIS = "Receiver Rois";
inline static const std::string_view N_SCAN_PARAMETERS = "Scan Parameters";
inline static const std::string_view N_ADDITIONAL_JSON_HEADER =
"Additional JSON Header";
MasterAttributes() = default;
~MasterAttributes() = default;
void
GetBinaryAttributes(rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void GetBinaryAttributes(writer *w);
#ifdef HDF5C
void WriteHDF5Attributes(H5::H5File *fd, H5::Group *group);
#endif
void GetCommonBinaryAttributes(
rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void GetFinalBinaryAttributes(
rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void GetCommonBinaryAttributes(writer *w);
void GetFinalBinaryAttributes(writer *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 WriteHDF5ROI(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 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);
#endif
void GetGotthardBinaryAttributes(
rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void GetJungfrauBinaryAttributes(writer *w);
#ifdef HDF5C
void WriteGotthardHDF5Attributes(H5::H5File *fd, H5::Group *group);
void WriteJungfrauHDF5Attributes(H5::Group *group);
#endif
void GetJungfrauBinaryAttributes(
rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void GetEigerBinaryAttributes(writer *w);
#ifdef HDF5C
void WriteJungfrauHDF5Attributes(H5::H5File *fd, H5::Group *group);
void WriteEigerHDF5Attributes(H5::Group *group);
#endif
void GetEigerBinaryAttributes(
rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void GetMythen3BinaryAttributes(writer *w);
#ifdef HDF5C
void WriteEigerHDF5Attributes(H5::H5File *fd, H5::Group *group);
void WriteMythen3HDF5Attributes(H5::Group *group);
#endif
void GetMythen3BinaryAttributes(
rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void GetGotthard2BinaryAttributes(writer *w);
#ifdef HDF5C
void WriteMythen3HDF5Attributes(H5::H5File *fd, H5::Group *group);
void WriteGotthard2HDF5Attributes(H5::Group *group);
#endif
void GetGotthard2BinaryAttributes(
rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void GetMoenchBinaryAttributes(writer *w);
#ifdef HDF5C
void WriteGotthard2HDF5Attributes(H5::H5File *fd, H5::Group *group);
void WriteMoenchHDF5Attributes(H5::Group *group);
#endif
void GetMoenchBinaryAttributes(
rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void GetCtbBinaryAttributes(writer *w);
#ifdef HDF5C
void WriteMoenchHDF5Attributes(H5::H5File *fd, H5::Group *group);
void WriteCtbHDF5Attributes(H5::Group *group);
#endif
void
GetCtbBinaryAttributes(rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void GetXilinxCtbBinaryAttributes(writer *w);
#ifdef HDF5C
void WriteCtbHDF5Attributes(H5::H5File *fd, H5::Group *group);
void WriteXilinxCtbHDF5Attributes(H5::Group *group);
#endif
void GetXilinxCtbBinaryAttributes(
rapidjson::PrettyWriter<rapidjson::StringBuffer> *w);
void WriteBinaryDetectorType(writer *w);
#ifdef HDF5C
void WriteXilinxCtbHDF5Attributes(H5::H5File *fd, H5::Group *group);
void WriteHDF5DetectorType(H5::Group *group);
#endif
void WriteBinaryTimingMode(writer *w);
#ifdef HDF5C
void WriteHDF5TimingMode(H5::Group *group);
#endif
void WriteBinaryGeometry(writer *w);
#ifdef HDF5C
void WriteHDF5Geometry(H5::Group *group);
#endif
void WriteBinaryImageSize(writer *w);
#ifdef HDF5C
void WriteHDF5ImageSize(H5::Group *group);
#endif
void WriteBinaryPixels(writer *w);
#ifdef HDF5C
void WriteHDF5Pixels(H5::Group *group);
#endif
void WriteBinaryMaxFramesPerFile(writer *w);
#ifdef HDF5C
void WriteHDF5MaxFramesPerFile(H5::Group *group);
#endif
void WriteBinaryFrameDiscardPolicy(writer *w);
#ifdef HDF5C
void WriteHDF5FrameDiscardPolicy(H5::Group *group);
#endif
void WriteBinaryFramePadding(writer *w);
#ifdef HDF5C
void WriteHDF5FramePadding(H5::Group *group);
#endif
void WriteBinaryTotalFrames(writer *w);
#ifdef HDF5C
void WriteHDF5TotalFrames(H5::Group *group);
#endif
void WriteBinaryFramesInFile(writer *w);
#ifdef HDF5C
void WriteHDF5FramesInFile(H5::Group *group);
#endif
void WriteBinaryExposureTme(writer *w);
#ifdef HDF5C
void WriteHDF5ExposureTime(H5::Group *group);
#endif
void WriteBinaryAcquisitionPeriod(writer *w);
#ifdef HDF5C
void WriteHDF5AcquisitionPeriod(H5::Group *group);
#endif
void WriteBinaryNumberOfUDPInterfaces(writer *w);
#ifdef HDF5C
void WriteHDF5NumberOfUDPInterfaces(H5::Group *group);
#endif
void WriteBinaryNumberOfRows(writer *w);
#ifdef HDF5C
void WriteHDF5NumberOfRows(H5::Group *group);
#endif
void WriteBinaryReadoutSpeed(writer *w);
#ifdef HDF5C
void WriteHDF5ReadoutSpeed(H5::Group *group);
#endif
void WriteBinaryDynamicRange(writer *w);
#ifdef HDF5C
void WriteHDF5DynamicRange(H5::Group *group);
#endif
void WriteBinaryTenGiga(writer *w);
#ifdef HDF5C
void WriteHDF5TenGiga(H5::Group *group);
#endif
void WriteBinaryThresholdEnergy(writer *w);
#ifdef HDF5C
void WriteHDF5ThresholdEnergy(H5::Group *group);
#endif
void WriteBinarySubExposureTime(writer *w);
#ifdef HDF5C
void WriteHDF5SubExposureTime(H5::Group *group);
#endif
void WriteBinarySubAcquisitionPeriod(writer *w);
#ifdef HDF5C
void WriteHDF5SubAcquisitionPeriod(H5::Group *group);
#endif
void WriteBinaryQuad(writer *w);
#ifdef HDF5C
void WriteHDF5Quad(H5::Group *group);
#endif
void WriteBinaryRateCorrections(writer *w);
#ifdef HDF5C
void WriteHDF5RateCorrections(H5::Group *group);
#endif
void WriteBinaryCounterMask(writer *w);
#ifdef HDF5C
void WriteHDF5CounterMask(H5::Group *group);
#endif
void WriteBinaryExptimeArray(writer *w);
#ifdef HDF5C
void WriteHDF5ExptimeArray(H5::Group *group);
#endif
void WriteBinaryGateDelayArray(writer *w);
#ifdef HDF5C
void WriteHDF5GateDelayArray(H5::Group *group);
#endif
void WriteBinaryGates(writer *w);
#ifdef HDF5C
void WriteHDF5Gates(H5::Group *group);
#endif
void WriteBinaryThresholdAllEnergy(writer *w);
#ifdef HDF5C
void WriteHDF5ThresholdAllEnergy(H5::Group *group);
#endif
void WriteBinaryBurstMode(writer *w);
#ifdef HDF5C
void WriteHDF5BurstMode(H5::Group *group);
#endif
void WriteBinaryAdcMask(writer *w);
#ifdef HDF5C
void WriteHDF5AdcMask(H5::Group *group);
#endif
void WriteBinaryAnalogFlag(writer *w);
#ifdef HDF5C
void WriteHDF5AnalogFlag(H5::Group *group);
#endif
void WriteBinaryAnalogSamples(writer *w);
#ifdef HDF5C
void WriteHDF5AnalogSamples(H5::Group *group);
#endif
void WriteBinaryDigitalFlag(writer *w);
#ifdef HDF5C
void WriteHDF5DigitalFlag(H5::Group *group);
#endif
void WriteBinaryDigitalSamples(writer *w);
#ifdef HDF5C
void WriteHDF5DigitalSamples(H5::Group *group);
#endif
void WriteBinaryDBitReorder(writer *w);
#ifdef HDF5C
void WriteHDF5DBitReorder(H5::Group *group);
#endif
void WriteBinaryDBitOffset(writer *w);
#ifdef HDF5C
void WriteHDF5DBitOffset(H5::Group *group);
#endif
void WriteBinaryDBitBitset(writer *w);
#ifdef HDF5C
void WriteHDF5DBitBitset(H5::Group *group);
#endif
void WriteBinaryTransceiverMask(writer *w);
#ifdef HDF5C
void WriteHDF5TransceiverMask(H5::Group *group);
#endif
void WriteBinaryTransceiverFlag(writer *w);
#ifdef HDF5C
void WriteHDF5TransceiverFlag(H5::Group *group);
#endif
void WriteBinaryTransceiverSamples(writer *w);
#ifdef HDF5C
void WriteHDF5TransceiverSamples(H5::Group *group);
#endif
/** writes according to type */
template <typename T> void WriteBinaryValue(writer *w, const T &value) {
if constexpr (std::is_same_v<T, int>) {
w->Int(value);
} else if constexpr (std::is_same_v<T, uint64_t>) {
w->Uint64(value);
} else if constexpr (std::is_same_v<T, int64_t>) {
w->Int64(value);
} else if constexpr (std::is_same_v<T, uint32_t>) {
w->Uint(value);
} else if constexpr (std::is_same_v<T, std::string>) {
w->String(value.c_str());
} else if constexpr (is_duration<T>::value) {
w->String(ToString(value).c_str());
} else {
throw RuntimeError("Unsupported type for Binary write: " +
std::string(typeid(T).name()));
}
}
/** For non-arrays */
template <typename T>
std::enable_if_t<(!std::is_class_v<T> || std::is_same_v<T, std::string>),
void>
WriteBinary(writer *w, const std::string &name, const T &value) {
w->Key(name.c_str());
WriteBinaryValue(w, value);
}
/** For arrays */
template <typename T>
std::enable_if_t<(std::is_class_v<T> && !std::is_same_v<T, std::string>),
void>
WriteBinary(writer *w, const std::string &name, const T &value) {
w->Key(name.c_str());
w->StartArray();
for (const auto &v : value) {
WriteBinaryValue(w, v);
}
w->EndArray();
}
#ifdef HDF5C
void WriteHDF5String(H5::Group *group, const std::string &name,
const std::string &value);
void WriteHDF5StringArray(H5::Group *group, const std::string &name,
const std::vector<std::string> &value);
/** get type */
template <typename T> H5::PredType const *GetHDF5Type() {
if constexpr (std::is_same_v<T, int>) {
return &H5::PredType::NATIVE_INT;
} else if constexpr (std::is_same_v<T, uint64_t>) {
return &H5::PredType::STD_U64LE;
} else if constexpr (std::is_same_v<T, int64_t>) {
return &H5::PredType::STD_I64LE;
} else if constexpr (std::is_same_v<T, uint32_t>) {
return &H5::PredType::STD_U32LE;
} else {
throw RuntimeError("Unsupported type for HDF5");
}
}
/** For non-arrays */
template <typename T>
typename std::enable_if<!std::is_class<T>::value, void>::type
WriteHDF5Int(H5::Group *group, const std::string &name, const T &value) {
H5::DataSpace dataspace(H5S_SCALAR);
auto h5type = GetHDF5Type<T>();
H5::DataSet dataset = group->createDataSet(name, *h5type, dataspace);
dataset.write(&value, *h5type);
}
/** For arrays */
template <typename T>
typename std::enable_if<std::is_class<T>::value, void>::type
WriteHDF5Int(H5::Group *group, const std::string &name, const T &value) {
using ElemT = typename T::value_type;
auto h5type = GetHDF5Type<ElemT>();
hsize_t dims[1] = {value.size()};
H5::DataSpace dataspace(1, dims);
H5::DataSet dataset = group->createDataSet(name, *h5type, dataspace);
dataset.write(value.data(), *h5type);
}
#endif
void WriteBinaryXY(writer *w, const std::string &name, const defs::xy &xy);
#ifdef HDF5C
void WriteHDF5XY(H5::Group *group, const std::string &name,
const defs::xy &xy);
#endif
void WriteBinaryVersion(writer *w);
#ifdef HDF5C
void WriteHDF5Version(H5::H5File *fd);
#endif
void WriteBinaryTimestamp(writer *w);
#ifdef HDF5C
void WriteHDF5Timestamp(H5::Group *group);
#endif
void WriteBinaryRois(writer *w);
#ifdef HDF5C
void WriteHDF5ROIs(H5::Group *group);
#endif
void WriteBinaryScanParameters(writer *w);
#ifdef HDF5C
void WriteHDF5ScanParameters(H5::Group *group);
#endif
void WriteBinaryJsonHeader(writer *w);
#ifdef HDF5C
void WriteHDF5JsonHeader(H5::Group *group);
#endif
void WriteBinaryFrameHeaderFormat(writer *w);
};
} // namespace sls

View File

@@ -49,7 +49,8 @@ std::string CreateMasterBinaryFile(const std::string &filePath,
void LinkHDF5FileInMaster(std::string &masterFileName,
std::string &dataFilename,
std::vector<std::string> parameterNames,
const bool silentMode, std::mutex *hdf5LibMutex) {
const bool silentMode, std::mutex *hdf5LibMutex,
size_t multiRoiSize) {
std::lock_guard<std::mutex> lock(*hdf5LibMutex);
std::unique_ptr<H5::H5File> fd{nullptr};
@@ -67,27 +68,35 @@ void LinkHDF5FileInMaster(std::string &masterFileName,
fd = make_unique<H5::H5File>(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();
@@ -131,13 +140,6 @@ std::string CreateMasterHDF5File(const std::string &filePath,
fd = make_unique<H5::H5File>(fileName.c_str(), createFlags,
H5::FileCreatPropList::DEFAULT, flist);
// attributes - version
double dValue = HDF5_WRITER_VERSION;
H5::DataSpace dataspace_attr = H5::DataSpace(H5S_SCALAR);
H5::Attribute attribute = fd->createAttribute(
"version", H5::PredType::NATIVE_DOUBLE, dataspace_attr);
attribute.write(H5::PredType::NATIVE_DOUBLE, &dValue);
// Create a group in the file
H5::Group group1(fd->createGroup("entry"));
H5::Group group2(group1.createGroup("data"));
@@ -160,33 +162,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<std::string> 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<std::string> parameterNames,
const std::vector<H5::DataType> parameterDataTypes,
std::mutex *hdf5LibMutex, bool gotthard25um) {
std::mutex *hdf5LibMutex, bool gotthard25um,
std::vector<defs::ROI> 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<std::mutex> lock(*hdf5LibMutex);
std::unique_ptr<H5::H5File> fd{nullptr};
try {
H5::Exception::dontPrint(); // to handle errors
H5Eset_auto(H5E_DEFAULT, (H5E_auto2_t)H5Eprint, stderr);
// file
H5::FileAccPropList fapl;
@@ -198,141 +223,172 @@ std::string CreateVirtualHDF5File(
fd = make_unique<H5::H5File>(fileName.c_str(), H5F_ACC_TRUNC,
H5::FileCreatPropList::DEFAULT, fapl);
// attributes - version
double dValue = HDF5_WRITER_VERSION;
H5::DataSpace dataspace_attr = H5::DataSpace(H5S_SCALAR);
H5::Attribute attribute = fd->createAttribute(
"version", H5::PredType::NATIVE_DOUBLE, dataspace_attr);
attribute.write(H5::PredType::NATIVE_DOUBLE, &dValue);
for (size_t iRoi = 0; iRoi != multiRoi.size(); ++iRoi) {
// 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);
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");
// property list
H5::DSetCreatPropList plist;
uint64_t fill_value = -1;
plist.setFillValue(dataType, &fill_value);
std::vector<H5::DSetCreatPropList> 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)
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<H5::DSetCreatPropList> 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<hsize_t>(nPixelsY),
static_cast<hsize_t>(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();

View File

@@ -21,7 +21,8 @@ std::string CreateMasterBinaryFile(const std::string &filePath,
void LinkHDF5FileInMaster(std::string &masterFileName,
std::string &dataFilename,
std::vector<std::string> 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<std::string> 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<std::string> parameterNames,
const std::vector<H5::DataType> parameterDataTypes,
std::mutex *hdf5LibMutex, bool gotthard25um);
std::mutex *hdf5LibMutex, bool gotthard25um,
std::vector<defs::ROI> multiRoi);
#endif
} // namespace masterFileUtility

View File

@@ -2,16 +2,16 @@
// Copyright (C) 2021 Contributors to the SLS Detector Package
/* Creates the slsMultiReceiver for running multiple receivers form a single
* binary */
#include "CommandLineOptions.h"
#include "sls/Receiver.h"
#include "sls/ToString.h"
#include "sls/container_utils.h"
#include "sls/logger.h"
#include "sls/network_utils.h"
#include "sls/sls_detector_defs.h"
#include "sls/versionAPI.h"
#include <csignal> //SIGINT
#include <cstring>
#include <iostream>
#include <semaphore.h>
#include <sys/wait.h> //wait
#include <unistd.h>
@@ -27,37 +27,13 @@
#define PRINT_IN_COLOR(c, f, ...) \
printf("\033[%dm" f RESET, 30 + c + 1, ##__VA_ARGS__)
sem_t semaphore;
/**
* Control+C Interrupt Handler
* to let all the processes know to exit properly
*/
void sigInterruptHandler(int p) { sem_post(&semaphore); }
/**
* prints usage of this example program
*/
std::string getHelpMessage() {
std::ostringstream os;
os << "\nUsage:\n\n"
<< "./slsMultiReceiver --version or -v\n"
<< "\t - Gets the slsMultiReceiver version\n\n"
<< "./slsMultiReceiver [start tcp port] [num recevers] [call back "
"option (optional)]\n"
<< "\t - tcp port has to be non-zero and 16 bit\n"
<< "\t - call back option is 0 (disabled) by default, 1 prints frame "
"header for debugging\n";
return os.str();
}
/**
* Start Acquisition Call back (slsMultiReceiver writes data if file write
* enabled) if registerCallBackRawDataReady or
* registerCallBackRawDataModifyReady registered, users get data
*/
int StartAcq(const slsDetectorDefs::startCallbackHeader callbackHeader,
void *objectPointer) {
void StartAcq(const slsDetectorDefs::startCallbackHeader callbackHeader,
void *objectPointer) {
LOG(sls::logINFOBLUE) << "#### Start Acquisition:"
<< "\n\t["
<< "\n\tUDP Port : "
@@ -74,7 +50,6 @@ int StartAcq(const slsDetectorDefs::startCallbackHeader callbackHeader,
<< "\n\tAdditional Json Header : "
<< sls::ToString(callbackHeader.addJsonHeader)
<< "\n\t]";
return 0;
}
/** Acquisition Finished Call back */
@@ -150,109 +125,48 @@ void GetData(slsDetectorDefs::sls_receiver_header &header,
// header->packetsMask.to_string().c_str(),
((uint8_t)(*((uint8_t *)(dataPointer)))), imageSize);
// // example of how to use roi or modify data that is later written to file
// slsDetectorDefs::ROI roi{0, 10, 0, 20};
// int width = roi.xmax - roi.xmin;
// int height = roi.ymax - roi.ymin;
// uint8_t *destPtr = (uint8_t *)dataPointer;
// for (int irow = roi.ymin; irow < roi.ymax; ++irow) {
// memcpy(destPtr,
// ((uint8_t *)(dataPointer + irow * callbackHeader.shape.x +
// roi.xmin)),
// width);
// destPtr += width;
// }
// memcpy((uint8_t*)dataPointer, (uint8_t*)dataPointer
// // setting roi for eg. changes size
// imageSize = width * height;
// if data is modified, can affect size
// only reduction in size allowed, not increase
// imageSize = 26000;
}
sem_t semaphore;
/**
* Example of main program using the Receiver class
*
* - Defines in file for:
* - Default Number of receivers is 1
* - Default Start TCP port is 1954
* Control+C Interrupt Handler
* to let all the processes know to exit properly
* All child processes will call the handler (parent process set to ignore)
*/
void sigInterruptHandler(int signal) {
(void)signal; // suppress unused warning if needed
sem_post(&semaphore);
}
int main(int argc, char *argv[]) {
// version
if (argc == 2) {
std::string sargv1 = std::string(argv[1]);
if (sargv1 == "--version" || sargv1 == "-v") {
std::cout << "slsMultiReceiver Version: " << APIRECEIVER
<< std::endl;
exit(EXIT_SUCCESS);
}
CommandLineOptions cli(AppType::MultiReceiver);
ParsedOptions opts;
try {
opts = cli.parse(argc, argv);
} catch (sls::RuntimeError &e) {
return EXIT_FAILURE;
}
auto &m = std::get<MultiReceiverOptions>(opts);
if (m.versionRequested || m.helpRequested) {
return EXIT_SUCCESS;
}
/** - set default values */
int numReceivers = 1;
uint16_t startTCPPort = DEFAULT_TCP_RX_PORTNO;
int withCallback = 0;
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << ']';
// close files on ctrl+c
sls::setupSignalHandler(SIGINT, sigInterruptHandler);
// handle locally on socket crash
sls::setupSignalHandler(SIGPIPE, SIG_IGN);
sem_init(&semaphore, 1, 0);
/** - get number of receivers and start tcp port from command line
* arguments */
if (argc > 1) {
try {
if (argc == 3 || argc == 4) {
startTCPPort = sls::StringTo<uint16_t>(argv[1]);
if (startTCPPort == 0) {
throw std::runtime_error("Invalid start tcp port");
}
numReceivers = std::stoi(argv[2]);
if (numReceivers > 1024) {
cprintf(RED,
"Did you mix up the order of the arguments?\n%s\n",
getHelpMessage().c_str());
return EXIT_FAILURE;
}
if (numReceivers == 0) {
cprintf(RED, "Invalid number of receivers.\n%s\n",
getHelpMessage().c_str());
return EXIT_FAILURE;
}
if (argc == 4) {
withCallback = std::stoi(argv[3]);
}
} else
throw std::runtime_error("Invalid number of arguments");
} catch (const std::exception &e) {
cprintf(RED, "Error: %s\n%s\n", e.what(), getHelpMessage().c_str());
return EXIT_FAILURE;
}
}
cprintf(BLUE, "Parent Process Created [ Tid: %ld ]\n", (long)gettid());
cprintf(RESET, "Number of Receivers: %d\n", numReceivers);
cprintf(RESET, "Start TCP Port: %hu\n", startTCPPort);
cprintf(RESET, "Callback Enable: %d\n", withCallback);
/** - Catch signal SIGINT to close files and call destructors properly */
struct sigaction sa;
sa.sa_flags = 0; // no flags
sa.sa_handler = sigInterruptHandler; // handler function
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
// of handler
if (sigaction(SIGINT, &sa, nullptr) == -1) {
cprintf(RED, "Could not set handler function for SIGINT\n");
}
/** - Ignore SIG_PIPE, prevents global signal handler, handle locally,
instead of a server crashing due to client crash when writing, it just
gives error */
struct sigaction asa;
asa.sa_flags = 0; // no flags
asa.sa_handler = SIG_IGN; // handler function
sigemptyset(&asa.sa_mask); // dont block additional signals during
// invocation of handler
if (sigaction(SIGPIPE, &asa, nullptr) == -1) {
cprintf(RED, "Could not set handler function for SIGPIPE\n");
}
/** - loop over number of receivers */
for (int i = 0; i < numReceivers; ++i) {
/** - loop over receivers */
for (int i = 0; i < m.numReceivers; ++i) {
/** - fork process to create child process */
pid_t pid = fork();
@@ -260,89 +174,89 @@ int main(int argc, char *argv[]) {
/** - if fork failed, raise SIGINT and properly destroy all child
* processes */
if (pid < 0) {
cprintf(RED, "fork() failed. Killing all the receiver objects\n");
LOG(sls::logERROR)
<< "fork() failed. Killing all the receiver objects";
raise(SIGINT);
}
/** - if child process */
else if (pid == 0) {
cprintf(BLUE, "Child process %d [ Tid: %ld ]\n", i, (long)gettid());
LOG(sls::logINFOBLUE)
<< "Child process " << i << " [ Tid: " << gettid() << ']';
try {
uint16_t port = startTCPPort + i;
uint16_t port = m.port + i;
sls::Receiver receiver(port);
/** - register callbacks. remember to set file write enable
* to 0 (using the client) if we should not write files and you
* will write data using the callbacks */
if (withCallback) {
if (m.callbackEnabled) {
/** - Call back for start acquisition */
cprintf(BLUE, "Registering StartAcq()\n");
LOG(sls::logINFOBLUE) << "Registering StartAcq()";
receiver.registerCallBackStartAcquisition(StartAcq,
nullptr);
/** - Call back for acquisition finished */
cprintf(BLUE, "Registering AcquisitionFinished()\n");
LOG(sls::logINFOBLUE)
<< "Registering AcquisitionFinished()";
receiver.registerCallBackAcquisitionFinished(
AcquisitionFinished, nullptr);
/* - Call back for raw data */
cprintf(BLUE, "Registering GetData() \n");
LOG(sls::logINFOBLUE) << "Registering GetData()";
receiver.registerCallBackRawDataReady(GetData, nullptr);
}
/** - as long as no Ctrl+C */
// each child process gets a copy of the semaphore
sem_wait(&semaphore);
sem_destroy(&semaphore);
LOG(sls::logINFOBLUE)
<< "Exiting Child Process [ Tid: " << gettid() << ']';
exit(EXIT_SUCCESS);
} catch (...) {
sem_destroy(&semaphore);
LOG(sls::logINFOBLUE)
<< "Exiting Child Process [ Tid: " << gettid() << " ]";
throw;
exit(EXIT_FAILURE);
}
cprintf(BLUE, "Exiting Child Process [ Tid: %ld ]\n",
(long)gettid());
exit(EXIT_SUCCESS);
}
}
/** - Parent process ignores SIGINT (exits only when all child process
* exits) */
sa.sa_flags = 0; // no flags
sa.sa_handler = SIG_IGN; // handler function
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
// of handler
if (sigaction(SIGINT, &sa, nullptr) == -1) {
cprintf(RED, "Could not set handler function for SIGINT\n");
}
/** - Parent process ignores SIGINT and waits for all the child processes to
* handle the signal */
sls::setupSignalHandler(SIGINT, SIG_IGN);
/** - Print Ready and Instructions how to exit */
std::cout << "Ready ... \n";
cprintf(RESET, "\n[ Press \'Ctrl+c\' to exit ]\n");
LOG(sls::logINFO) << "\n[ Press \'Ctrl+c\' to exit ]";
/** - Parent process waits for all child processes to exit */
for (;;) {
pid_t childPid = waitpid(-1, nullptr, 0);
int status;
pid_t childPid = waitpid(-1, &status, 0);
// no child closed
if (childPid == -1) {
if (errno == ECHILD) {
cprintf(GREEN, "All Child Processes have been closed\n");
LOG(sls::logINFOGREEN)
<< "All Child Processes have been closed";
break;
} else {
cprintf(RED, "Unexpected error from waitpid(): (%s)\n",
strerror(errno));
LOG(sls::logERROR)
<< "Unexpected error from waitpid(): " << strerror(errno);
break;
}
}
// child closed
cprintf(BLUE, "Exiting Child Process [ Tid: %ld ]\n",
(long int)childPid);
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
std::cerr << "Child " << childPid << " failed\n";
kill(0, SIGINT); // signal other children to exit
}
}
std::cout << "Goodbye!\n";
return 0;
return EXIT_SUCCESS;
}

View File

@@ -27,106 +27,14 @@ namespace sls {
Receiver::~Receiver() = default;
Receiver::Receiver(int argc, char *argv[]) : tcpipInterface(nullptr) {
// options
uint16_t tcpip_port_no = 1954;
uid_t userid = -1;
// parse command line for config
static struct option long_options[] = {
// These options set a flag.
//{"verbose", no_argument, &verbose_flag, 1},
// These options dont set a flag. We distinguish them by their indices.
{"rx_tcpport", required_argument, nullptr,
't'}, // TODO change or backward compatible to "port, p"?
{"uid", required_argument, nullptr, 'u'},
{"version", no_argument, nullptr, 'v'},
{"help", no_argument, nullptr, 'h'},
{nullptr, 0, nullptr, 0}};
// initialize global optind variable (required when instantiating multiple
// receivers in the same process)
optind = 1;
// getopt_long stores the option index here.
int option_index = 0;
int c = 0;
std::string help_message =
"\nUsage: " + std::string(argv[0]) + " [arguments]\n" +
"Possible arguments are:\n" +
"\t-t, --rx_tcpport <port> : TCP Communication Port with "
"client. Non-zero and 16 bit.\n" +
"\t-u, --uid <user id> : Set effective user id if receiver "
"\n" +
"\t started with privileges. \n\n";
while (c != -1) {
c = getopt_long(argc, argv, "hvt:u:", long_options, &option_index);
// Detect the end of the options.
if (c == -1)
break;
switch (c) {
case 't':
try {
tcpip_port_no = sls::StringTo<uint16_t>(optarg);
validatePortNumber(tcpip_port_no);
} catch (...) {
throw RuntimeError("Could not scan TCP port number." +
help_message);
}
break;
case 'u':
if (sscanf(optarg, "%u", &userid) != 1) {
throw RuntimeError("Could not scan uid" + help_message);
}
break;
case 'v':
std::cout << "slsReceiver Version: " << APIRECEIVER << std::endl;
LOG(logINFOBLUE) << "Exiting [ Tid: " << gettid() << " ]";
exit(EXIT_SUCCESS);
case 'h':
std::cout << help_message << std::endl;
exit(EXIT_SUCCESS);
default:
throw RuntimeError(help_message);
}
Receiver::Receiver(uint16_t port) {
validatePortNumber(port);
#ifdef SLS_USE_TESTS
if (port == 65535) {
throw sls::RuntimeError("Throwing for testing purposes. ");
}
// set effective id if provided
if (userid != static_cast<uid_t>(-1)) {
if (geteuid() == userid) {
LOG(logINFO) << "Process already has the same Effective UID "
<< userid;
} else {
if (seteuid(userid) != 0) {
std::ostringstream oss;
oss << "Could not set Effective UID to " << userid;
throw RuntimeError(oss.str());
}
if (geteuid() != userid) {
std::ostringstream oss;
oss << "Could not set Effective UID to " << userid << ". Got "
<< geteuid();
throw RuntimeError(oss.str());
}
LOG(logINFO) << "Process Effective UID changed to " << userid;
}
}
// might throw an exception
tcpipInterface = make_unique<ClientInterface>(tcpip_port_no);
}
Receiver::Receiver(uint16_t tcpip_port_no) {
// might throw an exception
tcpipInterface = make_unique<ClientInterface>(tcpip_port_no);
#endif
tcpipInterface = make_unique<ClientInterface>(port);
}
std::string Receiver::getReceiverVersion() {
@@ -134,7 +42,7 @@ std::string Receiver::getReceiverVersion() {
}
void Receiver::registerCallBackStartAcquisition(
int (*func)(const startCallbackHeader, void *), void *arg) {
void (*func)(const startCallbackHeader, void *), void *arg) {
tcpipInterface->registerCallBackStartAcquisition(func, arg);
}

View File

@@ -1,9 +1,12 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
/* slsReceiver */
#include "CommandLineOptions.h"
#include "sls/Receiver.h"
#include "sls/ToString.h"
#include "sls/container_utils.h"
#include "sls/logger.h"
#include "sls/network_utils.h"
#include "sls/sls_detector_defs.h"
#include <csignal> //SIGINT
@@ -18,44 +21,49 @@
sem_t semaphore;
void sigInterruptHandler(int p) { sem_post(&semaphore); }
/**
* Control+C Interrupt Handler
* to let all the other process know to exit properly
*/
void sigInterruptHandler(int signal) {
(void)signal; // suppress unused warning if needed
sem_post(&semaphore);
}
int main(int argc, char *argv[]) {
CommandLineOptions cli(AppType::SingleReceiver);
ParsedOptions opts;
try {
opts = cli.parse(argc, argv);
} catch (sls::RuntimeError &e) {
return EXIT_FAILURE;
}
auto &o = std::get<CommonOptions>(opts);
if (o.versionRequested || o.helpRequested) {
return EXIT_SUCCESS;
}
LOG(sls::logINFOBLUE) << "Current Process [ Tid: " << gettid() << " ]";
// close files on ctrl+c
sls::setupSignalHandler(SIGINT, sigInterruptHandler);
// handle locally on socket crash
sls::setupSignalHandler(SIGPIPE, SIG_IGN);
sem_init(&semaphore, 1, 0);
LOG(sls::logINFOBLUE) << "Created [ Tid: " << gettid() << " ]";
// Catch signal SIGINT to close files and call destructors properly
struct sigaction sa;
sa.sa_flags = 0; // no flags
sa.sa_handler = sigInterruptHandler; // handler function
sigemptyset(&sa.sa_mask); // dont block additional signals during invocation
// of handler
if (sigaction(SIGINT, &sa, nullptr) == -1) {
LOG(sls::logERROR) << "Could not set handler function for SIGINT";
}
// if socket crash, ignores SISPIPE, prevents global signal handler
// subsequent read/write to socket gives error - must handle locally
struct sigaction asa;
asa.sa_flags = 0; // no flags
asa.sa_handler = SIG_IGN; // handler function
sigemptyset(&asa.sa_mask); // dont block additional signals during
// invocation of handler
if (sigaction(SIGPIPE, &asa, nullptr) == -1) {
LOG(sls::logERROR) << "Could not set handler function for SIGPIPE";
}
try {
sls::Receiver r(argc, argv);
sls::Receiver r(o.port);
LOG(sls::logINFO) << "[ Press \'Ctrl+c\' to exit ]";
sem_wait(&semaphore);
sem_destroy(&semaphore);
} catch (...) {
// pass
sem_destroy(&semaphore);
LOG(sls::logINFOBLUE) << "Exiting [ Tid: " << gettid() << " ]";
throw;
}
LOG(sls::logINFOBLUE) << "Exiting [ Tid: " << gettid() << " ]";
LOG(sls::logINFO) << "Exiting Receiver";
return 0;
return EXIT_SUCCESS;
}

View File

@@ -19,8 +19,8 @@ namespace sls {
// files
// versions
#define HDF5_WRITER_VERSION (6.6) // 1 decimal places
#define BINARY_WRITER_VERSION (7.2) // 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
@@ -57,9 +57,6 @@ struct image_structure {
// parameters to calculate fifo depth
#define SAMPLE_TIME_IN_NS (100000000) // 100ms
// to differentiate between gotthard and short gotthard
#define GOTTHARD_PACKET_SIZE (1286)
#define DUMMY_PACKET_VALUE (0xFFFFFFFF)
#define LISTENER_PRIORITY (90)

View File

@@ -3,6 +3,10 @@
target_sources(tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/test-GeneralData.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test-CircularFifo.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test-ArrangeDataBasedOnBitList.cpp
${CMAKE_CURRENT_SOURCE_DIR}/test-Apps.cpp
)
target_include_directories(tests PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../src>")
message(STATUS "Resolved path: ${CMAKE_CURRENT_SOURCE_DIR}/../src")

View File

@@ -0,0 +1,344 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "CommandLineOptions.h"
#include "catch.hpp"
#include "sls/logger.h"
#include "sls/versionAPI.h"
#include <unistd.h>
namespace sls {
template <typename T, typename U> constexpr bool is_type() {
return std::is_same_v<std::decay_t<U>, T>;
}
TEST_CASE("CommandLineOption construction", "[detector]") {
CommandLineOptions s(AppType::SingleReceiver);
REQUIRE(s.getTypeString() == "slsReceiver");
REQUIRE(s.getVersion() ==
std::string("slsReceiver Version: ") + APIRECEIVER);
REQUIRE_NOTHROW(s.getHelpMessage());
CommandLineOptions m(AppType::MultiReceiver);
REQUIRE(m.getTypeString() == "slsMultiReceiver");
REQUIRE(m.getVersion() ==
std::string("slsMultiReceiver Version: ") + APIRECEIVER);
REQUIRE_NOTHROW(m.getHelpMessage());
CommandLineOptions f(AppType::FrameSynchronizer);
REQUIRE(f.getTypeString() == "slsFrameSynchronizer");
REQUIRE(f.getVersion() ==
std::string("slsFrameSynchronizer Version: ") + APIRECEIVER);
REQUIRE_NOTHROW(f.getHelpMessage());
}
TEST_CASE("Parse Help", "[detector]") {
for (auto app : {AppType::SingleReceiver, AppType::MultiReceiver,
AppType::FrameSynchronizer}) {
CommandLineOptions s(app);
ParsedOptions opts = s.parse({"", "-h"});
if (app == AppType::SingleReceiver) {
REQUIRE_NOTHROW(std::get<CommonOptions>(opts).helpRequested);
} else if (app == AppType::MultiReceiver) {
REQUIRE_NOTHROW(std::get<MultiReceiverOptions>(opts).helpRequested);
} else if (app == AppType::FrameSynchronizer) {
REQUIRE_NOTHROW(std::get<FrameSyncOptions>(opts).helpRequested);
}
}
}
TEST_CASE("Validate common options", "[detector]") {
std::string uidStr = std::to_string(getuid());
for (auto app : {AppType::SingleReceiver, AppType::MultiReceiver,
AppType::FrameSynchronizer}) {
CommandLineOptions s(app);
REQUIRE_NOTHROW(s.parse({}));
REQUIRE_NOTHROW(s.parse({"", "-v"}));
REQUIRE_NOTHROW(s.parse({"", "-h"}));
REQUIRE_NOTHROW(s.parse({"", "-h", "gdfg"})); // ignored extra args
REQUIRE_NOTHROW(s.parse({"", "-p", "1955"}));
REQUIRE_NOTHROW(s.parse({"", "-u", uidStr}));
REQUIRE_NOTHROW(s.parse({"", "-p", "1234", "-u", uidStr}));
}
}
TEST_CASE("Validate specific options", "[detector]") {
std::string uidStr = std::to_string(getuid());
CommandLineOptions s(AppType::SingleReceiver);
REQUIRE_NOTHROW(s.parse({"", "-t", "1955"}));
REQUIRE_THROWS(s.parse({"", "-c"}));
REQUIRE_THROWS(s.parse({"", "-n", "2"}));
REQUIRE_THROWS(s.parse({"", "-m", "2"}));
for (auto app : {AppType::MultiReceiver, AppType::FrameSynchronizer}) {
CommandLineOptions m(app);
REQUIRE_NOTHROW(m.parse({"", "-c"}));
REQUIRE_NOTHROW(m.parse({"", "-n", "2"}));
REQUIRE_NOTHROW(
m.parse({"", "-p", "1234", "-u", uidStr, "-c", "-n", "2"}));
REQUIRE_THROWS(m.parse({"", "-t", "1955"}));
REQUIRE_THROWS(m.parse({"", "-m", "2"}));
}
}
TEST_CASE("Parse version and help", "[detector]") {
for (auto app : {AppType::SingleReceiver, AppType::MultiReceiver,
AppType::FrameSynchronizer}) {
CommandLineOptions s(app);
auto opts = s.parse({});
std::visit(
[](const auto &o) {
REQUIRE(o.versionRequested == false); // default
REQUIRE(o.helpRequested == false); // default
},
opts);
opts = s.parse({"", "-v"});
std::visit(
[](const auto &o) {
REQUIRE(o.versionRequested == true);
REQUIRE(o.helpRequested == false);
},
opts);
opts = s.parse({"", "-h"});
std::visit(
[](const auto &o) {
REQUIRE(o.versionRequested == false);
REQUIRE(o.helpRequested == true);
},
opts);
opts = s.parse({"", "-h", "-v"});
std::visit(
[](const auto &o) {
REQUIRE(o.versionRequested == true);
REQUIRE(o.helpRequested == true);
},
opts);
opts = s.parse({"", "-v", "-h"});
std::visit(
[](const auto &o) {
REQUIRE(o.helpRequested == true);
REQUIRE(o.versionRequested == true);
},
opts);
opts = s.parse({"", "-v", "-h", "sdfsf"}); // ignores extra args
std::visit(
[](const auto &o) {
REQUIRE(o.helpRequested == true);
REQUIRE(o.versionRequested == true);
},
opts);
}
}
TEST_CASE("Parse port and uid", "[detector]") {
uid_t uid = getuid();
std::string uidStr = std::to_string(uid);
uid_t invalidUid = uid + 1000;
std::string invalidUidStr = std::to_string(invalidUid);
for (auto app : {AppType::SingleReceiver, AppType::MultiReceiver,
AppType::FrameSynchronizer}) {
CommandLineOptions s(app);
REQUIRE_THROWS(
s.parse({"", "-p", "1234", "-u", invalidUidStr})); // invalid uid
REQUIRE_THROWS(s.parse({"", "-p", "500"})); // invalid port
auto opts = s.parse({"", "-p", "1234", "-u", uidStr});
std::visit(
[&](const auto &o) {
REQUIRE(o.port == 1234);
REQUIRE(o.userid == uid);
},
opts);
opts = s.parse({"", "-p", "5678"});
std::visit(
[](const auto &o) {
REQUIRE(o.port == 5678);
REQUIRE(o.userid == static_cast<uid_t>(-1)); // default
},
opts);
opts = s.parse({});
std::visit(
[](const auto &o) {
REQUIRE(o.port == 1954); // default
REQUIRE(o.userid == static_cast<uid_t>(-1)); // default
},
opts);
}
}
TEST_CASE("Parse num receivers and opt arg (Specific opt)", "[detector]") {
for (auto app : {AppType::MultiReceiver, AppType::FrameSynchronizer}) {
CommandLineOptions s(app);
REQUIRE_THROWS(s.parse({"", "-n", "0"})); // invalid number of receivers
REQUIRE_THROWS(s.parse({"", "-n", "1001"})); // exceeds max receivers
REQUIRE_NOTHROW(s.parse({"", "-n", "10"})); // valid
auto opts = s.parse({""});
std::visit(
[](const auto &o) {
using T = decltype(o);
if constexpr (is_type<MultiReceiverOptions, T>()) {
REQUIRE(o.numReceivers == 1); // default
REQUIRE(o.callbackEnabled == false);
} else if constexpr (is_type<FrameSyncOptions, T>()) {
REQUIRE(o.numReceivers == 1); // default
REQUIRE(o.printHeaders == false);
}
},
opts);
opts = s.parse({"", "-n", "5"});
std::visit(
[](const auto &o) {
using T = decltype(o);
if constexpr (is_type<MultiReceiverOptions, T>()) {
REQUIRE(o.numReceivers == 5);
REQUIRE(o.callbackEnabled == false); // default
} else if constexpr (is_type<FrameSyncOptions, T>()) {
REQUIRE(o.numReceivers == 5);
REQUIRE(o.printHeaders == false); // default
}
},
opts);
opts = s.parse({"", "-c", "-n", "3"});
std::visit(
[](const auto &o) {
using T = decltype(o);
if constexpr (is_type<MultiReceiverOptions, T>()) {
REQUIRE(o.numReceivers == 3);
REQUIRE(o.callbackEnabled == true);
} else if constexpr (is_type<FrameSyncOptions, T>()) {
REQUIRE(o.numReceivers == 3);
REQUIRE(o.printHeaders == true);
}
},
opts);
}
}
TEST_CASE("Parse deprecated options", "[detector]") {
for (auto app : {AppType::SingleReceiver, AppType::MultiReceiver,
AppType::FrameSynchronizer}) {
CommandLineOptions s(app);
// argc 3 or 4, invalid
REQUIRE_THROWS(s.parse({"", "1954"}));
REQUIRE_THROWS(s.parse({
"",
"1954",
}));
// argc 3 or 4
if (app == AppType::SingleReceiver) {
REQUIRE_THROWS(
s.parse({"", "1954", "1"})); // deprecated unsupported
} else {
REQUIRE_THROWS(s.parse({"", "1954", "1", "1", "-p",
"1954"})); // mix deprecated and current
REQUIRE_THROWS(
s.parse({"", "1954", "1", "-c"})); // mix deprecated and current
REQUIRE_THROWS(s.parse(
{"", "1954", "1", "-n", "34"})); // mix deprecated and current
REQUIRE_THROWS(s.parse({"", "110", "1954"})); // mix order
REQUIRE_THROWS(s.parse({"", "1023", "10"})); // privileged port
REQUIRE_THROWS(s.parse({"", "2000", "0"})); // invalid num receivers
REQUIRE_THROWS(
s.parse({"", "2000", "1001"})); // invalid num receivers
REQUIRE_THROWS(s.parse({"", "1954", "1", "2"})); // invalid 3rd opt
REQUIRE_NOTHROW(s.parse({""}));
REQUIRE_NOTHROW(s.parse({"", "1954", "1"}));
REQUIRE_NOTHROW(s.parse({"", "1954", "1", "0"}));
REQUIRE_NOTHROW(s.parse({"", "1954", "1", "1"}));
// default
auto opts = s.parse({""});
std::visit(
[](const auto &o) {
using T = decltype(o);
if constexpr (is_type<MultiReceiverOptions, T>()) {
REQUIRE(o.port == 1954);
REQUIRE(o.numReceivers == 1);
REQUIRE(o.callbackEnabled == false);
} else if constexpr (is_type<FrameSyncOptions, T>()) {
REQUIRE(o.port == 1954);
REQUIRE(o.numReceivers == 1);
REQUIRE(o.printHeaders == false); // default
}
},
opts);
opts = s.parse({"", "1958", "10"});
std::visit(
[](const auto &o) {
using T = decltype(o);
if constexpr (is_type<MultiReceiverOptions, T>()) {
REQUIRE(o.port == 1958);
REQUIRE(o.numReceivers == 10);
REQUIRE(o.callbackEnabled == false); // default
} else if constexpr (is_type<FrameSyncOptions, T>()) {
REQUIRE(o.port == 1958);
REQUIRE(o.numReceivers == 10);
REQUIRE(o.printHeaders == false); // default
}
},
opts);
opts = s.parse({"", "1958", "10", "1"});
std::visit(
[](const auto &o) {
using T = decltype(o);
if constexpr (is_type<MultiReceiverOptions, T>()) {
REQUIRE(o.port == 1958);
REQUIRE(o.numReceivers == 10);
REQUIRE(o.callbackEnabled == true); // default
} else if constexpr (is_type<FrameSyncOptions, T>()) {
REQUIRE(o.port == 1958);
REQUIRE(o.numReceivers == 10);
REQUIRE(o.printHeaders == true); // default
}
},
opts);
}
}
// test function directly
// nargs can be 1, 3 or 4
REQUIRE_THROWS(CommandLineOptions::ParseDeprecated({"", ""}));
REQUIRE_THROWS(CommandLineOptions::ParseDeprecated({"", "", "", "", ""}));
// default
auto [p, n, o] = CommandLineOptions::ParseDeprecated({""});
REQUIRE(p == 1954);
REQUIRE(n == 1);
REQUIRE(o == false);
std::tie(p, n, o) = CommandLineOptions::ParseDeprecated({"", "1955", "6"});
REQUIRE(p == 1955);
REQUIRE(n == 6);
REQUIRE(o == false);
std::tie(p, n, o) =
CommandLineOptions::ParseDeprecated({"", "1955", "6", "1"});
REQUIRE(p == 1955);
REQUIRE(n == 6);
REQUIRE(o == true);
}
} // namespace sls

View File

@@ -0,0 +1,411 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2025 Contributors to the SLS Detector Package
/************************************************
* @file test-ArrangeDataBasedOnBitList.cpp
* @short test case for DataProcessor rearrange functions,
***********************************************/
#include "DataProcessor.h"
#include "GeneralData.h"
#include "catch.hpp"
#include <vector>
namespace sls {
// dummy GeneralData class for testing
class GeneralDataTest : public GeneralData {
public:
GeneralDataTest() { detType = slsDetectorDefs::CHIPTESTBOARD; }
int GetNumberOfAnalogDatabytes() { return nAnalogBytes; };
int GetNumberOfDigitalDatabytes() { return nDigitalBytes; };
int GetNumberOfTransceiverDatabytes() { return nTransceiverBytes; };
void SetNumberOfAnalogDatabytes(int value) { nAnalogBytes = value; }
void SetNumberOfDigitalDatabytes(int value) { nDigitalBytes = value; }
void SetNumberOfTransceiverDatabytes(int value) {
nTransceiverBytes = value;
}
void SetCtbDbitOffset(const int value) { ctbDbitOffset = value; }
void SetCtbDbitList(const std::vector<int> &value) { ctbDbitList = value; }
void SetCtbDbitReorder(const bool value) { ctbDbitReorder = value; }
private:
int nAnalogBytes{};
int nDigitalBytes{};
int nTransceiverBytes{};
};
// dummy DataProcessor class for testing
class DataProcessorTest : public DataProcessor {
public:
DataProcessorTest() : DataProcessor(0){};
~DataProcessorTest(){};
void ArrangeDbitData(size_t &size, char *data) {
DataProcessor::ArrangeDbitData(size, data);
}
void RemoveTrailingBits(size_t &size, char *data) {
DataProcessor::RemoveTrailingBits(size, data);
}
};
/**
* test fixture for Testing,
* num_analog_bytes = 1 byte has a value of 125
* num_transceiver_bytes = 2 both bytes have a value of 125
* num_digital_bytes is variable and is defined by number of samples
* default num sample is 5
* all bytes in digital data take a value of 255
*/
class DataProcessorTestFixture {
public:
DataProcessorTestFixture() {
// setup Test Fixture
dataprocessor = new DataProcessorTest;
generaldata = new GeneralDataTest;
generaldata->SetNumberOfAnalogDatabytes(num_analog_bytes);
generaldata->SetNumberOfTransceiverDatabytes(num_transceiver_bytes);
generaldata->SetNumberOfDigitalDatabytes(num_digital_bytes +
num_random_offset_bytes);
dataprocessor->SetGeneralData(generaldata);
}
~DataProcessorTestFixture() {
delete[] data;
delete dataprocessor;
delete generaldata;
}
size_t get_size() const {
return num_analog_bytes + num_digital_bytes + num_transceiver_bytes +
num_random_offset_bytes;
}
void set_num_samples(const size_t value) {
num_samples = value;
num_digital_bytes = num_samples * 8; // 64 (8 bytes) per sample
generaldata->SetNumberOfDigitalDatabytes(num_digital_bytes +
num_random_offset_bytes);
}
void set_random_offset_bytes(const size_t value) {
num_random_offset_bytes = value;
generaldata->SetNumberOfDigitalDatabytes(num_digital_bytes +
num_random_offset_bytes);
}
void set_data() {
delete[] data;
uint64_t max_bytes_per_bit =
num_samples % 8 == 0 ? num_samples / 8 : num_samples / 8 + 1;
uint64_t reserved_size =
get_size() - num_digital_bytes + max_bytes_per_bit * 64;
data = new char[reserved_size];
// set testing data
memset(data, dummy_value, num_analog_bytes); // set to dummy value
memset(data + num_analog_bytes, 0,
num_random_offset_bytes); // set to zero
memset(data + num_analog_bytes + num_random_offset_bytes, 0xFF,
num_digital_bytes); // all digital bits are one
memset(data + num_digital_bytes + num_analog_bytes +
num_random_offset_bytes,
dummy_value,
num_transceiver_bytes); // set to dummy value
}
DataProcessorTest *dataprocessor;
GeneralDataTest *generaldata;
const size_t num_analog_bytes = 1;
const size_t num_transceiver_bytes = 2;
const char dummy_value = static_cast<char>(125);
size_t num_digital_bytes = 40; // num_samples * 8 = 5 * 8 = 40
size_t num_random_offset_bytes = 0;
size_t num_samples = 5;
char *data = nullptr;
};
TEST_CASE_METHOD(DataProcessorTestFixture, "Remove Trailing Bits",
"[.dataprocessor][.bitoffset]") {
const size_t num_random_offset_bytes = 3;
set_random_offset_bytes(num_random_offset_bytes);
set_data();
generaldata->SetCtbDbitOffset(num_random_offset_bytes);
size_t expected_size = get_size() - num_random_offset_bytes;
char *expected_data = new char[expected_size];
memset(expected_data, dummy_value, num_analog_bytes); // set to 125
memset(expected_data + num_analog_bytes, 0xFF,
num_digital_bytes); // set to 1
memset(expected_data + num_digital_bytes + num_analog_bytes, dummy_value,
num_transceiver_bytes); // set to 125
size_t size = get_size();
dataprocessor->RemoveTrailingBits(size, data);
CHECK(size == expected_size);
CHECK(memcmp(data, expected_data, expected_size) == 0);
delete[] expected_data;
}
// parametric test tested with num_samples = 5, num_samples = 10, num_samples =
// 8
TEST_CASE_METHOD(DataProcessorTestFixture, "Reorder all",
"[.dataprocessor][.reorder]") {
// parameters: num_samples, expected_num_digital_bytes,
// expected_digital_part
auto parameters = GENERATE(
std::make_tuple(5, 64, std::vector<uint8_t>{0b00011111}),
std::make_tuple(10, 2 * 64, std::vector<uint8_t>{0xFF, 0b00000011}),
std::make_tuple(8, 64, std::vector<uint8_t>{0xFF}));
size_t num_samples, expected_num_digital_bytes;
std::vector<uint8_t> expected_digital_part;
std::tie(num_samples, expected_num_digital_bytes, expected_digital_part) =
parameters;
// set number of samples for test fixture -> create data
set_num_samples(num_samples);
set_data();
std::vector<int> bitlist(64);
std::iota(bitlist.begin(), bitlist.end(), 0);
generaldata->SetCtbDbitList(bitlist);
generaldata->SetCtbDbitReorder(true); // set reorder to true
const size_t expected_size =
num_analog_bytes + num_transceiver_bytes + expected_num_digital_bytes;
// create expected data
char *expected_data = new char[expected_size];
memset(expected_data, dummy_value, num_analog_bytes); // set to 125
for (size_t bit = 0; bit < 64; ++bit) {
memcpy(expected_data + num_analog_bytes +
expected_digital_part.size() * bit,
expected_digital_part.data(), expected_digital_part.size());
}
memset(expected_data + expected_num_digital_bytes + num_analog_bytes,
dummy_value,
num_transceiver_bytes); // set to 125
size_t size = get_size();
dataprocessor->ArrangeDbitData(size, data); // call reorder
CHECK(size == expected_size);
CHECK(memcmp(data, expected_data, expected_size) == 0);
delete[] expected_data;
}
TEST_CASE_METHOD(DataProcessorTestFixture,
"Reorder all and remove trailing bits",
"[.dataprocessor][.reorder]") {
// set number of samples for test fixture -> create data
const size_t num_random_offset_bytes = 3;
set_random_offset_bytes(num_random_offset_bytes);
set_data();
std::vector<int> bitlist(64);
std::iota(bitlist.begin(), bitlist.end(), 0);
generaldata->SetCtbDbitList(bitlist);
generaldata->SetCtbDbitOffset(num_random_offset_bytes);
generaldata->SetCtbDbitReorder(true); // set reorder to true
const size_t expected_num_digital_bytes = 64;
std::vector<uint8_t> expected_digital_part{0b00011111};
const size_t expected_size =
num_analog_bytes + num_transceiver_bytes + expected_num_digital_bytes;
// create expected data
char *expected_data = new char[expected_size];
memset(expected_data, dummy_value, num_analog_bytes); // set to 125
for (size_t bit = 0; bit < 64; ++bit) {
memcpy(expected_data + num_analog_bytes +
expected_digital_part.size() * bit,
expected_digital_part.data(), expected_digital_part.size());
}
memset(expected_data + expected_num_digital_bytes + num_analog_bytes,
dummy_value,
num_transceiver_bytes); // set to 125
size_t size = get_size();
dataprocessor->ArrangeDbitData(size, data); // call reorder
CHECK(size == expected_size);
CHECK(memcmp(data, expected_data, expected_size) == 0);
delete[] expected_data;
}
TEST_CASE_METHOD(DataProcessorTestFixture, "Arrange bitlist with reorder false",
"[.dataprocessor][.retrievebitlist]") {
// parameters: num_samples, bitlist, expected_num_digital_bytes,
// expected_digital_part
auto parameters = GENERATE(
std::make_tuple(5, std::vector<int>{1, 4, 5}, 5,
std::vector<uint8_t>{0b00000111}),
std::make_tuple(5, std::vector<int>{1, 5, 3, 7, 8, 50, 42, 60, 39}, 10,
std::vector<uint8_t>{0xFF, 0b00000001}),
std::make_tuple(5, std::vector<int>{1, 5, 3, 7, 8, 50, 42, 60}, 5,
std::vector<uint8_t>{0xFF}));
size_t num_samples, expected_num_digital_bytes;
std::vector<uint8_t> expected_digital_part;
std::vector<int> bitlist;
std::tie(num_samples, bitlist, expected_num_digital_bytes,
expected_digital_part) = parameters;
generaldata->SetCtbDbitList(bitlist);
generaldata->SetCtbDbitReorder(false);
set_num_samples(num_samples);
set_data();
size_t expected_size =
num_analog_bytes + num_transceiver_bytes + expected_num_digital_bytes;
// create expected data
char *expected_data = new char[expected_size];
memset(expected_data, dummy_value, num_analog_bytes);
for (size_t sample = 0; sample < num_samples; ++sample) {
memcpy(expected_data + num_analog_bytes +
expected_digital_part.size() * sample,
expected_digital_part.data(), expected_digital_part.size());
}
memset(expected_data + expected_num_digital_bytes + num_analog_bytes,
dummy_value, num_transceiver_bytes);
size_t size = get_size();
dataprocessor->ArrangeDbitData(size, data);
CHECK(size == expected_size);
CHECK(memcmp(data, expected_data, expected_size) == 0);
delete[] expected_data;
}
TEST_CASE_METHOD(DataProcessorTestFixture, "Arrange bitlist with reorder true",
"[.dataprocessor][.retrievebitlist]") {
// parameters: num_samples, bitlist, expected_num_digital_bytes,
// expected_digital_part
auto parameters = GENERATE(
std::make_tuple(5, std::vector<int>{1, 4, 5}, 3,
std::vector<uint8_t>{0b00011111}),
std::make_tuple(10, std::vector<int>{1, 4, 5}, 6,
std::vector<uint8_t>{0xFF, 0b00000011}),
std::make_tuple(8, std::vector<int>{1, 5, 3, 7, 8, 50, 42, 60, 39}, 9,
std::vector<uint8_t>{0xFF}));
size_t num_samples, expected_num_digital_bytes;
std::vector<uint8_t> expected_digital_part;
std::vector<int> bitlist;
std::tie(num_samples, bitlist, expected_num_digital_bytes,
expected_digital_part) = parameters;
generaldata->SetCtbDbitList(bitlist);
generaldata->SetCtbDbitReorder(true);
set_num_samples(num_samples);
set_data();
size_t expected_size =
num_analog_bytes + num_transceiver_bytes + expected_num_digital_bytes;
// create expected data
char *expected_data = new char[expected_size];
memset(expected_data, dummy_value, num_analog_bytes);
for (size_t sample = 0; sample < bitlist.size(); ++sample) {
memcpy(expected_data + num_analog_bytes +
expected_digital_part.size() * sample,
expected_digital_part.data(), expected_digital_part.size());
}
memset(expected_data + expected_num_digital_bytes + num_analog_bytes,
dummy_value, num_transceiver_bytes);
size_t size = get_size();
dataprocessor->ArrangeDbitData(size, data);
CHECK(size == expected_size);
CHECK(memcmp(data, expected_data, expected_size) == 0);
delete[] expected_data;
}
TEST_CASE_METHOD(DataProcessorTestFixture,
"Arrange bitlist and remove trailing bits",
"[.dataprocessor][.retrievebitlist]") {
size_t num_random_offset_bytes = 3;
std::vector<int> bitlist{1, 4, 5};
set_random_offset_bytes(num_random_offset_bytes);
set_data();
generaldata->SetCtbDbitList(bitlist);
generaldata->SetCtbDbitReorder(false);
generaldata->SetCtbDbitOffset(num_random_offset_bytes);
std::vector<uint8_t> expected_digital_part{0b00000111};
const size_t expected_num_digital_bytes = 5;
size_t expected_size =
num_analog_bytes + num_transceiver_bytes + expected_num_digital_bytes;
// create expected data
char *expected_data = new char[expected_size];
memset(expected_data, dummy_value, num_analog_bytes);
for (size_t sample = 0; sample < num_samples; ++sample) {
memcpy(expected_data + num_analog_bytes +
expected_digital_part.size() * sample,
expected_digital_part.data(), expected_digital_part.size());
}
memset(expected_data + expected_num_digital_bytes + num_analog_bytes,
dummy_value, num_transceiver_bytes);
size_t size = get_size();
dataprocessor->ArrangeDbitData(size, data);
CHECK(size == expected_size);
CHECK(memcmp(data, expected_data, expected_size) == 0);
delete[] expected_data;
}
} // namespace sls

View File

@@ -53,30 +53,3 @@
// CHECK(bunchId == 0xf012fb20010f195b);
// CHECK(subFrameNumber == -1);
// }
// TEST_CASE("Parse header gotthard data", "[receiver]") {
// GotthardData data;
// struct packet {
// uint32_t frameNumber;
// unsigned char data[GOTTHARD_PACKET_SIZE];
// } __attribute__((packed));
// packet test_packet;
// test_packet.frameNumber = 25698u;
// int index = 0;
// char *packetData = reinterpret_cast<char *>(&test_packet);
// uint32_t dynamicRange{0};
// bool oddStartingPacket{0};
// uint64_t frameNumber{0};
// uint32_t packetNumber{0};
// uint32_t subFrameNumber{0};
// uint64_t bunchId{0};
// data.GetHeaderInfo(index, packetData, dynamicRange, oddStartingPacket,
// frameNumber, packetNumber, subFrameNumber, bunchId);
// CHECK(frameNumber == test_packet.frameNumber/2);
// CHECK(subFrameNumber == -1);
// CHECK(bunchId == -1);
// }