Merge branch 'developer' into dev/fix-pattern-generator
Build on RHEL9 docker image / build (push) Successful in 4m15s
Build on RHEL8 docker image / build (push) Successful in 5m17s
Run Simulator Tests on local RHEL9 / build (push) Successful in 18m53s
Run Simulator Tests on local RHEL8 / build (push) Successful in 22m20s

This commit is contained in:
2026-07-16 15:10:32 +02:00
committed by GitHub
125 changed files with 6098 additions and 143916 deletions
+2 -1
View File
@@ -15,13 +15,14 @@ jobs:
steps:
- name: Clone repository
run: |
git lfs install --skip-smudge
echo Cloning ${{ github.ref_name }}
git clone https://${{secrets.GITHUB_TOKEN}}@gitea.psi.ch/${{ github.repository }}.git --branch=${{ github.ref_name }} .
- name: Build library
run: |
mkdir build && cd build
cmake .. -DSLS_USE_PYTHON=ON -DSLS_USE_TESTS=ON -DSLS_USE_SIMULATOR=ON
cmake .. -DSLS_USE_PYTHON=ON -DSLS_USE_TESTS=ON -DSLS_USE_SIMULATOR=ON -DSLS_USE_MATTERHORN=ON
make -j 2
- name: C++ unit tests
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
- name: Build library
run: |
mkdir build && cd build
cmake .. -DSLS_USE_PYTHON=ON -DSLS_USE_TESTS=ON -DSLS_USE_SIMULATOR=ON
cmake .. -DSLS_USE_PYTHON=ON -DSLS_USE_TESTS=ON -DSLS_USE_SIMULATOR=ON -DSLS_USE_MATTERHORN=ON
make -j 2
- name: C++ unit tests
+1 -1
View File
@@ -52,7 +52,7 @@ jobs:
fi
- name: Install System Packages
uses: awalsh128/cache-apt-pkgs-action@latest
uses: awalsh128/cache-apt-pkgs-action@v1.6.1
with:
packages: libhdf5-dev doxygen libfmt-dev
version: 1.0
+5 -2
View File
@@ -21,7 +21,8 @@ jobs:
cache: 'pip'
- run: pip install pytest numpy colorama
- uses: awalsh128/cache-apt-pkgs-action@latest
# using a stable action version instead of latest to create/restore cache for now
- uses: awalsh128/cache-apt-pkgs-action@v1.6.1
with:
packages: libhdf5-dev qtbase5-dev qt5-qmake libqt5svg5-dev libpng-dev libtiff-dev libfmt-dev
version: 1.0
@@ -37,7 +38,9 @@ jobs:
-DSLS_USE_PYTHON=ON \
-DSLS_USE_HDF5=ON \
-DSLS_USE_GUI=ON \
-DSLS_USE_MOENCH=ON
-DSLS_USE_MOENCH=ON \
-DSLS_TREAT_WARNINGS_AS_ERRORS=ON \
-DSLS_USE_MATTERHORN=ON
- name: Build
# Build your program with the given configuration
+2 -1
View File
@@ -20,7 +20,8 @@ jobs:
cache: 'pip'
- run: pip install pytest numpy colorama pyzmq
- uses: awalsh128/cache-apt-pkgs-action@latest
# using a stable action version instead of latest to create/restore cache for now
- uses: awalsh128/cache-apt-pkgs-action@v1.6.1
with:
packages: libhdf5-dev libfmt-dev
version: 1.0
+29 -7
View File
@@ -236,6 +236,7 @@ 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
option(SLS_TREAT_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF)
#Convenience option to switch off defaults when building Moench binaries only
option(SLS_BUILD_ONLY_MOENCH "compile only Moench" OFF)
@@ -249,6 +250,18 @@ if(SLS_BUILD_ONLY_MOENCH)
set(SLS_USE_MOENCH ON CACHE BOOL "Enable" FORCE)
endif()
option(SLS_BUILD_ONLY_MATTERHORN "compile only Matterhorn" OFF)
if(SLS_BUILD_ONLY_MATTERHORN)
message(STATUS "Build MATTERHORN server only!")
set(SLS_BUILD_SHARED_LIBRARIES OFF CACHE BOOL "Disabled for MATTERHORN_ONLY" FORCE)
set(SLS_USE_TEXTCLIENT OFF CACHE BOOL "Disabled for MATTERHORN_ONLY" FORCE)
set(SLS_USE_DETECTOR ON CACHE BOOL "Disabled for MATTERHORN_ONLY" FORCE)
set(SLS_USE_RECEIVER OFF CACHE BOOL "Disabled for MATTERHORN_ONLY" FORCE)
set(SLS_USE_RECEIVER_BINARIES OFF CACHE BOOL "Disabled for MATTERHORN_ONLY" FORCE)
set(SLS_USE_SERVER ON CACHE BOOL "Enable building cpp server" FORCE)
set(SLS_USE_MATTERHORN ON CACHE BOOL "Enable Matterhorn" FORCE)
endif()
#Convenience option to switch off defaults when building Jungfrau binaries only
option(SLS_BUILD_ONLY_JUNGFRAU "compile only Jungfrau" OFF)
if(SLS_BUILD_ONLY_JUNGFRAU)
@@ -347,7 +360,9 @@ if (NOT TARGET slsProjectWarnings)
-Wno-missing-field-initializers)
endif()
if(SLS_TREAT_WARNINGS_AS_ERRORS)
target_compile_options(slsProjectWarnings INTERFACE -Werror)
endif()
endif()
@@ -376,6 +391,10 @@ if (NOT TARGET slsProjectCSettings)
target_compile_options(slsProjectCSettings INTERFACE -fsanitize=address,undefined -fno-omit-frame-pointer)
target_link_libraries(slsProjectCSettings INTERFACE -fsanitize=address,undefined)
endif()
if(SLS_TREAT_WARNINGS_AS_ERRORS)
target_compile_options(slsProjectCSettings INTERFACE -Werror)
endif()
endif()
@@ -433,14 +452,15 @@ 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)
# cant add_subdirectory twice
if (SLS_USE_MATTERHORN AND !SLS_USE_SIMULATOR)
add_subdirectory(slsDetectorServers/matterhornServer)
endif()
if (SLS_USE_PYTHON)
find_package (Python 3.8 COMPONENTS Interpreter Development.Module REQUIRED)
set(PYBIND11_FINDPYTHON ON) # Needed for RH8
@@ -483,13 +503,15 @@ if(SLS_BUILD_DOCS)
add_subdirectory(docs)
endif(SLS_BUILD_DOCS)
if(SLS_USE_MOENCH)
if(SLS_USE_MOENCH OR SLS_USE_JUNGFRAU)
add_subdirectory(slsDetectorCalibration/tiffio)
endif()
if(SLS_USE_MOENCH)
add_subdirectory(slsDetectorCalibration/moenchExecutables)
endif(SLS_USE_MOENCH)
if(SLS_USE_JUNGFRAU)
add_subdirectory(slsDetectorCalibration/tiffio)
add_subdirectory(slsDetectorCalibration/jungfrauExecutables)
endif(SLS_USE_JUNGFRAU)
+6
View File
@@ -62,8 +62,14 @@ support for building rpms
removed unused function readDataFile/writeDataFile from file_utils.h
changed api: datastream=>udp_datastream, set/getDatastream=>set/getUDPDatastream
also implemetned for jungfrau, moench at receiver side (top/bottom)
added rx_streamdummyheader to send the zmq dummy header any time. Allows pre-configuring zmq processing before acq begins.
allow disabling one UDP interface in the receiver.
setting number of UDP interfaces can only be set at detector level and not at module level. (individual modules)
2 On-board Detector Server Compatibility
==========================================
+30 -6
View File
@@ -32,23 +32,47 @@ def argument_parser():
# TODO: should be configurable
header = r"""
#pragma once
// clang-format off
#include "RegisterHelperStructs.hpp"
namespace sls {
namespace Reg {
/// @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
MH_RO_SM_AXI = 0xB0010000,
FHDR_AXI = 0xB0011000,
AURORA_STATUS = 0xB0014000,
AURORA_STATUS2 = 0xB0015000,
PACKETIZERREG = 0x00000000, // TODO: fill in correct address
UNKNOWN = 0x00000000 // dont know yet
};
constexpr size_t IPCORE_REGISTER_BLOCK_SIZE =
0x1000; // size of each IP core address space in bytes // TODO: maybe add in
// other file definitions
// clang-format off
"""
postpend = r"""
constexpr RegisterField ModuleRow{
Frame_HDR_ModCoord_LSB_Reg, 0, 0xffff};
constexpr RegisterField ModuleCol{
Frame_HDR_ModCoord_LSB_Reg, 16, 0xffff};
constexpr RegisterField ModuleCoordz{
Frame_HDR_ModCoord_MSB_Reg, 0, 0xffff};
constexpr RegisterField ModuleIndex{
Frame_HDR_ModCoord_MSB_Reg, 16, 0xffff};
} // namespace Reg
} // namespace sls
// clang-format on
"""
+17 -16
View File
@@ -20,8 +20,8 @@ API_FILE = ROOT_DIR / "slsSupportLib/include/sls/versionAPI.h"
VERSION_FILE = ROOT_DIR / "VERSION"
parser = argparse.ArgumentParser(description = 'updates API version')
parser.add_argument('api_module_name', choices=["APILIB", "APIRECEIVER", "APICTB", "APIGOTTHARD2", "APIMOENCH", "APIEIGER", "APIXILINXCTB", "APIJUNGFRAU", "APIMYTHEN3"], help = 'module name to change api version options are: ["APILIB", "APIRECEIVER", "APICTB", "APIGOTTHARD2", "APIMOENCH", "APIEIGER", "APIXILINXCTB", "APIJUNGFRAU", "APIMYTHEN3"]')
parser.add_argument('api_dir', help = 'Relative or absolute path to the module code')
parser.add_argument('api_module_name', choices=["APILIB", "APIRECEIVER", "APICTB", "APIGOTTHARD2", "APIMOENCH", "APIEIGER", "APIXILINXCTB", "APIJUNGFRAU", "APIMYTHEN3", "APIMATTERHORN"], help = 'module name to change api version options are: ["APILIB", "APIRECEIVER", "APICTB", "APIGOTTHARD2", "APIMOENCH", "APIEIGER", "APIXILINXCTB", "APIJUNGFRAU", "APIMYTHEN3", "APIMATTERHORN"]')
parser.add_argument('api_dirs', nargs="+", help = 'Relative or absolute paths to the module code')
def update_api_file(new_api : str, api_module_name : str, api_file_name : str):
@@ -36,21 +36,22 @@ def update_api_file(new_api : str, api_module_name : str, api_file_name : str):
else:
api_file.write(line)
def get_latest_modification_date(directory : str):
def get_latest_modification_date(directories : list[str]):
latest_time = 0
latest_date = None
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith(".o"):
continue
full_path = os.path.join(root, file)
try:
mtime = os.path.getmtime(full_path)
if mtime > latest_time:
latest_time = mtime
except FileNotFoundError:
continue
for directory in directories:
for root, dirs, files in os.walk(directory):
for file in files:
if file.endswith(".o"):
continue
full_path = os.path.join(root, file)
try:
mtime = os.path.getmtime(full_path)
if mtime > latest_time:
latest_time = mtime
except FileNotFoundError:
continue
latest_date = datetime.fromtimestamp(latest_time).strftime("%y%m%d")
@@ -74,9 +75,9 @@ if __name__ == "__main__":
args = parser.parse_args()
api_dir = ROOT_DIR / args.api_dir
api_dirs = [ROOT_DIR / api_dir for api_dir in args.api_dirs]
update_api_version(args.api_module_name, api_dir)
update_api_version(args.api_module_name, api_dirs)
@@ -133,6 +133,7 @@ class AcquisitionTab(QtWidgets.QWidget):
self.view.labelDigital.setDisabled(True)
self.view.labelTransceiver.setDisabled(True)
self.view.spinBoxTransceiver.setDisabled(True)
self.plotTab.enableallCounterCheckBox(False)
case readoutMode.DIGITAL_ONLY:
self.view.spinBoxAnalog.setDisabled(True)
self.view.labelAnalog.setDisabled(True)
@@ -140,6 +141,7 @@ class AcquisitionTab(QtWidgets.QWidget):
self.view.labelDigital.setEnabled(True)
self.view.labelTransceiver.setDisabled(True)
self.view.spinBoxTransceiver.setDisabled(True)
self.plotTab.enableallCounterCheckBox(False)
case readoutMode.ANALOG_AND_DIGITAL:
self.view.spinBoxAnalog.setEnabled(True)
self.view.labelAnalog.setEnabled(True)
@@ -147,6 +149,7 @@ class AcquisitionTab(QtWidgets.QWidget):
self.view.labelDigital.setEnabled(True)
self.view.labelTransceiver.setDisabled(True)
self.view.spinBoxTransceiver.setDisabled(True)
self.plotTab.enableallCounterCheckBox(False)
case readoutMode.TRANSCEIVER_ONLY:
self.view.spinBoxAnalog.setDisabled(True)
self.view.labelAnalog.setDisabled(True)
@@ -154,6 +157,7 @@ class AcquisitionTab(QtWidgets.QWidget):
self.view.labelDigital.setDisabled(True)
self.view.labelTransceiver.setEnabled(True)
self.view.spinBoxTransceiver.setEnabled(True)
self.plotTab.enableallCounterCheckBox(self.plotTab.view.radioButtonImage.isChecked()) # enable counter checkboxes for matterhorn
case _:
self.view.spinBoxAnalog.setDisabled(True)
self.view.labelAnalog.setDisabled(True)
@@ -161,6 +165,7 @@ class AcquisitionTab(QtWidgets.QWidget):
self.view.labelDigital.setEnabled(True)
self.view.labelTransceiver.setEnabled(True)
self.view.spinBoxTransceiver.setEnabled(True)
self.plotTab.enableallCounterCheckBox(self.plotTab.view.radioButtonImage.isChecked()) # enable counter checkboxes for matterhorn
self.view.comboBoxROMode.currentIndexChanged.connect(self.setReadOut)
self.view.spinBoxAnalog.editingFinished.connect(self.setAnalog)
+105 -38
View File
@@ -4,10 +4,10 @@ import random
from pathlib import Path
import numpy as np
from PyQt5 import QtWidgets, QtGui, uic
from PyQt5 import QtWidgets, QtGui, QtCore, uic
from aare import transform, ReadoutMode
from aare._aare import Matterhorn10, Matterhorn02, Moench04
from aare._aare import Matterhorn10, Matterhorn02, Moench04, Moench05
import pyqtgraph as pg
from pyctbgui.utils import recordOrApplyPedestal
@@ -43,6 +43,7 @@ class PlotTab(QtWidgets.QWidget):
self.pedestalApply: bool = True
self.__acqFrames = None
self.logger = logging.getLogger('PlotTab')
self.plotSplitter = None
def setup_ui(self):
self.signalsTab = self.mainWindow.signalsTab
@@ -51,10 +52,17 @@ class PlotTab(QtWidgets.QWidget):
self.adcTab = self.mainWindow.adcTab
self.initializeColorMaps()
# TODO use list comprehension
self.checkBoxCounters = [self.view.checkBoxCounter0, self.view.checkBoxCounter1, self.view.checkBoxCounter2, self.view.checkBoxCounter3]
for checkBox in self.checkBoxCounters:
checkBox.setChecked(False)
checkBox.setEnabled(False)
self.imagePlots = (
self.mainWindow.plotAnalogImage,
self.mainWindow.plotDigitalImage,
self.mainWindow.plotTransceiverImage,
self.transceiverTab.transceiverImageViews,
)
def connect_ui(self):
@@ -93,12 +101,54 @@ class PlotTab(QtWidgets.QWidget):
self.view.radioButtonFixed.clicked.connect(partial(self.setColorRangeMode, Defines.colorRange.fixed))
self.view.radioButtonCenter.clicked.connect(partial(self.setColorRangeMode, Defines.colorRange.center))
for plot in self.imagePlots:
plot.scene.sigMouseMoved.connect(partial(self.showPlotValues, plot))
plot.getHistogramWidget().item.sigLevelChangeFinished.connect(partial(self.handleHistogramChange, plot))
for index, checkBox in enumerate(self.checkBoxCounters):
checkBox.stateChanged.connect(partial(self.displayCounter, index))
# show image Values for analog image
nMaxY = lambda : self.mainWindow.nAnalogRows
nMaxX = lambda : self.mainWindow.nAnalogCols
frame = lambda : self.mainWindow.analog_frame
plot = self.mainWindow.plotAnalogImage
plot.scene.sigMouseMoved.connect(partial(self.showPlotValues, plot, nMaxX, nMaxY, frame))
plot.getHistogramWidget().item.sigLevelChangeFinished.connect(partial(self.handleHistogramChange, plot))
# show image Values for digital image
nMaxY = lambda : self.mainWindow.nDigitalRows
nMaxX = lambda : self.mainWindow.nDigitalCols
frame = lambda : self.mainWindow.digital_frame
plot = self.mainWindow.plotDigitalImage
plot.scene.sigMouseMoved.connect(partial(self.showPlotValues, plot, nMaxX, nMaxY, frame))
plot.getHistogramWidget().item.sigLevelChangeFinished.connect(partial(self.handleHistogramChange, plot))
# show image Values for transceiver image
nMaxY = lambda : self.transceiverTab.nTransceiverRows
nMaxX = lambda : self.transceiverTab.nTransceiverCols
for index, image_view in enumerate(self.transceiverTab.transceiverImageViews):
frame = lambda : image_view.getImageItem().image
image_view.scene.sigMouseMoved.connect(partial(self.showPlotValues, image_view, nMaxX, nMaxY, frame))
image_view.getHistogramWidget().item.sigLevelChangeFinished.connect(partial(self.handleHistogramChange, image_view))
self.view.checkBoxShowLegend.stateChanged.connect(self.toggleLegend)
def displayCounter(self, index : int, state : int):
# toggle the display of the counter i and update the splitter
self.transceiverTab.shownCounters[index] = (state == QtCore.Qt.Checked)
self.transceiverTab.update_ImageSplitter()
def setCounterCheckBox(self, index : int, check : bool):
self.checkBoxCounters[index].setChecked(check)
def enableCounterCheckBox(self, index : int, enabled : bool):
is_transceiver = self.mainWindow.romode.value in [3, 4]
is_image = self.view.radioButtonImage.isChecked()
self.checkBoxCounters[index].setEnabled(enabled and is_transceiver and is_image)
def enableallCounterCheckBox(self, enabled : bool):
for checkBox in self.checkBoxCounters:
checkBox.setEnabled(enabled)
def refresh(self):
self.getZMQHWM()
@@ -262,7 +312,11 @@ class PlotTab(QtWidgets.QWidget):
updates UI views should be called after every change to cmin or cmax
"""
for plot in self.imagePlots:
plot.getHistogramWidget().item.setLevels(min=self.cmin, max=self.cmax)
if isinstance(plot, list): # hacky for now only transceiver image is a PlotSplitter
for p in plot:
p.getHistogramWidget().item.setLevels(min=self.cmin, max=self.cmax)
else:
plot.getHistogramWidget().item.setLevels(min=self.cmin, max=self.cmax)
self.view.cminSpinBox.setValue(self.cmin)
self.view.cmaxSpinBox.setValue(self.cmax)
@@ -271,7 +325,9 @@ class PlotTab(QtWidgets.QWidget):
# print(f'color map:{self.comboBoxColorMap.currentText()}')
self.mainWindow.plotAnalogImage.setColorMap(cm)
self.mainWindow.plotDigitalImage.setColorMap(cm)
self.mainWindow.plotTransceiverImage.setColorMap(cm)
for i, showncounter in enumerate(self.transceiverTab.shownCounters):
if showncounter:
self.transceiverTab.transceiverImageViews[i].setColorMap(cm)
def getZMQHWM(self):
@@ -343,11 +399,11 @@ class PlotTab(QtWidgets.QWidget):
self.mainWindow.transceiverPlots[i].hide()
def addAllSelectedTransceiverPlots(self):
for i in range(Defines.transceiver.count):
for i in range(Defines.transceiver.maxcount):
self.addSelectedTransceiverPlots(i)
def removeAllTransceiverPlots(self):
for i in range(Defines.transceiver.count):
for i in range(Defines.transceiver.maxcount):
self.mainWindow.transceiverPlots[i].hide()
def showPlot(self):
@@ -356,7 +412,7 @@ class PlotTab(QtWidgets.QWidget):
self.mainWindow.plotTransceiverWaveform.hide()
self.mainWindow.plotAnalogImage.hide()
self.mainWindow.plotDigitalImage.hide()
self.mainWindow.plotTransceiverImage.hide()
self.transceiverTab.transceiverImageSplitter.hide()
self.view.labelDigitalWaveformOption.setDisabled(True)
self.view.radioButtonOverlay.setDisabled(True)
self.view.radioButtonStripe.setDisabled(True)
@@ -379,7 +435,10 @@ class PlotTab(QtWidgets.QWidget):
if self.view.radioButtonWaveform.isChecked():
self.mainWindow.plotTransceiverWaveform.show()
elif self.view.radioButtonImage.isChecked():
self.mainWindow.plotTransceiverImage.show()
for i, showncounter in enumerate(self.transceiverTab.shownCounters):
self.transceiverTab.transceiverImageViews[i].setVisible(showncounter)
self.transceiverTab.transceiverImageSplitter.show()
def plotOptions(self):
@@ -404,14 +463,18 @@ class PlotTab(QtWidgets.QWidget):
'bottom', "<span style=\"color:black;font-size:14px\">Transceiver Sample [#]</span>")
self.view.stackedWidgetPlotType.setCurrentIndex(0)
self.enableallCounterCheckBox(False) # disable counter checkboxes for waveform
elif self.view.radioButtonImage.isChecked():
self.view.stackedWidgetPlotType.setCurrentIndex(2)
is_transceiver = self.mainWindow.romode.value in [3, 4]
self.enableallCounterCheckBox(is_transceiver) # enable counter checkboxes for matterhorn
self.setDecoder()
if self.view.radioButtonNoPlot.isChecked():
self.view.labelPlotOptions.hide()
self.view.stackedWidgetPlotType.hide()
self.enableallCounterCheckBox(False) # disable counter checkboxes when no plot is selected
# enable plotting
else:
self.view.labelPlotOptions.show()
@@ -419,45 +482,57 @@ class PlotTab(QtWidgets.QWidget):
self.mainWindow.read_timer.start(Defines.Time_Plot_Refresh_ms)
def setDecoder(self):
# TODO: really dont like to set attributes on the fly - hard to understand whats going on
if self.view.comboBoxPlot.currentText() == "Matterhorn02":
print("Initializing decoder for Matterhorn02")
self.mainWindow.nTransceiverRows = Matterhorn02.nRows
self.mainWindow.nTransceiverCols = Matterhorn02.nCols
self.transceiverTab.nTransceiverRows = Matterhorn02.nRows
self.transceiverTab.nTransceiverCols = Matterhorn02.nCols
self.transceiverTab.update_numCounters(1)
self.mainWindow.decoder = transform.Matterhorn02TransceiverTransform()
elif self.view.comboBoxPlot.currentText() == "Matterhorn1_16bit_1_counter":
print("Initializing decoder for Matterhorn1 with 1 counter 16 bit dynamic range")
self.mainWindow.nTransceiverRows = Matterhorn10.nRows
self.mainWindow.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.nTransceiverRows = Matterhorn10.nRows
self.transceiverTab.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.update_numCounters(1)
self.mainWindow.decoder = transform.Matterhorn10Transform(16, 1)
elif self.view.comboBoxPlot.currentText() == "Matterhorn1_16bit_4_counters":
print("Initializing decoder for Matterhorn1 with 4 counters 16 bit dynamic range")
self.mainWindow.nTransceiverRows = Matterhorn10.nRows*4
self.mainWindow.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.nTransceiverRows = Matterhorn10.nRows
self.transceiverTab.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.update_numCounters(4)
self.mainWindow.decoder = transform.Matterhorn10Transform(16, 4)
elif self.view.comboBoxPlot.currentText() == "Matterhorn1_8bit_1_counter":
print("Initializing decoder for Matterhorn1 with 1 counter 8 bit dynamic range")
self.mainWindow.nTransceiverRows = Matterhorn10.nRows
self.mainWindow.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.nTransceiverRows = Matterhorn10.nRows
self.transceiverTab.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.update_numCounters(1)
self.mainWindow.decoder = transform.Matterhorn10Transform(8, 1)
elif self.view.comboBoxPlot.currentText() == "Matterhorn1_8bit_4_counters":
print("Initializing decoder for Matterhorn1 with 4 counters 8 bit dynamic range")
self.mainWindow.nTransceiverRows = Matterhorn10.nRows*4
self.mainWindow.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.nTransceiverRows = Matterhorn10.nRows
self.transceiverTab.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.update_numCounters(4)
self.mainWindow.decoder = transform.Matterhorn10Transform(8, 4)
elif self.view.comboBoxPlot.currentText() == "Matterhorn1_4bit_4_counters":
print("Initializing decoder for Matterhorn1 with 4 counters 4 bit dynamic range")
self.mainWindow.nTransceiverRows = Matterhorn10.nRows*4
self.mainWindow.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.nTransceiverRows = Matterhorn10.nRows
self.transceiverTab.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.update_numCounters(4)
self.mainWindow.decoder = transform.Matterhorn10Transform(4, 4)
elif self.view.comboBoxPlot.currentText() == "Matterhorn1_4bit_1_counter":
print("Initializing decoder for Matterhorn1 with 1 counter 4 bit dynamic range")
self.mainWindow.nTransceiverRows = Matterhorn10.nRows
self.mainWindow.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.nTransceiverRows = Matterhorn10.nRows
self.transceiverTab.nTransceiverCols = Matterhorn10.nCols
self.transceiverTab.update_numCounters(1)
self.mainWindow.decoder = transform.Matterhorn10Transform(4, 1)
elif self.view.comboBoxPlot.currentText() == "Moench04":
self.mainWindow.nAnalogRows = Moench04.nRows
self.mainWindow.nAnalogCols = Moench04.nCols
self.mainWindow.decoder = transform.Moench04AnalogTransform()
elif self.view.comboBoxPlot.currentText() == "Moench05":
self.mainWindow.nAnalogRows = Moench05.nRows
self.mainWindow.nAnalogCols = Moench05.nCols
self.mainWindow.decoder = transform.Moench05Transform()
try:
if hasattr(self.mainWindow.decoder, "compatibility") and callable(getattr(self.mainWindow.decoder, "compatibility")):
@@ -533,21 +608,13 @@ class PlotTab(QtWidgets.QWidget):
# get the RGB Values
# print(color.getRgb())
def showPlotValues(self, sender, pos):
def showPlotValues(self, sender, get_nMaxX, get_nMaxY, get_frame, pos):
x = sender.getImageItem().mapFromScene(pos).x()
y = sender.getImageItem().mapFromScene(pos).y()
val = 0
nMaxY = self.mainWindow.nAnalogRows
nMaxX = self.mainWindow.nAnalogCols
frame = self.mainWindow.analog_frame
if sender == self.mainWindow.plotDigitalImage:
nMaxY = self.mainWindow.nDigitalRows
nMaxX = self.mainWindow.nDigitalCols
frame = self.mainWindow.digital_frame
elif sender == self.mainWindow.plotTransceiverImage:
nMaxY = self.mainWindow.nTransceiverRows
nMaxX = self.mainWindow.nTransceiverCols
frame = self.mainWindow.transceiver_frame
nMaxX = get_nMaxX()
nMaxY = get_nMaxY()
frame = get_frame()
if 0 <= x < nMaxX and 0 <= y < nMaxY and not np.array_equal(frame, []):
val = frame[int(y), int(x)]
message = f'[row, col]: [{y:.2f}, {x:.2f}] = {val:.2f}'
+57 -30
View File
@@ -2,7 +2,7 @@ from functools import partial
from pathlib import Path
import numpy as np
from PyQt5 import QtWidgets, uic
from PyQt5 import QtWidgets, QtCore, uic
import pyqtgraph as pg
from pyqtgraph import LegendItem
@@ -24,11 +24,18 @@ class TransceiverTab(QtWidgets.QWidget):
self.plotTab = None
self.legend: LegendItem | None = None
self.acquisitionTab = None
self.nCounters: int = Defines.transceiver.maxcount
self.nTransceiverRows : int = 0
self.nTransceiverCols : int = 0
self.shownCounters: list[bool] = [True] * Defines.transceiver.maxcount # per default show all 4 counters
self.transceiverImageSplitter = QtWidgets.QSplitter(QtCore.Qt.Vertical)
self.transceiverImageViews : list = [] # pg image view for each counter
self.firstTransceiverImage : list[bool] = [True] * Defines.transceiver.maxcount # to keep track of first image for each counter to maintain zoom state
def setup_ui(self):
self.plotTab = self.mainWindow.plotTab
self.acquisitionTab = self.mainWindow.acquisitionTab
for i in range(Defines.transceiver.count):
for i in range(Defines.transceiver.maxcount):
self.setTransceiverButtonColor(i, self.plotTab.getRandomColor())
self.initializeAllTransceiverPlots()
@@ -39,7 +46,7 @@ class TransceiverTab(QtWidgets.QWidget):
self.plotTab.subscribeToggleLegend(self.updateLegend)
def connect_ui(self):
for i in range(Defines.transceiver.count):
for i in range(Defines.transceiver.maxcount):
getattr(self.view, f"checkBoxTransceiver{i}").stateChanged.connect(partial(self.setTransceiverEnable, i))
getattr(self.view,
f"checkBoxTransceiver{i}Plot").stateChanged.connect(partial(self.setTransceiverEnablePlot, i))
@@ -55,7 +62,7 @@ class TransceiverTab(QtWidgets.QWidget):
"""
enabledPlots = []
self.legend.clear()
for i in range(Defines.transceiver.count):
for i in range(Defines.transceiver.maxcount):
if getattr(self.view, f'checkBoxTransceiver{i}Plot').isChecked():
plotName = getattr(self.view, f"labelTransceiver{i}").text()
enabledPlots.append((self.mainWindow.transceiverPlots[i], plotName))
@@ -104,7 +111,7 @@ class TransceiverTab(QtWidgets.QWidget):
trans_array = self._processWaveformData(data, dSamples, self.mainWindow.romode.value,
self.mainWindow.nDBitEnabled, self.nTransceiverEnabled)
idx = 0
for i in range(Defines.transceiver.count):
for i in range(Defines.transceiver.maxcount):
checkBoxPlot = getattr(self.view, f"checkBoxTransceiver{i}Plot")
checkBoxEn = getattr(self.view, f"checkBoxTransceiver{i}")
if checkBoxEn.isChecked() and checkBoxPlot.isChecked():
@@ -133,9 +140,7 @@ class TransceiverTab(QtWidgets.QWidget):
transceiverOffset += nDBitEnabled * (nbitsPerDBit // 8)
trans_array = np.array(np.frombuffer(data, offset=transceiverOffset, dtype=np.uint8))
tmp = self.mainWindow.decoder(trans_array)
return tmp
return self.mainWindow.decoder(trans_array)
def processImageData(self, data, dSamples):
"""
@@ -145,28 +150,47 @@ class TransceiverTab(QtWidgets.QWidget):
data: raw image data
"""
# get zoom state
viewBox = self.mainWindow.plotTransceiverImage.getView()
state = viewBox.getState()
image_states = [image_view.getView().getState() for image_view in self.transceiverImageViews]
transceiver_frame : np.ndarray = None
try:
self.mainWindow.transceiver_frame = self._processImageData(data, dSamples, self.mainWindow.romode.value,
transceiver_frame = self._processImageData(data, dSamples, self.mainWindow.romode.value,
self.mainWindow.nDBitEnabled)
self.plotTab.ignoreHistogramSignal = True
self.mainWindow.plotTransceiverImage.setImage(self.mainWindow.transceiver_frame)
for i in range(transceiver_frame.shape[0]):
self.transceiverImageViews[i].setImage(transceiver_frame[i])
except Exception as e:
self.mainWindow.statusbar.setStyleSheet("color:red")
self.acquisitionTab.updateCurrentFrame('Invalid Image')
self.mainWindow.statusbar.showMessage(str(e))
print("Error: ", str(e))
self.plotTab.setFrameLimits(self.mainWindow.transceiver_frame)
self.plotTab.setFrameLimits(transceiver_frame)
# keep the zoomed in state (not 1st image)
if self.mainWindow.firstTransceiverImage:
self.mainWindow.firstTransceiverImage = False
else:
viewBox.setState(state)
return self.mainWindow.transceiver_frame
for idx, image_view in enumerate(self.transceiverImageViews):
if(self.firstTransceiverImage[idx] and self.shownCounters[idx]):
self.firstTransceiverImage[idx] = False
else:
image_view.getView().setState(image_states[idx])
return transceiver_frame
def update_numCounters(self, num_counters):
# update the number of counters and adjust the image splitter accordingly
self.nCounters = num_counters
for i in range(Defines.transceiver.maxcount):
self.shownCounters[i] = i < self.nCounters
self.plotTab.setCounterCheckBox(i, self.shownCounters[i]) # check the counter checkbox
self.plotTab.enableCounterCheckBox(i, self.shownCounters[i]) # disable counter checkbox
self.update_ImageSplitter() # update the splitter to show/hide image views based on the number of counters
def update_ImageSplitter(self):
for i, showncounter in enumerate(self.shownCounters):
self.transceiverImageViews[i].setVisible(showncounter)
def initializeAllTransceiverPlots(self):
self.mainWindow.plotTransceiverWaveform = pg.plot()
@@ -174,7 +198,7 @@ class TransceiverTab(QtWidgets.QWidget):
self.mainWindow.verticalLayoutPlot.addWidget(self.mainWindow.plotTransceiverWaveform, 5)
self.mainWindow.transceiverPlots = {}
waveform = np.zeros(1000)
for i in range(Defines.transceiver.count):
for i in range(Defines.transceiver.maxcount):
pen = pg.mkPen(color=self.getTransceiverButtonColor(i), width=1)
legendName = getattr(self.view, f"labelTransceiver{i}").text()
self.mainWindow.transceiverPlots[i] = self.mainWindow.plotTransceiverWaveform.plot(waveform,
@@ -182,16 +206,19 @@ class TransceiverTab(QtWidgets.QWidget):
name=legendName)
self.mainWindow.transceiverPlots[i].hide()
self.mainWindow.plotTransceiverImage = pg.ImageView()
self.mainWindow.nTransceiverRows = 0
self.mainWindow.nTransceiverCols = 0
self.mainWindow.transceiver_frame = np.zeros(
(self.mainWindow.nTransceiverRows, self.mainWindow.nTransceiverCols))
self.mainWindow.plotTransceiverImage.setImage(self.mainWindow.transceiver_frame)
self.mainWindow.verticalLayoutPlot.addWidget(self.mainWindow.plotTransceiverImage, 6)
# initialize image
cm = pg.colormap.get('CET-L9') # prepare a linear color map
self.mainWindow.plotTransceiverImage.setColorMap(cm)
for i in range(self.nCounters):
imageView = pg.ImageView()
imageView.setColorMap(cm)
self.transceiverImageViews.append(imageView)
self.transceiverImageSplitter.addWidget(imageView)
self.update_ImageSplitter() # update the splitter
self.mainWindow.verticalLayoutPlot.addWidget(self.transceiverImageSplitter, 6)
def getTransceiverEnableReg(self):
retval = self.det.transceiverenable
+1118 -654
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -35,8 +35,8 @@ class Defines:
loops_count = 6
class transceiver:
count = 4
tabIndex = 4
maxcount = 4
maxtabIndex = 4
class slowAdc:
tabIndex = 2
+61 -17
View File
@@ -622,7 +622,7 @@ class Detector(CppDetectorApi):
>>> d.exptime = 5e-07
>>>
>>> # using timedelta (up to microseconds precision)
>>> from datatime import timedelta
>>> from datetime import timedelta
>>> d.exptime = timedelta(seconds = 1, microseconds = 3)
>>>
>>> # using DurationWrapper to set in seconds
@@ -674,7 +674,7 @@ class Detector(CppDetectorApi):
>>> d.period = 5e-07
>>>
>>> # using timedelta (up to microseconds precision)
>>> from datatime import timedelta
>>> from datetime import timedelta
>>> d.period = timedelta(seconds = 1, microseconds = 3)
>>>
>>> # using DurationWrapper to set in seconds
@@ -740,7 +740,7 @@ class Detector(CppDetectorApi):
>>> d.delay = 5e-07
>>>
>>> # using timedelta (up to microseconds precision)
>>> from datatime import timedelta
>>> from datetime import timedelta
>>> d.delay = timedelta(seconds = 1, microseconds = 3)
>>>
>>> # using DurationWrapper to set in seconds
@@ -2429,19 +2429,63 @@ class Detector(CppDetectorApi):
"""
@property
def datastream(self):
def udp_datastream(self):
"""
datastream [left|right] [0, 1]
[Eiger] Enables or disables data streaming from left or/and right side of detector for 10GbE mode. 1 (enabled) by default.
Get or set UDP data streaming for detector/receiver ports.
[Eiger]: LEFT, RIGHT - 10GbE UDP ports of the detector.
[Jungfrau][Moench]: TOP, BOTTOM - UDP ports of the receiver
(only when numinterfaces is set to 2).
:getter: Returns a dictionary containing the UDP data stream enable state
for all available ports. When multiple detector modules are present,
identical values are returned as a single boolean. If different
values are found, a list of booleans is returned.
:setter: Takes a tuple of ``(portPosition, bool)`` to set the UDP data
stream state for a single port.
Enum: portPosition
Example
-------
Get UDP streaming state for all ports:
>>> d.udp_datastream
{<portPosition.TOP: 2>: False, <portPosition.BOTTOM: 3>: True}
Multiple detectors with identical states:
>>> d.udp_datastream
{<portPosition.TOP: 2>: False, <portPosition.BOTTOM: 3>: True}
Multiple detectors with different states:
>>> d.udp_datastream
{<portPosition.TOP: 2>: [False, True], <portPosition.BOTTOM: 3>: [True, True]}
Enable UDP streaming for a specific port:
>>> from slsdet import portPosition
>>> d.udp_datastream = (portPosition.TOP, True)
Disable UDP streaming for a specific port:
>>> d.udp_datastream = (portPosition.BOTTOM, False)
"""
result = {}
for port in [defs.LEFT, defs.RIGHT]:
result[port] = element_if_equal(self.getDataStream(port))
if self.type in [detectorType.JUNGFRAU, detectorType.MOENCH]:
ports = [defs.TOP, defs.BOTTOM]
else:
ports = [defs.LEFT, defs.RIGHT]
for port in ports:
result[port] = element_if_equal(self.getUDPDataStream(port))
return result
@datastream.setter
def datastream(self, value):
ut.set_using_dict(self.setDataStream, *value)
@udp_datastream.setter
def udp_datastream(self, value):
self.setUDPDataStream(*value)
@property
@element
@@ -2473,7 +2517,7 @@ class Detector(CppDetectorApi):
>>> d.subexptime = 5e-07
>>>
>>> # using timedelta (up to microseconds precision)
>>> from datatime import timedelta
>>> from datetime import timedelta
>>> d.subexptime = timedelta(seconds = 1.23, microseconds = 203)
>>>
>>> # using DurationWrapper to set in seconds
@@ -2539,7 +2583,7 @@ class Detector(CppDetectorApi):
>>> d.subdeadtime = 5e-07
>>>
>>> # using timedelta (up to microseconds precision)
>>> from datatime import timedelta
>>> from datetime import timedelta
>>> d.subdeadtime = timedelta(seconds = 1.23, microseconds = 203)
>>>
>>> # using DurationWrapper to set in seconds
@@ -2736,7 +2780,7 @@ class Detector(CppDetectorApi):
>>> d.compdisabletime = 5e-07
>>>
>>> # using timedelta (up to microseconds precision)
>>> from datatime import timedelta
>>> from datetime import timedelta
>>> d.compdisabletime = timedelta(seconds = 1, microseconds = 3)
>>>
>>> # using DurationWrapper to set in seconds
@@ -2829,7 +2873,7 @@ class Detector(CppDetectorApi):
>>> d.storagecell_delay = 5e-07
>>>
>>> # using timedelta (up to microseconds precision)
>>> from datatime import timedelta
>>> from datetime import timedelta
>>> d.storagecell_delay = timedelta(seconds = 1, microseconds = 3)
>>>
>>> # using DurationWrapper to set in seconds
@@ -3175,7 +3219,7 @@ class Detector(CppDetectorApi):
>>> d.burstperiod = 5e-07
>>>
>>> # using timedelta (up to microseconds precision)
>>> from datatime import timedelta
>>> from datetime import timedelta
>>> d.burstperiod = timedelta(seconds = 1, microseconds = 3)
>>>
>>> # using DurationWrapper to set in seconds
@@ -3335,7 +3379,7 @@ class Detector(CppDetectorApi):
>>> d.gatedelay = 5e-07
>>>
>>> # using timedelta (up to microseconds precision)
>>> from datatime import timedelta
>>> from datetime import timedelta
>>> d.gatedelay = timedelta(seconds = 1, microseconds = 3)
>>>
>>> # using DurationWrapper to set in seconds
+18 -12
View File
@@ -653,9 +653,9 @@ void init_det(py::module &m) {
Detector::getNumberofUDPInterfaces,
py::arg() = Positions{});
CppDetectorApi.def("setNumberofUDPInterfaces",
(void (Detector::*)(int, sls::Positions)) &
(void (Detector::*)(int)) &
Detector::setNumberofUDPInterfaces,
py::arg(), py::arg() = Positions{});
py::arg());
CppDetectorApi.def("getSelectedUDPInterface",
(Result<int>(Detector::*)(sls::Positions) const) &
Detector::getSelectedUDPInterface,
@@ -841,6 +841,22 @@ void init_det(py::module &m) {
CppDetectorApi.def(
"setTransmissionDelay",
(void (Detector::*)(int)) & Detector::setTransmissionDelay, py::arg());
CppDetectorApi.def("getUDPDataStream",
(Result<bool>(Detector::*)(const defs::portPosition,
sls::Positions) const) &
Detector::getUDPDataStream,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def("setUDPDataStream",
(void (Detector::*)(const defs::portPosition, const bool,
sls::Positions)) &
Detector::setUDPDataStream,
py::arg(), py::arg(), py::arg() = Positions{});
CppDetectorApi.def("getRxDisabledUDPPortIndices",
(std::vector<int>(Detector::*)() const) &
Detector::getRxDisabledUDPPortIndices);
CppDetectorApi.def("getPortPositionList",
(std::vector<defs::portPosition>(Detector::*)() const) &
Detector::getPortPositionList);
CppDetectorApi.def("getUseReceiverFlag",
(Result<bool>(Detector::*)(sls::Positions) const) &
Detector::getUseReceiverFlag,
@@ -1174,16 +1190,6 @@ void init_det(py::module &m) {
CppDetectorApi.def("setQuad",
(void (Detector::*)(const bool)) & Detector::setQuad,
py::arg());
CppDetectorApi.def("getDataStream",
(Result<bool>(Detector::*)(const defs::portPosition,
sls::Positions) const) &
Detector::getDataStream,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def("setDataStream",
(void (Detector::*)(const defs::portPosition, const bool,
sls::Positions)) &
Detector::setDataStream,
py::arg(), py::arg(), py::arg() = Positions{});
CppDetectorApi.def("getTop",
(Result<bool>(Detector::*)(sls::Positions) const) &
Detector::getTop,
+87 -3
View File
@@ -11,9 +11,8 @@ from utils_for_test import (
LogLevel,
)
from slsdet import Detector
from slsdet._slsdet import slsDetectorDefs
from slsdet.utils import all_equal, element_if_equal
detectorType = slsDetectorDefs.detectorType
@@ -919,4 +918,89 @@ def test_type(session_simulator):
def test_numinterfaces(session_simulator):
d = Detector()
assert d.numinterfaces == 1
assert d.numinterfaces == 1
@pytest.mark.detectorintegration
def test_udp_datastream(session_simulator, request):
""" Test using udp_datastream for eiger, jungfrau and moench."""
det_type, num_interfaces, num_mods, d = session_simulator
assert d is not None
from slsdet import portPosition
if det_type in ['eiger']:
ports = [portPosition.LEFT, portPosition.RIGHT]
prev = [d.getUDPDataStream(i) for i in ports]
# ensure all equal for each value in prev
assert all_equal(prev)
prev_val = [element_if_equal(v) for v in prev]
#list
with pytest.raises(Exception) as exc_info:
d.udp_datastream = (ports[0], [True, False])
# invalid port position
with pytest.raises(Exception) as exc_info:
d.udp_datastream = (portPosition.TOP, True)
with pytest.raises(Exception) as exc_info:
d.udp_datastream = (portPosition.BOTTOM, True)
# without port position
with pytest.raises(Exception) as exc_info:
d.udp_datastream = True
d.udp_datastream = (ports[0], False)
assert d.udp_datastream[ports[0]] is False
d.udp_datastream = (ports[1], False)
assert d.udp_datastream[ports[1]] is False
d.udp_datastream = (ports[0], True)
assert d.udp_datastream[ports[0]] is True
d.udp_datastream = (ports[1], True)
assert d.udp_datastream[ports[1]] is True
d.setUDPDataStream(ports[0], element_if_equal(prev[0]))
d.setUDPDataStream(ports[1], element_if_equal(prev[1]))
elif det_type in ['jungfrau', 'moench'] and num_interfaces == 2:
ports = [portPosition.TOP, portPosition.BOTTOM]
prev = [d.getUDPDataStream(i) for i in ports]
# ensure all equal for each value in prev
assert all_equal(prev)
prev_val = [element_if_equal(v) for v in prev]
#list
with pytest.raises(Exception) as exc_info:
d.udp_datastream = (ports[0], [True, False])
# invalid port position
with pytest.raises(Exception) as exc_info:
d.udp_datastream = (portPosition.LEFT, True)
with pytest.raises(Exception) as exc_info:
d.udp_datastream = (portPosition.RIGHT, True)
# without port position
with pytest.raises(Exception) as exc_info:
d.udp_datastream = True
d.udp_datastream = (ports[0], False)
assert d.udp_datastream[ports[0]] is False
d.udp_datastream = (ports[1], False)
assert d.udp_datastream[ports[1]] is False
d.udp_datastream = (ports[0], True)
assert d.udp_datastream[ports[0]] is True
d.udp_datastream = (ports[1], True)
assert d.udp_datastream[ports[1]] is True
d.setUDPDataStream(ports[0], element_if_equal(prev[0]))
d.setUDPDataStream(ports[1], element_if_equal(prev[1]))
else:
with pytest.raises(Exception) as exc_info:
d.udp_datastream
Log(LogLevel.INFOGREEN, f"{request.node.name} passed")
@@ -76,6 +76,14 @@ foreach(exe ${MOENCH_EXECUTABLES})
slsProjectOptions
)
target_compile_options(${exe} PRIVATE
-Wno-unused-but-set-variable
-Wno-format-nonliteral
-Wno-format-security
-Wno-double-promotion
-Wno-unused-variable
-Wno-format-overflow)
set_target_properties(${exe} PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
@@ -204,9 +204,9 @@ int main(int argc, char *argv[]) {
*/
//#endif
if (totquad > cmin && cl.x >= xmin && cl.x <= xmax &&
if (totquad > static_cast<double>(cmin) && cl.x >= xmin && cl.x <= xmax &&
cl.y >= ymin && cl.y <= ymax &&
totquad < cmax) {
totquad < static_cast<double>(cmax)) {
// if (sum > cmin && totquad / sum > 0.8 && totquad / sum < 1.2 &&
// sum < cmax) {
@@ -1,10 +1,10 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "clogger.h"
#include "sls/ansi.h"
#include <errno.h>
#include <fcntl.h> // File control definitions
#include <linux/i2c-dev.h> // I2C_SLAVE, __u8 reg
#include <fcntl.h> // File control definitions
#include <stdio.h>
#include <stdlib.h> // atoi
#include <string.h> // memset
@@ -12,80 +12,102 @@
#include <termios.h> /* POSIX terminal control definitions */
#include <unistd.h> // read, close
#define PORTNAME "/dev/ttyBF1"
#define GOODBYE 200
#define BUFFERSIZE 16
#define I2C_DEVICE_FILE "/dev/i2c-0"
#define I2C_DEVICE_ADDRESS 0x4C
// #define I2C_DEVICE_ADDRESS 0x48
#define I2C_REGISTER_ADDRESS 0x40
#define PORTNAME "/dev/ttyBF1"
#define GOODBYE 200
#define BUFFERSIZE 16
#define INFILE "/sys/devices/platform/i2c-bfin-twi.0/i2c-0/0-0048/in0_input"
#define OUTFILE "/sys/devices/platform/i2c-bfin-twi.0/i2c-0/0-0048/out0_output"
#define OUTENABLE \
"/sys/devices/platform/i2c-bfin-twi.0/i2c-0/0-0048/out0_enable"
int i2c_open(const char *file, unsigned int addr) {
// device file
int fd = open(file, O_RDWR);
if (fd < 0) {
LOG(logERROR, ("Warning: Unable to open file %s\n", file));
int set_hv(int dac_value) {
if ((dac_value > 255) || (dac_value < 0)) {
LOG(logERROR, ("Invalid dac value %d\n", dac_value));
return -1;
}
// device address
if (ioctl(fd, I2C_SLAVE, addr & 0x7F) < 0) {
LOG(logERROR, ("Warning: Unable to set slave address:0x%x \n", addr));
return -2;
}
return fd;
}
dac_value = dac_value * 10;
int i2c_read() {
int fd = i2c_open(I2C_DEVICE_FILE, I2C_DEVICE_ADDRESS);
__u8 reg = I2C_REGISTER_ADDRESS & 0xff;
unsigned char buf = reg;
if (write(fd, &buf, 1) != 1) {
LOG(logERROR,
("Warning: Unable to write read request to register %d\n", reg));
FILE *file;
file = fopen(OUTFILE, "w");
if (file == NULL) {
perror("set_hv:");
LOG(logERROR, ("Cannot open out0_output file\n"));
return -1;
}
// read and update value (but old value read out)
if (read(fd, &buf, 1) != 1) {
LOG(logERROR, ("Warning: Unable to read register %d\n", reg));
return -2;
if (setvbuf(file, NULL, _IONBF, 0) != 0) {
perror("set_hv:");
LOG(logERROR, ("Cannot disable buffering\n"));
return -1;
}
// read again to read the updated value
if (read(fd, &buf, 1) != 1) {
LOG(logERROR, ("Warning: Unable to read register %d\n", reg));
return -2;
if (fprintf(file, "%d", dac_value) < 1) {
ferror(file);
LOG(logERROR, ("Couldn't write to out0_output file\n"));
return -1;
}
close(fd);
return buf;
}
int i2c_write(unsigned int value) {
__u8 val = value & 0xff;
int fd = i2c_open(I2C_DEVICE_FILE, I2C_DEVICE_ADDRESS);
if (fd < 0)
return fd;
__u8 reg = I2C_REGISTER_ADDRESS & 0xff;
char buf[3];
buf[0] = reg;
buf[1] = val;
if (write(fd, buf, 2) != 2) {
LOG(logERROR,
("Warning: Unable to write %d to register %d\n", val, reg));
if (fclose(file) != 0) {
perror("set_hv:");
LOG(logERROR, ("Troubles closing out0_output file\n"));
return -1;
}
close(fd);
return 0;
}
int enable_hv(int val) {
if ((val > 1) || (val < 0))
return -1;
FILE *file;
file = fopen(OUTENABLE, "w");
if (file == NULL) {
perror("enable_hv:");
LOG(logERROR, ("Cannot open out0_enable file\n"));
return -1;
}
if (setvbuf(file, NULL, _IONBF, 0) != 0) {
perror("enable_hv:");
LOG(logERROR, ("Cannot disable buffering\n"));
return -1;
}
if (fprintf(file, "%d", val) < 1) {
ferror(file);
LOG(logERROR, ("Couldn't write to out0_enable file\n"));
return -1;
}
if (fclose(file) != 0) {
perror("enable_hv:");
LOG(logERROR, ("Troubles closing out0_enable file\n"));
return -1;
}
return 0;
}
int get_hv() {
int value;
FILE *file;
file = fopen(INFILE, "r");
if (file == NULL) {
perror("get_hv:");
LOG(logERROR, ("Cannot open in0_input file\n"));
return -1;
}
if (fscanf(file, "%d", &value) < 1) {
ferror(file);
LOG(logERROR, ("Couldn't read from in0_input file\n"));
return -1;
}
if (fclose(file) != 0) {
perror("get_hv:");
LOG(logERROR, ("Troubles closing out0_enable file\n"));
return -1;
}
return value / 10;
}
int main(int argc, char *argv[]) {
enable_hv(0);
set_hv(0);
int fd = open(PORTNAME, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
LOG(logERROR, ("Warning: Unable to open port %s\n", PORTNAME));
@@ -126,7 +148,7 @@ int main(int argc, char *argv[]) {
int ival = 0;
char buffer[BUFFERSIZE];
memset(buffer, 0, BUFFERSIZE);
buffer[BUFFERSIZE - 1] = '\n';
// buffer[BUFFERSIZE - 1] = '\n';
LOG(logINFO, ("Ready...\n"));
while (ret != GOODBYE) {
@@ -149,35 +171,51 @@ int main(int argc, char *argv[]) {
}
// ok/ fail
memset(buffer, 0, BUFFERSIZE);
buffer[BUFFERSIZE - 1] = '\n';
if (i2c_write(ival) < 0)
strcpy(buffer, "fail ");
// buffer[BUFFERSIZE - 1] = '\n';
if (set_hv(ival) < 0)
strcpy(buffer, "fail\n");
else if (enable_hv(ival > 0 ? 1 : 0) < 0)
strcpy(buffer, "fail\n");
else
strcpy(buffer, "success ");
strcpy(buffer, "success\n");
/*
if (i2c_write(ival) < 0)
strcpy(buffer, "fail ");
else
strcpy(buffer, "success ");
*/
LOG(logINFO, ("Sending: '%s'\n", buffer));
n = write(fd, buffer, BUFFERSIZE);
n = write(fd, buffer, strlen(buffer)); // BUFFERSIZE);
LOG(logDEBUG1, ("Sent %d Bytes\n", n));
break;
case 'g':
ival = i2c_read();
// ok/ fail
memset(buffer, 0, BUFFERSIZE);
buffer[BUFFERSIZE - 1] = '\n';
ival = get_hv();
if (ival < 0)
strcpy(buffer, "fail ");
strcpy(buffer, "fail\n");
else
strcpy(buffer, "success ");
n = write(fd, buffer, BUFFERSIZE);
strcpy(buffer, "success\n");
/*
ival = i2c_read();
// ok/ fail
memset(buffer, 0, BUFFERSIZE);
buffer[BUFFERSIZE - 1] = '\n';
if (ival < 0)
strcpy(buffer, "fail ");
else
strcpy(buffer, "success ");
*/
n = write(fd, buffer, strlen(buffer)); // BUFFERSIZE);
LOG(logINFO, ("Sending: '%s'\n", buffer));
LOG(logDEBUG1, ("Sent %d Bytes\n", n));
// value
memset(buffer, 0, BUFFERSIZE);
buffer[BUFFERSIZE - 1] = '\n';
// buffer[BUFFERSIZE - 1] = '\n';
if (ival >= 0) {
LOG(logINFO, ("Sending: '%d'\n", ival));
sprintf(buffer, "%d ", ival);
n = write(fd, buffer, BUFFERSIZE);
sprintf(buffer, "%d\n", ival);
n = write(fd, buffer, strlen(buffer)); // BUFFERSIZE);
LOG(logINFO, ("Sent %d Bytes\n", n));
} else
LOG(logERROR, ("%s\n", buffer));
@@ -41,9 +41,9 @@ $(PROGS): $(OBJS)
hv9m_blackfin_server:9mhvserial_bf.c
$(BLACKFIN_CC) -o hv9m_blackfin_server 9mhvserial_bf.c -Wall #-DVERBOSE
$(BLACKFIN_CC) $(CFLAGS) -o hv9m_blackfin_server 9mhvserial_bf.c -Wall #-DVERBOSE
mv hv9m_blackfin_server $(DESTDIR)
rm hv9m_blackfin_server.gdb $(main_src)*.o $(md5_dir)*.o
rm hv9m_blackfin_server.gdb
clean:
rm -rf $(DESTDIR)/$(PROGS) *.o $(DESTDIR)/hv9m_blackfin_server $(main_src)*.o $(md5_dir)*.o
@@ -0,0 +1,3 @@
bin/ filter=lfs diff=lfs merge=lfs -text
bin/** filter=lfs diff=lfs merge=lfs -text
bin/matterhornDetectorServer filter=lfs diff=lfs merge=lfs -text
@@ -2,22 +2,40 @@
set(MATTERHORN_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/MatterhornApp.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/communication/SPICommunication.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/utils/HelperFunctions.cpp
)
set(MATTERHORN_INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/src
${CMAKE_CURRENT_SOURCE_DIR}/src/communication
${CMAKE_CURRENT_SOURCE_DIR}/src/utils
${CMAKE_CURRENT_SOURCE_DIR}/src/defs
${CMAKE_CURRENT_SOURCE_DIR}/../../slsSupportLib/include
${CMAKE_CURRENT_SOURCE_DIR}/../slsDetectorServer_cpp/include
)
if(SLS_USE_SIMULATOR)
list(APPEND MATTERHORN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/VirtualMatterhornServer.cpp)
set(MATTERHORN_SERVER_HEADER "VirtualMatterhornServer.hpp")
add_executable(matterhornDetectorServer_virtual ${MATTERHORN_SOURCES})
target_compile_definitions(matterhornDetectorServer_virtual PRIVATE
MATTERHORN_SERVER_HEADER=<VirtualMatterhornServer.hpp>
MATTERHORN_SERVER_CLASS=VirtualMatterhornServer
)
target_include_directories(matterhornDetectorServer_virtual
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/../../slsSupportLib/include
${CMAKE_CURRENT_SOURCE_DIR}/../slsDetectorServer_cpp/include)
PRIVATE ${MATTERHORN_INCLUDE_DIRS})
target_link_libraries(matterhornDetectorServer_virtual
PUBLIC
slsSupportStatic
slsServerStatic)
slsServerStatic
slsProjectOptions
PRIVATE
slsProjectWarnings
)
set_target_properties(matterhornDetectorServer_virtual PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
@@ -26,37 +44,61 @@ if(SLS_USE_SIMULATOR)
install(TARGETS matterhornDetectorServer_virtual
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
else()
list(APPEND MATTERHORN_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/MatterhornServer.cpp)
endif() # maybe better to have a else if build with simulators on one always uses the virtual server in MatterhornApp
if(SLS_USE_MATTERHORN)
if(CMAKE_CROSSCOMPILING) # only update version if cross compiling binaries
find_package(Python3 REQUIRED COMPONENTS Interpreter REQUIRED)
add_custom_target(update_server_version ALL
COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/etc/updateAPIVersion.py APIMATTERHORN ${CMAKE_SOURCE_DIR}/slsDetectorServers/matterhornServer ${CMAKE_SOURCE_DIR}/slsDetectorServers/slsDetectorServer_cpp
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
COMMENT "Updating APIMATTERHORN version"
VERBATIM)
endif()
add_executable(matterhornDetectorServer ${MATTERHORN_SOURCES})
if(CMAKE_CROSSCOMPILING) # only update version if cross compiling binaries
add_dependencies(matterhornDetectorServer update_server_version)
endif()
target_compile_definitions(matterhornDetectorServer PRIVATE
MATTERHORN_SERVER_HEADER=<MatterhornServer.hpp>
MATTERHORN_SERVER_CLASS=MatterhornServer
)
target_include_directories(matterhornDetectorServer
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
${CMAKE_CURRENT_SOURCE_DIR}/../../slsSupportLib/include
${CMAKE_CURRENT_SOURCE_DIR}/../slsDetectorServer_cpp/include)
PRIVATE ${MATTERHORN_INCLUDE_DIRS})
target_link_libraries(matterhornDetectorServer
PUBLIC
slsSupportStatic
#slsDetectorStatic
slsServerStatic)
slsServerStatic
slsProjectOptions
PRIVATE
slsProjectWarnings
)
if(CMAKE_CROSSCOMPILING) # change output directory to stay consitent with c server binaries when cross compiling
set(RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
else()
set(RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
endif()
set_target_properties(matterhornDetectorServer PROPERTIES
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
RUNTIME_OUTPUT_DIRECTORY ${RUNTIME_OUTPUT_DIRECTORY}
)
install(TARGETS matterhornDetectorServer
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
endif()
if(SLS_USE_TESTS)
add_subdirectory(tests)
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?
#)
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4bfbe17a6b0efbdcd3da78eafb1ac6c212eb81434f36267640f75fe743dc0aea
size 316776
@@ -1,113 +0,0 @@
#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 <array>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
namespace sls {
/// @brief Base class for Matterhorn Server, can be used to implement a virtual
/// server for testing and actual server
template <typename DerivedServer>
class BaseMatterhornServer
: public DetectorServer<BaseMatterhornServer<DerivedServer>> {
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<BaseMatterhornServer<DerivedServer>>(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 <typename DerivedServer>
ReturnCode
BaseMatterhornServer<DerivedServer>::processFunction(const detFuncs function_id,
ServerInterface &socket) {
switch (function_id) {
default:
throw RuntimeError(
fmt::format("Function {} not implemented",
getFunctionNameFromEnum((enum detFuncs)function_id)));
}
}
template <typename DerivedServer>
ReturnCode BaseMatterhornServer<DerivedServer>::get_num_udp_interfaces(
ServerInterface &socket) const {
return static_cast<ReturnCode>(
socket.sendResult(static_cast<int>(numUDPInterfaces)));
}
template <typename DerivedServer>
ReturnCode
BaseMatterhornServer<DerivedServer>::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<ReturnCode>(socket.sendResult(
version_cstr)); // TODO: check what would be possible return codes!!!
}
template <typename DerivedServer>
ReturnCode BaseMatterhornServer<DerivedServer>::get_detector_type(
ServerInterface &socket) {
int detectortype = slsDetectorDefs::detectorType::MATTERHORN;
return static_cast<ReturnCode>(socket.sendResult(detectortype));
}
template <typename DerivedServer>
std::string BaseMatterhornServer<DerivedServer>::getMatterhornServerVersion() {
return APIMATTERHORN;
}
template <typename DerivedServer>
ReturnCode
BaseMatterhornServer<DerivedServer>::initial_checks(ServerInterface &socket) {
return static_cast<DerivedServer *>(this)->initial_checks(socket);
}
} // namespace sls
@@ -1,27 +0,0 @@
#pragma once
#include "BaseMatterhornServer.h"
#include "TCPInterface.h"
#include "sls/sls_detector_defs.h"
#include <array>
#include <memory>
namespace sls {
class MatterhornServer : public BaseMatterhornServer<MatterhornServer> {
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
@@ -1,29 +0,0 @@
#include <cstdint>
#include <string_view>
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
@@ -1,24 +0,0 @@
#include "BaseMatterhornServer.h"
namespace sls {
class VirtualMatterhornServer
: public BaseMatterhornServer<VirtualMatterhornServer> {
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
@@ -0,0 +1,116 @@
#pragma once
#include "DetectorServer.hpp"
#include "TCPInterface.hpp"
#include "fmt/format.h"
#include "helpers/type_traits.hpp"
#include "sls/logger.h"
#include "sls/network_utils.h"
#include "sls/sls_detector_defs.h"
#include <array>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
namespace sls {
/// @brief Base class for Matterhorn Server, can be used to implement a virtual
/// server for testing and actual server
template <typename DerivedServer>
class BaseMatterhornServer
: public DetectorServer<BaseMatterhornServer<DerivedServer>> {
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(
std::unique_ptr<
DetectorServerImpl<is_stop_server<DerivedServer>::value>>
impl,
uint16_t port = DEFAULT_TCP_CNTRL_PORTNO)
: DetectorServer<BaseMatterhornServer<DerivedServer>>(std::move(impl),
port) {}
~BaseMatterhornServer() = default;
ProcessedResult set_counter_mask(ServerInterface &socket);
ProcessedResult get_counter_mask(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
*/
ProcessedResult processFunction(const detFuncs function_id,
ServerInterface &socket);
private:
DerivedServer *getDerived() { return static_cast<DerivedServer *>(this); }
const DerivedServer *getDerived() const {
return static_cast<const DerivedServer *>(this);
}
};
template <typename DerivedServer>
ProcessedResult
BaseMatterhornServer<DerivedServer>::processFunction(const detFuncs function_id,
ServerInterface &socket) {
switch (function_id) {
case detFuncs::F_SET_COUNTER_MASK:
return set_counter_mask(socket);
case detFuncs::F_GET_COUNTER_MASK:
return get_counter_mask(socket);
default:
throw RuntimeError(
fmt::format("Function {} not implemented",
getFunctionNameFromEnum((enum detFuncs)function_id)));
}
}
template <typename DerivedServer>
ProcessedResult
BaseMatterhornServer<DerivedServer>::set_counter_mask(ServerInterface &socket) {
uint32_t counter_mask{};
try {
(void)socket.Receive(counter_mask);
} catch (const SocketError &e) {
LOG(logERROR) << "Failed to receive counter mask: " << e.what();
return_fail("Failed to receive counter mask: " + std::string(e.what()));
}
try {
this->getImpl()->set_counter_mask(counter_mask);
} catch (const std::exception &e) {
return_fail("Failed to set counter mask: " + std::string(e.what()));
}
return send_ok(socket);
}
template <typename DerivedServer>
ProcessedResult BaseMatterhornServer<DerivedServer>::get_counter_mask(
ServerInterface &socket) const {
uint32_t counter_mask{};
try {
counter_mask = this->getImpl()->get_counter_mask();
} catch (const std::exception &e) {
return_fail("Failed to get counter mask: " + std::string(e.what()));
}
return send_result(socket, counter_mask);
}
} // namespace sls
@@ -0,0 +1,358 @@
#pragma once
#include "ArmBusCommunication.hpp"
#include "DetectorServerImpl.hpp"
#include "MemoryModel.hpp"
#include "communication/SPICommunication.hpp"
#include "defs/MatterhornDefs.hpp"
#include "defs/RegisterDefs.hpp"
#include "helpers/type_traits.hpp"
#include "sls/versionAPI.h"
#include "utils/HelperFunctions.hpp"
#include <cstdint>
#include <string>
namespace sls {
template <bool isStopServer>
class VirtualMatterhornServerImpl; // forward declare
template <typename DerivedMatterhornServerImpl>
class BaseMatterhornServerImpl
: public DetectorServerImpl<
is_stop_server<DerivedMatterhornServerImpl>::value> {
public:
BaseMatterhornServerImpl();
~BaseMatterhornServerImpl() = default;
// TODO: probably virtaul server specific details, can be moved to derived
// class
/// @brief initial setup of detector
void setupDetector();
/// @brief get matterhorn server version
std::string get_server_version() const;
static uint8_t get_detector_type();
static uint8_t get_num_udp_interfaces();
uint64_t get_num_frames() const;
void set_num_frames(const uint64_t num_frames);
uint32_t get_num_triggers() const;
void set_num_triggers(const uint32_t num_triggers);
uint32_t get_counter_mask() const;
void set_counter_mask(const uint32_t counter_mask);
void set_module_position(const size_t module_row, const size_t module_col,
const size_t module_index);
slsDetectorDefs::rxParameters get_receiver_parameters() const;
protected:
using MemoryModel = std::conditional_t<
std::is_same_v<DerivedMatterhornServerImpl,
VirtualMatterhornServerImpl<
is_stop_server<DerivedMatterhornServerImpl>::value>>,
VirtualMemoryModel<uint32_t>,
HardwareMemoryModel>; // 32 bit registers
// TODO: for now in MatterhornServer and not generic Server but can be
// templated on different IPCore types for each detector
BusCommunication<MatterhornDefs::MatterHornIPCores, MemoryModel>
busCommunication{};
using SPICommunicationClass = std::conditional_t<
std::is_same_v<DerivedMatterhornServerImpl,
VirtualMatterhornServerImpl<
is_stop_server<DerivedMatterhornServerImpl>::value>>,
VirtualSPICommunication<MatterhornDefs::MatterhornSPIRegisters>,
HardwareSPICommunication>;
SPICommunicationClass spiCommunication{};
private:
static constexpr uint8_t numUDPInterfaces =
1; // only one udp per module for now
};
template <typename DerivedMatterhornServerImpl>
BaseMatterhornServerImpl<
DerivedMatterhornServerImpl>::BaseMatterhornServerImpl() {
// map the IP core base addresses to memory
busCommunication.mapToMemory(); // TODO: should this happen in constructor?
// TODO: need to check if chip is attached
spiCommunication.open_spi(); // TODO: should this happen in constructor?
}
template <typename DerivedMatterhornServerImpl>
void BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::setupDetector() {
// TODO: extend
try {
// stop server does not talk to the board
if constexpr (!this->stop_server) {
set_num_frames(1);
set_num_triggers(1);
set_counter_mask(0xF); // enable counter all counters by default
}
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to setup detector: " << e.what();
this->detectorSetupStatus.error_message = std::string(e.what());
this->detectorSetupStatus.setup_status =
detector_setup_status::SETUP_STATUS::FAILED_SETUP;
}
this->detectorSetupStatus.setup_status =
detector_setup_status::SETUP_STATUS::SUCCESSFUL_SETUP;
}
template <typename DerivedMatterhornServerImpl>
std::string
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::get_server_version()
const {
return APIMATTERHORN;
}
template <typename DerivedMatterhornServerImpl>
uint8_t BaseMatterhornServerImpl<
DerivedMatterhornServerImpl>::get_num_udp_interfaces() {
return numUDPInterfaces;
}
template <typename DerivedMatterhornServerImpl>
uint8_t
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::get_detector_type() {
return slsDetectorDefs::detectorType::MATTERHORN;
}
template <typename DerivedMatterhornServerImpl>
uint64_t
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::get_num_frames() const {
try {
uint32_t num_frames =
busCommunication.readRegister(Reg::MH_SM_Frames_Reg);
return static_cast<uint64_t>(num_frames);
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to read number of frames from register: "
<< e.what();
throw;
}
}
template <typename DerivedMatterhornServerImpl>
void BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::set_num_frames(
const uint64_t num_frames) {
try {
busCommunication.writeRegister(Reg::MH_SM_Frames_Reg,
static_cast<uint32_t>(num_frames));
auto written_num_frames = busCommunication.readRegister(
Reg::MH_SM_Frames_Reg); // check if write was successful
if (num_frames != static_cast<uint64_t>(written_num_frames)) {
throw std::runtime_error(
fmt::format("Requested {} frames, but set {}", num_frames,
static_cast<uint64_t>(written_num_frames)));
}
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to set number of frames: " << e.what();
throw;
}
}
template <typename DerivedMatterhornServerImpl>
void BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::set_num_triggers(
const uint32_t num_triggers) {
try {
busCommunication.writeRegister(Reg::MH_SM_Triggers_Reg, num_triggers);
auto written_num_triggers = busCommunication.readRegister(
Reg::MH_SM_Triggers_Reg); // check if write was successful
if (num_triggers != written_num_triggers) {
throw std::runtime_error(
fmt::format("Requested {} triggers, but set {}", num_triggers,
written_num_triggers));
}
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to set number of triggers: " << e.what();
throw;
}
}
template <typename DerivedMatterhornServerImpl>
uint32_t
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::get_num_triggers()
const {
try {
uint32_t num_triggers =
busCommunication.readRegister(Reg::MH_SM_Triggers_Reg);
return num_triggers;
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to read number of triggers from register: "
<< e.what();
throw;
}
}
template <typename DerivedMatterhornServerImpl>
void BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::set_counter_mask(
const uint32_t counter_mask) {
// counter mask update to num consecutive counters and starting_counter
uint32_t spi_counter_mask{};
try {
spi_counter_mask = convertCounterMaskToSPICounterMask(counter_mask);
} catch (const std::invalid_argument &e) {
LOG(logERROR) << "Failed to convert counter mask to SPI counter mask: "
<< e.what();
throw std::invalid_argument(
"Failed to convert counter mask to SPI counter mask: " +
std::string(e.what()));
}
try {
auto reg_value = spiCommunication.SPIread(
SPIRegisters::NUM_COUNTERS.register_,
0); // TODO: how to handle different chip ids -> e.g. broadcast do
// we want it to be configurable for different chip ids? -
// Command overload for some of the SPI registers
setSPIRegisterField(reg_value, SPIRegisters::NUM_COUNTERS,
spi_counter_mask);
spiCommunication.SPIwrite(SPIRegisters::NUM_COUNTERS.register_, 0,
reg_value);
} catch (const std::exception &e) {
throw RuntimeError("Failed to set counter mask: " +
std::string(e.what()));
}
}
template <typename DerivedMatterhornServerImpl>
uint32_t
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::get_counter_mask()
const {
std::vector<std::byte> reg_value{};
try {
reg_value = spiCommunication.SPIread(
SPIRegisters::NUM_COUNTERS.register_,
0); // TODO: how to handle different chip ids -
} catch (const std::exception &e) {
throw sls::RuntimeError(
"Failed to read counter mask from SPI register: " +
std::string(e.what()));
}
// stores num_counters and starting_counter 0b0000 -> counter 0 enabled,
// 0b0001 -> counter 1 enabled, 0b0010
uint32_t spi_counter_mask =
getSPIRegisterField(reg_value, SPIRegisters::NUM_COUNTERS);
uint32_t actual_counter_mask =
convertSPICounterMaskToCounterMask(spi_counter_mask);
return actual_counter_mask;
}
template <typename DerivedMatterhornServerImpl>
void BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::set_module_position(
const size_t module_row, const size_t module_col,
const size_t module_index) {
// write to register
uint32_t register_value_LSB{};
uint32_t register_value_MSB{};
try {
register_value_LSB =
busCommunication.readRegister(Reg::Frame_HDR_ModCoord_LSB_Reg);
register_value_MSB =
busCommunication.readRegister(Reg::Frame_HDR_ModCoord_MSB_Reg);
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to read module position register: "
<< e.what();
throw;
}
try {
setRegisterField(register_value_LSB, Reg::ModuleRow, module_row);
setRegisterField(register_value_LSB, Reg::ModuleCol, module_col);
setRegisterField(register_value_MSB, Reg::ModuleCoordz, 0);
setRegisterField(register_value_MSB, Reg::ModuleIndex, module_index);
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to set module position register fields: "
<< e.what();
throw;
}
try {
busCommunication.writeRegister(Reg::Frame_HDR_ModCoord_LSB_Reg,
register_value_LSB);
auto written_register_value_LSB = busCommunication.readRegister(
Reg::Frame_HDR_ModCoord_LSB_Reg); // check if write was successful
busCommunication.writeRegister(Reg::Frame_HDR_ModCoord_MSB_Reg,
register_value_MSB);
auto written_register_value_MSB = busCommunication.readRegister(
Reg::Frame_HDR_ModCoord_MSB_Reg); // check if write was successful
if (register_value_LSB != written_register_value_LSB ||
register_value_MSB != written_register_value_MSB) {
throw std::runtime_error(
fmt::format("LSB: requested {}, but set {}. "
"MSB: requested {}, but set {}",
register_value_LSB, written_register_value_LSB,
register_value_MSB, written_register_value_MSB));
}
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to write module position register: "
<< e.what();
throw;
}
}
template <typename DerivedMatterhornServerImpl>
slsDetectorDefs::rxParameters
BaseMatterhornServerImpl<DerivedMatterhornServerImpl>::get_receiver_parameters()
const {
slsDetectorDefs::rxParameters rx_params{};
rx_params.udpInterfaces = numUDPInterfaces;
rx_params.udp_dstip = this->udpDetails[0].dstip;
rx_params.udp_dstport = this->udpDetails[0].dstport;
rx_params.udp_dstmac = this->udpDetails[0].dstmac;
rx_params.frames = get_num_frames();
rx_params.triggers = get_num_triggers();
// TODO: extend
// rx_params.expTimeNs = 0;
// rx_params.periodNs = 0;
// rx_params.dynamicRange = 0;
// rx_params.timMode = AUTO_TIMING;
// rx_params.counterMask = 0;
return rx_params;
}
} // namespace sls
@@ -1,11 +1,13 @@
#include "CommandLineOptions.h"
#include "VirtualMatterhornServer.h"
#include "CommandLineOptions.hpp"
#include MATTERHORN_SERVER_HEADER
#include "helpers/Helpers.hpp"
#include "sls/logger.h"
#include "sls/sls_detector_exceptions.h"
#include "sls/versionAPI.h"
#include <semaphore.h>
#include <csignal>
#include <fmt/format.h>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
@@ -62,6 +64,9 @@ int main(int argc, char *argv[]) {
LOG(TLogLevel::logINFOMAGENTA) << cli.printOptions();
// free shared memory from previous run (not removed if detector crashed)
freeSharedMemory();
// Register Ctrl+C handler
std::signal(SIGINT, sigInterruptHandler);
@@ -75,11 +80,12 @@ int main(int argc, char *argv[]) {
LOG(TLogLevel::logINFOBLUE) << "Stop Server [" << opts.port + 1 << "]";
try {
VirtualMatterhornServer stopServer(opts.port + 1);
MATTERHORN_SERVER_CLASS<true> stopServer(opts.port + 1);
while (!interruption) {
pause(); // wait for signal to exit
}
} catch (...) {
LOG(logERROR) << "Some Error occured in Stop Server, exiting";
kill(getppid(), SIGINT); // tell parent to exit // TODO: should then
// also return EXIT_FAILURE
}
@@ -93,13 +99,14 @@ int main(int argc, char *argv[]) {
LOG(TLogLevel::logINFOBLUE) << "Control Server [" << opts.port << "]\n";
try {
VirtualMatterhornServer server(
MATTERHORN_SERVER_CLASS server(
opts.port); // TODO use virtual if compiled with virtual
// simulators on
while (!interruption) {
pause(); // wait for signal to exit
}
} catch (...) {
LOG(logERROR) << "Some Error occured in Control Server, exiting";
LOG(sls::logINFOBLUE)
<< "Exiting Control Server [ Tid: " << gettid() << " ]";
LOG(sls::logINFO) << "Exiting Detector Server";
@@ -1,24 +0,0 @@
#include "MatterhornServer.h"
namespace sls {
MatterhornServer::MatterhornServer(uint16_t port)
: BaseMatterhornServer<MatterhornServer>(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<ReturnCode>(socket.sendResult(initial_checks_passed));
}
} // namespace sls
@@ -0,0 +1,41 @@
#pragma once
#include "BaseMatterhornServer.hpp"
#include "MatterhornServerImpl.hpp"
#include "TCPInterface.hpp"
#include "sls/sls_detector_defs.h"
#include <array>
#include <memory>
namespace sls {
template <bool isStopServer = false>
class MatterhornServer
: public BaseMatterhornServer<MatterhornServer<isStopServer>> {
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;
};
template <bool isStopServer>
MatterhornServer<isStopServer>::MatterhornServer(uint16_t port)
: BaseMatterhornServer<MatterhornServer<isStopServer>>(
std::make_unique<MatterhornServerImpl<isStopServer>>(), port) {
// should maybe be part of the constructor?
this->tcpInterface->startTCPServer();
// TODO: no init_server function for now is it neccessary to set the init
// flag
this->getImpl()->setupDetector();
}
} // namespace sls
@@ -0,0 +1,70 @@
#pragma once
#include "BaseMatterhornServerImpl.hpp"
namespace sls {
template <bool isStopServer = false>
class MatterhornServerImpl
: public BaseMatterhornServerImpl<MatterhornServerImpl<isStopServer>> {
public:
MatterhornServerImpl() = default;
~MatterhornServerImpl() = default;
slsDetectorDefs::runStatus get_run_status() const; // TODO: impement
void set_module_position_and_update_srcudpmac(
const std::array<int, 2> &position_info);
void set_source_udp_mac([[maybe_unused]] const uint64_t src_mac);
};
template <bool isStopServer>
slsDetectorDefs::runStatus
MatterhornServerImpl<isStopServer>::get_run_status() const {
// TODO: will also have a scanStatus - scanStatus should be in base
// implementation and shared between virtual and actual detector - split
// this function into two.
return slsDetectorDefs::runStatus::IDLE; // TODO: implement
}
template <bool isStopServer>
void MatterhornServerImpl<isStopServer>::
set_module_position_and_update_srcudpmac(
const std::array<int, 2> &position_info) {
// position_info = [num_modules_in_y, module_index]
const size_t module_row = position_info[1] % position_info[0];
if (position_info[0] <= 0) {
throw RuntimeError("Number of modules in y direction cannot be 0.");
}
const size_t module_col = position_info[1] / position_info[0];
try {
this->set_module_position(module_row, module_col, position_info[1]);
} catch (const std::exception &e) {
throw RuntimeError("Failed to set module position: " +
std::string(e.what()));
}
// TODO: update
if (this->udpDetails[0].srcmac ==
0) { // only configure if source mac address is not set already
uint64_t newSrcMac =
0x000000000000; // TODO: vendor address will be on SOM memory/
// different for 10G/100G
this->updateSrcMacAddress(newSrcMac);
}
}
template <bool isStopServer>
void MatterhornServerImpl<isStopServer>::set_source_udp_mac(
const uint64_t src_mac) {
throw RuntimeError(
"Cannot overwrite vendor specific source UDP MAC address.");
}
} // end namespace sls
@@ -1,24 +0,0 @@
#include "VirtualMatterhornServer.h"
namespace sls {
VirtualMatterhornServer::VirtualMatterhornServer(uint16_t port)
: BaseMatterhornServer<VirtualMatterhornServer>(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<ReturnCode>(socket.sendResult(initial_checks_passed));
}
} // namespace sls
@@ -0,0 +1,39 @@
#include "BaseMatterhornServer.hpp"
#include "VirtualMatterhornServerImpl.hpp"
namespace sls {
template <bool isStopServer = false>
class VirtualMatterhornServer
: public BaseMatterhornServer<VirtualMatterhornServer<isStopServer>> {
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;
};
template <bool isStopServer>
VirtualMatterhornServer<isStopServer>::VirtualMatterhornServer(uint16_t port)
: BaseMatterhornServer<VirtualMatterhornServer<isStopServer>>(
std::make_unique<VirtualMatterhornServerImpl<isStopServer>>(), port) {
LOG(logDEBUG) << "Initializing virtual Matterhorn server on port " << port;
// should maybe be part of the constructor?
this->tcpInterface->startTCPServer();
// TODO: no init_server function for now is it neccessary to set the init
// flag
this->getImpl()->setupDetector();
}
} // namespace sls
@@ -0,0 +1,87 @@
#pragma once
#include "BaseMatterhornServerImpl.hpp"
#include "sls/ToString.h"
namespace sls {
template <bool isStopServer = false>
class VirtualMatterhornServerImpl
: public BaseMatterhornServerImpl<
VirtualMatterhornServerImpl<isStopServer>> {
public:
VirtualMatterhornServerImpl();
~VirtualMatterhornServerImpl() = default;
slsDetectorDefs::runStatus get_run_status() const;
void set_module_position_and_update_srcudpmac(
const std::array<int, 2> &position_info);
void set_source_udp_mac(const uint64_t newsrcudpMac);
};
template <bool isStopServer>
VirtualMatterhornServerImpl<isStopServer>::VirtualMatterhornServerImpl() {
this->set_source_udp_ip(LOCALHOSTIP_INT);
}
template <bool isStopServer>
slsDetectorDefs::runStatus
VirtualMatterhornServerImpl<isStopServer>::get_run_status() const {
slsDetectorDefs::runStatus scanstatus{};
slsDetectorDefs::runStatus status{};
scanstatus = this->shm()->scanStatus;
status = this->shm()->status;
// TODO: why only error and running? what about other states?
if (scanstatus == slsDetectorDefs::runStatus::ERROR ||
scanstatus == slsDetectorDefs::runStatus::RUNNING) {
LOG(logINFO) << fmt::format("Scan status: {}\n", ToString(scanstatus));
return scanstatus;
}
LOG(logINFO) << fmt::format("Status: {}\n", ToString(status));
return status;
}
template <bool isStopServer>
void VirtualMatterhornServerImpl<isStopServer>::
set_module_position_and_update_srcudpmac(
const std::array<int, 2> &position_info) {
const size_t module_row = position_info[1] % position_info[0];
const size_t module_col = position_info[1] / position_info[0];
try {
this->set_module_position(module_row, module_col, position_info[1]);
} catch (const std::exception &e) {
throw RuntimeError("Failed to set module position: " +
std::string(e.what()));
}
// configure mac address based on module position
if (this->udpDetails[0].srcmac ==
0) { // only configure if source mac address is not set already
uint64_t newSrcMac =
generateMacAddressfromModulePosition(module_row, module_col);
this->updateSrcMacAddress(newSrcMac);
}
}
template <bool isStopServer>
void VirtualMatterhornServerImpl<isStopServer>::set_source_udp_mac(
const uint64_t newsrcudpMac) {
if (!isValidMac(newsrcudpMac)) {
throw RuntimeError("Invalid source MAC address: unicast bit or local "
"administration bit is not set");
}
this->updateSrcMacAddress(newsrcudpMac);
}
} // namespace sls
@@ -0,0 +1,120 @@
#include "SPICommunication.hpp"
#include <algorithm>
#include <fcntl.h>
#include <linux/spi/spidev.h>
#include <sys/ioctl.h>
#include <unistd.h>
namespace sls {
void HardwareSPICommunication::open_spi() {
// TODO device can change
spi_filedescriptor = open("/dev/spidev2.0", O_RDWR); // TODO use O_SYNC?
if (spi_filedescriptor < 0) {
throw RuntimeError("Could not open /dev/spidev2.0");
}
LOG(logINFO) << fmt::format("SPI Read: opened spidev2.0 with fd={}",
spi_filedescriptor);
}
void HardwareSPICommunication::close_spi() {
if (spi_filedescriptor >= 0) {
close(spi_filedescriptor);
LOG(logINFO) << "SPI Read: closed spidev2.0";
spi_filedescriptor = -1;
}
}
HardwareSPICommunication::~HardwareSPICommunication() { close_spi(); }
std::vector<std::byte>
HardwareSPICommunication::spi_read(const size_t n_bytes, const uint8_t chip_id,
const uint8_t register_id) const {
// allocate dummy data to shift out the data (first byte is command byte)
if (n_bytes == std::numeric_limits<size_t>::max()) {
throw RuntimeError("SPI read size overflow");
}
std::vector<std::byte> dummy_data(
n_bytes + 1, std::byte{0x00}); // +1 for the command byte
// First byte of the message is 4 bits chip_id then 4 bits register_id
dummy_data[0] =
static_cast<std::byte>(((chip_id & 0xF) << 4) | (register_id & 0xF));
// allocate data buffer to read out data into
std::vector<std::byte> read_data_buffer(n_bytes + 1, std::byte{0x00});
spi_ioc_transfer send_cmd{};
send_cmd.len = n_bytes + 1; // +1 for the command byte
send_cmd.tx_buf = reinterpret_cast<std::uintptr_t>(dummy_data.data());
send_cmd.rx_buf = reinterpret_cast<std::uintptr_t>(read_data_buffer.data());
// 0 - Normal operation, 1 - CSN remains zero after operation
// We use cs_change = 1 to not close the SPI transaction and
// allow for shifting the read out data back in to restore the
// register
send_cmd.cs_change = 1;
// transfer here
if (ioctl(spi_filedescriptor, SPI_IOC_MESSAGE(1), &send_cmd) < 0) {
throw RuntimeError(
fmt::format("SPI write failed with {}:{}", errno, strerror(errno)));
}
// copy data to output buffer
std::vector<std::byte> output_data(n_bytes);
std::memcpy(output_data.data(), read_data_buffer.data() + 1, n_bytes);
// copy the read out data back to the dummy data buffer to shift it back in
send_cmd.tx_buf = send_cmd.rx_buf;
send_cmd.cs_change =
0; // end the SPI transaction after shifting back in the data
if (ioctl(spi_filedescriptor, SPI_IOC_MESSAGE(1), &send_cmd) < 0) {
throw RuntimeError(
fmt::format("SPI write failed with {}:{}", errno, strerror(errno)));
}
return output_data;
}
void HardwareSPICommunication::spi_write(const uint8_t chip_id,
const uint8_t register_id,
const std::vector<std::byte> &data) {
const size_t n_bytes = data.size();
if (n_bytes == std::numeric_limits<size_t>::max()) {
throw RuntimeError("SPI read size overflow");
}
// First byte of the message is 4 bits chip_id then 4 bits register_id
std::vector<std::byte> write_data(n_bytes + 1); // +1 for the command byte
write_data[0] =
static_cast<std::byte>(((chip_id & 0xF) << 4) | (register_id & 0xF));
std::memcpy(write_data.data() + 1, data.data(), n_bytes);
spi_ioc_transfer send_cmd{};
send_cmd.len = n_bytes + 1; // +1 for the command byte
send_cmd.tx_buf = reinterpret_cast<std::uintptr_t>(write_data.data());
send_cmd.cs_change =
0; // end the SPI transaction after the write (we dont need to shift
// back in data here since we are not doing a read)
if (ioctl(spi_filedescriptor, SPI_IOC_MESSAGE(1), &send_cmd) < 0) {
throw RuntimeError(
fmt::format("SPI write failed with {}:{}", errno, strerror(errno)));
}
}
} // namespace sls
@@ -0,0 +1,205 @@
#include "MemoryModel.hpp"
#include "SPIRegisterHelperStructs.hpp"
#include "defs/MatterhornDefs.hpp"
#include "defs/SPIRegisterDefs.hpp"
#include "fmt/format.h"
#include "sls/logger.h"
#include "sls/sls_detector_exceptions.h"
#include <map>
#include <vector>
namespace sls {
/// @brief abstract base class for SPI communication
template <typename DerivedSPIModel> class SPICommunication {
public:
SPICommunication() = default;
~SPICommunication() = default;
std::vector<std::byte> SPIread(const SPIRegister &spi_reg,
const uint8_t chip_id) const;
void SPIwrite(const SPIRegister &spi_reg, const uint8_t chip_id,
const std::vector<std::byte> &data);
void open_spi();
private:
DerivedSPIModel *getDerived() {
return static_cast<DerivedSPIModel *>(this);
}
};
template <typename DerivedSPIModel>
void SPICommunication<DerivedSPIModel>::open_spi() {
getDerived()->open_spi();
}
template <typename DerivedSPIModel>
std::vector<std::byte>
SPICommunication<DerivedSPIModel>::SPIread(const SPIRegister &spi_reg,
const uint8_t chip_id) const {
if (chip_id >= MatterhornDefs::NUM_CHIPS_PER_MODULE) {
throw RuntimeError(
fmt::format("Chip id {} is out of range (0-{})", chip_id,
MatterhornDefs::NUM_CHIPS_PER_MODULE - 1));
}
return static_cast<const DerivedSPIModel *>(this)->spi_read(
spi_reg.n_bytes, chip_id, spi_reg.spi_register_id);
}
template <typename DerivedSPIModel>
void SPICommunication<DerivedSPIModel>::SPIwrite(
const SPIRegister &spi_reg, const uint8_t chip_id,
const std::vector<std::byte> &data) {
if (chip_id >= MatterhornDefs::NUM_CHIPS_PER_MODULE) {
throw RuntimeError(
fmt::format("Chip id {} is out of range (0-{})", chip_id,
MatterhornDefs::NUM_CHIPS_PER_MODULE - 1));
}
if (data.size() != spi_reg.n_bytes) {
throw RuntimeError(fmt::format("Data size {} does not match number of "
"bytes {} for SPI register {}",
data.size(), spi_reg.n_bytes,
spi_reg.spi_register_id));
}
getDerived()->spi_write(chip_id, spi_reg.spi_register_id, data);
getDerived()->spi_write(chip_id,
SPIRegisters::SPI_REG_ExtraClocks.spi_register_id,
std::vector<std::byte>{std::byte{
0x00}}); // extra clock trigger to actually load
// the new value into the register
}
/**
* Non destructive read from SPI register. Read n_bytes by shifting in
* dummy data while keeping csn 0 after the operation. Shift the read out
* data back in to restore the register.
*/
class HardwareSPICommunication
: public SPICommunication<HardwareSPICommunication> {
public:
HardwareSPICommunication() = default;
~HardwareSPICommunication();
void open_spi();
void spi_write(const uint8_t chip_id, const uint8_t register_id,
const std::vector<std::byte> &data);
std::vector<std::byte> spi_read(const size_t n_bytes, const uint8_t chip_id,
const uint8_t register_id) const;
private:
int spi_filedescriptor = -1;
void close_spi();
};
// template <SPIRegister... SPIRegisters, uint8_t NUM_CHIPS_PER_MODULE> // non
// type template parameters only for c++20
template <typename SPIRegisters> // TODO add a type trait to ensure it stores
// all fields
class VirtualSPICommunication
: public SPICommunication<VirtualSPICommunication<SPIRegisters>> {
public:
VirtualSPICommunication() {
// TODO should it be in the constructor?
/*
(virtual_registers.emplace(
SPIRegisters.spi_register_id,
VirtualMemoryModel<std::byte>{SPIRegisters.spi_register_id,
SPIRegisters.n_bytes *
NUM_CHIPS_PER_MODULE}),
...);
*/
LOG(logDEBUG) << fmt::format(
"Initializing virtual SPI communication with {} registers for {} "
"chips per module",
SPIRegisters::spiregisters.size(),
SPIRegisters::NUM_CHIPS_PER_MODULE);
for (const auto &reg : SPIRegisters::spiregisters) {
virtual_registers.emplace(
reg.spi_register_id,
VirtualMemoryModel<std::byte>{
reg.spi_register_id,
reg.n_bytes * SPIRegisters::NUM_CHIPS_PER_MODULE});
}
LOG(logDEBUG) << fmt::format(
"Initialized virtual SPI communication with {} registers for {} "
"chips per module",
virtual_registers.size(), SPIRegisters::NUM_CHIPS_PER_MODULE);
for (const auto &[register_id, register_memory] : virtual_registers) {
LOG(logDEBUG) << fmt::format(
"Virtual SPI register with id {} has virtual", register_id);
}
}
~VirtualSPICommunication() = default;
void open_spi() {
// resize the virtual register memory to the correct size based on
// the defined SPI registers
for (auto &[register_id, register_memory] : virtual_registers) {
LOG(logDEBUG) << fmt::format("Mapping virtual SPI register with id "
"{} to virtual memory",
register_id);
register_memory.mapToMemory();
}
}
std::vector<std::byte> spi_read(const size_t n_bytes, const uint8_t chip_id,
const uint8_t register_id) const {
auto mapped_register =
virtual_registers.at(register_id).getMappedMemoryPtr();
mapped_register +=
chip_id * n_bytes; // TODO: how to handle different chip ids ->
// e.g. broadcast do we want it to be
// configurable for different chip ids?
// TODO: should I emulate the shifting in of dummy data and shifting
// out of the register data here to be more realistic?
std::vector<std::byte> output_data(n_bytes);
std::memcpy(output_data.data(), mapped_register, n_bytes);
return output_data;
}
void spi_write(const uint8_t chip_id, const uint8_t register_id,
const std::vector<std::byte> &data) {
auto mapped_register =
virtual_registers.at(register_id).getMappedMemoryPtr();
mapped_register +=
chip_id * data.size(); // TODO: how to handle different
// chip ids -> e.g. broadcast do
// TODO: should I emulate the shifting in of dummy data and shifting
// out of
std::memcpy(mapped_register, data.data(), data.size());
}
private:
/// @brief map of register id to virtual memory model for each register
std::map<uint16_t, VirtualMemoryModel<std::byte>> virtual_registers{};
};
} // namespace sls
@@ -0,0 +1,87 @@
#pragma once
#include "fmt/format.h"
#include <cstdint>
#include <vector>
namespace sls {
struct SPIRegister {
/// @brief SPI register ID (0-15)
uint16_t spi_register_id{};
/// @brief number of bytes in the register
uint64_t n_bytes{};
};
struct SPIRegisterField {
/// @brief SPI register to which teh field belongs
SPIRegister register_{};
/// @brief least significant bit position of the field in the register
uint64_t lsb_position{};
/// @brief number of bits in the field
/// TODO: can it be larger?
uint32_t num_bits{};
};
// TODO: maybe change uint32_t but max field size is 32 bits so should be fine
// for now
void inline setSPIRegisterField(std::vector<std::byte> &register_value,
const SPIRegisterField &field,
uint32_t field_value) {
// check that the field value can fit in the bitmask
if ((field_value >> field.num_bits) != 0) {
throw std::invalid_argument(fmt::format(
"Value {} cannot fit in field {}", field_value, field.num_bits));
}
constexpr uint8_t bits_per_byte = 8;
// TODO: mmh doesnt feel very modern - maybe better to cast to uint32_t,
// alignment issues?
for (std::size_t i = 0; i < field.num_bits; ++i) {
std::size_t offset = field.lsb_position + i;
std::size_t byte_index = offset / bits_per_byte;
std::size_t bit_index = offset % bits_per_byte;
std::byte mask = std::byte(1) << bit_index;
const bool bit = (field_value >> i) & 0x1;
if (bit) {
register_value[byte_index] |=
mask; // set the bit in the register value
} else {
register_value[byte_index] &=
~mask; // clear the bit in the register value
}
}
}
uint32_t inline getSPIRegisterField(
const std::vector<std::byte> &register_value,
const SPIRegisterField &field) {
uint32_t field_value = 0;
constexpr uint8_t bits_per_byte = 8;
for (std::size_t i = 0; i < field.num_bits; ++i) {
std::size_t offset = field.lsb_position + i;
std::size_t byte_index = offset / bits_per_byte;
std::size_t bit_index = offset % bits_per_byte;
std::byte mask = std::byte(1) << bit_index;
field_value |=
(static_cast<uint32_t>((register_value[byte_index] & mask) >>
bit_index)
<< i); // extract the field value bit from the register value and
// set it in the correct position in the field value
}
return field_value;
}
} // namespace sls
@@ -0,0 +1,50 @@
#pragma once
#include "RegisterDefs.hpp"
#include "SPIRegisterDefs.hpp"
#include <array>
#include <cstdint>
namespace sls {
namespace MatterhornDefs {
constexpr uint8_t NUM_CHIPS_PER_MODULE = 8;
// TODO: should probably be a specialized template struct
/// @brief list of Matterhorn SPI registers
struct MatterhornSPIRegisters {
constexpr static std::array<SPIRegister, 12> spiregisters{
SPIRegisters::SPI_REG_ConfigUnit,
SPIRegisters::SPI_REG_ConfigCML,
SPIRegisters::SPI_REG_ManualSelector,
SPIRegisters::SPI_REG_CoreRSTUnit,
SPIRegisters::SPI_REG_StoreRSTUnit,
SPIRegisters::SPI_REG_Trimbits,
SPIRegisters::SPI_REG_McGyver,
SPIRegisters::SPI_REG_McGyver_par_load,
SPIRegisters::SPI_REG_ActionReg,
SPIRegisters::SPI_REG_InternalDACs,
SPIRegisters::SPI_REG_ChecksumReg,
SPIRegisters::SPI_REG_ExtraClocks};
constexpr static uint8_t NUM_CHIPS_PER_MODULE =
MatterhornDefs::NUM_CHIPS_PER_MODULE;
};
/// @brief list of Matterhorn IP cores
struct MatterHornIPCores {
using ipcore_enum_type = IPCore;
constexpr static std::array<IPCore, 5> ipcores{
IPCore::MH_RO_SM_AXI, IPCore::FHDR_AXI, IPCore::AURORA_STATUS,
IPCore::AURORA_STATUS2, IPCore::PACKETIZERREG};
constexpr static size_t ip_core_block_size = IPCORE_REGISTER_BLOCK_SIZE;
};
} // namespace MatterhornDefs
} // namespace sls
@@ -1,19 +1,27 @@
// clang-format off
#pragma once
#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
enum class IPCore : uint32_t {
MH_RO_SM_AXI = 0xB0010000,
FHDR_AXI = 0xB0011000,
AURORA_STATUS = 0xB0014000,
AURORA_STATUS2 = 0xB0015000,
PACKETIZERREG = 0x00000000, // TODO: need to update with actual address
UNKNOWN = 0x00000000 // dont know yet
};
constexpr size_t IPCORE_REGISTER_BLOCK_SIZE =
0x1000; // size of each IP core address space in bytes // TODO: maybe add in
// other file definitions
// clang-format off
namespace Reg {
// Register definitions
constexpr Register CTRL_Reg{IPCore::UNKNOWN, 0x0};
@@ -22,7 +30,7 @@ constexpr Register Status_Reg{IPCore::UNKNOWN, 0x4};
constexpr Register FPGAVersionReg{IPCore::UNKNOWN, 0x8};
constexpr Register FPGA_GIT_HEAD{IPCore::UNKNOWN, 0xc};
constexpr Register FPGA_GIT_HEADReg{IPCore::UNKNOWN, 0xc};
constexpr Register FixedPatternReg{IPCore::UNKNOWN, 0x10};
@@ -42,6 +50,8 @@ 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 MH_SM_Triggers_Reg{IPCore::MH_RO_SM_AXI, 0x18};
constexpr Register Frame_HDR_Set_Reg{IPCore::FHDR_AXI, 0x0};
constexpr Register Frame_HDR_FrameNumLSB_Reg{IPCore::FHDR_AXI, 0x4};
@@ -116,7 +126,7 @@ constexpr RegisterField FPGADetType{
FPGAVersionReg, 24, 0xff};
constexpr RegisterField FPGA_GIT_HEAD{
FPGA_GIT_HEAD, 0, 0xffffffff};
FPGA_GIT_HEADReg, 0, 0xffffffff};
constexpr RegisterField FixedPattern{
FixedPatternReg, 0, 0xffffffff};
@@ -136,6 +146,24 @@ constexpr RegisterField Start_Acquistion{
constexpr RegisterField Stop_Acquistion{
MH_SM_Ctrl_Reg, 1, 0x1};
constexpr RegisterField External_Counter_Enable{
MH_SM_Ctrl_Reg, 2, 0x1};
constexpr RegisterField Parallel_RO{
MH_SM_Ctrl_Reg, 3, 0x1};
constexpr RegisterField Trigger_Mode{
MH_SM_Ctrl_Reg, 4, 0x3};
constexpr RegisterField HW_Trigger_Polarity{
MH_SM_Ctrl_Reg, 6, 0x1};
constexpr RegisterField SW_Trigger{
MH_SM_Ctrl_Reg, 7, 0x1};
constexpr RegisterField Reset_Readout_SM{
MH_SM_Ctrl_Reg, 8, 0x1};
constexpr RegisterField MH_Readout_Exposure_Time{
MH_SM_Exposure_Reg, 0, 0xffffffff};
@@ -151,6 +179,9 @@ constexpr RegisterField MH_SM_StoreLength{
constexpr RegisterField MH_SM_ResetMHLength{
MH_SM_ResetMHLength_Reg, 0, 0xffffffff};
constexpr RegisterField MH_SM_Triggers{
MH_SM_Triggers_Reg, 0, 0xffffffff};
constexpr RegisterField Frame_Hdr_Set_Framenumber{
Frame_HDR_Set_Reg, 0, 0x1};
@@ -236,5 +267,17 @@ constexpr RegisterField Coordz{
PktCoordReg2, 0, 0xffff};
constexpr RegisterField ModuleRow{
Frame_HDR_ModCoord_LSB_Reg, 0, 0xffff};
constexpr RegisterField ModuleCol{
Frame_HDR_ModCoord_LSB_Reg, 16, 0xffff};
constexpr RegisterField ModuleCoordz{
Frame_HDR_ModCoord_MSB_Reg, 0, 0xffff};
constexpr RegisterField ModuleIndex{
Frame_HDR_ModCoord_MSB_Reg, 16, 0xffff};
} // namespace Reg
} // namespace sls
// clang-format on
@@ -0,0 +1,51 @@
#pragma once
#include "SPIRegisterHelperStructs.hpp"
#include <cstdint>
namespace sls {
namespace SPIRegisters {
// SPI registers
constexpr SPIRegister SPI_REG_ConfigUnit{0, 8};
constexpr SPIRegister SPI_REG_ConfigCML{1, 1};
constexpr SPIRegister SPI_REG_ManualSelector{2, 2};
constexpr SPIRegister SPI_REG_CoreRSTUnit{3, 4};
constexpr SPIRegister SPI_REG_StoreRSTUnit{4, 2};
constexpr SPIRegister SPI_REG_Trimbits{5, 256};
constexpr SPIRegister SPI_REG_McGyver{6, 512};
constexpr SPIRegister SPI_REG_McGyver_par_load{7, 512};
constexpr SPIRegister SPI_REG_ActionReg{11, 1};
constexpr SPIRegister SPI_REG_InternalDACs{13, 4};
constexpr SPIRegister SPI_REG_ChecksumReg{14, 32};
// Used to generate extra clocks after writing to trigger the load of the new
// value
constexpr SPIRegister SPI_REG_ExtraClocks{12,
1}; // TODO: dont know what size is
// SPI register fields
constexpr SPIRegisterField OUTPUT_MODE{SPI_REG_ConfigUnit, 4, 3};
/// @brief first two bits starting counter, second two bits number of counters
/// to read e.g. 0b0100 -> read counter 0 and 1, 0b0001 -> read counter 1
constexpr SPIRegisterField NUM_COUNTERS{SPI_REG_ConfigUnit, 8, 4};
/// @brief 00-> 16 bit, 01 -> 8 bit, 10 -> 4 bit, 11 -> reserved 16 bit
constexpr SPIRegisterField DYNAMIC_RANGE{SPI_REG_ConfigUnit, 14, 2};
// TODO: continue defining the rest of the fields as needed
} // namespace SPIRegisters
} // namespace sls
@@ -0,0 +1,75 @@
#include "HelperFunctions.hpp"
#include <stdexcept>
namespace sls {
uint32_t convertCounterMaskToSPICounterMask(const uint32_t counter_mask) {
uint32_t spi_counter_mask = 0;
switch (counter_mask) {
case 0b1:
spi_counter_mask = 0b0000; // counter 0 enabled
break;
case 0b10:
spi_counter_mask = 0b0001; // counter 1 enabled
break;
case 0b100:
spi_counter_mask = 0b0010; // counter 2 enabled
break;
case 0b1000:
spi_counter_mask = 0b0011; // counter 3 enabled
break;
case 0b11:
spi_counter_mask = 0b0100; // counter 0 and 1 enabled
break;
case 0b110:
spi_counter_mask = 0b0101; // counter 1 and 2 enabled
break;
case 0b1100:
spi_counter_mask = 0b0110; // counter 2 and 3 enabled
break;
case 0b1001:
spi_counter_mask = 0b0111; // counter 0 and 3 enabled
break;
case 0b111:
spi_counter_mask = 0b1000; // counter 0, 1 and 2 enabled
break;
case 0b1110:
spi_counter_mask = 0b1001; // counter 1, 2 and 3 enabled
break;
case 0b1101:
spi_counter_mask = 0b1010; // counter 0, 2 and 3 enabled
break;
case 0b1011:
spi_counter_mask = 0b1011; // counter 0, 1 and 3 enabled
break;
case 0b1111:
spi_counter_mask = 0b1100; // counter 0, 1, 2 and 3 enabled
break;
default:
throw std::invalid_argument(
"Invalid counter mask: Only contiguous counters are enabled "
"(including wrap around)");
}
return spi_counter_mask;
}
uint32_t convertSPICounterMaskToCounterMask(const uint32_t spi_counter_mask) {
uint32_t counter_mask{};
uint8_t start_counter = spi_counter_mask & 0b11; // extract starting counter
uint8_t num_counters =
((spi_counter_mask >> 2) & 0b11) + 1; // extract number of counters
constexpr uint8_t max_counters = 4;
for (uint8_t i = 0; i < num_counters; ++i) {
counter_mask |= (1 << ((start_counter + i) % max_counters));
}
return counter_mask;
}
} // namespace sls
@@ -0,0 +1,26 @@
/**
* @file HelperFunctions.hpp
* @short contains helper functions for the Matterhorn server implementation
* e.g. for processing of specific commands
*/
#include <cstdint>
namespace sls {
/**
* @brief converts the counter mask received from the client to the actual
* counter mask to be written to the SPI register based on the starting counter
* and number of counters to read
* @return actual counter mask to be written to the SPI register
*/
uint32_t convertCounterMaskToSPICounterMask(const uint32_t counter_mask);
/**
* @brief converts the actual counter mask read from the SPI register to the
* counter mask to be sent to the client e.g. bit set to 1 if counter
* enabled
* @return counter mask to be sent to the client
*/
uint32_t convertSPICounterMaskToCounterMask(const uint32_t spi_counter_mask);
} // namespace sls
@@ -0,0 +1,9 @@
target_sources(tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/test-HelperFunctions.cpp
${CMAKE_CURRENT_SOURCE_DIR}/../src/utils/HelperFunctions.cpp
)
target_include_directories(tests
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../src/utils>")
@@ -0,0 +1,48 @@
#include "HelperFunctions.hpp"
#include "catch.hpp"
#include <iostream>
namespace sls {
auto get_test_parameters_countermaskspiconversion() {
return GENERATE(std::make_tuple(uint32_t{0x1}, uint32_t{0b0000}),
std::make_tuple(uint32_t{0x2}, uint32_t{0b0001}),
std::make_tuple(uint32_t{0x4}, uint32_t{0b0010}),
std::make_tuple(uint32_t{0x3}, uint32_t{0b0100}),
std::make_tuple(uint32_t{0x6}, uint32_t{0b0101}),
std::make_tuple(uint32_t{0x7}, uint32_t{0b1000}),
std::make_tuple(uint32_t{0x8}, uint32_t{0b0011}),
std::make_tuple(uint32_t{0x9}, uint32_t{0b0111}),
std::make_tuple(uint32_t{0xB}, uint32_t{0b1011}),
std::make_tuple(uint32_t{0xC}, uint32_t{0b0110}),
std::make_tuple(uint32_t{0xD}, uint32_t{0b1010}),
std::make_tuple(uint32_t{0xE}, uint32_t{0b1001}),
std::make_tuple(uint32_t{0xF}, uint32_t{0b1100}),
std::make_tuple(uint32_t{0xB}, uint32_t{0b1011}));
}
TEST_CASE("Convert Counter Mask to SPI Counter Mask",
"[MatterHorn][HelperFunctions]") {
auto [counter_mask, expected_spi_counter_mask] =
get_test_parameters_countermaskspiconversion();
REQUIRE(convertCounterMaskToSPICounterMask(counter_mask) ==
expected_spi_counter_mask);
REQUIRE_THROWS(
convertCounterMaskToSPICounterMask(0xA)); // invalid counter mask
REQUIRE_THROWS(
convertCounterMaskToSPICounterMask(0x5)); // invalid counter mask
}
TEST_CASE("Convert SPI Counter Mask to Counter Mask",
"[MatterHorn][HelperFunctions]") {
auto [counter_mask, spi_counter_mask] =
get_test_parameters_countermaskspiconversion();
REQUIRE(convertSPICounterMaskToCounterMask(spi_counter_mask) ==
counter_mask);
}
} // namespace sls
@@ -0,0 +1,17 @@
#Usage: cmake .. -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake -DSLS_BUILD_ONLY_MATTERHORN=ON
set(SLS_ARM_COMPILER "/psi.ch/group/detector/firmware/arm64_linux/arm-gnu-toolchain-12.3.rel1-x86_64-aarch64-none-linux-gnu/" CACHE PATH "Path to the ARM cross compiler")
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(CMAKE_C_COMPILER "${SLS_ARM_COMPILER}/bin/aarch64-none-linux-gnu-gcc")
set(CMAKE_CXX_COMPILER "${SLS_ARM_COMPILER}/bin/aarch64-none-linux-gnu-g++")
set(CMAKE_AR "${SLS_ARM_COMPILER}/bin/aarch64-none-linux-gnu-ar")
set(CMAKE_RANLIB "${SLS_ARM_COMPILER}/bin/aarch64-none-linux-gnu-ranlib")
set(CMAKE_FIND_ROOT_PATH "${SLS_ARM_COMPILER}")
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
@@ -1,6 +1,8 @@
set(SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/TCPInterface.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/CommandLineOptions.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/MemoryModel.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/DetectorServerImpl.cpp
)
add_library(slsServerObject OBJECT
@@ -23,13 +25,13 @@ target_link_libraries(slsServerObject
set(DETECTOR_LIBRARY_TARGETS slsServerObject)
set(PUBLICHEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/TCPInterface.h
${CMAKE_CURRENT_SOURCE_DIR}/include/TCPInterface.hpp
)
#Shared library
if(SLS_BUILD_SHARED_LIBRARIES)
add_library(slsServerShared SHARED $<TARGET_OBJECTS:slsServerObject>)
target_link_libraries(slsServerShared PUBLIC slsServerObject)
target_link_libraries(slsServerShared PUBLIC slsServerObject PRIVATE slsProjectWarnings)
set_target_properties(slsServerShared PROPERTIES
VERSION ${PACKAGE_VERSION_MAJOR}.${PACKAGE_VERSION_MINOR}.${PACKAGE_VERSION_PATCH}
SOVERSION ${PACKAGE_VERSION_MAJOR}
@@ -42,7 +44,7 @@ endif(SLS_BUILD_SHARED_LIBRARIES)
#Static library
add_library(slsServerStatic STATIC $<TARGET_OBJECTS:slsServerObject>)
target_link_libraries(slsServerStatic PUBLIC slsServerObject)
target_link_libraries(slsServerStatic PUBLIC slsServerObject PRIVATE slsProjectWarnings)
set_target_properties(slsServerStatic PROPERTIES
ARCHIVE_OUTPUT_NAME SlsServerStatic
@@ -0,0 +1,87 @@
#pragma once
#include "RegisterHelperStructs.hpp"
#include "fmt/format.h"
#include <cstdint>
#include <fcntl.h>
#include <map>
#include <memory>
#include <stdexcept>
#include <sys/mman.h>
#include <vector>
// TODO: maybe should be templated on address type (e.g. uint32_t register or
// uint64_t register) for more flexibility?
namespace sls {
template <typename IPCores, typename MemoryModel> class BusCommunication {
using IPCoreEnumType = typename IPCores::ipcore_enum_type;
public:
BusCommunication();
void mapToMemory();
uint32_t readRegister(const Register &register_) const;
void writeRegister(const Register &register_, const uint32_t data);
private:
void bus_w(const uint32_t offset, IPCoreEnumType baseadress,
const uint32_t data);
uint32_t bus_r(const uint32_t offset, IPCoreEnumType baseadress) const;
/// @brief map from id of IP Core to memory model for the register block of
/// the IP core
std::map<IPCoreEnumType, MemoryModel> ipcoreregisterblocks{};
};
template <typename IPCores, typename MemoryModel>
BusCommunication<IPCores, MemoryModel>::BusCommunication() {
for (const auto &ip_core : IPCores::ipcores) {
ipcoreregisterblocks.emplace(ip_core,
MemoryModel{static_cast<uint32_t>(ip_core),
IPCores::ip_core_block_size});
}
}
template <typename IPCores, typename MemoryModel>
void BusCommunication<IPCores, MemoryModel>::mapToMemory() {
for (auto &map_elem : ipcoreregisterblocks) {
map_elem.second.mapToMemory();
}
}
template <typename IPCores, typename MemoryModel>
uint32_t BusCommunication<IPCores, MemoryModel>::readRegister(
const Register &register_) const {
return bus_r(register_.offset_in_bytes, register_.ip_core);
}
template <typename IPCores, typename MemoryModel>
void BusCommunication<IPCores, MemoryModel>::writeRegister(
const Register &register_, const uint32_t data) {
bus_w(register_.offset_in_bytes, register_.ip_core, data);
}
template <typename IPCores, typename MemoryModel>
uint32_t BusCommunication<IPCores, MemoryModel>::bus_r(
const uint32_t offset, const IPCoreEnumType baseadress) const {
auto ptr1 = ipcoreregisterblocks.at(baseadress).getMappedMemoryPtr() +
offset / (sizeof(uint32_t));
return *ptr1;
}
template <typename IPCores, typename MemoryModel>
void BusCommunication<IPCores, MemoryModel>::bus_w(
const uint32_t offset, const IPCoreEnumType baseadress,
const uint32_t data) {
auto ptr1 = ipcoreregisterblocks.at(baseadress).getMappedMemoryPtr() +
offset / (sizeof(uint32_t));
*ptr1 = data;
}
} // namespace sls
@@ -1,271 +0,0 @@
#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 <array>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
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{};
};
using ReturnCode = slsDetectorDefs::ReturnCode;
template <typename DerivedDetectorServer> 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> tcpInterface;
std::array<UDPInfo, 1>
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 <typename DerivedDetectorServer>
DetectorServer<DerivedDetectorServer>::DetectorServer(uint16_t port) {
validatePortNumber(port);
udpDetails[0].srcport = DEFAULT_UDP_SRC_PORTNO;
udpDetails[0].dstport = DEFAULT_UDP_DST_PORTNO;
std::function<ReturnCode(const detFuncs &, ServerInterface &)> fn =
[this](const detFuncs &function_id, ServerInterface &socket) {
return this->processFunction(function_id, socket);
};
tcpInterface = std::make_unique<TCPInterface>(fn, port);
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::processFunction(
const detFuncs function_id, ServerInterface &socket) {
switch (function_id) {
case detFuncs::F_GET_SERVER_VERSION:
return static_cast<DerivedDetectorServer *>(this)->get_version(socket);
case detFuncs::F_GET_DETECTOR_TYPE:
return static_cast<DerivedDetectorServer *>(this)->get_detector_type(
socket);
case detFuncs::F_INITIAL_CHECKS:
return static_cast<DerivedDetectorServer *>(this)->initial_checks(
socket);
case detFuncs::F_GET_NUM_INTERFACES:
return static_cast<DerivedDetectorServer *>(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<DerivedDetectorServer *>(this)->processFunction(function_id,
socket);
}
return ReturnCode::FAIL;
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_update_mode(
ServerInterface &socket) const {
return static_cast<ReturnCode>(
socket.sendResult(static_cast<int>(updateMode)));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::set_source_udp_mac(
ServerInterface &socket) {
uint64_t newsrcudpMac;
try {
int ret = socket.Receive<uint64_t>(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 <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_source_udp_mac(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].srcmac));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_source_udp_ip(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].srcip));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::set_destination_udp_mac(
ServerInterface &socket) {
uint64_t newDstMac;
try {
int ret = socket.Receive<uint64_t>(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 <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_destination_udp_mac(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].dstmac));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_destination_udp_ip(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].dstip));
}
template <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::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 <typename DerivedDetectorServer>
ReturnCode DetectorServer<DerivedDetectorServer>::get_destination_udp_port(
ServerInterface &socket) const {
return static_cast<ReturnCode>(socket.sendResult(udpDetails[0].dstport));
};
} // namespace sls
@@ -0,0 +1,523 @@
#pragma once
#include "DetectorServerImpl.hpp"
#include "TCPInterface.hpp"
#include "helpers/Helpers.hpp"
#include "helpers/type_traits.hpp"
#include "sls/logger.h"
#include "sls/network_utils.h"
#include "sls/sls_detector_defs.h"
#include "sls/versionAPI.h"
#include <array>
#include <cstring>
#include <functional>
#include <memory>
#include <string>
#include <unordered_map>
namespace sls {
using ReturnCode = slsDetectorDefs::ReturnCode;
template <typename DerivedDetectorServer> 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(
std::unique_ptr<
DetectorServerImpl<is_stop_server<DerivedDetectorServer>::value>>
impl_,
uint16_t port = DEFAULT_TCP_CNTRL_PORTNO);
~DetectorServer() = default;
protected:
/// @brief TCP/IP interface for communication with the client
std::unique_ptr<TCPInterface> tcpInterface;
std::unique_ptr<
DetectorServerImpl<is_stop_server<DerivedDetectorServer>::value>>
impl;
auto *getImpl() {
return static_cast<typename implementation_type_trait<
DerivedDetectorServer>::ImplType *>(impl.get());
}
const auto *getImpl() const {
return static_cast<const typename implementation_type_trait<
DerivedDetectorServer>::ImplType *>(impl.get());
}
private:
/// @brief get derived class
DerivedDetectorServer *getDerived() {
return static_cast<DerivedDetectorServer *>(this);
}
const DerivedDetectorServer *getDerived() const {
return static_cast<const DerivedDetectorServer *>(this);
}
ProcessedResult processFunction(const detFuncs function_id,
ServerInterface &socket);
// TODO dont know what this does?
ProcessedResult get_update_mode(ServerInterface &socket) const;
ProcessedResult get_source_udp_mac(ServerInterface &socket) const;
ProcessedResult set_source_udp_mac(ServerInterface &socket);
ProcessedResult get_source_udp_ip(ServerInterface &socket) const;
ProcessedResult set_source_udp_ip(ServerInterface &socket);
ProcessedResult get_source_udp_port(ServerInterface &socket) const;
ProcessedResult set_destination_udp_mac(ServerInterface &socket);
ProcessedResult get_destination_udp_mac(ServerInterface &socket) const;
ProcessedResult set_destination_udp_ip(ServerInterface &socket);
ProcessedResult get_destination_udp_ip(ServerInterface &socket) const;
ProcessedResult set_destination_udp_port(ServerInterface &socket);
ProcessedResult get_destination_udp_port(ServerInterface &socket) const;
ProcessedResult get_num_frames(ServerInterface &socket) const;
ProcessedResult set_num_frames(ServerInterface &socket);
ProcessedResult get_num_triggers(ServerInterface &socket) const;
ProcessedResult set_num_triggers(ServerInterface &socket);
ProcessedResult get_version(ServerInterface &socket) const;
ProcessedResult get_num_udp_interfaces(ServerInterface &socket) const;
ProcessedResult get_detector_type(ServerInterface &socket) const;
ProcessedResult get_receiver_parameters(ServerInterface &socket) const;
ProcessedResult get_run_status(ServerInterface &socket) const;
ProcessedResult initial_checks(ServerInterface &socket) const;
ProcessedResult
set_module_position_and_update_srcudpmac(ServerInterface &socket);
};
template <typename DerivedDetectorServer>
DetectorServer<DerivedDetectorServer>::DetectorServer(
std::unique_ptr<
DetectorServerImpl<is_stop_server<DerivedDetectorServer>::value>>
impl_,
uint16_t port)
: impl(std::move(impl_)) {
validatePortNumber(port);
std::function<ProcessedResult(const detFuncs &, ServerInterface &)> fn =
[this](const detFuncs &function_id, ServerInterface &socket) {
return this->processFunction(function_id, socket);
};
tcpInterface = std::make_unique<TCPInterface>(fn, port);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::processFunction(
const detFuncs function_id, ServerInterface &socket) {
switch (function_id) {
case detFuncs::F_GET_SERVER_VERSION:
return get_version(socket);
case detFuncs::F_GET_DETECTOR_TYPE:
return get_detector_type(socket);
case detFuncs::F_INITIAL_CHECKS:
return initial_checks(socket);
case detFuncs::F_GET_NUM_INTERFACES:
return 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);
case detFuncs::F_GET_RUN_STATUS:
return get_run_status(socket);
case detFuncs::F_GET_NUM_FRAMES:
return get_num_frames(socket);
case detFuncs::F_SET_NUM_FRAMES:
return set_num_frames(socket);
case detFuncs::F_GET_NUM_TRIGGERS:
return get_num_triggers(socket);
case detFuncs::F_SET_NUM_TRIGGERS:
return set_num_triggers(socket);
case detFuncs::F_GET_RECEIVER_PARAMETERS:
return get_receiver_parameters(socket);
case detFuncs::F_SET_POSITION:
return set_module_position_and_update_srcudpmac(socket);
default:
LOG(logDEBUG) << "Checking specific server functions for function ID: "
<< function_id;
// process detector specific functions
return getDerived()->processFunction(function_id, socket);
}
return return_fail("Function not implemented");
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_update_mode(
ServerInterface &socket) const {
const bool updateMode = impl->get_update_mode();
// TODO: catch the socket error during Send and add error message to the
// ProcessedResult but DatSocket shared with receiver - some refactoring
return send_result(socket, static_cast<uint32_t>(updateMode));
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_source_udp_mac(
ServerInterface &socket) const {
auto srcUdpMac = impl->get_source_udp_mac();
return send_result(socket, srcUdpMac);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_source_udp_mac(
ServerInterface &socket) {
uint64_t newsrcudpMac;
try {
(void)socket.Receive<uint64_t>(newsrcudpMac);
} catch (const SocketError &e) {
LOG(logERROR) << "Failed to receive new source UDP MAC address: "
<< e.what();
return return_fail("Failed to receive new source UDP MAC address: " +
std::string(e.what()));
}
try {
getImpl()->set_source_udp_mac(newsrcudpMac);
} catch (const std::exception &e) {
LOG(logERROR) << "Failed to set source UDP MAC address: " << e.what();
return_fail("Failed to set source UDP MAC address: " +
std::string(e.what()));
}
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_source_udp_ip(
ServerInterface &socket) {
uint32_t newSrcIp;
try {
(void)socket.Receive(newSrcIp);
} catch (const SocketError &e) {
auto error_message = "Failed to receive new source UDP IP address: " +
std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
impl->set_source_udp_ip(newSrcIp);
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_source_udp_ip(
ServerInterface &socket) const {
uint32_t src_UdpIp = impl->get_source_udp_ip();
return send_result(socket, src_UdpIp);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_destination_udp_mac(
ServerInterface &socket) {
uint64_t newDstMac;
try {
(void)socket.Receive<uint64_t>(newDstMac);
} catch (const SocketError &e) {
auto error_message =
"Failed to receive new destination UDP MAC address: " +
std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
impl->set_destination_udp_mac(newDstMac);
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_destination_udp_mac(
ServerInterface &socket) const {
auto dstUdpMac = impl->get_destination_udp_mac();
return send_result(socket, dstUdpMac);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_destination_udp_ip(
ServerInterface &socket) {
uint32_t newDstIp;
try {
(void)socket.Receive(newDstIp);
} catch (const SocketError &e) {
auto error_message =
"Failed to receive new destination UDP IP address: " +
std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
impl->set_destination_udp_ip(newDstIp);
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_destination_udp_ip(
ServerInterface &socket) const {
uint32_t dstUdpIp = impl->get_destination_udp_ip();
return send_result(socket, dstUdpIp);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_destination_udp_port(
ServerInterface &socket) {
uint16_t newDstPort;
try {
(void)socket.Receive(newDstPort);
} catch (const SocketError &e) {
auto error_message =
"Failed to receive new destination UDP port number: " +
std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
impl->set_destination_udp_port(newDstPort);
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_destination_udp_port(
ServerInterface &socket) const {
uint16_t dstUdpPort = impl->get_destination_udp_port();
return send_result(socket, dstUdpPort);
};
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_num_frames(
ServerInterface &socket) const {
uint64_t num_frames{};
try {
num_frames = getImpl()->get_num_frames();
} catch (const std::exception &e) {
auto error_message =
"Failed to get number of frames: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
return send_result(socket, num_frames);
}
template <typename DerivedDetectorServer>
ProcessedResult
DetectorServer<DerivedDetectorServer>::set_num_frames(ServerInterface &socket) {
int64_t num_frames{};
try {
(void)socket.Receive(num_frames);
} catch (const SocketError &e) {
auto error_message =
"Failed to receive number of frames: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
try {
getImpl()->set_num_frames(num_frames);
} catch (const std::exception &e) {
auto error_message =
"Failed to set number of frames: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_num_triggers(
ServerInterface &socket) const {
uint64_t num_triggers{};
try {
num_triggers = static_cast<uint64_t>(getImpl()->get_num_triggers());
} catch (const std::exception &e) {
auto error_message =
"Failed to get number of triggers: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
return send_result(socket, num_triggers);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::set_num_triggers(
ServerInterface &socket) {
int64_t num_triggers{};
try {
(void)socket.Receive(num_triggers);
} catch (const SocketError &e) {
auto error_message =
"Failed to receive number of triggers: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
try {
getImpl()->set_num_triggers(num_triggers);
} catch (const std::exception &e) {
auto error_message =
"Failed to set number of triggers: " + std::string(e.what());
LOG(logERROR) << error_message;
return return_fail(error_message);
}
return send_ok(socket);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_version(
ServerInterface &socket) const {
auto version =
getImpl()->get_server_version(); // TODO: get Impl from derived server
char version_cstr[MAX_STR_LENGTH]{};
std::snprintf(version_cstr, sizeof(version_cstr), "%s",
version.c_str()); // ensures temination
LOG(TLogLevel::logDEBUG) << "Server Version: " << version;
return send_result(
socket,
version_cstr); // TODO: check what would be possible return codes!!!
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_num_udp_interfaces(
ServerInterface &socket) const {
int num_udp_interfaces = getImpl()->get_num_udp_interfaces();
return send_result(socket, num_udp_interfaces);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_detector_type(
ServerInterface &socket) const {
uint32_t detectortype = getImpl()->get_detector_type();
return send_result(socket, detectortype);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_receiver_parameters(
ServerInterface &socket) const {
slsDetectorDefs::rxParameters rx_params =
getImpl()->get_receiver_parameters();
return send_result(socket, rx_params);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::get_run_status(
ServerInterface &socket) const {
slsDetectorDefs::runStatus status = getImpl()->get_run_status();
return send_result(socket, status);
}
template <typename DerivedDetectorServer>
ProcessedResult DetectorServer<DerivedDetectorServer>::initial_checks(
ServerInterface &socket) const {
auto detectorsetupstatus = getImpl()->get_detector_setup_status();
// TODO: should there be a time limit?
while (detectorsetupstatus.setup_status ==
detector_setup_status::NOT_SETUP) {
std::this_thread::sleep_for(std::chrono::seconds(1));
detectorsetupstatus = getImpl()->get_detector_setup_status();
}
if (detectorsetupstatus.setup_status ==
detector_setup_status::FAILED_SETUP) {
return return_fail("Initial checks failed: " +
detectorsetupstatus.error_message);
} else {
return send_result<bool>(socket, true);
}
}
template <typename DerivedDetectorServer>
ProcessedResult
DetectorServer<DerivedDetectorServer>::set_module_position_and_update_srcudpmac(
ServerInterface &socket) {
std::array<int, 2> position_info{}; // [num_modules_in_y, module_index]
try {
(void)socket.Receive(position_info.data(),
position_info.size() * sizeof(int));
} catch (const SocketError &e) {
LOG(logERROR)
<< "Failed to receive num modules in y dimension and module index: "
<< e.what();
return_fail(
"Failed to receive num modules in y dimension and module index: " +
std::string(e.what()));
}
try {
getImpl()->set_module_position_and_update_srcudpmac(position_info);
} catch (const std::exception &e) {
return_fail("Failed to set module position: " + std::string(e.what()));
}
return send_ok(socket);
}
} // namespace sls
@@ -0,0 +1,109 @@
#pragma once
#include "sls/SharedMemory.h"
#include <array>
#include <atomic>
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{};
};
/// @brief struct to store detector setup status
struct detector_setup_status {
enum SETUP_STATUS : uint8_t {
FAILED_SETUP = 0,
SUCCESSFUL_SETUP = 1,
NOT_SETUP = 2
};
/// @brief true if setupDetector() was successful, false otherwise
SETUP_STATUS setup_status{NOT_SETUP};
/// @brief error message if setupDetector() failed, empty otherwise
std::string error_message{};
};
/// @brief Shared memory structure for stop server to store run status
struct acquisitionStatus {
/* FIXED PATTERN FOR STATIC FUNCTIONS. DO NOT CHANGE, ONLY APPEND ------*/
int shmversion;
bool isValid{true}; // false if freed to block access from python or c++ api
std::atomic<slsDetectorDefs::runStatus> scanStatus{
slsDetectorDefs::runStatus::IDLE}; // idle, running or error
std::atomic<bool> scanStop{false};
// TODO: only neccessary for virtual, maybe have two shared memory
// structures, one for virtual
std::atomic<slsDetectorDefs::runStatus> status{
slsDetectorDefs::runStatus::IDLE};
std::atomic<bool> stop{false};
};
template <bool isStopServer> class DetectorServerImpl {
public:
DetectorServerImpl();
~DetectorServerImpl();
bool get_update_mode() const;
uint64_t get_source_udp_mac() const;
void set_source_udp_ip(const uint32_t srcip);
uint32_t get_source_udp_ip() const;
void set_destination_udp_ip(const uint32_t dstip);
uint32_t get_destination_udp_ip() const;
void set_destination_udp_mac(const uint64_t dstmac);
uint64_t get_destination_udp_mac() const;
void set_destination_udp_port(const uint16_t dstport);
uint16_t get_destination_udp_port() const;
detector_setup_status get_detector_setup_status() const;
protected:
std::array<UDPInfo, 1>
udpDetails{}; // TODO: for now only one receiver per module
/// @brief TODO what is this?
bool updateMode{
false}; // what should the default be - can update module size etc.
/// @brief shared mempory with aquisition status
mutable SharedMemory<acquisitionStatus> shm{
0, 0}; // TODO: is mutable really neccessary?
/// @brief sets source UDP MAC address in udpDetails and updates udp
/// header
/// @param srcmac
void updateSrcMacAddress(const uint64_t srcmac);
/// @brief true if setupDetector() was successful, false otherwise
detector_setup_status detectorSetupStatus{};
/// @brief true if the derived server is a stop server, false otherwise
static constexpr bool stop_server = isStopServer;
private:
/// @brief creates and maps shared memory
void createSharedMemory();
};
} // namespace sls
@@ -0,0 +1,68 @@
#pragma once
#include "fmt/format.h"
#include "sls/logger.h"
#include "sls/sls_detector_exceptions.h"
#include <cstdint>
#include <vector>
namespace sls {
/// @brief class to handle memory mapping and access for hardware IP cores
class HardwareMemoryModel {
public:
HardwareMemoryModel(const uint32_t IPcore_base_address,
const size_t size_memory_space_);
~HardwareMemoryModel();
void mapToMemory();
void unmapMemory();
volatile uint32_t *getMappedMemoryPtr() const;
private:
volatile uint32_t *mapped_memory_ptr{nullptr};
/// @brief offset of the IP core base address in the memory space, used for
/// mapping
const size_t IPCore_base_address{0};
/// @brief size mapped memory region [bytes]
const size_t size_memory_space{0};
};
/// @brief class to handle memory mapping and access for virtual IP cores (e.g.
/// use software implementation of memory)
template <typename DataType> class VirtualMemoryModel {
public:
// IPcore_base_address is not used for virtual memory model but kept for
// compatibility with HardwareMemoryModel interface
VirtualMemoryModel(const uint32_t IPcore_base_address,
const size_t size_memory_space_)
: size_memory_space(size_memory_space_) {
(void)IPcore_base_address; // suppress unused parameter warning
}
~VirtualMemoryModel() = default;
void mapToMemory() {
mapped_memory.resize(
size_memory_space /
sizeof(DataType)); // TODO: should it be zero initialized?
}
DataType *getMappedMemoryPtr() { return mapped_memory.data(); }
const DataType *getMappedMemoryPtr() const { return mapped_memory.data(); }
private:
std::vector<DataType> mapped_memory{};
/// @brief size mapped memory region [bytes]
const size_t size_memory_space{0};
};
} // namespace sls
@@ -0,0 +1,62 @@
#pragma once
#include <cstdint>
#include <exception>
#include <fmt/format.h>
#include <string_view>
namespace sls {
enum class IPCore : uint32_t; // forward declaration of IPCore enum class
/// @brief struct representing 32 bit register
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{};
};
// TODO: maybe static member function of RegisterField?
template <typename T>
void setRegisterField(uint32_t &registervalue, const RegisterField &reg_field,
T field_value) {
if (field_value > static_cast<T>(reg_field.bitmask)) {
throw std::invalid_argument(
fmt::format("Value {} cannot fit in field with bitmask {}",
field_value, reg_field.bitmask));
}
// Clear the bits corresponding to the field
registervalue &= ~(reg_field.bitmask << reg_field.bit_position);
// Set the new value for the field
registervalue |= (static_cast<uint32_t>(field_value) & reg_field.bitmask)
<< reg_field.bit_position;
}
template <typename T>
T getRegisterField(const uint32_t &registervalue,
const RegisterField &reg_field) {
// Extract the bits corresponding to the field and shift them to get the
// value
auto field_value =
(registervalue >> reg_field.bit_position) & reg_field.bitmask;
return static_cast<T>(field_value);
}
} // namespace sls
@@ -10,6 +10,31 @@
namespace sls {
using ReturnCode = slsDetectorDefs::ReturnCode;
struct ProcessedResult {
/// @brief return code of the processed command
slsDetectorDefs::ReturnCode returnCode{};
/// @brief error message to be sent to client in case of failure
std::string error_message{};
};
// communication helpers
inline ProcessedResult return_fail(std::string_view error_message) {
return ProcessedResult{ReturnCode::FAIL,
static_cast<std::string>(error_message)};
}
inline ProcessedResult send_ok(ServerInterface &socket) {
return ProcessedResult{
static_cast<ReturnCode>(socket.Send(ReturnCode::OK))};
}
template <typename T>
inline ProcessedResult send_result(ServerInterface &socket, const T &value) {
return ProcessedResult{static_cast<ReturnCode>(socket.sendResult(value))};
}
/**
* @brief TCPInterface class handles communication and processing of commands
* from Client to Server.
@@ -19,9 +44,10 @@ class TCPInterface {
public:
~TCPInterface();
TCPInterface(std::function<slsDetectorDefs::ReturnCode(
const detFuncs &, ServerInterface &)> &processFunction_,
const uint16_t portNumber = DEFAULT_TCP_CNTRL_PORTNO);
TCPInterface(
std::function<ProcessedResult(const detFuncs &, ServerInterface &)>
&processFunction_,
const uint16_t portNumber = DEFAULT_TCP_CNTRL_PORTNO);
/// @brief creates tcp thread
void startTCPServer();
@@ -40,12 +66,11 @@ class TCPInterface {
* @param function_id The ID of the function recived by the server and to
* be executed
*/
slsDetectorDefs::ReturnCode processReceivedData(const detFuncs function_id,
ServerInterface &socket);
ProcessedResult processReceivedData(const detFuncs function_id,
ServerInterface &socket);
/// @brief map of function IDs and corresponding functions
std::function<slsDetectorDefs::ReturnCode(const detFuncs &,
ServerInterface &)>
std::function<ProcessedResult(const detFuncs &, ServerInterface &)>
processFunction;
/// @brief TCP/IP port number for the detector server
@@ -0,0 +1,19 @@
/** @file Defs.hpp
* @brief this file contains some definitions used in the slsDetectorServer_cpp
* project.
*/
#pragma once
#include <cstdint>
namespace sls {
/// @brief Individual/Group bit offset in a 48 bit MAC address - 0 indicates
/// unicast mac address
constexpr uint8_t INDIVIDUAL_GROUP_BIT_OFFSET = 40; // 1000 0000
/// @brief Universal/Local bit offset in a 48 bit MAC address - 1 indicates
/// locally administered mac address, 0 indicates universally administered mac
/// address
constexpr uint8_t UNIVERSAL_LOCAL_BIT_OFFSET = 41; // 0100 0000
} // namespace sls
@@ -0,0 +1,64 @@
#pragma once
#include "Defs.hpp"
#include "DetectorServerImpl.hpp"
#include "sls/SharedMemory.h"
#include "sls/sls_detector_defs.h"
#include <cstdint>
#include <cstdlib>
namespace sls {
constexpr uint64_t mac_mask = 0xffffffffffff0000;
constexpr uint8_t offset_row_position_in_mac = 8; // given in bits
constexpr uint8_t offset_col_position_in_mac = 0; // given in bits
/// @brief generates a random locally administered unicast MAC address for the
/// source UDP
/// @return generated MAC address
inline uint64_t generateRandomMacAddress() {
uint64_t mac =
0xAA0000000000; // locally administered unicast address (0xA: 0b1010) //
// TODO maybe 0x02000000000 better?
for (int i = 2; i < 5; ++i) {
mac |= (static_cast<uint64_t>(std::rand() % 256) << (i * 8));
}
return mac;
}
/// @brief generates a MAC address based on the module's row and column
/// position, last 32 bits of the MAC address are set to module_row and
/// module_col
/// @param module_row
/// @param module_col
/// @return generated MAC address
inline uint64_t generateMacAddressfromModulePosition(const uint8_t module_row,
const uint8_t module_col) {
uint64_t newSrcMac = generateRandomMacAddress();
newSrcMac = (newSrcMac & mac_mask) |
(module_row << offset_row_position_in_mac) |
(module_col << offset_col_position_in_mac);
return newSrcMac;
}
/// @brief check that mac is unicast and locally administered
/// @param mac
/// @return true if mac is valid, false otherwise
inline bool isValidMac(const uint64_t mac) {
if ((mac << INDIVIDUAL_GROUP_BIT_OFFSET) == 0 &&
(mac << UNIVERSAL_LOCAL_BIT_OFFSET) == 1) {
return true;
}
return false;
}
inline void freeSharedMemory() {
SharedMemory<acquisitionStatus> shm(0, -1, "server");
if (shm.exists()) {
shm.removeSharedMemory();
}
}
} // namespace sls
@@ -0,0 +1,57 @@
#pragma once
#include <type_traits>
namespace sls {
// forward declares
template <bool isStopServer> class MatterhornServer;
template <bool isStopServer> class VirtualMatterhornServer;
template <bool isStopServer> class MatterhornServerImpl;
template <bool isStopServer> class VirtualMatterhornServerImpl;
template <typename DerivedServer> class BaseMatterhornServer;
// type trait to get implementation type
template <typename DetectorServer> struct implementation_type_trait;
template <bool isStopServer>
struct implementation_type_trait<
BaseMatterhornServer<MatterhornServer<isStopServer>>> {
using ImplType = MatterhornServerImpl<isStopServer>;
};
template <bool isStopServer>
struct implementation_type_trait<
BaseMatterhornServer<VirtualMatterhornServer<isStopServer>>> {
using ImplType = VirtualMatterhornServerImpl<isStopServer>;
};
// type trait to get stop server flag from Detector Server
template <typename DetectorServerImpl>
struct is_stop_server : std::false_type {};
template <>
struct is_stop_server<VirtualMatterhornServerImpl<true>> : std::true_type {};
template <>
struct is_stop_server<MatterhornServerImpl<true>> : std::true_type {};
template <>
struct is_stop_server<VirtualMatterhornServer<true>> : std::true_type {};
template <> struct is_stop_server<MatterhornServer<true>> : std::true_type {};
template <>
struct is_stop_server<BaseMatterhornServer<VirtualMatterhornServer<true>>>
: std::true_type {};
template <>
struct is_stop_server<BaseMatterhornServer<MatterhornServer<true>>>
: std::true_type {};
} // namespace sls
@@ -1,4 +1,4 @@
#include "CommandLineOptions.h"
#include "CommandLineOptions.hpp"
#include "sls/ToString.h"
#include "sls/sls_detector_exceptions.h"
@@ -0,0 +1,117 @@
#include "DetectorServerImpl.hpp"
#include "sls/logger.h"
#include "sls/sls_detector_exceptions.h"
#include <fmt/format.h>
namespace sls {
template class DetectorServerImpl<true>; // forward declare
template class DetectorServerImpl<false>; // forward declare
template <bool isStopServer>
DetectorServerImpl<isStopServer>::DetectorServerImpl() {
udpDetails[0].srcport = DEFAULT_UDP_SRC_PORTNO;
udpDetails[0].dstport = DEFAULT_UDP_DST_PORTNO;
createSharedMemory();
}
template <bool isStopServer>
DetectorServerImpl<isStopServer>::~DetectorServerImpl() {
shm.removeSharedMemory();
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::createSharedMemory() {
shm = SharedMemory<acquisitionStatus>(0, -1, "server");
if (shm.exists()) {
shm.openSharedMemory(true); // stop server
} else {
LOG(logINFOBLUE) << "Creating shared memory for acquisition status";
try {
shm.createSharedMemory();
} catch (const SharedMemoryAlreadyExistsError &e) {
shm.openSharedMemory(true); // potential race conditions between
// stop and control server
}
}
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::updateSrcMacAddress(
const uint64_t srcmac) {
LOG(logINFO) << "Updating source MAC address to: "
<< fmt::format("{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
(srcmac >> 40) & 0xff, (srcmac >> 32) & 0xff,
(srcmac >> 24) & 0xff, (srcmac >> 16) & 0xff,
(srcmac >> 8) & 0xff, srcmac & 0xff);
udpDetails[0].srcmac = srcmac;
// TODO: update UDP header with new source MAC address
// TODO: do i need to keep track of the configured member ?
}
template <bool isStopServer>
bool DetectorServerImpl<isStopServer>::get_update_mode() const {
return updateMode;
}
template <bool isStopServer>
uint64_t DetectorServerImpl<isStopServer>::get_source_udp_mac() const {
return udpDetails[0].srcmac;
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::set_source_udp_ip(const uint32_t srcip) {
udpDetails[0].srcip = srcip;
}
template <bool isStopServer>
uint32_t DetectorServerImpl<isStopServer>::get_source_udp_ip() const {
return udpDetails[0].srcip;
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::set_destination_udp_ip(
const uint32_t dstip) {
udpDetails[0].dstip = dstip;
}
template <bool isStopServer>
uint32_t DetectorServerImpl<isStopServer>::get_destination_udp_ip() const {
return udpDetails[0].dstip;
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::set_destination_udp_mac(
const uint64_t dstmac) {
// TODO: configuremac, check unicast address
udpDetails[0].dstmac = dstmac;
}
template <bool isStopServer>
uint64_t DetectorServerImpl<isStopServer>::get_destination_udp_mac() const {
return udpDetails[0].dstmac;
}
template <bool isStopServer>
void DetectorServerImpl<isStopServer>::set_destination_udp_port(
const uint16_t dstport) {
udpDetails[0].dstport = dstport;
}
template <bool isStopServer>
uint16_t DetectorServerImpl<isStopServer>::get_destination_udp_port() const {
return udpDetails[0].dstport;
}
template <bool isStopServer>
detector_setup_status
DetectorServerImpl<isStopServer>::get_detector_setup_status() const {
return detectorSetupStatus;
}
} // namespace sls
@@ -0,0 +1,62 @@
#include "MemoryModel.hpp"
#include <fcntl.h>
#include <memory>
#include <stdexcept>
#include <stdio.h>
#include <sys/mman.h>
#include <unistd.h>
namespace sls {
HardwareMemoryModel::HardwareMemoryModel(const uint32_t IPcore_base_address,
const size_t size_memory_space_)
: IPCore_base_address(IPcore_base_address),
size_memory_space(size_memory_space_) {}
void HardwareMemoryModel::mapToMemory() {
int fd = open("/dev/mem", O_RDWR | O_SYNC, 0);
if (fd == -1) {
throw RuntimeError("Can't find /dev/mem");
}
auto void_mmap_ptr =
mmap(nullptr, size_memory_space, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
IPCore_base_address);
if (void_mmap_ptr == MAP_FAILED) {
throw RuntimeError(
fmt::format("Failed to map base address: {}",
IPCore_base_address)); // TODO: needs ToString
}
mapped_memory_ptr = reinterpret_cast<volatile uint32_t *>(void_mmap_ptr);
close(fd);
}
volatile uint32_t *HardwareMemoryModel::getMappedMemoryPtr() const {
return mapped_memory_ptr;
}
void HardwareMemoryModel::unmapMemory() {
if (mapped_memory_ptr != nullptr) {
if (munmap(reinterpret_cast<void *>(
const_cast<uint32_t *>(mapped_memory_ptr)),
size_memory_space) < 0) {
LOG(logWARNING)
<< fmt::format("Failed to unmap memory for IP core: {}",
IPCore_base_address); // TODO: needs ToString
}
mapped_memory_ptr = nullptr;
}
}
HardwareMemoryModel::~HardwareMemoryModel() {
LOG(logDEBUG1) << "HardwareMemoryModel destructor called, unmapping memory";
unmapMemory();
}
} // namespace sls
@@ -1,4 +1,4 @@
#include "TCPInterface.h"
#include "TCPInterface.hpp"
#include "fmt/format.h"
#include "sls/logger.h"
@@ -8,8 +8,8 @@
namespace sls {
TCPInterface::TCPInterface(
std::function<slsDetectorDefs::ReturnCode(
const detFuncs &, ServerInterface &)> &processFunction_,
std::function<ProcessedResult(const detFuncs &, ServerInterface &)>
&processFunction_,
const uint16_t portNumber_)
: processFunction(processFunction_), portNumber(portNumber_),
server(portNumber_) {
@@ -44,19 +44,23 @@ void TCPInterface::startTCPServerClientConnection() {
auto socket = server.accept();
try {
socket.Receive(function_id);
(void)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(
auto processedResult = processReceivedData(
static_cast<detFuncs>(function_id), socket);
if (returncode == slsDetectorDefs::ReturnCode::FAIL) {
// TODO: should technically fail before
if (processedResult.returnCode ==
slsDetectorDefs::ReturnCode::FAIL) {
throw RuntimeError(fmt::format(
"Error processing command with fnum: {}",
getFunctionNameFromEnum((enum detFuncs)function_id)));
"Error while processing command with fnum: {}, Error: "
"{}",
getFunctionNameFromEnum((enum detFuncs)function_id),
processedResult.error_message));
}
} catch (const RuntimeError &e) {
@@ -76,22 +80,20 @@ void TCPInterface::startTCPServerClientConnection() {
LOG(logINFOBLUE) << "Exiting TCP Server";
}
slsDetectorDefs::ReturnCode
TCPInterface::processReceivedData(const detFuncs function_id,
ServerInterface &socket) {
ProcessedResult TCPInterface::processReceivedData(const detFuncs function_id,
ServerInterface &socket) {
LOG(logDEBUG1) << "calling function fnum: " << function_id << " ("
<< getFunctionNameFromEnum((enum detFuncs)function_id)
<< ")";
slsDetectorDefs::ReturnCode returncode =
processFunction(function_id, socket);
ProcessedResult processedResult = processFunction(function_id, socket);
LOG(logDEBUG1) << "Function "
<< getFunctionNameFromEnum((enum detFuncs)function_id)
<< " finished";
return returncode;
return processedResult;
}
} // namespace sls
@@ -80,7 +80,7 @@ _sd() {
local IS_PATH=0
local SLS_COMMANDS=" acquire activate adcclk adcenable adcenable10g adcindex adcinvert adclist adcname adcphase adcpipeline adcreg adcvpp apulse asamples autocompdisable badchannels blockingtrigger burstmode burstperiod bursts burstsl bustest cdsgain chipversion clearbit clearbusy clientversion clkdiv clkfreq clkphase collectionmode column compdisabletime confadc config configtransceiver counters currentsource dac dacindex daclist dacname dacvalues datastream dbitclk dbitphase dbitpipeline defaultdac defaultpattern define_bit define_reg definelist_bit definelist_reg delay delayl detectorserverversion detsize diodelay dpulse dr drlist dsamples execcommand exptime exptime1 exptime2 exptime3 extrastoragecells extsampling extsamplingsrc extsig fformat filtercells filterresistor findex firmwaretest firmwareversion fliprows flowcontrol10g fmaster fname foverwrite fpath framecounter frames framesl frametime free fwrite gaincaps gainmode gappixels gatedelay gatedelay1 gatedelay2 gatedelay3 gates getbit hardwareversion highvoltage hostname im_a im_b im_c im_d im_io imagetest include initialchecks inj_ch interpolation interruptsubframe kernelversion lastclient led lock master maxadcphaseshift maxclkphaseshift maxdbitphaseshift measuredperiod measuredsubperiod moduleid nextframenumber nmod numinterfaces overflow packageversion parallel parameters partialreset patfname patioctrl patlimits patloop patloop0 patloop1 patloop2 patmask patnloop patnloop0 patnloop1 patnloop2 patsetbit pattern patternstart patwait patwait0 patwait1 patwait2 patwaittime patwaittime0 patwaittime1 patwaittime2 patword pedestalmode period periodl polarity port power powerchip powerdac powerindex powerlist powername powervalues programfpga pulse pulsechip pulsenmove pumpprobe quad ratecorr readnrows readout readoutspeed readoutspeedlist rebootcontroller reg resetdacs resetfpga romode row runclk runtime rx_arping rx_clearroi rx_dbitlist rx_dbitoffset rx_dbitreorder rx_discardpolicy rx_fifodepth rx_frameindex rx_framescaught rx_framesperfile rx_hostname rx_jsonaddheader rx_jsonpara rx_lastclient rx_lock rx_missingpackets rx_padding rx_printconfig rx_realudpsocksize rx_roi rx_silent rx_start rx_status rx_stop rx_streamdummyheader rx_tcpport rx_threads rx_udpsocksize rx_version rx_zmqfreq rx_zmqhwm rx_zmqip rx_zmqport rx_zmqstartfnum rx_zmqstream samples savepattern scan scanerrmsg selinterface serialnumber setbit settings settingslist settingspath signalindex signallist signalname sleep slowadc slowadcindex slowadclist slowadcname slowadcvalues start status stop stopport storagecell_delay storagecell_start subdeadtime subexptime sync syncclk temp_10ge temp_adc temp_control temp_dcdc temp_event temp_fpga temp_fpgaext temp_fpgafl temp_fpgafr temp_slowadc temp_sodl temp_sodr temp_threshold templist tempvalues tengiga threshold thresholdnotb timing timing_info_decoder timinglist timingsource top transceiverenable trigger triggers triggersl trimbits trimen trimval tsamples txdelay txdelay_frame txdelay_left txdelay_right type udp_cleardst udp_dstip udp_dstip2 udp_dstlist udp_dstmac udp_dstmac2 udp_dstport udp_dstport2 udp_firstdst udp_numdst udp_reconfigure udp_srcip udp_srcip2 udp_srcmac udp_srcmac2 udp_validate update updatedetectorserver updatekernel updatemode user v_limit vchip_comp_adc vchip_comp_fe vchip_cs vchip_opa_1st vchip_opa_fd vchip_ref_comp_fe versions veto vetoalg vetofile vetophoton vetoref vetostream virtual vm_a vm_b vm_c vm_d vm_io zmqhwm zmqip zmqport "
local SLS_COMMANDS=" acquire activate adcclk adcenable adcenable10g adcindex adcinvert adclist adcname adcphase adcpipeline adcreg adcvpp apulse asamples autocompdisable badchannels blockingtrigger burstmode burstperiod bursts burstsl bustest cdsgain chipversion clearbit clearbusy clientversion clkdiv clkfreq clkphase collectionmode column compdisabletime confadc config configtransceiver counters currentsource dac dacindex daclist dacname dacvalues dbitclk dbitphase dbitpipeline defaultdac defaultpattern define_bit define_reg definelist_bit definelist_reg delay delayl detectorserverversion detsize diodelay dpulse dr drlist dsamples execcommand exptime exptime1 exptime2 exptime3 extrastoragecells extsampling extsamplingsrc extsig fformat filtercells filterresistor findex firmwaretest firmwareversion fliprows flowcontrol10g fmaster fname foverwrite fpath framecounter frames framesl frametime free fwrite gaincaps gainmode gappixels gatedelay gatedelay1 gatedelay2 gatedelay3 gates getbit hardwareversion highvoltage hostname im_a im_b im_c im_d im_io imagetest include initialchecks inj_ch interpolation interruptsubframe kernelversion lastclient led lock master maxadcphaseshift maxclkphaseshift maxdbitphaseshift measuredperiod measuredsubperiod moduleid nextframenumber nmod numinterfaces overflow packageversion parallel parameters partialreset patfname patioctrl patlimits patloop patloop0 patloop1 patloop2 patmask patnloop patnloop0 patnloop1 patnloop2 patsetbit pattern patternstart patwait patwait0 patwait1 patwait2 patwaittime patwaittime0 patwaittime1 patwaittime2 patword pedestalmode period periodl polarity port power powerchip powerdac powerindex powerlist powername powervalues programfpga pulse pulsechip pulsenmove pumpprobe quad ratecorr readnrows readout readoutspeed readoutspeedlist rebootcontroller reg resetdacs resetfpga romode row runclk runtime rx_arping rx_clearroi rx_dbitlist rx_dbitoffset rx_dbitreorder rx_discardpolicy rx_fifodepth rx_frameindex rx_framescaught rx_framesperfile rx_hostname rx_jsonaddheader rx_jsonpara rx_lastclient rx_lock rx_missingpackets rx_padding rx_printconfig rx_realudpsocksize rx_roi rx_silent rx_start rx_status rx_stop rx_streamdummyheader rx_tcpport rx_threads rx_udpsocksize rx_version rx_zmqfreq rx_zmqhwm rx_zmqip rx_zmqport rx_zmqstartfnum rx_zmqstream samples savepattern scan scanerrmsg selinterface serialnumber setbit settings settingslist settingspath signalindex signallist signalname sleep slowadc slowadcindex slowadclist slowadcname slowadcvalues start status stop stopport storagecell_delay storagecell_start subdeadtime subexptime sync syncclk temp_10ge temp_adc temp_control temp_dcdc temp_event temp_fpga temp_fpgaext temp_fpgafl temp_fpgafr temp_slowadc temp_sodl temp_sodr temp_threshold templist tempvalues tengiga threshold thresholdnotb timing timing_info_decoder timinglist timingsource top transceiverenable trigger triggers triggersl trimbits trimen trimval tsamples txdelay txdelay_frame txdelay_left txdelay_right type udp_cleardst udp_datastream udp_dstip udp_dstip2 udp_dstlist udp_dstmac udp_dstmac2 udp_dstport udp_dstport2 udp_firstdst udp_numdst udp_reconfigure udp_srcip udp_srcip2 udp_srcmac udp_srcmac2 udp_validate update updatedetectorserver updatekernel updatemode user v_limit vchip_comp_adc vchip_comp_fe vchip_cs vchip_opa_1st vchip_opa_fd vchip_ref_comp_fe versions veto vetoalg vetofile vetophoton vetoref vetostream virtual vm_a vm_b vm_c vm_d vm_io zmqhwm zmqip zmqport "
__acquire() {
FCN_RETURN=""
return 0
@@ -600,23 +600,6 @@ fi
fi
return 0
}
__datastream() {
FCN_RETURN=""
if [[ ${IS_GET} -eq 1 ]]; then
if [[ "${cword}" == "2" ]]; then
FCN_RETURN="bottom left right top"
fi
fi
if [[ ${IS_GET} -eq 0 ]]; then
if [[ "${cword}" == "2" ]]; then
FCN_RETURN="bottom left right top"
fi
if [[ "${cword}" == "3" ]]; then
FCN_RETURN="0 1"
fi
fi
return 0
}
__dbitclk() {
FCN_RETURN=""
if [[ ${IS_GET} -eq 1 ]]; then
@@ -2993,6 +2976,23 @@ __udp_cleardst() {
FCN_RETURN=""
return 0
}
__udp_datastream() {
FCN_RETURN=""
if [[ ${IS_GET} -eq 1 ]]; then
if [[ "${cword}" == "2" ]]; then
FCN_RETURN="bottom left right top"
fi
fi
if [[ ${IS_GET} -eq 0 ]]; then
if [[ "${cword}" == "2" ]]; then
FCN_RETURN="bottom left right top"
fi
if [[ "${cword}" == "3" ]]; then
FCN_RETURN="0 1"
fi
fi
return 0
}
__udp_dstip() {
FCN_RETURN=""
if [[ ${IS_GET} -eq 0 ]]; then
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -4,7 +4,7 @@
_sd() {
local SLS_COMMANDS=" acquire activate adcclk adcenable adcenable10g adcindex adcinvert adclist adcname adcphase adcpipeline adcreg adcvpp apulse asamples autocompdisable badchannels blockingtrigger burstmode burstperiod bursts burstsl bustest cdsgain chipversion clearbit clearbusy clientversion clkdiv clkfreq clkphase collectionmode column compdisabletime confadc config configtransceiver counters currentsource dac dacindex daclist dacname dacvalues datastream dbitclk dbitphase dbitpipeline defaultdac defaultpattern define_bit define_reg definelist_bit definelist_reg delay delayl detectorserverversion detsize diodelay dpulse dr drlist dsamples execcommand exptime exptime1 exptime2 exptime3 extrastoragecells extsampling extsamplingsrc extsig fformat filtercells filterresistor findex firmwaretest firmwareversion fliprows flowcontrol10g fmaster fname foverwrite fpath framecounter frames framesl frametime free fwrite gaincaps gainmode gappixels gatedelay gatedelay1 gatedelay2 gatedelay3 gates getbit hardwareversion highvoltage hostname im_a im_b im_c im_d im_io imagetest include initialchecks inj_ch interpolation interruptsubframe kernelversion lastclient led lock master maxadcphaseshift maxclkphaseshift maxdbitphaseshift measuredperiod measuredsubperiod moduleid nextframenumber nmod numinterfaces overflow packageversion parallel parameters partialreset patfname patioctrl patlimits patloop patloop0 patloop1 patloop2 patmask patnloop patnloop0 patnloop1 patnloop2 patsetbit pattern patternstart patwait patwait0 patwait1 patwait2 patwaittime patwaittime0 patwaittime1 patwaittime2 patword pedestalmode period periodl polarity port power powerchip powerdac powerindex powerlist powername powervalues programfpga pulse pulsechip pulsenmove pumpprobe quad ratecorr readnrows readout readoutspeed readoutspeedlist rebootcontroller reg resetdacs resetfpga romode row runclk runtime rx_arping rx_clearroi rx_dbitlist rx_dbitoffset rx_dbitreorder rx_discardpolicy rx_fifodepth rx_frameindex rx_framescaught rx_framesperfile rx_hostname rx_jsonaddheader rx_jsonpara rx_lastclient rx_lock rx_missingpackets rx_padding rx_printconfig rx_realudpsocksize rx_roi rx_silent rx_start rx_status rx_stop rx_streamdummyheader rx_tcpport rx_threads rx_udpsocksize rx_version rx_zmqfreq rx_zmqhwm rx_zmqip rx_zmqport rx_zmqstartfnum rx_zmqstream samples savepattern scan scanerrmsg selinterface serialnumber setbit settings settingslist settingspath signalindex signallist signalname sleep slowadc slowadcindex slowadclist slowadcname slowadcvalues start status stop stopport storagecell_delay storagecell_start subdeadtime subexptime sync syncclk temp_10ge temp_adc temp_control temp_dcdc temp_event temp_fpga temp_fpgaext temp_fpgafl temp_fpgafr temp_slowadc temp_sodl temp_sodr temp_threshold templist tempvalues tengiga threshold thresholdnotb timing timing_info_decoder timinglist timingsource top transceiverenable trigger triggers triggersl trimbits trimen trimval tsamples txdelay txdelay_frame txdelay_left txdelay_right type udp_cleardst udp_dstip udp_dstip2 udp_dstlist udp_dstmac udp_dstmac2 udp_dstport udp_dstport2 udp_firstdst udp_numdst udp_reconfigure udp_srcip udp_srcip2 udp_srcmac udp_srcmac2 udp_validate update updatedetectorserver updatekernel updatemode user v_limit vchip_comp_adc vchip_comp_fe vchip_cs vchip_opa_1st vchip_opa_fd vchip_ref_comp_fe versions veto vetoalg vetofile vetophoton vetoref vetostream virtual vm_a vm_b vm_c vm_d vm_io zmqhwm zmqip zmqport "
local SLS_COMMANDS=" acquire activate adcclk adcenable adcenable10g adcindex adcinvert adclist adcname adcphase adcpipeline adcreg adcvpp apulse asamples autocompdisable badchannels blockingtrigger burstmode burstperiod bursts burstsl bustest cdsgain chipversion clearbit clearbusy clientversion clkdiv clkfreq clkphase collectionmode column compdisabletime confadc config configtransceiver counters currentsource dac dacindex daclist dacname dacvalues dbitclk dbitphase dbitpipeline defaultdac defaultpattern define_bit define_reg definelist_bit definelist_reg delay delayl detectorserverversion detsize diodelay dpulse dr drlist dsamples execcommand exptime exptime1 exptime2 exptime3 extrastoragecells extsampling extsamplingsrc extsig fformat filtercells filterresistor findex firmwaretest firmwareversion fliprows flowcontrol10g fmaster fname foverwrite fpath framecounter frames framesl frametime free fwrite gaincaps gainmode gappixels gatedelay gatedelay1 gatedelay2 gatedelay3 gates getbit hardwareversion highvoltage hostname im_a im_b im_c im_d im_io imagetest include initialchecks inj_ch interpolation interruptsubframe kernelversion lastclient led lock master maxadcphaseshift maxclkphaseshift maxdbitphaseshift measuredperiod measuredsubperiod moduleid nextframenumber nmod numinterfaces overflow packageversion parallel parameters partialreset patfname patioctrl patlimits patloop patloop0 patloop1 patloop2 patmask patnloop patnloop0 patnloop1 patnloop2 patsetbit pattern patternstart patwait patwait0 patwait1 patwait2 patwaittime patwaittime0 patwaittime1 patwaittime2 patword pedestalmode period periodl polarity port power powerchip powerdac powerindex powerlist powername powervalues programfpga pulse pulsechip pulsenmove pumpprobe quad ratecorr readnrows readout readoutspeed readoutspeedlist rebootcontroller reg resetdacs resetfpga romode row runclk runtime rx_arping rx_clearroi rx_dbitlist rx_dbitoffset rx_dbitreorder rx_discardpolicy rx_fifodepth rx_frameindex rx_framescaught rx_framesperfile rx_hostname rx_jsonaddheader rx_jsonpara rx_lastclient rx_lock rx_missingpackets rx_padding rx_printconfig rx_realudpsocksize rx_roi rx_silent rx_start rx_status rx_stop rx_streamdummyheader rx_tcpport rx_threads rx_udpsocksize rx_version rx_zmqfreq rx_zmqhwm rx_zmqip rx_zmqport rx_zmqstartfnum rx_zmqstream samples savepattern scan scanerrmsg selinterface serialnumber setbit settings settingslist settingspath signalindex signallist signalname sleep slowadc slowadcindex slowadclist slowadcname slowadcvalues start status stop stopport storagecell_delay storagecell_start subdeadtime subexptime sync syncclk temp_10ge temp_adc temp_control temp_dcdc temp_event temp_fpga temp_fpgaext temp_fpgafl temp_fpgafr temp_slowadc temp_sodl temp_sodr temp_threshold templist tempvalues tengiga threshold thresholdnotb timing timing_info_decoder timinglist timingsource top transceiverenable trigger triggers triggersl trimbits trimen trimval tsamples txdelay txdelay_frame txdelay_left txdelay_right type udp_cleardst udp_datastream udp_dstip udp_dstip2 udp_dstlist udp_dstmac udp_dstmac2 udp_dstport udp_dstport2 udp_firstdst udp_numdst udp_reconfigure udp_srcip udp_srcip2 udp_srcmac udp_srcmac2 udp_validate update updatedetectorserver updatekernel updatemode user v_limit vchip_comp_adc vchip_comp_fe vchip_cs vchip_opa_1st vchip_opa_fd vchip_ref_comp_fe versions veto vetoalg vetofile vetophoton vetoref vetostream virtual vm_a vm_b vm_c vm_d vm_io zmqhwm zmqip zmqport "
__acquire() {
FCN_RETURN=""
return 0
@@ -524,23 +524,6 @@ fi
fi
return 0
}
__datastream() {
FCN_RETURN=""
if [[ ${IS_GET} -eq 1 ]]; then
if [[ "${cword}" == "2" ]]; then
FCN_RETURN="bottom left right top"
fi
fi
if [[ ${IS_GET} -eq 0 ]]; then
if [[ "${cword}" == "2" ]]; then
FCN_RETURN="bottom left right top"
fi
if [[ "${cword}" == "3" ]]; then
FCN_RETURN="0 1"
fi
fi
return 0
}
__dbitclk() {
FCN_RETURN=""
if [[ ${IS_GET} -eq 1 ]]; then
@@ -2917,6 +2900,23 @@ __udp_cleardst() {
FCN_RETURN=""
return 0
}
__udp_datastream() {
FCN_RETURN=""
if [[ ${IS_GET} -eq 1 ]]; then
if [[ "${cword}" == "2" ]]; then
FCN_RETURN="bottom left right top"
fi
fi
if [[ ${IS_GET} -eq 0 ]]; then
if [[ "${cword}" == "2" ]]; then
FCN_RETURN="bottom left right top"
fi
if [[ "${cword}" == "3" ]]; then
FCN_RETURN="0 1"
fi
fi
return 0
}
__udp_dstip() {
FCN_RETURN=""
if [[ ${IS_GET} -eq 0 ]]; then
+13 -13
View File
@@ -826,15 +826,6 @@ nextframenumber:
function: setNextFrameNumber
input_types: [ uint64_t ]
numinterfaces:
help: "[1, 2]\n\t[Jungfrau][Moench] Number of udp interfaces to stream data from detector. Default: 1.\n\tAlso enables second interface in receiver for listening (Writes a file per interface if writing enabled).\n\tAlso restarts client and receiver zmq sockets if zmq streaming enabled.\n\t[Eiger] Only gets with result 2."
inherit_actions: INTEGER_COMMAND_VEC_ID
actions:
GET:
function: getNumberofUDPInterfaces
PUT:
function: setNumberofUDPInterfaces
selinterface:
help: "[0, 1]\n\t[Jungfrau][Moench] The udp interface to stream data from detector. Effective only when number of interfaces is 1. Default: 0 (outer)"
inherit_actions: INTEGER_COMMAND_VEC_ID
@@ -1521,6 +1512,15 @@ zmqport:
function: setClientZmqPort
################# INTEGER_COMMAND_SET_NOID_GET_ID ############
numinterfaces:
help: "[1, 2]\n\t[Jungfrau][Moench] Number of udp interfaces to stream data from detector. Default: 1.\n\tAlso enables second interface in receiver for listening (Writes a file per interface if writing enabled).\n\tAlso restarts client and receiver zmq sockets if zmq streaming enabled.\n\t[Eiger] Only gets with result 2."
inherit_actions: INTEGER_COMMAND_SET_NOID_GET_ID
actions:
GET:
function: getNumberofUDPInterfaces
PUT:
function: setNumberofUDPInterfaces
sync:
inherit_actions: INTEGER_COMMAND_SET_NOID_GET_ID
help: "[0, 1]\n\t[Jungfrau][Moench] Enables or disables synchronization between modules. Sync mode requires at least one master configured. Also requires flatband cabling between master and slave with termination board."
@@ -3617,13 +3617,13 @@ quad:
cast_input: [ true ]
output: [ args.front() ]
datastream:
help: "[left|right] [0, 1]\n\t[Eiger] Enables or disables data streaming from left or/and right side of detector for 10 GbE mode. 1 (enabled) by default."
udp_datastream:
help: "[left|right|top|bottom] [0, 1]\n\tEnables or disables UDP data streaming from left or right of 10GbE UDP port of the detector. Options: left, right. Both ports are enabled (1) by default.\n\tEnables or disables UDP data streaming from the top or bottom of receiver. This option is available only when numinterfaces is set to 2. Options: top, bottom. Both interfaces are enabled (1) by default."
actions:
GET:
argc: 1
require_det_id: true
function: getDataStream
function: getUDPDataStream
input: [ 'args[0]' ]
input_types: [ defs::portPosition ]
cast_input: [ true ]
@@ -3631,7 +3631,7 @@ datastream:
PUT:
argc: 2
require_det_id: true
function: setDataStream
function: setUDPDataStream
input: [ 'args[0]', 'args[1]' ]
input_types: [ defs::portPosition, bool ]
cast_input: [ true, true ]
@@ -1,4 +1,3 @@
#configuration
detectorversion: firmwareversion
softwareversion: detectorserverversion
receiverversion: rx_version
@@ -8,8 +7,6 @@ detsizechan: detsize
trimdir: settingspath
settingsdir: settingspath
flippeddatax: fliprows
#acquisition parameters
cycles: triggers
cyclesl: triggersl
clkdivider: readoutspeed
@@ -18,10 +15,6 @@ vhighvoltage: highvoltage
digitest: imagetest
filter: filterresistor
readnlines: readnrows
# temperature
# super old dacs
vtr: vtrim
vrf: vrpreamp
vrs: vrshaper
@@ -33,8 +26,6 @@ vshaperneg: vrshaper_n
viinsh: vishaper
vpl: vcal_n
vph: vcal_p
# dacs
vthreshold: dac
vsvp: dac
vsvn: dac
@@ -93,17 +84,11 @@ vb_sda: dac
vcasc_sfp: dac
vipre_cds: dac
ibias_sfp: dac
defaultdacs: resetdacs
#acquisition
busy: clearbusy
receiver: rx_status
framescaught: rx_framescaught
startingfnum: nextframenumber
#Network Configuration (Detector<->Receiver)
detectorip: udp_srcip
detectorip2: udp_srcip2
detectormac: udp_srcmac
@@ -118,15 +103,11 @@ flowcontrol_10g: flowcontrol10g
txndelay_frame: txdelay_frame
txndelay_left: txdelay_left
txndelay_right: txdelay_right
#Receiver Config
r_silent: rx_silent
r_discardpolicy: rx_discardpolicy
r_padding: rx_padding
r_lock: rx_lock
r_lastclient: rx_lastclient
#File
fileformat: fformat
outdir: fpath
index: findex
@@ -134,23 +115,13 @@ enablefwrite: fwrite
masterfile: fmaster
overwrite: foverwrite
r_framesperfile: rx_framesperfile
#ZMQ Streaming Parameters (Receiver<->Client)
r_readfreq: rx_zmqfreq
rx_readfreq: rx_zmqfreq
rx_datastream: rx_zmqstream
#Eiger Specific
resmat: partialreset
#Jungfrau Specific
storagecells: extrastoragecells
auto_comp_disable: autocompdisable
comp_disable_time: compdisabletime
#Gotthard2 Specific
#Mythen3 Specific
#CTB Specific
adc: slowadc
flags: romode
i_a: im_a
@@ -158,16 +129,10 @@ i_b: im_b
i_c: im_c
i_d: im_d
i_io: im_io
#Pattern
patternX: pattern
#Moench
#Advanced
copydetectorserver: updatedetectorserver
#Insignificant
nframes: framecounter
now: runtime
timestamp: frametime
frameindex: rx_frameindex
frameindex: rx_frameindex
datastream: udp_datastream
@@ -2038,53 +2038,6 @@ dacvalues:
help: ''
infer_action: true
is_description: true
datastream:
actions:
GET:
args:
- arg_types:
- defs::portPosition
argc: 1
cast_input:
- true
check_det_id: false
convert_det_id: true
function: getDataStream
input:
- args[0]
input_types:
- defs::portPosition
output:
- OutString(t)
require_det_id: true
store_result_in_t: true
PUT:
args:
- arg_types:
- defs::portPosition
- bool
argc: 2
cast_input:
- true
- true
check_det_id: false
convert_det_id: true
function: setDataStream
input:
- args[0]
- args[1]
input_types:
- defs::portPosition
- bool
output:
- ToString(args)
require_det_id: true
store_result_in_t: false
command_name: datastream
function_alias: datastream
help: "[left|right] [0, 1]\n\t[Eiger] Enables or disables data streaming from left\
\ or/and right side of detector for 10 GbE mode. 1 (enabled) by default."
infer_action: true
dbitclk:
actions:
GET:
@@ -5671,7 +5624,7 @@ numinterfaces:
argc: 1
cast_input:
- true
check_det_id: false
check_det_id: true
convert_det_id: true
function: setNumberofUDPInterfaces
input:
@@ -5680,7 +5633,7 @@ numinterfaces:
- int
output:
- args.front()
require_det_id: true
require_det_id: false
store_result_in_t: false
command_name: numinterfaces
function_alias: numinterfaces
@@ -12035,6 +11988,56 @@ udp_cleardst:
help: "\n\tClears udp destination details on the detector."
infer_action: true
template: true
udp_datastream:
actions:
GET:
args:
- arg_types:
- defs::portPosition
argc: 1
cast_input:
- true
check_det_id: false
convert_det_id: true
function: getUDPDataStream
input:
- args[0]
input_types:
- defs::portPosition
output:
- OutString(t)
require_det_id: true
store_result_in_t: true
PUT:
args:
- arg_types:
- defs::portPosition
- bool
argc: 2
cast_input:
- true
- true
check_det_id: false
convert_det_id: true
function: setUDPDataStream
input:
- args[0]
- args[1]
input_types:
- defs::portPosition
- bool
output:
- ToString(args)
require_det_id: true
store_result_in_t: false
command_name: udp_datastream
function_alias: udp_datastream
help: "[left|right|top|bottom] [0, 1]\n\tEnables or disables UDP data streaming\
\ from left or right of 10GbE UDP port of the detector. Options: left, right.\
\ Both ports are enabled (1) by default.\n\tEnables or disables UDP data streaming\
\ from the top or bottom of receiver. This option is available only when numinterfaces\
\ is set to 2. Options: top, bottom. Both interfaces are enabled (1) by default."
infer_action: true
udp_dstip:
actions:
GET:
+35 -13
View File
@@ -719,8 +719,9 @@ class Detector {
* restarts client and receiver zmq sockets if zmq streaming enabled. \n
* [Gotthard2] second interface enabled to send veto information via 10Gbps
* for debugging. By default, if veto enabled, it is sent via 2.5 gbps
* interface. \nSetting this resets the receiver roi */
void setNumberofUDPInterfaces(int n, Positions pos = {});
* interface. \nSetting this resets the receiver roi and any udp datastream
* disables */
void setNumberofUDPInterfaces(int n);
/** [Jungfrau][Moench] */
Result<int> getSelectedUDPInterface(Positions pos = {}) const;
@@ -897,6 +898,37 @@ class Detector {
*/
void setTransmissionDelay(int step);
/** [Eiger] Returns whether the 10GbE UDP data stream from detector is
* enabled. Options: LEFT, RIGHT [Jungfrau][Moench] Returns whether the UDP
* data stream from receiver is enabled. Options: TOP, BOTTOM
*
*/
Result<bool> getUDPDataStream(const defs::portPosition port,
Positions pos = {}) const;
/** [Eiger] Enables or disables UDP data streaming from left or right of
* 10GbE UDP port of the detector. Default: enabled. Options: LEFT, RIGHT \n
* [Jungfrau][Moench] Enables or disables UDP data streaming from the top or
* bottom of receiver. Default: enabled. Options: TOP, BOTTOM. This option
* is available only when numinterfaces is set to 2.
*/
void setUDPDataStream(const defs::portPosition port, const bool enable,
Positions pos = {});
/** List of disabled UDP ports with index (moduleIndex * 2 + portIndex),
* where portIndex is 0 for BOTTOM/LEFT port, and 1 for TOP/RIGHT port.
* It is the index with 'd' in the file name when writing data. \n
* [Eiger] LEFT, RIGHT
* [Jungfrau][Moench] TOP, BOTTOM
* This feature is available only when numinterfaces is set to 2.
*/
std::vector<int> getRxDisabledUDPPortIndices() const;
/** list of possible port positions.
* [Eiger] LEFT, RIGHT
* [Jungfrau][Moench] TOP, BOTTOM
*/
std::vector<defs::portPosition> getPortPositionList() const;
///@}
/** @name Receiver Configuration */
@@ -1252,16 +1284,6 @@ class Detector {
* hardware required). */
void setQuad(const bool enable);
/** [Eiger] */
Result<bool> getDataStream(const defs::portPosition port,
Positions pos = {}) const;
/** [Eiger] enable or disable data streaming from left or right of detector
* for 10GbE. Default: enabled
*/
void setDataStream(const defs::portPosition port, const bool enable,
Positions pos = {});
/** [Eiger] Advanced */
Result<bool> getTop(Positions pos = {}) const;
@@ -2272,7 +2294,7 @@ class Detector {
private:
std::vector<uint16_t> getValidPortNumbers(uint16_t start_port);
void updateRxRateCorrections();
void setNumberofUDPInterfaces_(int n, Positions pos);
void setNumberofUDPInterfaces_(int n);
};
} // namespace sls
+82 -77
View File
@@ -2426,82 +2426,6 @@ std::string Caller::dacname(int action) {
return os.str();
}
std::string Caller::datastream(int action) {
std::ostringstream os;
// print help
if (action == slsDetectorDefs::HELP_ACTION) {
os << R"V0G0N([left|right] [0, 1]
[Eiger] Enables or disables data streaming from left or/and right side of detector for 10 GbE mode. 1 (enabled) by default. )V0G0N"
<< std::endl;
return os.str();
}
// check if action and arguments are valid
if (action == slsDetectorDefs::GET_ACTION) {
if (1 && args.size() != 1) {
throw RuntimeError("Wrong number of arguments for action GET");
}
if (args.size() == 1) {
try {
StringTo<defs::portPosition>(args[0]);
} catch (...) {
throw RuntimeError(
"Could not convert argument 0 to defs::portPosition");
}
}
}
else if (action == slsDetectorDefs::PUT_ACTION) {
if (1 && args.size() != 2) {
throw RuntimeError("Wrong number of arguments for action PUT");
}
if (args.size() == 2) {
try {
StringTo<defs::portPosition>(args[0]);
} catch (...) {
throw RuntimeError(
"Could not convert argument 0 to defs::portPosition");
}
try {
StringTo<bool>(args[1]);
} catch (...) {
throw RuntimeError("Could not convert argument 1 to bool");
}
}
}
else {
throw RuntimeError("INTERNAL ERROR: Invalid action: supported actions "
"are ['GET', 'PUT']");
}
// generate code for each action
if (action == slsDetectorDefs::GET_ACTION) {
if (args.size() == 1) {
auto arg0 = StringTo<defs::portPosition>(args[0]);
auto t = det->getDataStream(arg0, std::vector<int>{det_id});
os << OutString(t) << '\n';
}
}
if (action == slsDetectorDefs::PUT_ACTION) {
if (args.size() == 2) {
auto arg0 = StringTo<defs::portPosition>(args[0]);
auto arg1 = StringTo<bool>(args[1]);
det->setDataStream(arg0, arg1, std::vector<int>{det_id});
os << ToString(args) << '\n';
}
}
return os.str();
}
std::string Caller::dbitclk(int action) {
std::ostringstream os;
@@ -7052,8 +6976,12 @@ std::string Caller::numinterfaces(int action) {
if (action == slsDetectorDefs::PUT_ACTION) {
if (args.size() == 1) {
if (det_id != -1) {
throw RuntimeError(
"Cannot execute numinterfaces at module level");
}
auto arg0 = StringTo<int>(args[0]);
det->setNumberofUDPInterfaces(arg0, std::vector<int>{det_id});
det->setNumberofUDPInterfaces(arg0);
os << args.front() << '\n';
}
}
@@ -14985,6 +14913,83 @@ std::string Caller::udp_cleardst(int action) {
return os.str();
}
std::string Caller::udp_datastream(int action) {
std::ostringstream os;
// print help
if (action == slsDetectorDefs::HELP_ACTION) {
os << R"V0G0N([left|right|top|bottom] [0, 1]
Enables or disables UDP data streaming from left or right of 10GbE UDP port of the detector. Options: left, right. Both ports are enabled (1) by default.
Enables or disables UDP data streaming from the top or bottom of receiver. This option is available only when numinterfaces is set to 2. Options: top, bottom. Both interfaces are enabled (1) by default. )V0G0N"
<< std::endl;
return os.str();
}
// check if action and arguments are valid
if (action == slsDetectorDefs::GET_ACTION) {
if (1 && args.size() != 1) {
throw RuntimeError("Wrong number of arguments for action GET");
}
if (args.size() == 1) {
try {
StringTo<defs::portPosition>(args[0]);
} catch (...) {
throw RuntimeError(
"Could not convert argument 0 to defs::portPosition");
}
}
}
else if (action == slsDetectorDefs::PUT_ACTION) {
if (1 && args.size() != 2) {
throw RuntimeError("Wrong number of arguments for action PUT");
}
if (args.size() == 2) {
try {
StringTo<defs::portPosition>(args[0]);
} catch (...) {
throw RuntimeError(
"Could not convert argument 0 to defs::portPosition");
}
try {
StringTo<bool>(args[1]);
} catch (...) {
throw RuntimeError("Could not convert argument 1 to bool");
}
}
}
else {
throw RuntimeError("INTERNAL ERROR: Invalid action: supported actions "
"are ['GET', 'PUT']");
}
// generate code for each action
if (action == slsDetectorDefs::GET_ACTION) {
if (args.size() == 1) {
auto arg0 = StringTo<defs::portPosition>(args[0]);
auto t = det->getUDPDataStream(arg0, std::vector<int>{det_id});
os << OutString(t) << '\n';
}
}
if (action == slsDetectorDefs::PUT_ACTION) {
if (args.size() == 2) {
auto arg0 = StringTo<defs::portPosition>(args[0]);
auto arg1 = StringTo<bool>(args[1]);
det->setUDPDataStream(arg0, arg1, std::vector<int>{det_id});
os << ToString(args) << '\n';
}
}
return os.str();
}
std::string Caller::udp_dstlist(int action) {
std::ostringstream os;
+3 -2
View File
@@ -105,7 +105,6 @@ class Caller {
std::string daclist(int action);
std::string dacname(int action);
std::string dacvalues(int action);
std::string datastream(int action);
std::string dbitclk(int action);
std::string dbitphase(int action);
std::string dbitpipeline(int action);
@@ -345,6 +344,7 @@ class Caller {
std::string txdelay_right(int action);
std::string type(int action);
std::string udp_cleardst(int action);
std::string udp_datastream(int action);
std::string udp_dstip(int action);
std::string udp_dstip2(int action);
std::string udp_dstlist(int action);
@@ -474,7 +474,6 @@ class Caller {
{"daclist", &Caller::daclist},
{"dacname", &Caller::dacname},
{"dacvalues", &Caller::dacvalues},
{"datastream", &Caller::datastream},
{"dbitclk", &Caller::dbitclk},
{"dbitphase", &Caller::dbitphase},
{"dbitpipeline", &Caller::dbitpipeline},
@@ -718,6 +717,7 @@ class Caller {
{"txdelay_right", &Caller::txdelay_right},
{"type", &Caller::type},
{"udp_cleardst", &Caller::udp_cleardst},
{"udp_datastream", &Caller::udp_datastream},
{"udp_dstip", &Caller::udp_dstip},
{"udp_dstip2", &Caller::udp_dstip2},
{"udp_dstlist", &Caller::udp_dstlist},
@@ -903,6 +903,7 @@ class Caller {
{"now", "runtime"},
{"timestamp", "frametime"},
{"frameindex", "rx_frameindex"},
{"datastream", "udp_datastream"},
};
+1 -1
View File
@@ -1,6 +1,6 @@
#include "CtbConfig.h"
#include "SharedMemory.h"
#include "sls/SharedMemory.h"
#include "sls/ToString.h"
#include "sls/string_utils.h"
+26 -18
View File
@@ -1082,17 +1082,17 @@ Result<int> Detector::getNumberofUDPInterfaces(Positions pos) const {
return pimpl->Parallel(&Module::getNumberofUDPInterfacesFromShm, pos);
}
void Detector::setNumberofUDPInterfaces(int n, Positions pos) {
void Detector::setNumberofUDPInterfaces(int n) {
auto detType = getDetectorType().squash();
if (detType != defs::JUNGFRAU && detType != defs::MOENCH) {
throw RuntimeError(
"Cannot set number of udp interfaces for this detector.");
}
// also called by vetostream (for gotthard2)
setNumberofUDPInterfaces_(n, pos);
setNumberofUDPInterfaces_(n);
}
void Detector::setNumberofUDPInterfaces_(int n, Positions pos) {
void Detector::setNumberofUDPInterfaces_(int n) {
if (!size()) {
throw RuntimeError("No modules added.");
}
@@ -1102,10 +1102,10 @@ void Detector::setNumberofUDPInterfaces_(int n, Positions pos) {
bool previouslyReceiverStreaming = false;
uint16_t rxStartingPort = 0;
if (useReceiver) {
previouslyReceiverStreaming = getRxZmqDataStream(pos).squash(true);
previouslyReceiverStreaming = getRxZmqDataStream().squash(true);
rxStartingPort = getRxZmqPort({0}).squash(0);
}
pimpl->Parallel(&Module::setNumberofUDPInterfaces, pos, n);
pimpl->Parallel(&Module::setNumberofUDPInterfaces, {}, n);
// ensure receiver zmq socket ports are multiplied by 2 (2 interfaces)
setClientZmqPort(clientStartingPort, -1);
if (getUseReceiverFlag().squash(false)) {
@@ -1117,8 +1117,8 @@ void Detector::setNumberofUDPInterfaces_(int n, Positions pos) {
pimpl->setDataStreamingToClient(true);
}
if (previouslyReceiverStreaming) {
setRxZmqDataStream(false, pos);
setRxZmqDataStream(true, pos);
setRxZmqDataStream(false);
setRxZmqDataStream(true);
}
}
@@ -1317,6 +1317,24 @@ void Detector::setTransmissionDelay(int step) {
pimpl->setTransmissionDelay(step);
}
Result<bool> Detector::getUDPDataStream(const defs::portPosition port,
Positions pos) const {
return pimpl->getUDPDataStream(port, pos);
}
void Detector::setUDPDataStream(const defs::portPosition port,
const bool enable, Positions pos) {
pimpl->setUDPDataStream(port, enable, pos);
}
std::vector<int> Detector::getRxDisabledUDPPortIndices() const {
return pimpl->getRxDisabledUDPPortIndices();
}
std::vector<defs::portPosition> Detector::getPortPositionList() const {
return pimpl->getPortPositionList();
}
// Receiver
Result<bool> Detector::getUseReceiverFlag(Positions pos) const {
@@ -1750,16 +1768,6 @@ void Detector::setQuad(const bool enable) {
pimpl->Parallel(&Module::setQuad, {}, enable);
}
Result<bool> Detector::getDataStream(const defs::portPosition port,
Positions pos) const {
return pimpl->Parallel(&Module::getDataStream, pos, port);
}
void Detector::setDataStream(const defs::portPosition port, const bool enable,
Positions pos) {
pimpl->Parallel(&Module::setDataStream, pos, port, enable);
}
Result<bool> Detector::getTop(Positions pos) const {
return pimpl->Parallel(&Module::getTop, pos);
}
@@ -2012,7 +2020,7 @@ void Detector::setVetoStream(defs::streamingInterface interface,
? 2
: 1);
if (numinterfaces != old_numinterfaces) {
setNumberofUDPInterfaces_(numinterfaces, pos);
setNumberofUDPInterfaces_(numinterfaces);
}
}
+115 -44
View File
@@ -2,7 +2,7 @@
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "DetectorImpl.h"
#include "Module.h"
#include "SharedMemory.h"
#include "sls/SharedMemory.h"
#include "sls/ZmqSocket.h"
#include "sls/detectorData.h"
#include "sls/file_utils.h"
@@ -1126,10 +1126,17 @@ int DetectorImpl::acquire() {
// start receiver
if (receiver) {
Parallel(&Module::startReceiver, {});
}
startProcessingThread(receiver);
// to catch dummy zmq packets for disabled ports,
// start processing thread before startReceiver
if (dataReady != nullptr)
startRxZmqProcessingThread();
Parallel(&Module::startReceiver, {});
if (dataReady == nullptr)
startRxProgressThread();
}
// start and read all
try {
@@ -1314,53 +1321,46 @@ void DetectorImpl::printProgress(double progress) {
std::cout << '\r' << std::flush;
}
void DetectorImpl::startProcessingThread(bool receiver) {
void DetectorImpl::startRxZmqProcessingThread() {
dataProcessingThread =
std::thread(&DetectorImpl::processData, this, receiver);
std::thread(&DetectorImpl::readFrameFromReceiver, this);
}
void DetectorImpl::processData(bool receiver) {
if (receiver) {
if (dataReady != nullptr) {
readFrameFromReceiver();
}
// only update progress
else {
LOG(logINFO) << "Type 'q' and hit enter to stop acquisition";
double progress = 0;
printProgress(progress);
void DetectorImpl::startRxProgressThread() {
dataProcessingThread = std::thread(&DetectorImpl::printRxProgress, this);
}
while (true) {
// to exit acquire by typing q
if (kbhit() != 0) {
if (fgetc(stdin) == 'q') {
LOG(logINFO)
<< "Caught the command to stop acquisition";
stopDetector({});
}
}
// get and print progress
double temp =
(double)Parallel(&Module::getReceiverProgress, {0})
.squash();
if (temp != progress) {
printProgress(progress);
progress = temp;
}
void DetectorImpl::printRxProgress() {
LOG(logINFO) << "Type 'q' and hit enter to stop acquisition";
double progress = 0;
printProgress(progress);
// exiting loop
if (getJoinThreadFlag()) {
// print progress one final time before exiting
progress =
(double)Parallel(&Module::getReceiverProgress, {0})
.squash();
printProgress(progress);
break;
}
// otherwise error when connecting to the receiver too fast
std::this_thread::sleep_for(std::chrono::milliseconds(100));
while (true) {
// to exit acquire by typing q
if (kbhit() != 0) {
if (fgetc(stdin) == 'q') {
LOG(logINFO) << "Caught the command to stop acquisition";
stopDetector({});
}
}
// get and print progress
double temp =
(double)Parallel(&Module::getReceiverProgress, {0}).squash();
if (temp != progress) {
progress = temp;
printProgress(progress);
}
// exiting loop
if (getJoinThreadFlag()) {
// print progress one final time before exiting
progress =
(double)Parallel(&Module::getReceiverProgress, {0}).squash();
printProgress(progress);
break;
}
// otherwise error when connecting to the receiver too fast
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
@@ -1644,6 +1644,77 @@ void DetectorImpl::verifyUniqueHost(
}
}
void DetectorImpl::assertTwoUDPDataInterfaces(const std::string &cmd) const {
// assert globally
auto numInterfaces =
Parallel(&Module::getNumberofUDPInterfacesFromShm, {})
.tsquash("Inconsistent number of UDP interfaces among modules");
if (numInterfaces != 2) {
throw RuntimeError(
"Cannot " + cmd +
". Change number of udp interfaces to 2 (cmd = numinterfaces).");
}
}
Result<bool> DetectorImpl::getUDPDataStream(const defs::portPosition port,
Positions pos) const {
assertTwoUDPDataInterfaces("get enable/disable UDP ports");
return Parallel(&Module::getUDPDataStream, pos, port);
}
void DetectorImpl::setUDPDataStream(const defs::portPosition port,
const bool enable, Positions pos) {
assertTwoUDPDataInterfaces("set enable/disable UDP ports");
Parallel(&Module::setUDPDataStream, pos, port, enable);
updateRxUDPDatastreamMetadata();
}
void DetectorImpl::updateRxUDPDatastreamMetadata() {
assertTwoUDPDataInterfaces(
"update Disbaled UDP ports metadata in receiver");
std::vector<int> disable;
auto portList = getPortPositionList();
if (portList.size() != 2) {
throw RuntimeError("Invalid port size. Expected 2.");
}
// bottom and left is port 0
auto port0 = Parallel(&Module::getUDPDataStream, {}, portList[0]);
auto port1 = Parallel(&Module::getUDPDataStream, {}, portList[1]);
// if any of them are disabled
if (port0.any(false) || port1.any(false)) {
// for each module: if disabled, push port index
for (size_t i = 0; i != port0.size(); ++i) {
if (!port0[i]) {
disable.push_back(i * 2);
}
if (!port1[i]) {
disable.push_back(i * 2 + 1);
}
}
}
modules[0]->updateRxUDPPortDisableMetadata(disable);
}
std::vector<int> DetectorImpl::getRxDisabledUDPPortIndices() const {
assertTwoUDPDataInterfaces("get Disbaled UDP ports metadata from receiver");
return modules[0]->getRxUDPPortDisableMetadata();
}
std::vector<defs::portPosition> DetectorImpl::getPortPositionList() const {
switch (shm()->detType) {
case defs::JUNGFRAU:
case defs::MOENCH:
return std::vector<defs::portPosition>{defs::BOTTOM, defs::TOP};
case defs::EIGER:
return std::vector<defs::portPosition>{defs::LEFT, defs::RIGHT};
default:
throw RuntimeError("port Position does not exist for this detector");
}
}
std::vector<defs::ROI> DetectorImpl::getRxROI(int module_id) const {
if (shm()->detType == CHIPTESTBOARD ||
shm()->detType == defs::XILINX_CHIPTESTBOARD) {
+14 -5
View File
@@ -3,8 +3,8 @@
#pragma once
#include "CtbConfig.h"
#include "SharedMemory.h"
#include "sls/Result.h"
#include "sls/SharedMemory.h"
#include "sls/ZmqSocket.h"
#include "sls/logger.h"
#include "sls/sls_detector_defs.h"
@@ -278,10 +278,9 @@ class DetectorImpl : public virtual slsDetectorDefs {
void stopDetector(Positions pos);
/**
* Combines data from all readouts and gives it to the gui
* or just gives progress of acquisition by polling receivers
* gives progress of acquisition by polling receivers
*/
void processData(bool receiver);
void printRxProgress();
/**
* Convert raw file
@@ -310,6 +309,15 @@ class DetectorImpl : public virtual slsDetectorDefs {
std::vector<std::pair<std::string, uint16_t>>
verifyUniqueRxHost(const std::vector<std::string> &names) const;
void assertTwoUDPDataInterfaces(const std::string &cmd) const;
Result<bool> getUDPDataStream(const defs::portPosition port,
Positions pos) const;
void setUDPDataStream(const defs::portPosition port, const bool enable,
Positions pos);
void updateRxUDPDatastreamMetadata();
std::vector<int> getRxDisabledUDPPortIndices() const;
std::vector<defs::portPosition> getPortPositionList() const;
defs::xy getPortGeometry() const;
std::vector<defs::ROI> getRxROI(int module_id = -1) const;
void setRxROI(const std::vector<defs::ROI> &args);
@@ -441,7 +449,8 @@ class DetectorImpl : public virtual slsDetectorDefs {
void printProgress(double progress);
void startProcessingThread(bool receiver);
void startRxZmqProcessingThread();
void startRxProgressThread();
/**
* Check if processing thread is ready to join main thread
+71 -13
View File
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "Module.h"
#include "SharedMemory.h"
#include "sls/ClientSocket.h"
#include "sls/SharedMemory.h"
#include "sls/ToString.h"
#include "sls/Version.h"
#include "sls/bit_utils.h"
@@ -1407,6 +1407,76 @@ void Module::setTransmissionDelayRight(int value) {
sendToDetector(F_SET_TRANSMISSION_DELAY_RIGHT, value, nullptr);
}
bool Module::getUDPDataStream(const portPosition port) const {
// receiver only
if (shm()->detType == JUNGFRAU || shm()->detType == MOENCH) {
if (!shm()->useReceiverFlag) {
throw RuntimeError("No receiver to get udp datastream.");
}
return sendToReceiver<int>(F_RECEIVER_GET_UDP_DATASTREAM,
static_cast<int>(port));
} else
return sendToDetector<int>(F_GET_DATASTREAM, static_cast<int>(port));
}
void Module::setUDPDataStream(const portPosition port, const bool enable) {
int args[]{static_cast<int>(port), static_cast<int>(enable)};
if (shm()->detType == JUNGFRAU || shm()->detType == MOENCH) {
if (!shm()->useReceiverFlag) {
throw RuntimeError("No receiver to set udp datastream.");
}
sendToReceiver(F_RECEIVER_SET_UDP_DATASTREAM, args, nullptr);
} else {
sendToDetector(F_SET_DATASTREAM, args, nullptr);
if (shm()->useReceiverFlag) {
sendToReceiver(F_RECEIVER_SET_UDP_DATASTREAM, args, nullptr);
}
}
}
void Module::updateRxUDPPortDisableMetadata(const std::vector<int> &disable) {
if (!shm()->useReceiverFlag) {
return;
}
LOG(logDEBUG) << "Updating UDP port disable metadata in Receiver 0";
auto client = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort);
client.Send(F_RECEIVER_SET_UDP_PORT_DISABLE_META);
client.setFnum(F_RECEIVER_SET_UDP_PORT_DISABLE_META);
auto nports = static_cast<int>(disable.size());
client.Send(nports);
if (nports > 0) {
client.Send(disable);
}
if (client.Receive<int>() == FAIL) {
throw ReceiverError("Receiver " + std::to_string(moduleIndex) +
" returned error: " + client.readErrorMessage());
}
}
std::vector<int> Module::getRxUDPPortDisableMetadata() const {
if (!shm()->useReceiverFlag) {
throw RuntimeError("No receiver to get disabled udp port indices.");
}
LOG(logDEBUG) << "Getting UDP port disable metadata in Receiver 0";
auto client = ReceiverSocket(shm()->rxHostname, shm()->rxTCPPort);
client.Send(F_RECEIVER_GET_UDP_PORT_DISABLE_META);
client.setFnum(F_RECEIVER_GET_UDP_PORT_DISABLE_META);
if (client.Receive<int>() == FAIL) {
throw ReceiverError("Receiver " + std::to_string(moduleIndex) +
" returned error: " + client.readErrorMessage());
}
auto nports = client.Receive<int>();
std::vector<int> retval(nports);
if (nports > 0) {
client.Receive(retval);
}
return retval;
}
// Receiver Config
bool Module::getUseReceiverFlag() const { return shm()->useReceiverFlag; }
@@ -1948,18 +2018,6 @@ void Module::setQuad(const bool enable) {
}
}
bool Module::getDataStream(const portPosition port) const {
return sendToDetector<int>(F_GET_DATASTREAM, static_cast<int>(port));
}
void Module::setDataStream(const portPosition port, const bool enable) {
int args[]{static_cast<int>(port), static_cast<int>(enable)};
sendToDetector(F_SET_DATASTREAM, args, nullptr);
if (shm()->useReceiverFlag) {
sendToReceiver(F_RECEIVER_SET_DATASTREAM, args, nullptr);
}
}
bool Module::getTop() const {
return (static_cast<bool>(sendToDetector<int>(F_GET_TOP)));
}
+5 -3
View File
@@ -1,9 +1,9 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#pragma once
#include "SharedMemory.h"
#include "sls/ClientSocket.h"
#include "sls/Pattern.h"
#include "sls/SharedMemory.h"
#include "sls/StaticVector.h"
#include "sls/bit_utils.h"
#include "sls/logger.h"
@@ -279,6 +279,10 @@ class Module : public virtual slsDetectorDefs {
void setTransmissionDelayLeft(int value);
int getTransmissionDelayRight() const;
void setTransmissionDelayRight(int value);
bool getUDPDataStream(const portPosition port) const;
void setUDPDataStream(const portPosition port, const bool enable);
void updateRxUDPPortDisableMetadata(const std::vector<int> &disable);
std::vector<int> getRxUDPPortDisableMetadata() const;
/**************************************************
* *
@@ -388,8 +392,6 @@ class Module : public virtual slsDetectorDefs {
void pulseChip(int n_pulses = 0);
bool getQuad() const;
void setQuad(const bool enable);
bool getDataStream(const portPosition port) const;
void setDataStream(const portPosition port, const bool enable);
bool getTop() const;
void setTop(bool value);
+16 -16
View File
@@ -694,22 +694,6 @@ int InferAction::dacvalues() {
}
}
int InferAction::datastream() {
if (args.size() == 1) {
return slsDetectorDefs::GET_ACTION;
}
if (args.size() == 2) {
return slsDetectorDefs::PUT_ACTION;
}
else {
throw RuntimeError("Could not infer action: Wrong number of arguments");
}
}
int InferAction::dbitclk() {
if (args.size() == 0) {
@@ -4025,6 +4009,22 @@ int InferAction::udp_cleardst() {
}
}
int InferAction::udp_datastream() {
if (args.size() == 1) {
return slsDetectorDefs::GET_ACTION;
}
if (args.size() == 2) {
return slsDetectorDefs::PUT_ACTION;
}
else {
throw RuntimeError("Could not infer action: Wrong number of arguments");
}
}
int InferAction::udp_dstip() {
if (args.size() == 0) {
+2 -2
View File
@@ -56,7 +56,6 @@ class InferAction {
int daclist();
int dacname();
int dacvalues();
int datastream();
int dbitclk();
int dbitphase();
int dbitpipeline();
@@ -296,6 +295,7 @@ class InferAction {
int txdelay_right();
int type();
int udp_cleardst();
int udp_datastream();
int udp_dstip();
int udp_dstip2();
int udp_dstlist();
@@ -390,7 +390,6 @@ class InferAction {
{"daclist", &InferAction::daclist},
{"dacname", &InferAction::dacname},
{"dacvalues", &InferAction::dacvalues},
{"datastream", &InferAction::datastream},
{"dbitclk", &InferAction::dbitclk},
{"dbitphase", &InferAction::dbitphase},
{"dbitpipeline", &InferAction::dbitpipeline},
@@ -634,6 +633,7 @@ class InferAction {
{"txdelay_right", &InferAction::txdelay_right},
{"type", &InferAction::type},
{"udp_cleardst", &InferAction::udp_cleardst},
{"udp_datastream", &InferAction::udp_datastream},
{"udp_dstip", &InferAction::udp_dstip},
{"udp_dstip2", &InferAction::udp_dstip2},
{"udp_dstlist", &InferAction::udp_dstlist},
+1
View File
@@ -6,6 +6,7 @@ target_sources(tests PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/test-SharedMemory.cpp
${CMAKE_CURRENT_SOURCE_DIR}/acquire/Acquire.cpp
${CMAKE_CURRENT_SOURCE_DIR}/acquire/CTBState.cpp
${CMAKE_CURRENT_SOURCE_DIR}/acquire/ExpectedState.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Caller/test-Caller.cpp
@@ -589,46 +589,6 @@ TEST_CASE("quad", "[.detectorintegration]") {
}
}
TEST_CASE("datastream", "[.detectorintegration]") {
Detector det;
Caller caller(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::EIGER) {
auto prev_val_left = det.getDataStream(defs::LEFT);
auto prev_val_right = det.getDataStream(defs::RIGHT);
// no "left" or "right"
REQUIRE_THROWS(caller.call("datastream", {"1"}, -1, PUT));
{
std::ostringstream oss;
caller.call("datastream", {"left", "0"}, -1, PUT, oss);
REQUIRE(oss.str() == "datastream [left, 0]\n");
}
{
std::ostringstream oss;
caller.call("datastream", {"right", "0"}, -1, PUT, oss);
REQUIRE(oss.str() == "datastream [right, 0]\n");
}
{
std::ostringstream oss;
caller.call("datastream", {"left", "1"}, -1, PUT, oss);
REQUIRE(oss.str() == "datastream [left, 1]\n");
}
{
std::ostringstream oss;
caller.call("datastream", {"right", "1"}, -1, PUT, oss);
REQUIRE(oss.str() == "datastream [right, 1]\n");
}
for (int i = 0; i != det.size(); ++i) {
det.setDataStream(defs::LEFT, prev_val_left[i], {i});
det.setDataStream(defs::RIGHT, prev_val_right[i], {i});
}
} else {
REQUIRE_THROWS(caller.call("datastream", {}, -1, GET));
REQUIRE_THROWS(caller.call("datastream", {"1"}, -1, PUT));
REQUIRE_THROWS(caller.call("datastream", {"left", "1"}, -1, PUT));
}
}
TEST_CASE("top", "[.detectorintegration]") {
Detector det;
Caller caller(&det);
@@ -99,31 +99,4 @@ void test_onchip_dac_caller(defs::dacIndex index, const std::string &dacname,
}
}
std::pair<uint64_t, int>
calculate_ctb_image_size(const acq::CTBState &test_info, bool isXilinxCtb) {
LOG(logDEBUG1) << test_info;
sls::CtbImageInputs inputs{};
inputs.mode = test_info.readout_mode;
inputs.nAnalogSamples = test_info.num_adc_samples;
inputs.adcMask = test_info.adc_enable_10g;
if (!isXilinxCtb && !test_info.ten_giga) {
inputs.adcMask = test_info.adc_enable_1g;
}
inputs.nTransceiverSamples = test_info.num_trans_samples;
inputs.transceiverMask = test_info.transceiver_mask;
inputs.nDigitalSamples = test_info.num_dbit_samples;
inputs.dbitOffset = test_info.dbit_offset;
inputs.dbitReorder = test_info.dbit_reorder;
inputs.dbitList = test_info.dbit_list;
auto out = computeCtbImageSize(inputs);
uint64_t image_size =
out.nAnalogBytes + out.nDigitalBytes + out.nTransceiverBytes;
LOG(logDEBUG1) << "Expected image size: " << image_size;
int npixelx = out.nPixelsX;
LOG(logDEBUG1) << "Expected number of pixels in x: " << npixelx;
return std::make_pair(image_size, npixelx);
}
} // namespace sls
@@ -2,7 +2,7 @@
// Copyright (C) 2021 Contributors to the SLS Detector Package
#pragma once
#include "acquire/CTBState.h"
#include "checks/MasterFileChecks.h"
#include "sls/sls_detector_defs.h"
#include <chrono>
@@ -13,6 +13,8 @@
namespace sls {
namespace acq = sls::test::acquire;
namespace mf = sls::test::master_file;
namespace checks = sls::test::checks;
void test_valid_port_caller(const std::string &command,
const std::vector<std::string> &arguments,
@@ -23,7 +25,42 @@ void test_dac_caller(slsDetectorDefs::dacIndex index,
void test_onchip_dac_caller(slsDetectorDefs::dacIndex index,
const std::string &dacname, int dacvalue);
std::pair<uint64_t, int>
calculate_ctb_image_size(const acq::CTBState &test_info, bool isXilinxCtb);
/**
* Helper function to run an acquisition and check the master file (both binary
* and hdf5) for expected values. The function takes in a lambda that is called
* with the master filechecker object to perform checks on the master file. The
* acquisition is run with default acquisition and file states, but these can be
* modified within the lambda if needed. This version has the master file
* checker object created within the function instead of using a helper
* function, to allow for more flexibility in handling exceptions (especially
* HDF5 exceptions) and logging.
*/
template <typename F>
void test_run_with_master_file_checker(Detector &det, F f) {
auto acq_state = acq::default_acquisition_state();
auto file_state = acq::default_file_state();
std::array<defs::fileFormat, 2> formats = {defs::BINARY, defs::HDF5};
for (const auto &format : formats) {
file_state.file_format = format;
acq::run(det, acq_state, file_state);
std::string fname = acq::get_master_file_name(file_state);
if (format == defs::HDF5) {
#ifdef HDF5C
try {
mf::Checker<mf::H5Context> checker(fname);
f(det, acq_state, file_state, checker);
} catch (H5::Exception &e) {
LOG(logERROR) << "HDF5 error: " << e.getDetailMsg();
throw;
}
#endif
} else {
mf::Checker<mf::JsonContext> checker(fname);
f(det, acq_state, file_state, checker);
}
}
}
} // namespace sls
@@ -1,29 +1,15 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "acquire/ExpectedState.h"
#include "checks/MasterFileChecks.h"
#include "sls/Detector.h"
#include "sls/ToString.h"
#include "sls/logger.h"
#include "test-Caller-global.h"
#include "catch.hpp"
#include <filesystem>
#include <fstream>
#include <rapidjson/document.h>
#include <rapidjson/error/en.h>
#include <sstream>
#include <string>
#ifdef HDF5C
#include "H5Cpp.h"
#endif
namespace sls {
namespace mf = sls::test::master_file;
namespace acq = sls::test::acquire;
namespace checks = sls::test::checks;
TEST_CASE("check_master_file_attributes",
"[.detectorintegration][.disable_check_data_file]") {
@@ -32,10 +18,6 @@ TEST_CASE("check_master_file_attributes",
auto detType = det.getDetectorType().squash(defs::GENERIC);
INFO("Testing master file attributes with " << ToString(detType));
// currently num frame = 1 (default)
auto acq_state = acq::default_acquisition_state();
auto file_state = acq::default_file_state();
// if ctb, set to default and restore after test
std::optional<acq::CTBState> ctb_state = std::nullopt;
if (detType == defs::CHIPTESTBOARD ||
@@ -44,36 +26,75 @@ TEST_CASE("check_master_file_attributes",
}
acq::CTBStateGuard ctb_guard(det, ctb_state);
// binary => /tmp/sls_test_master_0.json
file_state.file_format = defs::BINARY;
acq::run(det, acq_state, file_state);
test_run_with_master_file_checker(
det, [&](auto &det, auto &acq_state, auto &file_state, auto &checker) {
// get expected state of parameters and check against master file
auto expected_state = acq::build_expected_state(
det, acq_state, file_state, ctb_state);
checks::check_metadata(checker, expected_state);
});
}
std::string fname = acq::get_master_file_name(file_state);
mf::Checker<mf::JsonContext> checker(fname);
TEST_CASE("udp_datastream with master file",
"[.detectorintegration][.disable_check_data_file]") {
Detector det;
auto det_type = det.getDetectorType().squash();
if (det_type == defs::EIGER) {
auto prev_val_left = det.getUDPDataStream(defs::LEFT);
auto prev_val_right = det.getUDPDataStream(defs::RIGHT);
// get expected state of parameters and check against master file
acq::ExpectedState expected_state =
acq::build_expected_state(det, acq_state, file_state, ctb_state);
checks::check_metadata(checker, expected_state);
det.setUDPDataStream(defs::LEFT, false);
// check master file
{
// expected
std::vector<defs::portPosition> expected_ports =
det.getPortPositionList();
std::vector<int> expected_disabled_ports =
det.getRxDisabledUDPPortIndices();
REQUIRE(expected_disabled_ports.size() > 0);
#ifdef HDF5C
try {
// hdf5 => /tmp/sls_test_master_0.h5
file_state.file_format = defs::HDF5;
acq::run(det, acq_state, file_state);
test_run_with_master_file_checker(
det, [&](auto &det, auto &acq_state, auto &file_state,
auto &checker) {
checks::check_udp_ports_type(checker, expected_ports);
checks::check_udp_ports_disabled(checker,
expected_disabled_ports);
});
}
std::string fname = acq::get_master_file_name(file_state);
mf::Checker<mf::H5Context> checker(fname);
for (int i = 0; i != det.size(); ++i) {
det.setUDPDataStream(defs::LEFT, prev_val_left[i], {i});
det.setUDPDataStream(defs::RIGHT, prev_val_right[i], {i});
}
} else if ((det_type == defs::JUNGFRAU || det_type == defs::MOENCH) &&
(det.getNumberofUDPInterfaces().squash(0) == 2)) {
auto prev_val_top = det.getUDPDataStream(defs::TOP);
auto prev_val_bottom = det.getUDPDataStream(defs::BOTTOM);
// get expected state of parameters and check against master file
acq::ExpectedState expected_state =
acq::build_expected_state(det, acq_state, file_state, ctb_state);
checks::check_metadata(checker, expected_state);
} catch (H5::Exception &e) {
LOG(logERROR) << "HDF5 error: " << e.getDetailMsg();
throw;
det.setUDPDataStream(defs::TOP, false);
// check master file
{
// expected
std::vector<defs::portPosition> expected_ports =
det.getPortPositionList();
std::vector<int> expected_disabled_ports =
det.getRxDisabledUDPPortIndices();
REQUIRE(expected_disabled_ports.size() > 0);
test_run_with_master_file_checker(
det, [&](auto &det, auto &acq_state, auto &file_state,
auto &checker) {
checks::check_udp_ports_type(checker, expected_ports);
checks::check_udp_ports_disabled(checker,
expected_disabled_ports);
});
}
for (int i = 0; i != det.size(); ++i) {
det.setUDPDataStream(defs::TOP, prev_val_top[i], {i});
det.setUDPDataStream(defs::BOTTOM, prev_val_bottom[i], {i});
}
}
#endif
}
} // namespace sls
@@ -127,7 +127,8 @@ TEST_CASE("rx_framescaught", "[.detectorintegration]") {
}
}
TEST_CASE("rx_missingpackets", "[.detectorintegration]") {
TEST_CASE("rx_missingpackets",
"[.detectorintegration][.disable_check_data_file]") {
Detector det;
Caller caller(&det);
auto prev_val = det.getFileWrite();
@@ -3068,6 +3068,95 @@ TEST_CASE("txdelay", "[.detectorintegration]") {
}
}
TEST_CASE("udp_datastream", "[.detectorintegration]") {
Detector det;
Caller caller(&det);
auto det_type = det.getDetectorType().squash();
if (det_type == defs::EIGER) {
auto prev_val_left = det.getUDPDataStream(defs::LEFT);
auto prev_val_right = det.getUDPDataStream(defs::RIGHT);
// invalid args
REQUIRE_THROWS(caller.call("udp_datastream", {"top", "1"}, -1, PUT));
REQUIRE_THROWS(caller.call("udp_datastream", {"bottom", "1"}, -1, PUT));
// no "left" or "right" argument
REQUIRE_THROWS(caller.call("udp_datastream", {"1"}, -1, PUT));
{
std::ostringstream oss;
caller.call("udp_datastream", {"left", "0"}, -1, PUT, oss);
REQUIRE(oss.str() == "udp_datastream [left, 0]\n");
}
{
std::ostringstream oss;
caller.call("udp_datastream", {"right", "0"}, -1, PUT, oss);
REQUIRE(oss.str() == "udp_datastream [right, 0]\n");
}
{
std::ostringstream oss;
caller.call("udp_datastream", {"left", "1"}, -1, PUT, oss);
REQUIRE(oss.str() == "udp_datastream [left, 1]\n");
}
{
std::ostringstream oss;
caller.call("udp_datastream", {"right", "1"}, -1, PUT, oss);
REQUIRE(oss.str() == "udp_datastream [right, 1]\n");
}
for (int i = 0; i != det.size(); ++i) {
det.setUDPDataStream(defs::LEFT, prev_val_left[i], {i});
det.setUDPDataStream(defs::RIGHT, prev_val_right[i], {i});
}
} else if (det_type == defs::JUNGFRAU || det_type == defs::MOENCH) {
// throw with 1 interface
if (det.getNumberofUDPInterfaces().squash() == 1) {
REQUIRE_THROWS(
caller.call("udp_datastream", {"top", "0"}, -1, PUT));
}
// 2 interfaces
else {
auto prev_val_top = det.getUDPDataStream(defs::TOP);
auto prev_val_bottom = det.getUDPDataStream(defs::BOTTOM);
// invalid args
REQUIRE_THROWS(
caller.call("udp_datastream", {"left", "1"}, -1, PUT));
REQUIRE_THROWS(
caller.call("udp_datastream", {"right", "1"}, -1, PUT));
// no "top" or "bottom" argument
REQUIRE_THROWS(caller.call("udp_datastream", {"1"}, -1, PUT));
{
std::ostringstream oss;
caller.call("udp_datastream", {"top", "0"}, -1, PUT, oss);
REQUIRE(oss.str() == "udp_datastream [top, 0]\n");
}
{
std::ostringstream oss;
caller.call("udp_datastream", {"bottom", "0"}, -1, PUT, oss);
REQUIRE(oss.str() == "udp_datastream [bottom, 0]\n");
}
{
std::ostringstream oss;
caller.call("udp_datastream", {"top", "1"}, -1, PUT, oss);
REQUIRE(oss.str() == "udp_datastream [top, 1]\n");
}
{
std::ostringstream oss;
caller.call("udp_datastream", {"bottom", "1"}, -1, PUT, oss);
REQUIRE(oss.str() == "udp_datastream [bottom, 1]\n");
}
for (int i = 0; i != det.size(); ++i) {
det.setUDPDataStream(defs::TOP, prev_val_top[i], {i});
det.setUDPDataStream(defs::BOTTOM, prev_val_bottom[i], {i});
}
}
} else {
REQUIRE_THROWS(caller.call("udp_datastream", {}, -1, GET));
REQUIRE_THROWS(caller.call("udp_datastream", {"1"}, -1, PUT));
REQUIRE_THROWS(caller.call("udp_datastream", {"left", "1"}, -1, PUT));
REQUIRE_THROWS(caller.call("udp_datastream", {"top", "1"}, -1, PUT));
}
}
/* ZMQ Streaming Parameters (Receiver<->Client) */
TEST_CASE("zmqport", "[.detectorintegration]") {
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: LGPL-3.0-or-other
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "CTBState.h"
#include "GeneralData.h"
namespace sls::test::acquire {
std::pair<uint64_t, int> calculate_ctb_image_size(const CTBState &test_info,
bool isXilinxCtb) {
LOG(logDEBUG1) << test_info;
CtbImageInputs inputs{};
inputs.mode = test_info.readout_mode;
inputs.nAnalogSamples = test_info.num_adc_samples;
inputs.adcMask = test_info.adc_enable_10g;
if (!isXilinxCtb && !test_info.ten_giga) {
inputs.adcMask = test_info.adc_enable_1g;
}
inputs.nTransceiverSamples = test_info.num_trans_samples;
inputs.transceiverMask = test_info.transceiver_mask;
inputs.nDigitalSamples = test_info.num_dbit_samples;
inputs.dbitOffset = test_info.dbit_offset;
inputs.dbitReorder = test_info.dbit_reorder;
inputs.dbitList = test_info.dbit_list;
auto out = computeCtbImageSize(inputs);
uint64_t image_size =
out.nAnalogBytes + out.nDigitalBytes + out.nTransceiverBytes;
LOG(logDEBUG1) << "Expected image size: " << image_size;
int npixelx = out.nPixelsX;
LOG(logDEBUG1) << "Expected number of pixels in x: " << npixelx;
return std::make_pair(image_size, npixelx);
}
} // namespace sls::test::acquire
@@ -135,4 +135,14 @@ class CTBStateGuard {
CTBState saved_;
};
/**
* @brief
* @param test_info current CTB state
* @param isXilinxCtb if the detector type is Xilinx CTB
* @return std::pair<uint64_t, int> pair of image size in bytes and number of
* channels in dimension X (Currently only analog channels)
*/
std::pair<uint64_t, int> calculate_ctb_image_size(const CTBState &test_info,
bool isXilinxCtb);
} // namespace sls::test::acquire
@@ -2,7 +2,6 @@
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "ExpectedState.h"
#include "Caller/test-Caller-global.h"
#include "receiver_defs.h"
// unnamed namespace for internal linkage
@@ -47,7 +46,7 @@ defs::xy get_port_shape(const Detector &det,
"CTB state must be provided to calculate expected port shape");
}
portSize.x =
sls::calculate_ctb_image_size(
acq::calculate_ctb_image_size(
ctb_state.value(), det_type == defs::XILINX_CHIPTESTBOARD)
.second;
portSize.y = 1;
@@ -136,6 +135,14 @@ int get_num_udp_interfaces(const Detector &det) {
"Inconsistent number of UDP interfaces");
}
std::vector<defs::portPosition> get_udp_port_types(const Detector &det) {
return det.getPortPositionList();
}
std::vector<int> get_udp_ports_disabled(const Detector &det) {
return det.getRxDisabledUDPPortIndices();
}
int get_read_n_rows(const Detector &det) {
return det.getReadNRows().tsquash("Inconsistent number of read rows");
}
@@ -184,6 +191,10 @@ acq::JungfrauExpectedState build_jungfrau_specific_state(const Detector &det) {
e.exptime = get_exptime(det);
e.period = get_period(det);
e.num_udp_interfaces = get_num_udp_interfaces(det);
if (e.num_udp_interfaces == 2) {
e.udp_port_types = get_udp_port_types(det);
e.udp_ports_disabled = get_udp_ports_disabled(det);
}
e.read_n_rows = get_read_n_rows(det);
e.readout_speed = get_readout_speed(det);
return e;
@@ -195,6 +206,10 @@ acq::MoenchExpectedState build_moench_specific_state(const Detector &det) {
e.exptime = get_exptime(det);
e.period = get_period(det);
e.num_udp_interfaces = get_num_udp_interfaces(det);
if (e.num_udp_interfaces == 2) {
e.udp_port_types = get_udp_port_types(det);
e.udp_ports_disabled = get_udp_ports_disabled(det);
}
e.read_n_rows = get_read_n_rows(det);
e.readout_speed = get_readout_speed(det);
return e;
@@ -213,6 +228,8 @@ acq::EigerExpectedState build_eiger_specific_state(const Detector &det) {
e.sub_exptime = sub_exptime;
e.sub_period = sub_period;
e.quad = det.getQuad().tsquash("Inconsistent quad setting");
e.udp_port_types = get_udp_port_types(det);
e.udp_ports_disabled = get_udp_ports_disabled(det);
e.read_n_rows = get_read_n_rows(det);
{
for (auto item : det.getRateCorrection())
@@ -342,7 +359,7 @@ int get_expected_image_size(const Detector &det,
}
LOG(logINFORED) << ctb_state.value();
image_size =
sls::calculate_ctb_image_size(
acq::calculate_ctb_image_size(
ctb_state.value(), (det_type == defs::XILINX_CHIPTESTBOARD))
.first;
break;
@@ -31,6 +31,8 @@ struct JungfrauExpectedState {
ns exptime{};
ns period{};
int num_udp_interfaces{};
std::vector<defs::portPosition> udp_port_types;
std::vector<int> udp_ports_disabled;
int read_n_rows{};
defs::speedLevel readout_speed{};
};
@@ -40,6 +42,8 @@ struct MoenchExpectedState {
ns exptime{};
ns period{};
int num_udp_interfaces{};
std::vector<defs::portPosition> udp_port_types;
std::vector<int> udp_ports_disabled;
int read_n_rows{};
defs::speedLevel readout_speed{};
};
@@ -54,6 +58,8 @@ struct EigerExpectedState {
ns sub_exptime{};
ns sub_period{};
bool quad{};
std::vector<defs::portPosition> udp_port_types;
std::vector<int> udp_ports_disabled;
int read_n_rows{};
std::vector<int64_t> rate_corrections{};
defs::speedLevel readout_speed{};
@@ -128,6 +128,22 @@ void check_num_udp_interfaces(CheckerT &checker, const int &value) {
value);
}
template <typename CheckerT>
void check_udp_ports_type(CheckerT &checker,
const std::vector<defs::portPosition> &value) {
REQUIRE(value.size() == 2);
std::vector<std::string> ports = {ToString(value[0]), ToString(value[1])};
checker.template check<std::vector<std::string>>(
MasterAttributes::N_UDP_PORTS_TYPE.data(), ports);
}
template <typename CheckerT>
void check_udp_ports_disabled(CheckerT &checker,
const std::vector<int> &value) {
checker.template check<std::vector<int>>(
MasterAttributes::N_UDP_PORTS_DISABLED.data(), value);
}
template <typename CheckerT>
void check_read_n_rows(CheckerT &checker, const int &value) {
checker.template check<int>(MasterAttributes::N_NUMBER_OF_ROWS.data(),
@@ -318,6 +334,10 @@ void check_jungfrau_metadata(CheckerT &checker,
check_exptime(checker, st.exptime);
check_period(checker, st.period);
check_num_udp_interfaces(checker, st.num_udp_interfaces);
if (st.num_udp_interfaces == 2) {
check_udp_ports_type(checker, st.udp_port_types);
check_udp_ports_disabled(checker, st.udp_ports_disabled);
}
check_read_n_rows(checker, st.read_n_rows);
check_readout_speed(checker, st.readout_speed);
}
@@ -331,6 +351,10 @@ void check_moench_metadata(CheckerT &checker,
check_exptime(checker, st.exptime);
check_period(checker, st.period);
check_num_udp_interfaces(checker, st.num_udp_interfaces);
if (st.num_udp_interfaces == 2) {
check_udp_ports_type(checker, st.udp_port_types);
check_udp_ports_disabled(checker, st.udp_ports_disabled);
}
check_read_n_rows(checker, st.read_n_rows);
check_readout_speed(checker, st.readout_speed);
}
@@ -349,6 +373,8 @@ void check_eiger_metadata(CheckerT &checker,
check_sub_exptime(checker, st.sub_exptime);
check_sub_period(checker, st.sub_period);
check_quad(checker, st.quad);
check_udp_ports_type(checker, st.udp_port_types);
check_udp_ports_disabled(checker, st.udp_ports_disabled);
check_read_n_rows(checker, st.read_n_rows);
check_rate_corrections(checker, st.rate_corrections);
check_readout_speed(checker, st.readout_speed);
@@ -238,6 +238,23 @@ template <> struct Reader<H5Context, std::array<ns, 3UL>> {
}
};
template <> struct Reader<H5Context, std::vector<int>> {
static std::vector<int> read(const H5Context &ctx, const std::string &name,
AccessType access) {
if (access == AccessType::Attribute) {
throw RuntimeError("'std::vector<int>' attribute access not "
"supported for HDF5");
}
require_dataset(ctx, name);
auto ds = ctx.file.openDataSet(HDF5_GROUP + name);
auto len = get_1d_size(ds);
std::vector<int> out{};
out.resize(len);
ds.read(out.data(), H5::PredType::NATIVE_INT);
return out;
}
};
template <> struct Reader<H5Context, std::vector<int64_t>> {
static std::vector<int64_t>
read(const H5Context &ctx, const std::string &name, AccessType access) {
@@ -255,6 +272,29 @@ template <> struct Reader<H5Context, std::vector<int64_t>> {
}
};
template <> struct Reader<H5Context, std::vector<std::string>> {
static std::vector<std::string>
read(const H5Context &ctx, const std::string &name, AccessType access) {
if (access == AccessType::Attribute) {
throw RuntimeError(
"'std::vector<std::string>' attribute access not "
"supported for HDF5");
}
require_dataset(ctx, name);
auto ds = ctx.file.openDataSet(HDF5_GROUP + name);
H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
std::vector<const char *> raw;
raw.resize(get_1d_size(ds));
ds.read(raw.data(), strType);
std::vector<std::string> out;
out.reserve(raw.size());
for (auto c : raw) {
out.emplace_back(c);
}
return out;
}
};
template <> struct Reader<H5Context, std::map<std::string, std::string>> {
static std::map<std::string, std::string>
read(const H5Context &ctx, const std::string &name, AccessType access) {
@@ -120,6 +120,17 @@ template <> struct Reader<JsonContext, std::array<ns, 3UL>> {
}
};
template <> struct Reader<JsonContext, std::vector<int>> {
static std::vector<int> read(const JsonContext &ctx,
const std::string &name, AccessType access) {
std::vector<int> out{};
for (const auto &item : ctx.doc[name.c_str()].GetArray()) {
out.push_back(item.GetInt());
}
return out;
}
};
template <> struct Reader<JsonContext, std::vector<int64_t>> {
static std::vector<int64_t>
read(const JsonContext &ctx, const std::string &name, AccessType access) {
@@ -131,6 +142,17 @@ template <> struct Reader<JsonContext, std::vector<int64_t>> {
}
};
template <> struct Reader<JsonContext, std::vector<std::string>> {
static std::vector<std::string>
read(const JsonContext &ctx, const std::string &name, AccessType access) {
std::vector<std::string> out{};
for (const auto &item : ctx.doc[name.c_str()].GetArray()) {
out.push_back(item.GetString());
}
return out;
}
};
template <> struct Reader<JsonContext, std::map<std::string, std::string>> {
static std::map<std::string, std::string>
read(const JsonContext &ctx, const std::string &name, AccessType access) {
+1 -1
View File
@@ -4,7 +4,7 @@
#include <stdlib.h>
#include "CtbConfig.h"
#include "SharedMemory.h"
#include "sls/SharedMemory.h"
#include <fstream>
#include <set>
+1 -1
View File
@@ -2,8 +2,8 @@
// Copyright (C) 2021 Contributors to the SLS Detector Package
#include "Detector.h"
#include "Module.h"
#include "SharedMemory.h"
#include "catch.hpp"
#include "sls/SharedMemory.h"
namespace sls {
@@ -2,8 +2,8 @@
// Copyright (C) 2021 Contributors to the SLS Detector Package
#define DISABLE_STATIC_ASSERT // to be able to test obsolete shm without isValid
#include "SharedMemory.h"
#include "catch.hpp"
#include "sls/SharedMemory.h"
#include "sls/string_utils.h"
#include <filesystem>
+93 -13
View File
@@ -207,7 +207,7 @@ int ClientInterface::functionTable(){
flist[F_GET_RECEIVER_STREAMING_HWM] = &ClientInterface::get_streaming_hwm;
flist[F_SET_RECEIVER_STREAMING_HWM] = &ClientInterface::set_streaming_hwm;
flist[F_RECEIVER_SET_ALL_THRESHOLD] = &ClientInterface::set_all_threshold;
flist[F_RECEIVER_SET_DATASTREAM] = &ClientInterface::set_detector_datastream;
flist[F_RECEIVER_SET_UDP_DATASTREAM] = &ClientInterface::set_port_udp_datastream;
flist[F_GET_RECEIVER_ARPING] = &ClientInterface::get_arping;
flist[F_SET_RECEIVER_ARPING] = &ClientInterface::set_arping;
flist[F_RECEIVER_GET_RECEIVER_ROI] = &ClientInterface::get_receiver_roi;
@@ -221,6 +221,10 @@ int ClientInterface::functionTable(){
flist[F_SET_RECEIVER_DBIT_REORDER] = &ClientInterface::set_dbit_reorder;
flist[F_RECEIVER_GET_ROI_METADATA] = &ClientInterface::get_roi_metadata;
flist[F_SET_RECEIVER_READOUT_SPEED] = &ClientInterface::set_readout_speed;
flist[F_RECEIVER_GET_UDP_DATASTREAM] = &ClientInterface::get_port_udp_datastream;
flist[F_RECEIVER_SET_UDP_PORT_DISABLE_META] = &ClientInterface::set_udp_port_disable_meta;
flist[F_RECEIVER_GET_UDP_PORT_DISABLE_META] = &ClientInterface::get_udp_port_disable_meta;
for (int i = NUM_DET_FUNCTIONS + 1; i < NUM_REC_FUNCTIONS ; i++) {
LOG(logDEBUG1) << "function fnum: " << i << " (" <<
@@ -383,8 +387,8 @@ int ClientInterface::setup_receiver(Interface &socket) {
impl()->setSubPeriod(std::chrono::nanoseconds(arg.subExpTimeNs) +
std::chrono::nanoseconds(arg.subDeadTimeNs));
impl()->setActivate(static_cast<bool>(arg.activate));
impl()->setDetectorDataStream(LEFT, arg.dataStreamLeft);
impl()->setDetectorDataStream(RIGHT, arg.dataStreamRight);
impl()->setUDPDataStream(LEFT, arg.dataStreamLeft);
impl()->setUDPDataStream(RIGHT, arg.dataStreamRight);
impl()->setQuad(arg.quad == 0 ? false : true);
impl()->setThresholdEnergy(arg.thresholdEnergyeV[0]);
}
@@ -398,7 +402,7 @@ int ClientInterface::setup_receiver(Interface &socket) {
}
impl()->setThresholdEnergy(val);
}
if (detType == EIGER || detType == MYTHEN3) {
if (detType == EIGER || detType == MYTHEN3 || detType == MATTERHORN) {
impl()->setDynamicRange(arg.dynamicRange);
}
impl()->setTimingMode(arg.timMode);
@@ -429,6 +433,10 @@ int ClientInterface::setup_receiver(Interface &socket) {
impl()->setGateDelay3(std::chrono::nanoseconds(arg.gateDelay3Ns));
impl()->setNumberOfGates(arg.gates);
}
if (detType == MATTERHORN) {
impl()->setCounterMask(arg.countermask);
}
LOG(logDEBUG) << "set counter mask to " << arg.countermask;
if (detType == GOTTHARD2) {
impl()->setBurstMode(arg.burstType);
}
@@ -450,6 +458,7 @@ void ClientInterface::setDetectorType(detectorType arg) {
case MOENCH:
case MYTHEN3:
case GOTTHARD2:
case MATTERHORN:
break;
default:
throw RuntimeError("Unknown detector type: " + std::to_string(arg));
@@ -666,12 +675,21 @@ int ClientInterface::set_dynamic_range(Interface &socket) {
break;
*/
case 4:
if (detType == MATTERHORN || detType == EIGER) {
exists = true;
}
break;
case 12:
if (detType == EIGER) {
exists = true;
}
break;
case 8:
if (detType == MATTERHORN || detType == EIGER ||
detType == MYTHEN3) {
exists = true;
}
break;
case 32:
if (detType == EIGER || detType == MYTHEN3) {
exists = true;
@@ -1653,27 +1671,58 @@ int ClientInterface::set_all_threshold(Interface &socket) {
return socket.Send(OK);
}
int ClientInterface::set_detector_datastream(Interface &socket) {
int args[2]{-1, -1};
socket.Receive(args);
portPosition port = static_cast<portPosition>(args[0]);
void ClientInterface::validate_port_position(const portPosition port) {
bool exists = false;
switch (port) {
case LEFT:
case RIGHT:
if (detType == EIGER) {
exists = true;
}
break;
case TOP:
case BOTTOM:
if (detType == JUNGFRAU || detType == MOENCH) {
exists = true;
}
break;
default:
throw RuntimeError("Invalid port type");
}
bool enable = static_cast<int>(args[1]);
LOG(logDEBUG1) << "Setting datastream (" << ToString(port) << ") to "
<< ToString(enable);
if (detType != EIGER)
if (!exists) {
modeNotImplemented("Port type", port);
}
}
int ClientInterface::set_port_udp_datastream(Interface &socket) {
if (detType != EIGER && detType != JUNGFRAU && detType != MOENCH)
functionNotImplemented();
int args[2]{-1, -1};
socket.Receive(args);
portPosition port = static_cast<portPosition>(args[0]);
validate_port_position(port);
bool enable = static_cast<bool>(args[1]);
if (impl()->getNumberofUDPInterfaces() == 1)
throw RuntimeError("Cannot change UDP port datastream with only 1 "
"interface enabled. Hint: set 'numinterfaces' to 2");
LOG(logDEBUG1) << "Setting udp datastream (" << ToString(port) << ") to "
<< ToString(enable);
verifyIdle(socket);
impl()->setDetectorDataStream(port, enable);
impl()->setUDPDataStream(port, enable);
return socket.Send(OK);
}
int ClientInterface::get_port_udp_datastream(Interface &socket) {
auto arg = socket.Receive<int>();
portPosition port = static_cast<portPosition>(arg);
validate_port_position(port);
LOG(logDEBUG1) << "Getting udp datastream (" << ToString(port) << ")";
if (detType != EIGER && detType != JUNGFRAU && detType != MOENCH)
functionNotImplemented();
auto retval = static_cast<int>(impl()->getUDPDataStream(port));
return socket.sendResult(retval);
}
int ClientInterface::get_arping(Interface &socket) {
auto retval = static_cast<int>(impl()->getArping());
LOG(logDEBUG1) << "arping thread status:" << retval;
@@ -1884,4 +1933,35 @@ int ClientInterface::set_readout_speed(Interface &socket) {
return socket.Send(OK);
}
int ClientInterface::set_udp_port_disable_meta(Interface &socket) {
auto nports = socket.Receive<int>();
std::vector<int> portsDisabled;
if (nports > 0) {
portsDisabled.resize(nports);
socket.Receive(portsDisabled);
LOG(logDEBUG1) << "Disabled Ports Metadata:" << ToString(portsDisabled);
}
verifyIdle(socket);
try {
impl()->setUDPPortsDisabledMetadata(portsDisabled);
} catch (const std::exception &e) {
throw RuntimeError("Could not update UDP ports disabled metadata [" +
std::string(e.what()) + ']');
}
return socket.Send(OK);
}
int ClientInterface::get_udp_port_disable_meta(Interface &socket) {
auto retvals = impl()->getUDPPortsDisabledMetadata();
LOG(logDEBUG1) << "Receiver disabled udp ports retval:"
<< ToString(retvals);
socket.Send(OK);
auto size = static_cast<int>(retvals.size());
socket.Send(size);
if (size > 0)
socket.Send(retvals);
return OK;
}
} // namespace sls

Some files were not shown because too many files have changed in this diff Show More