diff --git a/.gitea/workflows/docker-rh8-build-test.yml b/.gitea/workflows/docker-rh8-build-test.yml index ed569282d..3d070f71e 100644 --- a/.gitea/workflows/docker-rh8-build-test.yml +++ b/.gitea/workflows/docker-rh8-build-test.yml @@ -34,6 +34,7 @@ jobs: python3.12 -m pip install pytest python3.12 -m pip install colorama python3.12 -m pip install numpy + python3.12 -m pip install pyzmq - name: Python unit tests working-directory: ${{gitea.workspace}} diff --git a/.gitea/workflows/docker-rh9-build-test.yml b/.gitea/workflows/docker-rh9-build-test.yml index 9c2e392fd..2783d69ec 100644 --- a/.gitea/workflows/docker-rh9-build-test.yml +++ b/.gitea/workflows/docker-rh9-build-test.yml @@ -32,6 +32,7 @@ jobs: python3.12 -m pip install pytest python3.12 -m pip install colorama python3.12 -m pip install numpy + python3.12 -m pip install pyzmq - name: Python unit tests working-directory: ${{gitea.workspace}} diff --git a/.github/workflows/build_documentation.yml b/.github/workflows/build_documentation.yml index 64ef03451..a7fd764c4 100644 --- a/.github/workflows/build_documentation.yml +++ b/.github/workflows/build_documentation.yml @@ -54,7 +54,7 @@ jobs: - name: Install System Packages uses: awalsh128/cache-apt-pkgs-action@latest with: - packages: libhdf5-dev doxygen + packages: libhdf5-dev doxygen libfmt-dev version: 1.0 - name: Setup Python diff --git a/.github/workflows/cmake.yaml b/.github/workflows/cmake.yaml index 9ca278e9e..773700f1c 100644 --- a/.github/workflows/cmake.yaml +++ b/.github/workflows/cmake.yaml @@ -23,7 +23,7 @@ jobs: - uses: awalsh128/cache-apt-pkgs-action@latest with: - packages: libhdf5-dev qtbase5-dev qt5-qmake libqt5svg5-dev libpng-dev libtiff-dev + packages: libhdf5-dev qtbase5-dev qt5-qmake libqt5svg5-dev libpng-dev libtiff-dev libfmt-dev version: 1.0 - name: Configure CMake diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index 215efb8a3..96d04205e 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -18,11 +18,11 @@ jobs: with: python-version: 3.12 cache: 'pip' - - run: pip install pytest numpy colorama + - run: pip install pytest numpy colorama pyzmq - uses: awalsh128/cache-apt-pkgs-action@latest with: - packages: libhdf5-dev + packages: libhdf5-dev libfmt-dev version: 1.0 - name: Configure CMake diff --git a/CMakeLists.txt b/CMakeLists.txt index cb9645225..fb5d5df0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ if(NOT CMAKE_USE_PTHREADS_INIT) message(FATAL_ERROR "A POSIX threads (pthread) implementation is required, but was not found.") endif() +set(SLS_LOG_MAX_REPORTING_LEVEL "sls::TLogLevel::logINFO" CACHE STRING "Set the maximum logging level for the project") option(SLS_USE_SYSTEM_ZMQ "Use system installed libzmq" OFF) @@ -39,6 +40,7 @@ option(SLS_USE_SYSTEM_ZMQ "Use system installed libzmq" OFF) include(FetchContent) option(SLS_FETCH_ZMQ_FROM_GITHUB "Fetch zmq from github" OFF) option(SLS_FETCH_PYBIND11_FROM_GITHUB "Fetch pybind11 from github" OFF) +option(SLS_FETCH_FMT_FROM_GITHUB "Fetch fmt from github" OFF) # Allow FetchContent_Populate to be called with a single argument # otherwise deprecated warning is issued @@ -187,6 +189,40 @@ if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) set(SLS_MASTER_PROJECT ON) endif() +if (SLS_FETCH_FMT_FROM_GITHUB) + set(FMT_TEST OFF CACHE INTERNAL "disabling fmt tests") + FetchContent_Declare( + fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 12.1.0 + GIT_PROGRESS TRUE + USES_TERMINAL_DOWNLOAD TRUE + ) + set(FMT_INSTALL ON CACHE BOOL "") + FetchContent_MakeAvailable(fmt) + set_property(TARGET fmt PROPERTY POSITION_INDEPENDENT_CODE ON) +else() + # downloaded from https://github.com/fmtlib/fmt/releases/tag/12.1.0 + set(FMT_TEST OFF CACHE INTERNAL "disabling fmt tests") + FetchContent_Declare( + fmt + URL ${CMAKE_CURRENT_SOURCE_DIR}/libs/fmt/fmt-12.1.0.tar.gz + # Compute hash: md5sum fmt-12.1.0.tar.gz + URL_HASH MD5=92eb6f492e4838e5f024ce5207beafc7) + FetchContent_MakeAvailable(fmt) + set_property(TARGET fmt PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() + +# Export fmt +if(SLS_MASTER_PROJECT) + install(TARGETS fmt + EXPORT ${TARGETS_EXPORT_NAME} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + ) +endif() option(SLS_USE_HDF5 "HDF5 File format" OFF) @@ -194,6 +230,7 @@ option(SLS_BUILD_SHARED_LIBRARIES "Build shared libaries" OFF) option(SLS_USE_TEXTCLIENT "Text Client" ON) option(SLS_USE_DETECTOR "Detector libs" ON) option(SLS_USE_RECEIVER "Receiver" ON) +option(SLS_USE_SERVER "Server" ON) option(SLS_USE_RECEIVER_BINARIES "Receiver binaries" ON) option(SLS_USE_GUI "GUI" OFF) option(SLS_USE_SIMULATOR "Simulator" OFF) @@ -208,6 +245,7 @@ option(SLS_TUNE_LOCAL "tune to local machine" OFF) option(SLS_DEVEL_HEADERS "install headers for devel" OFF) option(SLS_USE_MOENCH "compile zmq and post processing for Moench" OFF) option(SLS_USE_JUNGFRAU "compile post processing for Jungfrau" OFF) +option(SLS_USE_MATTERHORN "compile matterhorn server" OFF) option(SLS_INSTALL_VERSIONED_BINARIES "Add version number to binaries on install" OFF) #Needed for multi version RPM #Convenience option to switch off defaults when building Moench binaries only @@ -282,18 +320,15 @@ if(SLS_EXT_BUILD) endif() - # slsProjectOptions and slsProjectWarnings are used # to control options for the libraries if(NOT TARGET slsProjectOptions) add_library(slsProjectOptions INTERFACE) - target_compile_features(slsProjectOptions INTERFACE cxx_std_17) + target_compile_features(slsProjectOptions INTERFACE cxx_std_17) + target_compile_definitions(slsProjectOptions + INTERFACE LOG_MAX_REPORTING_LEVEL=${SLS_LOG_MAX_REPORTING_LEVEL}) endif() -set(LOG_MAX_REPORTING_LEVEL sls::logINFO CACHE STRING "set logging level") -target_compile_definitions(slsProjectOptions - INTERFACE LOG_MAX_REPORTING_LEVEL=${LOG_MAX_REPORTING_LEVEL} -) if (NOT TARGET slsProjectWarnings) add_library(slsProjectWarnings INTERFACE) @@ -393,7 +428,12 @@ endif() if (SLS_USE_DETECTOR OR SLS_USE_TEXTCLIENT) add_subdirectory(slsDetectorSoftware) -endif () +endif() + +# TODO refactor with simulators +if (SLS_USE_SERVER) + add_subdirectory(slsDetectorServers/slsDetectorServer_cpp) +endif() if (SLS_USE_RECEIVER) add_subdirectory(slsReceiverSoftware) @@ -404,6 +444,10 @@ if (SLS_USE_GUI) add_subdirectory(slsDetectorGui) endif (SLS_USE_GUI) +if (SLS_USE_MATTERHORN) + add_subdirectory(slsDetectorServers/matterhornServer) +endif() + if (SLS_USE_SIMULATOR) add_subdirectory(slsDetectorServers) endif (SLS_USE_SIMULATOR) diff --git a/README.md b/README.md index f468bd102..3727bb07f 100644 --- a/README.md +++ b/README.md @@ -83,51 +83,62 @@ To use the basic building blocks, meaning sls_detector_get/put and the shared li > **Note:** For v9.x.x of slsDetectorPackage and older, C++11 compatible compiler. -#### Python bindings -* Python >= 3.8 +Additionally the core requires the following dependencies: -* pybind11 2.13.6 (packaged in libs) + * fmt 12.1.0 (packaged in libs) (since version 11.0.0) + * ZeroMQ 4.3.4 (packaged in libs) + * rapidjson (packaged in libs) -> **Note:** Refer [pybind11 notes](#4-pybind-and-zeromq). - -#### ZeroMQ - -* Zeromq 4.3.4 (packaged in libs) +> **Note:** Both fmt, ZeroMQ and rapidjson are bundled in libs. One does not need to pre-install them on the system. Alternatively, one can fetch fmt and ZeroMQ from GitHub by passing the cmake options ``-DSLS_FETCH_FMT_FROM_GITHUB=ON`` and ``-DSLS_FETCH_ZEROMQ_FROM_GITHUB=ON`` respectively. > **Note:** Refer [zeromq notes](#4-pybind-and-zeromq). -#### GUI +#### Dependencies to build Python module -* Qt 5.9 +To build the python module the following dependencies are needed: -* Qwt 6.1.5 (packaged in libs) + * Python >= 3.8 + * pybind11 2.13.6 (packaged in libs) + +> **Note:** pybind11 is bundled in libs. One does not need to pre-install it on the system. Alternatively, one can fetch pybind11 from GitHub by passing the cmake option ``-DSLS_FETCH_PYBIND11_FROM_GITHUB=ON``. -#### Moench executables +> **Note:** Refer [pybind11 notes](#4-pybind-and-zeromq). -* libtiff -#### Documentation +#### Dependencies to build documentation -The documentation is built with -* Doxygen (to extract C++ classes etc.) +To build this documentation that you are reading now the following dependencies are needed: -* Breathe (Sphinx plugin to handle doxygen xml) + * Doxygen (to extract C++ classes etc.) + * Breathe (Sphinx plugin to handle doxygen xml) + * Sphinx with sphinx_rtd_theme -* Sphinx with sphinx_rtd_theme +#### Dependencies to build GUI -#### Packaged in libs/ +To build the GUI the following dependencies are needed: -* catch2 (unit testing) + * Qt 5.9 + * Qwt 6.1.5 (packaged in libs) -* rapidjson (streaming from receiver) +> **Note:** Qwt is bundled in libs. One does not need to pre-install it on the system. -* pybind11 (python bindings) -* qwt (gui plotting) +#### Dependencies to build Moench and Jungfrau executables -* libzmq (streaming to/from receiver) + +To build the Moench and Jungfrau executables for preprocessing and calibration the following dependencies are needed: + + * libtiff + +#### Dependencies to build Tests + +To build the tests the following dependencies are needed: + + * Catch2 3.4.0 (packaged in libs) + +> **Note:** Catch2 is bundled in libs. One does not need to pre-install it on the system. ### 3.2. Download Source Code from github ``` diff --git a/conda-recipes/main-library/meta.yaml b/conda-recipes/main-library/meta.yaml index 54e97c1be..1c495645d 100755 --- a/conda-recipes/main-library/meta.yaml +++ b/conda-recipes/main-library/meta.yaml @@ -30,6 +30,7 @@ requirements: - zlib - expat - zeromq + - fmt run: - libstdcxx-ng diff --git a/docs/src/Testing.rst b/docs/src/Testing.rst index 4371ee956..ad67326d8 100644 --- a/docs/src/Testing.rst +++ b/docs/src/Testing.rst @@ -101,7 +101,7 @@ To run only tests requiring virtual detectors use the following command: #in build python -m pytest -m detectorintegration ../python/tests/ -There is a helper test fixture in ``slsDetectorSoftware/python/tests/conftest.py`` called ``test_with_simulators`` that sets up virtual detectors and yields the test for all detectors. The set up is done for every test automatically. +There is a helper test fixture in ``slsDetectorSoftware/python/tests/conftest.py`` called ``session_simulator`` that sets up virtual detectors and yields the test for all detectors. The set up is done for every test automatically. Note that the fixture persist over the entire session e.g. the fixture is setup one detector at a time and runs all tests using this fixture before cleaning up and moving on to the next detector. It saves time if the setup and cleanup is expensive. Example usage: @@ -110,10 +110,13 @@ Example usage: import pytest @pytest.mark.detectorintegration - def test_example_with_simulator(test_with_simulators): + def test_example_with_simulator(session_simulator): # your test code here -If you want to run the test only for a specific test use the parametrized test fixture: +.. Note:: + As the detector is set up only once makes sure to not change the state of the detector in a way that affects other tests. If you want to change the state of the detector make sure to reset it at the end of your test. + +If you want to run the test only for a specific detector use the parametrized test fixture: Example usage: @@ -122,45 +125,10 @@ Example usage: import pytest @pytest.mark.detectorintegration - @pytest.mark.parametrize("setup_parameters", [([""], )], indirect=True) - def test_example_with_specific_simulators(test_with_simulators, setup_parameters): + @pytest.mark.parametrize("session_simulator", [("", , ), ("", , )], indirect=True) + def test_example_with_specific_simulators(session_simulator): # your test code here +.. Note:: + The parametrized test fixture is setup per file and not for the entire session. -There is another helper test fixture in ``slsDetectorSoftware/python/tests/conftest.py`` called ``session_simulator`` that sets up virtual detectors and yields the test for all detectors. The difference with the previous fixture ``test_with_simulators`` is that this fixture will set up one detector at a time and run all the tests using this fixture before cleaning up and moving on to the next detector. It saves time if the setup and cleanup is expensive. - -Example usage: - -.. code-block:: python - - import pytest - - @pytest.mark.detectorintegration - def test_define_reg(session_simulator, request): - """ Test setting define_reg for ctb and xilinx_ctb.""" - det_type, num_interfaces, num_mods, d = session_simulator - assert d is not None - - from slsdet import RegisterAddress - - if det_type in ['ctb', 'xilinx_ctb']: - # your test code here - -For more specific parameters, you can parametrize the fixture like below: - -.. code-block:: python - - import pytest - - @pytest.mark.detectorintegration - @pytest.mark.parametrize( - "session_simulator", - [ - ("ctb", 1, 1), - ("xilinx_ctb", 1, 1), - ], - indirect=True, - ) - def test_define_reg(session_simulator): - det_type, num_interfaces, num_mods, d = session_simulator - # your test code here diff --git a/docs/src/dependencies.rst b/docs/src/dependencies.rst index a4b02f98e..a64d7a219 100644 --- a/docs/src/dependencies.rst +++ b/docs/src/dependencies.rst @@ -16,61 +16,82 @@ the shared libraries these are needed: * CMake >= 3.14 * C++17 compatible compiler. (We test with gcc and clang) -.. note :: +.. note:: For v9.x.x of slsDetectorPackage and older, C++11 compatible compiler. ------------------------ -Python bindings ------------------------ +Additionally the core requires the following dependencies: - * Python >= 3.8 - * pybind11 2.13.6 (packaged in libs) + * fmt 12.1.0 (packaged in libs) + * ZeroMQ 4.3.4 (packaged in libs) + * rapidjson (packaged in libs) -.. note :: +.. note:: - Refer :ref:`pybind11 notes. ` + Both fmt, ZeroMQ and rapidjson are bundled in libs. One does not need to pre-install them on the system. Alternatively, one can fetch fmt and ZeroMQ from GitHub by passing the cmake options ``-DSLS_FETCH_FMT_FROM_GITHUB=ON`` and ``-DSLS_FETCH_ZEROMQ_FROM_GITHUB=ON`` respectively. ------------------------ -ZeroMQ ------------------------ - - * Zeromq 4.3.4 (packaged in libs) - -.. note :: +.. note:: Refer :ref:`zeromq notes. ` ------------------------ -GUI ------------------------ + +------------------------------------ +Dependencies to build Python module +------------------------------------ + +To build the python module the following dependencies are needed: + + * Python >= 3.8 + * pybind11 2.13.6 (packaged in libs) + +.. note:: + + pybind11 is bundled in libs. One does not need to pre-install it on the system. Alternatively, one can fetch pybind11 from GitHub by passing the cmake option ``-DSLS_FETCH_PYBIND11_FROM_GITHUB=ON``. + +.. note:: + + Refer :ref:`pybind11 notes. ` + +------------------------------- +Dependencies to build documentation +------------------------------- + +To build this documentation that you are reading now the following dependencies are needed: + + * Doxygen (to extract C++ classes etc.) + * Breathe (Sphinx plugin to handle doxygen xml) + * Sphinx with sphinx_rtd_theme + +------------------------- +Dependencies to build GUI +------------------------- + +To build the GUI the following dependencies are needed: * Qt 5.9 * Qwt 6.1.5 (packaged in libs) ------------------------ -Moench executables ------------------------ +.. note:: + + Qwt is bundled in libs. One does not need to pre-install it on the system. + +------------------------------------------------------ +Dependencies to build Moench and Jungfrau executables +----------------------------------------------------- + +To build the Moench and Jungfrau executables for preprocessing and calibration the following dependencies are needed: * libtiff ------------------------ -Documentation ------------------------ +-------------------------------------------------- +Dependencies to build Tests +-------------------------------------------------- -The documentation that you are reading now is built with +To build the tests the following dependencies are needed: - * Doxygen (to extract C++ classes etc.) - * Breathe (Sphinx plugin to handle doxygen xml) - * Sphinx with sphinx_rtd_theme + * Catch2 3.4.0 (packaged in libs) ------------------------ -Packaged in libs/ ------------------------ - - * catch2 (unit testing) - * rapidjson (streaming from receiver) - * pybind11 (python bindings) - * qwt (gui plotting) - * libzmq (streaming to/from receiver) \ No newline at end of file +.. note:: + + Catch2 is bundled in libs. One does not need to pre-install it on the system. diff --git a/etc/generate_registerdefs.py b/etc/generate_registerdefs.py new file mode 100644 index 000000000..91178b0e3 --- /dev/null +++ b/etc/generate_registerdefs.py @@ -0,0 +1,113 @@ +import pandas as pd +import argparse + +def Ip_core_name_to_enum_type(ip_core_name : str) -> str: + """Convert IP core name to enum type IPCore.""" + if pd.isna(ip_core_name): + return "IPCore::UNKNOWN" + + return f"IPCore::{ip_core_name.upper()}" + +def create_bitmask_and_offset(from_bit : int, to_bit : int) -> tuple[int, int]: + """Create a bitmask for a register field given the from_bit and to_bit.""" + if from_bit < 0 or to_bit < 0 or from_bit > to_bit or from_bit >= 32 or to_bit >= 32: + raise ValueError(f"Invalid bit range: from_bit={from_bit}, to_bit={to_bit}") + + offset = from_bit + num_bits = to_bit - from_bit + 1 + adress_space = 0xFFFFFFFF # Assuming a 32-bit address space for the bitmask + mask = (adress_space >> (32 - num_bits)) + return mask, offset # to get value of field: (register_value >> offset) & mask, to set value of field: register_value = (register_value & ~(mask << offset)) | ((field_value & mask) << offset) + +def argument_parser(): + parser = argparse.ArgumentParser(description="Generate register definitions from a CSV file.") + parser.add_argument("--csv_file", required=True, help="Path to the CSV file containing register definitions.") + parser.add_argument("--output_header_file", required=True, help="Path to the output header file to write the register definitions to.") + parser.add_argument( + "--overwrite", + action="store_true", + help="Overwrite the output file instead of appending.", + ) + return parser.parse_args() + +# TODO: should be configurable +header = r""" +// clang-format off +#include "RegisterHelperStructs.hpp" + +namespace sls { + +/// @brief Enum for IP cores, value are adresses +enum class IPCore : uint32_t { + MH_RO_SM_AXI = 0, // dummy adresses for now + FHDR_AXI = 1, + AURORA_STATUS = 2, + AURORA_STATUS2 = 3, + PACKETIZERREG = 4, + UNKNOWN = 5 +}; +""" + +postpend = r""" +} // namespace sls +// clang-format on +""" + +def main(): + + args = argument_parser() + + registers_list = pd.read_csv(args.csv_file) + + registers = registers_list.drop_duplicates(subset=["Reg_name", "Address"]) + + file_mode = "w" if args.overwrite else "a" + header_file = open(args.output_header_file, file_mode) + + if args.overwrite: + header_file.write(header) + header_file.write("\n\n") + + header_file.write("// Register definitions") + header_file.write("\n") + + for index, row in registers.iterrows(): + local_address_offset_in_bytes = row["Address"] + register_name = row["Reg_name"] + ip_core_name = row["Interface"] + + define_register_string = ( + f"constexpr Register {register_name}{{" + f"{Ip_core_name_to_enum_type(ip_core_name)}, {hex(int(local_address_offset_in_bytes, 16))}}};" + ) + + header_file.write(f"{define_register_string}\n") + header_file.write("\n") + + header_file.write("\n") + header_file.write("\n") + + header_file.write("// Register fields") + header_file.write("\n") + + for index, row in registers_list.iterrows(): + register_name = row["Reg_name"] + field_name = row["Name"] + from_bit = row["From_bit"] + to_bit = row["To_bit"] + mask, offset = create_bitmask_and_offset(from_bit, to_bit) + + define_registerfield_string = ( + f"constexpr RegisterField {field_name}{{\n" + f" {register_name}, {offset}, {hex(mask)}}};" + ) + + header_file.write(f"{define_registerfield_string}\n") + header_file.write("\n") + + header_file.write(postpend) # TODO: have to take care xof it manually when in append mode - ugly + header_file.close() + + +if __name__ == "__main__": + main() diff --git a/libs/fmt/fmt-12.1.0.tar.gz b/libs/fmt/fmt-12.1.0.tar.gz new file mode 100644 index 000000000..4af9bd165 Binary files /dev/null and b/libs/fmt/fmt-12.1.0.tar.gz differ diff --git a/pyctbgui/pyctbgui/services/Acquisition.py b/pyctbgui/pyctbgui/services/Acquisition.py index 58c972c51..b32b8a659 100644 --- a/pyctbgui/pyctbgui/services/Acquisition.py +++ b/pyctbgui/pyctbgui/services/Acquisition.py @@ -7,7 +7,7 @@ import zmq from PyQt5 import QtWidgets, uic import logging -from slsdet import readoutMode, runStatus, detectorType +from slsdet import readoutMode, runStatus, detectorType, Hz, MHz, kHz from pyctbgui.utils.defines import Defines from pyctbgui.utils.numpyWriter.npy_writer import NumpyFileManager from pyctbgui.utils.numpyWriter.npz_writer import NpzFileWriter @@ -58,12 +58,6 @@ class AcquisitionTab(QtWidgets.QWidget): self.view.spinBoxADCPipeline.setDisabled(True) self.view.spinBoxDBITPhase.setDisabled(True) self.view.spinBoxDBITPipeline.setDisabled(True) - self.view.labelRunF.setText("Run Clock Frequency (kHz):") - self.view.labelDBITF.setText("DBIT Clock Frequency (kHz):") - self.view.labelADCF.setText("ADC Clock Frequency (kHz):") - self.view.spinBoxRunF.setMaximum(250000) - self.view.spinBoxDBITF.setMaximum(250000) - self.view.spinBoxADCF.setMaximum(250000) def connect_ui(self): # For Acquistions Tab @@ -74,8 +68,11 @@ class AcquisitionTab(QtWidgets.QWidget): if self.det.type in [detectorType.CHIPTESTBOARD, detectorType.XILINX_CHIPTESTBOARD]: self.view.spinBoxRunF.editingFinished.connect(self.setRunFrequency) + self.view.comboBoxRunF.currentIndexChanged.connect(self.getRunFrequency) self.view.spinBoxADCF.editingFinished.connect(self.setADCFrequency) + self.view.comboBoxADCF.currentIndexChanged.connect(self.getADCFrequency) self.view.spinBoxDBITF.editingFinished.connect(self.setDBITFrequency) + self.view.comboBoxDBITF.currentIndexChanged.connect(self.getDBITFrequency) if self.det.type == detectorType.CHIPTESTBOARD: self.view.spinBoxADCPhase.editingFinished.connect(self.setADCPhase) self.view.spinBoxADCPipeline.editingFinished.connect(self.setADCPipeline) @@ -195,20 +192,43 @@ class AcquisitionTab(QtWidgets.QWidget): self.view.comboBoxROMode.currentIndexChanged.connect(self.setReadOut) self.getReadout() + + def _getFrequency(self, det_attr, spinbox, spinSetter, combobox): + spinbox.editingFinished.disconnect() + f = getattr(self.det, det_attr).value + unit = combobox.currentIndex() + + if unit == 2: #Hz + spinbox.setValue(f) + elif unit == 1: #kHz + spinbox.setValue(f / 1e3) + else: + spinbox.setValue(f / 1e6) + + spinbox.editingFinished.connect(spinSetter) + + def _setFrequency(self, det_attr, spinbox, combobox, title, getter): + value = spinbox.value() + idx = combobox.currentIndex() + + if idx == 0: + val = MHz(value) + elif idx == 1: + val = kHz(value) + else: + val = Hz((int)(value)) + + try: + setattr(self.det, det_attr, val) + except Exception as e: + QtWidgets.QMessageBox.warning(self.mainWindow, title + " Fail", str(e), QtWidgets.QMessageBox.Ok) + getter() + def getRunFrequency(self): - self.view.spinBoxRunF.editingFinished.disconnect() - self.view.spinBoxRunF.setValue(self.det.runclk) - self.view.spinBoxRunF.editingFinished.connect(self.setRunFrequency) + self._getFrequency('runclk', self.view.spinBoxRunF, self.setRunFrequency, self.view.comboBoxRunF) def setRunFrequency(self): - self.view.spinBoxRunF.editingFinished.disconnect() - try: - self.det.runclk = self.view.spinBoxRunF.value() - except Exception as e: - QtWidgets.QMessageBox.warning(self.mainWindow, "Run Frequency Fail", str(e), QtWidgets.QMessageBox.Ok) - # TODO: handling double event exceptions - self.view.spinBoxRunF.editingFinished.connect(self.setRunFrequency) - self.getRunFrequency() + self._setFrequency('runclk', self.view.spinBoxRunF, self.view.comboBoxRunF, "Run Frequency Fail", self.getRunFrequency) def getTransceiver(self): self.view.spinBoxTransceiver.editingFinished.disconnect() @@ -260,19 +280,10 @@ class AcquisitionTab(QtWidgets.QWidget): self.getDigital() def getADCFrequency(self): - self.view.spinBoxADCF.editingFinished.disconnect() - self.view.spinBoxADCF.setValue(self.det.adcclk) - self.view.spinBoxADCF.editingFinished.connect(self.setADCFrequency) + self._getFrequency('adcclk', self.view.spinBoxADCF, self.setADCFrequency, self.view.comboBoxADCF) def setADCFrequency(self): - self.view.spinBoxADCF.editingFinished.disconnect() - try: - self.det.adcclk = self.view.spinBoxADCF.value() - except Exception as e: - QtWidgets.QMessageBox.warning(self.mainWindow, "ADC Frequency Fail", str(e), QtWidgets.QMessageBox.Ok) - # TODO: handling double event exceptions - self.view.spinBoxADCF.editingFinished.connect(self.setADCFrequency) - self.getADCFrequency() + self._setFrequency('adcclk', self.view.spinBoxADCF, self.view.comboBoxADCF, "ADC Frequency Fail", self.getADCFrequency) def getADCPhase(self): self.view.spinBoxADCPhase.editingFinished.disconnect() @@ -305,19 +316,10 @@ class AcquisitionTab(QtWidgets.QWidget): self.getADCPipeline() def getDBITFrequency(self): - self.view.spinBoxDBITF.editingFinished.disconnect() - self.view.spinBoxDBITF.setValue(self.det.dbitclk) - self.view.spinBoxDBITF.editingFinished.connect(self.setDBITFrequency) + self._getFrequency('dbitclk', self.view.spinBoxDBITF, self.setDBITFrequency, self.view.comboBoxDBITF) def setDBITFrequency(self): - self.view.spinBoxDBITF.editingFinished.disconnect() - try: - self.det.dbitclk = self.view.spinBoxDBITF.value() - except Exception as e: - QtWidgets.QMessageBox.warning(self.mainWindow, "DBit Frequency Fail", str(e), QtWidgets.QMessageBox.Ok) - # TODO: handling double event exceptions - self.view.spinBoxDBITF.editingFinished.connect(self.setDBITFrequency) - self.getDBITFrequency() + self._setFrequency('dbitclk', self.view.spinBoxDBITF, self.view.comboBoxDBITF, "DBit Frequency Fail", self.getDBITFrequency) def getDBITPhase(self): self.view.spinBoxDBITPhase.editingFinished.disconnect() diff --git a/pyctbgui/pyctbgui/ui/acquisition.ui b/pyctbgui/pyctbgui/ui/acquisition.ui index c2d15dff6..ac36336ed 100644 --- a/pyctbgui/pyctbgui/ui/acquisition.ui +++ b/pyctbgui/pyctbgui/ui/acquisition.ui @@ -36,7 +36,7 @@ - + 0 @@ -45,13 +45,13 @@ - 125 + 120 31 - 125 + 120 31 @@ -61,18 +61,17 @@ QAbstractSpinBox::UpDownArrows + + 3 + - 1000 + 999999999.000000000000000 + + + 1.000000000000000 - 0 - - - - - - - Read Out Mode: + 0.000000000000000 @@ -117,13 +116,67 @@ + + + + Read Out Mode: + + + - Run Clock Frequency (MHz): + Run Clock Frequency: + + + + + 60 + 31 + + + + + 60 + 16777215 + + + + + MHz + + + + + kHz + + + + + Hz + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 10 + 20 + + + + @@ -152,13 +205,13 @@ - 125 + 185 32 - 125 + 185 32 @@ -210,6 +263,12 @@ 201 + + + 190 + 0 + + QFrame::StyledPanel @@ -217,16 +276,128 @@ QFrame::Sunken - - - + + + + ADC Pipeline: + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + - 200 - 0 + 10 + 20 + + + + - DBIT Clock Frequency (MHz): + DBIT Clock Phase (a.u.): + + + + + + + + 0 + 0 + + + + + 120 + 31 + + + + + 120 + 31 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 3 + + + 999999999.000000000000000 + + + 1.000000000000000 + + + 0.000000000000000 + + + + + + + ADC Clock Phase (a.u.): + + + + + + + DBIT Pipeline: + + + + + + + Digital Samples: + + + + + + + + 0 + 0 + + + + + 120 + 31 + + + + + 120 + 31 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 3 + + + 999999999.000000000000000 + + + 1.000000000000000 + + + 0.000000000000000 @@ -245,213 +416,7 @@ - ADC Clock Frequency (MHz): - - - - - - - - 0 - 0 - - - - - 125 - 31 - - - - - 125 - 31 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 9999 - - - - - - - DBIT Pipeline: - - - - - - - - 0 - 0 - - - - - 125 - 31 - - - - - 125 - 31 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1000 - - - - - - - - 0 - 0 - - - - - 125 - 31 - - - - - 125 - 31 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - ADC Pipeline: - - - - - - - ADC Clock Phase (a.u.): - - - - - - - DBIT Clock Phase (a.u.): - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 10 - 20 - - - - - - - - - 0 - 0 - - - - - 125 - 31 - - - - - 125 - 31 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1000 - - - - - - - - 0 - 0 - - - - - 125 - 31 - - - - - 125 - 31 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 0 - 0 - - - - - 125 - 31 - - - - - 125 - 31 - - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 9999 + ADC Clock Frequency: @@ -462,7 +427,51 @@ - + + + + + 200 + 0 + + + + DBIT Clock Frequency: + + + + + + + + 60 + 31 + + + + + 60 + 16777215 + + + + + MHz + + + + + kHz + + + + + Hz + + + + + @@ -472,13 +481,13 @@ - 125 + 185 32 - 125 + 185 32 @@ -493,14 +502,60 @@ - - - - Digital Samples: + + + + + 0 + 0 + + + + + 185 + 31 + + + + + 185 + 31 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 9999 - + + + + + 0 + 0 + + + + + 185 + 31 + + + + + 185 + 31 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + @@ -510,13 +565,13 @@ - 125 + 185 32 - 125 + 185 32 @@ -534,6 +589,90 @@ + + + + + 0 + 0 + + + + + 185 + 31 + + + + + 185 + 31 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + 9999 + + + + + + + + 0 + 0 + + + + + 185 + 31 + + + + + 185 + 31 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 60 + 31 + + + + + 60 + 16777215 + + + + + MHz + + + + + kHz + + + + + Hz + + + + diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 13e6abb3d..ffec1ac60 100755 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -13,6 +13,7 @@ pybind11_add_module(_slsdet src/DurationWrapper.cpp src/pedestal.cpp src/bit.cpp + src/frequency.cpp ) target_link_libraries(_slsdet PUBLIC diff --git a/python/slsdet/__init__.py b/python/slsdet/__init__.py index 753267d81..f08e8acab 100755 --- a/python/slsdet/__init__.py +++ b/python/slsdet/__init__.py @@ -34,6 +34,9 @@ scanParameters = _slsdet.scanParameters currentSrcParameters = _slsdet.currentSrcParameters DurationWrapper = _slsdet.DurationWrapper pedestalParameters = _slsdet.pedestalParameters +Hz = _slsdet.Hz +kHz = _slsdet.kHz +MHz = _slsdet.MHz import os def read_version(): diff --git a/python/slsdet/detector.py b/python/slsdet/detector.py index eeed02b79..56e29800f 100755 --- a/python/slsdet/detector.py +++ b/python/slsdet/detector.py @@ -14,11 +14,12 @@ powerIndex = slsDetectorDefs.powerIndex detectorType = slsDetectorDefs.detectorType streamingInterface = slsDetectorDefs.streamingInterface + defs = slsDetectorDefs from .utils import element_if_equal, all_equal, get_set_bits, list_to_bitmask from .utils import Geometry, to_geo, element, reduce_time, is_iterable, hostname_list -from ._slsdet import xy, freeSharedMemory, getUserDetails +from ._slsdet import xy, Hz, freeSharedMemory, getUserDetails from .gaincaps import Mythen3GainCapsWrapper from . import utils as ut from .proxy import JsonProxy, ClkDivProxy, MaxPhaseProxy, ClkFreqProxy, PatLoopProxy, PatNLoopProxy, PatWaitProxy, PatWaitTimeProxy @@ -3441,15 +3442,21 @@ class Detector(CppDetectorApi): @element def runclk(self): """ - [Ctb] Sets Run clock frequency in MHz. \n - [Xilinx Ctb] Sets Run clock frequency in kHz. - """ + [Ctb][Xilinx Ctb] Sets Run clock frequency. + Example + -------- + >>> d.runclk + >>> 10MHz + >>> d.runclk = MHz(5) + >>> d.runclk = Hz(5 * 1000 * 1000) + >>> d.runclk = kHz(2000) + """ return self.getRUNClock() @runclk.setter def runclk(self, freq): - ut.set_using_dict(self.setRUNClock, freq) + ut.set_using_dict(self.setRUNClock, freq) @property @element @@ -3526,10 +3533,16 @@ class Detector(CppDetectorApi): @element def dbitclk(self): """ - [Ctb] Sets clock for latching the digital bits in MHz. \n - [Xilinx Ctb] clock for latching the digital bits in kHz. - """ + [Ctb][Xilinx Ctb] Sets clock for latching the digital bits. + Example + -------- + >>> d.dbitclk + >>> 10MHz + >>> d.dbitclk = MHz(5) + >>> d.dbitclk = Hz(5 * 1000 * 1000) + >>> d.dbitclk = kHz(2000) + """ return self.getDBITClock() @dbitclk.setter @@ -3657,10 +3670,16 @@ class Detector(CppDetectorApi): @element def adcclk(self): """ - [Ctb] Sets ADC clock frequency in MHz. \n - [Xilinx Ctb] Sets ADC clock frequency in kHz. - """ + [Ctb][Xilinx Ctb] Sets ADC clock frequency. + Example + -------- + >>> d.adcclk + >>> 10MHz + >>> d.adcclk = MHz(5) + >>> d.adcclk = Hz(5 * 1000 * 1000) + >>> d.adcclk = kHz(2000) + """ return self.getADCClock() @adcclk.setter @@ -3671,7 +3690,7 @@ class Detector(CppDetectorApi): @element def syncclk(self): """ - [Ctb] Sync clock in MHz. + [Ctb] Sync clock. :setter: Not implemented """ @@ -3704,7 +3723,7 @@ class Detector(CppDetectorApi): [Ctb][Mythen3][Xilinx Ctb] Gets the pattern file name including path of the last pattern uploaded. Returns an empty if nothing was uploaded or via a server default file """ - return self.getPatterFileName() + return self.getPatternFileName() def patternstart(self): """[Ctb][Mythen3][Xilinx Ctb] Starts pattern. """ diff --git a/python/src/detector.cpp b/python/src/detector.cpp index 3d2b3a049..ff14eaafa 100644 --- a/python/src/detector.cpp +++ b/python/src/detector.cpp @@ -1529,23 +1529,31 @@ void init_det(py::module &m) { Detector::setNumberOfAnalogSamples, py::arg(), py::arg() = Positions{}); CppDetectorApi.def("getADCClock", - (Result(Detector::*)(sls::Positions) const) & + (Result(Detector::*)(sls::Positions) const) & Detector::getADCClock, py::arg() = Positions{}); CppDetectorApi.def("setADCClock", - (void (Detector::*)(int, sls::Positions)) & + (void (Detector::*)(defs::Hz, sls::Positions)) & Detector::setADCClock, py::arg(), py::arg() = Positions{}); CppDetectorApi.def("getRUNClock", - (Result(Detector::*)(sls::Positions) const) & + (Result(Detector::*)(sls::Positions) const) & Detector::getRUNClock, py::arg() = Positions{}); CppDetectorApi.def("setRUNClock", - (void (Detector::*)(int, sls::Positions)) & + (void (Detector::*)(defs::Hz, sls::Positions)) & Detector::setRUNClock, py::arg(), py::arg() = Positions{}); + CppDetectorApi.def("getDBITClock", + (Result(Detector::*)(sls::Positions) const) & + Detector::getDBITClock, + py::arg() = Positions{}); + CppDetectorApi.def("setDBITClock", + (void (Detector::*)(defs::Hz, sls::Positions)) & + Detector::setDBITClock, + py::arg(), py::arg() = Positions{}); CppDetectorApi.def("getSYNCClock", - (Result(Detector::*)(sls::Positions) const) & + (Result(Detector::*)(sls::Positions) const) & Detector::getSYNCClock, py::arg() = Positions{}); CppDetectorApi.def("getPowerList", @@ -1646,14 +1654,6 @@ void init_det(py::module &m) { (void (Detector::*)(defs::readoutMode, sls::Positions)) & Detector::setReadoutMode, py::arg(), py::arg() = Positions{}); - CppDetectorApi.def("getDBITClock", - (Result(Detector::*)(sls::Positions) const) & - Detector::getDBITClock, - py::arg() = Positions{}); - CppDetectorApi.def("setDBITClock", - (void (Detector::*)(int, sls::Positions)) & - Detector::setDBITClock, - py::arg(), py::arg() = Positions{}); CppDetectorApi.def("getExternalSamplingSource", (Result(Detector::*)(sls::Positions) const) & Detector::getExternalSamplingSource, @@ -1891,9 +1891,9 @@ void init_det(py::module &m) { Detector::configureTransceiver, py::arg() = Positions{}); CppDetectorApi.def( - "getPatterFileName", + "getPatternFileName", (Result(Detector::*)(sls::Positions) const) & - Detector::getPatterFileName, + Detector::getPatternFileName, py::arg() = Positions{}); CppDetectorApi.def( "setPattern", diff --git a/python/src/frequency.cpp b/python/src/frequency.cpp new file mode 100644 index 000000000..ff08cf878 --- /dev/null +++ b/python/src/frequency.cpp @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: LGPL-3.0-or-other +// Copyright (C) 2021 Contributors to the SLS Detector Package +/* +This file contains Python bindings for the Hz and for conversion to other units from and to string. +*/ +#include "py_headers.h" +#include + +#include "sls/ToString.h" +#include "sls/sls_detector_defs.h" + +namespace py = pybind11; + +constexpr double kHz = 1e3; +constexpr double MHz = 1e6; + +void init_freq(py::module &m) { + + py::class_ Hz(m, "Hz"); + Hz.def(py::init()); + Hz.def_readwrite("value", &slsDetectorDefs::Hz::value); + Hz.def("__repr__", [](const slsDetectorDefs::Hz &f) { + return sls::ToString(f); + }); + Hz.def("__str__", [](const slsDetectorDefs::Hz &f) { + return sls::ToString(f); + }); + + Hz.def(py::self == py::self); + Hz.def("__mul__", [](const slsDetectorDefs::Hz &h, int x) { + return slsDetectorDefs::Hz(h.value * x); + }, py::is_operator()); + Hz.def("__rmul__", [](const slsDetectorDefs::Hz &h, int x) { + return slsDetectorDefs::Hz(h.value * x); + }, py::is_operator()); + Hz.def("__truediv__", [](const slsDetectorDefs::Hz &h, int x) { + return slsDetectorDefs::Hz(h.value / x); + }, py::is_operator()); + Hz.def("__add__", [](const slsDetectorDefs::Hz &a, + const slsDetectorDefs::Hz &b) { + return slsDetectorDefs::Hz(a.value + b.value); + }, py::is_operator()); + Hz.def("__sub__", [](const slsDetectorDefs::Hz &a, + const slsDetectorDefs::Hz &b) { + return slsDetectorDefs::Hz(a.value - b.value); + }, py::is_operator()); + + m.def("kHz", [](double v) { + return slsDetectorDefs::Hz(static_cast(std::round(v * kHz))); + }); + + m.def("MHz", [](double v) { + return slsDetectorDefs::Hz(static_cast(std::round(v * MHz))); + }); +} \ No newline at end of file diff --git a/python/src/main.cpp b/python/src/main.cpp index 214f2b81c..6bc583cbd 100644 --- a/python/src/main.cpp +++ b/python/src/main.cpp @@ -21,6 +21,7 @@ void init_source(py::module &); void init_duration(py::module &); void init_pedestal(py::module &); void init_bit(py::module &); +void init_freq(py::module &); PYBIND11_MODULE(_slsdet, m) { m.doc() = R"pbdoc( @@ -42,6 +43,7 @@ PYBIND11_MODULE(_slsdet, m) { init_duration(m); init_pedestal(m); init_bit(m); + init_freq(m); // init_experimental(m); py::module io = m.def_submodule("io", "Submodule for io"); diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 3b1d65f69..e39c6cf20 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -58,39 +58,56 @@ DEFAULT_SIMULATOR_CONFIGS = [ SIMULATOR_IDS = [f"{det_type}_{num_interface}if_{num_mod}mod" for det_type, num_interface, num_mod in DEFAULT_SIMULATOR_CONFIGS] +''' +for more specific parameters +@pytest.mark.detectorintegration +@pytest.mark.parametrize( + "session_simulator", + [ + ("ctb", 1, 1), + ("xilinx_ctb", 1, 1), + ], + indirect=True, +) +def test_define_reg(session_simulator): + det_type, num_interfaces, num_mods, d = session_simulator +''' @pytest.fixture(scope="session") def session_simulator(request): """ Fixture to start the detector server once and clean up at the end. Expects request.param = (det_type, num_interfaces, num_mods) """ - det_type, num_interfaces, num_mods = request.param - fp = sys.stdout + try: + det_type, num_interfaces, num_mods = request.param + fp = sys.stdout - # set up: once per server - Log(LogLevel.INFOBLUE, - f'---- {det_type} | interfaces={num_interfaces} | modules={num_mods} ----', fp) + # set up: once per server + Log(LogLevel.INFOBLUE, + f'---- {det_type} | interfaces={num_interfaces} | modules={num_mods} ----', fp) - cleanup(fp) - startDetectorVirtualServer(det_type, num_mods, fp, True) - startReceiver(num_mods, fp, True) + cleanup(fp) + startDetectorVirtualServer(det_type, num_mods, fp, True, True) + startReceiver(num_mods, fp, True) - Log(LogLevel.INFOBLUE, f'Waiting for server to start up and connect', fp) - d = loadConfig( - name=det_type, - log_file_fp=fp, - num_mods=num_mods, - num_frames=1, - num_interfaces=num_interfaces, - ) + Log(LogLevel.INFOBLUE, f'Waiting for server to start up and connect', fp) + d = loadConfig( + name=det_type, + log_file_fp=fp, + num_mods=num_mods, + num_frames=1, + num_interfaces=num_interfaces, + ) - loadBasicSettings(name=det_type, d=d, fp=fp) + loadBasicSettings(name=det_type, d=d, fp=fp) - yield det_type, num_interfaces, num_mods, d - - cleanup(fp) + yield det_type, num_interfaces, num_mods, d + cleanup(fp) + except Exception as e: + Log(LogLevel.ERROR, f'Tests Failed.', fp) + cleanup(fp) def pytest_generate_tests(metafunc): if "session_simulator" not in metafunc.fixturenames: @@ -110,62 +127,4 @@ def pytest_generate_tests(metafunc): indirect=True ) -''' -for more specific parameters -@pytest.mark.detectorintegration -@pytest.mark.parametrize( - "session_simulator", - [ - ("ctb", 1, 1), - ("xilinx_ctb", 1, 1), - ], - indirect=True, -) -def test_define_reg(session_simulator): - det_type, num_interfaces, num_mods, d = session_simulator -''' - -#helper fixture for servers -@pytest.fixture(scope='module') -def setup_parameters(request): # only setup once per module if same parameters used for the scopes - try: - servers, nmods = request.param # comes from @pytest.mark.parametrize(..., indirect=True) - return servers, nmods - except AttributeError: - # fallback default if the test did not parametrize - return (['eiger', 'jungfrau', 'mythen3', 'gotthard2', 'ctb', 'moench', 'xilinx_ctb'], 2) - -@pytest.fixture(scope='module') -def test_with_simulators(setup_parameters): - """ Fixture to automatically setup virtual detector servers for testing. """ - - fp = sys.stdout - - servers, nmods = setup_parameters - print("servers:", servers) - print("nmods:", nmods) - try: - for server in servers: - for ninterfaces in range(1,2): - if ninterfaces == 2 and server != 'jungfrau' and server != 'moench': - continue - - msg = f'Starting Python API Tests for {server}' - - if server == 'jungfrau' or server == 'moench': - msg += f' with {ninterfaces} interfaces' - - Log(LogLevel.INFOBLUE, msg, fp) - cleanup(fp) - startDetectorVirtualServer(server, nmods, fp) - startReceiver(nmods, fp) - d = loadConfig(name=server, log_file_fp=fp, num_mods=nmods, num_frames=1, num_interfaces=ninterfaces) - #loadBasicSettings(name=server, d=d, fp=fp) - yield # run test - cleanup(fp) # teardown - except Exception as e: - traceback.print_exc(file=fp) - Log(LogLevel.ERROR, f'Tests Failed.', fp) - cleanup(fp) - diff --git a/python/tests/test_ROI.py b/python/tests/test_ROI.py new file mode 100644 index 000000000..f6117f90d --- /dev/null +++ b/python/tests/test_ROI.py @@ -0,0 +1,59 @@ +import pytest +import sys + +from conftest import session_simulator + +from slsdet import Detector + +from slsdet._slsdet import slsDetectorDefs + +detectorType = slsDetectorDefs.detectorType + +@pytest.mark.detectorintegration +def test_rx_ROI(session_simulator): + """ Test rx_ROI property of Detector class. """ + det_type, num_interfaces, num_mods, d = session_simulator + assert d is not None + + if d.type == detectorType.CHIPTESTBOARD or d.type == detectorType.XILINX_CHIPTESTBOARD: + pytest.skip("Skipping ROI test for ctb/xilinx_ctb detector types.") + + if(d.type == detectorType.MYTHEN3 or d.type == detectorType.GOTTHARD2): + d.rx_roi = (0, 10) + roi = d.rx_roi + assert roi == [(0, 10, -1, -1)] + + #d.rx_roi = [[5,15, 0, 1]] # not allowed for mythen3 + + d.rx_roi = [0,10, -1, -1] + + assert d.rx_roi == [(0,10,-1,-1)] + d.rx_clearroi() + else: + + d.rx_roi = (0, 10, 10, 20) + roi = d.rx_roi + assert roi == [(0, 10, 10, 20)] + + d.rx_roi = [5,15,15,25] + + assert d.rx_roi == [(5,15,15,25)] + + if d.nmod > 1 and (d.type != detectorType.JUNGFRAU) or (d.numinterfaces == 2 and d.type != detectorType.EIGER): + d.rx_roi = [[0,10,0,20], [5,20,410,420]] + + roi = d.rx_roi + assert roi == [(0,10,0,20), (5,20,410,420)] #in same file for jungfrau + + d.rx_clearroi() + roi = d.rx_roi + assert roi == [(-1,-1,-1,-1)] + + + + + + + + + diff --git a/python/tests/test_det_api.py b/python/tests/test_det_api.py index e8d89560b..5c8cef0bc 100644 --- a/python/tests/test_det_api.py +++ b/python/tests/test_det_api.py @@ -12,6 +12,9 @@ from utils_for_test import ( ) from slsdet import Detector +from slsdet._slsdet import slsDetectorDefs + +detectorType = slsDetectorDefs.detectorType @pytest.mark.detectorintegration @@ -396,6 +399,193 @@ def test_patternstart(session_simulator, request): Log(LogLevel.INFOGREEN, f"✅ {request.node.name} passed") + +@pytest.mark.detectorintegration +def test_runclk(session_simulator, request): + """ Test using runclk for ctb and xilinx_ctb.""" + det_type, num_interfaces, num_mods, d = session_simulator + assert d is not None + + from slsdet import Hz, MHz, kHz + + if det_type in ['ctb', 'xilinx_ctb']: + prev_runclk = d.getRUNClock() + + d.runclk + + # invalid value type + with pytest.raises(Exception) as exc_info: + d.runclk = 5e6 + + with pytest.raises(Exception) as exc_info: + d.runclk = 5 * 1000 * 1000 + + with pytest.raises(Exception) as exc_info: + d.runclk = Hz(5e6) + + d.runclk = MHz(15) + assert d.runclk.value == 15_000_000 + + d.runclk = MHz(14.5) + assert d.runclk.value == 14_500_000 + + d.runclk = kHz(15000.5) + assert d.runclk.value == 15_000_500 + + # invalid values from server + # max is 300MHz + with pytest.raises(Exception) as exc_info: + d.runclk = MHz(301) + + # min is 2MHz for ctb and 10MHz for xilinx_ctb + if det_type == 'ctb': + with pytest.raises(Exception) as exc_info: + d.runclk = MHz(1) + else: + with pytest.raises(Exception) as exc_info: + d.runclk = MHz(9) + + c = MHz(2) + for rc in [5, 10, 15, 20]: + d.runclk = rc * c + assert d.runclk.value == 40_000_000 + + for i in range(len(d)): + d.setRUNClock(prev_runclk[i], [i]) + + Log(LogLevel.INFOGREEN, f"✅ {request.node.name} passed") + + +@pytest.mark.detectorintegration +def test_adcclk(session_simulator, request): + """ Test using adcclk for ctb and xilinx_ctb.""" + det_type, num_interfaces, num_mods, d = session_simulator + assert d is not None + + from slsdet import Hz, MHz, kHz + + if det_type in ['ctb', 'xilinx_ctb']: + prev_adcclk = d.getADCClock() + + d.adcclk + + # invalid value type + with pytest.raises(Exception) as exc_info: + d.adcclk = 5e6 + + with pytest.raises(Exception) as exc_info: + d.adcclk = 5 * 1000 * 1000 + + with pytest.raises(Exception) as exc_info: + d.adcclk = Hz(5e6) + + d.adcclk = MHz(15) + assert d.adcclk.value == 15_000_000 + + d.adcclk = MHz(14.5) + assert d.adcclk.value == 14_500_000 + + d.adcclk = kHz(15000.5) + assert d.adcclk.value == 15_000_500 + + # invalid values from server + # max is 300MHz for xilinx and 54 MHz for ctb + if det_type == 'ctb': + with pytest.raises(Exception) as exc_info: + d.adcclk = MHz(66) + else: + with pytest.raises(Exception) as exc_info: + d.adcclk = MHz(301) + + # min is 2MHz for ctb and 10MHz for xilinx_ctb + if det_type == 'ctb': + with pytest.raises(Exception) as exc_info: + d.adcclk = MHz(1) + else: + with pytest.raises(Exception) as exc_info: + d.adcclk = MHz(9) + + c = MHz(2) + for rc in [5, 10, 15, 20]: + d.adcclk = rc * c + assert d.adcclk.value == 40_000_000 + + for i in range(len(d)): + d.setADCClock(prev_adcclk[i], [i]) + + Log(LogLevel.INFOGREEN, f"✅ {request.node.name} passed") + + +@pytest.mark.detectorintegration +def test_dbitclk(session_simulator, request): + """ Test using dbitclk for ctb and xilinx_ctb.""" + det_type, num_interfaces, num_mods, d = session_simulator + assert d is not None + + from slsdet import Hz, MHz, kHz + + if det_type in ['ctb', 'xilinx_ctb']: + prev_dbitclk = d.getDBITClock() + + d.dbitclk + + # invalid value type + with pytest.raises(Exception) as exc_info: + d.dbitclk = 5e6 + + with pytest.raises(Exception) as exc_info: + d.dbitclk = 5 * 1000 * 1000 + + with pytest.raises(Exception) as exc_info: + d.dbitclk = Hz(5e6) + + d.dbitclk = MHz(15) + assert d.dbitclk.value == 15_000_000 + + d.dbitclk = MHz(14.5) + assert d.dbitclk.value == 14_500_000 + + d.dbitclk = kHz(15000.5) + assert d.dbitclk.value == 15_000_500 + + # invalid values from server + # max is 300MHz + with pytest.raises(Exception) as exc_info: + d.dbitclk = MHz(301) + + # min is 2MHz for ctb and 10MHz for xilinx_ctb + if det_type == 'ctb': + with pytest.raises(Exception) as exc_info: + d.dbitclk = MHz(1) + else: + with pytest.raises(Exception) as exc_info: + d.dbitclk = MHz(9) + + c = MHz(2) + for rc in [5, 10, 15, 20]: + d.dbitclk = rc * c + assert d.dbitclk.value == 40_000_000 + + for i in range(len(d)): + d.setDBITClock(prev_dbitclk[i], [i]) + + Log(LogLevel.INFOGREEN, f"✅ {request.node.name} passed") + + + +@pytest.mark.detectorintegration +def test_syncclk(session_simulator, request): + """ Test using syncclk for ctb.""" + det_type, num_interfaces, num_mods, d = session_simulator + assert d is not None + + if det_type in ['ctb']: + d.syncclk + + Log(LogLevel.INFOGREEN, f"✅ {request.node.name} passed") + + + @pytest.mark.detectorintegration def test_v_limit(session_simulator, request): """Test v_limit.""" @@ -715,3 +905,18 @@ def test_dac(session_simulator, request): Log(LogLevel.INFOGREEN, f"✅ {request.node.name} passed") + +@pytest.mark.detectorintegration +@pytest.mark.parametrize("session_simulator",[("moench", 1, 2)],indirect=True) +def test_type(session_simulator): + + d = Detector() + assert d.type == detectorType.MOENCH + + +@pytest.mark.detectorintegration +@pytest.mark.parametrize("session_simulator",[("moench", 1, 2), ("jungfrau", 1, 2)],indirect=True) +def test_numinterfaces(session_simulator): + + d = Detector() + assert d.numinterfaces == 1 \ No newline at end of file diff --git a/python/tests/test_free.py b/python/tests/test_free.py index 4f5393ca9..91e959ef4 100644 --- a/python/tests/test_free.py +++ b/python/tests/test_free.py @@ -17,46 +17,18 @@ sys.path.append(str(scripts_dir)) from slsdet import Detector, Ctb, freeSharedMemory + from utils_for_test import ( Log, LogLevel, - cleanup, - startDetectorVirtualServer, - connectToVirtualServers, SERVER_START_PORTNO ) -''' -scope = module =>Once per test file/module -to share expensive setup like startDetectorVirtualServer -''' -@pytest.fixture(scope="module") -def det_config(): - return { - "name": "ctb", - "num_mods": 1 - } - -@pytest.fixture(scope="module", autouse=True) -def setup_simulator(det_config): - """Fixture to start the detector server once and clean up at the end.""" - fp = sys.stdout - - cleanup(fp) - startDetectorVirtualServer(det_config["name"], det_config["num_mods"], fp) - - Log(LogLevel.INFOBLUE, f'Waiting for server to start up and connect') - connectToVirtualServers(det_config["name"], det_config["num_mods"]) - Log(LogLevel.INFOBLUE, f'Freeing shm before tests') - freeSharedMemory() - - yield # tests run here - - cleanup(fp) - +from conftest import session_simulator @pytest.mark.detectorintegration -def test_exptime_after_free_should_raise(setup_simulator): +@pytest.mark.parametrize("session_simulator",[("ctb", 1, 1)],indirect=True) +def test_exptime_after_free_should_raise(session_simulator): Log(LogLevel.INFOBLUE, f'\nRunning test_exptime_after_free_should_raise') @@ -78,7 +50,8 @@ def free_and_create_shm(): k.hostname = f"localhost:{SERVER_START_PORTNO}" # free and recreate shm, maps to local shm struct @pytest.mark.detectorintegration -def test_exptime_after_not_passing_var_should_raise(setup_simulator): +@pytest.mark.parametrize("session_simulator",[("ctb", 1, 1)],indirect=True) +def test_exptime_after_not_passing_var_should_raise(session_simulator): Log(LogLevel.INFOBLUE, f'\nRunning test_exptime_after_not_passing_var_should_raise') @@ -102,7 +75,8 @@ def free_and_create_shm_passing_ctb_var(k): k.hostname = f"localhost:{SERVER_START_PORTNO}" # free and recreate shm, maps to local shm struct @pytest.mark.detectorintegration -def test_exptime_after_passing_ctb_var_should_raise(setup_simulator): +@pytest.mark.parametrize("session_simulator",[("ctb", 1, 1)],indirect=True) +def test_exptime_after_passing_ctb_var_should_raise(session_simulator): Log(LogLevel.INFOBLUE, f'\nRunning test_exptime_after_passing_ctb_var_should_raise') d = Ctb() # creates multi shm (assuming no shm exists) @@ -125,7 +99,8 @@ def free_and_create_shm_returning_ctb(): return k @pytest.mark.detectorintegration -def test_exptime_after_returning_ctb_should_raise(setup_simulator): +@pytest.mark.parametrize("session_simulator",[("ctb", 1, 1)],indirect=True) +def test_exptime_after_returning_ctb_should_raise(session_simulator): Log(LogLevel.INFOBLUE, f'\nRunning test_exptime_after_returning_ctb_should_raise') d = Ctb() # creates multi shm (assuming no shm exists) @@ -148,7 +123,8 @@ def test_exptime_after_returning_ctb_should_raise(setup_simulator): assert str(exc_info.value) == "Shared memory is invalid or freed. Close resources before access." @pytest.mark.detectorintegration -def test_hostname_twice_acess_old_should_raise(setup_simulator): +@pytest.mark.parametrize("session_simulator",[("ctb", 1, 1)],indirect=True) +def test_hostname_twice_acess_old_should_raise(session_simulator): Log(LogLevel.INFOBLUE, f'\nRunning test_hostname_twice_acess_old_should_raise') d = Ctb() # creates multi shm (assuming no shm exists) diff --git a/python/tests/test_freq.py b/python/tests/test_freq.py new file mode 100644 index 000000000..c4e8b216e --- /dev/null +++ b/python/tests/test_freq.py @@ -0,0 +1,48 @@ +from slsdet import Hz, MHz, kHz + +def test_Hz(): + f = Hz(1) + assert f.value == 1 + f = Hz(1 * 1000) + assert f.value == 1000 + f = MHz(5) + assert f.value == 5_000_000 + f = MHz(0.5) + assert f.value == 500_000 + f = kHz(2.5) + assert f.value == 2500 + f = kHz(5000) + assert f.value == 5_000_000 + +def test_rounding_exact(): + f = MHz(1.234) + assert f.value == round(1_234_000) + + +def test_mul(): + c = MHz(1) + assert (c * 2).value == 2_000_000 + assert (c * 4).value == 4_000_000 + + +def test_rmul(): + c = MHz(1) + assert (2 * c).value == 2_000_000 + assert (4 * c).value == 4_000_000 + + c = c * 2 + assert c.value == 2_000_000 + + for rc in [1, 2, 4, 8]: + c = rc * c + assert c.value == 128_000_000 + + +def test_div(): + c = MHz(1) + assert (c / 2).value == 500_000 + +def test_eq(): + assert MHz(1) == MHz(1) + assert MHz(1) != MHz(2) + assert MHz(1) == kHz(1000) \ No newline at end of file diff --git a/python/tests/test_pythonAPI.py b/python/tests/test_pythonAPI.py deleted file mode 100644 index 9bcc4214b..000000000 --- a/python/tests/test_pythonAPI.py +++ /dev/null @@ -1,51 +0,0 @@ -import pytest -import sys - -from conftest import test_with_simulators - -from slsdet import Detector - -from utils_for_test import ( - Log, - LogLevel, -) - -@pytest.mark.detectorintegration -@pytest.mark.parametrize("setup_parameters", [(["moench"], 2)], indirect=True) -def test_rx_ROI_moench(test_with_simulators, setup_parameters): - """ Test setting and getting rx_ROI property of Detector class for moench. """ - - d = Detector() - d.rx_roi = (0, 10, 10, 20) - roi = d.rx_roi - assert roi == [(0, 10, 10, 20)] - - d.rx_roi = [5,15,15,25] - - assert d.rx_roi == [(5,15,15,25)] - - d.rx_roi = [[0,10,0,20], [5,20,410,420]] - - roi = d.rx_roi - assert roi == [(0,10,0,20), (5,20,410,420)] - - d.rx_clearroi() - roi = d.rx_roi - assert roi == [(-1,-1,-1,-1)] - -@pytest.mark.detectorintegration -@pytest.mark.parametrize("setup_parameters", [(["mythen3"], 1)], indirect=True) -def test_rx_ROI_mythen(test_with_simulators, setup_parameters): - """ Test setting and getting rx_ROI property of Detector class for mythen. """ - - d = Detector() - d.rx_roi = (0, 10) - roi = d.rx_roi - assert roi == [(0, 10, -1, -1)] - - #d.rx_roi = [[5,15, 0, 1]] # not allowed for mythen3 - - d.rx_roi = [0,10, -1, -1] - - assert d.rx_roi == [(0,10,-1,-1)] - diff --git a/slsDetectorServers/CMakeLists.txt b/slsDetectorServers/CMakeLists.txt index 9d1db734c..3a04f2bf9 100644 --- a/slsDetectorServers/CMakeLists.txt +++ b/slsDetectorServers/CMakeLists.txt @@ -1,8 +1,6 @@ # SPDX-License-Identifier: LGPL-3.0-or-other # Copyright (C) 2021 Contributors to the SLS Detector Package - - # Install fake the library install(TARGETS slsProjectCSettings EXPORT "${TARGETS_EXPORT_NAME}" @@ -17,3 +15,5 @@ add_subdirectory(jungfrauDetectorServer) add_subdirectory(mythen3DetectorServer) add_subdirectory(gotthard2DetectorServer) add_subdirectory(moenchDetectorServer) +add_subdirectory(matterhornServer) + diff --git a/slsDetectorServers/ctbDetectorServer/RegisterDefs.h b/slsDetectorServers/ctbDetectorServer/RegisterDefs.h index 02a35dd1e..333f367b6 100644 --- a/slsDetectorServers/ctbDetectorServer/RegisterDefs.h +++ b/slsDetectorServers/ctbDetectorServer/RegisterDefs.h @@ -628,11 +628,14 @@ #define ADC_SLOW_CTRL_DONE_OFST (1) #define ADC_SLOW_CTRL_DONE_MSK (0x00000001 << ADC_SLOW_CTRL_DONE_OFST) +/* Clock Measurement base reg */ +#define PLL_FREQ_MEASURE_REG (0x44 << MEM_MAP_SHIFT) + /* SPI */ #define SPI_CTRL_REG (0x48 << MEM_MAP_SHIFT) - #define SPI_CTRL_RX_EMPTY_BIT 2 - #define SPI_CTRL_CHIPSELECT_BIT 4 - #define SPI_CTRL_NBIT_OFST 16 +#define SPI_CTRL_RX_EMPTY_BIT 2 +#define SPI_CTRL_CHIPSELECT_BIT 4 +#define SPI_CTRL_NBIT_OFST 16 #define SPI_WRITEDATA_REG (0x49 << MEM_MAP_SHIFT) #define SPI_READDATA_REG (0x4A << MEM_MAP_SHIFT) diff --git a/slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer b/slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer index a52a40b9c..b717a96dd 100755 Binary files a/slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer and b/slsDetectorServers/ctbDetectorServer/bin/ctbDetectorServer_developer differ diff --git a/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.c b/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.c index e41569764..895cbed2c 100644 --- a/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.c +++ b/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.c @@ -13,6 +13,7 @@ #include "communication_funcs_UDP.h" #include "loadPattern.h" +#include #include #include #include @@ -65,7 +66,7 @@ uint8_t adcEnableMask_10g = 0xFF; uint32_t transceiverMask = DEFAULT_TRANSCEIVER_MASK; int32_t clkPhase[NUM_CLOCKS] = {}; -uint32_t clkFrequency[NUM_CLOCKS] = {40, 20, 20, 200}; +uint32_t clkFrequency[NUM_CLOCKS] = {}; int dacValues[NDAC_ONLY] = {}; int powerValues[NPWR] = {}; // powerIndex (A->IO, Chip) @@ -557,7 +558,7 @@ void setupDetector() { ALTERA_PLL_SetDefines(PLL_CNTRL_REG, PLL_PARAM_REG, PLL_CNTRL_RCNFG_PRMTR_RST_MSK, PLL_CNTRL_WR_PRMTR_MSK, PLL_CNTRL_PLL_RST_MSK, PLL_CNTRL_ADDR_MSK, - PLL_CNTRL_ADDR_OFST); + PLL_CNTRL_ADDR_OFST, PLL_FREQ_MEASURE_REG); ALTERA_PLL_ResetPLLAndReconfiguration(); resetCore(); @@ -632,10 +633,16 @@ void setupDetector() { DEFAULT_NUM_SAMPLES); // update databytes and allocate ram setNumTransceiverSamples(DEFAULT_NUM_SAMPLES); setNumFrames(DEFAULT_NUM_FRAMES); - setExpTime(DEFAULT_EXPTIME); + initError = setExpTime(DEFAULT_EXPTIME, initErrorMessage); + if (initError == FAIL) + return; setNumTriggers(DEFAULT_NUM_CYCLES); - setPeriod(DEFAULT_PERIOD); - setDelayAfterTrigger(DEFAULT_DELAY); + initError = setPeriod(DEFAULT_PERIOD, initErrorMessage); + if (initError == FAIL) + return; + initError = setDelayAfterTrigger(DEFAULT_DELAY, initErrorMessage); + if (initError == FAIL) + return; setTiming(DEFAULT_TIMING_MODE); setADCEnableMask(BIT32_MSK); setADCEnableMask_10G(BIT32_MSK); @@ -1134,64 +1141,132 @@ int setNumTransceiverSamples(int val) { int getNumTransceiverSamples() { return ntSamples; } -int setExpTime(int64_t val) { +int setExpTime(int64_t val, char *mess) { setPatternWaitInterval(0, val); - // validate for tolerance - int64_t retval = getExpTime(); + // validate + uint64_t arg_clocks = ns_to_clocks(val, clkFrequency[RUN_CLK]); + uint64_t retval_clocks = getPatternWaitClocks(0); + if (arg_clocks != retval_clocks) { + sprintf(mess, + "Failed to set exposure time. Could not set number of clocks " + "to %lld, read %lld\n", + (long long int)arg_clocks, (long long int)retval_clocks); + LOG(logERROR, (mess)); + return FAIL; + } + + // log rounding if any + int64_t retval = getPatternWaitInterval(0); if (val != retval) { + LOG(logWARNING, ("Rounding to %lld ns due to clock frequency\n", + (long long int)retval)); + } + + return OK; +} + +int getExpTime(int64_t *retval, char *mess) { + *retval = getPatternWaitInterval(0); + if (*retval == -1) { + sprintf(mess, "Failed to get exposure time.\n"); + LOG(logERROR, (mess)); return FAIL; } return OK; } -int64_t getExpTime() { return getPatternWaitInterval(0); } - -int setPeriod(int64_t val) { +int setPeriod(int64_t val, char *mess) { if (val < 0) { - LOG(logERROR, ("Invalid period: %lld ns\n", (long long int)val)); + sprintf(mess, "Invalid period: %lld ns\n", (long long int)val); + LOG(logERROR, (mess)); return FAIL; } LOG(logINFO, ("Setting period %lld ns\n", (long long int)val)); - val *= (1E-3 * clkFrequency[SYNC_CLK]); - set64BitReg(val, PERIOD_LSB_REG, PERIOD_MSB_REG); + uint64_t arg_clocks = ns_to_clocks(val, clkFrequency[SYNC_CLK]); + set64BitReg(arg_clocks, PERIOD_LSB_REG, PERIOD_MSB_REG); - // validate for tolerance - int64_t retval = getPeriod(); - val /= (1E-3 * clkFrequency[SYNC_CLK]); - if (val != retval) { + // validate + uint64_t retval_clocks = get64BitReg(PERIOD_LSB_REG, PERIOD_MSB_REG); + if (arg_clocks != retval_clocks) { + sprintf(mess, + "Failed to set period. Could not set number of clocks " + "to %lld, red %lld\n", + (long long int)arg_clocks, (long long int)retval_clocks); + LOG(logERROR, (mess)); return FAIL; } + + // log rounding if any + int64_t retval = 0; + if (getPeriod(&retval, mess) == FAIL) { + return FAIL; + } + if (val != retval) { + LOG(logWARNING, ("Rounding to %lld ns due to clock frequency\n", + (long long int)retval)); + } + return OK; } -int64_t getPeriod() { - return get64BitReg(PERIOD_LSB_REG, PERIOD_MSB_REG) / - (1E-3 * clkFrequency[SYNC_CLK]); +int getPeriod(int64_t *retval, char *mess) { + if (clkFrequency[SYNC_CLK] == 0) { + sprintf(mess, "Cannot get period. Sync clock frequency is 0.\n"); + LOG(logERROR, (mess)); + return FAIL; + } + uint64_t numClocks = get64BitReg(PERIOD_LSB_REG, PERIOD_MSB_REG); + *retval = clocks_to_ns(numClocks, clkFrequency[SYNC_CLK]); + return OK; } -int setDelayAfterTrigger(int64_t val) { +int setDelayAfterTrigger(int64_t val, char *mess) { if (val < 0) { - LOG(logERROR, - ("Invalid delay after trigger: %lld ns\n", (long long int)val)); + sprintf(mess, "Invalid delay after trigger: %lld ns\n", + (long long int)val); + LOG(logERROR, (mess)); return FAIL; } LOG(logINFO, ("Setting delay after trigger %lld ns\n", (long long int)val)); - val *= (1E-3 * clkFrequency[SYNC_CLK]); - set64BitReg(val, DELAY_LSB_REG, DELAY_MSB_REG); + uint64_t arg_clocks = ns_to_clocks(val, clkFrequency[SYNC_CLK]); + set64BitReg(arg_clocks, DELAY_LSB_REG, DELAY_MSB_REG); - // validate for tolerance - int64_t retval = getDelayAfterTrigger(); - val /= (1E-3 * clkFrequency[SYNC_CLK]); - if (val != retval) { + // validate + uint64_t retval_clocks = get64BitReg(DELAY_LSB_REG, DELAY_MSB_REG); + if (arg_clocks != retval_clocks) { + sprintf( + mess, + "Failed to set delay after trigger. Could not set number of clocks " + "to %lld, read %lld\n", + (long long int)arg_clocks, (long long int)retval_clocks); + LOG(logERROR, (mess)); return FAIL; } + + // log rounding if any + int64_t retval = 0; + if (getDelayAfterTrigger(&retval, mess) == FAIL) { + return FAIL; + } + if (val != retval) { + LOG(logWARNING, ("Rounding to %lld ns due to clock frequency\n", + (long long int)retval)); + } + return OK; } -int64_t getDelayAfterTrigger() { - return get64BitReg(DELAY_LSB_REG, DELAY_MSB_REG) / - (1E-3 * clkFrequency[SYNC_CLK]); +int getDelayAfterTrigger(int64_t *retval, char *mess) { + if (clkFrequency[SYNC_CLK] == 0) { + sprintf(mess, + "Cannot get delay after trigger. Sync clock frequency is 0.\n"); + LOG(logERROR, (mess)); + return FAIL; + } + uint64_t numClocks = get64BitReg(DELAY_LSB_REG, DELAY_MSB_REG); + *retval = clocks_to_ns(numClocks, clkFrequency[SYNC_CLK]); + return OK; } int64_t getNumFramesLeft() { @@ -1202,14 +1277,27 @@ int64_t getNumTriggersLeft() { return get64BitReg(CYCLES_LEFT_LSB_REG, CYCLES_LEFT_MSB_REG); } -int64_t getDelayAfterTriggerLeft() { - return get64BitReg(DELAY_LEFT_LSB_REG, DELAY_LEFT_MSB_REG) / - (1E-3 * clkFrequency[SYNC_CLK]); +int getDelayAfterTriggerLeft(int64_t *retval, char *mess) { + if (clkFrequency[SYNC_CLK] == 0) { + sprintf(mess, "Cannot get delay after trigger left. Sync clock " + "frequency is 0.\n"); + LOG(logERROR, (mess)); + return FAIL; + } + uint64_t numClocks = get64BitReg(DELAY_LEFT_LSB_REG, DELAY_LEFT_MSB_REG); + *retval = clocks_to_ns(numClocks, clkFrequency[SYNC_CLK]); + return OK; } -int64_t getPeriodLeft() { - return get64BitReg(PERIOD_LEFT_LSB_REG, PERIOD_LEFT_MSB_REG) / - (1E-3 * clkFrequency[SYNC_CLK]); +int getPeriodLeft(int64_t *retval, char *mess) { + if (clkFrequency[SYNC_CLK] == 0) { + sprintf(mess, "Cannot get period left. Sync clock frequency is 0.\n"); + LOG(logERROR, (mess)); + return FAIL; + } + uint64_t numClocks = get64BitReg(PERIOD_LEFT_LSB_REG, PERIOD_LEFT_MSB_REG); + *retval = clocks_to_ns(numClocks, clkFrequency[SYNC_CLK]); + return OK; } int64_t getFramesFromStart() { @@ -1218,13 +1306,14 @@ int64_t getFramesFromStart() { } int64_t getActualTime() { - return get64BitReg(TIME_FROM_START_LSB_REG, TIME_FROM_START_MSB_REG) / - (1E-3 * CLK_FREQ); + // reg in unit of 100ns + return get64BitReg(TIME_FROM_START_LSB_REG, TIME_FROM_START_MSB_REG) * 100; } int64_t getMeasurementTime() { - return get64BitReg(START_FRAME_TIME_LSB_REG, START_FRAME_TIME_MSB_REG) / - (1E-3 * CLK_FREQ); + // reg in unit of 100ns + return get64BitReg(START_FRAME_TIME_LSB_REG, START_FRAME_TIME_MSB_REG) * + 100; } /* parameters - settings */ @@ -1793,7 +1882,10 @@ int getPowerADC(enum powerIndex index, int *retval, char *mess) { #ifdef VIRTUAL return 0; #endif - *retval = bus_r(POWER_MONITOR_BASE_REG + ((int)adcIndex << MEM_MAP_SHIFT)); + // read register containing the measurement (16-bit signed), sign extend to + // int + *retval = (int)(int16_t)bus_r(POWER_MONITOR_BASE_REG + + ((int)adcIndex << MEM_MAP_SHIFT)); return OK; } @@ -2205,13 +2297,12 @@ int getMaxPhase(enum CLKINDEX ind) { LOG(logERROR, ("Unknown clock index %d to get max phase\n", ind)); return -1; } - int ret = ((double)PLL_VCO_FREQ_MHZ / (double)clkFrequency[ind]) * + int ret = ((double)PLL_VCO_FREQ_HZ / (double)clkFrequency[ind]) * MAX_PHASE_SHIFTS_STEPS; char *clock_names[] = {CLK_NAMES}; - LOG(logDEBUG1, - ("Max Phase Shift (%s): %d (Clock: %d MHz, VCO:%d MHz)\n", - clock_names[ind], ret, clkFrequency[ind], PLL_VCO_FREQ_MHZ)); + LOG(logDEBUG1, ("Max Phase Shift (%s): %d (Clock: %d Hz, VCO:%d Hz)\n", + clock_names[ind], ret, clkFrequency[ind], PLL_VCO_FREQ_HZ)); return ret; } @@ -2247,12 +2338,12 @@ int setFrequency(enum CLKINDEX ind, int val) { return FAIL; } char *clock_names[] = {CLK_NAMES}; - LOG(logINFO, ("\tSetting %s clock (%d) frequency to %d MHz\n", + LOG(logINFO, ("\tSetting %s clock (%d) frequency to %d Hz\n", clock_names[ind], ind, val)); // check adc clk too high if (ind == ADC_CLK && val > MAXIMUM_ADC_CLK) { - LOG(logERROR, ("Frequency %d MHz too high for ADC\n", val)); + LOG(logERROR, ("Frequency %d Hz too high for ADC\n", val)); return FAIL; } @@ -2264,8 +2355,8 @@ int setFrequency(enum CLKINDEX ind, int val) { // Calculate and set output frequency clkFrequency[ind] = - ALTERA_PLL_SetOuputFrequency(ind, PLL_VCO_FREQ_MHZ, val); - LOG(logINFO, ("\t%s clock (%d) frequency set to %d MHz\n", clock_names[ind], + ALTERA_PLL_SetOutputFrequency(ind, PLL_VCO_FREQ_HZ, val); + LOG(logINFO, ("\t%s clock (%d) frequency set to %d Hz\n", clock_names[ind], ind, clkFrequency[ind])); // phase reset by pll (when setting output frequency) @@ -2293,6 +2384,17 @@ int getFrequency(enum CLKINDEX ind) { LOG(logERROR, ("Unknown clock index %d to get frequency\n", ind)); return -1; } +#ifndef VIRTUAL + // get the measured frequency from the firmware + int measuredFreqHz = ALTERA_PLL_getFrequency(ind); + + // checking against 0 here ensures compatibility with old firmware, TODO: + // remove this check at some point + if (measuredFreqHz != 0) { + // Round to nearest Hz. (should we round at all ?) + clkFrequency[ind] = measuredFreqHz; + } +#endif return clkFrequency[ind]; } @@ -2506,10 +2608,21 @@ void *start_timer(void *arg) { return NULL; } - int64_t periodNs = getPeriod(); + int64_t periodNs = 0; + int64_t expUs = 0; + { + char mess[MAX_STR_LENGTH] = {0}; + if (getPeriod(&periodNs, mess) == FAIL) { + LOG(logERROR, ("Failed to get period.\n")); + return NULL; + } + if (getExpTime(&expUs, mess) == FAIL) { + LOG(logERROR, ("Failed to get exposure time.\n")); + return NULL; + } + expUs /= 1000; + } int numFrames = (getNumFrames() * getNumTriggers()); - int64_t expUs = getExpTime() / 1000; - int imageSize = dataBytes; int dataSize = UDP_PACKET_DATA_BYTES; int packetSize = sizeof(sls_detector_header) + dataSize; diff --git a/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.h b/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.h index 3dccdf8c1..e5db4281f 100644 --- a/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.h +++ b/slsDetectorServers/ctbDetectorServer/slsDetectorFunctionList.h @@ -98,10 +98,10 @@ void setNumFrames(int64_t val); int64_t getNumFrames(); void setNumTriggers(int64_t val); int64_t getNumTriggers(); -int setExpTime(int64_t val); -int64_t getExpTime(); -int setPeriod(int64_t val); -int64_t getPeriod(); +int setExpTime(int64_t val, char *mess); +int getExpTime(int64_t *retval, char *mess); +int setPeriod(int64_t val, char *mess); +int getPeriod(int64_t *retval, char *mess); int setNumAnalogSamples(int val); int getNumAnalogSamples(); int setNumDigitalSamples(int val); @@ -111,10 +111,10 @@ int getNumTransceiverSamples(); int64_t getNumFramesLeft(); int64_t getNumTriggersLeft(); -int setDelayAfterTrigger(int64_t val); -int64_t getDelayAfterTrigger(); -int64_t getDelayAfterTriggerLeft(); -int64_t getPeriodLeft(); +int setDelayAfterTrigger(int64_t val, char *mess); +int getDelayAfterTrigger(int64_t *retval, char *mess); +int getDelayAfterTriggerLeft(int64_t *retval, char *mess); +int getPeriodLeft(int64_t *retval, char *mess); int64_t getFramesFromStart(); int64_t getActualTime(); int64_t getMeasurementTime(); diff --git a/slsDetectorServers/ctbDetectorServer/slsDetectorServer_defs.h b/slsDetectorServers/ctbDetectorServer/slsDetectorServer_defs.h index c18d9fc06..61b59b66a 100644 --- a/slsDetectorServers/ctbDetectorServer/slsDetectorServer_defs.h +++ b/slsDetectorServers/ctbDetectorServer/slsDetectorServer_defs.h @@ -5,7 +5,7 @@ #include "sls/sls_detector_defs.h" #define MIN_REQRD_VRSN_T_RD_API 0x181130 -#define REQRD_FRMWR_VRSN 0x230705 +#define REQRD_FRMWR_VRSN 0x260504 #define NUM_HARDWARE_VERSIONS (1) #define HARDWARE_VERSION_NUMBERS \ @@ -44,13 +44,16 @@ #define DEFAULT_VLIMIT (0) #define DEFAULT_TIMING_MODE (AUTO_TIMING) #define DEFAULT_TX_UDP_PORT (0x7e9a) -#define DEFAULT_RUN_CLK (200) // 40 -#define DEFAULT_ADC_CLK (40) // 20 -#define DEFAULT_SYNC_CLK (40) // 20 -#define DEFAULT_DBIT_CLK (200) -#define NS_TO_CLK_CYCLE (1E-3) // ns to MHz +#define DEFAULT_RUN_CLK (80000000) // 80 MHz +#define DEFAULT_ADC_CLK (40000000) // 40 MHz +#define DEFAULT_SYNC_CLK (40000000) // 40 MHz +#define DEFAULT_DBIT_CLK (200000000) // 200 MHz +#define NS_TO_CLK_CYCLE (1E-9) // ns to MHz #define DEFAULT_TRANSCEIVER_MASK (0x3) +#define MIN_CLK_FREQ (2000000) // 2 MHz +#define MAX_CLK_FREQ (300000000) // 300 MHz + #define MAX_TRANSCEIVER_MASK (0xF) #define MAX_TRANSCEIVER_SAMPLES (0xFFFF) @@ -89,8 +92,8 @@ #define BIT32_MSK (0xFFFFFFFF) #define BIT16_MASK (0xFFFF) -#define MAXIMUM_ADC_CLK (65) -#define PLL_VCO_FREQ_MHZ (800) +#define MAXIMUM_ADC_CLK (65000000) // 65 MHz +#define PLL_VCO_FREQ_HZ (800000000) // 800MHz /* Struct Definitions */ typedef struct udp_header_struct { diff --git a/slsDetectorServers/gotthard2DetectorServer/slsDetectorFunctionList.h b/slsDetectorServers/gotthard2DetectorServer/slsDetectorFunctionList.h index 7c442f7b1..124a058f8 100644 --- a/slsDetectorServers/gotthard2DetectorServer/slsDetectorFunctionList.h +++ b/slsDetectorServers/gotthard2DetectorServer/slsDetectorFunctionList.h @@ -163,7 +163,6 @@ int setPhase(enum CLKINDEX ind, int val, int degrees); int getPhase(enum CLKINDEX ind, int degrees); int getMaxPhase(enum CLKINDEX ind); int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); -// void setFrequency(enum CLKINDEX ind, int val); int getFrequency(enum CLKINDEX ind); int getVCOFrequency(enum CLKINDEX ind); int setReadoutSpeed(int val); diff --git a/slsDetectorServers/matterhornServer/CMakeLists.txt b/slsDetectorServers/matterhornServer/CMakeLists.txt new file mode 100644 index 000000000..e4d237e41 --- /dev/null +++ b/slsDetectorServers/matterhornServer/CMakeLists.txt @@ -0,0 +1,62 @@ + + +set(MATTERHORN_SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/src/MatterhornApp.cpp +) + +if(SLS_USE_SIMULATOR) + list(APPEND MATTERHORN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/VirtualMatterhornServer.cpp) + + add_executable(matterhornDetectorServer_virtual ${MATTERHORN_SOURCES}) + + target_include_directories(matterhornDetectorServer_virtual + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../slsSupportLib/include + ${CMAKE_CURRENT_SOURCE_DIR}/../slsDetectorServer_cpp/include) + + target_link_libraries(matterhornDetectorServer_virtual + PUBLIC + slsSupportStatic + slsServerStatic) + + set_target_properties(matterhornDetectorServer_virtual PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) + + install(TARGETS matterhornDetectorServer_virtual + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) +else() + list(APPEND MATTERHORN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/MatterhornServer.cpp) + + add_executable(matterhornDetectorServer ${MATTERHORN_SOURCES}) + + target_include_directories(matterhornDetectorServer + PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/../../slsSupportLib/include + ${CMAKE_CURRENT_SOURCE_DIR}/../slsDetectorServer_cpp/include) + + target_link_libraries(matterhornDetectorServer + PUBLIC + slsSupportStatic + #slsDetectorStatic + slsServerStatic) + + set_target_properties(matterhornDetectorServer PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + ) + + install(TARGETS matterhornDetectorServer + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ) + +endif() + + +#target_compile_definitions(matterhornDetectorServer_virtual +# PUBLIC VIRTUAL STOP_SERVER #what is this stop server should we really have a generic ServerAPP and pass compile options to create server e.g. MatterHorn? +#) + + + + diff --git a/slsDetectorServers/matterhornServer/include/BaseMatterhornServer.h b/slsDetectorServers/matterhornServer/include/BaseMatterhornServer.h new file mode 100644 index 000000000..4ca5e46cc --- /dev/null +++ b/slsDetectorServers/matterhornServer/include/BaseMatterhornServer.h @@ -0,0 +1,113 @@ +#pragma once +#include "DetectorServer.h" +#include "TCPInterface.h" +// #include "communication_funcs.h" +#include "fmt/format.h" +#include "sls/logger.h" +#include "sls/network_utils.h" +#include "sls/sls_detector_defs.h" +#include "sls/versionAPI.h" +#include +#include +#include +#include +#include +#include + +namespace sls { + +/// @brief Base class for Matterhorn Server, can be used to implement a virtual +/// server for testing and actual server +template +class BaseMatterhornServer + : public DetectorServer> { + + public: + /** + * Constructor + * Starts up a Matterhorn server. + * Assembles a Matterhorn server using TCP and UDP detector interfaces + * throws an exception in case of failure + * @param port TCP/IP port number + */ + explicit BaseMatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO) + : DetectorServer>(port) {} + + ~BaseMatterhornServer() = default; + + ReturnCode get_version(ServerInterface &socket); + + ReturnCode get_detector_type(ServerInterface &socket); + + ReturnCode initial_checks(ServerInterface &socket); + + ReturnCode get_num_udp_interfaces(ServerInterface &socket) const; + + /** + * @brief call function corresponding to the function ID received from the + * client and send back the result + * @param function_id the function ID received from the client + * @param socket the socket to send the result back to the client + */ + ReturnCode processFunction(const detFuncs function_id, + ServerInterface &socket); + + private: + static std::string getMatterhornServerVersion(); + + static constexpr uint8_t numUDPInterfaces = + 1; // only one udp per module for now +}; + +template +ReturnCode +BaseMatterhornServer::processFunction(const detFuncs function_id, + ServerInterface &socket) { + + switch (function_id) { + default: + throw RuntimeError( + fmt::format("Function {} not implemented", + getFunctionNameFromEnum((enum detFuncs)function_id))); + } +} + +template +ReturnCode BaseMatterhornServer::get_num_udp_interfaces( + ServerInterface &socket) const { + return static_cast( + socket.sendResult(static_cast(numUDPInterfaces))); +} + +template +ReturnCode +BaseMatterhornServer::get_version(ServerInterface &socket) { + + auto version = getMatterhornServerVersion(); + char version_cstr[MAX_STR_LENGTH]{}; + strncpy(version_cstr, version.c_str(), version.size()); + LOG(TLogLevel::logDEBUG) << "Matterhorn Server Version: " << version; + return static_cast(socket.sendResult( + version_cstr)); // TODO: check what would be possible return codes!!! +} + +template +ReturnCode BaseMatterhornServer::get_detector_type( + ServerInterface &socket) { + int detectortype = slsDetectorDefs::detectorType::MATTERHORN; + return static_cast(socket.sendResult(detectortype)); +} + +template +std::string BaseMatterhornServer::getMatterhornServerVersion() { + return APIMATTERHORN; +} + +template +ReturnCode +BaseMatterhornServer::initial_checks(ServerInterface &socket) { + + return static_cast(this)->initial_checks(socket); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/include/MatterhornServer.h b/slsDetectorServers/matterhornServer/include/MatterhornServer.h new file mode 100644 index 000000000..586818b71 --- /dev/null +++ b/slsDetectorServers/matterhornServer/include/MatterhornServer.h @@ -0,0 +1,27 @@ +#pragma once +#include "BaseMatterhornServer.h" +#include "TCPInterface.h" +#include "sls/sls_detector_defs.h" +#include +#include + +namespace sls { + +class MatterhornServer : public BaseMatterhornServer { + + public: + /** + * Constructor + * Starts up a Matterhorn server. + * Assembles a Matterhorn server using TCP and UDP detector interfaces + * throws an exception in case of failure + * @param port TCP/IP port number + */ + explicit MatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO); + + ~MatterhornServer() = default; + + ReturnCode initial_checks(ServerInterface &socket); +}; + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/include/RegisterDefs.hpp b/slsDetectorServers/matterhornServer/include/RegisterDefs.hpp new file mode 100644 index 000000000..60335ccb4 --- /dev/null +++ b/slsDetectorServers/matterhornServer/include/RegisterDefs.hpp @@ -0,0 +1,240 @@ + +// clang-format off +#include "RegisterHelperStructs.hpp" + +namespace sls { + +/// @brief Enum for IP cores, value are adresses +constexpr enum class IPCore : uint32_t { + MH_RO_SM_AXI = 0, // dummy adresses for now + FHDR_AXI = 1, + AURORA_STATUS = 2, + AURORA_STATUS2 = 3, + PACKETIZERREG = 4, + UNKNOWN = 5 +}; + + +// Register definitions +constexpr Register CTRL_Reg{IPCore::UNKNOWN, 0x0}; + +constexpr Register Status_Reg{IPCore::UNKNOWN, 0x4}; + +constexpr Register FPGAVersionReg{IPCore::UNKNOWN, 0x8}; + +constexpr Register FPGA_GIT_HEAD{IPCore::UNKNOWN, 0xc}; + +constexpr Register FixedPatternReg{IPCore::UNKNOWN, 0x10}; + +constexpr Register ApiVersionReg{IPCore::UNKNOWN, 0x14}; + +constexpr Register Chip_ID_Reg{IPCore::UNKNOWN, 0x18}; + +constexpr Register MH_SM_Ctrl_Reg{IPCore::MH_RO_SM_AXI, 0x0}; + +constexpr Register MH_SM_Exposure_Reg{IPCore::MH_RO_SM_AXI, 0x4}; + +constexpr Register MH_SM_Period_Reg{IPCore::MH_RO_SM_AXI, 0x8}; + +constexpr Register MH_SM_Frames_Reg{IPCore::MH_RO_SM_AXI, 0xc}; + +constexpr Register MH_SM_StoreLength_Reg{IPCore::MH_RO_SM_AXI, 0x10}; + +constexpr Register MH_SM_ResetMHLength_Reg{IPCore::MH_RO_SM_AXI, 0x14}; + +constexpr Register Frame_HDR_Set_Reg{IPCore::FHDR_AXI, 0x0}; + +constexpr Register Frame_HDR_FrameNumLSB_Reg{IPCore::FHDR_AXI, 0x4}; + +constexpr Register Frame_HDR_FrameNumMSB_Reg{IPCore::FHDR_AXI, 0x8}; + +constexpr Register Frame_HDR_TimestampLSB_Reg{IPCore::FHDR_AXI, 0xc}; + +constexpr Register Frame_HDR_TimestampMSB_Reg{IPCore::FHDR_AXI, 0x10}; + +constexpr Register Frame_HDR_ModCoord_LSB_Reg{IPCore::FHDR_AXI, 0x14}; + +constexpr Register Frame_HDR_ModCoord_MSB_Reg{IPCore::FHDR_AXI, 0x18}; + +constexpr Register Frame_HDR_PktctrMax_Reg{IPCore::FHDR_AXI, 0x1c}; + +constexpr Register Aurora_Valid_DW_Reg{IPCore::AURORA_STATUS, 0x0}; + +constexpr Register Aurora_Valid_Bytes_Reg{IPCore::AURORA_STATUS, 0x4}; + +constexpr Register Aurora_Busy_Up_Cycles_Reg{IPCore::AURORA_STATUS, 0x8}; + +constexpr Register Aurora_Hard_Errors_Reg{IPCore::AURORA_STATUS, 0xc}; + +constexpr Register Aurora_Soft_Errors_Reg{IPCore::AURORA_STATUS, 0x10}; + +constexpr Register Aurora_Channel_n_Lanes_Up_Reg{IPCore::AURORA_STATUS, 0x14}; + +constexpr Register Aurora_GT_PLL_Lock_Reg{IPCore::AURORA_STATUS2, 0x0}; + +constexpr Register PktPacketLengthReg{IPCore::PACKETIZERREG, 0xa100}; + +constexpr Register PktNoPacketsReg{IPCore::PACKETIZERREG, 0xa104}; + +constexpr Register PktCtrlReg{IPCore::PACKETIZERREG, 0xa108}; + +constexpr Register PktCoordReg1{IPCore::PACKETIZERREG, 0xa10c}; + +constexpr Register PktCoordReg2{IPCore::PACKETIZERREG, 0xa110}; + + + +// Register fields +constexpr RegisterField Power_VIO{ + CTRL_Reg, 0, 0x1}; + +constexpr RegisterField Power_Vcc_A{ + CTRL_Reg, 1, 0x1}; + +constexpr RegisterField Power_Vcc_B{ + CTRL_Reg, 2, 0x1}; + +constexpr RegisterField Power_Vcc_C{ + CTRL_Reg, 3, 0x1}; + +constexpr RegisterField Power_Vcc_D{ + CTRL_Reg, 4, 0x1}; + +constexpr RegisterField MH_Enable_Enable{ + CTRL_Reg, 5, 0x1}; + +constexpr RegisterField MH_Clk_Enable{ + CTRL_Reg, 6, 0x1}; + +constexpr RegisterField sm_busy{ + Status_Reg, 0, 0x1}; + +constexpr RegisterField FPGACompDate{ + FPGAVersionReg, 0, 0xffffff}; + +constexpr RegisterField FPGADetType{ + FPGAVersionReg, 24, 0xff}; + +constexpr RegisterField FPGA_GIT_HEAD{ + FPGA_GIT_HEAD, 0, 0xffffffff}; + +constexpr RegisterField FixedPattern{ + FixedPatternReg, 0, 0xffffffff}; + +constexpr RegisterField ApiCompDate{ + ApiVersionReg, 0, 0xffffff}; + +constexpr RegisterField ApiDetType{ + ApiVersionReg, 24, 0xff}; + +constexpr RegisterField ChipID{ + Chip_ID_Reg, 0, 0x7}; + +constexpr RegisterField Start_Acquistion{ + MH_SM_Ctrl_Reg, 0, 0x1}; + +constexpr RegisterField Stop_Acquistion{ + MH_SM_Ctrl_Reg, 1, 0x1}; + +constexpr RegisterField MH_Readout_Exposure_Time{ + MH_SM_Exposure_Reg, 0, 0xffffffff}; + +constexpr RegisterField MH_Readout_Period_Time{ + MH_SM_Period_Reg, 0, 0xffffffff}; + +constexpr RegisterField MH_Readout_Frames{ + MH_SM_Frames_Reg, 0, 0xffffffff}; + +constexpr RegisterField MH_SM_StoreLength{ + MH_SM_StoreLength_Reg, 0, 0xffffffff}; + +constexpr RegisterField MH_SM_ResetMHLength{ + MH_SM_ResetMHLength_Reg, 0, 0xffffffff}; + +constexpr RegisterField Frame_Hdr_Set_Framenumber{ + Frame_HDR_Set_Reg, 0, 0x1}; + +constexpr RegisterField Frame_Hdr_Set_Timestamp{ + Frame_HDR_Set_Reg, 1, 0x1}; + +constexpr RegisterField Frame_Hdr_Framenumber_LSB{ + Frame_HDR_FrameNumLSB_Reg, 0, 0xffffffff}; + +constexpr RegisterField Frame_Hdr_Framenumber_MSB{ + Frame_HDR_FrameNumMSB_Reg, 0, 0xffffffff}; + +constexpr RegisterField Frame_Hdr_Timestamp_LSB{ + Frame_HDR_TimestampLSB_Reg, 0, 0xffffffff}; + +constexpr RegisterField Frame_Hdr_Timestamp_MSB{ + Frame_HDR_TimestampMSB_Reg, 0, 0xffffffff}; + +constexpr RegisterField Frame_HDR_ModCoord_LSB{ + Frame_HDR_ModCoord_LSB_Reg, 0, 0xffffffff}; + +constexpr RegisterField Frame_HDR_ModCoord_MSB{ + Frame_HDR_ModCoord_MSB_Reg, 0, 0xffffffff}; + +constexpr RegisterField Frame_HDR_PktctrMax{ + Frame_HDR_PktctrMax_Reg, 0, 0xff}; + +constexpr RegisterField Aurora_Number_Valid_DW{ + Aurora_Valid_DW_Reg, 0, 0xffffffff}; + +constexpr RegisterField Aurora_Valid_Bytes{ + Aurora_Valid_Bytes_Reg, 0, 0xffffffff}; + +constexpr RegisterField Aurora_Busy_Up_Cycles{ + Aurora_Busy_Up_Cycles_Reg, 0, 0xffffffff}; + +constexpr RegisterField Aurora_Hard_Errors{ + Aurora_Hard_Errors_Reg, 0, 0xffffffff}; + +constexpr RegisterField Aurora_Soft_Errors{ + Aurora_Soft_Errors_Reg, 0, 0xffffffff}; + +constexpr RegisterField Aurora_Lanes_Up{ + Aurora_Channel_n_Lanes_Up_Reg, 0, 0xf}; + +constexpr RegisterField Aurora_Channel_Up{ + Aurora_Channel_n_Lanes_Up_Reg, 4, 0x1}; + +constexpr RegisterField Aurora_GT_PLL_Lock{ + Aurora_GT_PLL_Lock_Reg, 0, 0x1}; + +constexpr RegisterField Aurora_GT_PLL_Lock_Counter{ + Aurora_GT_PLL_Lock_Reg, 4, 0x1ffffff}; + +constexpr RegisterField PacketLength1G{ + PktPacketLengthReg, 0, 0xffff}; + +constexpr RegisterField PacketLength10G{ + PktPacketLengthReg, 16, 0xffff}; + +constexpr RegisterField NoPackets1G{ + PktNoPacketsReg, 0, 0x3f}; + +constexpr RegisterField NoPackets10G{ + PktNoPacketsReg, 16, 0x3f}; + +constexpr RegisterField NoServers{ + PktCtrlReg, 0, 0x3f}; + +constexpr RegisterField ServerStart{ + PktCtrlReg, 8, 0x1f}; + +constexpr RegisterField EthInterf{ + PktCtrlReg, 16, 0x1}; + +constexpr RegisterField Coordx{ + PktCoordReg1, 0, 0xffff}; + +constexpr RegisterField Coordy{ + PktCoordReg1, 16, 0xffff}; + +constexpr RegisterField Coordz{ + PktCoordReg2, 0, 0xffff}; + + +} // namespace sls +// clang-format on diff --git a/slsDetectorServers/matterhornServer/include/RegisterHelperStructs.hpp b/slsDetectorServers/matterhornServer/include/RegisterHelperStructs.hpp new file mode 100644 index 000000000..5f561a285 --- /dev/null +++ b/slsDetectorServers/matterhornServer/include/RegisterHelperStructs.hpp @@ -0,0 +1,29 @@ +#include +#include + +namespace sls { + +enum class IPCore : uint32_t; // forward declaration of IPCore enum class + +struct Register { + /// @brief IP core address space + const IPCore ip_core{}; // TODO replace by enum type + + /// @brief Offset of the register in bytes from the base address of the IP + /// core + const uint32_t offset_in_bytes{}; +}; + +struct RegisterField { + /// @brief Register to which the field belongs + const Register register_{}; + + /// @brief Bit position of the least significant bit of the field in the + /// register + const uint32_t bit_position{}; + + /// @brief Bitmask for the field + const uint32_t bitmask{}; +}; + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/include/VirtualMatterhornServer.h b/slsDetectorServers/matterhornServer/include/VirtualMatterhornServer.h new file mode 100644 index 000000000..61cb31bda --- /dev/null +++ b/slsDetectorServers/matterhornServer/include/VirtualMatterhornServer.h @@ -0,0 +1,24 @@ + +#include "BaseMatterhornServer.h" + +namespace sls { + +class VirtualMatterhornServer + : public BaseMatterhornServer { + + public: + /** + * Constructor + * Starts up a virtual Matterhorn server. + * Assembles a virtual Matterhorn server using TCP and UDP detector + * interfaces throws an exception in case of failure + * @param port TCP/IP port number + */ + explicit VirtualMatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO); + + ~VirtualMatterhornServer() = default; + + ReturnCode initial_checks(ServerInterface &socket); +}; + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp b/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp new file mode 100644 index 000000000..a60b328a7 --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp @@ -0,0 +1,113 @@ +#include "CommandLineOptions.h" +#include "VirtualMatterhornServer.h" +#include "sls/logger.h" +#include "sls/sls_detector_exceptions.h" +#include "sls/versionAPI.h" +#include + +#include +#include +#include +#include + +// gettid added in glibc 2.30 +#if __GLIBC__ == 2 && __GLIBC_MINOR__ < 30 +#include +#define gettid() syscall(SYS_gettid) +#endif + +using namespace sls; + +pid_t pid = -1; + +static volatile sig_atomic_t interruption = 0; + +/** + * Control+C Interrupt Handler + * to let all the other process know to exit properly + */ +void sigInterruptHandler(int signal) { + (void)signal; // suppress unused warning if needed + interruption = 1; +} + +// TODO: should be a generic ServerApp for all detectors +int main(int argc, char *argv[]) { + + CommandLineOptions cli; + DetectorServerOptions opts{}; + try { + opts = cli.parse(argc, argv); + } catch (sls::RuntimeError &e) { + return EXIT_FAILURE; + } + if (opts.versionRequested) { + std::cout << fmt::format("MatterhornServer Version: {}", APIMATTERHORN) + << std::endl; + + return EXIT_SUCCESS; + } + + if (opts.helpRequested) { + return EXIT_SUCCESS; + } + + LOG(TLogLevel::logINFOMAGENTA) << cli.printOptions(); + + // Register Ctrl+C handler + std::signal(SIGINT, sigInterruptHandler); + + // handle locally on socket crash + signal(SIGPIPE, SIG_IGN); + + pid = fork(); // fork process for control and stop server + + if (pid == 0) { + // Stop server Process + + LOG(TLogLevel::logINFOBLUE) << "Stop Server [" << opts.port + 1 << "]"; + try { + VirtualMatterhornServer stopServer(opts.port + 1); + while (!interruption) { + pause(); // wait for signal to exit + } + } catch (...) { + kill(getppid(), SIGINT); // tell parent to exit // TODO: should then + // also return EXIT_FAILURE + } + LOG(TLogLevel::logINFOBLUE) + << "Exiting Stop Server [ Tid: " << gettid() << " ]"; + LOG(sls::logINFO) << "Exiting Stop Server"; + } else if (pid > 0) { + // parent + // Control Server Process + + LOG(TLogLevel::logINFOBLUE) << "Control Server [" << opts.port << "]\n"; + + try { + VirtualMatterhornServer server( + opts.port); // TODO use virtual if compiled with virtual + // simulators on + while (!interruption) { + pause(); // wait for signal to exit + } + } catch (...) { + LOG(sls::logINFOBLUE) + << "Exiting Control Server [ Tid: " << gettid() << " ]"; + LOG(sls::logINFO) << "Exiting Detector Server"; + kill(pid, SIGINT); // tell child to exit + waitpid(pid, nullptr, 0); // wait for child to exit + return EXIT_FAILURE; + } + waitpid(pid, nullptr, 0); // wait for child to exit + LOG(sls::logINFOBLUE) + << "Exiting Detector Control Server [ Tid: " << gettid() << " ]"; + LOG(sls::logINFO) << "Exiting Detector Server"; + } else { + LOG(sls::logERROR) + << "Failed to fork process for control and stop server"; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/slsDetectorServers/matterhornServer/src/MatterhornServer.cpp b/slsDetectorServers/matterhornServer/src/MatterhornServer.cpp new file mode 100644 index 000000000..522d5b486 --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/MatterhornServer.cpp @@ -0,0 +1,24 @@ +#include "MatterhornServer.h" + +namespace sls { + +MatterhornServer::MatterhornServer(uint16_t port) + : BaseMatterhornServer(port) { + + // TODO: when do i set the udp mac and ip ? + + // should maybe be part of the constructor? + tcpInterface->startTCPServer(); + + // need a function to setup detector - e.g. set all registers etc. +} + +ReturnCode MatterhornServer::initial_checks(ServerInterface &socket) { + + // TODO: add more checks here, for now just return true to be able to test + // the should check firmware -client compatibility + bool initial_checks_passed = true; + return static_cast(socket.sendResult(initial_checks_passed)); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.cpp b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.cpp new file mode 100644 index 000000000..8c82da20c --- /dev/null +++ b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.cpp @@ -0,0 +1,24 @@ +#include "VirtualMatterhornServer.h" + +namespace sls { + +VirtualMatterhornServer::VirtualMatterhornServer(uint16_t port) + : BaseMatterhornServer(port) { + + udpDetails[0].srcip = LOCALHOSTIP_INT; + + // should maybe be part of the constructor? + tcpInterface->startTCPServer(); + + // need a function to setup detector - e.g. set all registers etc. +} + +ReturnCode VirtualMatterhornServer::initial_checks(ServerInterface &socket) { + + // TODO: add more checks here, for now just return true to be able to test + // the should check firmware -client compatibility + bool initial_checks_passed = true; + return static_cast(socket.sendResult(initial_checks_passed)); +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServer_developer b/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServer_developer index 490129740..28608b061 100755 Binary files a/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServer_developer and b/slsDetectorServers/mythen3DetectorServer/bin/mythen3DetectorServer_developer differ diff --git a/slsDetectorServers/mythen3DetectorServer/slsDetectorFunctionList.h b/slsDetectorServers/mythen3DetectorServer/slsDetectorFunctionList.h index 09f49d589..47bb0e1c4 100644 --- a/slsDetectorServers/mythen3DetectorServer/slsDetectorFunctionList.h +++ b/slsDetectorServers/mythen3DetectorServer/slsDetectorFunctionList.h @@ -195,7 +195,6 @@ int setPhase(enum CLKINDEX ind, int val, int degrees); int getPhase(enum CLKINDEX ind, int degrees); int getMaxPhase(enum CLKINDEX ind); int validatePhaseinDegrees(enum CLKINDEX ind, int val, int retval); -// void setFrequency(enum CLKINDEX ind, int val); int getFrequency(enum CLKINDEX ind); int getVCOFrequency(enum CLKINDEX ind); int getMaxClockDivider(); diff --git a/slsDetectorServers/slsDetectorServer/include/ALTERA_PLL.h b/slsDetectorServers/slsDetectorServer/include/ALTERA_PLL.h index d531db5b0..9c8b88677 100644 --- a/slsDetectorServers/slsDetectorServer/include/ALTERA_PLL.h +++ b/slsDetectorServers/slsDetectorServer/include/ALTERA_PLL.h @@ -20,6 +20,10 @@ void ALTERA_PLL_SetDefines(uint32_t creg, uint32_t preg, uint32_t rprmsk, uint32_t wpmsk, uint32_t prmsk, uint32_t amsk, int aofst, uint32_t wd2msk, int clk2Index); +#elif defined(CHIPTESTBOARDD) +void ALTERA_PLL_SetDefines(uint32_t creg, uint32_t preg, uint32_t rprmsk, + uint32_t wpmsk, uint32_t prmsk, uint32_t amsk, + int aofst, uint32_t freqreg); #else /** * Set Defines @@ -71,8 +75,15 @@ void ALTERA_PLL_SetModePolling(); /** * Calculate and write output frequency * @param clkIndex clock index - * @param pllVCOFreqMhz PLL VCO Frequency in Mhz + * @param pllVCOFreqHz PLL VCO Frequency in Hz * @param value frequency to set to * @param frequency set */ -int ALTERA_PLL_SetOuputFrequency(int clkIndex, int pllVCOFreqMhz, int value); +int ALTERA_PLL_SetOutputFrequency(int clkIndex, int pllVCOFreqHz, int value); + +/** + * get measured clock frequency + */ +#if defined(CHIPTESTBOARDD) +uint32_t ALTERA_PLL_getFrequency(uint32_t clkIDX); +#endif diff --git a/slsDetectorServers/slsDetectorServer/include/common.h b/slsDetectorServers/slsDetectorServer/include/common.h index 7c6139f16..6dbbaae4e 100644 --- a/slsDetectorServers/slsDetectorServer/include/common.h +++ b/slsDetectorServers/slsDetectorServer/include/common.h @@ -2,6 +2,7 @@ // Copyright (C) 2021 Contributors to the SLS Detector Package #pragma once +#include "clogger.h" #include "sls/md5.h" #include // int64_t #include @@ -21,6 +22,19 @@ enum numberMode { DEC, HEX }; enum PROGRAM_INDEX { PROGRAM_FPGA, PROGRAM_KERNEL, PROGRAM_SERVER }; +#define NS_PER_SEC 1000000000ULL +#define HALF_NS_PER_SEC (NS_PER_SEC / 2) +static inline uint64_t ns_to_clocks(uint64_t t, uint32_t freq_hz) { + return (t * (uint64_t)freq_hz + HALF_NS_PER_SEC) / NS_PER_SEC; +} +static inline uint64_t clocks_to_ns(uint64_t clocks, uint32_t freq_hz) { + if (freq_hz == 0) { + LOG(logERROR, ("Frequency is 0, cannot convert clocks to ns\n")); + return (uint64_t)-1; + } + return (clocks * (uint64_t)NS_PER_SEC + freq_hz / 2) / freq_hz; +} + /** * Convert a value from a range to a different range (eg voltage to dac or vice * versa) diff --git a/slsDetectorServers/slsDetectorServer/src/ALTERA_PLL.c b/slsDetectorServers/slsDetectorServer/src/ALTERA_PLL.c index 0bf12e61f..eed289ae5 100644 --- a/slsDetectorServers/slsDetectorServer/src/ALTERA_PLL.c +++ b/slsDetectorServers/slsDetectorServer/src/ALTERA_PLL.c @@ -130,7 +130,8 @@ uint32_t ALTERA_PLL_Cntrl_WrPrmtrMask = 0x0; #if defined(JUNGFRAUD) uint32_t ALTERA_PLL_Cntrl_DBIT_PLL_WrPrmtrMask = 0x0; int ALTERA_PLL_Cntrl_DBIT_ClkIndex = 0; - +#elif defined(CHIPTESTBOARDD) +uint32_t ALTERA_PLL_FREQ_MEASURE_BASE = 0x0; #endif uint32_t ALTERA_PLL_Cntrl_PLLRstMask = 0x0; uint32_t ALTERA_PLL_Cntrl_AddrMask = 0x0; @@ -150,6 +151,19 @@ void ALTERA_PLL_SetDefines(uint32_t creg, uint32_t preg, uint32_t rprmsk, ALTERA_PLL_Cntrl_DBIT_PLL_WrPrmtrMask = wd2msk; ALTERA_PLL_Cntrl_DBIT_ClkIndex = clk2Index; } +#elif defined(CHIPTESTBOARDD) +void ALTERA_PLL_SetDefines(uint32_t creg, uint32_t preg, uint32_t rprmsk, + uint32_t wpmsk, uint32_t prmsk, uint32_t amsk, + int aofst, uint32_t freqreg) { + ALTERA_PLL_Cntrl_Reg = creg; + ALTERA_PLL_Param_Reg = preg; + ALTERA_PLL_Cntrl_RcnfgPrmtrRstMask = rprmsk; + ALTERA_PLL_Cntrl_WrPrmtrMask = wpmsk; + ALTERA_PLL_Cntrl_PLLRstMask = prmsk; + ALTERA_PLL_Cntrl_AddrMask = amsk; + ALTERA_PLL_Cntrl_AddrOfst = aofst; + ALTERA_PLL_FREQ_MEASURE_BASE = freqreg; +} #else void ALTERA_PLL_SetDefines(uint32_t creg, uint32_t preg, uint32_t rprmsk, uint32_t wpmsk, uint32_t prmsk, uint32_t amsk, @@ -269,12 +283,12 @@ void ALTERA_PLL_SetModePolling() { ALTERA_PLL_MODE_PLLNG_MD_VAL, 0); } -int ALTERA_PLL_SetOuputFrequency(int clkIndex, int pllVCOFreqMhz, int value) { - LOG(logDEBUG1, ("C%d: Setting output frequency to %d (pllvcofreq: %dMhz)\n", - clkIndex, value, pllVCOFreqMhz)); +int ALTERA_PLL_SetOutputFrequency(int clkIndex, int pllVCOFreqHz, int value) { + LOG(logDEBUG1, ("C%d: Setting output frequency to %d (pllvcofreq: %dHz)\n", + clkIndex, value, pllVCOFreqHz)); - // calculate output frequency - uint32_t total_div = (float)pllVCOFreqMhz / (float)value; + // calculate output frequency, round to next closest integer division + uint32_t total_div = (pllVCOFreqHz + value / 2) / value; // assume 50% duty cycle uint32_t low_count = total_div / 2; @@ -307,11 +321,24 @@ int ALTERA_PLL_SetOuputFrequency(int clkIndex, int pllVCOFreqMhz, int value) { // as adc clock is stopped temporarily when resetting pll) ALTERA_PLL_ResetPLL(); - /*double temp = ((double)pllVCOFreqMhz / (double)(low_count + high_count)); + /*double temp = ((double)pllVCOFreqHz / (double)(low_count + high_count)); if ((temp - (int)temp) > 0.0001) { temp += 0.5; } return (int)temp; */ + +#if defined(CHIPTESTBOARDD) && !defined(VIRTUAL) + // wait for firmware to measure the actual frequency + usleep(2 * 1000 * 1000); + value = ALTERA_PLL_getFrequency(clkIndex); + LOG(logDEBUG1, ("Frequency is %d\n", value)); +#endif return value; } + +#if defined(CHIPTESTBOARDD) +uint32_t ALTERA_PLL_getFrequency(uint32_t clk_index) { + return bus_r(ALTERA_PLL_FREQ_MEASURE_BASE + clk_index * 2); +} +#endif \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer/src/XILINX_PLL.c b/slsDetectorServers/slsDetectorServer/src/XILINX_PLL.c index 8adec26be..09f31df52 100644 --- a/slsDetectorServers/slsDetectorServer/src/XILINX_PLL.c +++ b/slsDetectorServers/slsDetectorServer/src/XILINX_PLL.c @@ -11,9 +11,9 @@ // leave some things away) // clang-format off -#define XILINX_PLL_INPUT_FREQ (100000) // 100 MHz -#define XILINX_PLL_MIN_FREQ (10000) -#define XILINX_PLL_MAX_FREQ (250000) +#define XILINX_PLL_INPUT_FREQ (100000000) // 100 MHz +#define XILINX_PLL_MIN_FREQ (10000000) +#define XILINX_PLL_MAX_FREQ (250000000) #define XILINX_PLL_MAX_CLK_DIV (256) #define XILINX_PLL_NUM_CLKS (7) #define XILINX_PLL_MAX_NUM_CLKS_FOR_GET (3) @@ -71,14 +71,14 @@ // clang-format on -// freq in kHz !! +// freq in Hz !! int XILINX_PLL_setFrequency(uint32_t clk_index, uint32_t freq) { if (clk_index >= XILINX_PLL_NUM_CLKS) { LOG(logERROR, ("XILINX_PLL: Invalid clock index %d\n", clk_index)); return 1; } if (freq < XILINX_PLL_MIN_FREQ || freq > XILINX_PLL_MAX_FREQ) { - LOG(logERROR, ("XILINX_PLL: Frequency %d kHz is out of range\n", freq)); + LOG(logERROR, ("XILINX_PLL: Frequency %d Hz is out of range\n", freq)); return 1; } @@ -160,11 +160,7 @@ uint32_t XILINX_PLL_getFrequency(uint32_t clk_index) { clk_index -= XILINX_PLL_MEASURE_BASE_ADDR0_MAX_CLKS; base_addr = XILINX_PLL_MEASURE_BASE_ADDR1; } - uint32_t addr = base_addr + clk_index * XILINX_PLL_MEASURE_WIDTH; - uint32_t counter_val = bus_r_csp2(addr); - // Hz => round to nearest kHz - uint32_t freq_kHz = (counter_val + 500) / 1000; // round to nearest kHz - return freq_kHz; + return bus_r_csp2(base_addr + clk_index * XILINX_PLL_MEASURE_WIDTH); } bool XILINX_PLL_isLocked() { diff --git a/slsDetectorServers/slsDetectorServer/src/loadPattern.c b/slsDetectorServers/slsDetectorServer/src/loadPattern.c index 929460897..154094b87 100644 --- a/slsDetectorServers/slsDetectorServer/src/loadPattern.c +++ b/slsDetectorServers/slsDetectorServer/src/loadPattern.c @@ -11,7 +11,7 @@ #ifdef MYTHEN3D extern enum TLogLevel trimmingPrint; -extern uint32_t clkDivider[]; +extern int getFrequency(enum CLKINDEX ind); #endif #if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) extern uint32_t clkFrequency[]; @@ -277,6 +277,14 @@ int validate_getPatternWaitClocksAndInterval(char *message, int level, *waittime = getPatternWaitClocks(level); } else { *waittime = getPatternWaitInterval(level); + if (*waittime == (uint64_t)-1) { + sprintf( + message, + "Cannot get pattern wait interval for level %d. runclk is 0.\n", + level); + LOG(logERROR, (message)); + return FAIL; + } } return OK; } @@ -297,17 +305,17 @@ uint64_t getPatternWaitClocks(int level) { uint64_t getPatternWaitInterval(int level) { uint64_t numClocks = getPatternWaitClocks(level); - int runclk = 0; + uint32_t runclk = 0; #if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) runclk = clkFrequency[RUN_CLK]; #elif MYTHEN3D - runclk = clkDivider[SYSTEM_C0]; + runclk = getFrequency(SYSTEM_C0); #endif if (runclk == 0) { LOG(logERROR, ("runclk is 0. Cannot divide by 0. Returning -1.\n")); return -1; } - return numClocks / (NS_TO_CLK_CYCLE * runclk); + return clocks_to_ns(numClocks, runclk); } int validate_setPatternWaitClocksAndInterval(char *message, int level, @@ -321,27 +329,50 @@ int validate_setPatternWaitClocksAndInterval(char *message, int level, return FAIL; } - uint64_t retval = 0; if (clocks) { setPatternWaitClocks(level, waittime); // validate result - retval = getPatternWaitClocks(level); + uint64_t retval = getPatternWaitClocks(level); LOG(logDEBUG1, ("Pattern wait time in clocks (level:%d) retval: %d\n", level, (long long int)retval)); - } else { - setPatternWaitInterval(level, waittime); - // validate result - retval = getPatternWaitInterval(level); - LOG(logDEBUG1, ("Pattern wait time (level:%d) retval: %d\n", level, - (long long int)retval)); + + int ret = OK; + char mode[128]; + memset(mode, 0, sizeof(mode)); + sprintf(mode, "set pattern Loop %d wait time (clocks)", level); + validate64(&ret, message, waittime, retval, mode, DEC); + return ret; } - int ret = OK; - char mode[128]; - memset(mode, 0, sizeof(mode)); - sprintf(mode, "set pattern Loop %d wait time", level); - validate64(&ret, message, waittime, retval, mode, DEC); - return ret; + // interval + setPatternWaitInterval(level, waittime); + + // validate + uint32_t runclk = 0; +#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + runclk = clkFrequency[RUN_CLK]; +#elif MYTHEN3D + runclk = getFrequency(SYSTEM_C0); +#endif + uint64_t arg_clocks = ns_to_clocks(waittime, runclk); + uint64_t retval_clocks = getPatternWaitClocks(level); + if (arg_clocks != retval_clocks) { + sprintf(message, + "Failed to set pattern loop %d wait interval. Could not set " + "number of clocks to %lld, read %lld\n", + level, (long long int)arg_clocks, (long long int)retval_clocks); + LOG(logERROR, (message)); + return FAIL; + } + + // log rounding if any + uint64_t retval = getPatternWaitInterval(level); + if (waittime != retval) { + LOG(logWARNING, ("Rounding to %lld ns due to clock frequency\n", + (long long int)retval)); + } + + return OK; } void setPatternWaitClocks(int level, uint64_t t) { @@ -375,13 +406,13 @@ void setPatternWaitInterval(int level, uint64_t t) { #endif ("Setting Pattern Wait Time (level:%d) :%lld ns\n", level, (long long int)t)); - int runclk = 0; + uint32_t runclk = 0; #if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) runclk = clkFrequency[RUN_CLK]; #elif MYTHEN3D - runclk = clkDivider[SYSTEM_C0]; + runclk = getFrequency(SYSTEM_C0); #endif - uint64_t numClocks = t * (NS_TO_CLK_CYCLE * runclk); + uint64_t numClocks = ns_to_clocks(t, runclk); setPatternWaitClocks(level, numClocks); } diff --git a/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c b/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c index 5b708a09f..f4d9d930f 100644 --- a/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c +++ b/slsDetectorServers/slsDetectorServer/src/slsDetectorServer_funcs.c @@ -2402,7 +2402,11 @@ int get_exptime(int file_des) { "for this detector\n"); LOG(logERROR, (mess)); } else { +#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + ret = getExpTime(&retval, mess); +#else retval = getExpTime(); +#endif LOG(logDEBUG1, ("retval exptime %lld ns\n", (long long int)retval)); } #endif @@ -2472,6 +2476,9 @@ int set_exptime(int file_des) { "for this detector\n"); LOG(logERROR, (mess)); } else { +#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + ret = setExpTime(val, mess); +#else ret = setExpTime(val); int64_t retval = getExpTime(); LOG(logDEBUG1, ("retval exptime %lld ns\n", (long long int)retval)); @@ -2482,6 +2489,7 @@ int set_exptime(int file_des) { (long long int)val, (long long int)retval); LOG(logERROR, (mess)); } +#endif } #endif } @@ -2494,7 +2502,11 @@ int get_period(int file_des) { int64_t retval = -1; // get only +#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + ret = getPeriod(&retval, mess); +#else retval = getPeriod(); +#endif LOG(logDEBUG1, ("retval period %lld ns\n", (long long int)retval)); return Server_SendResult(file_des, INT64, &retval, sizeof(retval)); } @@ -2510,6 +2522,9 @@ int set_period(int file_des) { // only set if (Server_VerifyLock() == OK) { +#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + ret = setPeriod(arg, mess); +#else ret = setPeriod(arg); int64_t retval = getPeriod(); LOG(logDEBUG1, ("retval period %lld ns\n", (long long int)retval)); @@ -2518,6 +2533,7 @@ int set_period(int file_des) { (long long int)arg, (long long int)retval); LOG(logERROR, (mess)); } +#endif } return Server_SendResult(file_des, INT64, NULL, 0); } @@ -2533,7 +2549,11 @@ int get_delay_after_trigger(int file_des) { functionNotImplemented(); #else // get only +#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + ret = getDelayAfterTrigger(&retval, mess); +#else retval = getDelayAfterTrigger(); +#endif LOG(logDEBUG1, ("retval delay after trigger %lld ns\n", (long long int)retval)); #endif @@ -2557,6 +2577,9 @@ int set_delay_after_trigger(int file_des) { #else // only set if (Server_VerifyLock() == OK) { +#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + ret = setDelayAfterTrigger(arg, mess); +#else ret = setDelayAfterTrigger(arg); int64_t retval = getDelayAfterTrigger(); LOG(logDEBUG1, @@ -2568,6 +2591,7 @@ int set_delay_after_trigger(int file_des) { (long long int)arg, (long long int)retval); LOG(logERROR, (mess)); } +#endif } #endif return Server_SendResult(file_des, INT64, NULL, 0); @@ -2802,7 +2826,11 @@ int get_period_left(int file_des) { functionNotImplemented(); #else // get only +#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + ret = getPeriodLeft(&retval, mess); +#else retval = getPeriodLeft(); +#endif LOG(logDEBUG1, ("retval period left %lld ns\n", (long long int)retval)); #endif return Server_SendResult(file_des, INT64, &retval, sizeof(retval)); @@ -2819,7 +2847,11 @@ int get_delay_after_trigger_left(int file_des) { functionNotImplemented(); #else // get only +#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + ret = getDelayAfterTriggerLeft(&retval, mess); +#else retval = getDelayAfterTriggerLeft(); +#endif LOG(logDEBUG1, ("retval delay after trigger left %lld ns\n", (long long int)retval)); #endif @@ -3306,10 +3338,6 @@ int set_pattern_wait_clocks(int file_des) { if (ret == OK) { ret = validate_getPatternWaitClocksAndInterval(mess, loopLevel, &retval, 1); - if ((int64_t)timeval != GET_FLAG) { - validate64(&ret, mess, (int64_t)timeval, retval, - "set pattern wait clocks", DEC); - } } } #endif @@ -5693,11 +5721,9 @@ int set_clock_frequency(int file_des) { case ADC_CLOCK: c = ADC_CLK; break; -#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) case DBIT_CLOCK: c = DBIT_CLK; break; -#endif case RUN_CLOCK: c = RUN_CLK; break; @@ -5718,26 +5744,38 @@ int set_clock_frequency(int file_des) { (int)c); if (getFrequency(c) == val) { - LOG(logINFO, ("Same %s: %d %s\n", modeName, val, - myDetectorType == GOTTHARD2 ? "Hz" : "MHz")); - } else { + LOG(logINFO, ("Same %s: %d %s\n", modeName, val, "Hz")); + } else if (val < MIN_CLK_FREQ || val > MAX_CLK_FREQ) { + ret = FAIL; + sprintf(mess, + "Cannot set frequency to %f MHz. Frequency outside " + "limits (%f - %f MHz)\n", + val / 1e6, MIN_CLK_FREQ / 1e6, MAX_CLK_FREQ / 1e6); + LOG(logERROR, (mess)); + } +#ifdef CHIPTESTBOARDD + else if (ind == ADC_CLOCK && (val > MAXIMUM_ADC_CLK)) { + ret = FAIL; + sprintf(mess, + "Cannot set ADC clock frequency to %f MHz. Frequency " + "outside limits (<= %f MHz)\n", + val / 1e6, MAXIMUM_ADC_CLK / 1e6); + LOG(logERROR, (mess)); + } +#endif + else { int ret = setFrequency(c, val); if (ret == FAIL) { - sprintf(mess, "Could not set %s to %d %s\n", modeName, val, - myDetectorType == XILINX_CHIPTESTBOARD ? "kHz" - : "MHz"); + sprintf(mess, "Could not set %s to %f MHz\n", modeName, + val / 1e6); LOG(logERROR, (mess)); } else { int retval = getFrequency(c); LOG(logDEBUG1, - ("retval %s: %d %s\n", modeName, retval, - myDetectorType == XILINX_CHIPTESTBOARD ? "kHz" - : "MHz")); -#if !defined(XILINX_CHIPTESTBOARDD) - // XCTB will give the actual frequency, which is not + ("retval %s: %f MHz\n", modeName, retval / 1e6)); + // both CTB's will give the actual frequency, which is not // 100% identical to the set frequency validate(&ret, mess, val, retval, modeName, DEC); -#endif } } } @@ -5790,14 +5828,8 @@ int get_clock_frequency(int file_des) { if (ret == OK) { retval = getFrequency(c); char *clock_names[] = {CLK_NAMES}; - LOG(logDEBUG1, - ("retval %s clock (%d) frequency: %d %s\n", clock_names[c], (int)c, - retval, - myDetectorType == XILINX_CHIPTESTBOARD - ? "kHz" - : (myDetectorType == GOTTHARD2 || myDetectorType == MYTHEN3 - ? "Hz" - : "MHz"))); + LOG(logDEBUG1, ("retval %s clock (%d) frequency: %d %s\n", + clock_names[c], (int)c, retval, "Hz")); } #endif return Server_SendResult(file_des, INT32, &retval, sizeof(retval)); @@ -7029,6 +7061,11 @@ int get_receiver_parameters(int file_des) { // exptime #ifdef MYTHEN3D i64 = 0; +#elif defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + if (getExpTime(&i64, mess) == FAIL) { + sprintf(mess, "Could not get exposure time.\n"); + return sendError(file_des); + } #else i64 = getExpTime(); #endif @@ -7037,7 +7074,15 @@ int get_receiver_parameters(int file_des) { return printSocketReadError(); // period + i64 = 0; +#if defined(CHIPTESTBOARDD) || defined(XILINX_CHIPTESTBOARDD) + if (getPeriod(&i64, mess) == FAIL) { + sprintf(mess, "Could not get period.\n"); + return sendError(file_des); + } +#else i64 = getPeriod(); +#endif n += sendData(file_des, &i64, sizeof(i64), INT64); if (n < 0) return printSocketReadError(); @@ -10894,13 +10939,6 @@ int set_pattern_wait_interval(int file_des) { if (Server_VerifyLock() == OK) { ret = validate_setPatternWaitClocksAndInterval(mess, loopLevel, timeval, 0); - if (ret == OK) { - uint64_t retval = 0; - ret = validate_getPatternWaitClocksAndInterval(mess, loopLevel, - &retval, 0); - validate64(&ret, mess, (int64_t)timeval, retval, - "set pattern wait interval", DEC); - } } #endif @@ -11057,9 +11095,11 @@ int spi_read(int file_des) { #elif defined(CHIPTESTBOARDD) // set spi to 8 bit per word (-1 comes from the firmware), set chipselect bus_w(SPI_CTRL_REG, + ((8 - 1) << SPI_CTRL_NBIT_OFST) + (1 << SPI_CTRL_CHIPSELECT_BIT)); for (int i = 0; i < n_bytes + 1; ++i) { // TODO: should we make bus_w to this address blocking in the firmware + // // to remove usleep ? bus_w(SPI_WRITEDATA_REG, local_tx[i]); usleep_bf(BFIN_SPI_WAIT_uSECONDS); diff --git a/slsDetectorServers/slsDetectorServer_cpp/CMakeLists.txt b/slsDetectorServers/slsDetectorServer_cpp/CMakeLists.txt new file mode 100644 index 000000000..9060b99f2 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/CMakeLists.txt @@ -0,0 +1,64 @@ +set(SOURCES + ${CMAKE_CURRENT_SOURCE_DIR}/src/TCPInterface.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/CommandLineOptions.cpp +) + +add_library(slsServerObject OBJECT + ${SOURCES} +) + +target_include_directories(slsServerObject PUBLIC + "$" + "$" +) + +target_link_libraries(slsServerObject + PUBLIC + slsProjectOptions + slsSupportStatic + PRIVATE + slsProjectWarnings +) + +set(DETECTOR_LIBRARY_TARGETS slsServerObject) + +set(PUBLICHEADERS + ${CMAKE_CURRENT_SOURCE_DIR}/include/TCPInterface.h +) + +#Shared library +if(SLS_BUILD_SHARED_LIBRARIES) + add_library(slsServerShared SHARED $) + target_link_libraries(slsServerShared PUBLIC slsServerObject) + set_target_properties(slsServerShared PROPERTIES + VERSION ${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH} + SOVERSION ${PACKAGE_VERSION_MAJOR} + LIBRARY_OUTPUT_NAME SlsServer + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + PUBLIC_HEADER "${PUBLICHEADERS}" + ) + list(APPEND DETECTOR_LIBRARY_TARGETS slsServerShared) +endif(SLS_BUILD_SHARED_LIBRARIES) + +#Static library +add_library(slsServerStatic STATIC $) +target_link_libraries(slsServerStatic PUBLIC slsServerObject) + +set_target_properties(slsServerStatic PROPERTIES + ARCHIVE_OUTPUT_NAME SlsServerStatic + ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin + PUBLIC_HEADER "${PUBLICHEADERS}" +) +list(APPEND DETECTOR_LIBRARY_TARGETS slsServerStatic) + +if((CMAKE_BUILD_TYPE STREQUAL "Release") AND SLS_LTO_AVAILABLE) + set_property(TARGET ${DETECTOR_LIBRARY_TARGETS} PROPERTY INTERPROCEDURAL_OPTIMIZATION True) +endif() + +install(TARGETS ${DETECTOR_LIBRARY_TARGETS} + EXPORT "${TARGETS_EXPORT_NAME}" + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/sls +) diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/CommandLineOptions.h b/slsDetectorServers/slsDetectorServer_cpp/include/CommandLineOptions.h new file mode 100644 index 000000000..7ec69b175 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/CommandLineOptions.h @@ -0,0 +1,66 @@ +#include "sls/sls_detector_defs.h" +#include +#include +#include +#include + +namespace sls { + +struct DetectorServerOptions { + // TODO: careful changed for other detectors + uint16_t port{DEFAULT_TCP_CNTRL_PORTNO}; + /// @brief ignore firmware version compatibility + bool ignoreFirmwareCompatibility{false}; + /// @brief safe startup - skip initial detector setup and checks + bool safeStartup{false}; + bool versionRequested{false}; + bool helpRequested{false}; +}; + +template +struct SpecificDetectorServerOptions : DetectorServerOptions {}; + +// template specialization +// template <> +// struct SpecificDetectorServerOptions {}; + +// TODO should be a general server specific class or even shared with +// CommandLIneOptions in Receiver +class CommandLineOptions { + + public: + CommandLineOptions() = default; + + ~CommandLineOptions() = default; + + DetectorServerOptions parse(int argc, char *argv[]); + + std::string printOptions() const; + + private: + std::string getHelpMessage(const std::string &executable) const; + + uint16_t parsePort(const char *optarg) const; + + void parse_deprecated(const int &opt, char *argv[]); + + DetectorServerOptions detectorserveroptions{}; + + static constexpr std::array options{ + {{"help", no_argument, nullptr, 'h'}, + {"version", no_argument, nullptr, 'v'}, + {"port", required_argument, nullptr, 'p'}, + {"ignore_fw_compatibility", no_argument, nullptr, + 'f'}, // ignore firmware compatibility check + {"safe_startup", no_argument, nullptr, 's'}, // safe startup + // deprecated options for backward compatibility + {"devel", no_argument, nullptr, 'd'}, // safe_startup mode + {"update", no_argument, nullptr, 'u'}, // firmware compatibility check + + {nullptr, 0, nullptr, 0}}}; + + inline static const char optstring[] = "hvp:fs" + "du"; // second part is deprecated +}; + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServer.h b/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServer.h new file mode 100644 index 000000000..7f1a6eb89 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/DetectorServer.h @@ -0,0 +1,269 @@ +#pragma once +#include "TCPInterface.h" +// #include "communication_funcs.h" +#include "sls/logger.h" +#include "sls/network_utils.h" +#include "sls/sls_detector_defs.h" +#include "sls/versionAPI.h" +#include +#include +#include +#include +#include +#include + +namespace sls { + +// TODO move to defs? +/// @brief struct saving udp details (one UDP port per module) +struct UDPInfo { + uint16_t srcport{}; + uint16_t dstport{}; + uint64_t srcmac{}; + uint64_t dstmac{}; + uint32_t srcip{}; + uint32_t dstip{}; +}; + +template class DetectorServer { + + public: + /** + * Constructor + * Creates a detector server. + * Assembles a detector server using TCP and UDP detector interfaces + * throws an exception in case of failure + * @param port TCP/IP port number + */ + explicit DetectorServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO); + + protected: + /// @brief TCP/IP interface for communication with the client + std::unique_ptr tcpInterface; + + std::array + udpDetails{}; // TODO: for now only one receiver per module + + /// @brief TODO what is this? + bool updateMode{true}; + + private: + ReturnCode processFunction(const detFuncs function_id, + ServerInterface &socket); + + // TODO dont know what this does? + ReturnCode get_update_mode(ServerInterface &socket) const; + + ReturnCode get_source_udp_mac(ServerInterface &socket) const; + + ReturnCode set_source_udp_mac(ServerInterface &socket); + + ReturnCode get_source_udp_ip(ServerInterface &socket) const; + + ReturnCode set_source_udp_ip(ServerInterface &socket); + + ReturnCode get_source_udp_port(ServerInterface &socket) const; + + ReturnCode set_destination_udp_mac(ServerInterface &socket); + + ReturnCode get_destination_udp_mac(ServerInterface &socket) const; + + ReturnCode set_destination_udp_ip(ServerInterface &socket); + + ReturnCode get_destination_udp_ip(ServerInterface &socket) const; + + ReturnCode set_destination_udp_port(ServerInterface &socket); + + ReturnCode get_destination_udp_port(ServerInterface &socket) const; +}; + +template +DetectorServer::DetectorServer(uint16_t port) { + validatePortNumber(port); + + udpDetails[0].srcport = DEFAULT_UDP_SRC_PORTNO; + udpDetails[0].dstport = DEFAULT_UDP_DST_PORTNO; + + std::function fn = + [this](const detFuncs &function_id, ServerInterface &socket) { + return this->processFunction(function_id, socket); + }; + tcpInterface = std::make_unique(fn, port); +} + +template +ReturnCode DetectorServer::processFunction( + const detFuncs function_id, ServerInterface &socket) { + + switch (function_id) { + case detFuncs::F_GET_SERVER_VERSION: + return static_cast(this)->get_version(socket); + case detFuncs::F_GET_DETECTOR_TYPE: + return static_cast(this)->get_detector_type( + socket); + case detFuncs::F_INITIAL_CHECKS: + return static_cast(this)->initial_checks( + socket); + case detFuncs::F_GET_NUM_INTERFACES: + return static_cast(this) + ->get_num_udp_interfaces(socket); + case detFuncs::F_GET_UPDATE_MODE: + return get_update_mode(socket); + case detFuncs::F_SET_SOURCE_UDP_MAC: + return set_source_udp_mac(socket); + case detFuncs::F_GET_SOURCE_UDP_MAC: + return get_source_udp_mac(socket); + case detFuncs::F_SET_SOURCE_UDP_IP: + return set_source_udp_ip(socket); + case detFuncs::F_GET_SOURCE_UDP_IP: + return get_source_udp_ip(socket); + case detFuncs::F_SET_DEST_UDP_MAC: + return set_destination_udp_mac(socket); + case detFuncs::F_GET_DEST_UDP_MAC: + return get_destination_udp_mac(socket); + case detFuncs::F_SET_DEST_UDP_IP: + return set_destination_udp_ip(socket); + case detFuncs::F_GET_DEST_UDP_IP: + return get_destination_udp_ip(socket); + case detFuncs::F_SET_DEST_UDP_PORT: + return set_destination_udp_port(socket); + case detFuncs::F_GET_DEST_UDP_PORT: + return get_destination_udp_port(socket); + + default: + LOG(logDEBUG) << "Checking specific server functions for function ID: " + << function_id; + // process detector specific functions + static_cast(this)->processFunction(function_id, + socket); + } + + return ReturnCode::FAIL; +} + +template +ReturnCode DetectorServer::get_update_mode( + ServerInterface &socket) const { + + return static_cast( + socket.sendResult(static_cast(updateMode))); +} + +template +ReturnCode DetectorServer::set_source_udp_mac( + ServerInterface &socket) { + uint64_t newsrcudpMac; + + try { + int ret = socket.Receive(newsrcudpMac); + } catch (const SocketError &e) { + LOG(logERROR) << "Failed to receive new source UDP MAC address: " + << e.what(); + return ReturnCode::FAIL; + } + + udpDetails[0].srcmac = newsrcudpMac; + // TODO: configuremac, check unicast address + return ReturnCode::OK; +} + +template +ReturnCode DetectorServer::get_source_udp_mac( + ServerInterface &socket) const { + return static_cast(socket.sendResult(udpDetails[0].srcmac)); +} + +template +ReturnCode DetectorServer::set_source_udp_ip( + ServerInterface &socket) { + uint32_t newSrcIp; + + try { + int ret = socket.Receive(newSrcIp); + } catch (const SocketError &e) { + LOG(logERROR) << "Failed to receive new source UDP IP address: " + << e.what(); + return ReturnCode::FAIL; + } + + udpDetails[0].srcip = newSrcIp; + return ReturnCode::OK; +} + +template +ReturnCode DetectorServer::get_source_udp_ip( + ServerInterface &socket) const { + return static_cast(socket.sendResult(udpDetails[0].srcip)); +} + +template +ReturnCode DetectorServer::set_destination_udp_mac( + ServerInterface &socket) { + uint64_t newDstMac; + + try { + int ret = socket.Receive(newDstMac); + } catch (const SocketError &e) { + LOG(logERROR) << "Failed to receive new destination UDP MAC address: " + << e.what(); + return ReturnCode::FAIL; + } + + udpDetails[0].dstmac = newDstMac; + // TODO: configuremac, check unicast address + return ReturnCode::OK; +} + +template +ReturnCode DetectorServer::get_destination_udp_mac( + ServerInterface &socket) const { + return static_cast(socket.sendResult(udpDetails[0].dstmac)); +} + +template +ReturnCode DetectorServer::set_destination_udp_ip( + ServerInterface &socket) { + uint32_t newDstIp; + + try { + int ret = socket.Receive(newDstIp); + } catch (const SocketError &e) { + LOG(logERROR) << "Failed to receive new destination UDP IP address: " + << e.what(); + return ReturnCode::FAIL; + } + + udpDetails[0].dstip = newDstIp; + return ReturnCode::OK; +} + +template +ReturnCode DetectorServer::get_destination_udp_ip( + ServerInterface &socket) const { + return static_cast(socket.sendResult(udpDetails[0].dstip)); +} + +template +ReturnCode DetectorServer::set_destination_udp_port( + ServerInterface &socket) { + uint16_t newDstPort; + + try { + int ret = socket.Receive(newDstPort); + } catch (const SocketError &e) { + LOG(logERROR) << "Failed to receive new destination UDP port number: " + << e.what(); + return ReturnCode::FAIL; + } + + udpDetails[0].dstport = newDstPort; + return ReturnCode::OK; +} + +template +ReturnCode DetectorServer::get_destination_udp_port( + ServerInterface &socket) const { + return static_cast(socket.sendResult(udpDetails[0].dstport)); +}; + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/include/TCPInterface.h b/slsDetectorServers/slsDetectorServer_cpp/include/TCPInterface.h new file mode 100644 index 000000000..9538a3e42 --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/include/TCPInterface.h @@ -0,0 +1,60 @@ +#pragma once +#include "sls/ServerSocket.h" +#include "sls/sls_detector_defs.h" +#include "sls/sls_detector_funcs.h" + +#include +#include +#include +#include + +namespace sls { + +/** + * @brief TCPInterface class handles communication and processing of commands + * from Client to Server. + */ +class TCPInterface { + + public: + ~TCPInterface(); + + TCPInterface(std::function + &processFunction_, + const uint16_t portNumber = DEFAULT_TCP_CNTRL_PORTNO); + + /// @brief creates tcp thread + void startTCPServer(); + + std::atomic killTcpThread{false}; + + private: + /** + * @brief starts the TCP/IP server to listen for client commands and process + * them + */ + void startTCPServerClientConnection(); + + /** + * @brief decodes the received command and calls the corresponding function + * @param function_id The ID of the function recived by the server and to + * be executed + */ + ReturnCode processReceivedData(const detFuncs function_id, + ServerInterface &socket); + + /// @brief map of function IDs and corresponding functions + std::function + processFunction; + + /// @brief TCP/IP port number for the detector server + uint16_t portNumber{}; + + /// @brief socket for TCP/IP communication with the client + ServerSocket server; + + /// @brief thread for running the TCP/IP server + std::unique_ptr tcpThread; +}; + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/slsDetectorServer_cpp/src/CommandLineOptions.cpp b/slsDetectorServers/slsDetectorServer_cpp/src/CommandLineOptions.cpp new file mode 100644 index 000000000..49d6a016b --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/src/CommandLineOptions.cpp @@ -0,0 +1,116 @@ +#include "CommandLineOptions.h" +#include "sls/ToString.h" +#include "sls/sls_detector_exceptions.h" + +#include +#include +#include +#include + +namespace sls { + +uint16_t CommandLineOptions::parsePort(const char *optarg) const { + uint16_t val = 0; // TODO: in c code its unsigned int + + try { + val = sls::StringTo(optarg); + } catch (const std::exception &e) { + throw sls::RuntimeError(fmt::format( + "Could not parse port number {}. {}", optarg, e.what())); + } + + if (val < 1024) { + throw sls::RuntimeError( + "Invalid/ privileged port number parsed. Min: 1024."); + } + return val; +} + +std::string +CommandLineOptions::getHelpMessage(const std::string &executable) const { + // TODO: update if we keep it Matterhonr specific - refactor a bit better - + // e.g. if compiled with detector macro + std::string helpmessage = fmt::format( + "Usage: {}" + " [arguments]\n" + "Possible arguments are:\n" + "\t-v, --version : Software version\n" + "\t-p, --port : TCP communication port with client. " + "\n" + "\t-s, --safe_startup : Safe startup mode. Skips initial " + "detector setup and checks. \n" + "\t-f, --ignore_fw_compatibility : Ignore firmware compatibility " + "check. \n", + executable); + return helpmessage; +} + +void CommandLineOptions::parse_deprecated(const int &opt, char *argv[]) { + + switch (opt) { + case 'd': + std::cout << "Warning: -d/--devel option is deprecated. Use " + "-l/--safe_startup instead." + << std::endl; + detectorserveroptions.safeStartup = true; + break; + case 'u': + std::cout << "Warning: -u/--update option is deprecated. Use " + "-f/--ignore_fw_compatibility instead." + << std::endl; + detectorserveroptions.ignoreFirmwareCompatibility = true; + break; + default: + throw std::runtime_error(fmt::format("Wrong command line arguments. {}", + getHelpMessage(argv[0]))); + } +} + +std::string CommandLineOptions::printOptions() const { + std::string msg = "setting up detector server"; + + if (detectorserveroptions.ignoreFirmwareCompatibility) { + msg += " skipping firmware compatibility checks"; + msg += detectorserveroptions.safeStartup ? " and" : ""; + } + if (detectorserveroptions.safeStartup) { + msg += " in safe startup mode e.g. skipping any initial detector setup " + "and checks"; + } + + return msg; +} + +DetectorServerOptions CommandLineOptions::parse(int argc, char *argv[]) { + + int opt, option_index = 0; + + while ((opt = getopt_long(argc, argv, optstring, options.data(), + &option_index)) != -1) { + switch (opt) { + case 'h': + std::cout << getHelpMessage(argv[0]) << std::endl; + detectorserveroptions.helpRequested = true; // to exit in main + break; + case 'v': + detectorserveroptions.versionRequested = true; // to exit in main + break; + case 'p': + detectorserveroptions.port = parsePort(optarg); + break; + case 'f': + detectorserveroptions.ignoreFirmwareCompatibility = true; + break; + case 's': + detectorserveroptions.safeStartup = true; + break; + default: + parse_deprecated(opt, argv); // to handle deprecated options and + // throw error for wrong options + } + } + + return detectorserveroptions; +} + +} // namespace sls diff --git a/slsDetectorServers/slsDetectorServer_cpp/src/TCPInterface.cpp b/slsDetectorServers/slsDetectorServer_cpp/src/TCPInterface.cpp new file mode 100644 index 000000000..9d2d7911e --- /dev/null +++ b/slsDetectorServers/slsDetectorServer_cpp/src/TCPInterface.cpp @@ -0,0 +1,95 @@ +#include "TCPInterface.h" + +#include "fmt/format.h" +#include "sls/logger.h" +#include "sls/string_utils.h" +#include + +namespace sls { + +TCPInterface::TCPInterface( + std::function + &processFunction_, + const uint16_t portNumber_) + : processFunction(processFunction_), portNumber(portNumber_), + server(portNumber_) { + // validatePortNumber(portNumber); TODO: where to validate? +} + +TCPInterface::~TCPInterface() { + killTcpThread = true; // mmh but if the receiver is stuck in a function, + // this will be of no help + LOG(logINFO) << "Shutting down TCP Socket on port " << portNumber; + server.shutdown(); + LOG(logDEBUG) << "TCP Socket closed on port " << portNumber; + if (tcpThread && tcpThread->joinable()) { + tcpThread->join(); + } +} + +void TCPInterface::startTCPServer() { + + tcpThread = std::make_unique( + &TCPInterface::startTCPServerClientConnection, this); +} + +void TCPInterface::startTCPServerClientConnection() { + + LOG(logINFO) << "SLS Server starting TCP Server on port " << portNumber + << '\n'; + + int function_id{}; // TODO should it be an enum type + while (!killTcpThread) { + try { + auto socket = server.accept(); + try { + + socket.Receive(function_id); + if (function_id < 0 || function_id >= NUM_DET_FUNCTIONS) { + throw RuntimeError(fmt::format( + "{}:{}", UNRECOGNIZED_FNUM_ENUM, + getFunctionNameFromEnum((enum detFuncs)function_id))); + } + auto returncode = processReceivedData( + static_cast(function_id), socket); + + if (returncode == FAIL) { + throw RuntimeError(fmt::format( + "Error processing command with fnum: {}", + getFunctionNameFromEnum((enum detFuncs)function_id))); + } + + } catch (const RuntimeError &e) { + // We had an error needs to be sent to client + char mess[MAX_STR_LENGTH]{}; + LOG(logERROR) << "Error processing command: " << e.what(); + strcpy_safe(mess, e.what()); + socket.Send(slsDetectorDefs::FAIL); + socket.Send(mess); + } + // TODO handle exiting server if tcp command was to exit server + } catch (const RuntimeError &e) { + LOG(logERROR) << "Accept failed: " << e.what(); + } + } + + LOG(logINFOBLUE) << "Exiting TCP Server"; +} + +ReturnCode TCPInterface::processReceivedData(const detFuncs function_id, + ServerInterface &socket) { + + LOG(logDEBUG1) << "calling function fnum: " << function_id << " (" + << getFunctionNameFromEnum((enum detFuncs)function_id) + << ")"; + + ReturnCode returncode = processFunction(function_id, socket); + + LOG(logDEBUG1) << "Function " + << getFunctionNameFromEnum((enum detFuncs)function_id) + << " finished"; + + return returncode; +} + +} // namespace sls \ No newline at end of file diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer b/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer index 28c2a4532..175991ee8 100755 Binary files a/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer and b/slsDetectorServers/xilinx_ctbDetectorServer/bin/xilinx_ctbDetectorServer_developer differ diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c index 625dbabd0..6d4e13a1a 100644 --- a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c +++ b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.c @@ -363,7 +363,7 @@ void initStopServer() { return; } #ifdef VIRTUAL - sharedMemory_setStop(0); + setupDetector(); #endif } initCheckDone = 1; @@ -389,9 +389,13 @@ void setupDetector() { vLimit = DEFAULT_VLIMIT; #ifdef VIRTUAL - sharedMemory_setStatus(IDLE); - setupUDPCommParameters(); - initializePatternWord(); + if (isControlServer) { + sharedMemory_setStatus(IDLE); + setupUDPCommParameters(); + initializePatternWord(); + } else { + sharedMemory_setStop(0); + } #endif // initialization only at start up (restart fpga) initError = waitTransceiverReset(initErrorMessage); @@ -444,9 +448,15 @@ void setupDetector() { setNumFrames(DEFAULT_NUM_FRAMES); setNumTriggers(DEFAULT_NUM_CYCLES); setTiming(DEFAULT_TIMING_MODE); - setExpTime(DEFAULT_EXPTIME); - setPeriod(DEFAULT_PERIOD); - setDelayAfterTrigger(DEFAULT_DELAY); + initError = setExpTime(DEFAULT_EXPTIME, initErrorMessage); + if (initError == FAIL) + return; + initError = setPeriod(DEFAULT_PERIOD, initErrorMessage); + if (initError == FAIL) + return; + initError = setDelayAfterTrigger(DEFAULT_DELAY, initErrorMessage); + if (initError == FAIL) + return; setNextFrameNumber(DEFAULT_STARTING_FRAME_NUMBER); } @@ -766,63 +776,132 @@ int getNumTransceiverSamples() { return ((bus_r(NO_SAMPLES_X_REG) & NO_SAMPLES_X_MSK) >> NO_SAMPLES_X_OFST); } -int setExpTime(int64_t val) { +int setExpTime(int64_t val, char *mess) { setPatternWaitInterval(0, val); - // validate for tolerance - int64_t retval = getExpTime(); + // validate + uint64_t arg_clocks = ns_to_clocks(val, clkFrequency[RUN_CLK]); + uint64_t retval_clocks = getPatternWaitClocks(0); + if (arg_clocks != retval_clocks) { + sprintf(mess, + "Failed to set exposure time. Could not set number of clocks " + "to %lld, read %lld\n", + (long long int)arg_clocks, (long long int)retval_clocks); + LOG(logERROR, (mess)); + return FAIL; + } + + // log rounding if any + int64_t retval = getPatternWaitInterval(0); if (val != retval) { + LOG(logWARNING, ("Rounding to %lld ns due to clock frequency\n", + (long long int)retval)); + } + + return OK; +} + +int getExpTime(int64_t *retval, char *mess) { + *retval = getPatternWaitInterval(0); + if (*retval == -1) { + sprintf(mess, "Failed to get exposure time.\n"); + LOG(logERROR, (mess)); return FAIL; } return OK; } -int64_t getExpTime() { return getPatternWaitInterval(0); } - -int setPeriod(int64_t val) { +int setPeriod(int64_t val, char *mess) { if (val < 0) { - LOG(logERROR, ("Invalid period: %lld ns\n", (long long int)val)); + sprintf(mess, "Invalid period: %lld ns\n", (long long int)val); + LOG(logERROR, (mess)); return FAIL; } LOG(logINFO, ("Setting period %lld ns\n", (long long int)val)); - val *= (NS_TO_CLK_CYCLE * clkFrequency[RUN_CLK]); - setU64BitReg(val, PERIOD_IN_REG_1, PERIOD_IN_REG_2); + uint64_t arg_clocks = ns_to_clocks(val, clkFrequency[RUN_CLK]); + setU64BitReg(arg_clocks, PERIOD_IN_REG_1, PERIOD_IN_REG_2); - // validate for tolerance - int64_t retval = getPeriod(); - val /= (NS_TO_CLK_CYCLE * clkFrequency[RUN_CLK]); - if (val != retval) { + // validate + uint64_t retval_clocks = getU64BitReg(PERIOD_IN_REG_1, PERIOD_IN_REG_2); + if (arg_clocks != retval_clocks) { + sprintf(mess, + "Failed to set period. Could not set number of clocks " + "to %lld, red %lld\n", + (long long int)arg_clocks, (long long int)retval_clocks); + LOG(logERROR, (mess)); return FAIL; } + + // log rounding if any + int64_t retval = 0; + if (getPeriod(&retval, mess) == FAIL) { + return FAIL; + } + if (val != retval) { + LOG(logWARNING, ("Rounding to %lld ns due to clock frequency\n", + (long long int)retval)); + } + return OK; } -int64_t getPeriod() { - return getU64BitReg(PERIOD_IN_REG_1, PERIOD_IN_REG_2) / - (NS_TO_CLK_CYCLE * clkFrequency[RUN_CLK]); +int getPeriod(int64_t *retval, char *mess) { + if (clkFrequency[RUN_CLK] == 0) { + sprintf(mess, "Cannot get period. Run clock frequency is 0.\n"); + LOG(logERROR, (mess)); + return FAIL; + } + uint64_t numClocks = getU64BitReg(PERIOD_IN_REG_1, PERIOD_IN_REG_2); + *retval = clocks_to_ns(numClocks, clkFrequency[RUN_CLK]); + return OK; } -int setDelayAfterTrigger(int64_t val) { +int setDelayAfterTrigger(int64_t val, char *mess) { if (val < 0) { - LOG(logERROR, ("Invalid delay after trigger: %ld ns\n", val)); + sprintf(mess, "Invalid delay after trigger: %lld ns\n", + (long long int)val); + LOG(logERROR, (mess)); return FAIL; } - LOG(logINFO, ("Setting delay after trigger %ld ns\n", val)); - val *= (NS_TO_CLK_CYCLE * clkFrequency[RUN_CLK]); - setU64BitReg(val, DELAY_IN_REG_1, DELAY_IN_REG_2); + LOG(logINFO, ("Setting delay after trigger %lld ns\n", (long long int)val)); + uint64_t arg_clocks = ns_to_clocks(val, clkFrequency[RUN_CLK]); + setU64BitReg(arg_clocks, DELAY_IN_REG_1, DELAY_IN_REG_2); - // validate for tolerance - int64_t retval = getDelayAfterTrigger(); - val /= (NS_TO_CLK_CYCLE * clkFrequency[RUN_CLK]); - if (val != retval) { + // validate + uint64_t retval_clocks = getU64BitReg(DELAY_IN_REG_1, DELAY_IN_REG_2); + if (arg_clocks != retval_clocks) { + sprintf( + mess, + "Failed to set delay after trigger. Could not set number of clocks " + "to %lld, read %lld\n", + (long long int)arg_clocks, (long long int)retval_clocks); + LOG(logERROR, (mess)); return FAIL; } + + // log rounding if any + int64_t retval = 0; + if (getDelayAfterTrigger(&retval, mess) == FAIL) { + return FAIL; + } + if (val != retval) { + LOG(logWARNING, ("Rounding to %lld ns due to clock frequency\n", + (long long int)retval)); + } + return OK; } -int64_t getDelayAfterTrigger() { - return getU64BitReg(DELAY_IN_REG_1, DELAY_IN_REG_2) / - (NS_TO_CLK_CYCLE * clkFrequency[RUN_CLK]); +int getDelayAfterTrigger(int64_t *retval, char *mess) { + if (clkFrequency[RUN_CLK] == 0) { + sprintf(mess, + "Cannot get delay after trigger. Run clock frequency is 0.\n"); + LOG(logERROR, (mess)); + return FAIL; + } + uint64_t numClocks = getU64BitReg(DELAY_IN_REG_1, DELAY_IN_REG_2); + *retval = clocks_to_ns(numClocks, clkFrequency[RUN_CLK]); + return OK; } int64_t getNumFramesLeft() { @@ -833,14 +912,27 @@ int64_t getNumTriggersLeft() { return getU64BitReg(CYCLES_OUT_REG_1, CYCLES_OUT_REG_2); } -int64_t getDelayAfterTriggerLeft() { - return getU64BitReg(DELAY_OUT_REG_1, DELAY_OUT_REG_2) / - (NS_TO_CLK_CYCLE * clkFrequency[RUN_CLK]); +int getDelayAfterTriggerLeft(int64_t *retval, char *mess) { + if (clkFrequency[RUN_CLK] == 0) { + sprintf(mess, "Cannot get delay after trigger left. Run clock " + "frequency is 0.\n"); + LOG(logERROR, (mess)); + return FAIL; + } + uint64_t numClocks = getU64BitReg(DELAY_OUT_REG_1, DELAY_OUT_REG_2); + *retval = clocks_to_ns(numClocks, clkFrequency[RUN_CLK]); + return OK; } -int64_t getPeriodLeft() { - return getU64BitReg(PERIOD_OUT_REG_1, PERIOD_OUT_REG_2) / - (NS_TO_CLK_CYCLE * clkFrequency[RUN_CLK]); +int getPeriodLeft(int64_t *retval, char *mess) { + if (clkFrequency[RUN_CLK] == 0) { + sprintf(mess, "Cannot get period left. Run clock frequency is 0.\n"); + LOG(logERROR, (mess)); + return FAIL; + } + uint64_t numClocks = getU64BitReg(PERIOD_OUT_REG_1, PERIOD_OUT_REG_2); + *retval = clocks_to_ns(numClocks, clkFrequency[RUN_CLK]); + return OK; } int64_t getFramesFromStart() { @@ -849,13 +941,14 @@ int64_t getFramesFromStart() { } int64_t getActualTime() { - return getU64BitReg(TIME_FROM_START_OUT_REG_1, TIME_FROM_START_OUT_REG_2) / - (NS_TO_CLK_CYCLE * clkFrequency[SYNC_CLK]); + // in unit of 100ns + return getU64BitReg(TIME_FROM_START_OUT_REG_1, TIME_FROM_START_OUT_REG_2) * + 100; } int64_t getMeasurementTime() { - return getU64BitReg(FRAME_TIME_OUT_REG_1, FRAME_TIME_OUT_REG_2) / - (NS_TO_CLK_CYCLE * clkFrequency[SYNC_CLK]); + // in unit of 100ns + return getU64BitReg(FRAME_TIME_OUT_REG_1, FRAME_TIME_OUT_REG_2) * 100; } /* parameters - dac, adc, hv */ @@ -1447,11 +1540,22 @@ void *start_timer(void *arg) { if (!isControlServer) { return NULL; } + int64_t periodNs = 0; + int64_t expUs = 0; + { + char mess[MAX_STR_LENGTH] = {0}; + if (getPeriod(&periodNs, mess) == FAIL) { + LOG(logERROR, ("Failed to get period.\n")); + return NULL; + } + if (getExpTime(&expUs, mess) == FAIL) { + LOG(logERROR, ("Failed to get exposure time.\n")); + return NULL; + } + expUs /= 1000; + } - int64_t periodNs = getPeriod(); int numFrames = (getNumFrames() * getNumTriggers()); - int64_t expUs = getExpTime() / 1000; - int imageSize = calculateDataBytes(); int maxDataSize = MAX_DATA_SIZE_IN_PACKET; int packetSize = sizeof(sls_detector_header) + maxDataSize; @@ -1716,11 +1820,11 @@ int setFrequency(enum CLKINDEX ind, int val) { } char *clock_names[] = {CLK_NAMES}; - LOG(logINFO, ("\tSetting %s clock (%d) frequency to %d kHz\n", + LOG(logINFO, ("\tSetting %s clock (%d) frequency to %d Hz\n", clock_names[ind], ind, val)); if (XILINX_PLL_setFrequency(ind, val) == FAIL) { - LOG(logERROR, ("\tCould not set %s clock (%d) frequency to %d kHz\n", + LOG(logERROR, ("\tCould not set %s clock (%d) frequency to %d Hz\n", clock_names[ind], ind, val)); return FAIL; } diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h index 4d1ce9889..14de21741 100644 --- a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h +++ b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorFunctionList.h @@ -84,10 +84,10 @@ void setNumFrames(int64_t val); int64_t getNumFrames(); void setNumTriggers(int64_t val); int64_t getNumTriggers(); -int setExpTime(int64_t val); -int64_t getExpTime(); -int setPeriod(int64_t val); -int64_t getPeriod(); +int setExpTime(int64_t val, char *mess); +int getExpTime(int64_t *retval, char *mess); +int setPeriod(int64_t val, char *mess); +int getPeriod(int64_t *retval, char *mess); int setNumAnalogSamples(int val); int getNumAnalogSamples(); int setNumDigitalSamples(int val); @@ -97,10 +97,10 @@ int getNumTransceiverSamples(); int64_t getNumFramesLeft(); int64_t getNumTriggersLeft(); -int setDelayAfterTrigger(int64_t val); -int64_t getDelayAfterTrigger(); -int64_t getDelayAfterTriggerLeft(); -int64_t getPeriodLeft(); +int setDelayAfterTrigger(int64_t val, char *mess); +int getDelayAfterTrigger(int64_t *retval, char *mess); +int getDelayAfterTriggerLeft(int64_t *retval, char *mess); +int getPeriodLeft(int64_t *retval, char *mess); int64_t getFramesFromStart(); int64_t getActualTime(); int64_t getMeasurementTime(); diff --git a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorServer_defs.h b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorServer_defs.h index 757df2e0c..adf4691bc 100644 --- a/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorServer_defs.h +++ b/slsDetectorServers/xilinx_ctbDetectorServer/slsDetectorServer_defs.h @@ -158,8 +158,11 @@ typedef struct udp_header_struct { enum CLKINDEX { RUN_CLK, ADC_CLK, SYNC_CLK, DBIT_CLK, NUM_CLOCKS }; #define CLK_NAMES "run", "adc", "sync", "dbit" -#define DEFAULT_RUN_CLK (20000) // 20 MHz -#define DEFAULT_ADC_CLK (100000) // 100 MHz -#define DEFAULT_SYNC_CLK (20000) // 20 MHz -#define DEFAULT_DBIT_CLK (100000) // 100 MHz -#define NS_TO_CLK_CYCLE (1E-6) // ns to kHz +#define DEFAULT_RUN_CLK (20000000) // 20 MHz +#define DEFAULT_ADC_CLK (100000000) // 100 MHz +#define DEFAULT_SYNC_CLK (20000000) // 20 MHz +#define DEFAULT_DBIT_CLK (100000000) // 100 MHz +#define NS_TO_CLK_CYCLE (1E-9) // ns to Hz + +#define MIN_CLK_FREQ (10000000) // 10 MHz +#define MAX_CLK_FREQ (300000000) // 300 MHz diff --git a/slsDetectorSoftware/generator/Caller.in.h b/slsDetectorSoftware/generator/Caller.in.h index f7128620e..e18dec02c 100644 --- a/slsDetectorSoftware/generator/Caller.in.h +++ b/slsDetectorSoftware/generator/Caller.in.h @@ -54,6 +54,9 @@ class Caller { return ToString(value, unit); } + std::string OutString(const Result &value, + const std::string &unit); + std::vector getAllCommands(); std::map GetDeprecatedCommands(); std::string list(int action); @@ -96,6 +99,7 @@ class Caller { defs::dacIndex parseDacIndex(int argIndex, bool isCtb); bool parseMV(int argIndex); defs::powerIndex parsePowerIndex(int argIndex); + defs::FrequencyUnit parseFrequencyUnit(const std::string &s); FunctionMap functions{ {"list", &Caller::list}, diff --git a/slsDetectorSoftware/generator/autocomplete/autocomplete.py b/slsDetectorSoftware/generator/autocomplete/autocomplete.py index 356e82eab..f236bcc67 100644 --- a/slsDetectorSoftware/generator/autocomplete/autocomplete.py +++ b/slsDetectorSoftware/generator/autocomplete/autocomplete.py @@ -16,6 +16,7 @@ type_values = { 'special::mv': ["mv", "mV"], "special::deg": ["deg"], "special::time_unit": ["s", "ms", "us", "ns"], + "special::freq_unit": ["Hz", "kHz", "MHz"], "special::hard": ["hard"], "special::force-delete-normal-file": ["--force-delete-normal-file"], "special::currentSourceFix": ["fix", "nofix"], @@ -40,11 +41,11 @@ def get_types(arg_types): #list of options with a command line call that fetches them #TODO! Rename sls_detector_get if "defs::dacIndex" in arg_types: - return "`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" + return r"`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" elif "defs::detectorSettings" in arg_types: - return "`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" + return r"`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" elif "defs::timingMode" in arg_types: - return "`sls_detector_get timinglist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" + return r"`sls_detector_get timinglist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" return ret diff --git a/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh b/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh index ff46a218a..433bd33d4 100644 --- a/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh +++ b/slsDetectorSoftware/generator/autocomplete/bash_autocomplete.sh @@ -96,10 +96,18 @@ return 0 } __adcclk() { FCN_RETURN="" +if [[ ${IS_GET} -eq 1 ]]; then +if [[ "${cword}" == "2" ]]; then +FCN_RETURN="Hz MHz kHz" +fi +fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then FCN_RETURN="" fi +if [[ "${cword}" == "3" ]]; then +FCN_RETURN="Hz MHz kHz" +fi fi return 0 } @@ -565,12 +573,12 @@ __dacname() { FCN_RETURN="" if [[ ${IS_GET} -eq 1 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then FCN_RETURN="" @@ -611,10 +619,18 @@ return 0 } __dbitclk() { FCN_RETURN="" +if [[ ${IS_GET} -eq 1 ]]; then +if [[ "${cword}" == "2" ]]; then +FCN_RETURN="Hz MHz kHz" +fi +fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then FCN_RETURN="" fi +if [[ "${cword}" == "3" ]]; then +FCN_RETURN="Hz MHz kHz" +fi fi return 0 } @@ -648,21 +664,21 @@ __defaultdac() { FCN_RETURN="" if [[ ${IS_GET} -eq 1 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "4" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 @@ -671,13 +687,13 @@ __defaultpattern() { FCN_RETURN="" if [[ ${IS_GET} -eq 1 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "4" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 @@ -2120,10 +2136,18 @@ return 0 } __runclk() { FCN_RETURN="" +if [[ ${IS_GET} -eq 1 ]]; then +if [[ "${cword}" == "2" ]]; then +FCN_RETURN="Hz MHz kHz" +fi +fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then FCN_RETURN="" fi +if [[ "${cword}" == "3" ]]; then +FCN_RETURN="Hz MHz kHz" +fi fi return 0 } @@ -2408,7 +2432,7 @@ __scan() { FCN_RETURN="" if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then FCN_RETURN="" @@ -2461,7 +2485,7 @@ __settings() { FCN_RETURN="" if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 @@ -2571,12 +2595,12 @@ __slowadcname() { FCN_RETURN="" if [[ ${IS_GET} -eq 1 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then FCN_RETURN="" @@ -2680,6 +2704,16 @@ return 0 } __syncclk() { FCN_RETURN="" +if [[ ${IS_GET} -eq 1 ]]; then +if [[ "${cword}" == "2" ]]; then +FCN_RETURN="Hz MHz kHz" +fi +fi +if [[ ${IS_GET} -eq 0 ]]; then +if [[ "${cword}" == "2" ]]; then +FCN_RETURN="Hz MHz kHz" +fi +fi return 0 } __temp_10ge() { @@ -2773,13 +2807,13 @@ if [[ "${cword}" == "2" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "3" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "4" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "5" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 @@ -2791,13 +2825,13 @@ if [[ "${cword}" == "2" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "3" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "4" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "5" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 @@ -2806,7 +2840,7 @@ __timing() { FCN_RETURN="" if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get timinglist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get timinglist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 diff --git a/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh b/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh index 92073ccea..953f971d2 100644 --- a/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh +++ b/slsDetectorSoftware/generator/autocomplete/zsh_autocomplete.sh @@ -20,10 +20,18 @@ return 0 } __adcclk() { FCN_RETURN="" +if [[ ${IS_GET} -eq 1 ]]; then +if [[ "${cword}" == "2" ]]; then +FCN_RETURN="Hz MHz kHz" +fi +fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then FCN_RETURN="" fi +if [[ "${cword}" == "3" ]]; then +FCN_RETURN="Hz MHz kHz" +fi fi return 0 } @@ -489,12 +497,12 @@ __dacname() { FCN_RETURN="" if [[ ${IS_GET} -eq 1 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then FCN_RETURN="" @@ -535,10 +543,18 @@ return 0 } __dbitclk() { FCN_RETURN="" +if [[ ${IS_GET} -eq 1 ]]; then +if [[ "${cword}" == "2" ]]; then +FCN_RETURN="Hz MHz kHz" +fi +fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then FCN_RETURN="" fi +if [[ "${cword}" == "3" ]]; then +FCN_RETURN="Hz MHz kHz" +fi fi return 0 } @@ -572,21 +588,21 @@ __defaultdac() { FCN_RETURN="" if [[ ${IS_GET} -eq 1 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "4" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 @@ -595,13 +611,13 @@ __defaultpattern() { FCN_RETURN="" if [[ ${IS_GET} -eq 1 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "4" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 @@ -2044,10 +2060,18 @@ return 0 } __runclk() { FCN_RETURN="" +if [[ ${IS_GET} -eq 1 ]]; then +if [[ "${cword}" == "2" ]]; then +FCN_RETURN="Hz MHz kHz" +fi +fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then FCN_RETURN="" fi +if [[ "${cword}" == "3" ]]; then +FCN_RETURN="Hz MHz kHz" +fi fi return 0 } @@ -2332,7 +2356,7 @@ __scan() { FCN_RETURN="" if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then FCN_RETURN="" @@ -2385,7 +2409,7 @@ __settings() { FCN_RETURN="" if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 @@ -2495,12 +2519,12 @@ __slowadcname() { FCN_RETURN="" if [[ ${IS_GET} -eq 1 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get daclist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "3" ]]; then FCN_RETURN="" @@ -2604,6 +2628,16 @@ return 0 } __syncclk() { FCN_RETURN="" +if [[ ${IS_GET} -eq 1 ]]; then +if [[ "${cword}" == "2" ]]; then +FCN_RETURN="Hz MHz kHz" +fi +fi +if [[ ${IS_GET} -eq 0 ]]; then +if [[ "${cword}" == "2" ]]; then +FCN_RETURN="Hz MHz kHz" +fi +fi return 0 } __temp_10ge() { @@ -2697,13 +2731,13 @@ if [[ "${cword}" == "2" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "3" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "4" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "5" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 @@ -2715,13 +2749,13 @@ if [[ "${cword}" == "2" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "3" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi if [[ "${cword}" == "4" ]]; then FCN_RETURN="" fi if [[ "${cword}" == "5" ]]; then -FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get settingslist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 @@ -2730,7 +2764,7 @@ __timing() { FCN_RETURN="" if [[ ${IS_GET} -eq 0 ]]; then if [[ "${cword}" == "2" ]]; then -FCN_RETURN="`sls_detector_get timinglist | sed -e 's/.*\[\(.*\)\].*/\1/' | sed 's/,//g'`" +FCN_RETURN="`sls_detector_get timinglist | sed -e 's/.*\[\(.*\)\].*/\\1/' | sed 's/,//g'`" fi fi return 0 diff --git a/slsDetectorSoftware/generator/commands.yaml b/slsDetectorSoftware/generator/commands.yaml index 20f94b12a..9e069f830 100644 --- a/slsDetectorSoftware/generator/commands.yaml +++ b/slsDetectorSoftware/generator/commands.yaml @@ -2,6 +2,56 @@ # detectors: MYTHEN3 ################# TEMPLATES ################# +FREQ_COMMAND: + infer_action: true + help: "" + template: true + actions: + GET: + require_det_id: true + function: '' + args: + - argc: 0 + output: [ OutString(t) ] + - argc: 1 + arg_types: [ special::freq_unit ] + output: [ "OutString(t , args[0])" ] + PUT: + function: '' + require_det_id: true + input: [ converted_freq ] + input_types: [ defs::Hz ] + args: + - argc: 1 + arg_types: [ std::string ] + + separate_freq_units: + input: 'args[0]' + output: [ converted_freq, unit ] + output: [ 'args[0]' ] + - argc: 2 + arg_types: [ int, special::freq_unit ] + + convert_to_freq: + input: [ 'args[0]', 'args[1]' ] + output: converted_freq + output: [ 'args[0]', 'args[1]' ] + +FREQ_GET_COMMAND: + infer_action: true + help: "" + template: true + actions: + GET: + require_det_id: true + function: '' + args: + - argc: 0 + output: [ OutString(t) ] + - argc: 1 + arg_types: [ special::freq_unit ] + output: [ "OutString(t , args[0])" ] + TIME_COMMAND: infer_action: true help: "" @@ -401,6 +451,44 @@ CTB_GET_INDEX: ################# COMMANDS ################################## +################# FREQ_COMMAND ############# +adcclk: + help: "[n_clk] [(optional unit) Hz(default)|kHz|MHz]\n\t[Ctb][Xilinx Ctb] ADC clock frequency." + inherit_actions: FREQ_COMMAND + actions: + GET: + function: getADCClock + PUT: + function: setADCClock + +runclk: + help: "[n_clk] [(optional unit) Hz(default)|kHz|MHz]\n\t[Ctb][Xilinx Ctb] Run clock frequency." + inherit_actions: FREQ_COMMAND + actions: + GET: + function: getRUNClock + PUT: + function: setRUNClock + + +dbitclk: + help: "[n_clk] [(optional unit) Hz(default)|kHz|MHz]\n\t[Ctb][Xilinx Ctb] Clock for latching the digital bits." + inherit_actions: FREQ_COMMAND + actions: + GET: + function: getDBITClock + PUT: + function: setDBITClock + +################# FREQ_GET_COMMAND ############# + +syncclk: + inherit_actions: FREQ_GET_COMMAND + help: "[n_clk] [(optional unit) Hz(default)|kHz|MHz]\n\t[Ctb] Sync clock." + actions: + GET: + function: getSYNCClock + ################# TIME_COMMAND ############# period: @@ -1243,23 +1331,6 @@ asamples: PUT: function: setNumberOfAnalogSamples -adcclk: - help: "[n_clk in MHz]\n\t[Ctb] ADC clock frequency in MHz.\n\t[xilinx Ctb] ADC clock frequency in kHz." - inherit_actions: INTEGER_COMMAND_VEC_ID - actions: - GET: - function: getADCClock - PUT: - function: setADCClock - -runclk: - help: "[n_clk in MHz]\n\t[Ctb] Run clock in MHz.\n\t[xilinx Ctb] Run clock in kHz." - inherit_actions: INTEGER_COMMAND_VEC_ID - actions: - GET: - function: getRUNClock - PUT: - function: setRUNClock dsamples: help: "[n_value]\n\t[Ctb] Number of digital samples expected." @@ -1289,15 +1360,6 @@ romode: function: setReadoutMode input_types: [ defs::readoutMode ] -dbitclk: - help: "[n_clk in MHz]\n\t[Ctb] Clock for latching the digital bits in MHz.\n\t[xilinx Ctb] Clock for latching the digital bits in kHz." - inherit_actions: INTEGER_COMMAND_VEC_ID - actions: - GET: - function: getDBITClock - PUT: - function: setDBITClock - extsampling: help: "[0, 1]\n\t[Ctb] Enable for external sampling signal for digital data to signal by extsampling src command. For advanced users only." inherit_actions: INTEGER_COMMAND_VEC_ID @@ -1912,19 +1974,12 @@ burstsl: GET: function: getNumberOfBurstsLeft -syncclk: - inherit_actions: GET_COMMAND - help: "[n_clk in MHz]\n\t[Ctb] Sync clock in MHz." - actions: - GET: - function: getSYNCClock - patfname: inherit_actions: GET_COMMAND help: "\n\t[Ctb][Mythen3][Xilinx Ctb] Gets the pattern file name including path of the last pattern uploaded. Returns an empty if nothing was uploaded or via a server default file" actions: GET: - function: getPatterFileName + function: getPatternFileName lastclient: inherit_actions: GET_COMMAND diff --git a/slsDetectorSoftware/generator/commands_parser/commands_parser.py b/slsDetectorSoftware/generator/commands_parser/commands_parser.py index 9a11cd789..576cbfa21 100644 --- a/slsDetectorSoftware/generator/commands_parser/commands_parser.py +++ b/slsDetectorSoftware/generator/commands_parser/commands_parser.py @@ -50,7 +50,6 @@ class CommandParser: if len(arg['input_types']) != len(arg['input']): raise ValueError(f'Argument {arg} does not have the correct number of inputs') if 'separate_time_units' in arg: - if arg['separate_time_units']['input'] == "": raise ValueError(f'Argument {arg} does not have the correct number of inputs for separate_time_units') if len(arg['separate_time_units']['output']) != 2: @@ -60,6 +59,16 @@ class CommandParser: raise ValueError(f'Argument {arg} does not have the correct number of inputs for convert_to_time') if len(arg['convert_to_time']['output']) == "": raise ValueError(f'Argument {arg} does not have the correct number of outputs for convert_to_time') + if 'separate_freq_units' in arg: + if arg['separate_freq_units']['input'] == "": + raise ValueError(f'Argument {arg} does not have the correct number of inputs for separate_freq_units') + if len(arg['separate_freq_units']['output']) != 2: + raise ValueError(f'Argument {arg} does not have the correct number of outputs for separate_freq_units') + if 'convert_to_freq' in arg: + if len(arg['convert_to_freq']['input']) != 2: + raise ValueError(f'Argument {arg} does not have the correct number of inputs for convert_to_freq') + if len(arg['convert_to_freq']['output']) == "": + raise ValueError(f'Argument {arg} does not have the correct number of outputs for convert_to_freq') # if infer_action: # if arg['argc'] in self.argc_set: # raise ValueError(f'Argument {arg} has a duplicate argc') diff --git a/slsDetectorSoftware/generator/cpp_codegen/codegen.py b/slsDetectorSoftware/generator/cpp_codegen/codegen.py index fef9959d0..a9e875b09 100644 --- a/slsDetectorSoftware/generator/cpp_codegen/codegen.py +++ b/slsDetectorSoftware/generator/cpp_codegen/codegen.py @@ -142,6 +142,16 @@ class CodeGenerator: if 'convert_to_time' in arg and arg['convert_to_time']: self.write_line(f'auto {arg["convert_to_time"]["output"]} = ' f'StringTo < time::ns > ({", ".join(arg["convert_to_time"]["input"])});') + if 'separate_freq_units' in arg and arg['separate_freq_units']: + self.write_line(f'std::string tmp_freq({arg["separate_freq_units"]["input"]});') + self.write_line(f'std::string {arg["separate_freq_units"]["output"][1]}' + f' = RemoveUnit(tmp_freq);') + self.write_line(f'auto {arg["separate_freq_units"]["output"][0]} = ' + f'StringTo < defs::Hz > (tmp_freq,' + f' {arg["separate_freq_units"]["output"][1]});') + if 'convert_to_freq' in arg and arg['convert_to_freq']: + self.write_line(f'auto {arg["convert_to_freq"]["output"]} = ' + f'StringTo < defs::Hz > ({", ".join(arg["convert_to_freq"]["input"])});') input_arguments = [] if 'exceptions' in arg: for exception in arg['exceptions']: diff --git a/slsDetectorSoftware/generator/extended_commands.yaml b/slsDetectorSoftware/generator/extended_commands.yaml index 9dd24bb5d..c29c13bc5 100644 --- a/slsDetectorSoftware/generator/extended_commands.yaml +++ b/slsDetectorSoftware/generator/extended_commands.yaml @@ -86,28 +86,69 @@ adcclk: - OutString(t) require_det_id: true store_result_in_t: true + - arg_types: + - special::freq_unit + argc: 1 + cast_input: [] + check_det_id: false + convert_det_id: true + function: getADCClock + input: [] + input_types: [] + output: + - OutString(t , args[0]) + require_det_id: true + store_result_in_t: true PUT: args: - arg_types: - - int + - std::string argc: 1 cast_input: - - true + - false check_det_id: false convert_det_id: true function: setADCClock input: - - args[0] + - converted_freq input_types: - - int + - defs::Hz output: - - args.front() + - args[0] + require_det_id: true + separate_freq_units: + input: args[0] + output: + - converted_freq + - unit + store_result_in_t: false + - arg_types: + - int + - special::freq_unit + argc: 2 + cast_input: + - false + check_det_id: false + convert_det_id: true + convert_to_freq: + input: + - args[0] + - args[1] + output: converted_freq + function: setADCClock + input: + - converted_freq + input_types: + - defs::Hz + output: + - args[0] + - args[1] require_det_id: true store_result_in_t: false command_name: adcclk function_alias: adcclk - help: "[n_clk in MHz]\n\t[Ctb] ADC clock frequency in MHz.\n\t[xilinx Ctb] ADC clock\ - \ frequency in kHz." + help: "[n_clk] [(optional unit) Hz(default)|kHz|MHz]\n\t[Ctb][Xilinx Ctb] ADC clock\ + \ frequency." infer_action: true template: true adcenable: @@ -2060,28 +2101,69 @@ dbitclk: - OutString(t) require_det_id: true store_result_in_t: true + - arg_types: + - special::freq_unit + argc: 1 + cast_input: [] + check_det_id: false + convert_det_id: true + function: getDBITClock + input: [] + input_types: [] + output: + - OutString(t , args[0]) + require_det_id: true + store_result_in_t: true PUT: args: - arg_types: - - int + - std::string argc: 1 cast_input: - - true + - false check_det_id: false convert_det_id: true function: setDBITClock input: - - args[0] + - converted_freq input_types: - - int + - defs::Hz output: - - args.front() + - args[0] + require_det_id: true + separate_freq_units: + input: args[0] + output: + - converted_freq + - unit + store_result_in_t: false + - arg_types: + - int + - special::freq_unit + argc: 2 + cast_input: + - false + check_det_id: false + convert_det_id: true + convert_to_freq: + input: + - args[0] + - args[1] + output: converted_freq + function: setDBITClock + input: + - converted_freq + input_types: + - defs::Hz + output: + - args[0] + - args[1] require_det_id: true store_result_in_t: false command_name: dbitclk function_alias: dbitclk - help: "[n_clk in MHz]\n\t[Ctb] Clock for latching the digital bits in MHz.\n\t[xilinx\ - \ Ctb] Clock for latching the digital bits in kHz." + help: "[n_clk] [(optional unit) Hz(default)|kHz|MHz]\n\t[Ctb][Xilinx Ctb] Clock\ + \ for latching the digital bits." infer_action: true template: true dbitphase: @@ -5788,7 +5870,7 @@ patfname: cast_input: [] check_det_id: false convert_det_id: true - function: getPatterFileName + function: getPatternFileName input: [] input_types: [] output: @@ -8140,27 +8222,69 @@ runclk: - OutString(t) require_det_id: true store_result_in_t: true + - arg_types: + - special::freq_unit + argc: 1 + cast_input: [] + check_det_id: false + convert_det_id: true + function: getRUNClock + input: [] + input_types: [] + output: + - OutString(t , args[0]) + require_det_id: true + store_result_in_t: true PUT: args: - arg_types: - - int + - std::string argc: 1 cast_input: - - true + - false check_det_id: false convert_det_id: true function: setRUNClock input: - - args[0] + - converted_freq input_types: - - int + - defs::Hz output: - - args.front() + - args[0] + require_det_id: true + separate_freq_units: + input: args[0] + output: + - converted_freq + - unit + store_result_in_t: false + - arg_types: + - int + - special::freq_unit + argc: 2 + cast_input: + - false + check_det_id: false + convert_det_id: true + convert_to_freq: + input: + - args[0] + - args[1] + output: converted_freq + function: setRUNClock + input: + - converted_freq + input_types: + - defs::Hz + output: + - args[0] + - args[1] require_det_id: true store_result_in_t: false command_name: runclk function_alias: runclk - help: "[n_clk in MHz]\n\t[Ctb] Run clock in MHz.\n\t[xilinx Ctb] Run clock in kHz." + help: "[n_clk] [(optional unit) Hz(default)|kHz|MHz]\n\t[Ctb][Xilinx Ctb] Run clock\ + \ frequency." infer_action: true template: true runtime: @@ -10575,9 +10699,22 @@ syncclk: - OutString(t) require_det_id: true store_result_in_t: true + - arg_types: + - special::freq_unit + argc: 1 + cast_input: [] + check_det_id: false + convert_det_id: true + function: getSYNCClock + input: [] + input_types: [] + output: + - OutString(t , args[0]) + require_det_id: true + store_result_in_t: true command_name: syncclk function_alias: syncclk - help: "[n_clk in MHz]\n\t[Ctb] Sync clock in MHz." + help: "[n_clk] [(optional unit) Hz(default)|kHz|MHz]\n\t[Ctb] Sync clock." infer_action: true template: true temp_10ge: diff --git a/slsDetectorSoftware/generator/gen_commands.py b/slsDetectorSoftware/generator/gen_commands.py index 45dbc878f..4a12ee1af 100644 --- a/slsDetectorSoftware/generator/gen_commands.py +++ b/slsDetectorSoftware/generator/gen_commands.py @@ -122,7 +122,25 @@ def generate( f'StringTo < time::ns > ({", ".join(arg["convert_to_time"]["input"])});') codegen.write_line( f'}} catch (...) {{ throw RuntimeError("Could not convert arguments to time::ns");}}') + elif 'separate_freq_units' in arg and arg['separate_freq_units']: + codegen.write_line(f'try {{') + # TODO: refactor this repeating code + codegen.write_line(f'std::string tmp_freq({arg["separate_freq_units"]["input"]});') + codegen.write_line(f'std::string {arg["separate_freq_units"]["output"][1]}' + f' = RemoveUnit(tmp_freq);') + codegen.write_line(f'auto {arg["separate_freq_units"]["output"][0]} = ' + f'StringTo < defs::Hz > (tmp_freq,' + f' {arg["separate_freq_units"]["output"][1]});') + codegen.write_line( + f'}} catch (...) {{ throw RuntimeError("Could not convert argument to defs::Hz");}}') + elif 'convert_to_freq' in arg and arg['convert_to_freq']: + codegen.write_line(f'try {{') + + codegen.write_line( + f'StringTo < defs::Hz > ({", ".join(arg["convert_to_freq"]["input"])});') + codegen.write_line( + f'}} catch (...) {{ throw RuntimeError("Could not convert arguments to defs::Hz");}}') for i in range(len(arg['input'])): if not arg['cast_input'][i]: continue diff --git a/slsDetectorSoftware/generator/readme.md b/slsDetectorSoftware/generator/readme.md index d480d6c43..2e7054660 100644 --- a/slsDetectorSoftware/generator/readme.md +++ b/slsDetectorSoftware/generator/readme.md @@ -302,7 +302,13 @@ write_arg in codegen reads the argument fields and generate c++ code accordingly std::string $output[1]$ = RemoveUnit(tmp_time); auto $output[0]$ = StringTo(tmp_time, $output[1]$); ``` -- convert_to_time: takes three parameters: input[0], input[1], output +- separate_freq_units: takes three parameters: input, output[0], output[1] each one is a variable name + ```cpp + std::string tmp_freq($input$); + std::string $output[1]$ = RemoveUnit(tmp_freq); + auto $output[0]$ = StringTo(tmp_freq, $output[1]$); + ``` +- convert_to_time and convert_to_freq: takes three parameters: input[0], input[1], output ```cpp auto output = StringTo(input[0], input[1]); ``` diff --git a/slsDetectorSoftware/include/sls/Detector.h b/slsDetectorSoftware/include/sls/Detector.h index a44a46e5f..d107370de 100644 --- a/slsDetectorSoftware/include/sls/Detector.h +++ b/slsDetectorSoftware/include/sls/Detector.h @@ -1613,20 +1613,26 @@ class Detector { /** [CTB] */ void setNumberOfAnalogSamples(int value, Positions pos = {}); - /** [CTB] in MHz, [XCTB] in kHz */ - Result getADCClock(Positions pos = {}) const; + /** [CTB][XCTB] */ + Result getADCClock(Positions pos = {}) const; - /** [CTB] in MHz, [XCTB] in kHz */ - void setADCClock(int value_in_MHz, Positions pos = {}); + /** [CTB][XCTB] */ + void setADCClock(defs::Hz val, Positions pos = {}); - /** [CTB] in MHz, [XCTB] in kHz */ - Result getRUNClock(Positions pos = {}) const; + /** [CTB][XCTB] */ + Result getRUNClock(Positions pos = {}) const; - /** [CTB] in MHz, [XCTB] in kHz */ - void setRUNClock(int value_in_MHz, Positions pos = {}); + /** [CTB][XCTB] */ + void setRUNClock(defs::Hz val, Positions pos = {}); - /** [CTB] in MHZ */ - Result getSYNCClock(Positions pos = {}) const; + /** [CTB][XCTB] */ + Result getDBITClock(Positions pos = {}) const; + + /** [CTB][XCTB] */ + void setDBITClock(defs::Hz val, Positions pos = {}); + + /** [CTB][XCTB] */ + Result getSYNCClock(Positions pos = {}) const; /** gets list of power enums */ std::vector getPowerList() const; @@ -1721,12 +1727,6 @@ class Detector { */ void setReadoutMode(defs::readoutMode value, Positions pos = {}); - /** [CTB] in MHz, [XCTB] in kHz */ - Result getDBITClock(Positions pos = {}) const; - - /** [CTB] in MHz, [XCTB] in kHz */ - void setDBITClock(int value_in_MHz, Positions pos = {}); - /** [CTB] */ Result getExternalSamplingSource(Positions pos = {}) const; @@ -1934,7 +1934,7 @@ class Detector { /** [CTB][Mythen3][Xilinx CTB] Gets the pattern file name including path of * the last pattern uploaded. \n Returns an empty if nothing was uploaded or * via a server default file*/ - Result getPatterFileName(Positions pos = {}) const; + Result getPatternFileName(Positions pos = {}) const; /** [CTB][Mythen3][Xilinx CTB] Loads ASCII pattern file directly to server * (instead of executing line by line)*/ diff --git a/slsDetectorSoftware/include/sls/Result.h b/slsDetectorSoftware/include/sls/Result.h index 0cb937408..5554bdaa8 100644 --- a/slsDetectorSoftware/include/sls/Result.h +++ b/slsDetectorSoftware/include/sls/Result.h @@ -38,6 +38,7 @@ template > class Result { template ::value && (std::is_same::value || + std::is_same::value || std::is_same::value)>::type> Result(const Result &from) { vec.reserve(from.size()); @@ -49,6 +50,7 @@ template > class Result { template ::value && (std::is_same::value || + std::is_same::value || std::is_same::value)>::type> Result(Result &from) { vec.reserve(from.size()); @@ -60,6 +62,7 @@ template > class Result { template ::value && (std::is_same::value || + std::is_same::value || std::is_same::value)>::type> Result(Result &&from) { vec.reserve(from.size()); diff --git a/slsDetectorSoftware/src/Caller.cpp b/slsDetectorSoftware/src/Caller.cpp index 17405241e..1e72aba17 100644 --- a/slsDetectorSoftware/src/Caller.cpp +++ b/slsDetectorSoftware/src/Caller.cpp @@ -72,34 +72,46 @@ std::string Caller::adcclk(int action) { std::ostringstream os; // print help if (action == slsDetectorDefs::HELP_ACTION) { - os << R"V0G0N([n_clk in MHz] - [Ctb] ADC clock frequency in MHz. - [xilinx Ctb] ADC clock frequency in kHz. )V0G0N" + os << R"V0G0N([n_clk] [(optional unit) Hz(default)|kHz|MHz] + [Ctb][Xilinx Ctb] ADC clock frequency. )V0G0N" << std::endl; return os.str(); } // check if action and arguments are valid if (action == slsDetectorDefs::GET_ACTION) { - if (1 && args.size() != 0) { + if (1 && args.size() != 0 && args.size() != 1) { throw RuntimeError("Wrong number of arguments for action GET"); } if (args.size() == 0) { } + if (args.size() == 1) { + } + } else if (action == slsDetectorDefs::PUT_ACTION) { - if (1 && args.size() != 1) { + if (1 && args.size() != 1 && args.size() != 2) { throw RuntimeError("Wrong number of arguments for action PUT"); } if (args.size() == 1) { try { - StringTo(args[0]); + std::string tmp_freq(args[0]); + std::string unit = RemoveUnit(tmp_freq); + auto converted_freq = StringTo(tmp_freq, unit); } catch (...) { - throw RuntimeError("Could not convert argument 0 to int"); + throw RuntimeError("Could not convert argument to defs::Hz"); + } + } + + if (args.size() == 2) { + try { + StringTo(args[0], args[1]); + } catch (...) { + throw RuntimeError("Could not convert arguments to defs::Hz"); } } @@ -117,13 +129,26 @@ std::string Caller::adcclk(int action) { auto t = det->getADCClock(std::vector{det_id}); os << OutString(t) << '\n'; } + + if (args.size() == 1) { + auto t = det->getADCClock(std::vector{det_id}); + os << OutString(t, args[0]) << '\n'; + } } if (action == slsDetectorDefs::PUT_ACTION) { if (args.size() == 1) { - auto arg0 = StringTo(args[0]); - det->setADCClock(arg0, std::vector{det_id}); - os << args.front() << '\n'; + std::string tmp_freq(args[0]); + std::string unit = RemoveUnit(tmp_freq); + auto converted_freq = StringTo(tmp_freq, unit); + det->setADCClock(converted_freq, std::vector{det_id}); + os << args[0] << '\n'; + } + + if (args.size() == 2) { + auto converted_freq = StringTo(args[0], args[1]); + det->setADCClock(converted_freq, std::vector{det_id}); + os << args[0] << args[1] << '\n'; } } @@ -2482,34 +2507,46 @@ std::string Caller::dbitclk(int action) { std::ostringstream os; // print help if (action == slsDetectorDefs::HELP_ACTION) { - os << R"V0G0N([n_clk in MHz] - [Ctb] Clock for latching the digital bits in MHz. - [xilinx Ctb] Clock for latching the digital bits in kHz. )V0G0N" + os << R"V0G0N([n_clk] [(optional unit) Hz(default)|kHz|MHz] + [Ctb][Xilinx Ctb] Clock for latching the digital bits. )V0G0N" << std::endl; return os.str(); } // check if action and arguments are valid if (action == slsDetectorDefs::GET_ACTION) { - if (1 && args.size() != 0) { + if (1 && args.size() != 0 && args.size() != 1) { throw RuntimeError("Wrong number of arguments for action GET"); } if (args.size() == 0) { } + if (args.size() == 1) { + } + } else if (action == slsDetectorDefs::PUT_ACTION) { - if (1 && args.size() != 1) { + if (1 && args.size() != 1 && args.size() != 2) { throw RuntimeError("Wrong number of arguments for action PUT"); } if (args.size() == 1) { try { - StringTo(args[0]); + std::string tmp_freq(args[0]); + std::string unit = RemoveUnit(tmp_freq); + auto converted_freq = StringTo(tmp_freq, unit); } catch (...) { - throw RuntimeError("Could not convert argument 0 to int"); + throw RuntimeError("Could not convert argument to defs::Hz"); + } + } + + if (args.size() == 2) { + try { + StringTo(args[0], args[1]); + } catch (...) { + throw RuntimeError("Could not convert arguments to defs::Hz"); } } @@ -2527,13 +2564,26 @@ std::string Caller::dbitclk(int action) { auto t = det->getDBITClock(std::vector{det_id}); os << OutString(t) << '\n'; } + + if (args.size() == 1) { + auto t = det->getDBITClock(std::vector{det_id}); + os << OutString(t, args[0]) << '\n'; + } } if (action == slsDetectorDefs::PUT_ACTION) { if (args.size() == 1) { - auto arg0 = StringTo(args[0]); - det->setDBITClock(arg0, std::vector{det_id}); - os << args.front() << '\n'; + std::string tmp_freq(args[0]); + std::string unit = RemoveUnit(tmp_freq); + auto converted_freq = StringTo(tmp_freq, unit); + det->setDBITClock(converted_freq, std::vector{det_id}); + os << args[0] << '\n'; + } + + if (args.size() == 2) { + auto converted_freq = StringTo(args[0], args[1]); + det->setDBITClock(converted_freq, std::vector{det_id}); + os << args[0] << args[1] << '\n'; } } @@ -7312,7 +7362,7 @@ std::string Caller::patfname(int action) { // generate code for each action if (action == slsDetectorDefs::GET_ACTION) { if (args.size() == 0) { - auto t = det->getPatterFileName(std::vector{det_id}); + auto t = det->getPatternFileName(std::vector{det_id}); os << OutString(t) << '\n'; } } @@ -9909,34 +9959,46 @@ std::string Caller::runclk(int action) { std::ostringstream os; // print help if (action == slsDetectorDefs::HELP_ACTION) { - os << R"V0G0N([n_clk in MHz] - [Ctb] Run clock in MHz. - [xilinx Ctb] Run clock in kHz. )V0G0N" + os << R"V0G0N([n_clk] [(optional unit) Hz(default)|kHz|MHz] + [Ctb][Xilinx Ctb] Run clock frequency. )V0G0N" << std::endl; return os.str(); } // check if action and arguments are valid if (action == slsDetectorDefs::GET_ACTION) { - if (1 && args.size() != 0) { + if (1 && args.size() != 0 && args.size() != 1) { throw RuntimeError("Wrong number of arguments for action GET"); } if (args.size() == 0) { } + if (args.size() == 1) { + } + } else if (action == slsDetectorDefs::PUT_ACTION) { - if (1 && args.size() != 1) { + if (1 && args.size() != 1 && args.size() != 2) { throw RuntimeError("Wrong number of arguments for action PUT"); } if (args.size() == 1) { try { - StringTo(args[0]); + std::string tmp_freq(args[0]); + std::string unit = RemoveUnit(tmp_freq); + auto converted_freq = StringTo(tmp_freq, unit); } catch (...) { - throw RuntimeError("Could not convert argument 0 to int"); + throw RuntimeError("Could not convert argument to defs::Hz"); + } + } + + if (args.size() == 2) { + try { + StringTo(args[0], args[1]); + } catch (...) { + throw RuntimeError("Could not convert arguments to defs::Hz"); } } @@ -9954,13 +10016,26 @@ std::string Caller::runclk(int action) { auto t = det->getRUNClock(std::vector{det_id}); os << OutString(t) << '\n'; } + + if (args.size() == 1) { + auto t = det->getRUNClock(std::vector{det_id}); + os << OutString(t, args[0]) << '\n'; + } } if (action == slsDetectorDefs::PUT_ACTION) { if (args.size() == 1) { - auto arg0 = StringTo(args[0]); - det->setRUNClock(arg0, std::vector{det_id}); - os << args.front() << '\n'; + std::string tmp_freq(args[0]); + std::string unit = RemoveUnit(tmp_freq); + auto converted_freq = StringTo(tmp_freq, unit); + det->setRUNClock(converted_freq, std::vector{det_id}); + os << args[0] << '\n'; + } + + if (args.size() == 2) { + auto converted_freq = StringTo(args[0], args[1]); + det->setRUNClock(converted_freq, std::vector{det_id}); + os << args[0] << args[1] << '\n'; } } @@ -13062,21 +13137,24 @@ std::string Caller::syncclk(int action) { std::ostringstream os; // print help if (action == slsDetectorDefs::HELP_ACTION) { - os << R"V0G0N([n_clk in MHz] - [Ctb] Sync clock in MHz. )V0G0N" + os << R"V0G0N([n_clk] [(optional unit) Hz(default)|kHz|MHz] + [Ctb] Sync clock. )V0G0N" << std::endl; return os.str(); } // check if action and arguments are valid if (action == slsDetectorDefs::GET_ACTION) { - if (1 && args.size() != 0) { + if (1 && args.size() != 0 && args.size() != 1) { throw RuntimeError("Wrong number of arguments for action GET"); } if (args.size() == 0) { } + if (args.size() == 1) { + } + } else { @@ -13091,6 +13169,11 @@ std::string Caller::syncclk(int action) { auto t = det->getSYNCClock(std::vector{det_id}); os << OutString(t) << '\n'; } + + if (args.size() == 1) { + auto t = det->getSYNCClock(std::vector{det_id}); + os << OutString(t, args[0]) << '\n'; + } } return os.str(); diff --git a/slsDetectorSoftware/src/Caller.h b/slsDetectorSoftware/src/Caller.h index ce9e7cc93..d35ffe569 100644 --- a/slsDetectorSoftware/src/Caller.h +++ b/slsDetectorSoftware/src/Caller.h @@ -54,6 +54,9 @@ class Caller { return ToString(value, unit); } + std::string OutString(const Result &value, + const std::string &unit); + std::vector getAllCommands(); std::map GetDeprecatedCommands(); std::string list(int action); @@ -421,6 +424,7 @@ class Caller { defs::dacIndex parseDacIndex(int argIndex, bool isCtb); bool parseMV(int argIndex); defs::powerIndex parsePowerIndex(int argIndex); + defs::FrequencyUnit parseFrequencyUnit(const std::string &s); FunctionMap functions{ {"list", &Caller::list}, diff --git a/slsDetectorSoftware/src/CallerSpecial.cpp b/slsDetectorSoftware/src/CallerSpecial.cpp index cf339ee5d..50cd1e72d 100644 --- a/slsDetectorSoftware/src/CallerSpecial.cpp +++ b/slsDetectorSoftware/src/CallerSpecial.cpp @@ -11,6 +11,14 @@ namespace sls { // some helper functions to print +std::string Caller::OutString(const Result &value, + const std::string &unit) { + auto u = parseFrequencyUnit(unit); + if (value.equal()) + return ToString(value.front(), u); + return ToString(value, u); +} + std::vector Caller::getAllCommands() { std::vector ret; for (auto it : functions) @@ -2105,4 +2113,21 @@ std::string Caller::powervalues(int action) { return os.str(); } +defs::FrequencyUnit Caller::parseFrequencyUnit(const std::string &unit) { + auto unitLower = [&] { + std::string result = unit; + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return std::tolower(c); }); + return result; + }(); + if (unitLower == "hz") + return defs::FrequencyUnit::Hz; + if (unitLower == "khz") + return defs::FrequencyUnit::kHz; + if (unitLower == "mhz") + return defs::FrequencyUnit::MHz; + + throw std::runtime_error("Unknown frequency unit: " + unit); +} + } // namespace sls \ No newline at end of file diff --git a/slsDetectorSoftware/src/Detector.cpp b/slsDetectorSoftware/src/Detector.cpp index a6bd47a2f..01e9f5a44 100644 --- a/slsDetectorSoftware/src/Detector.cpp +++ b/slsDetectorSoftware/src/Detector.cpp @@ -2144,25 +2144,34 @@ void Detector::setNumberOfAnalogSamples(int value, Positions pos) { pimpl->Parallel(&Module::setNumberOfAnalogSamples, pos, value); } -Result Detector::getADCClock(Positions pos) const { +Result Detector::getADCClock(Positions pos) const { return pimpl->Parallel(&Module::getClockFrequency, pos, defs::ADC_CLOCK); } -void Detector::setADCClock(int value_in_MHz, Positions pos) { +void Detector::setADCClock(defs::Hz val, Positions pos) { pimpl->Parallel(&Module::setClockFrequency, pos, defs::ADC_CLOCK, - value_in_MHz); + val.value); } -Result Detector::getRUNClock(Positions pos) const { +Result Detector::getRUNClock(Positions pos) const { return pimpl->Parallel(&Module::getClockFrequency, pos, defs::RUN_CLOCK); } -void Detector::setRUNClock(int value_in_MHz, Positions pos) { +void Detector::setRUNClock(defs::Hz val, Positions pos) { pimpl->Parallel(&Module::setClockFrequency, pos, defs::RUN_CLOCK, - value_in_MHz); + val.value); } -Result Detector::getSYNCClock(Positions pos) const { +Result Detector::getDBITClock(Positions pos) const { + return pimpl->Parallel(&Module::getClockFrequency, pos, defs::DBIT_CLOCK); +} + +void Detector::setDBITClock(defs::Hz val, Positions pos) { + pimpl->Parallel(&Module::setClockFrequency, pos, defs::DBIT_CLOCK, + val.value); +} + +Result Detector::getSYNCClock(Positions pos) const { return pimpl->Parallel(&Module::getClockFrequency, pos, defs::SYNC_CLOCK); } @@ -2310,15 +2319,6 @@ void Detector::setReadoutMode(defs::readoutMode value, Positions pos) { pimpl->Parallel(&Module::setReadoutMode, pos, value); } -Result Detector::getDBITClock(Positions pos) const { - return pimpl->Parallel(&Module::getClockFrequency, pos, defs::DBIT_CLOCK); -} - -void Detector::setDBITClock(int value_in_MHz, Positions pos) { - pimpl->Parallel(&Module::setClockFrequency, pos, defs::DBIT_CLOCK, - value_in_MHz); -} - Result Detector::getSlowADC(defs::dacIndex index, Positions pos) const { if (index < defs::SLOW_ADC0 || index > defs::SLOW_ADC7) { throw RuntimeError("Unknown Slow ADC Index"); @@ -2609,8 +2609,8 @@ void Detector::configureTransceiver(Positions pos) { // Pattern -Result Detector::getPatterFileName(Positions pos) const { - return pimpl->Parallel(&Module::getPatterFileName, pos); +Result Detector::getPatternFileName(Positions pos) const { + return pimpl->Parallel(&Module::getPatternFileName, pos); } void Detector::setPattern(const std::string &fname, Positions pos) { diff --git a/slsDetectorSoftware/src/DetectorImpl.cpp b/slsDetectorSoftware/src/DetectorImpl.cpp index 3ce196ce9..b83e3a52e 100644 --- a/slsDetectorSoftware/src/DetectorImpl.cpp +++ b/slsDetectorSoftware/src/DetectorImpl.cpp @@ -196,7 +196,7 @@ void DetectorImpl::setHostname(const std::vector &name) { } void DetectorImpl::addModule(const std::string &name) { - LOG(logINFO) << "Adding module " << name; + LOG(TLogLevel::logINFO) << "Adding module " << name; auto host = verifyUniqueDetHost(name); std::string hostname = host.first; uint16_t port = host.second; diff --git a/slsDetectorSoftware/src/Module.cpp b/slsDetectorSoftware/src/Module.cpp index 6724f1d9f..17fe8841b 100644 --- a/slsDetectorSoftware/src/Module.cpp +++ b/slsDetectorSoftware/src/Module.cpp @@ -50,8 +50,6 @@ std::string Module::getHostname() const { return shm()->hostname; } void Module::setHostname(const std::string &hostname, const bool initialChecks) { strcpy_safe(shm()->hostname, hostname.c_str()); - auto client = DetectorSocket(shm()->hostname, shm()->controlPort); - client.close(); try { checkDetectorVersionCompatibility(); initialDetectorServerChecks(); @@ -87,9 +85,11 @@ std::string Module::getControlServerLongVersion() const { // throw with old server version (sends 8 bytes) catch (RuntimeError &e) { std::string emsg = std::string(e.what()); + if (emsg.find(F_GET_SERVER_VERSION) && emsg.find("8 bytes")) { throwDeprecatedServerVersion(); } + throw; } } @@ -146,7 +146,6 @@ std::string Module::getReceiverSoftwareVersion() const { // static function slsDetectorDefs::detectorType Module::getTypeFromDetector(const std::string &hostname, uint16_t cport) { - LOG(logDEBUG1) << "Getting Module type "; ClientSocket socket("Detector", hostname, cport); socket.Send(F_GET_DETECTOR_TYPE); socket.setFnum(F_GET_DETECTOR_TYPE); @@ -2669,7 +2668,7 @@ void Module::configureTransceiver() { } // Pattern -std::string Module::getPatterFileName() const { +std::string Module::getPatternFileName() const { char retval[MAX_STR_LENGTH]{}; sendToDetector(F_GET_PATTERN_FILE_NAME, nullptr, retval); return retval; @@ -3515,6 +3514,9 @@ void Module::initialDetectorServerChecks() { void Module::checkDetectorVersionCompatibility() { std::string detServers[2] = {getControlServerLongVersion(), getStopServerLongVersion()}; + LOG(logDEBUG1) + << "Checking detector version compatibility with client version " + << detServers[0] << " and " << detServers[1]; for (int i = 0; i != 2; ++i) { // det and client (sem. versioning) Version det(detServers[i]); @@ -3563,6 +3565,8 @@ const std::string Module::getDetectorAPI() const { return APIGOTTHARD2; case XILINX_CHIPTESTBOARD: return APIXILINXCTB; + case MATTERHORN: + return APIMATTERHORN; default: throw NotImplementedError( "Detector type not implemented to get Detector API"); diff --git a/slsDetectorSoftware/src/Module.h b/slsDetectorSoftware/src/Module.h index 1f1f5c2e0..cf4672c93 100644 --- a/slsDetectorSoftware/src/Module.h +++ b/slsDetectorSoftware/src/Module.h @@ -536,7 +536,7 @@ class Module : public virtual slsDetectorDefs { * Pattern * * * * ************************************************/ - std::string getPatterFileName() const; + std::string getPatternFileName() const; void setPattern(const Pattern &pat, const std::string &fname); Pattern getPattern(); void loadDefaultPattern(); diff --git a/slsDetectorSoftware/src/inferAction.cpp b/slsDetectorSoftware/src/inferAction.cpp index 25467d1d2..af2ff6396 100644 --- a/slsDetectorSoftware/src/inferAction.cpp +++ b/slsDetectorSoftware/src/inferAction.cpp @@ -63,6 +63,12 @@ int InferAction::adcclk() { } if (args.size() == 1) { + throw RuntimeError( + "sls_detector is disabled for command: adcclk with number of " + "arguments 1. Use sls_detector_get or sls_detector_put"); + } + + if (args.size() == 2) { return slsDetectorDefs::PUT_ACTION; } @@ -711,6 +717,12 @@ int InferAction::dbitclk() { } if (args.size() == 1) { + throw RuntimeError( + "sls_detector is disabled for command: dbitclk with number of " + "arguments 1. Use sls_detector_get or sls_detector_put"); + } + + if (args.size() == 2) { return slsDetectorDefs::PUT_ACTION; } @@ -2595,6 +2607,12 @@ int InferAction::runclk() { } if (args.size() == 1) { + throw RuntimeError( + "sls_detector is disabled for command: runclk with number of " + "arguments 1. Use sls_detector_get or sls_detector_put"); + } + + if (args.size() == 2) { return slsDetectorDefs::PUT_ACTION; } @@ -3477,6 +3495,10 @@ int InferAction::syncclk() { return slsDetectorDefs::GET_ACTION; } + if (args.size() == 1) { + return slsDetectorDefs::GET_ACTION; + } + else { throw RuntimeError("Could not infer action: Wrong number of arguments"); diff --git a/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp b/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp index 71c64048f..214c2ffa8 100644 --- a/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp +++ b/slsDetectorSoftware/tests/Caller/test-Caller-chiptestboard.cpp @@ -921,23 +921,65 @@ TEST_CASE("adcclk", "[.detectorintegration]") { Caller caller(&det); auto det_type = det.getDetectorType().squash(); - if (det_type == defs::CHIPTESTBOARD) { + if (det_type == defs::CHIPTESTBOARD || + det_type == defs::XILINX_CHIPTESTBOARD) { auto prev_val = det.getADCClock(); + + REQUIRE_NOTHROW(caller.call("adcclk", {"MHZ"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("adcclk", {"mhz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("adcclk", {"MHz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("adcclk", {"kHz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("adcclk", {"Hz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("adcclk", {}, -1, GET)); + // min + if (det_type == defs::CHIPTESTBOARD) + REQUIRE_THROWS(caller.call("adcclk", {"1", "MHz"}, -1, PUT)); + else + REQUIRE_THROWS(caller.call("adcclk", {"9", "MHz"}, -1, PUT)); + // max + if (det_type == defs::CHIPTESTBOARD) + REQUIRE_THROWS(caller.call("adcclk", {"66", "MHz"}, -1, PUT)); + else + REQUIRE_THROWS(caller.call("adcclk", {"301", "MHz"}, -1, PUT)); + { std::ostringstream oss; - caller.call("adcclk", {"20"}, -1, PUT, oss); - REQUIRE(oss.str() == "adcclk 20\n"); + caller.call("adcclk", {"20MHz"}, -1, PUT, oss); + REQUIRE(oss.str() == "adcclk 20MHz\n"); } { std::ostringstream oss; - caller.call("adcclk", {"10"}, -1, PUT, oss); - REQUIRE(oss.str() == "adcclk 10\n"); + caller.call("adcclk", {"10000000"}, -1, PUT, oss); + REQUIRE(oss.str() == "adcclk 10000000\n"); + } + + { + std::ostringstream oss; + caller.call("adcclk", {}, -1, GET, oss); + REQUIRE(oss.str() == "adcclk 10MHz\n"); + } + { + std::ostringstream oss; + caller.call("adcclk", {"15000", "kHz"}, -1, PUT, oss); + REQUIRE(oss.str() == "adcclk 15000kHz\n"); } { std::ostringstream oss; caller.call("adcclk", {}, -1, GET, oss); - REQUIRE(oss.str() == "adcclk 10\n"); + REQUIRE(oss.str() == "adcclk 15MHz\n"); } + { + std::ostringstream oss; + caller.call("adcclk", {"15.75", "MHz"}, -1, PUT, oss); + REQUIRE(oss.str() == "adcclk 15.75MHz\n"); + } + { + std::ostringstream oss; + caller.call("adcclk", {}, -1, GET, oss); + REQUIRE(oss.str() == "adcclk 15.75MHz\n"); + } + std::cout << "Resetting adc clock to :" << ToString(prev_val) + << std::endl; for (int i = 0; i != det.size(); ++i) { det.setADCClock(prev_val[i], {i}); } @@ -952,25 +994,119 @@ TEST_CASE("runclk", "[.detectorintegration]") { Caller caller(&det); auto det_type = det.getDetectorType().squash(); - if (det_type == defs::CHIPTESTBOARD) { + if (det_type == defs::CHIPTESTBOARD || + det_type == defs::XILINX_CHIPTESTBOARD) { auto prev_val = det.getRUNClock(); + + REQUIRE_NOTHROW(caller.call("runclk", {"MHZ"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("runclk", {"mhz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("runclk", {"MHz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("runclk", {"kHz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("runclk", {"Hz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("runclk", {}, -1, GET)); + // min + if (det_type == defs::CHIPTESTBOARD) + REQUIRE_THROWS(caller.call("runclk", {"1", "MHz"}, -1, PUT)); + else + REQUIRE_THROWS(caller.call("runclk", {"9", "MHz"}, -1, PUT)); + // max + REQUIRE_THROWS(caller.call("runclk", {"301", "MHz"}, -1, PUT)); + { std::ostringstream oss; - caller.call("runclk", {"20"}, -1, PUT, oss); - REQUIRE(oss.str() == "runclk 20\n"); + caller.call("runclk", {"20MHz"}, -1, PUT, oss); + REQUIRE(oss.str() == "runclk 20MHz\n"); } { std::ostringstream oss; - caller.call("runclk", {"10"}, -1, PUT, oss); - REQUIRE(oss.str() == "runclk 10\n"); + caller.call("runclk", {"10000000"}, -1, PUT, oss); + REQUIRE(oss.str() == "runclk 10000000\n"); + } + + { + std::ostringstream oss; + caller.call("runclk", {}, -1, GET, oss); + REQUIRE(oss.str() == "runclk 10MHz\n"); + } + { + std::ostringstream oss; + caller.call("runclk", {"15000", "kHz"}, -1, PUT, oss); + REQUIRE(oss.str() == "runclk 15000kHz\n"); } { std::ostringstream oss; caller.call("runclk", {}, -1, GET, oss); - REQUIRE(oss.str() == "runclk 10\n"); + REQUIRE(oss.str() == "runclk 15MHz\n"); + } + { + std::ostringstream oss; + caller.call("runclk", {"15.75", "MHz"}, -1, PUT, oss); + REQUIRE(oss.str() == "runclk 15.75MHz\n"); + } + { + std::ostringstream oss; + caller.call("runclk", {}, -1, GET, oss); + REQUIRE(oss.str() == "runclk 15.75MHz\n"); + } + // tolerance + auto prev_exptime = det.getExptime(); + auto prev_period = det.getPeriod(); + auto prev_delay = det.getDelayAfterTrigger(); + { + caller.call("runclk", {"80", "MHz"}, -1, PUT); + { + std::ostringstream oss; + REQUIRE_NOTHROW( + caller.call("exptime", {"10012", "ns"}, -1, PUT)); + REQUIRE_NOTHROW(caller.call("exptime", {"ns"}, -1, GET, oss)); + REQUIRE(oss.str() == "exptime 10013ns\n"); + } + { + std::ostringstream oss; + REQUIRE_NOTHROW( + caller.call("exptime", {"10013", "ns"}, -1, PUT)); + REQUIRE_NOTHROW(caller.call("exptime", {"ns"}, -1, GET, oss)); + REQUIRE(oss.str() == "exptime 10013ns\n"); + } + { + std::ostringstream oss; + REQUIRE_NOTHROW( + caller.call("exptime", {"10019", "ns"}, -1, PUT)); + REQUIRE_NOTHROW(caller.call("exptime", {"ns"}, -1, GET, oss)); + REQUIRE(oss.str() == "exptime 10025ns\n"); + } + { + std::ostringstream oss; + REQUIRE_NOTHROW( + caller.call("period", {"10125", "ns"}, -1, PUT)); + REQUIRE_NOTHROW(caller.call("period", {"ns"}, -1, GET, oss)); + REQUIRE(oss.str() == "period 10125ns\n"); + } + { + std::ostringstream oss; + REQUIRE_NOTHROW( + caller.call("period", {"10124", "ns"}, -1, PUT)); + REQUIRE_NOTHROW(caller.call("period", {"ns"}, -1, GET, oss)); + REQUIRE(oss.str() == "period 10125ns\n"); + } + { + std::ostringstream oss; + REQUIRE_NOTHROW(caller.call("delay", {"10125", "ns"}, -1, PUT)); + REQUIRE_NOTHROW(caller.call("delay", {"ns"}, -1, GET, oss)); + REQUIRE(oss.str() == "delay 10125ns\n"); + } + { + std::ostringstream oss; + REQUIRE_NOTHROW(caller.call("delay", {"10124", "ns"}, -1, PUT)); + REQUIRE_NOTHROW(caller.call("delay", {"ns"}, -1, GET, oss)); + REQUIRE(oss.str() == "delay 10125ns\n"); + } } for (int i = 0; i != det.size(); ++i) { det.setRUNClock(prev_val[i], {i}); + det.setExptime(prev_exptime[i], {i}); + det.setPeriod(prev_period[i], {i}); + det.setDelayAfterTrigger(prev_delay[i], {i}); } } else { // clock index might work @@ -983,6 +1119,11 @@ TEST_CASE("syncclk", "[.detectorintegration]") { Caller caller(&det); auto det_type = det.getDetectorType().squash(); if (det_type == defs::CHIPTESTBOARD) { + REQUIRE_NOTHROW(caller.call("syncclk", {"MHZ"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("syncclk", {"mhz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("syncclk", {"MHz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("syncclk", {"kHz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("syncclk", {"Hz"}, -1, GET)); REQUIRE_NOTHROW(caller.call("syncclk", {}, -1, GET)); } else { // clock index might work @@ -1249,22 +1390,59 @@ TEST_CASE("dbitclk", "[.detectorintegration]") { Caller caller(&det); auto det_type = det.getDetectorType().squash(); - if (det_type == defs::CHIPTESTBOARD) { + if (det_type == defs::CHIPTESTBOARD || + det_type == defs::XILINX_CHIPTESTBOARD) { auto prev_val = det.getDBITClock(); + + REQUIRE_NOTHROW(caller.call("dbitclk", {"MHZ"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("dbitclk", {"mhz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("dbitclk", {"MHz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("dbitclk", {"kHz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("dbitclk", {"Hz"}, -1, GET)); + REQUIRE_NOTHROW(caller.call("dbitclk", {}, -1, GET)); + // min + if (det_type == defs::CHIPTESTBOARD) + REQUIRE_THROWS(caller.call("dbitclk", {"1", "MHz"}, -1, PUT)); + else + REQUIRE_THROWS(caller.call("dbitclk", {"9", "MHz"}, -1, PUT)); + // max + REQUIRE_THROWS(caller.call("dbitclk", {"301", "MHz"}, -1, PUT)); + { std::ostringstream oss; - caller.call("dbitclk", {"20"}, -1, PUT, oss); - REQUIRE(oss.str() == "dbitclk 20\n"); + caller.call("dbitclk", {"20MHz"}, -1, PUT, oss); + REQUIRE(oss.str() == "dbitclk 20MHz\n"); } { std::ostringstream oss; - caller.call("dbitclk", {"10"}, -1, PUT, oss); - REQUIRE(oss.str() == "dbitclk 10\n"); + caller.call("dbitclk", {"10000000"}, -1, PUT, oss); + REQUIRE(oss.str() == "dbitclk 10000000\n"); + } + + { + std::ostringstream oss; + caller.call("dbitclk", {}, -1, GET, oss); + REQUIRE(oss.str() == "dbitclk 10MHz\n"); + } + { + std::ostringstream oss; + caller.call("dbitclk", {"15000", "kHz"}, -1, PUT, oss); + REQUIRE(oss.str() == "dbitclk 15000kHz\n"); } { std::ostringstream oss; caller.call("dbitclk", {}, -1, GET, oss); - REQUIRE(oss.str() == "dbitclk 10\n"); + REQUIRE(oss.str() == "dbitclk 15MHz\n"); + } + { + std::ostringstream oss; + caller.call("dbitclk", {"15.75", "MHz"}, -1, PUT, oss); + REQUIRE(oss.str() == "dbitclk 15.75MHz\n"); + } + { + std::ostringstream oss; + caller.call("dbitclk", {}, -1, GET, oss); + REQUIRE(oss.str() == "dbitclk 15.75MHz\n"); } for (int i = 0; i != det.size(); ++i) { det.setDBITClock(prev_val[i], {i}); diff --git a/slsReceiverSoftware/src/ClientInterface.cpp b/slsReceiverSoftware/src/ClientInterface.cpp index 7acf59e2e..0c08534a8 100644 --- a/slsReceiverSoftware/src/ClientInterface.cpp +++ b/slsReceiverSoftware/src/ClientInterface.cpp @@ -85,7 +85,6 @@ void ClientInterface::startTCPServer() { << '\n'; while (!killTcpThread) { - LOG(logDEBUG1) << "Start accept loop"; try { auto socket = server.accept(); try { @@ -96,7 +95,7 @@ void ClientInterface::startTCPServer() { // We had an error needs to be sent to client char mess[MAX_STR_LENGTH]{}; strcpy_safe(mess, e.what()); - socket.Send(FAIL); + socket.Send(slsDetectorDefs::FAIL); socket.Send(mess); } // if tcp command was to exit server diff --git a/slsSupportLib/CMakeLists.txt b/slsSupportLib/CMakeLists.txt index 995d24819..e0431c3cc 100755 --- a/slsSupportLib/CMakeLists.txt +++ b/slsSupportLib/CMakeLists.txt @@ -95,7 +95,7 @@ target_link_libraries(slsSupportObject slsProjectOptions ${STD_FS_LIB} # from helpers.cmake Threads::Threads # slsDetector and Receiver need this - + fmt::fmt PRIVATE slsProjectWarnings md5sls diff --git a/slsSupportLib/include/sls/DataSocket.h b/slsSupportLib/include/sls/DataSocket.h index 01215c20e..31d9afa72 100644 --- a/slsSupportLib/include/sls/DataSocket.h +++ b/slsSupportLib/include/sls/DataSocket.h @@ -3,6 +3,7 @@ #pragma once #include "sls/TypeTraits.h" +#include "sls/logger.h" #include #include #include diff --git a/slsSupportLib/include/sls/ServerInterface.h b/slsSupportLib/include/sls/ServerInterface.h index 475201983..d4ed04576 100644 --- a/slsSupportLib/include/sls/ServerInterface.h +++ b/slsSupportLib/include/sls/ServerInterface.h @@ -3,6 +3,8 @@ #pragma once #include "sls/DataSocket.h" +#include "sls/logger.h" + namespace sls { class ServerInterface; } diff --git a/slsSupportLib/include/sls/ToString.h b/slsSupportLib/include/sls/ToString.h index 777a98207..7035f9c38 100644 --- a/slsSupportLib/include/sls/ToString.h +++ b/slsSupportLib/include/sls/ToString.h @@ -108,6 +108,12 @@ ToString(From t) { } } +/** Convert frequency with specified output unit */ +std::string ToString(defs::Hz f, defs::FrequencyUnit unit); + +/** Convert frequency automatically selecting the unit */ +std::string ToString(defs::Hz f); + /** Conversion of floating point values, removes trailing zeros*/ template typename std::enable_if::value, std::string>::type @@ -279,7 +285,23 @@ ToString(const T &container, const std::string &unit) { return os.str(); } +/** Container and specified unit, call ToString(value, FrequencyUnit) */ template +typename std::enable_if::value, std::string>::type +ToString(const T &container, defs::FrequencyUnit unit) { + std::ostringstream os; + os << '['; + if (!container.empty()) { + auto it = container.cbegin(); + os << ToString(*it++, unit); + while (it != container.cend()) + os << ", " << ToString(*it++, unit); + } + os << ']'; + return os.str(); +} + +template ::value, int> = 0> T StringTo(const std::string &t, const std::string &unit) { double tval{0}; try { @@ -304,6 +326,33 @@ T StringTo(const std::string &t, const std::string &unit) { } } +template ::value, int> = 0> +T StringTo(const std::string &f, const std::string &unit) { + double fval{0}; + try { + fval = std::stod(f); + } catch (const std::invalid_argument &e) { + throw RuntimeError("Could not convert string to frequency"); + } + auto unitLower = [&] { + std::string result = unit; + std::transform(result.begin(), result.end(), result.begin(), + [](unsigned char c) { return std::tolower(c); }); + return result; + }(); + + if (unitLower == "mhz") { + return T(static_cast(fval * 1e6)); + } else if (unitLower == "khz") { + return T(static_cast(fval * 1e3)); + } else if (unitLower.empty() || unitLower == "hz") { + return T(static_cast(fval)); + } else { + throw RuntimeError( + "Invalid unit in conversion from string to frequency"); + } +} + template T StringTo(const std::string &t) { std::string tmp{t}; auto unit = RemoveUnit(tmp); diff --git a/slsSupportLib/include/sls/TypeTraits.h b/slsSupportLib/include/sls/TypeTraits.h index 1a153e67b..926b2227d 100644 --- a/slsSupportLib/include/sls/TypeTraits.h +++ b/slsSupportLib/include/sls/TypeTraits.h @@ -1,6 +1,8 @@ // SPDX-License-Identifier: LGPL-3.0-or-other // Copyright (C) 2021 Contributors to the SLS Detector Package #pragma once +#include "sls/sls_detector_defs.h" + #include #include @@ -120,4 +122,9 @@ struct has_bool_isValid : std::false_type {}; template struct has_bool_isValid().isValid)>> : std::is_same().isValid), bool> {}; + +template struct is_frequency : std::false_type {}; + +template <> struct is_frequency : std::true_type {}; + } // namespace sls \ No newline at end of file diff --git a/slsSupportLib/include/sls/logger.h b/slsSupportLib/include/sls/logger.h index 9b949d802..5754286e7 100644 --- a/slsSupportLib/include/sls/logger.h +++ b/slsSupportLib/include/sls/logger.h @@ -27,11 +27,6 @@ enum TLogLevel { logDEBUG5 }; -// Compiler should optimize away anything below this value -#ifndef LOG_MAX_REPORTING_LEVEL -#define LOG_MAX_REPORTING_LEVEL sls::logINFO -#endif - #define __AT__ \ std::string(__FILE__) + std::string("::") + std::string(__func__) + \ std::string("(): ") @@ -43,7 +38,7 @@ enum TLogLevel { class Logger { std::ostringstream os; - TLogLevel level = LOG_MAX_REPORTING_LEVEL; + TLogLevel level = ReportingLevel(); public: Logger() = default; @@ -55,7 +50,7 @@ class Logger { } static TLogLevel &ReportingLevel() { // singelton eeh - static TLogLevel reportingLevel = logINFO; + static TLogLevel reportingLevel = LOG_MAX_REPORTING_LEVEL; return reportingLevel; } diff --git a/slsSupportLib/include/sls/sls_detector_defs.h b/slsSupportLib/include/sls/sls_detector_defs.h index f8b4b7877..283eb2f5e 100644 --- a/slsSupportLib/include/sls/sls_detector_defs.h +++ b/slsSupportLib/include/sls/sls_detector_defs.h @@ -46,6 +46,9 @@ #define DEFAULT_UDP_SRC_PORTNO 32410 #define DEFAULT_UDP_DST_PORTNO 50001 +/** for virtual detectors */ +#define LOCALHOSTIP_INT 2130706433 + #define MAX_UDP_DESTINATION 32 #define SLS_DETECTOR_HEADER_VERSION 0x2 @@ -82,8 +85,13 @@ #define DEFAULT_STREAMING_TIMER_IN_MS 500 #define NUM_RX_THREAD_IDS 9 + // NOLINTEND(cppcoreguidelines-macro-usage) #ifdef __cplusplus + +// TODO: why are all these defs inside a class? - why not static +enum ReturnCode { OK = 0, FAIL = 1 }; + class slsDetectorDefs { public: #endif @@ -98,7 +106,9 @@ class slsDetectorDefs { MOENCH, MYTHEN3, GOTTHARD2, - XILINX_CHIPTESTBOARD + XILINX_CHIPTESTBOARD, + MATTERHORN // TODO: maybe better to have it under a namespace + // slsDetectorDefs instead of grouped in a class }; /** return values */ @@ -211,6 +221,16 @@ class slsDetectorDefs { std::map addJsonHeader; }; + struct Hz { + int value{0}; + explicit Hz(int v) : value(v){}; + constexpr bool operator==(const Hz &other) const { + return (value == other.value); + } + }; + + enum class FrequencyUnit { Hz, kHz, MHz }; + #endif enum frameDiscardPolicy { NO_DISCARD, @@ -758,6 +778,13 @@ struct detParameters { nChipY = 1; nDacs = 14; break; + case slsDetectorDefs::detectorType::MATTERHORN: + nChanX = 256; + nChanY = 256; + nChipX = 4; + nChipY = 2; + nDacs = 31; + break; default: throw sls::RuntimeError("Unknown detector type! " + std::to_string(type)); @@ -837,7 +864,6 @@ typedef struct { #endif #ifdef __cplusplus - // TODO! discuss this #include //hmm... but currently no way around namespace sls { diff --git a/slsSupportLib/include/sls/versionAPI.h b/slsSupportLib/include/sls/versionAPI.h index 89017dad9..32fba195b 100644 --- a/slsSupportLib/include/sls/versionAPI.h +++ b/slsSupportLib/include/sls/versionAPI.h @@ -1,12 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-or-other // Copyright (C) 2021 Contributors to the SLS Detector Package /** API versions */ -#define APILIB "0.0.0 0x250909" -#define APIRECEIVER "0.0.0 0x250822" -#define APICTB "0.0.0 0x260427" -#define APIGOTTHARD2 "0.0.0 0x260427" -#define APIMOENCH "0.0.0 0x260424" -#define APIEIGER "0.0.0 0x260424" -#define APIXILINXCTB "0.0.0 0x260427" -#define APIJUNGFRAU "0.0.0 0x260424" -#define APIMYTHEN3 "0.0.0 0x260427" +#define APILIB "0.0.0 0x250909" +#define APIRECEIVER "0.0.0 0x250822" +#define APICTB "0.0.0 0x260506" +#define APIGOTTHARD2 "0.0.0 0x260427" +#define APIMOENCH "0.0.0 0x260424" +#define APIEIGER "0.0.0 0x260424" +#define APIXILINXCTB "0.0.0 0x260506" +#define APIJUNGFRAU "0.0.0 0x260424" +#define APIMYTHEN3 "0.0.0 0x260506" +#define APIMATTERHORN "0.0.0 0x260212" diff --git a/slsSupportLib/src/ServerSocket.cpp b/slsSupportLib/src/ServerSocket.cpp index e61c5d132..08c12b562 100644 --- a/slsSupportLib/src/ServerSocket.cpp +++ b/slsSupportLib/src/ServerSocket.cpp @@ -52,9 +52,11 @@ ServerInterface ServerSocket::accept() { if (newSocket == -1) { throw SocketError("Server ERROR: socket accept failed\n"); } + char tc[INET_ADDRSTRLEN]{}; inet_ntop(AF_INET, &(clientAddr.sin_addr), tc, INET_ADDRSTRLEN); thisClient = IpAddr{tc}; + // Set socket buffer size return ServerInterface(newSocket); } diff --git a/slsSupportLib/src/ToString.cpp b/slsSupportLib/src/ToString.cpp index f8ac4c684..505b4517f 100644 --- a/slsSupportLib/src/ToString.cpp +++ b/slsSupportLib/src/ToString.cpp @@ -221,6 +221,8 @@ std::string ToString(const defs::detectorType s) { return std::string("Gotthard2"); case defs::XILINX_CHIPTESTBOARD: return std::string("Xilinx_ChipTestBoard"); + case defs::MATTERHORN: + return std::string("Matterhorn"); default: return std::string("Unknown"); } @@ -739,6 +741,36 @@ std::string ToString(const defs::collectionMode s) { const std::string &ToString(const std::string &s) { return s; } +std::string ToString(defs::Hz f, defs::FrequencyUnit unit) { + double val = static_cast(f.value); + std::ostringstream os; + switch (unit) { + case defs::FrequencyUnit::Hz: + os << val << "Hz"; + break; + case defs::FrequencyUnit::kHz: + os << val / (static_cast(1e3)) << "kHz"; + break; + case defs::FrequencyUnit::MHz: + os << val / (static_cast(1e6)) << "MHz"; + break; + default: + throw std::runtime_error("Unknown frequency unit"); + } + return os.str(); +} + +std::string ToString(defs::Hz f) { + int val = f.value; + if (val < 1e3) { + return ToString(f, defs::FrequencyUnit::Hz); + } else if (val < 1e6) { + return ToString(f, defs::FrequencyUnit::kHz); + } else { + return ToString(f, defs::FrequencyUnit::MHz); + } +} + template <> defs::detectorType StringTo(const std::string &s) { if (s == "Eiger") return defs::EIGER; @@ -756,6 +788,8 @@ template <> defs::detectorType StringTo(const std::string &s) { return defs::GOTTHARD2; if (s == "Xilinx_ChipTestBoard") return defs::XILINX_CHIPTESTBOARD; + if (s == "Matterhorn") + return defs::MATTERHORN; throw RuntimeError("Unknown detector type " + s); } diff --git a/slsSupportLib/tests/test-ToString.cpp b/slsSupportLib/tests/test-ToString.cpp index 3d35004e8..0c35bdfc0 100644 --- a/slsSupportLib/tests/test-ToString.cpp +++ b/slsSupportLib/tests/test-ToString.cpp @@ -76,6 +76,15 @@ TEST_CASE("conversion from duration to string", "[support]") { REQUIRE(ToString(us(-100)) == "-100us"); } +TEST_CASE("conversion from frequency to string", "[support]") { + REQUIRE(ToString(defs::Hz(150)) == "150Hz"); + REQUIRE(ToString(defs::Hz(1500)) == "1.5kHz"); + REQUIRE(ToString(defs::Hz(1500000)) == "1.5MHz"); + REQUIRE(ToString(defs::Hz(150), defs::FrequencyUnit::Hz) == "150Hz"); + REQUIRE(ToString(defs::Hz(150), defs::FrequencyUnit::kHz) == "0.15kHz"); + REQUIRE(ToString(defs::Hz(150), defs::FrequencyUnit::MHz) == "0.00015MHz"); +} + TEST_CASE("Convert vector of time", "[support]") { std::vector vec{ns(150), us(10), ns(600)}; REQUIRE(ToString(vec) == "[150ns, 10us, 600ns]"); @@ -155,6 +164,15 @@ TEST_CASE("string to std::chrono::duration", "[support]") { REQUIRE_THROWS(StringTo("asvn")); } +TEST_CASE("string to frequency", "[support]") { + REQUIRE(StringTo("150") == defs::Hz(150)); + REQUIRE(StringTo("150Hz") == defs::Hz(150)); + REQUIRE(StringTo("1.5kHz") == defs::Hz(1500)); + REQUIRE(StringTo("1.5MHz") == defs::Hz(1500000)); + REQUIRE_THROWS(StringTo("5xs")); + REQUIRE_THROWS(StringTo("asvn")); +} + TEST_CASE("string to detectorType") { using dt = slsDetectorDefs::detectorType; REQUIRE(StringTo
("Eiger") == dt::EIGER); diff --git a/tests/scripts/utils_for_test.py b/tests/scripts/utils_for_test.py index ae8930860..14c90e196 100644 --- a/tests/scripts/utils_for_test.py +++ b/tests/scripts/utils_for_test.py @@ -257,7 +257,7 @@ def connectToVirtualServers(name, num_mods, ctb_object=False): counts_sec = 5 while (counts_sec != 0): try: - d.virtual = [num_mods, SERVER_START_PORTNO] + d.virtual = [num_mods, SERVER_START_PORTNO] # sets the hostnames break except Exception as e: # stop server still not up, wait a bit longer @@ -288,13 +288,16 @@ def startReceiver(num_mods, fp, no_log_file = False, quiet_mode=False): def loadConfig(name, rx_hostname = 'localhost', settingsdir = None, log_file_fp = None, num_mods = 1, num_frames = 1, num_interfaces = 1): Log(LogLevel.INFO, 'Loading config', log_file_fp, True) try: - d = connectToVirtualServers(name, num_mods) + if name == 'ctb' or name == 'xilinx_ctb': + d = connectToVirtualServers(name, num_mods, ctb_object=True) + else: + d = connectToVirtualServers(name, num_mods) if name == 'jungfrau' or name == 'moench': d.numinterfaces = num_interfaces d.udp_dstport = DEFAULT_UDP_DST_PORTNO - if name == 'eiger' or num_interfaces == 2: + if d.numinterfaces == 2: d.udp_dstport2 = DEFAULT_UDP_DST_PORTNO + 1 d.rx_hostname = rx_hostname