From 70e7879dd9a8466b6cf8d82f34b2074df8b6e7b9 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 9 Apr 2025 17:52:23 +0200 Subject: [PATCH 01/33] m3 server fix for trimbits and badchannels that are shifted by 1 --- slsDetectorServers/mythen3DetectorServer/mythen3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slsDetectorServers/mythen3DetectorServer/mythen3.c b/slsDetectorServers/mythen3DetectorServer/mythen3.c index f4dc01871..a03fd736d 100644 --- a/slsDetectorServers/mythen3DetectorServer/mythen3.c +++ b/slsDetectorServers/mythen3DetectorServer/mythen3.c @@ -304,7 +304,7 @@ patternParameters *setChannelRegisterChip(int ichip, char *mask, chanReg |= (0x1 << (3 + icounter)); } } - + chanReg /= 2; // deserialize if (chanReg & CHAN_REG_BAD_CHANNEL_MSK) { LOG(logINFOBLUE, From 3e38de50929eab6e5034c56075afe3475b90bd9a Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 9 Apr 2025 18:13:54 +0200 Subject: [PATCH 02/33] m3 binary in --- ...rverv9.1.0 => mythen3DetectorServerv9.1.1} | Bin 301556 -> 305652 bytes slsSupportLib/include/sls/versionAPI.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename slsDetectorServers/mythen3DetectorServer/bin/{mythen3DetectorServerv9.1.0 => mythen3DetectorServerv9.1.1} (52%) diff --git a/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServerv9.1.0 b/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServerv9.1.1 similarity index 52% rename from slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServerv9.1.0 rename to slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServerv9.1.1 index 3a6717777ca2fcb1373fec7d96227792cbaebcc4..fe2e412c758c1bf1d6dd6b50bfe2a95b66fd4902 100755 GIT binary patch delta 47889 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 Date: Wed, 9 Apr 2025 18:16:47 +0200 Subject: [PATCH 03/33] updated release notes and serverbin --- RELEASE.txt | 136 ++------------------------ serverBin/mythen3DetectorServerv9.1.0 | 1 - serverBin/mythen3DetectorServerv9.1.1 | 1 + 3 files changed, 11 insertions(+), 127 deletions(-) delete mode 120000 serverBin/mythen3DetectorServerv9.1.0 create mode 120000 serverBin/mythen3DetectorServerv9.1.1 diff --git a/RELEASE.txt b/RELEASE.txt index aec864ed6..bb5a313bd 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,15 +1,13 @@ -SLS Detector Package Minor Release 9.1.0 released on 28.03.2025 -=============================================================== +SLS Detector Package Bug Fix Release 9.1.1 released on xx.04.2025 +================================================================== -This document describes the differences between v9.1.0 and v9.0.0 +This document describes the differences between v9.1.1 and v9.1.0 CONTENTS -------- - 1 Changes - 1.1 New or Changed Features - 1.2 Resolved Features + 1 Resolved Features 2 On-board Detector Server Compatibility 3 Firmware Requirements 4 Kernel Requirements @@ -18,130 +16,16 @@ This document describes the differences between v9.1.0 and v9.0.0 -1 Changes -========== - - - -1.1 New or Changed Features -============================ - - - Receiver - -------- - - - * Frame Synchronizer (experimental) - Added a new binary, similar to slsMultiReceiver, to collect images from - several receivers and stream them out as a ZMQ multipart message - (one part for each UDP port). No reconstuction of the image. Includeds start - and end ZMQ messages as well for the start and end callback parameters. - - - Documentation - ------------- - - - * Command line - multi module and multi detector indices - Help on this topic has been added to the 'Command line' topic. - - - * Row and column index (UDP header or callback) - Help on how this is determined from the hostname is added to the 'UDP - Header' and the 'Quick Start Guide' topics. Also added to the help in ' - hostname' command line help. Please note that this can be overwritten by - corresponding row and column commands. - - - -1.2 Resolved Features +1 Resolved Features ====================== - Firmware - --------- + Detector Server + --------------- - * [Jungfrau] Column select and filter resistor - Configuration fix for chip v1.1 for these parameters - - - Firmware &/ On-board Detector Server - ------------------------------------ - - - * [Jungfrau] Timing Info Decoder - Only allowed for hardware v2.0 now. - - - * [Jungfrau] Auto Comparator Disable - chip v1.0 - Previously, this mode for chip v1.0 automatically disabled the on-chip - gain switching compatator after a fixed portion of the exposure time. - Now, one must set also the comparator disable time using 'compdisabletime' - just as in chip v1.1. - - - * [Mythen3] Default period on server start up is 0 now. - - - Client - ------- - - - * Command line - Multi detector index inside file - Multi detector index '[index]-' was ignored silently in the config/parameter - file since 5.0.0. Now, it will throw an exception. Please use the multi - detector index on the 'config' or 'parameter' command instead. - - - * [Mythen3] patternX command autocompletes the argument to a path now. - - - Receiver - -------- - - - * Multiple Receiver objects in multiple threads - slsMultiReceiver uses child processes, but if user rewrote to use multiple - receiver objects in multiple threads instead, a callback mutex is now - implemented to handle the locking mechanism between threads for the callbacks. - - - * Removed potentially unsafe str().c_str() calls. - - - * slsMultiReceiver Ctrl + C - Now cleans up properly upon Ctrl + C, including exiting the Arping thread. - - - * slsMultiReceiver version - --version or -v now gives the version of slsMultiReceiver. - - - ZMQ - --- - - - * [Moench] Reduced significant print out in zmq processing using energy - threshold. - - - * [Moench] Zmq dummy packet restreaming command did nothing - Temporary solution was to move from 'stop' to 'rx_stop' as 'stop' did not - go further if module is idle. - - - * [Moench] Too many Zmq dummy packets- unclear end in acquire - Give time to process dummy packet before restreaming it and wait more - before restreaming to reduce amoutn of zmq dummy packets to process. - - - Simulators - ----------- - - - * [Jungfrau][Moench] Slightly faster transmistting time by removing sleeping - only if there is a transmission delay + * [Mythen3] Fix trimbits and badchannels + They were shifted by 1. Fixed. @@ -151,7 +35,7 @@ This document describes the differences between v9.1.0 and v9.0.0 Eiger 9.0.0 Jungfrau 9.1.0 - Mythen3 9.1.0 + Mythen3 9.1.1 Gotthard2 9.0.0 Gotthard 9.0.0 Moench 9.0.0 diff --git a/serverBin/mythen3DetectorServerv9.1.0 b/serverBin/mythen3DetectorServerv9.1.0 deleted file mode 120000 index f8c18a0e4..000000000 --- a/serverBin/mythen3DetectorServerv9.1.0 +++ /dev/null @@ -1 +0,0 @@ -../slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServerv9.1.0 \ No newline at end of file diff --git a/serverBin/mythen3DetectorServerv9.1.1 b/serverBin/mythen3DetectorServerv9.1.1 new file mode 120000 index 000000000..e31a17b7a --- /dev/null +++ b/serverBin/mythen3DetectorServerv9.1.1 @@ -0,0 +1 @@ +../slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServerv9.1.1 \ No newline at end of file From cfde194834933b4eda0751039ab871963c7a4082 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 9 Apr 2025 18:38:06 +0200 Subject: [PATCH 04/33] fixed versioning in server from preivous commit --- .../bin/mythen3DetectorServerv9.1.1 | Bin 305652 -> 305652 bytes slsSupportLib/include/sls/versionAPI.h | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServerv9.1.1 b/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServerv9.1.1 index fe2e412c758c1bf1d6dd6b50bfe2a95b66fd4902..9dc94f65e4df73e93561c0acfcd0df749d80cf18 100755 GIT binary patch delta 36 ucmV+<0Nelc(-QR460o$Z0YrnftB19#0k^fQ0-t~ZF^5$w1GiNy1fP9es}P?6 delta 36 ucmV+<0Nelc(-QR460o$Z0R)4!tB19#0k^fQ0-t~ZFo#tv1GiNy1fP9YAP`;v diff --git a/slsSupportLib/include/sls/versionAPI.h b/slsSupportLib/include/sls/versionAPI.h index 13710cf44..49150ec51 100644 --- a/slsSupportLib/include/sls/versionAPI.h +++ b/slsSupportLib/include/sls/versionAPI.h @@ -10,4 +10,4 @@ #define APIJUNGFRAU "9.1.0 0x250318" #define APILIB "9.1.0 0x250325" #define APIRECEIVER "9.1.0 0x250325" -#define APIMYTHEN3 "9.1.0 0x250409" +#define APIMYTHEN3 "9.1.1 0x250409" From 7b3accf372faec694a69df7fce74f8d0cefcdcaa Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Thu, 10 Apr 2025 13:11:16 +0200 Subject: [PATCH 05/33] commenting out the example in receiver data call back changing size as it affects users using debugging mode to print out headers --- slsReceiverSoftware/src/MultiReceiverApp.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/slsReceiverSoftware/src/MultiReceiverApp.cpp b/slsReceiverSoftware/src/MultiReceiverApp.cpp index b8510ae92..2312aada0 100644 --- a/slsReceiverSoftware/src/MultiReceiverApp.cpp +++ b/slsReceiverSoftware/src/MultiReceiverApp.cpp @@ -150,8 +150,9 @@ void GetData(slsDetectorDefs::sls_receiver_header &header, // header->packetsMask.to_string().c_str(), ((uint8_t)(*((uint8_t *)(dataPointer)))), imageSize); - // if data is modified, eg ROI and size is reduced - imageSize = 26000; + // if data is modified, can affect size + // only reduction in size allowed, not increase + // imageSize = 26000; } /** From 9aceb07d808a25c508895afd315435762b52fd10 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Fri, 11 Apr 2025 10:30:28 +0200 Subject: [PATCH 06/33] update the comment about how to modify data on a data call back from the receiver --- slsReceiverSoftware/src/MultiReceiverApp.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/slsReceiverSoftware/src/MultiReceiverApp.cpp b/slsReceiverSoftware/src/MultiReceiverApp.cpp index 2312aada0..b4fd94122 100644 --- a/slsReceiverSoftware/src/MultiReceiverApp.cpp +++ b/slsReceiverSoftware/src/MultiReceiverApp.cpp @@ -150,9 +150,21 @@ void GetData(slsDetectorDefs::sls_receiver_header &header, // header->packetsMask.to_string().c_str(), ((uint8_t)(*((uint8_t *)(dataPointer)))), imageSize); - // if data is modified, can affect size - // only reduction in size allowed, not increase - // imageSize = 26000; + // // 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; } /** From af386a736e3d8f894ff1b552ac74d96153ac32ed Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Fri, 2 May 2025 16:19:34 +0200 Subject: [PATCH 07/33] first draft of frame synchonizer test. also updated tests updated test_simiulator.py --- python/CMakeLists.txt | 7 +- .../tests/Caller/test-Caller-rx.cpp | 36 +-- tests/CMakeLists.txt | 1 + tests/scripts/test_frame_synchronizer.py | 223 ++++++++++++++++++ tests/scripts/test_simulators.py | 184 +++++++-------- 5 files changed, 340 insertions(+), 111 deletions(-) create mode 100644 tests/scripts/test_frame_synchronizer.py diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 1bd44d267..36c0c35a7 100755 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -64,6 +64,10 @@ configure_file( scripts/test_virtual.py ${CMAKE_BINARY_DIR}/test_virtual.py ) +configure_file(scripts/frameSynchronizerPullSocket.py + ${CMAKE_BINARY_DIR}/frameSynchronizerPullSocket.py COPYONLY) + + configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/../VERSION ${CMAKE_BINARY_DIR}/bin/slsdet/VERSION ) @@ -76,4 +80,5 @@ if(SLS_INSTALL_PYTHONEXT) install(FILES ${PYTHON_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/python/slsdet) install(FILES ../VERSION DESTINATION ${CMAKE_INSTALL_PREFIX}/python/slsdet) -endif() \ No newline at end of file +endif() + diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp index 38fe14e86..66cc474d6 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-rx.cpp @@ -445,23 +445,25 @@ TEST_CASE("rx_arping", "[.cmdcall][.rx]") { Detector det; Caller caller(&det); auto prev_val = det.getRxArping(); - { - std::ostringstream oss; - caller.call("rx_arping", {"1"}, -1, PUT, oss); - REQUIRE(oss.str() == "rx_arping 1\n"); - } - { - std::ostringstream oss; - caller.call("rx_arping", {}, -1, GET, oss); - REQUIRE(oss.str() == "rx_arping 1\n"); - } - { - std::ostringstream oss; - caller.call("rx_arping", {"0"}, -1, PUT, oss); - REQUIRE(oss.str() == "rx_arping 0\n"); - } - for (int i = 0; i != det.size(); ++i) { - det.setRxArping(prev_val[i], {i}); + if (det.getDestinationUDPIP()[0].str() != "127.0.0.1") { + { + std::ostringstream oss; + caller.call("rx_arping", {"1"}, -1, PUT, oss); + REQUIRE(oss.str() == "rx_arping 1\n"); + } + { + std::ostringstream oss; + caller.call("rx_arping", {}, -1, GET, oss); + REQUIRE(oss.str() == "rx_arping 1\n"); + } + { + std::ostringstream oss; + caller.call("rx_arping", {"0"}, -1, PUT, oss); + REQUIRE(oss.str() == "rx_arping 0\n"); + } + for (int i = 0; i != det.size(); ++i) { + det.setRxArping(prev_val[i], {i}); + } } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f222cc599..7f717667d 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -60,3 +60,4 @@ include(Catch) catch_discover_tests(tests) configure_file(scripts/test_simulators.py ${CMAKE_BINARY_DIR}/bin/test_simulators.py COPYONLY) +configure_file(scripts/test_frame_synchronizer.py ${CMAKE_BINARY_DIR}/bin/test_frame_synchronizer.py COPYONLY) diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py new file mode 100644 index 000000000..ab7febe4a --- /dev/null +++ b/tests/scripts/test_frame_synchronizer.py @@ -0,0 +1,223 @@ +# SPDX-License-Identifier: LGPL-3.0-or-other +# Copyright (C) 2021 Contributors to the SLS Detector Package +''' +This file is used to start up simulators, receivers and run all the tests on them and finally kill the simulators and receivers. +''' +import argparse +import os, sys, subprocess, time, colorama +import shlex + +from colorama import Fore, Style +from slsdet import Detector, detectorType, detectorSettings +from slsdet.defines import DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO +SERVER_START_PORTNO=1900 + + +colorama.init(autoreset=True) + +def Log(color, message): + print(f"{color}{message}{Style.RESET_ALL}", flush=True) + +class RuntimeException (Exception): + def __init__ (self, message): + super().__init__(Log(Fore.RED, message)) + +def checkIfProcessRunning(processName): + cmd = f"pgrep -f {processName}" + res = subprocess.getoutput(cmd) + return res.strip().splitlines() + + +def killProcess(name): + pids = checkIfProcessRunning(name) + if pids: + Log(Fore.GREEN, f"Killing '{name}' processes with PIDs: {', '.join(pids)}") + for pid in pids: + try: + p = subprocess.run(['kill', pid]) + if p.returncode != 0 and bool(checkIfProcessRunning(name)): + raise RuntimeException(f"Could not kill {name} with pid {pid}") + except Exception as e: + Log(Fore.RED, f"Failed to kill process {name} pid:{pid}. Exception occured: [code:{e}, msg:{e.stderr}]") + raise + #else: + # Log(Fore.WHITE, 'process not running : ' + name) + + +def cleanup(fp): + ''' + kill both servers, receivers and clean shared memory + ''' + Log(Fore.GREEN, 'Cleaning up...') + killProcess('DetectorServer_virtual') + killProcess('slsReceiver') + killProcess('slsMultiReceiver') + killProcess('slsFrameSynchronizer') + killProcess('frameSynchronizerPullSocket') + cleanSharedmemory(fp) + +def cleanSharedmemory(fp): + Log(Fore.GREEN, 'Cleaning up shared memory...') + try: + p = subprocess.run(['sls_detector_get', 'free'], stdout=fp, stderr=fp) + except: + Log(Fore.RED, 'Could not free shared memory') + raise + +def startProcessInBackground(name): + try: + # in background and dont print output + p = subprocess.Popen(shlex.split(name), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) + Log(Fore.GREEN, 'Starting up ' + name + ' ...') + except Exception as e: + Log(Fore.RED, f'Could not start {name}:{e}') + raise + +def startServers(name, num_mods): + for i in range(num_mods): + port_no = SERVER_START_PORTNO + (i * 2) + startProcessInBackground(name + 'DetectorServer_virtual -p' + str(port_no)) + time.sleep(6) + +def startFrameSynchronizerPullSocket(): + startProcessInBackground('python frameSynchronizerPullSocket.py') + tStartup = 4 + Log(Fore.WHITE, 'Takes ' + str(tStartup) + ' seconds... Please be patient') + time.sleep(tStartup) + if not checkIfProcessRunning('frameSynchonizerPull'): + Log(Fore.RED, "Could not start pull socket. Its not running.") + raise + +def startFrameSynchronizer(num_mods): + Log(Fore.GREEN, "Going to start frame synchonizer") + # in 10.0.0 + #startProcessInBackground('slsFrameSynchronizer -n ' + str(num_mods) + ' -p ' + str(DEFAULT_TCP_RX_PORTNO)) + startProcessInBackground('slsFrameSynchronizer ' + str(DEFAULT_TCP_RX_PORTNO) + ' ' + str(num_mods)) + tStartup = 1 * num_mods + Log(Fore.WHITE, 'Takes ' + str(tStartup) + ' seconds... Please be patient') + time.sleep(tStartup) + +def loadConfig(name, num_mods, rx_hostname, settingsdir, num_frames): + Log(Fore.GREEN, 'Loading config') + try: + d = Detector() + d.virtual = [num_mods, SERVER_START_PORTNO] + d.udp_dstport = DEFAULT_UDP_DST_PORTNO + + if name == 'eiger': + d.udp_dstport2 = DEFAULT_UDP_DST_PORTNO + 1 + + d.rx_hostname = rx_hostname + d.udp_dstip = 'auto' + d.udp_srcip = 'auto' + + if name == 'eiger': + d.trimen = [4500, 5400, 6400] + d.settingspath = settingsdir + '/eiger/' + d.setThresholdEnergy(4500, detectorSettings.STANDARD) + elif d.type == detectorType.JUNGFRAU or d.type == detectorType.MOENCH or d.type == detectorType.XILINX_CHIPTESTBOARD: + d.powerchip = 1 + + if d.type == detectorType.XILINX_CHIPTESTBOARD: + d.configureTransceiver() + + d.frames = num_frames + except Exception as e: + Log(Fore.RED, f'Could not load config for {name}. Error: {str(e)}') + raise + +def startTests(name, fp, fname, num_frames): + Log(Fore.GREEN, 'Tests for ' + name) + cmd = 'tests --abort [.cmdcall] -s -o ' + fname + + d = Detector() + d.acquire() + fnum = d.rx_framescaught[0] + if fnum == num_frames: + Log(Fore.RED, "{name} caught only {fnum}. Expected {num_frames}") + raise + + Log(Fore.GREEN, 'Tests successful for ' + name) + + +# parse cmd line for rx_hostname and settingspath using the argparse library +parser = argparse.ArgumentParser(description = 'automated tests with the virtual detector servers') +parser.add_argument('rx_hostname', nargs='?', default='localhost', help = 'hostname/ip of the current machine') +parser.add_argument('settingspath', nargs='?', default='../../settingsdir', help = 'Relative or absolut path to the settingspath') +parser.add_argument('-n', '--num-mods', nargs='?', default=2, type=int, help = 'Number of modules to test with') +parser.add_argument('-f', '--num-frames', nargs='?', default=1, type=int, help = 'Number of frames to test with') +parser.add_argument('-s', '--servers', nargs='*', help='Detector servers to run') +args = parser.parse_args() + +if args.servers is None: + servers = [ + #'eiger', + 'jungfrau', + #'mythen3', + #'gotthard2', + #'ctb', + #'moench', + #'xilinx_ctb' + ] +else: + servers = args.servers + + +Log(Fore.WHITE, 'Arguments:\nrx_hostname: ' + args.rx_hostname + '\nsettingspath: \'' + args.settingspath + '\nservers: \'' + ' '.join(servers) + '\nnum_mods: \'' + str(args.num_mods) + '\nnum_frames: \'' + str(args.num_frames) + '\'') + + +# redirect to file +prefix_fname = '/tmp/slsFrameSynchronizer_test' +original_stdout = sys.stdout +original_stderr = sys.stderr +fname = prefix_fname + '_log.txt' +Log(Fore.BLUE, '\nLog File: ' + fname) + +with open(fname, 'w') as fp: + + try: + cleanup(fp) + + testError = False + for server in servers: + try: + # print to terminal for progress + sys.stdout = original_stdout + sys.stderr = original_stderr + file_results = prefix_fname + '_results_cmd_' + server + '.txt' + Log(Fore.BLUE, 'Synchonizer tests for ' + server + ' (results: ' + file_results + ')') + sys.stdout = fp + sys.stderr = fp + Log(Fore.BLUE, 'Synchonizer tests for ' + server + ' (results: ' + file_results + ')') + + # cmd tests for det + cleanup(fp) + startServers(server, args.num_mods) + startFrameSynchronizerPullSocket() + startFrameSynchronizer(args.num_mods) + loadConfig(server, args.num_mods, args.rx_hostname, args.settingspath, args.num_frames) + startTests(server, fp, file_results, args.num_frames) + cleanup(fp) + + except Exception as e: + # redirect to terminal + sys.stdout = original_stdout + sys.stderr = original_stderr + Log(Fore.RED, f'Exception caught while testing {server}. Cleaning up...') + testError = True + break + + # redirect to terminal + sys.stdout = original_stdout + sys.stderr = original_stderr + if not testError: + Log(Fore.GREEN, 'Passed all sync tests\n' + str(servers)) + + + except Exception as e: + # redirect to terminal + sys.stdout = original_stdout + sys.stderr = original_stderr + Log(Fore.RED, f'Exception caught with general testing. Cleaning up...') + cleanSharedmemory(sys.stdout) + \ No newline at end of file diff --git a/tests/scripts/test_simulators.py b/tests/scripts/test_simulators.py index 34f7ca9ba..7715ede32 100644 --- a/tests/scripts/test_simulators.py +++ b/tests/scripts/test_simulators.py @@ -4,9 +4,9 @@ This file is used to start up simulators, receivers and run all the tests on them and finally kill the simulators and receivers. ''' import argparse -import os, sys, subprocess, time, colorama, signal +import os, sys, subprocess, time, colorama -from colorama import Fore +from colorama import Fore, Style from slsdet import Detector, detectorType, detectorSettings from slsdet.defines import DEFAULT_TCP_CNTRL_PORTNO, DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO HALFMOD2_TCP_CNTRL_PORTNO=1955 @@ -14,68 +14,46 @@ HALFMOD2_TCP_RX_PORTNO=1957 colorama.init(autoreset=True) +def Log(color, message): + print(f"{color}{message}{Style.RESET_ALL}", flush=True) + class RuntimeException (Exception): def __init__ (self, message): - super().__init__(Fore.RED + message) + super().__init__(Log(Fore.RED, message)) -def Log(color, message): - print('\n' + color + message, flush=True) - - def checkIfProcessRunning(processName): - cmd = "ps -ef | grep " + processName - print(cmd) - res=subprocess.getoutput(cmd) - print(res) - # eg. of output - #l_user 250506 243295 0 14:38 pts/5 00:00:00 /bin/sh -c ps -ef | grep slsReceiver - #l_user 250508 250506 0 14:38 pts/5 00:00:00 grep slsReceiver - - print('how many') - cmd = "ps -ef | grep " + processName + " | wc -l" - print(cmd) - res=subprocess.getoutput(cmd) - print(res) - - if res == '2': - return False - return True + cmd = f"pgrep -f {processName}" + res = subprocess.getoutput(cmd) + return res.strip().splitlines() def killProcess(name): - if checkIfProcessRunning(name): - Log(Fore.GREEN, 'killing ' + name) - p = subprocess.run(['killall', name]) - if p.returncode != 0: - raise RuntimeException('killall failed for ' + name) - else: - print('process not running : ' + name) + pids = checkIfProcessRunning(name) + if pids: + Log(Fore.GREEN, f"Killing '{name}' processes with PIDs: {', '.join(pids)}") + for pid in pids: + try: + p = subprocess.run(['kill', pid]) + if p.returncode != 0 and bool(checkIfProcessRunning(name)): + raise RuntimeException(f"Could not kill {name} with pid {pid}") + except Exception as e: + Log(Fore.RED, f"Failed to kill process {name} pid:{pid}. Exception occured: [code:{e}, msg:{e.stderr}]") + raise + #else: + # Log(Fore.WHITE, 'process not running : ' + name) -def killAllStaleProcesses(): - killProcess('eigerDetectorServer_virtual') - killProcess('jungfrauDetectorServer_virtual') - killProcess('mythen3DetectorServer_virtual') - killProcess('gotthard2DetectorServer_virtual') - killProcess('gotthardDetectorServer_virtual') - killProcess('ctbDetectorServer_virtual') - killProcess('moenchDetectorServer_virtual') - killProcess('xilinx_ctbDetectorServer_virtual') - killProcess('slsReceiver') - killProcess('slsMultiReceiver') - cleanSharedmemory() - -def cleanup(name): +def cleanup(fp): ''' kill both servers, receivers and clean shared memory ''' Log(Fore.GREEN, 'Cleaning up...') - killProcess(name + 'DetectorServer_virtual') + killProcess('DetectorServer_virtual') killProcess('slsReceiver') killProcess('slsMultiReceiver') - cleanSharedmemory() + cleanSharedmemory(fp) -def cleanSharedmemory(): +def cleanSharedmemory(fp): Log(Fore.GREEN, 'Cleaning up shared memory...') try: p = subprocess.run(['sls_detector_get', 'free'], stdout=fp, stderr=fp) @@ -88,8 +66,8 @@ def startProcessInBackground(name): # in background and dont print output p = subprocess.Popen(name.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) Log(Fore.GREEN, 'Starting up ' + name + ' ...') - except: - Log(Fore.RED, 'Could not start ' + name) + except Exception as e: + Log(Fore.RED, f'Could not start {name}:{e}') raise def startServer(name): @@ -128,6 +106,7 @@ def loadConfig(name, rx_hostname, settingsdir): d.hostname = 'localhost' d.rx_hostname = rx_hostname d.udp_dstip = 'auto' + d.udp_dstip = 'auto' if d.type == detectorType.GOTTHARD: d.udp_srcip = d.udp_dstip else: @@ -143,14 +122,19 @@ def loadConfig(name, rx_hostname, settingsdir): def startCmdTests(name, fp, fname): Log(Fore.GREEN, 'Cmd Tests for ' + name) cmd = 'tests --abort [.cmdcall] -s -o ' + fname - p = subprocess.run(cmd.split(), stdout=fp, stderr=fp, check=True, text=True) - p.check_returncode() + try: + subprocess.run(cmd.split(), stdout=fp, stderr=fp, check=True, text=True) + except subprocess.CalledProcessError as e: + pass with open (fname, 'r') as f: for line in f: if "FAILED" in line: msg = 'Cmd tests failed for ' + name + '!!!' + sys.stdout = original_stdout Log(Fore.RED, msg) + Log(Fore.RED, line) + sys.stdout = fp raise Exception(msg) Log(Fore.GREEN, 'Cmd Tests successful for ' + name) @@ -158,14 +142,18 @@ def startCmdTests(name, fp, fname): def startGeneralTests(fp, fname): Log(Fore.GREEN, 'General Tests') cmd = 'tests --abort -s -o ' + fname - p = subprocess.run(cmd.split(), stdout=fp, stderr=fp, check=True, text=True) - p.check_returncode() + try: + subprocess.run(cmd.split(), stdout=fp, stderr=fp, check=True, text=True) + except subprocess.CalledProcessError as e: + pass with open (fname, 'r') as f: for line in f: if "FAILED" in line: msg = 'General tests failed !!!' - Log(Fore.RED, msg) + sys.stdout = original_stdout + Log(Fore.RED, msg + '\n' + line) + sys.stdout = fp raise Exception(msg) Log(Fore.GREEN, 'General Tests successful') @@ -174,12 +162,10 @@ def startGeneralTests(fp, fname): # parse cmd line for rx_hostname and settingspath using the argparse library parser = argparse.ArgumentParser(description = 'automated tests with the virtual detector servers') -parser.add_argument('rx_hostname', help = 'hostname/ip of the current machine') -parser.add_argument('settingspath', help = 'Relative or absolut path to the settingspath') +parser.add_argument('rx_hostname', nargs='?', default='localhost', help = 'hostname/ip of the current machine') +parser.add_argument('settingspath', nargs='?', default='../../settingsdir', help = 'Relative or absolut path to the settingspath') parser.add_argument('-s', '--servers', help='Detector servers to run', nargs='*') args = parser.parse_args() -if args.rx_hostname == 'localhost': - raise RuntimeException('Cannot use localhost for rx_hostname for the tests (fails for rx_arping for eg.)') if args.servers is None: servers = [ @@ -196,7 +182,7 @@ else: servers = args.servers -Log(Fore.WHITE, 'Arguments:\nrx_hostname: ' + args.rx_hostname + '\nsettingspath: \'' + args.settingspath + '\'') +Log(Fore.WHITE, 'Arguments:\nrx_hostname: ' + args.rx_hostname + '\nsettingspath: \'' + args.settingspath + '\nservers: \'' + ' '.join(servers) + '\'') # redirect to file @@ -208,46 +194,58 @@ Log(Fore.BLUE, '\nLog File: ' + fname) with open(fname, 'w') as fp: + + # general tests file_results = prefix_fname + '_results_general.txt' Log(Fore.BLUE, 'General tests (results: ' + file_results + ')') sys.stdout = fp sys.stderr = fp Log(Fore.BLUE, 'General tests (results: ' + file_results + ')') - startGeneralTests(fp, file_results) - killAllStaleProcesses() + try: + startGeneralTests(fp, file_results) + cleanup(fp) - for server in servers: - try: - # print to terminal for progress - sys.stdout = original_stdout - sys.stderr = original_stderr - file_results = prefix_fname + '_results_cmd_' + server + '.txt' - Log(Fore.BLUE, 'Cmd tests for ' + server + ' (results: ' + file_results + ')') - sys.stdout = fp - sys.stderr = fp - Log(Fore.BLUE, 'Cmd tests for ' + server + ' (results: ' + file_results + ')') - - # cmd tests for det - cleanup(server) - startServer(server) - startReceiver(server) - loadConfig(server, args.rx_hostname, args.settingspath) - startCmdTests(server, fp, file_results) - cleanup(server) - except: - Log(Fore.RED, 'Exception caught. Cleaning up.') - cleanup(server) - sys.stdout = original_stdout - sys.stderr = original_stderr - Log(Fore.RED, 'Cmd tests failed for ' + server + '!!!') - raise + testError = False + for server in servers: + try: + # print to terminal for progress + sys.stdout = original_stdout + sys.stderr = original_stderr + file_results = prefix_fname + '_results_cmd_' + server + '.txt' + Log(Fore.BLUE, 'Cmd tests for ' + server + ' (results: ' + file_results + ')') + sys.stdout = fp + sys.stderr = fp + Log(Fore.BLUE, 'Cmd tests for ' + server + ' (results: ' + file_results + ')') + + # cmd tests for det + cleanup(fp) + startServer(server) + startReceiver(server) + loadConfig(server, args.rx_hostname, args.settingspath) + startCmdTests(server, fp, file_results) + cleanup(fp) + + except Exception as e: + # redirect to terminal + sys.stdout = original_stdout + sys.stderr = original_stderr + Log(Fore.RED, f'Exception caught while testing {server}. Cleaning up...') + testError = True + break + + # redirect to terminal + sys.stdout = original_stdout + sys.stderr = original_stderr + if not testError: + Log(Fore.GREEN, 'Passed all tests for virtual detectors \n' + str(servers)) - Log(Fore.GREEN, 'Passed all tests for virtual detectors \n' + str(servers)) - -# redirect to terminal -sys.stdout = original_stdout -sys.stderr = original_stderr -Log(Fore.GREEN, 'Passed all tests for virtual detectors \n' + str(servers) + '\nYayyyy! :) ') \ No newline at end of file + except Exception as e: + # redirect to terminal + sys.stdout = original_stdout + sys.stderr = original_stderr + Log(Fore.RED, f'Exception caught with general testing. Cleaning up...') + cleanSharedmemory(sys.stdout) + \ No newline at end of file From 0f4d10912bed4e94c4a06fb482e655d554f01244 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Fri, 2 May 2025 22:48:48 +0200 Subject: [PATCH 08/33] test frame synchonizer now runs for all modules (only jungfrua enableD) with num modules and num frames as arguments (also servers) and checks the output log of pull socket to ensure there are expected number of headers, moduels and series end htypes --- python/CMakeLists.txt | 2 +- tests/scripts/test_frame_synchronizer.py | 113 ++++++++++++++--------- 2 files changed, 72 insertions(+), 43 deletions(-) diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 36c0c35a7..9765e55d1 100755 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -65,7 +65,7 @@ configure_file( scripts/test_virtual.py ) configure_file(scripts/frameSynchronizerPullSocket.py - ${CMAKE_BINARY_DIR}/frameSynchronizerPullSocket.py COPYONLY) + ${CMAKE_BINARY_DIR}/bin/frameSynchronizerPullSocket.py COPYONLY) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/../VERSION diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py index ab7febe4a..2d88b6fcb 100644 --- a/tests/scripts/test_frame_synchronizer.py +++ b/tests/scripts/test_frame_synchronizer.py @@ -5,7 +5,7 @@ This file is used to start up simulators, receivers and run all the tests on the ''' import argparse import os, sys, subprocess, time, colorama -import shlex +import shlex, traceback, json from colorama import Fore, Style from slsdet import Detector, detectorType, detectorSettings @@ -15,8 +15,8 @@ SERVER_START_PORTNO=1900 colorama.init(autoreset=True) -def Log(color, message): - print(f"{color}{message}{Style.RESET_ALL}", flush=True) +def Log(color, message, stream=sys.stdout): + print(f"{color}{message}{Style.RESET_ALL}", file=stream, flush=True) class RuntimeException (Exception): def __init__ (self, message): @@ -28,10 +28,10 @@ def checkIfProcessRunning(processName): return res.strip().splitlines() -def killProcess(name): +def killProcess(name, fp): pids = checkIfProcessRunning(name) if pids: - Log(Fore.GREEN, f"Killing '{name}' processes with PIDs: {', '.join(pids)}") + Log(Fore.WHITE, f"Killing '{name}' processes with PIDs: {', '.join(pids)}", fp) for pid in pids: try: p = subprocess.run(['kill', pid]) @@ -48,16 +48,17 @@ def cleanup(fp): ''' kill both servers, receivers and clean shared memory ''' - Log(Fore.GREEN, 'Cleaning up...') - killProcess('DetectorServer_virtual') - killProcess('slsReceiver') - killProcess('slsMultiReceiver') - killProcess('slsFrameSynchronizer') - killProcess('frameSynchronizerPullSocket') + Log(Fore.WHITE, 'Cleaning up') + Log(Fore.WHITE, 'Cleaning up', fp) + killProcess('DetectorServer_virtual', fp) + killProcess('slsReceiver', fp) + killProcess('slsMultiReceiver', fp) + killProcess('slsFrameSynchronizer', fp) + killProcess('frameSynchronizerPullSocket', fp) cleanSharedmemory(fp) def cleanSharedmemory(fp): - Log(Fore.GREEN, 'Cleaning up shared memory...') + Log(Fore.WHITE, 'Cleaning up shared memory...', fp) try: p = subprocess.run(['sls_detector_get', 'free'], stdout=fp, stderr=fp) except: @@ -68,37 +69,41 @@ def startProcessInBackground(name): try: # in background and dont print output p = subprocess.Popen(shlex.split(name), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) - Log(Fore.GREEN, 'Starting up ' + name + ' ...') + Log(Fore.WHITE, 'Starting up ' + name + ' ...', fp) except Exception as e: Log(Fore.RED, f'Could not start {name}:{e}') raise def startServers(name, num_mods): + Log(Fore.WHITE, 'Starting server') for i in range(num_mods): port_no = SERVER_START_PORTNO + (i * 2) startProcessInBackground(name + 'DetectorServer_virtual -p' + str(port_no)) time.sleep(6) -def startFrameSynchronizerPullSocket(): - startProcessInBackground('python frameSynchronizerPullSocket.py') - tStartup = 4 - Log(Fore.WHITE, 'Takes ' + str(tStartup) + ' seconds... Please be patient') - time.sleep(tStartup) - if not checkIfProcessRunning('frameSynchonizerPull'): - Log(Fore.RED, "Could not start pull socket. Its not running.") +def startFrameSynchronizerPullSocket(fname, fp): + Log(Fore.WHITE, 'Starting sync pull socket') + Log(Fore.WHITE, f"Starting up Synchronizer pull socket. Log: {fname}", fp) + Log(Fore.WHITE, f"Synchronizer pull socket log: {fname}") + cmd = ['python', '-u', 'frameSynchronizerPullSocket.py'] + try: + with open(fname, 'w') as fp: + subprocess.Popen(cmd, stdout=fp, stderr=fp, text=True) + except Exception as e: + Log(Fore.RED, f"failed to start synchronizer pull socket: {e}") raise def startFrameSynchronizer(num_mods): - Log(Fore.GREEN, "Going to start frame synchonizer") + Log(Fore.WHITE, 'Starting frame synchronizer') # in 10.0.0 #startProcessInBackground('slsFrameSynchronizer -n ' + str(num_mods) + ' -p ' + str(DEFAULT_TCP_RX_PORTNO)) startProcessInBackground('slsFrameSynchronizer ' + str(DEFAULT_TCP_RX_PORTNO) + ' ' + str(num_mods)) tStartup = 1 * num_mods - Log(Fore.WHITE, 'Takes ' + str(tStartup) + ' seconds... Please be patient') time.sleep(tStartup) -def loadConfig(name, num_mods, rx_hostname, settingsdir, num_frames): - Log(Fore.GREEN, 'Loading config') +def loadConfig(name, num_mods, rx_hostname, settingsdir, num_frames, fp): + Log(Fore.WHITE, 'Loading config') + Log(Fore.WHITE, 'Loading config', fp) try: d = Detector() d.virtual = [num_mods, SERVER_START_PORTNO] @@ -126,18 +131,46 @@ def loadConfig(name, num_mods, rx_hostname, settingsdir, num_frames): Log(Fore.RED, f'Could not load config for {name}. Error: {str(e)}') raise -def startTests(name, fp, fname, num_frames): - Log(Fore.GREEN, 'Tests for ' + name) +def validate_htype_counts(log_path, num_mods, num_frames): + htype_counts = { + "header": 0, + "series_end": 0, + "module": 0 + } + + with open(log_path, 'r') as f: + for line in f: + line = line.strip() + if not line or not line.startswith('{'): + continue + try: + data = json.loads(line) + htype = data.get("htype") + if htype in htype_counts: + htype_counts[htype] += 1 + except json.JSONDecodeError: + continue # or log malformed line + + for htype, expected_count in [("header", num_mods), ("series_end", num_mods * num_frames), ("module", num_mods)]: + if htype_counts[htype] != expected_count: + msg = f"Expected 2 '{htype}' entries, found {htype_counts[htype]}" + Log(Fore.RED, msg) + raise RuntimeError(msg) + +def startTests(name, num_mods, num_frames, fp, file_pull_socket): + Log(Fore.WHITE, 'Tests for ' + name) + Log(Fore.WHITE, 'Tests for ' + name, fp) cmd = 'tests --abort [.cmdcall] -s -o ' + fname d = Detector() d.acquire() fnum = d.rx_framescaught[0] - if fnum == num_frames: - Log(Fore.RED, "{name} caught only {fnum}. Expected {num_frames}") + if fnum != num_frames: + Log(Fore.RED, f"{name} caught only {fnum}. Expected {num_frames}") raise - Log(Fore.GREEN, 'Tests successful for ' + name) + validate_htype_counts(file_pull_socket, num_mods, num_frames) + Log(Fore.GREEN, f"Log file htype checks passed for {name}", fp) # parse cmd line for rx_hostname and settingspath using the argparse library @@ -176,27 +209,20 @@ Log(Fore.BLUE, '\nLog File: ' + fname) with open(fname, 'w') as fp: try: - cleanup(fp) - testError = False for server in servers: try: - # print to terminal for progress - sys.stdout = original_stdout - sys.stderr = original_stderr - file_results = prefix_fname + '_results_cmd_' + server + '.txt' - Log(Fore.BLUE, 'Synchonizer tests for ' + server + ' (results: ' + file_results + ')') - sys.stdout = fp - sys.stderr = fp - Log(Fore.BLUE, 'Synchonizer tests for ' + server + ' (results: ' + file_results + ')') + Log(Fore.BLUE, '\nSynchonizer tests for ' + server, fp) + Log(Fore.BLUE, '\nSynchonizer tests for ' + server) # cmd tests for det cleanup(fp) startServers(server, args.num_mods) - startFrameSynchronizerPullSocket() + file_pull_socket = prefix_fname + '_pull_socket_' + server + '.txt' + startFrameSynchronizerPullSocket(file_pull_socket, fp) startFrameSynchronizer(args.num_mods) - loadConfig(server, args.num_mods, args.rx_hostname, args.settingspath, args.num_frames) - startTests(server, fp, file_results, args.num_frames) + loadConfig(server, args.num_mods, args.rx_hostname, args.settingspath, args.num_frames, fp) + startTests(server, args.num_mods, args.num_frames, fp, file_pull_socket) cleanup(fp) except Exception as e: @@ -204,6 +230,9 @@ with open(fname, 'w') as fp: sys.stdout = original_stdout sys.stderr = original_stderr Log(Fore.RED, f'Exception caught while testing {server}. Cleaning up...') + with open(fname, 'a') as fp_error: + traceback.print_exc(file=fp_error) # This will log the full traceback + testError = True break From 6c604e234076abe93513aa4eac3a38f9f9905f38 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Sat, 3 May 2025 00:11:55 +0200 Subject: [PATCH 09/33] small fixes, but bug introduced here 'https://github.com/slsdetectorgroup/slsDetectorPackage/pull/968/commits/40895b01488662184710f8362c30b20a7828da7b' --- slsReceiverSoftware/src/FrameSynchronizerApp.cpp | 9 +++++---- tests/scripts/test_frame_synchronizer.py | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/slsReceiverSoftware/src/FrameSynchronizerApp.cpp b/slsReceiverSoftware/src/FrameSynchronizerApp.cpp index 3d793e46f..96275e1a7 100644 --- a/slsReceiverSoftware/src/FrameSynchronizerApp.cpp +++ b/slsReceiverSoftware/src/FrameSynchronizerApp.cpp @@ -130,7 +130,7 @@ std::set get_valid_fnums(const PortFrameMap &port_frame_map) { // collect all unique frame numbers from all ports std::set unique_fnums; - for (auto it = port_frame_map.begin(); it != port_frame_map.begin(); ++it) { + for (auto it = port_frame_map.begin(); it != port_frame_map.end(); ++it) { const FrameMap &frame_map = it->second; for (auto frame = frame_map.begin(); frame != frame_map.end(); ++frame) { @@ -151,9 +151,10 @@ std::set get_valid_fnums(const PortFrameMap &port_frame_map) { LOG(sls::logDEBUG) << "Fnum " << fnum << " is missing in port " << port; // invalid: fnum greater than all in that port - auto last_frame = std::prev(frame_map.end()); - auto last_fnum = last_frame->first; - if (fnum > last_fnum) { + auto last_frame = + frame_map.upper_bound(fnum); // std::prev(frame_map.end()); + // auto last_fnum = last_frame->first; + if (last_frame == frame_map.end()) { //(fnum > last_fnum) { LOG(sls::logDEBUG) << "And no larger fnum found. Fnum " << fnum << " is invalid.\n"; is_valid = false; diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py index 2d88b6fcb..f901672cf 100644 --- a/tests/scripts/test_frame_synchronizer.py +++ b/tests/scripts/test_frame_synchronizer.py @@ -234,6 +234,7 @@ with open(fname, 'w') as fp: traceback.print_exc(file=fp_error) # This will log the full traceback testError = True + cleanup(fp) break # redirect to terminal @@ -248,5 +249,5 @@ with open(fname, 'w') as fp: sys.stdout = original_stdout sys.stderr = original_stderr Log(Fore.RED, f'Exception caught with general testing. Cleaning up...') - cleanSharedmemory(sys.stdout) + cleanup(fp) \ No newline at end of file From 0ecc6030c6aa0eb3a47bf59f448e26ef24b76b76 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Mon, 5 May 2025 14:03:00 +0200 Subject: [PATCH 10/33] fixed fram sync issue. mainly due to zmq msg being cleaned up before sending it --- .../src/FrameSynchronizerApp.cpp | 54 +++++++++++-------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/slsReceiverSoftware/src/FrameSynchronizerApp.cpp b/slsReceiverSoftware/src/FrameSynchronizerApp.cpp index 96275e1a7..22983dc88 100644 --- a/slsReceiverSoftware/src/FrameSynchronizerApp.cpp +++ b/slsReceiverSoftware/src/FrameSynchronizerApp.cpp @@ -107,11 +107,11 @@ void zmq_free(void *data, void *hint) { delete[] static_cast(data); } void print_frames(const PortFrameMap &frame_port_map) { LOG(sls::logDEBUG) << "Printing frames"; for (const auto &it : frame_port_map) { - uint16_t udpPort = it.first; + const uint16_t udpPort = it.first; const auto &frame_map = it.second; LOG(sls::logDEBUG) << "UDP port: " << udpPort; for (const auto &frame : frame_map) { - uint64_t fnum = frame.first; + const uint64_t fnum = frame.first; const auto &msg_list = frame.second; LOG(sls::logDEBUG) << " acq index: " << fnum << '[' << msg_list.size() << ']'; @@ -130,31 +130,32 @@ std::set get_valid_fnums(const PortFrameMap &port_frame_map) { // collect all unique frame numbers from all ports std::set unique_fnums; - for (auto it = port_frame_map.begin(); it != port_frame_map.end(); ++it) { - const FrameMap &frame_map = it->second; - for (auto frame = frame_map.begin(); frame != frame_map.end(); - ++frame) { - unique_fnums.insert(frame->first); + for (const auto &it : port_frame_map) { + const FrameMap &frame_map = it.second; + for (const auto &frame : frame_map) { + unique_fnums.insert(frame.first); } } // collect valid frame numbers for (auto &fnum : unique_fnums) { bool is_valid = true; - for (auto it = port_frame_map.begin(); it != port_frame_map.end(); - ++it) { - uint16_t port = it->first; - const FrameMap &frame_map = it->second; + for (const auto &it : port_frame_map) { + const uint16_t port = it.first; + const FrameMap &frame_map = it.second; auto frame = frame_map.find(fnum); // invalid: fnum missing in one port if (frame == frame_map.end()) { LOG(sls::logDEBUG) << "Fnum " << fnum << " is missing in port " << port; + /* // invalid: fnum greater than all in that port - auto last_frame = - frame_map.upper_bound(fnum); // std::prev(frame_map.end()); - // auto last_fnum = last_frame->first; - if (last_frame == frame_map.end()) { //(fnum > last_fnum) { + auto last_frame = std::prev(frame_map.end()); + auto last_fnum = last_frame->first; + if (fnum > last_fnum) { + */ + auto upper_frame = frame_map.upper_bound(fnum); + if (upper_frame == frame_map.end()) { LOG(sls::logDEBUG) << "And no larger fnum found. Fnum " << fnum << " is invalid.\n"; is_valid = false; @@ -224,29 +225,38 @@ void Correlate(FrameStatus *stat) { // sending all valid fnum data packets for (const auto &fnum : valid_fnums) { ZmqMsgList msg_list; - PortFrameMap &port_frame_map = stat->frames; - for (auto it = port_frame_map.begin(); - it != port_frame_map.end(); ++it) { - uint16_t port = it->first; - const FrameMap &frame_map = it->second; + // PortFrameMap &port_frame_map = stat->frames; + for (const auto &it : stat->frames) { + const uint16_t port = it.first; + const FrameMap &frame_map = it.second; auto frame = frame_map.find(fnum); if (frame != frame_map.end()) { msg_list.insert(msg_list.end(), stat->frames[port][fnum].begin(), stat->frames[port][fnum].end()); // clean up - for (zmq_msg_t *msg : stat->frames[port][fnum]) { + /*for (zmq_msg_t *msg : stat->frames[port][fnum]) { if (msg) { zmq_msg_close(msg); delete msg; } - } + }*/ stat->frames[port].erase(fnum); } } LOG(printHeadersLevel) << "Sending data packets for fnum " << fnum; zmq_send_multipart(socket, msg_list); + for (const auto &it : stat->frames) { + const uint16_t port = it.first; + // clean up + for (zmq_msg_t *msg : stat->frames[port][fnum]) { + if (msg) { + zmq_msg_close(msg); + delete msg; + } + } + } } } // sending all end packets From 15030ab73256f3266e444239afa71c137992b611 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Mon, 5 May 2025 14:04:06 +0200 Subject: [PATCH 11/33] minor --- slsReceiverSoftware/src/FrameSynchronizerApp.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/slsReceiverSoftware/src/FrameSynchronizerApp.cpp b/slsReceiverSoftware/src/FrameSynchronizerApp.cpp index 22983dc88..c37b9b9db 100644 --- a/slsReceiverSoftware/src/FrameSynchronizerApp.cpp +++ b/slsReceiverSoftware/src/FrameSynchronizerApp.cpp @@ -148,12 +148,6 @@ std::set get_valid_fnums(const PortFrameMap &port_frame_map) { if (frame == frame_map.end()) { LOG(sls::logDEBUG) << "Fnum " << fnum << " is missing in port " << port; - /* - // invalid: fnum greater than all in that port - auto last_frame = std::prev(frame_map.end()); - auto last_fnum = last_frame->first; - if (fnum > last_fnum) { - */ auto upper_frame = frame_map.upper_bound(fnum); if (upper_frame == frame_map.end()) { LOG(sls::logDEBUG) << "And no larger fnum found. Fnum " @@ -234,13 +228,6 @@ void Correlate(FrameStatus *stat) { msg_list.insert(msg_list.end(), stat->frames[port][fnum].begin(), stat->frames[port][fnum].end()); - // clean up - /*for (zmq_msg_t *msg : stat->frames[port][fnum]) { - if (msg) { - zmq_msg_close(msg); - delete msg; - } - }*/ stat->frames[port].erase(fnum); } } From b00a37bc16f04a63ac0591411051fb4742616212 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Mon, 5 May 2025 14:05:08 +0200 Subject: [PATCH 12/33] minor --- slsReceiverSoftware/src/FrameSynchronizerApp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/slsReceiverSoftware/src/FrameSynchronizerApp.cpp b/slsReceiverSoftware/src/FrameSynchronizerApp.cpp index c37b9b9db..f9658e5ee 100644 --- a/slsReceiverSoftware/src/FrameSynchronizerApp.cpp +++ b/slsReceiverSoftware/src/FrameSynchronizerApp.cpp @@ -219,7 +219,6 @@ void Correlate(FrameStatus *stat) { // sending all valid fnum data packets for (const auto &fnum : valid_fnums) { ZmqMsgList msg_list; - // PortFrameMap &port_frame_map = stat->frames; for (const auto &it : stat->frames) { const uint16_t port = it.first; const FrameMap &frame_map = it.second; From 9889a31f816a4a9814e7949a4cc4a526fb674e80 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Mon, 5 May 2025 17:22:47 +0200 Subject: [PATCH 13/33] works for all detectors --- tests/scripts/test_frame_synchronizer.py | 43 ++++++++++++------------ tests/scripts/test_simulators.py | 1 + 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py index f901672cf..621654520 100644 --- a/tests/scripts/test_frame_synchronizer.py +++ b/tests/scripts/test_frame_synchronizer.py @@ -65,7 +65,7 @@ def cleanSharedmemory(fp): Log(Fore.RED, 'Could not free shared memory') raise -def startProcessInBackground(name): +def startProcessInBackground(name, fp): try: # in background and dont print output p = subprocess.Popen(shlex.split(name), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) @@ -78,7 +78,7 @@ def startServers(name, num_mods): Log(Fore.WHITE, 'Starting server') for i in range(num_mods): port_no = SERVER_START_PORTNO + (i * 2) - startProcessInBackground(name + 'DetectorServer_virtual -p' + str(port_no)) + startProcessInBackground(name + 'DetectorServer_virtual -p' + str(port_no), fp) time.sleep(6) def startFrameSynchronizerPullSocket(fname, fp): @@ -93,11 +93,11 @@ def startFrameSynchronizerPullSocket(fname, fp): Log(Fore.RED, f"failed to start synchronizer pull socket: {e}") raise -def startFrameSynchronizer(num_mods): +def startFrameSynchronizer(num_mods, fp): Log(Fore.WHITE, 'Starting frame synchronizer') # in 10.0.0 #startProcessInBackground('slsFrameSynchronizer -n ' + str(num_mods) + ' -p ' + str(DEFAULT_TCP_RX_PORTNO)) - startProcessInBackground('slsFrameSynchronizer ' + str(DEFAULT_TCP_RX_PORTNO) + ' ' + str(num_mods)) + startProcessInBackground('slsFrameSynchronizer ' + str(DEFAULT_TCP_RX_PORTNO) + ' ' + str(num_mods), fp) tStartup = 1 * num_mods time.sleep(tStartup) @@ -114,13 +114,10 @@ def loadConfig(name, num_mods, rx_hostname, settingsdir, num_frames, fp): d.rx_hostname = rx_hostname d.udp_dstip = 'auto' - d.udp_srcip = 'auto' + if name != "eiger": + d.udp_srcip = 'auto' - if name == 'eiger': - d.trimen = [4500, 5400, 6400] - d.settingspath = settingsdir + '/eiger/' - d.setThresholdEnergy(4500, detectorSettings.STANDARD) - elif d.type == detectorType.JUNGFRAU or d.type == detectorType.MOENCH or d.type == detectorType.XILINX_CHIPTESTBOARD: + if name == "jungfrau" or name == "moench" or name == "xilinx_ctb": d.powerchip = 1 if d.type == detectorType.XILINX_CHIPTESTBOARD: @@ -131,13 +128,14 @@ def loadConfig(name, num_mods, rx_hostname, settingsdir, num_frames, fp): Log(Fore.RED, f'Could not load config for {name}. Error: {str(e)}') raise -def validate_htype_counts(log_path, num_mods, num_frames): +def validate_htype_counts(log_path, num_mods, num_ports_per_module, num_frames): htype_counts = { "header": 0, "series_end": 0, "module": 0 } + # get a count of each htype from file with open(log_path, 'r') as f: for line in f: line = line.strip() @@ -151,9 +149,9 @@ def validate_htype_counts(log_path, num_mods, num_frames): except json.JSONDecodeError: continue # or log malformed line - for htype, expected_count in [("header", num_mods), ("series_end", num_mods * num_frames), ("module", num_mods)]: + for htype, expected_count in [("header", num_mods), ("series_end", num_mods), ("module", num_ports_per_module * num_mods * num_frames)]: if htype_counts[htype] != expected_count: - msg = f"Expected 2 '{htype}' entries, found {htype_counts[htype]}" + msg = f"Expected {expected_count} '{htype}' entries, found {htype_counts[htype]}" Log(Fore.RED, msg) raise RuntimeError(msg) @@ -163,13 +161,16 @@ def startTests(name, num_mods, num_frames, fp, file_pull_socket): cmd = 'tests --abort [.cmdcall] -s -o ' + fname d = Detector() + num_ports_per_module = d.numinterfaces + if name == "gotthard2": + num_ports_per_module = 1 d.acquire() fnum = d.rx_framescaught[0] if fnum != num_frames: Log(Fore.RED, f"{name} caught only {fnum}. Expected {num_frames}") raise - validate_htype_counts(file_pull_socket, num_mods, num_frames) + validate_htype_counts(file_pull_socket, num_mods, num_ports_per_module, num_frames) Log(Fore.GREEN, f"Log file htype checks passed for {name}", fp) @@ -184,13 +185,13 @@ args = parser.parse_args() if args.servers is None: servers = [ - #'eiger', + 'eiger', 'jungfrau', - #'mythen3', - #'gotthard2', - #'ctb', - #'moench', - #'xilinx_ctb' + 'mythen3', + 'gotthard2', + 'ctb', + 'moench', + 'xilinx_ctb' ] else: servers = args.servers @@ -220,7 +221,7 @@ with open(fname, 'w') as fp: startServers(server, args.num_mods) file_pull_socket = prefix_fname + '_pull_socket_' + server + '.txt' startFrameSynchronizerPullSocket(file_pull_socket, fp) - startFrameSynchronizer(args.num_mods) + startFrameSynchronizer(args.num_mods, fp) loadConfig(server, args.num_mods, args.rx_hostname, args.settingspath, args.num_frames, fp) startTests(server, args.num_mods, args.num_frames, fp, file_pull_socket) cleanup(fp) diff --git a/tests/scripts/test_simulators.py b/tests/scripts/test_simulators.py index 7715ede32..6c59c50c0 100644 --- a/tests/scripts/test_simulators.py +++ b/tests/scripts/test_simulators.py @@ -204,6 +204,7 @@ with open(fname, 'w') as fp: Log(Fore.BLUE, 'General tests (results: ' + file_results + ')') try: + cleanup(fp) startGeneralTests(fp, file_results) cleanup(fp) From 2c93d40e81dba77a236c0e8f0c99b63642ee6e12 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Tue, 6 May 2025 17:37:40 +0200 Subject: [PATCH 14/33] moved log out to utils --- tests/scripts/test_frame_synchronizer.py | 80 +++++++++++------------- tests/scripts/test_simulators.py | 67 +++++++++----------- tests/scripts/utils_for_test.py | 41 ++++++++++++ 3 files changed, 105 insertions(+), 83 deletions(-) create mode 100644 tests/scripts/utils_for_test.py diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py index 621654520..cbb37e01e 100644 --- a/tests/scripts/test_frame_synchronizer.py +++ b/tests/scripts/test_frame_synchronizer.py @@ -1,26 +1,23 @@ # SPDX-License-Identifier: LGPL-3.0-or-other # Copyright (C) 2021 Contributors to the SLS Detector Package ''' -This file is used to start up simulators, receivers and run all the tests on them and finally kill the simulators and receivers. +This file is used to start up simulators, frame synchronizer, pull sockets, acquire, test and kill them finally. ''' import argparse -import os, sys, subprocess, time, colorama +import os, sys, subprocess, time import shlex, traceback, json -from colorama import Fore, Style + from slsdet import Detector, detectorType, detectorSettings from slsdet.defines import DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO SERVER_START_PORTNO=1900 +from utils_for_test import Log, LogLevel -colorama.init(autoreset=True) - -def Log(color, message, stream=sys.stdout): - print(f"{color}{message}{Style.RESET_ALL}", file=stream, flush=True) class RuntimeException (Exception): def __init__ (self, message): - super().__init__(Log(Fore.RED, message)) + super().__init__(Log(LogLevel.ERROR, message)) def checkIfProcessRunning(processName): cmd = f"pgrep -f {processName}" @@ -31,25 +28,24 @@ def checkIfProcessRunning(processName): def killProcess(name, fp): pids = checkIfProcessRunning(name) if pids: - Log(Fore.WHITE, f"Killing '{name}' processes with PIDs: {', '.join(pids)}", fp) + Log(LogLevel.INFO, f"Killing '{name}' processes with PIDs: {', '.join(pids)}", fp) for pid in pids: try: p = subprocess.run(['kill', pid]) if p.returncode != 0 and bool(checkIfProcessRunning(name)): raise RuntimeException(f"Could not kill {name} with pid {pid}") except Exception as e: - Log(Fore.RED, f"Failed to kill process {name} pid:{pid}. Exception occured: [code:{e}, msg:{e.stderr}]") - raise + raise RuntimeException(f"Failed to kill process {name} pid:{pid}. Exception occured: [code:{e}, msg:{e.stderr}]") #else: - # Log(Fore.WHITE, 'process not running : ' + name) + # Log(LogLevel.INFO, 'process not running : ' + name) def cleanup(fp): ''' kill both servers, receivers and clean shared memory ''' - Log(Fore.WHITE, 'Cleaning up') - Log(Fore.WHITE, 'Cleaning up', fp) + Log(LogLevel.INFO, 'Cleaning up') + Log(LogLevel.INFO, 'Cleaning up', fp) killProcess('DetectorServer_virtual', fp) killProcess('slsReceiver', fp) killProcess('slsMultiReceiver', fp) @@ -58,43 +54,40 @@ def cleanup(fp): cleanSharedmemory(fp) def cleanSharedmemory(fp): - Log(Fore.WHITE, 'Cleaning up shared memory...', fp) + Log(LogLevel.INFO, 'Cleaning up shared memory...', fp) try: p = subprocess.run(['sls_detector_get', 'free'], stdout=fp, stderr=fp) except: - Log(Fore.RED, 'Could not free shared memory') - raise + raise RuntimeException('Could not free shared memory') def startProcessInBackground(name, fp): try: # in background and dont print output p = subprocess.Popen(shlex.split(name), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) - Log(Fore.WHITE, 'Starting up ' + name + ' ...', fp) + Log(LogLevel.INFO, 'Starting up ' + name + ' ...', fp) except Exception as e: - Log(Fore.RED, f'Could not start {name}:{e}') - raise + raise RuntimeException(f'Could not start {name}:{e}') def startServers(name, num_mods): - Log(Fore.WHITE, 'Starting server') + Log(LogLevel.INFO, 'Starting server') for i in range(num_mods): port_no = SERVER_START_PORTNO + (i * 2) startProcessInBackground(name + 'DetectorServer_virtual -p' + str(port_no), fp) time.sleep(6) def startFrameSynchronizerPullSocket(fname, fp): - Log(Fore.WHITE, 'Starting sync pull socket') - Log(Fore.WHITE, f"Starting up Synchronizer pull socket. Log: {fname}", fp) - Log(Fore.WHITE, f"Synchronizer pull socket log: {fname}") + Log(LogLevel.INFO, 'Starting sync pull socket') + Log(LogLevel.INFO, f"Starting up Synchronizer pull socket. Log: {fname}", fp) + Log(LogLevel.INFO, f"Synchronizer pull socket log: {fname}") cmd = ['python', '-u', 'frameSynchronizerPullSocket.py'] try: with open(fname, 'w') as fp: subprocess.Popen(cmd, stdout=fp, stderr=fp, text=True) except Exception as e: - Log(Fore.RED, f"failed to start synchronizer pull socket: {e}") - raise + raise RuntimeException(f"failed to start synchronizer pull socket: {e}") def startFrameSynchronizer(num_mods, fp): - Log(Fore.WHITE, 'Starting frame synchronizer') + Log(LogLevel.INFO, 'Starting frame synchronizer') # in 10.0.0 #startProcessInBackground('slsFrameSynchronizer -n ' + str(num_mods) + ' -p ' + str(DEFAULT_TCP_RX_PORTNO)) startProcessInBackground('slsFrameSynchronizer ' + str(DEFAULT_TCP_RX_PORTNO) + ' ' + str(num_mods), fp) @@ -102,8 +95,8 @@ def startFrameSynchronizer(num_mods, fp): time.sleep(tStartup) def loadConfig(name, num_mods, rx_hostname, settingsdir, num_frames, fp): - Log(Fore.WHITE, 'Loading config') - Log(Fore.WHITE, 'Loading config', fp) + Log(LogLevel.INFO, 'Loading config') + Log(LogLevel.INFO, 'Loading config', fp) try: d = Detector() d.virtual = [num_mods, SERVER_START_PORTNO] @@ -125,8 +118,7 @@ def loadConfig(name, num_mods, rx_hostname, settingsdir, num_frames, fp): d.frames = num_frames except Exception as e: - Log(Fore.RED, f'Could not load config for {name}. Error: {str(e)}') - raise + raise RuntimeException(f'Could not load config for {name}. Error: {str(e)}') def validate_htype_counts(log_path, num_mods, num_ports_per_module, num_frames): htype_counts = { @@ -152,12 +144,11 @@ def validate_htype_counts(log_path, num_mods, num_ports_per_module, num_frames): for htype, expected_count in [("header", num_mods), ("series_end", num_mods), ("module", num_ports_per_module * num_mods * num_frames)]: if htype_counts[htype] != expected_count: msg = f"Expected {expected_count} '{htype}' entries, found {htype_counts[htype]}" - Log(Fore.RED, msg) - raise RuntimeError(msg) + raise RuntimeException(msg) def startTests(name, num_mods, num_frames, fp, file_pull_socket): - Log(Fore.WHITE, 'Tests for ' + name) - Log(Fore.WHITE, 'Tests for ' + name, fp) + Log(LogLevel.INFO, 'Tests for ' + name) + Log(LogLevel.INFO, 'Tests for ' + name, fp) cmd = 'tests --abort [.cmdcall] -s -o ' + fname d = Detector() @@ -167,11 +158,10 @@ def startTests(name, num_mods, num_frames, fp, file_pull_socket): d.acquire() fnum = d.rx_framescaught[0] if fnum != num_frames: - Log(Fore.RED, f"{name} caught only {fnum}. Expected {num_frames}") - raise + raise RuntimeException(f"{name} caught only {fnum}. Expected {num_frames}") validate_htype_counts(file_pull_socket, num_mods, num_ports_per_module, num_frames) - Log(Fore.GREEN, f"Log file htype checks passed for {name}", fp) + Log(LogLevel.INFOGREEN, f"Log file htype checks passed for {name}", fp) # parse cmd line for rx_hostname and settingspath using the argparse library @@ -197,7 +187,7 @@ else: servers = args.servers -Log(Fore.WHITE, 'Arguments:\nrx_hostname: ' + args.rx_hostname + '\nsettingspath: \'' + args.settingspath + '\nservers: \'' + ' '.join(servers) + '\nnum_mods: \'' + str(args.num_mods) + '\nnum_frames: \'' + str(args.num_frames) + '\'') +Log(LogLevel.INFO, 'Arguments:\nrx_hostname: ' + args.rx_hostname + '\nsettingspath: \'' + args.settingspath + '\nservers: \'' + ' '.join(servers) + '\nnum_mods: \'' + str(args.num_mods) + '\nnum_frames: \'' + str(args.num_frames) + '\'') # redirect to file @@ -205,7 +195,7 @@ prefix_fname = '/tmp/slsFrameSynchronizer_test' original_stdout = sys.stdout original_stderr = sys.stderr fname = prefix_fname + '_log.txt' -Log(Fore.BLUE, '\nLog File: ' + fname) +Log(LogLevel.INFOBLUE, '\nLog File: ' + fname) with open(fname, 'w') as fp: @@ -213,8 +203,8 @@ with open(fname, 'w') as fp: testError = False for server in servers: try: - Log(Fore.BLUE, '\nSynchonizer tests for ' + server, fp) - Log(Fore.BLUE, '\nSynchonizer tests for ' + server) + Log(LogLevel.INFOBLUE, '\nSynchonizer tests for ' + server, fp) + Log(LogLevel.INFOBLUE, '\nSynchonizer tests for ' + server) # cmd tests for det cleanup(fp) @@ -230,7 +220,7 @@ with open(fname, 'w') as fp: # redirect to terminal sys.stdout = original_stdout sys.stderr = original_stderr - Log(Fore.RED, f'Exception caught while testing {server}. Cleaning up...') + Log(LogLevel.INFORED, f'Exception caught while testing {server}. Cleaning up...') with open(fname, 'a') as fp_error: traceback.print_exc(file=fp_error) # This will log the full traceback @@ -242,13 +232,13 @@ with open(fname, 'w') as fp: sys.stdout = original_stdout sys.stderr = original_stderr if not testError: - Log(Fore.GREEN, 'Passed all sync tests\n' + str(servers)) + Log(LogLevel.INFOGREEN, 'Passed all sync tests\n' + str(servers)) except Exception as e: # redirect to terminal sys.stdout = original_stdout sys.stderr = original_stderr - Log(Fore.RED, f'Exception caught with general testing. Cleaning up...') + Log(LogLevel.INFORED, f'Exception caught with general testing. Cleaning up...') cleanup(fp) \ No newline at end of file diff --git a/tests/scripts/test_simulators.py b/tests/scripts/test_simulators.py index 6c59c50c0..480418f3c 100644 --- a/tests/scripts/test_simulators.py +++ b/tests/scripts/test_simulators.py @@ -4,22 +4,18 @@ This file is used to start up simulators, receivers and run all the tests on them and finally kill the simulators and receivers. ''' import argparse -import os, sys, subprocess, time, colorama +import os, sys, subprocess, time -from colorama import Fore, Style from slsdet import Detector, detectorType, detectorSettings from slsdet.defines import DEFAULT_TCP_CNTRL_PORTNO, DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO HALFMOD2_TCP_CNTRL_PORTNO=1955 HALFMOD2_TCP_RX_PORTNO=1957 -colorama.init(autoreset=True) - -def Log(color, message): - print(f"{color}{message}{Style.RESET_ALL}", flush=True) +from utils_for_test import Log, LogLevel class RuntimeException (Exception): def __init__ (self, message): - super().__init__(Log(Fore.RED, message)) + super().__init__(Log(LogLevel.INFORED, message)) def checkIfProcessRunning(processName): cmd = f"pgrep -f {processName}" @@ -30,45 +26,42 @@ def checkIfProcessRunning(processName): def killProcess(name): pids = checkIfProcessRunning(name) if pids: - Log(Fore.GREEN, f"Killing '{name}' processes with PIDs: {', '.join(pids)}") + Log(LogLevel.INFOGREEN, f"Killing '{name}' processes with PIDs: {', '.join(pids)}") for pid in pids: try: p = subprocess.run(['kill', pid]) if p.returncode != 0 and bool(checkIfProcessRunning(name)): raise RuntimeException(f"Could not kill {name} with pid {pid}") except Exception as e: - Log(Fore.RED, f"Failed to kill process {name} pid:{pid}. Exception occured: [code:{e}, msg:{e.stderr}]") - raise + raise RuntimeException(f"Failed to kill process {name} pid:{pid}. Exception occured: [code:{e}, msg:{e.stderr}]") #else: - # Log(Fore.WHITE, 'process not running : ' + name) + # Log(LogLevel.INFO, 'process not running : ' + name) def cleanup(fp): ''' kill both servers, receivers and clean shared memory ''' - Log(Fore.GREEN, 'Cleaning up...') + Log(LogLevel.INFOGREEN, 'Cleaning up...') killProcess('DetectorServer_virtual') killProcess('slsReceiver') killProcess('slsMultiReceiver') cleanSharedmemory(fp) def cleanSharedmemory(fp): - Log(Fore.GREEN, 'Cleaning up shared memory...') + Log(LogLevel.INFOGREEN, 'Cleaning up shared memory...') try: p = subprocess.run(['sls_detector_get', 'free'], stdout=fp, stderr=fp) except: - Log(Fore.RED, 'Could not free shared memory') - raise + raise RuntimeException('Could not free shared memory') def startProcessInBackground(name): try: # in background and dont print output p = subprocess.Popen(name.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) - Log(Fore.GREEN, 'Starting up ' + name + ' ...') + Log(LogLevel.INFOGREEN, 'Starting up ' + name + ' ...') except Exception as e: - Log(Fore.RED, f'Could not start {name}:{e}') - raise + raise RuntimeException(f'Could not start {name}:{e}') def startServer(name): @@ -77,7 +70,7 @@ def startServer(name): if name == 'eiger': startProcessInBackground(name + 'DetectorServer_virtual -p' + str(HALFMOD2_TCP_CNTRL_PORTNO)) tStartup = 6 - Log(Fore.WHITE, 'Takes ' + str(tStartup) + ' seconds... Please be patient') + Log(LogLevel.INFO, 'Takes ' + str(tStartup) + ' seconds... Please be patient') time.sleep(tStartup) def startReceiver(name): @@ -88,7 +81,7 @@ def startReceiver(name): time.sleep(2) def loadConfig(name, rx_hostname, settingsdir): - Log(Fore.GREEN, 'Loading config') + Log(LogLevel.INFOGREEN, 'Loading config') try: d = Detector() if name == 'eiger': @@ -116,11 +109,10 @@ def loadConfig(name, rx_hostname, settingsdir): if d.type == detectorType.XILINX_CHIPTESTBOARD: d.configureTransceiver() except: - Log(Fore.RED, 'Could not load config for ' + name) - raise + raise RuntimeException('Could not load config for ' + name) def startCmdTests(name, fp, fname): - Log(Fore.GREEN, 'Cmd Tests for ' + name) + Log(LogLevel.INFOGREEN, 'Cmd Tests for ' + name) cmd = 'tests --abort [.cmdcall] -s -o ' + fname try: subprocess.run(cmd.split(), stdout=fp, stderr=fp, check=True, text=True) @@ -132,15 +124,14 @@ def startCmdTests(name, fp, fname): if "FAILED" in line: msg = 'Cmd tests failed for ' + name + '!!!' sys.stdout = original_stdout - Log(Fore.RED, msg) - Log(Fore.RED, line) + Log(LogLevel.ERROR, f"{msg}\n{line}") sys.stdout = fp raise Exception(msg) - Log(Fore.GREEN, 'Cmd Tests successful for ' + name) + Log(LogLevel.INFOGREEN, 'Cmd Tests successful for ' + name) def startGeneralTests(fp, fname): - Log(Fore.GREEN, 'General Tests') + Log(LogLevel.INFOGREEN, 'General Tests') cmd = 'tests --abort -s -o ' + fname try: subprocess.run(cmd.split(), stdout=fp, stderr=fp, check=True, text=True) @@ -152,11 +143,11 @@ def startGeneralTests(fp, fname): if "FAILED" in line: msg = 'General tests failed !!!' sys.stdout = original_stdout - Log(Fore.RED, msg + '\n' + line) + Log(LogLevel.ERROR, msg + '\n' + line) sys.stdout = fp raise Exception(msg) - Log(Fore.GREEN, 'General Tests successful') + Log(LogLevel.INFOGREEN, 'General Tests successful') @@ -182,7 +173,7 @@ else: servers = args.servers -Log(Fore.WHITE, 'Arguments:\nrx_hostname: ' + args.rx_hostname + '\nsettingspath: \'' + args.settingspath + '\nservers: \'' + ' '.join(servers) + '\'') +Log(LogLevel.INFO, 'Arguments:\nrx_hostname: ' + args.rx_hostname + '\nsettingspath: \'' + args.settingspath + '\nservers: \'' + ' '.join(servers) + '\'') # redirect to file @@ -190,7 +181,7 @@ prefix_fname = '/tmp/slsDetectorPackage_virtual_test' original_stdout = sys.stdout original_stderr = sys.stderr fname = prefix_fname + '_log.txt' -Log(Fore.BLUE, '\nLog File: ' + fname) +Log(LogLevel.INFOBLUE, '\nLog File: ' + fname) with open(fname, 'w') as fp: @@ -198,10 +189,10 @@ with open(fname, 'w') as fp: # general tests file_results = prefix_fname + '_results_general.txt' - Log(Fore.BLUE, 'General tests (results: ' + file_results + ')') + Log(LogLevel.INFOBLUE, 'General tests (results: ' + file_results + ')') sys.stdout = fp sys.stderr = fp - Log(Fore.BLUE, 'General tests (results: ' + file_results + ')') + Log(LogLevel.INFOBLUE, 'General tests (results: ' + file_results + ')') try: cleanup(fp) @@ -215,10 +206,10 @@ with open(fname, 'w') as fp: sys.stdout = original_stdout sys.stderr = original_stderr file_results = prefix_fname + '_results_cmd_' + server + '.txt' - Log(Fore.BLUE, 'Cmd tests for ' + server + ' (results: ' + file_results + ')') + Log(LogLevel.INFOBLUE, 'Cmd tests for ' + server + ' (results: ' + file_results + ')') sys.stdout = fp sys.stderr = fp - Log(Fore.BLUE, 'Cmd tests for ' + server + ' (results: ' + file_results + ')') + Log(LogLevel.INFOBLUE, 'Cmd tests for ' + server + ' (results: ' + file_results + ')') # cmd tests for det cleanup(fp) @@ -232,7 +223,7 @@ with open(fname, 'w') as fp: # redirect to terminal sys.stdout = original_stdout sys.stderr = original_stderr - Log(Fore.RED, f'Exception caught while testing {server}. Cleaning up...') + Log(LogLevel.INFORED, f'Exception caught while testing {server}. Cleaning up...') testError = True break @@ -240,13 +231,13 @@ with open(fname, 'w') as fp: sys.stdout = original_stdout sys.stderr = original_stderr if not testError: - Log(Fore.GREEN, 'Passed all tests for virtual detectors \n' + str(servers)) + Log(LogLevel.INFOGREEN, 'Passed all tests for virtual detectors \n' + str(servers)) except Exception as e: # redirect to terminal sys.stdout = original_stdout sys.stderr = original_stderr - Log(Fore.RED, f'Exception caught with general testing. Cleaning up...') + Log(LogLevel.INFORED, f'Exception caught with general testing. Cleaning up...') cleanSharedmemory(sys.stdout) \ No newline at end of file diff --git a/tests/scripts/utils_for_test.py b/tests/scripts/utils_for_test.py new file mode 100644 index 000000000..5b59994c7 --- /dev/null +++ b/tests/scripts/utils_for_test.py @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: LGPL-3.0-or-other +# Copyright (C) 2021 Contributors to the SLS Detector Package +''' +This file is used for common utils used for integration tests between simulators and receivers. +''' + +import sys +from enum import Enum +from colorama import Fore, Style, init + +init(autoreset=True) + +class LogLevel(Enum): + INFO = 0 + INFORED = 1 + INFOGREEN = 2 + INFOBLUE = 3 + WARNING = 4 + ERROR = 5 + DEBUG = 6 + +LOG_LABELS = { + LogLevel.WARNING: "WARNING: ", + LogLevel.ERROR: "ERROR: ", + LogLevel.DEBUG: "DEBUG: " +} + +LOG_COLORS = { + LogLevel.INFO: Fore.WHITE, + LogLevel.INFORED: Fore.RED, + LogLevel.INFOGREEN: Fore.GREEN, + LogLevel.INFOBLUE: Fore.BLUE, + LogLevel.WARNING: Fore.YELLOW, + LogLevel.ERROR: Fore.RED, + LogLevel.DEBUG: Fore.CYAN +} + +def Log(level: LogLevel, message: str, stream=sys.stdout): + color = LOG_COLORS.get(level, Fore.WHITE) + label = LOG_LABELS.get(level, "") + print(f"{color}{label}{message}{Style.RESET_ALL}", file=stream, flush=True) \ No newline at end of file From 684f511bf811fec06f53ab7930155bfadf630a7a Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Tue, 6 May 2025 17:38:07 +0200 Subject: [PATCH 15/33] copy in make --- tests/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7f717667d..4811f1798 100755 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -61,3 +61,4 @@ catch_discover_tests(tests) configure_file(scripts/test_simulators.py ${CMAKE_BINARY_DIR}/bin/test_simulators.py COPYONLY) configure_file(scripts/test_frame_synchronizer.py ${CMAKE_BINARY_DIR}/bin/test_frame_synchronizer.py COPYONLY) +configure_file(scripts/utils_for_test.py ${CMAKE_BINARY_DIR}/bin/utils_for_test.py COPYONLY) From 03eded30f9fe0d58a2e933f1c9c8931728320759 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 7 May 2025 13:57:12 +0200 Subject: [PATCH 16/33] refactored test_simulator and test_frame_synchronizer tests --- tests/scripts/test_frame_synchronizer.py | 329 ++++++++--------------- tests/scripts/test_simulators.py | 300 +++++---------------- tests/scripts/utils_for_test.py | 208 +++++++++++++- 3 files changed, 391 insertions(+), 446 deletions(-) diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py index cbb37e01e..b8468070c 100644 --- a/tests/scripts/test_frame_synchronizer.py +++ b/tests/scripts/test_frame_synchronizer.py @@ -3,242 +3,137 @@ ''' This file is used to start up simulators, frame synchronizer, pull sockets, acquire, test and kill them finally. ''' -import argparse -import os, sys, subprocess, time -import shlex, traceback, json + +import sys, time +import traceback, json + +from slsdet import Detector +from slsdet.defines import DEFAULT_TCP_RX_PORTNO + +from utils_for_test import ( + Log, + LogLevel, + RuntimeException, + checkIfProcessRunning, + killProcess, + cleanup, + cleanSharedmemory, + startProcessInBackground, + startProcessInBackgroundWithLogFile, + startDetectorVirtualServer, + loadConfig, + ParseArguments +) + +LOG_PREFIX_FNAME = '/tmp/slsFrameSynchronizer_test' +MAIN_LOG_FNAME = LOG_PREFIX_FNAME + '_log.txt' +PULL_SOCKET_PREFIX_FNAME = LOG_PREFIX_FNAME + '_pull_socket_' -from slsdet import Detector, detectorType, detectorSettings -from slsdet.defines import DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO -SERVER_START_PORTNO=1900 - -from utils_for_test import Log, LogLevel - - -class RuntimeException (Exception): - def __init__ (self, message): - super().__init__(Log(LogLevel.ERROR, message)) - -def checkIfProcessRunning(processName): - cmd = f"pgrep -f {processName}" - res = subprocess.getoutput(cmd) - return res.strip().splitlines() - - -def killProcess(name, fp): - pids = checkIfProcessRunning(name) - if pids: - Log(LogLevel.INFO, f"Killing '{name}' processes with PIDs: {', '.join(pids)}", fp) - for pid in pids: - try: - p = subprocess.run(['kill', pid]) - if p.returncode != 0 and bool(checkIfProcessRunning(name)): - raise RuntimeException(f"Could not kill {name} with pid {pid}") - except Exception as e: - raise RuntimeException(f"Failed to kill process {name} pid:{pid}. Exception occured: [code:{e}, msg:{e.stderr}]") - #else: - # Log(LogLevel.INFO, 'process not running : ' + name) - - -def cleanup(fp): - ''' - kill both servers, receivers and clean shared memory - ''' - Log(LogLevel.INFO, 'Cleaning up') - Log(LogLevel.INFO, 'Cleaning up', fp) - killProcess('DetectorServer_virtual', fp) - killProcess('slsReceiver', fp) - killProcess('slsMultiReceiver', fp) - killProcess('slsFrameSynchronizer', fp) - killProcess('frameSynchronizerPullSocket', fp) - cleanSharedmemory(fp) - -def cleanSharedmemory(fp): - Log(LogLevel.INFO, 'Cleaning up shared memory...', fp) - try: - p = subprocess.run(['sls_detector_get', 'free'], stdout=fp, stderr=fp) - except: - raise RuntimeException('Could not free shared memory') - -def startProcessInBackground(name, fp): - try: - # in background and dont print output - p = subprocess.Popen(shlex.split(name), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) - Log(LogLevel.INFO, 'Starting up ' + name + ' ...', fp) - except Exception as e: - raise RuntimeException(f'Could not start {name}:{e}') - -def startServers(name, num_mods): - Log(LogLevel.INFO, 'Starting server') - for i in range(num_mods): - port_no = SERVER_START_PORTNO + (i * 2) - startProcessInBackground(name + 'DetectorServer_virtual -p' + str(port_no), fp) - time.sleep(6) - -def startFrameSynchronizerPullSocket(fname, fp): - Log(LogLevel.INFO, 'Starting sync pull socket') - Log(LogLevel.INFO, f"Starting up Synchronizer pull socket. Log: {fname}", fp) - Log(LogLevel.INFO, f"Synchronizer pull socket log: {fname}") +def startFrameSynchronizerPullSocket(name, fp): + fname = PULL_SOCKET_PREFIX_FNAME + name + '.txt' cmd = ['python', '-u', 'frameSynchronizerPullSocket.py'] - try: - with open(fname, 'w') as fp: - subprocess.Popen(cmd, stdout=fp, stderr=fp, text=True) - except Exception as e: - raise RuntimeException(f"failed to start synchronizer pull socket: {e}") + startProcessInBackgroundWithLogFile(cmd, fp, fname) + def startFrameSynchronizer(num_mods, fp): - Log(LogLevel.INFO, 'Starting frame synchronizer') + cmd = ['slsFrameSynchronizer', str(DEFAULT_TCP_RX_PORTNO), str(num_mods)] # in 10.0.0 - #startProcessInBackground('slsFrameSynchronizer -n ' + str(num_mods) + ' -p ' + str(DEFAULT_TCP_RX_PORTNO)) - startProcessInBackground('slsFrameSynchronizer ' + str(DEFAULT_TCP_RX_PORTNO) + ' ' + str(num_mods), fp) - tStartup = 1 * num_mods - time.sleep(tStartup) + #cmd = ['slsFrameSynchronizer', '-p', str(DEFAULT_TCP_RX_PORTNO), '-n', str(num_mods)] + startProcessInBackground(cmd, fp) + time.sleep(1) -def loadConfig(name, num_mods, rx_hostname, settingsdir, num_frames, fp): - Log(LogLevel.INFO, 'Loading config') - Log(LogLevel.INFO, 'Loading config', fp) - try: - d = Detector() - d.virtual = [num_mods, SERVER_START_PORTNO] - d.udp_dstport = DEFAULT_UDP_DST_PORTNO - if name == 'eiger': - d.udp_dstport2 = DEFAULT_UDP_DST_PORTNO + 1 +def acquire(): + Log(LogLevel.INFO, 'Acquiring') + Log(LogLevel.INFO, 'Acquiring', fp) + d = Detector() + d.acquire() - d.rx_hostname = rx_hostname - d.udp_dstip = 'auto' - if name != "eiger": - d.udp_srcip = 'auto' - if name == "jungfrau" or name == "moench" or name == "xilinx_ctb": - d.powerchip = 1 +def testFramesCaught(name, num_frames): + d = Detector() + fnum = d.rx_framescaught[0] + if fnum != num_frames: + raise RuntimeException(f"{name} caught only {fnum}. Expected {num_frames}") + + Log(LogLevel.INFOGREEN, f'Frames caught test passed for {name}') + Log(LogLevel.INFOGREEN, f'Frames caught test passed for {name}', fp) - if d.type == detectorType.XILINX_CHIPTESTBOARD: - d.configureTransceiver() - d.frames = num_frames - except Exception as e: - raise RuntimeException(f'Could not load config for {name}. Error: {str(e)}') +def testZmqHeadetTypeCount(name, num_mods, num_frames, fp): -def validate_htype_counts(log_path, num_mods, num_ports_per_module, num_frames): + Log(LogLevel.INFO, f"Testing Zmq Header type count for {name}") + Log(LogLevel.INFO, f"Testing Zmq Header type count for {name}", fp) htype_counts = { "header": 0, "series_end": 0, "module": 0 } - # get a count of each htype from file - with open(log_path, 'r') as f: - for line in f: - line = line.strip() - if not line or not line.startswith('{'): - continue - try: - data = json.loads(line) - htype = data.get("htype") - if htype in htype_counts: - htype_counts[htype] += 1 - except json.JSONDecodeError: - continue # or log malformed line - - for htype, expected_count in [("header", num_mods), ("series_end", num_mods), ("module", num_ports_per_module * num_mods * num_frames)]: - if htype_counts[htype] != expected_count: - msg = f"Expected {expected_count} '{htype}' entries, found {htype_counts[htype]}" - raise RuntimeException(msg) - -def startTests(name, num_mods, num_frames, fp, file_pull_socket): - Log(LogLevel.INFO, 'Tests for ' + name) - Log(LogLevel.INFO, 'Tests for ' + name, fp) - cmd = 'tests --abort [.cmdcall] -s -o ' + fname - - d = Detector() - num_ports_per_module = d.numinterfaces - if name == "gotthard2": - num_ports_per_module = 1 - d.acquire() - fnum = d.rx_framescaught[0] - if fnum != num_frames: - raise RuntimeException(f"{name} caught only {fnum}. Expected {num_frames}") - - validate_htype_counts(file_pull_socket, num_mods, num_ports_per_module, num_frames) - Log(LogLevel.INFOGREEN, f"Log file htype checks passed for {name}", fp) - - -# parse cmd line for rx_hostname and settingspath using the argparse library -parser = argparse.ArgumentParser(description = 'automated tests with the virtual detector servers') -parser.add_argument('rx_hostname', nargs='?', default='localhost', help = 'hostname/ip of the current machine') -parser.add_argument('settingspath', nargs='?', default='../../settingsdir', help = 'Relative or absolut path to the settingspath') -parser.add_argument('-n', '--num-mods', nargs='?', default=2, type=int, help = 'Number of modules to test with') -parser.add_argument('-f', '--num-frames', nargs='?', default=1, type=int, help = 'Number of frames to test with') -parser.add_argument('-s', '--servers', nargs='*', help='Detector servers to run') -args = parser.parse_args() - -if args.servers is None: - servers = [ - 'eiger', - 'jungfrau', - 'mythen3', - 'gotthard2', - 'ctb', - 'moench', - 'xilinx_ctb' - ] -else: - servers = args.servers - - -Log(LogLevel.INFO, 'Arguments:\nrx_hostname: ' + args.rx_hostname + '\nsettingspath: \'' + args.settingspath + '\nservers: \'' + ' '.join(servers) + '\nnum_mods: \'' + str(args.num_mods) + '\nnum_frames: \'' + str(args.num_frames) + '\'') - - -# redirect to file -prefix_fname = '/tmp/slsFrameSynchronizer_test' -original_stdout = sys.stdout -original_stderr = sys.stderr -fname = prefix_fname + '_log.txt' -Log(LogLevel.INFOBLUE, '\nLog File: ' + fname) - -with open(fname, 'w') as fp: - try: - testError = False - for server in servers: - try: - Log(LogLevel.INFOBLUE, '\nSynchonizer tests for ' + server, fp) - Log(LogLevel.INFOBLUE, '\nSynchonizer tests for ' + server) - - # cmd tests for det - cleanup(fp) - startServers(server, args.num_mods) - file_pull_socket = prefix_fname + '_pull_socket_' + server + '.txt' - startFrameSynchronizerPullSocket(file_pull_socket, fp) - startFrameSynchronizer(args.num_mods, fp) - loadConfig(server, args.num_mods, args.rx_hostname, args.settingspath, args.num_frames, fp) - startTests(server, args.num_mods, args.num_frames, fp, file_pull_socket) - cleanup(fp) - - except Exception as e: - # redirect to terminal - sys.stdout = original_stdout - sys.stderr = original_stderr - Log(LogLevel.INFORED, f'Exception caught while testing {server}. Cleaning up...') - with open(fname, 'a') as fp_error: - traceback.print_exc(file=fp_error) # This will log the full traceback - - testError = True - cleanup(fp) - break - - # redirect to terminal - sys.stdout = original_stdout - sys.stderr = original_stderr - if not testError: - Log(LogLevel.INFOGREEN, 'Passed all sync tests\n' + str(servers)) - + # get a count of each htype from file + pull_socket_fname = PULL_SOCKET_PREFIX_FNAME + name + '.txt' + with open(pull_socket_fname, 'r') as log_fp: + for line in log_fp: + line = line.strip() + if not line or not line.startswith('{'): + continue + try: + data = json.loads(line) + htype = data.get("htype") + if htype in htype_counts: + htype_counts[htype] += 1 + except json.JSONDecodeError: + continue + # test if file contents matches expected counts + d = Detector() + num_ports_per_module = 1 if name == "gotthard2" else d.numinterfaces + total_num_frame_parts = num_ports_per_module * num_mods * num_frames + for htype, expected_count in [("header", num_mods), ("series_end", num_mods), ("module", total_num_frame_parts)]: + if htype_counts[htype] != expected_count: + msg = f"Expected {expected_count} '{htype}' entries, found {htype_counts[htype]}" + raise RuntimeException(msg) except Exception as e: - # redirect to terminal - sys.stdout = original_stdout - sys.stderr = original_stderr - Log(LogLevel.INFORED, f'Exception caught with general testing. Cleaning up...') - cleanup(fp) - \ No newline at end of file + raise RuntimeException(f'Failed to get zmq header count type. Error:{e}') + + Log(LogLevel.INFOGREEN, f"Zmq Header type count test passed for {name}") + Log(LogLevel.INFOGREEN, f"Zmq Header type count test passed for {name}", fp) + + +def startTestsForAll(args, fp): + for server in args.servers: + try: + Log(LogLevel.INFOBLUE, f'Synchronizer Tests for {server}') + Log(LogLevel.INFOBLUE, f'Synchronizer Tests for {server}', fp) + cleanup(fp) + startDetectorVirtualServer(server, args.num_mods, fp) + startFrameSynchronizerPullSocket(server, fp) + startFrameSynchronizer(args.num_mods, fp) + loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, fp=fp, num_mods=args.num_mods, num_frames=args.num_frames) + acquire() + testFramesCaught(server, args.num_frames) + testZmqHeadetTypeCount(server, args.num_mods, args.num_frames, fp) + except Exception as e: + raise RuntimeException(f'Synchronizer Tests failed') + + Log(LogLevel.INFOGREEN, 'Passed all synchronizer tests for all detectors \n' + str(args.servers)) + + +if __name__ == '__main__': + args = ParseArguments(description='Automated tests to test frame synchronizer', default_num_mods=2) + + Log(LogLevel.INFOBLUE, '\nLog File: ' + MAIN_LOG_FNAME + '\n') + + with open(MAIN_LOG_FNAME, 'w') as fp: + try: + startTestsForAll(args, fp) + cleanup(fp) + except Exception as e: + with open(MAIN_LOG_FNAME, 'a') as fp_error: + traceback.print_exc(file=fp_error) + cleanup(fp) + Log(LogLevel.ERROR, f'Tests Failed.') + + diff --git a/tests/scripts/test_simulators.py b/tests/scripts/test_simulators.py index 480418f3c..fc0a54a34 100644 --- a/tests/scripts/test_simulators.py +++ b/tests/scripts/test_simulators.py @@ -4,240 +4,86 @@ This file is used to start up simulators, receivers and run all the tests on them and finally kill the simulators and receivers. ''' import argparse -import os, sys, subprocess, time +import sys, subprocess, time, traceback -from slsdet import Detector, detectorType, detectorSettings -from slsdet.defines import DEFAULT_TCP_CNTRL_PORTNO, DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO -HALFMOD2_TCP_CNTRL_PORTNO=1955 -HALFMOD2_TCP_RX_PORTNO=1957 +from slsdet import Detector +from slsdet.defines import DEFAULT_TCP_RX_PORTNO -from utils_for_test import Log, LogLevel - -class RuntimeException (Exception): - def __init__ (self, message): - super().__init__(Log(LogLevel.INFORED, message)) - -def checkIfProcessRunning(processName): - cmd = f"pgrep -f {processName}" - res = subprocess.getoutput(cmd) - return res.strip().splitlines() +from utils_for_test import ( + Log, + LogLevel, + RuntimeException, + checkIfProcessRunning, + killProcess, + cleanup, + cleanSharedmemory, + startProcessInBackground, + runProcessWithLogFile, + startDetectorVirtualServer, + loadConfig, + ParseArguments +) -def killProcess(name): - pids = checkIfProcessRunning(name) - if pids: - Log(LogLevel.INFOGREEN, f"Killing '{name}' processes with PIDs: {', '.join(pids)}") - for pid in pids: - try: - p = subprocess.run(['kill', pid]) - if p.returncode != 0 and bool(checkIfProcessRunning(name)): - raise RuntimeException(f"Could not kill {name} with pid {pid}") - except Exception as e: - raise RuntimeException(f"Failed to kill process {name} pid:{pid}. Exception occured: [code:{e}, msg:{e.stderr}]") - #else: - # Log(LogLevel.INFO, 'process not running : ' + name) +LOG_PREFIX_FNAME = '/tmp/slsDetectorPackage_virtual_test' +MAIN_LOG_FNAME = LOG_PREFIX_FNAME + '_log.txt' +GENERAL_TESTS_LOG_FNAME = LOG_PREFIX_FNAME + '_results_general.txt' +CMD_TEST_LOG_PREFIX_FNAME = LOG_PREFIX_FNAME + '_results_cmd_' -def cleanup(fp): - ''' - kill both servers, receivers and clean shared memory - ''' - Log(LogLevel.INFOGREEN, 'Cleaning up...') - killProcess('DetectorServer_virtual') - killProcess('slsReceiver') - killProcess('slsMultiReceiver') - cleanSharedmemory(fp) - -def cleanSharedmemory(fp): - Log(LogLevel.INFOGREEN, 'Cleaning up shared memory...') - try: - p = subprocess.run(['sls_detector_get', 'free'], stdout=fp, stderr=fp) - except: - raise RuntimeException('Could not free shared memory') - -def startProcessInBackground(name): - try: - # in background and dont print output - p = subprocess.Popen(name.split(), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) - Log(LogLevel.INFOGREEN, 'Starting up ' + name + ' ...') - except Exception as e: - raise RuntimeException(f'Could not start {name}:{e}') - -def startServer(name): - - startProcessInBackground(name + 'DetectorServer_virtual') - # second half - if name == 'eiger': - startProcessInBackground(name + 'DetectorServer_virtual -p' + str(HALFMOD2_TCP_CNTRL_PORTNO)) - tStartup = 6 - Log(LogLevel.INFO, 'Takes ' + str(tStartup) + ' seconds... Please be patient') - time.sleep(tStartup) - -def startReceiver(name): - startProcessInBackground('slsReceiver') - # second half - if name == 'eiger': - startProcessInBackground('slsReceiver -t' + str(HALFMOD2_TCP_RX_PORTNO)) - time.sleep(2) - -def loadConfig(name, rx_hostname, settingsdir): - Log(LogLevel.INFOGREEN, 'Loading config') - try: - d = Detector() - if name == 'eiger': - d.hostname = 'localhost:' + str(DEFAULT_TCP_CNTRL_PORTNO) + '+localhost:' + str(HALFMOD2_TCP_CNTRL_PORTNO) - #d.udp_dstport = {2: 50003} - # will set up for every module - d.udp_dstport = DEFAULT_UDP_DST_PORTNO - d.udp_dstport2 = DEFAULT_UDP_DST_PORTNO + 1 - d.rx_hostname = rx_hostname + ':' + str(DEFAULT_TCP_RX_PORTNO) + '+' + rx_hostname + ':' + str(HALFMOD2_TCP_RX_PORTNO) - d.udp_dstip = 'auto' - d.trimen = [4500, 5400, 6400] - d.settingspath = settingsdir + '/eiger/' - d.setThresholdEnergy(4500, detectorSettings.STANDARD) - else: - d.hostname = 'localhost' - d.rx_hostname = rx_hostname - d.udp_dstip = 'auto' - d.udp_dstip = 'auto' - if d.type == detectorType.GOTTHARD: - d.udp_srcip = d.udp_dstip - else: - d.udp_srcip = 'auto' - if d.type == detectorType.JUNGFRAU or d.type == detectorType.MOENCH or d.type == detectorType.XILINX_CHIPTESTBOARD: - d.powerchip = 1 - if d.type == detectorType.XILINX_CHIPTESTBOARD: - d.configureTransceiver() - except: - raise RuntimeException('Could not load config for ' + name) - -def startCmdTests(name, fp, fname): - Log(LogLevel.INFOGREEN, 'Cmd Tests for ' + name) - cmd = 'tests --abort [.cmdcall] -s -o ' + fname - try: - subprocess.run(cmd.split(), stdout=fp, stderr=fp, check=True, text=True) - except subprocess.CalledProcessError as e: - pass - - with open (fname, 'r') as f: - for line in f: - if "FAILED" in line: - msg = 'Cmd tests failed for ' + name + '!!!' - sys.stdout = original_stdout - Log(LogLevel.ERROR, f"{msg}\n{line}") - sys.stdout = fp - raise Exception(msg) - - Log(LogLevel.INFOGREEN, 'Cmd Tests successful for ' + name) - -def startGeneralTests(fp, fname): - Log(LogLevel.INFOGREEN, 'General Tests') - cmd = 'tests --abort -s -o ' + fname - try: - subprocess.run(cmd.split(), stdout=fp, stderr=fp, check=True, text=True) - except subprocess.CalledProcessError as e: - pass - - with open (fname, 'r') as f: - for line in f: - if "FAILED" in line: - msg = 'General tests failed !!!' - sys.stdout = original_stdout - Log(LogLevel.ERROR, msg + '\n' + line) - sys.stdout = fp - raise Exception(msg) - - Log(LogLevel.INFOGREEN, 'General Tests successful') - - - -# parse cmd line for rx_hostname and settingspath using the argparse library -parser = argparse.ArgumentParser(description = 'automated tests with the virtual detector servers') -parser.add_argument('rx_hostname', nargs='?', default='localhost', help = 'hostname/ip of the current machine') -parser.add_argument('settingspath', nargs='?', default='../../settingsdir', help = 'Relative or absolut path to the settingspath') -parser.add_argument('-s', '--servers', help='Detector servers to run', nargs='*') -args = parser.parse_args() - -if args.servers is None: - servers = [ - 'eiger', - 'jungfrau', - 'mythen3', - 'gotthard2', - 'gotthard', - 'ctb', - 'moench', - 'xilinx_ctb' - ] -else: - servers = args.servers - - -Log(LogLevel.INFO, 'Arguments:\nrx_hostname: ' + args.rx_hostname + '\nsettingspath: \'' + args.settingspath + '\nservers: \'' + ' '.join(servers) + '\'') - - -# redirect to file -prefix_fname = '/tmp/slsDetectorPackage_virtual_test' -original_stdout = sys.stdout -original_stderr = sys.stderr -fname = prefix_fname + '_log.txt' -Log(LogLevel.INFOBLUE, '\nLog File: ' + fname) - -with open(fname, 'w') as fp: - - - - # general tests - file_results = prefix_fname + '_results_general.txt' - Log(LogLevel.INFOBLUE, 'General tests (results: ' + file_results + ')') - sys.stdout = fp - sys.stderr = fp - Log(LogLevel.INFOBLUE, 'General tests (results: ' + file_results + ')') +def startReceiver(num_mods, fp): + if num_mods == 1: + cmd = ['slsReceiver'] + else: + cmd = ['slsMultiReceiver', str(DEFAULT_TCP_RX_PORTNO), str(num_mods)] + # in 10.0.0 + #cmd = ['slsMultiReceiver', '-p', str(DEFAULT_TCP_RX_PORTNO), '-n', str(num_mods)] + startProcessInBackground(cmd, fp) + time.sleep(1) +def startGeneralTests(fp): + fname = GENERAL_TESTS_LOG_FNAME + cmd = ['tests', '--abort', '-s'] try: cleanup(fp) - startGeneralTests(fp, file_results) - cleanup(fp) - - testError = False - for server in servers: - try: - # print to terminal for progress - sys.stdout = original_stdout - sys.stderr = original_stderr - file_results = prefix_fname + '_results_cmd_' + server + '.txt' - Log(LogLevel.INFOBLUE, 'Cmd tests for ' + server + ' (results: ' + file_results + ')') - sys.stdout = fp - sys.stderr = fp - Log(LogLevel.INFOBLUE, 'Cmd tests for ' + server + ' (results: ' + file_results + ')') - - # cmd tests for det - cleanup(fp) - startServer(server) - startReceiver(server) - loadConfig(server, args.rx_hostname, args.settingspath) - startCmdTests(server, fp, file_results) - cleanup(fp) - - except Exception as e: - # redirect to terminal - sys.stdout = original_stdout - sys.stderr = original_stderr - Log(LogLevel.INFORED, f'Exception caught while testing {server}. Cleaning up...') - testError = True - break - - # redirect to terminal - sys.stdout = original_stdout - sys.stderr = original_stderr - if not testError: - Log(LogLevel.INFOGREEN, 'Passed all tests for virtual detectors \n' + str(servers)) - - + runProcessWithLogFile('General Tests', cmd, fp, fname) except Exception as e: - # redirect to terminal - sys.stdout = original_stdout - sys.stderr = original_stderr - Log(LogLevel.INFORED, f'Exception caught with general testing. Cleaning up...') - cleanSharedmemory(sys.stdout) - \ No newline at end of file + raise RuntimeException(f'General tests failed.') from e + + +def startCmdTestsForAll(args, fp): + for server in args.servers: + try: + num_mods = 2 if server == 'eiger' else 1 + fname = CMD_TEST_LOG_PREFIX_FNAME + server + '.txt' + cmd = ['tests', '--abort', '[.cmdcall]', '-s'] + + Log(LogLevel.INFOBLUE, f'Starting Cmd Tests for {server}') + cleanup(fp) + startDetectorVirtualServer(name=server, num_mods=num_mods, fp=fp) + startReceiver(num_mods, fp) + loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, fp=fp, num_mods=num_mods) + runProcessWithLogFile('Cmd Tests for ' + server, cmd, fp, fname) + except Exception as e: + raise RuntimeException(f'Cmd Tests failed for {server}.') + + Log(LogLevel.INFOGREEN, 'Passed all tests for all detectors \n' + str(args.servers)) + + +if __name__ == '__main__': + args = ParseArguments('Automated tests with the virtual detector servers') + if args.num_mods > 1: + raise RuntimeException(f'Cannot support multiple modules at the moment (except Eiger).') + + Log(LogLevel.INFOBLUE, '\nLog File: ' + MAIN_LOG_FNAME + '\n') + + with open(MAIN_LOG_FNAME, 'w') as fp: + try: + startGeneralTests(fp) + startCmdTestsForAll(args, fp) + cleanup(fp) + except Exception as e: + with open(MAIN_LOG_FNAME, 'a') as fp_error: + traceback.print_exc(file=fp_error) + cleanup(fp) + Log(LogLevel.ERROR, f'Tests Failed.') diff --git a/tests/scripts/utils_for_test.py b/tests/scripts/utils_for_test.py index 5b59994c7..2969d5599 100644 --- a/tests/scripts/utils_for_test.py +++ b/tests/scripts/utils_for_test.py @@ -4,12 +4,17 @@ This file is used for common utils used for integration tests between simulators and receivers. ''' -import sys +import sys, subprocess, time, argparse from enum import Enum from colorama import Fore, Style, init +from slsdet import Detector, detectorSettings +from slsdet.defines import DEFAULT_TCP_RX_PORTNO, DEFAULT_UDP_DST_PORTNO +SERVER_START_PORTNO=1900 + init(autoreset=True) + class LogLevel(Enum): INFO = 0 INFORED = 1 @@ -19,12 +24,14 @@ class LogLevel(Enum): ERROR = 5 DEBUG = 6 + LOG_LABELS = { LogLevel.WARNING: "WARNING: ", LogLevel.ERROR: "ERROR: ", LogLevel.DEBUG: "DEBUG: " } + LOG_COLORS = { LogLevel.INFO: Fore.WHITE, LogLevel.INFORED: Fore.RED, @@ -35,7 +42,204 @@ LOG_COLORS = { LogLevel.DEBUG: Fore.CYAN } + def Log(level: LogLevel, message: str, stream=sys.stdout): color = LOG_COLORS.get(level, Fore.WHITE) label = LOG_LABELS.get(level, "") - print(f"{color}{label}{message}{Style.RESET_ALL}", file=stream, flush=True) \ No newline at end of file + print(f"{color}{label}{message}{Style.RESET_ALL}", file=stream, flush=True) + + +class RuntimeException (Exception): + def __init__ (self, message): + Log(LogLevel.ERROR, message) + super().__init__(message) + + +def checkIfProcessRunning(processName): + cmd = f"pgrep -f {processName}" + res = subprocess.getoutput(cmd) + return res.strip().splitlines() + + +def killProcess(name, fp): + pids = checkIfProcessRunning(name) + if pids: + Log(LogLevel.INFO, f"Killing '{name}' processes with PIDs: {', '.join(pids)}", fp) + for pid in pids: + try: + p = subprocess.run(['kill', pid]) + if p.returncode != 0 and bool(checkIfProcessRunning(name)): + raise RuntimeException(f"Could not kill {name} with pid {pid}") + except Exception as e: + raise RuntimeException(f"Failed to kill process {name} pid:{pid}. Exception occured: [code:{e}, msg:{e.stderr}]") + #else: + # Log(LogLevel.INFO, 'process not running : ' + name) + + +def cleanSharedmemory(fp): + Log(LogLevel.INFO, 'Cleaning up shared memory', fp) + try: + p = subprocess.run(['sls_detector_get', 'free'], stdout=fp, stderr=fp) + except: + raise RuntimeException('Could not free shared memory') + + +def cleanup(fp): + Log(LogLevel.INFO, 'Cleaning up') + Log(LogLevel.INFO, 'Cleaning up', fp) + killProcess('DetectorServer_virtual', fp) + killProcess('slsReceiver', fp) + killProcess('slsMultiReceiver', fp) + killProcess('slsFrameSynchronizer', fp) + killProcess('frameSynchronizerPullSocket', fp) + cleanSharedmemory(fp) + + +def startProcessInBackground(cmd, fp): + Log(LogLevel.INFO, 'Starting up ' + ' '.join(cmd)) + Log(LogLevel.INFO, 'Starting up ' + ' '.join(cmd), fp) + try: + p = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) + except Exception as e: + raise RuntimeException(f'Failed to start {cmd}:{e}') + + +def startProcessInBackgroundWithLogFile(cmd, fp, log_file_name): + Log(LogLevel.INFOBLUE, 'Starting up ' + ' '.join(cmd) + '. Log: ' + log_file_name) + Log(LogLevel.INFOBLUE, 'Starting up ' + ' '.join(cmd) + '. Log: ' + log_file_name, fp) + try: + with open(log_file_name, 'w') as log_fp: + subprocess.Popen(cmd, stdout=log_fp, stderr=log_fp, text=True) + except Exception as e: + raise RuntimeException(f'Failed to start {cmd}:{e}') + + +def runProcessWithLogFile(name, cmd, fp, log_file_name): + Log(LogLevel.INFOBLUE, 'Running ' + name + '. Log: ' + log_file_name) + Log(LogLevel.INFOBLUE, 'Running ' + name + '. Log: ' + log_file_name, fp) + Log(LogLevel.INFOBLUE, 'Cmd: ' + ' '.join(cmd), fp) + try: + with open(log_file_name, 'w') as log_fp: + subprocess.run(cmd, stdout=log_fp, stderr=log_fp, check=True, text=True) + except subprocess.CalledProcessError as e: + pass + except Exception as e: + Log(LogLevel.ERROR, f'Failed to run {name}:{e}', fp) + raise RuntimeException(f'Failed to run {name}:{e}') + + with open (log_file_name, 'r') as f: + for line in f: + if "FAILED" in line: + raise RuntimeException(f'{line}') + + Log(LogLevel.INFOGREEN, name + ' successful!\n') + Log(LogLevel.INFOGREEN, name + ' successful!\n', fp) + + +def startDetectorVirtualServer(name :str, num_mods, fp): + for i in range(num_mods): + port_no = SERVER_START_PORTNO + (i * 2) + cmd = [name + 'DetectorServer_virtual', '-p', str(port_no)] + startProcessInBackground(cmd, fp) + match name: + case 'jungfrau': + time.sleep(7) + case 'gotthard2': + time.sleep(5) + case _: + time.sleep(3) + + +def connectToVirtualServers(name, num_mods): + try: + d = Detector() + except Exception as e: + raise RuntimeException(f'Could not create Detector object for {name}. Error: {str(e)}') + + counts_sec = 5 + while (counts_sec != 0): + try: + d.virtual = [num_mods, SERVER_START_PORTNO] + break + except Exception as e: + # stop server still not up, wait a bit longer + if "Cannot connect to" in str(e): + Log(LogLevel.WARNING, f'Still waiting for {name} virtual server to be up...{counts_sec}s left') + time.sleep(1) + counts_sec -= 1 + else: + raise + + return d + + +def loadConfig(name, rx_hostname, settingsdir, fp, num_mods = 1, num_frames = 1): + Log(LogLevel.INFO, 'Loading config') + Log(LogLevel.INFO, 'Loading config', fp) + try: + d = connectToVirtualServers(name, num_mods) + d.udp_dstport = DEFAULT_UDP_DST_PORTNO + if name == 'eiger': + d.udp_dstport2 = DEFAULT_UDP_DST_PORTNO + 1 + + d.rx_hostname = rx_hostname + d.udp_dstip = 'auto' + if name != "eiger": + if name == "gotthard": + d.udp_srcip = d.udp_dstip + else: + d.udp_srcip = 'auto' + + if name == "jungfrau" or name == "moench" or name == "xilinx_ctb": + d.powerchip = 1 + + if name == "xilinx_ctb": + d.configureTransceiver() + + if name == "eiger": + d.trimen = [4500, 5400, 6400] + d.settingspath = settingsdir + '/eiger/' + d.setThresholdEnergy(4500, detectorSettings.STANDARD) + + d.frames = num_frames + except Exception as e: + raise RuntimeException(f'Could not load config for {name}. Error: {str(e)}') + + +def ParseArguments(description, default_num_mods=1): + parser = argparse.ArgumentParser(description) + + parser.add_argument('rx_hostname', nargs='?', default='localhost', + help='Hostname/IP of the current machine') + parser.add_argument('settingspath', nargs='?', default='../../settingsdir', + help='Relative or absolute path to the settings directory') + parser.add_argument('-n', '--num-mods', nargs='?', default=default_num_mods, type=int, + help='Number of modules to test with') + parser.add_argument('-f', '--num-frames', nargs='?', default=1, type=int, + help='Number of frames to test with') + parser.add_argument('-s', '--servers', nargs='*', + help='Detector servers to run') + + args = parser.parse_args() + + # Set default server list if not provided + if args.servers is None: + args.servers = [ + 'eiger', + 'jungfrau', + 'mythen3', + 'gotthard2', + 'gotthard' + 'ctb', + 'moench', + 'xilinx_ctb' + ] + + Log(LogLevel.INFO, 'Arguments:\n' + + 'rx_hostname: ' + args.rx_hostname + + '\nsettingspath: \'' + args.settingspath + + '\nservers: \'' + ' '.join(args.servers) + + '\nnum_mods: \'' + str(args.num_mods) + + '\nnum_frames: \'' + str(args.num_frames) + '\'') + + return args From f5bf5095250e51251df8618a039a1f332426a615 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 7 May 2025 13:58:21 +0200 Subject: [PATCH 17/33] typo --- tests/scripts/utils_for_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/utils_for_test.py b/tests/scripts/utils_for_test.py index 2969d5599..540fcdcd9 100644 --- a/tests/scripts/utils_for_test.py +++ b/tests/scripts/utils_for_test.py @@ -229,7 +229,7 @@ def ParseArguments(description, default_num_mods=1): 'jungfrau', 'mythen3', 'gotthard2', - 'gotthard' + 'gotthard', 'ctb', 'moench', 'xilinx_ctb' From 5f6dea2d6d3017050226da637e9bd64b3dbc994e Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 7 May 2025 14:06:30 +0200 Subject: [PATCH 18/33] fix for gotthard to work --- tests/scripts/test_frame_synchronizer.py | 1 + tests/scripts/utils_for_test.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py index b8468070c..dc3c68281 100644 --- a/tests/scripts/test_frame_synchronizer.py +++ b/tests/scripts/test_frame_synchronizer.py @@ -115,6 +115,7 @@ def startTestsForAll(args, fp): acquire() testFramesCaught(server, args.num_frames) testZmqHeadetTypeCount(server, args.num_mods, args.num_frames, fp) + Log(LogLevel.INFO, '\n') except Exception as e: raise RuntimeException(f'Synchronizer Tests failed') diff --git a/tests/scripts/utils_for_test.py b/tests/scripts/utils_for_test.py index 540fcdcd9..138ed4076 100644 --- a/tests/scripts/utils_for_test.py +++ b/tests/scripts/utils_for_test.py @@ -140,6 +140,8 @@ def startDetectorVirtualServer(name :str, num_mods, fp): for i in range(num_mods): port_no = SERVER_START_PORTNO + (i * 2) cmd = [name + 'DetectorServer_virtual', '-p', str(port_no)] + if name == 'gotthard': + cmd += ['-m', '1'] startProcessInBackground(cmd, fp) match name: case 'jungfrau': From a85ad6cd848a8d20d1c071391dd31cfafe724a1f Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 7 May 2025 15:58:28 +0200 Subject: [PATCH 19/33] minor --- tests/scripts/test_frame_synchronizer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py index dc3c68281..384a33b1e 100644 --- a/tests/scripts/test_frame_synchronizer.py +++ b/tests/scripts/test_frame_synchronizer.py @@ -44,7 +44,7 @@ def startFrameSynchronizer(num_mods, fp): time.sleep(1) -def acquire(): +def acquire(fp): Log(LogLevel.INFO, 'Acquiring') Log(LogLevel.INFO, 'Acquiring', fp) d = Detector() @@ -112,7 +112,7 @@ def startTestsForAll(args, fp): startFrameSynchronizerPullSocket(server, fp) startFrameSynchronizer(args.num_mods, fp) loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, fp=fp, num_mods=args.num_mods, num_frames=args.num_frames) - acquire() + acquire(fp) testFramesCaught(server, args.num_frames) testZmqHeadetTypeCount(server, args.num_mods, args.num_frames, fp) Log(LogLevel.INFO, '\n') From c0ae090be0b50c983f3213b2b0b8ff7c6d9c6b4a Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 7 May 2025 15:56:41 +0200 Subject: [PATCH 20/33] better error messageS --- tests/scripts/test_frame_synchronizer.py | 4 ++-- tests/scripts/test_simulators.py | 2 +- tests/scripts/utils_for_test.py | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/scripts/test_frame_synchronizer.py b/tests/scripts/test_frame_synchronizer.py index 384a33b1e..87c784cf7 100644 --- a/tests/scripts/test_frame_synchronizer.py +++ b/tests/scripts/test_frame_synchronizer.py @@ -96,7 +96,7 @@ def testZmqHeadetTypeCount(name, num_mods, num_frames, fp): msg = f"Expected {expected_count} '{htype}' entries, found {htype_counts[htype]}" raise RuntimeException(msg) except Exception as e: - raise RuntimeException(f'Failed to get zmq header count type. Error:{e}') + raise RuntimeException(f'Failed to get zmq header count type. Error:{str(e)}') from e Log(LogLevel.INFOGREEN, f"Zmq Header type count test passed for {name}") Log(LogLevel.INFOGREEN, f"Zmq Header type count test passed for {name}", fp) @@ -117,7 +117,7 @@ def startTestsForAll(args, fp): testZmqHeadetTypeCount(server, args.num_mods, args.num_frames, fp) Log(LogLevel.INFO, '\n') except Exception as e: - raise RuntimeException(f'Synchronizer Tests failed') + raise RuntimeException(f'Synchronizer Tests failed') from e Log(LogLevel.INFOGREEN, 'Passed all synchronizer tests for all detectors \n' + str(args.servers)) diff --git a/tests/scripts/test_simulators.py b/tests/scripts/test_simulators.py index fc0a54a34..d6f4371d9 100644 --- a/tests/scripts/test_simulators.py +++ b/tests/scripts/test_simulators.py @@ -65,7 +65,7 @@ def startCmdTestsForAll(args, fp): loadConfig(name=server, rx_hostname=args.rx_hostname, settingsdir=args.settingspath, fp=fp, num_mods=num_mods) runProcessWithLogFile('Cmd Tests for ' + server, cmd, fp, fname) except Exception as e: - raise RuntimeException(f'Cmd Tests failed for {server}.') + raise RuntimeException(f'Cmd Tests failed for {server}.') from e Log(LogLevel.INFOGREEN, 'Passed all tests for all detectors \n' + str(args.servers)) diff --git a/tests/scripts/utils_for_test.py b/tests/scripts/utils_for_test.py index 138ed4076..5f209899a 100644 --- a/tests/scripts/utils_for_test.py +++ b/tests/scripts/utils_for_test.py @@ -71,7 +71,7 @@ def killProcess(name, fp): if p.returncode != 0 and bool(checkIfProcessRunning(name)): raise RuntimeException(f"Could not kill {name} with pid {pid}") except Exception as e: - raise RuntimeException(f"Failed to kill process {name} pid:{pid}. Exception occured: [code:{e}, msg:{e.stderr}]") + raise RuntimeException(f"Failed to kill process {name} pid:{pid}. Error: {str(e)}]") from e #else: # Log(LogLevel.INFO, 'process not running : ' + name) @@ -101,7 +101,7 @@ def startProcessInBackground(cmd, fp): try: p = subprocess.Popen(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, restore_signals=False) except Exception as e: - raise RuntimeException(f'Failed to start {cmd}:{e}') + raise RuntimeException(f'Failed to start {cmd}:{str(e)}') from e def startProcessInBackgroundWithLogFile(cmd, fp, log_file_name): @@ -111,7 +111,7 @@ def startProcessInBackgroundWithLogFile(cmd, fp, log_file_name): with open(log_file_name, 'w') as log_fp: subprocess.Popen(cmd, stdout=log_fp, stderr=log_fp, text=True) except Exception as e: - raise RuntimeException(f'Failed to start {cmd}:{e}') + raise RuntimeException(f'Failed to start {cmd}:{str(e)}') from e def runProcessWithLogFile(name, cmd, fp, log_file_name): @@ -124,8 +124,8 @@ def runProcessWithLogFile(name, cmd, fp, log_file_name): except subprocess.CalledProcessError as e: pass except Exception as e: - Log(LogLevel.ERROR, f'Failed to run {name}:{e}', fp) - raise RuntimeException(f'Failed to run {name}:{e}') + Log(LogLevel.ERROR, f'Failed to run {name}:{str(e)}', fp) + raise RuntimeException(f'Failed to run {name}:{str(e)}') with open (log_file_name, 'r') as f: for line in f: @@ -156,7 +156,7 @@ def connectToVirtualServers(name, num_mods): try: d = Detector() except Exception as e: - raise RuntimeException(f'Could not create Detector object for {name}. Error: {str(e)}') + raise RuntimeException(f'Could not create Detector object for {name}. Error: {str(e)}') from e counts_sec = 5 while (counts_sec != 0): @@ -205,7 +205,7 @@ def loadConfig(name, rx_hostname, settingsdir, fp, num_mods = 1, num_frames = 1) d.frames = num_frames except Exception as e: - raise RuntimeException(f'Could not load config for {name}. Error: {str(e)}') + raise RuntimeException(f'Could not load config for {name}. Error: {str(e)}') from e def ParseArguments(description, default_num_mods=1): From 674865120b3d167532ce23e54cd41a26cf8763d1 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 7 May 2025 16:03:43 +0200 Subject: [PATCH 21/33] typo --- tests/scripts/utils_for_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/utils_for_test.py b/tests/scripts/utils_for_test.py index 5f209899a..389bfad4a 100644 --- a/tests/scripts/utils_for_test.py +++ b/tests/scripts/utils_for_test.py @@ -71,7 +71,7 @@ def killProcess(name, fp): if p.returncode != 0 and bool(checkIfProcessRunning(name)): raise RuntimeException(f"Could not kill {name} with pid {pid}") except Exception as e: - raise RuntimeException(f"Failed to kill process {name} pid:{pid}. Error: {str(e)}]") from e + raise RuntimeException(f"Failed to kill process {name} pid:{pid}. Error: {str(e)}") from e #else: # Log(LogLevel.INFO, 'process not running : ' + name) From 2c4cd807480f7facf001e07b2a6027f381fefea3 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 7 May 2025 16:33:50 +0200 Subject: [PATCH 22/33] moving the erasure of the fnum to after sending the zmg packets and also deleteing all old frames when end of acquisition --- .../src/FrameSynchronizerApp.cpp | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/slsReceiverSoftware/src/FrameSynchronizerApp.cpp b/slsReceiverSoftware/src/FrameSynchronizerApp.cpp index f9658e5ee..2dc84c30b 100644 --- a/slsReceiverSoftware/src/FrameSynchronizerApp.cpp +++ b/slsReceiverSoftware/src/FrameSynchronizerApp.cpp @@ -227,20 +227,24 @@ void Correlate(FrameStatus *stat) { msg_list.insert(msg_list.end(), stat->frames[port][fnum].begin(), stat->frames[port][fnum].end()); - stat->frames[port].erase(fnum); } } LOG(printHeadersLevel) << "Sending data packets for fnum " << fnum; zmq_send_multipart(socket, msg_list); + // clean up for (const auto &it : stat->frames) { const uint16_t port = it.first; - // clean up - for (zmq_msg_t *msg : stat->frames[port][fnum]) { - if (msg) { - zmq_msg_close(msg); - delete msg; + const FrameMap &frame_map = it.second; + auto frame = frame_map.find(fnum); + if (frame != frame_map.end()) { + for (zmq_msg_t *msg : frame->second) { + if (msg) { + zmq_msg_close(msg); + delete msg; + } } + stat->frames[port].erase(fnum); } } } @@ -256,6 +260,21 @@ void Correlate(FrameStatus *stat) { } } stat->ends.clear(); + // clean up old frames + for (auto &it : stat->frames) { + FrameMap &frame_map = it.second; + for (auto &frame : frame_map) { + for (zmq_msg_t *msg : frame.second) { + if (msg) { + zmq_msg_close(msg); + delete msg; + } + } + frame.second.clear(); + } + frame_map.clear(); + } + stat->frames.clear(); } } } From 894c2277232d67bd8e8b184d52ce9ffa10ff3ae3 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Tue, 13 May 2025 23:26:04 +0200 Subject: [PATCH 23/33] update release notes and package version --- RELEASE.txt | 17 ++++++++++++++++- VERSION | 2 +- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/RELEASE.txt b/RELEASE.txt index bb5a313bd..607cd412c 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -24,10 +24,25 @@ This document describes the differences between v9.1.1 and v9.1.0 --------------- - * [Mythen3] Fix trimbits and badchannels + * [Mythen3] Fix trimbits and badchannels They were shifted by 1. Fixed. + Receiver + -------- + + + * slsFrameSynchronizer - does not stream out frames + It streamed out start and stop zmq packets but did + not stream out frames with no error message. Fixed. + + + * slsMultiReceiver - 3rd Command line argument (vebose option) + When this option to print headers is enabled, the sample call back + headers update the image size to a random number 26112. This + is fixed and left commented out for easy reference for users. + + 2 On-board Detector Server Compatibility ========================================== diff --git a/VERSION b/VERSION index e977f5eae..8ce0f0f36 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -9.1.0 \ No newline at end of file +9.1.1 \ No newline at end of file From 3bfa3bfba3572adafef43844d20b2fd6b9b32f1b Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Tue, 13 May 2025 23:30:35 +0200 Subject: [PATCH 24/33] update receiver version --- slsSupportLib/include/sls/versionAPI.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slsSupportLib/include/sls/versionAPI.h b/slsSupportLib/include/sls/versionAPI.h index 49150ec51..77eba3c20 100644 --- a/slsSupportLib/include/sls/versionAPI.h +++ b/slsSupportLib/include/sls/versionAPI.h @@ -9,5 +9,5 @@ #define APIXILINXCTB "9.1.0 0x250204" #define APIJUNGFRAU "9.1.0 0x250318" #define APILIB "9.1.0 0x250325" -#define APIRECEIVER "9.1.0 0x250325" #define APIMYTHEN3 "9.1.1 0x250409" +#define APIRECEIVER "9.1.1 0x250513" From d739453f2bbec45e1c6c83e7c6b975cac0e7c843 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 14 May 2025 09:10:15 +0200 Subject: [PATCH 25/33] added support to release notes --- RELEASE.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASE.txt b/RELEASE.txt index 607cd412c..64ea11733 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -225,3 +225,4 @@ This document describes the differences between v9.1.1 and v9.1.0 dhanya.thattil@psi.ch erik.frojdh@psi.ch + alice.mazzoleni@psi.ch From 2ee7e36fb3f84f93fe7af71368aa37b2d121a00b Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 21 May 2025 14:30:10 +0200 Subject: [PATCH 26/33] rearranged receiver topics, differentiated btween receiver variants and added info about slsFrameSynchronizer --- docs/src/index.rst | 3 +- docs/src/receivers.rst | 22 +++++----- docs/src/slsreceiver.rst | 88 ++++++++++++++++++++++++++++++++++++---- docs/src/udpconfig.rst | 2 +- 4 files changed, 94 insertions(+), 21 deletions(-) diff --git a/docs/src/index.rst b/docs/src/index.rst index af291cd6e..7dfa8b1b6 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -79,8 +79,9 @@ Welcome to slsDetectorPackage's documentation! :caption: Receiver :maxdepth: 2 - receivers slsreceiver + receivers + .. toctree:: :caption: Receiver Files diff --git a/docs/src/receivers.rst b/docs/src/receivers.rst index 29937cf84..af919faa2 100644 --- a/docs/src/receivers.rst +++ b/docs/src/receivers.rst @@ -1,25 +1,25 @@ -Receivers +Custom Receiver ================= -Receiver processes can be run on same or different machines as the client, receives the data from the detector (via UDP packets). -When using the slsReceiver/ slsMultiReceiver, they can be further configured by the client control software (via TCP/IP) to set file name, file path, progress of acquisition etc. +The receiver essentially listens to UDP data packats sent out by the detector. + +To know more about detector receiver setup in the config file, please check out :ref:`the detector-receiver UDP configuration in the config file` and the :ref:`detector udp format`. -To know more about detector receiver configuration, please check out :ref:`detector udp header and udp commands in the config file ` +| Please note the following when using a custom receiver: -Custom Receiver ----------------- +* **udp_dstmac** must be configured in the config file. This parameter is not required when using an in-built receiver. -| When using custom receiver with our package, ensure that **udp_dstmac** is also configured in the config file. This parameter is not required when using slsReceiver. +* Cannot use "auto" for **udp_dstip**. -| Cannot use "auto" for **udp_dstip**. +* No **rx_** commands in the config file. These commands are for configuring the slsReceiver. -| Also ensure that there are no **rx_** commands in the config file. These commands are for configuring the slsReceiver. + + +The main difference is the lack of **rx_** commands or file commands (eg. **f**write, **f**path) and the **udp_dstmac** is required in config file. Example of a custom receiver config file -* The main difference is the lack of **rx_** commands or file commands (eg. fwrite, fpath) and the udp_dstmac is required in config file. - .. code-block:: bash # detector hostname diff --git a/docs/src/slsreceiver.rst b/docs/src/slsreceiver.rst index 0413eb10a..847f12a05 100644 --- a/docs/src/slsreceiver.rst +++ b/docs/src/slsreceiver.rst @@ -1,8 +1,44 @@ -slsReceiver/ slsMultiReceiver +In-built Receiver ================================ -| One has to start the slsReceiver before loading config file or using any receiver commands (prefix: **rx_** ) + +The receiver essentially listens to UDP data packets sent out by the detector. It's main features are: + +- **Listening**: Receives UDP data from the detector. +- **Writing to File**: Optionally writes received data to disk. +- **Streaming via ZMQ**: Optionally streams out the data using ZeroMQ. + +Each of these operations runs asynchronously and in parallel for each UDP port. + + +.. note :: + + * Can be run on the same or different machine as the client. + * Can be configured by the client. (set file name/ discard policy, get progress etc.) + * Has to be started before the client runs any receiver specific command. + + +Receiver Variants +----------------- +There are three main receiver types. How to start them is described :ref:`below`. + + ++----------------------+------------------+-----------------------------------------+--------------------+--------------------+---------------------+ +| Receiver Type | Modules Supported| Internal Architecture | ZMQ Streaming | ZMQ Synchronization| Image Reconstruction| ++======================+==================+=========================================+====================+====================+=====================+ +| slsReceiver | 1 | Threads per port | Disabled by default| No | No | ++----------------------+------------------+-----------------------------------------+--------------------+--------------------+---------------------+ +| slsMultiReceiver | Multiple | Multiple child processes of slsReceiver | Disabled by default| No | No | ++----------------------+------------------+-----------------------------------------+--------------------+--------------------+---------------------+ +| slsFrameSynchronizer | Multiple | Multi-threading of slsReceiver | Enabled | Yes, across ports | No | ++----------------------+------------------+-----------------------------------------+--------------------+--------------------+---------------------+ + + +.. _Starting up the Receiver: + +Starting up the Receiver +------------------------- For a Single Module .. code-block:: bash @@ -18,25 +54,35 @@ For Multiple Modules # each receiver (for each module) requires a unique tcp port (if all on same machine) - # using slsReceiver in multiple consoles + # using slsReceiver in multiple consoles (one for each module) slsReceiver slsReceiver -t1955 - # slsMultiReceiver [starting port] [number of receivers] + # slsMultiReceiver [starting port] [number of receivers/modules] slsMultiReceiver 2012 2 + slsFrameSynchronizer 2012 2 - # slsMultiReceiver [starting port] [number of receivers] [print each frame header for debugging] + + # slsMultiReceiver [starting port] [number of receivers/modules] [print for debugging] slsMultiReceiver 2012 2 1 + slsFrameSynchronizer 2012 2 1 + Client Commands ----------------- -| One can remove **udp_dstmac** from the config file, as the slsReceiver fetches this from the **udp_ip**. +* Client commands to the receiver begin with **rx_** or **f_** (file commands). -| One can use "auto" for **udp_dstip** if one wants to use default ip of **rx_hostname**. +* **rx_hostname** has to be the first command to the receiver so the client knows which reciever process to communicate with. -| The first command to the receiver (**rx_** commands) should be **rx_hostname**. The following are the different ways to establish contact. +* Can use 'auto' for **udp_dstip** if using 1GbE interface or the :ref:`virtual simulators`. + + +To know more about detector receiver setup in the config file, please check out :ref:`the detector-receiver UDP configuration in the config file` and the :ref:`detector udp format`. + + +The following are the different ways to establish contact using **rx_hostname** command. .. code-block:: bash @@ -91,6 +137,32 @@ Client Commands sls_detector_get -h rx_framescaught +Example of a config file using in-built receiver + +.. code-block:: bash + + # detector hostname + hostname bchip052+bchip053+ + + # udp destination port (receiver) + # sets increasing destination udp ports starting at 50004 + udp_dstport 50004 + + # udp destination ip (receiver) + 0:udp_dstip 10.0.1.100 + 1:udp_dstip 10.0.2.100 + + # udp source ip (same subnet as udp_dstip) + 0:udp_srcip 10.0.1.184 + 1:udp_srcip 10.0.2.184 + + # udp destination mac - not required (picked up from udp_dstip) + #udp_dstmac 22:47:d5:48:ad:ef + + # connects to receivers at increasing tcp port starting at 1954 + rx_hostname mpc3434 + # same as rx_hostname mpc3434:1954+mpc3434:1955+ + Performance diff --git a/docs/src/udpconfig.rst b/docs/src/udpconfig.rst index f3e56d82a..847b11899 100644 --- a/docs/src/udpconfig.rst +++ b/docs/src/udpconfig.rst @@ -1,4 +1,4 @@ -.. _detector udp header: +.. _detector udp header config: Config file From 3619a032acee2abab475d0a68ddf8700ab19a135 Mon Sep 17 00:00:00 2001 From: froejdh_e Date: Wed, 21 May 2025 15:46:04 +0200 Subject: [PATCH 27/33] rewrote end() for StaticVector --- slsSupportLib/include/sls/StaticVector.h | 6 +-- slsSupportLib/tests/test-StaticVector.cpp | 49 ++++++++++++++--------- 2 files changed, 32 insertions(+), 23 deletions(-) diff --git a/slsSupportLib/include/sls/StaticVector.h b/slsSupportLib/include/sls/StaticVector.h index 3c671f80b..cf72aa05d 100644 --- a/slsSupportLib/include/sls/StaticVector.h +++ b/slsSupportLib/include/sls/StaticVector.h @@ -113,10 +113,10 @@ template class StaticVector { // auto begin() noexcept -> decltype(data_.begin()) { return data_.begin(); // } const_iterator begin() const noexcept { return data_.begin(); } - iterator end() noexcept { return &data_[current_size]; } - const_iterator end() const noexcept { return &data_[current_size]; } + iterator end() noexcept { return data_.begin()+current_size; } + const_iterator end() const noexcept { return data_.begin()+current_size; } const_iterator cbegin() const noexcept { return data_.cbegin(); } - const_iterator cend() const noexcept { return &data_[current_size]; } + const_iterator cend() const noexcept { return data_.cbegin()+current_size; } void size_check(size_type s) const { if (s > Capacity) { diff --git a/slsSupportLib/tests/test-StaticVector.cpp b/slsSupportLib/tests/test-StaticVector.cpp index 6f9f5da50..2ff548fce 100644 --- a/slsSupportLib/tests/test-StaticVector.cpp +++ b/slsSupportLib/tests/test-StaticVector.cpp @@ -8,12 +8,15 @@ #include #include -namespace sls { + +using sls::StaticVector; TEST_CASE("StaticVector is a container") { - REQUIRE(is_container>::value == true); + REQUIRE(sls::is_container>::value == true); } + + TEST_CASE("Comparing StaticVector containers") { StaticVector a{0, 1, 2}; StaticVector b{0, 1, 2}; @@ -90,10 +93,17 @@ TEST_CASE("Copy construct from array") { REQUIRE(fcc == arr); } +TEST_CASE("Construct from a smaller StaticVector") { + StaticVector sv{1, 2, 3}; + StaticVector sv2{sv}; + REQUIRE(sv == sv2); +} + TEST_CASE("Free function and method gives the same iterators") { StaticVector fcc{1, 2, 3}; REQUIRE(std::begin(fcc) == fcc.begin()); } + SCENARIO("StaticVectors can be sized and resized", "[support]") { GIVEN("A default constructed container") { @@ -246,23 +256,23 @@ SCENARIO("Sorting, removing and other manipulation of a container", REQUIRE(a[3] == 90); } } - // WHEN("Sorting is done using free function for begin and end") { - // std::sort(begin(a), end(a)); - // THEN("it also works") { - // REQUIRE(a[0] == 12); - // REQUIRE(a[1] == 12); - // REQUIRE(a[2] == 14); - // REQUIRE(a[3] == 90); - // } - // } - // WHEN("Erasing elements of a certain value") { - // a.erase(std::remove(begin(a), end(a), 12)); - // THEN("all elements of that value are removed") { - // REQUIRE(a.size() == 2); - // REQUIRE(a[0] == 14); - // REQUIRE(a[1] == 90); - // } - // } + WHEN("Sorting is done using free function for begin and end") { + std::sort(std::begin(a), std::end(a)); + THEN("it also works") { + REQUIRE(a[0] == 12); + REQUIRE(a[1] == 12); + REQUIRE(a[2] == 14); + REQUIRE(a[3] == 90); + } + } + WHEN("Erasing elements of a certain value") { + a.erase(std::remove(std::begin(a), std::end(a), 12)); + THEN("all elements of that value are removed") { + REQUIRE(a.size() == 2); + REQUIRE(a[0] == 14); + REQUIRE(a[1] == 90); + } + } } } @@ -335,4 +345,3 @@ TEST_CASE("StaticVector stream") { REQUIRE(oss.str() == "[33, 85667, 2]"); } -} // namespace sls From b8b067d74b3998f7008d53c3c97c1540072f6790 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 21 May 2025 15:46:49 +0200 Subject: [PATCH 28/33] typo --- docs/src/receivers.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/receivers.rst b/docs/src/receivers.rst index af919faa2..31ff8f734 100644 --- a/docs/src/receivers.rst +++ b/docs/src/receivers.rst @@ -1,7 +1,7 @@ Custom Receiver ================= -The receiver essentially listens to UDP data packats sent out by the detector. +The receiver essentially listens to UDP data packets sent out by the detector. To know more about detector receiver setup in the config file, please check out :ref:`the detector-receiver UDP configuration in the config file` and the :ref:`detector udp format`. From d414c7459b68d58089f07ec218768b8c7c886c28 Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 21 May 2025 17:05:38 +0200 Subject: [PATCH 29/33] minor aesthetics --- docs/src/serverupgrade.rst | 1 + docs/src/slsreceiver.rst | 23 ++++++++++++++--------- docs/src/virtualserver.rst | 1 + 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/docs/src/serverupgrade.rst b/docs/src/serverupgrade.rst index c6fe1675a..a215da8ae 100644 --- a/docs/src/serverupgrade.rst +++ b/docs/src/serverupgrade.rst @@ -1,4 +1,5 @@ .. _Detector Server Upgrade: + Upgrade ======== diff --git a/docs/src/slsreceiver.rst b/docs/src/slsreceiver.rst index 847f12a05..8e96ca8aa 100644 --- a/docs/src/slsreceiver.rst +++ b/docs/src/slsreceiver.rst @@ -23,16 +23,21 @@ Receiver Variants ----------------- There are three main receiver types. How to start them is described :ref:`below`. ++----------------------+--------------------+-----------------------------------------+--------------------------------+ +| Receiver Type | slsReceiver | slsMultiReceiver |slsFrameSynchronizer | ++======================+====================+=========================================+================================+ +| Modules Supported | 1 | Multiple | Multiple | ++----------------------+--------------------+-----------------------------------------+--------------------------------+ +| Internal Architecture| Threads per porttt | Multiple child processes of slsReceiver | Multi-threading of slsReceiver | ++----------------------+--------------------+-----------------------------------------+--------------------------------+ +| ZMQ Streaming | Disabled by default| Disabled by default | Enabled, not optional | ++----------------------+--------------------+-----------------------------------------+--------------------------------+ +| ZMQ Synchronization | No | No | Yes, across ports | ++----------------------+--------------------+-----------------------------------------+--------------------------------+ +| Image Reconstruction | No | No | No | ++----------------------+--------------------+-----------------------------------------+--------------------------------+ + -+----------------------+------------------+-----------------------------------------+--------------------+--------------------+---------------------+ -| Receiver Type | Modules Supported| Internal Architecture | ZMQ Streaming | ZMQ Synchronization| Image Reconstruction| -+======================+==================+=========================================+====================+====================+=====================+ -| slsReceiver | 1 | Threads per port | Disabled by default| No | No | -+----------------------+------------------+-----------------------------------------+--------------------+--------------------+---------------------+ -| slsMultiReceiver | Multiple | Multiple child processes of slsReceiver | Disabled by default| No | No | -+----------------------+------------------+-----------------------------------------+--------------------+--------------------+---------------------+ -| slsFrameSynchronizer | Multiple | Multi-threading of slsReceiver | Enabled | Yes, across ports | No | -+----------------------+------------------+-----------------------------------------+--------------------+--------------------+---------------------+ .. _Starting up the Receiver: diff --git a/docs/src/virtualserver.rst b/docs/src/virtualserver.rst index ae1073d8e..226a94c98 100644 --- a/docs/src/virtualserver.rst +++ b/docs/src/virtualserver.rst @@ -1,4 +1,5 @@ .. _Virtual Detector Servers: + Simulators =========== From 348d8202650c1480b521cd51565cee0d2f5b14ef Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Wed, 21 May 2025 17:27:31 +0200 Subject: [PATCH 30/33] minor --- docs/src/slsreceiver.rst | 53 +++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 28 deletions(-) diff --git a/docs/src/slsreceiver.rst b/docs/src/slsreceiver.rst index 8e96ca8aa..932c25a05 100644 --- a/docs/src/slsreceiver.rst +++ b/docs/src/slsreceiver.rst @@ -46,12 +46,10 @@ Starting up the Receiver ------------------------- For a Single Module .. code-block:: bash + + slsReceiver # default port 1954 - # default port 1954 - slsReceiver - - # custom port 2012 - slsReceiver -t2012 + slsReceiver -t2012 # custom port 2012 For Multiple Modules @@ -59,27 +57,24 @@ For Multiple Modules # each receiver (for each module) requires a unique tcp port (if all on same machine) - # using slsReceiver in multiple consoles (one for each module) + # option 1 (one for each module) slsReceiver slsReceiver -t1955 - # slsMultiReceiver [starting port] [number of receivers/modules] + # option 2 slsMultiReceiver 2012 2 + + # option 3 slsFrameSynchronizer 2012 2 - # slsMultiReceiver [starting port] [number of receivers/modules] [print for debugging] - slsMultiReceiver 2012 2 1 - slsFrameSynchronizer 2012 2 1 - - Client Commands ----------------- * Client commands to the receiver begin with **rx_** or **f_** (file commands). -* **rx_hostname** has to be the first command to the receiver so the client knows which reciever process to communicate with. +* **rx_hostname** has to be the first command to the receiver so the client knows which receiver process to communicate with. * Can use 'auto' for **udp_dstip** if using 1GbE interface or the :ref:`virtual simulators`. @@ -91,35 +86,37 @@ The following are the different ways to establish contact using **rx_hostname** .. code-block:: bash - # default receiver tcp port (1954) + # ---single module--- + + # default receiver port at 1954 + rx_hostname xxx + + # custom receiver port + rx_hostname xxx:1957 # option 1 + + rx_tcpport 1957 # option 2 rx_hostname xxx - # custom receiver port - rx_hostname xxx:1957 - # custom receiver port - rx_tcpport 1954 - rx_hostname xxx + # ---multi module--- - # multi modules with custom ports - rx_hostname xxx:1955+xxx:1956+ - - - # multi modules using increasing tcp ports when using multi detector command + # using increasing tcp ports rx_tcpport 1955 rx_hostname xxx - # or specify multi modules with custom ports on same rxr pc - 0:rx_tcpport 1954 + # custom ports + rx_hostname xxx:1955+xxx:1958+ # option 1 + + 0:rx_tcpport 1954 # option 2 1:rx_tcpport 1955 2:rx_tcpport 1956 rx_hostname xxx - # multi modules with custom ports on different rxr pc + # custom ports on different receiver machines 0:rx_tcpport 1954 0:rx_hostname xxx 1:rx_tcpport 1955 - 1:rx_hostname yyy + 1:rx_hostname yyyrxr | Example commands: From 9f9b1d496c8fedcf75cfc76a9fecee8fd6818b72 Mon Sep 17 00:00:00 2001 From: froejdh_e Date: Thu, 22 May 2025 09:51:01 +0200 Subject: [PATCH 31/33] added expat to host section --- conda-recipe/meta.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/conda-recipe/meta.yaml b/conda-recipe/meta.yaml index b6523a7e6..8dde14f70 100755 --- a/conda-recipe/meta.yaml +++ b/conda-recipe/meta.yaml @@ -27,6 +27,7 @@ requirements: - libgl-devel - libtiff - zlib + - expat run: - libstdcxx-ng @@ -87,6 +88,7 @@ outputs: - {{ compiler('c') }} - {{compiler('cxx')}} - {{ pin_subpackage('slsdetlib', exact=True) }} + run: - {{ pin_subpackage('slsdetlib', exact=True) }} From 376514606f3ef85052e9634ec4b955dd4e34fd0c Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Thu, 22 May 2025 10:51:57 +0200 Subject: [PATCH 32/33] updated relase notes --- RELEASE.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RELEASE.txt b/RELEASE.txt index 64ea11733..b9ff9c4d1 100644 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -1,4 +1,4 @@ -SLS Detector Package Bug Fix Release 9.1.1 released on xx.04.2025 +SLS Detector Package Bug Fix Release 9.1.1 released on 22.05.2025 ================================================================== This document describes the differences between v9.1.1 and v9.1.0 @@ -35,6 +35,7 @@ This document describes the differences between v9.1.1 and v9.1.0 * slsFrameSynchronizer - does not stream out frames It streamed out start and stop zmq packets but did not stream out frames with no error message. Fixed. + Updated documentation. * slsMultiReceiver - 3rd Command line argument (vebose option) From 9bb25445f574a69df97af8909a12658230fa78cd Mon Sep 17 00:00:00 2001 From: Dhanya Thattil Date: Thu, 22 May 2025 10:54:02 +0200 Subject: [PATCH 33/33] formatting --- slsSupportLib/include/sls/StaticVector.h | 8 +++++--- slsSupportLib/tests/test-StaticVector.cpp | 4 ---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/slsSupportLib/include/sls/StaticVector.h b/slsSupportLib/include/sls/StaticVector.h index cf72aa05d..931ea17fa 100644 --- a/slsSupportLib/include/sls/StaticVector.h +++ b/slsSupportLib/include/sls/StaticVector.h @@ -113,10 +113,12 @@ template class StaticVector { // auto begin() noexcept -> decltype(data_.begin()) { return data_.begin(); // } const_iterator begin() const noexcept { return data_.begin(); } - iterator end() noexcept { return data_.begin()+current_size; } - const_iterator end() const noexcept { return data_.begin()+current_size; } + iterator end() noexcept { return data_.begin() + current_size; } + const_iterator end() const noexcept { return data_.begin() + current_size; } const_iterator cbegin() const noexcept { return data_.cbegin(); } - const_iterator cend() const noexcept { return data_.cbegin()+current_size; } + const_iterator cend() const noexcept { + return data_.cbegin() + current_size; + } void size_check(size_type s) const { if (s > Capacity) { diff --git a/slsSupportLib/tests/test-StaticVector.cpp b/slsSupportLib/tests/test-StaticVector.cpp index 2ff548fce..fc40f1a0f 100644 --- a/slsSupportLib/tests/test-StaticVector.cpp +++ b/slsSupportLib/tests/test-StaticVector.cpp @@ -8,15 +8,12 @@ #include #include - using sls::StaticVector; TEST_CASE("StaticVector is a container") { REQUIRE(sls::is_container>::value == true); } - - TEST_CASE("Comparing StaticVector containers") { StaticVector a{0, 1, 2}; StaticVector b{0, 1, 2}; @@ -344,4 +341,3 @@ TEST_CASE("StaticVector stream") { oss << vec; REQUIRE(oss.str() == "[33, 85667, 2]"); } -