diff --git a/.gitea/workflows/docker-rh8-build-test.yml b/.gitea/workflows/docker-rh8-build-test.yml
index 3d070f71e..9bb09949b 100644
--- a/.gitea/workflows/docker-rh8-build-test.yml
+++ b/.gitea/workflows/docker-rh8-build-test.yml
@@ -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
diff --git a/.gitea/workflows/docker-rh9-build-test.yml b/.gitea/workflows/docker-rh9-build-test.yml
index 2783d69ec..7d2fbb400 100644
--- a/.gitea/workflows/docker-rh9-build-test.yml
+++ b/.gitea/workflows/docker-rh9-build-test.yml
@@ -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
diff --git a/.github/workflows/build_documentation.yml b/.github/workflows/build_documentation.yml
index ebbce9606..458084c0a 100644
--- a/.github/workflows/build_documentation.yml
+++ b/.github/workflows/build_documentation.yml
@@ -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
diff --git a/.github/workflows/cmake.yaml b/.github/workflows/cmake.yaml
index 1c4d7b0f6..b90214279 100644
--- a/.github/workflows/cmake.yaml
+++ b/.github/workflows/cmake.yaml
@@ -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
diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml
index 96d04205e..f5ee18c63 100644
--- a/.github/workflows/run_tests.yaml
+++ b/.github/workflows/run_tests.yaml
@@ -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
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8830eb86e..5ea532cee 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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)
diff --git a/RELEASE.md b/RELEASE.md
index c67a67438..11563b7af 100644
--- a/RELEASE.md
+++ b/RELEASE.md
@@ -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
==========================================
diff --git a/etc/generate_registerdefs.py b/etc/generate_registerdefs.py
index 91178b0e3..aaaaa9720 100644
--- a/etc/generate_registerdefs.py
+++ b/etc/generate_registerdefs.py
@@ -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
"""
diff --git a/etc/updateAPIVersion.py b/etc/updateAPIVersion.py
index 84b160384..74c1289ec 100644
--- a/etc/updateAPIVersion.py
+++ b/etc/updateAPIVersion.py
@@ -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)
diff --git a/pyctbgui/pyctbgui/services/Acquisition.py b/pyctbgui/pyctbgui/services/Acquisition.py
index b32b8a659..dcf06707d 100644
--- a/pyctbgui/pyctbgui/services/Acquisition.py
+++ b/pyctbgui/pyctbgui/services/Acquisition.py
@@ -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)
diff --git a/pyctbgui/pyctbgui/services/Plot.py b/pyctbgui/pyctbgui/services/Plot.py
index a14ef1a0a..688265484 100644
--- a/pyctbgui/pyctbgui/services/Plot.py
+++ b/pyctbgui/pyctbgui/services/Plot.py
@@ -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', "Transceiver Sample [#]")
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}'
diff --git a/pyctbgui/pyctbgui/services/Transceiver.py b/pyctbgui/pyctbgui/services/Transceiver.py
index 13bb72fca..828decd26 100644
--- a/pyctbgui/pyctbgui/services/Transceiver.py
+++ b/pyctbgui/pyctbgui/services/Transceiver.py
@@ -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
diff --git a/pyctbgui/pyctbgui/ui/plot.ui b/pyctbgui/pyctbgui/ui/plot.ui
index 848d48f16..01d2b5ec1 100644
--- a/pyctbgui/pyctbgui/ui/plot.ui
+++ b/pyctbgui/pyctbgui/ui/plot.ui
@@ -6,8 +6,8 @@
0
0
- 860
- 800
+ 1937
+ 1104
@@ -25,7 +25,7 @@
10
50
841
- 141
+ 161
@@ -35,13 +35,10 @@
QFrame::Raised
- -
-
-
- true
-
+
-
+
- Type:
+ Options:
@@ -50,6 +47,12 @@
false
+
+
+ 0
+ 31
+
+
Distribution
@@ -58,28 +61,193 @@
- -
-
-
- Qt::Vertical
+
-
+
+
+ true
-
+
- 20
- 40
+ 0
+ 31
-
+
+ Waveform
+
+
+ true
+
+
+ buttonGroup
+
+
+
+ -
+
+
+
+ 0
+ 31
+
+
+
+ Counter 2
+
+
+
+ -
+
+
+
+ 0
+ 31
+
+
+
+ Counter 3
+
+
+
+ -
+
+
+ true
+
+
+
+ 0
+ 31
+
+
+
+ No Plot
+
+
+ false
+
+
+ buttonGroup
+
+
+
+ -
+
+
+
+ 0
+ 31
+
+
+
+ Counter 0
+
+
+
+ -
+
+
+ true
+
+
+
+ 0
+ 31
+
+
+
+ <html><head/><body><p>If set to high readout, zmq HWM is set to 2 and buffer size to 1MB to drop zmq packets to catch up.</p><p>If set to low readout (default), zmq HWM is set to zmq default (1000) and buffer size to os default to not drop any zmq packets.</p></body></html>
+
+
-
+
+ Low - drop no zmq packet
+
+
+ -
+
+ High - drop zmq packets to catch up
+
+
+
+
+ -
+
+
+ true
+
+
+
+ 0
+ 31
+
+
+
+ Type:
+
+
+
+ -
+
+
+
+ 150
+ 16777215
+
+
+
+ <html><head/><body><p>If set to high readout, zmq HWM is set to 2 and buffer size to 1MB to drop zmq packets to catch up.</p><p>If set to low readout (default), zmq HWM is set to zmq default (1000) and buffer size to os default to not drop any zmq packets.</p></body></html>
+
+
+ Readout speed:
+
+
+
+ -
+
+
+ true
+
+
+
+ 0
+ 31
+
+
+
+ Image
+
+
+ buttonGroup
+
+
-
+
+
+ 16777215
+ 31
+
+
- 2
+ 0
+
+ 0
+
+
+ 0
+
-
+
+
+ 0
+ 31
+
+
Digital plot:
@@ -87,6 +255,12 @@
-
+
+
+ 0
+ 31
+
+
Overlay
@@ -110,6 +284,12 @@
-
+
+
+ 0
+ 31
+
+
show legend
@@ -120,6 +300,12 @@
-
+
+
+ 0
+ 31
+
+
Stripe
@@ -149,6 +335,42 @@
+
+ 0
+
+
+ 0
+
+
-
+
+
+ Decoder:
+
+
+
+ -
+
+
+ Color Map:
+
+
+
+ -
+
+
+ true
+
+
+
+ 120
+ 31
+
+
+
+ Color map of the image
+
+
+
-
@@ -202,6 +424,11 @@
Moench04
+
+ -
+
+ Moench05
+
-
@@ -210,35 +437,6 @@
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- true
-
-
-
- 120
- 31
-
-
-
- Color map of the image
-
-
-
-
@@ -255,292 +453,661 @@
- -
-
-
- Decoder:
+
-
+
+
+ Qt::Horizontal
-
-
- -
-
-
- Color Map:
+
+
+ 40
+ 20
+
-
+
- -
-
+
-
+
+
+
+ 0
+ 31
+
+
- Options:
+ Counter 1
- -
-
-
- true
-
-
- No Plot
-
-
- false
-
-
- buttonGroup
-
-
-
- -
-
-
- true
-
+
-
+
0
31
-
- <html><head/><body><p>If set to high readout, zmq HWM is set to 2 and buffer size to 1MB to drop zmq packets to catch up.</p><p>If set to low readout (default), zmq HWM is set to zmq default (1000) and buffer size to os default to not drop any zmq packets.</p></body></html>
-
-
-
-
- Low - drop no zmq packet
-
-
- -
-
- High - drop zmq packets to catch up
-
-
-
-
- -
-
<html><head/><body><p>If set to high readout, zmq HWM is set to 2 and buffer size to 1MB to drop zmq packets to catch up.</p><p>If set to low readout (default), zmq HWM is set to zmq default (1000) and buffer size to os default to not drop any zmq packets.</p></body></html>
- Readout speed:
+ Counters:
- -
-
-
- true
-
-
- Image
-
-
- buttonGroup
-
-
-
- -
-
-
- true
-
-
- Waveform
-
-
- true
-
-
- buttonGroup
-
-
-
- 0
- 480
+ 10
+ 440
841
- 311
+ 400
+
+
+ 0
+ 400
+
+
+
+
+ 16777215
+ 16777215
+
+
QFrame::StyledPanel
QFrame::Sunken
-
- -
-
+
+
-
+
false
+
+
+ 0
+ 31
+
+
+
+
+ 16777215
+ 16777215
+
+
Fit Panel ADC:
- -
-
+
-
+
false
-
- Max:
+
+
+ 0
+ 31
+
-
-
- -
-
-
- false
+
+
+ 16777215
+ 16777215
+
-
- Values
-
-
-
- -
-
-
- false
+
+ QFrame::NoFrame
Dynamic Range:
- -
-
+
-
+
false
+
+
+ 0
+ 31
+
+
+
+
+ 16777215
+ 16777215
+
+
- Y:
+ Serial Offset:
- -
-
+
-
+
false
+
+
+ 100
+ 31
+
+
+
+
+ 45
+ 16777215
+
+
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
- -
-
+
-
+
false
+
+
+ 100
+ 31
+
+
+
+
+ 16777215
+ 16777215
+
+
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
- -
-
+
-
+
false
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
Min:
- -
-
+
-
+
false
+
+
+ 245
+ 31
+
+
+
+
+ 200
+ 16777215
+
+
- X:
+ Pedestal Subtract
- -
+
-
false
+
+
+ 100
+ 31
+
+
+
+
+ 45
+ 16777215
+
+
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
- -
-
+
-
+
false
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ 0
+ 31
+
+
+
+ Raw Data
- -
-
+
-
+
false
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+ 0
+ 31
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Min:
- -
+
-
+
+
+ false
+
+
+
+ 0
+ 31
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ N Counters:
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Max:
+
+
+
+ -
false
+
+
+ 100
+ 31
+
+
+
+
+ 45
+ 16777215
+
+
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
- -
-
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
false
+
+
+ 100
+ 31
+
+
+
+
+ 45
+ 16777215
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 31
+
+
Image Pixels:
- -
+
-
+
+
+ false
+
+
+
+ 100
+ 31
+
+
+
+
+ 45
+ 16777215
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 31
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ X:
+
+
+
+ -
false
+
+
+ 100
+ 31
+
+
+
+
+ 16777215
+ 16777215
+
+
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
- -
+
-
+
+
+ false
+
+
+
+ 100
+ 31
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ false
+
+
+
+ 100
+ 31
+
+
+
+
+ 45
+ 16777215
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
false
+
+
+ 100
+ 31
+
+
+
+
+ 45
+ 16777215
+
+
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
- -
-
+
-
+
+
+ false
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 16777215
+
+
+
+ Plot Bit:
+
+
+
+ -
+
+
+ false
+
+
+
+ 115
+ 0
+
+
+
+
+ 156
+ 16777215
+
+
+
+ Max:
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 31
+
+
+
+
+ 16777215
+ 31
+
+
+
+ Y:
+
+
+
+ -
+
+
+ false
+
+
+
+ 100
+ 31
+
+
+
+
+ 45
+ 16777215
+
+
+
+ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
+
+
+
+ -
+
+
+ false
+
+
+ X
+
+
+
+ -
+
false
@@ -549,77 +1116,17 @@
- -
-
+
-
+
false
- Min:
+ Values
- -
-
-
- false
-
-
- X
-
-
-
- -
-
-
- false
-
-
- Max:
-
-
-
- -
-
-
- false
-
-
- Serial offset:
-
-
-
- -
-
-
- false
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
- false
-
-
- Plot Bit:
-
-
-
- -
-
-
- false
-
-
- Raw Data
-
-
-
- -
+
-
@@ -635,241 +1142,223 @@
- -
-
-
- false
-
-
- Pedestal Subtract
-
-
-
- -
-
-
- false
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
- false
-
-
- Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
-
-
-
- -
-
-
- false
-
-
- N Counters:
-
-
-
10
- 219
+ 221
841
- 81
+ 91
+
+
+ 0
+ 0
+
+
QFrame::StyledPanel
QFrame::Raised
-
-
-
- 0
- -7
- 841
- 111
-
+
+
+ 9
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- Color Range:
-
-
-
- -
-
-
-
-
- 0
- 0
- 121
- 91
-
-
-
-
-
-
-
-
- 16777215
- 15
-
-
-
- All
-
-
- true
-
-
- buttonGroup_4
-
-
-
- -
-
-
- true
-
-
-
- 16777215
- 15
-
-
-
- 3-97%
-
-
- buttonGroup_4
-
-
-
- -
-
-
-
- 16777215
- 15
-
-
-
- Fixed
-
-
- buttonGroup_4
-
-
-
-
-
-
-
- -
-
-
-
- 50
- 16777215
-
-
-
- min:
-
-
-
- -
-
-
- false
-
-
- -1000.000000000000000
-
-
- 100000.000000000000000
-
-
-
- -
-
-
-
- 50
- 16777215
-
-
-
- max:
-
-
-
- -
-
-
- false
-
-
- 100000.000000000000000
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 40
- 20
-
-
-
-
-
-
+
+ 9
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Plain
+
+
+
-
+
+
+ true
+
+
+
+ 16777215
+ 15
+
+
+
+ 3-97%
+
+
+ buttonGroup_4
+
+
+
+ -
+
+
+
+ 16777215
+ 15
+
+
+
+ All
+
+
+ true
+
+
+ buttonGroup_4
+
+
+
+ -
+
+
+
+ 16777215
+ 15
+
+
+
+ Fixed
+
+
+ buttonGroup_4
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 31
+
+
+
+
+ 16777215
+ 31
+
+
+
+ Color Range:
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 31
+
+
+
+ -1000.000000000000000
+
+
+ 100000.000000000000000
+
+
+
+ -
+
+
+
+ 50
+ 31
+
+
+
+ max:
+
+
+
+ -
+
+
+ false
+
+
+
+ 0
+ 31
+
+
+
+ 100000.000000000000000
+
+
+
+ -
+
+
+
+ 50
+ 31
+
+
+
+ min:
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 40
+ 20
+
+
+
+
+
10
- 330
+ 322
841
- 101
+ 108
@@ -878,208 +1367,183 @@
QFrame::Raised
-
-
-
- 0
- 0
- 841
- 51
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- true
-
-
- Pedestal:
-
-
-
- -
-
-
-
- 250
- 0
-
-
-
- recorded frames: 0
-
-
-
- -
-
-
- true
-
-
- Record
-
-
- true
-
-
- false
-
-
- buttonGroup_3
-
-
-
- -
-
-
- true
-
-
- Apply
-
-
- true
-
-
- buttonGroup_3
-
-
-
- -
-
-
- true
-
-
-
- 0
- 31
-
-
-
- background-color: rgb(199, 213, 207);
-
-
- Reset
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Fixed
-
-
-
- 40
- 20
-
-
-
-
-
-
-
-
-
- -1
- 49
- 841
- 51
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
-
- 120
- 31
-
-
-
- background-color: rgb(199, 213, 207);
-
-
- Load Pedestal
-
-
-
- -
-
-
-
- 120
- 31
-
-
-
- background-color: rgb(199, 213, 207);
-
-
- Save Pedestal
-
-
-
- -
-
-
- Qt::Horizontal
-
-
- QSizePolicy::Preferred
-
-
-
- 40
- 20
-
-
-
-
-
-
+
+ -
+
+
+ true
+
+
+
+ 16777215
+ 31
+
+
+
+ Apply
+
+
+ true
+
+
+ buttonGroup_3
+
+
+
+ -
+
+
+ true
+
+
+
+ 16777215
+ 31
+
+
+
+ Record
+
+
+ true
+
+
+ false
+
+
+ buttonGroup_3
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16777215
+ 31
+
+
+
+ recorded frames: 0
+
+
+
+ -
+
+
+ true
+
+
+
+ 0
+ 31
+
+
+
+ background-color: rgb(199, 213, 207);
+
+
+ Reset
+
+
+
+ -
+
+
+ true
+
+
+
+ 150
+ 0
+
+
+
+
+ 16777215
+ 31
+
+
+
+ Pedestal:
+
+
+
+ -
+
+
+ QFrame::NoFrame
+
+
+ QFrame::Raised
+
+
+
-
+
+
+
+ 120
+ 31
+
+
+
+ background-color: rgb(199, 213, 207);
+
+
+ Save Pedestal
+
+
+
+ -
+
+
+
+ 120
+ 31
+
+
+
+ background-color: rgb(199, 213, 207);
+
+
+ Load Pedestal
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QSizePolicy::Fixed
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
-
diff --git a/pyctbgui/pyctbgui/utils/defines.py b/pyctbgui/pyctbgui/utils/defines.py
index 2226b5c33..b4ecfc04e 100644
--- a/pyctbgui/pyctbgui/utils/defines.py
+++ b/pyctbgui/pyctbgui/utils/defines.py
@@ -35,8 +35,8 @@ class Defines:
loops_count = 6
class transceiver:
- count = 4
- tabIndex = 4
+ maxcount = 4
+ maxtabIndex = 4
class slowAdc:
tabIndex = 2
diff --git a/python/slsdet/detector.py b/python/slsdet/detector.py
index a984fedd5..211ddc602 100755
--- a/python/slsdet/detector.py
+++ b/python/slsdet/detector.py
@@ -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
+ {: False, : True}
+
+ Multiple detectors with identical states:
+
+ >>> d.udp_datastream
+ {: False, : True}
+
+ Multiple detectors with different states:
+
+ >>> d.udp_datastream
+ {: [False, True], : [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
diff --git a/python/src/detector.cpp b/python/src/detector.cpp
index 482976edc..647b77974 100644
--- a/python/src/detector.cpp
+++ b/python/src/detector.cpp
@@ -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(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(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(Detector::*)() const) &
+ Detector::getRxDisabledUDPPortIndices);
+ CppDetectorApi.def("getPortPositionList",
+ (std::vector(Detector::*)() const) &
+ Detector::getPortPositionList);
CppDetectorApi.def("getUseReceiverFlag",
(Result(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(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(Detector::*)(sls::Positions) const) &
Detector::getTop,
diff --git a/python/tests/test_det_api.py b/python/tests/test_det_api.py
index 5c8cef0bc..cdda5b02c 100644
--- a/python/tests/test_det_api.py
+++ b/python/tests/test_det_api.py
@@ -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
\ No newline at end of file
+ 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")
diff --git a/slsDetectorCalibration/moenchExecutables/CMakeLists.txt b/slsDetectorCalibration/moenchExecutables/CMakeLists.txt
index 44b212df3..55faa558d 100644
--- a/slsDetectorCalibration/moenchExecutables/CMakeLists.txt
+++ b/slsDetectorCalibration/moenchExecutables/CMakeLists.txt
@@ -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
diff --git a/slsDetectorCalibration/moenchExecutables/moench03Interpolation.cpp b/slsDetectorCalibration/moenchExecutables/moench03Interpolation.cpp
index cbbe32746..dc3e71583 100644
--- a/slsDetectorCalibration/moenchExecutables/moench03Interpolation.cpp
+++ b/slsDetectorCalibration/moenchExecutables/moench03Interpolation.cpp
@@ -204,9 +204,9 @@ int main(int argc, char *argv[]) {
*/
//#endif
- if (totquad > cmin && cl.x >= xmin && cl.x <= xmax &&
+ if (totquad > static_cast(cmin) && cl.x >= xmin && cl.x <= xmax &&
cl.y >= ymin && cl.y <= ymax &&
- totquad < cmax) {
+ totquad < static_cast(cmax)) {
// if (sum > cmin && totquad / sum > 0.8 && totquad / sum < 1.2 &&
// sum < cmax) {
diff --git a/slsDetectorServers/eigerDetectorServer/9mhvserial_bf.c b/slsDetectorServers/eigerDetectorServer/9mhvserial_bf.c
index f67a46dbf..8cd2c7c96 100644
--- a/slsDetectorServers/eigerDetectorServer/9mhvserial_bf.c
+++ b/slsDetectorServers/eigerDetectorServer/9mhvserial_bf.c
@@ -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
-#include // File control definitions
-#include // I2C_SLAVE, __u8 reg
+#include // File control definitions
#include
#include // atoi
#include // memset
@@ -12,80 +12,102 @@
#include /* POSIX terminal control definitions */
#include // 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));
diff --git a/slsDetectorServers/eigerDetectorServer/Makefile b/slsDetectorServers/eigerDetectorServer/Makefile
index f7f2be00a..950547c7d 100755
--- a/slsDetectorServers/eigerDetectorServer/Makefile
+++ b/slsDetectorServers/eigerDetectorServer/Makefile
@@ -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
diff --git a/slsDetectorServers/matterhornServer/.gitattributes b/slsDetectorServers/matterhornServer/.gitattributes
new file mode 100644
index 000000000..087a700fc
--- /dev/null
+++ b/slsDetectorServers/matterhornServer/.gitattributes
@@ -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
diff --git a/slsDetectorServers/matterhornServer/CMakeLists.txt b/slsDetectorServers/matterhornServer/CMakeLists.txt
index e4d237e41..34d539b08 100644
--- a/slsDetectorServers/matterhornServer/CMakeLists.txt
+++ b/slsDetectorServers/matterhornServer/CMakeLists.txt
@@ -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=
+ 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=
+ 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?
-#)
-
-
-
diff --git a/slsDetectorServers/matterhornServer/bin/matterhornDetectorServer b/slsDetectorServers/matterhornServer/bin/matterhornDetectorServer
new file mode 100755
index 000000000..74a8fc366
--- /dev/null
+++ b/slsDetectorServers/matterhornServer/bin/matterhornDetectorServer
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4bfbe17a6b0efbdcd3da78eafb1ac6c212eb81434f36267640f75fe743dc0aea
+size 316776
diff --git a/slsDetectorServers/matterhornServer/include/BaseMatterhornServer.h b/slsDetectorServers/matterhornServer/include/BaseMatterhornServer.h
deleted file mode 100644
index 4ca5e46cc..000000000
--- a/slsDetectorServers/matterhornServer/include/BaseMatterhornServer.h
+++ /dev/null
@@ -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
-#include
-#include
-#include
-#include
-#include
-
-namespace sls {
-
-/// @brief Base class for Matterhorn Server, can be used to implement a virtual
-/// server for testing and actual server
-template
-class BaseMatterhornServer
- : public DetectorServer> {
-
- public:
- /**
- * Constructor
- * Starts up a Matterhorn server.
- * Assembles a Matterhorn server using TCP and UDP detector interfaces
- * throws an exception in case of failure
- * @param port TCP/IP port number
- */
- explicit BaseMatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO)
- : DetectorServer>(port) {}
-
- ~BaseMatterhornServer() = default;
-
- ReturnCode get_version(ServerInterface &socket);
-
- ReturnCode get_detector_type(ServerInterface &socket);
-
- ReturnCode initial_checks(ServerInterface &socket);
-
- ReturnCode get_num_udp_interfaces(ServerInterface &socket) const;
-
- /**
- * @brief call function corresponding to the function ID received from the
- * client and send back the result
- * @param function_id the function ID received from the client
- * @param socket the socket to send the result back to the client
- */
- ReturnCode processFunction(const detFuncs function_id,
- ServerInterface &socket);
-
- private:
- static std::string getMatterhornServerVersion();
-
- static constexpr uint8_t numUDPInterfaces =
- 1; // only one udp per module for now
-};
-
-template
-ReturnCode
-BaseMatterhornServer::processFunction(const detFuncs function_id,
- ServerInterface &socket) {
-
- switch (function_id) {
- default:
- throw RuntimeError(
- fmt::format("Function {} not implemented",
- getFunctionNameFromEnum((enum detFuncs)function_id)));
- }
-}
-
-template
-ReturnCode BaseMatterhornServer::get_num_udp_interfaces(
- ServerInterface &socket) const {
- return static_cast(
- socket.sendResult(static_cast(numUDPInterfaces)));
-}
-
-template
-ReturnCode
-BaseMatterhornServer::get_version(ServerInterface &socket) {
-
- auto version = getMatterhornServerVersion();
- char version_cstr[MAX_STR_LENGTH]{};
- strncpy(version_cstr, version.c_str(), version.size());
- LOG(TLogLevel::logDEBUG) << "Matterhorn Server Version: " << version;
- return static_cast(socket.sendResult(
- version_cstr)); // TODO: check what would be possible return codes!!!
-}
-
-template
-ReturnCode BaseMatterhornServer::get_detector_type(
- ServerInterface &socket) {
- int detectortype = slsDetectorDefs::detectorType::MATTERHORN;
- return static_cast(socket.sendResult(detectortype));
-}
-
-template
-std::string BaseMatterhornServer::getMatterhornServerVersion() {
- return APIMATTERHORN;
-}
-
-template
-ReturnCode
-BaseMatterhornServer::initial_checks(ServerInterface &socket) {
-
- return static_cast(this)->initial_checks(socket);
-}
-
-} // namespace sls
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/include/MatterhornServer.h b/slsDetectorServers/matterhornServer/include/MatterhornServer.h
deleted file mode 100644
index 586818b71..000000000
--- a/slsDetectorServers/matterhornServer/include/MatterhornServer.h
+++ /dev/null
@@ -1,27 +0,0 @@
-#pragma once
-#include "BaseMatterhornServer.h"
-#include "TCPInterface.h"
-#include "sls/sls_detector_defs.h"
-#include
-#include
-
-namespace sls {
-
-class MatterhornServer : public BaseMatterhornServer {
-
- public:
- /**
- * Constructor
- * Starts up a Matterhorn server.
- * Assembles a Matterhorn server using TCP and UDP detector interfaces
- * throws an exception in case of failure
- * @param port TCP/IP port number
- */
- explicit MatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO);
-
- ~MatterhornServer() = default;
-
- ReturnCode initial_checks(ServerInterface &socket);
-};
-
-} // namespace sls
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/include/RegisterHelperStructs.hpp b/slsDetectorServers/matterhornServer/include/RegisterHelperStructs.hpp
deleted file mode 100644
index 5f561a285..000000000
--- a/slsDetectorServers/matterhornServer/include/RegisterHelperStructs.hpp
+++ /dev/null
@@ -1,29 +0,0 @@
-#include
-#include
-
-namespace sls {
-
-enum class IPCore : uint32_t; // forward declaration of IPCore enum class
-
-struct Register {
- /// @brief IP core address space
- const IPCore ip_core{}; // TODO replace by enum type
-
- /// @brief Offset of the register in bytes from the base address of the IP
- /// core
- const uint32_t offset_in_bytes{};
-};
-
-struct RegisterField {
- /// @brief Register to which the field belongs
- const Register register_{};
-
- /// @brief Bit position of the least significant bit of the field in the
- /// register
- const uint32_t bit_position{};
-
- /// @brief Bitmask for the field
- const uint32_t bitmask{};
-};
-
-} // namespace sls
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/include/VirtualMatterhornServer.h b/slsDetectorServers/matterhornServer/include/VirtualMatterhornServer.h
deleted file mode 100644
index 61cb31bda..000000000
--- a/slsDetectorServers/matterhornServer/include/VirtualMatterhornServer.h
+++ /dev/null
@@ -1,24 +0,0 @@
-
-#include "BaseMatterhornServer.h"
-
-namespace sls {
-
-class VirtualMatterhornServer
- : public BaseMatterhornServer {
-
- public:
- /**
- * Constructor
- * Starts up a virtual Matterhorn server.
- * Assembles a virtual Matterhorn server using TCP and UDP detector
- * interfaces throws an exception in case of failure
- * @param port TCP/IP port number
- */
- explicit VirtualMatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO);
-
- ~VirtualMatterhornServer() = default;
-
- ReturnCode initial_checks(ServerInterface &socket);
-};
-
-} // namespace sls
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/src/BaseMatterhornServer.hpp b/slsDetectorServers/matterhornServer/src/BaseMatterhornServer.hpp
new file mode 100644
index 000000000..79d1c1883
--- /dev/null
+++ b/slsDetectorServers/matterhornServer/src/BaseMatterhornServer.hpp
@@ -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
+#include
+#include
+#include
+#include
+#include
+
+namespace sls {
+
+/// @brief Base class for Matterhorn Server, can be used to implement a virtual
+/// server for testing and actual server
+template
+class BaseMatterhornServer
+ : public DetectorServer> {
+
+ public:
+ /**
+ * Constructor
+ * Starts up a Matterhorn server.
+ * Assembles a Matterhorn server using TCP and UDP detector interfaces
+ * throws an exception in case of failure
+ * @param port TCP/IP port number
+ */
+ explicit BaseMatterhornServer(
+ std::unique_ptr<
+ DetectorServerImpl::value>>
+ impl,
+ uint16_t port = DEFAULT_TCP_CNTRL_PORTNO)
+ : DetectorServer>(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(this); }
+
+ const DerivedServer *getDerived() const {
+ return static_cast(this);
+ }
+};
+
+template
+ProcessedResult
+BaseMatterhornServer::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
+ProcessedResult
+BaseMatterhornServer::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
+ProcessedResult BaseMatterhornServer::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
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/src/BaseMatterhornServerImpl.hpp b/slsDetectorServers/matterhornServer/src/BaseMatterhornServerImpl.hpp
new file mode 100644
index 000000000..9d0e45d22
--- /dev/null
+++ b/slsDetectorServers/matterhornServer/src/BaseMatterhornServerImpl.hpp
@@ -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
+#include
+
+namespace sls {
+
+template
+class VirtualMatterhornServerImpl; // forward declare
+
+template
+class BaseMatterhornServerImpl
+ : public DetectorServerImpl<
+ is_stop_server::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::value>>,
+ VirtualMemoryModel,
+ 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
+ busCommunication{};
+
+ using SPICommunicationClass = std::conditional_t<
+ std::is_same_v::value>>,
+ VirtualSPICommunication,
+ HardwareSPICommunication>;
+
+ SPICommunicationClass spiCommunication{};
+
+ private:
+ static constexpr uint8_t numUDPInterfaces =
+ 1; // only one udp per module for now
+};
+
+template
+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
+void BaseMatterhornServerImpl::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
+std::string
+BaseMatterhornServerImpl::get_server_version()
+ const {
+
+ return APIMATTERHORN;
+}
+
+template
+uint8_t BaseMatterhornServerImpl<
+ DerivedMatterhornServerImpl>::get_num_udp_interfaces() {
+ return numUDPInterfaces;
+}
+
+template
+uint8_t
+BaseMatterhornServerImpl::get_detector_type() {
+ return slsDetectorDefs::detectorType::MATTERHORN;
+}
+
+template
+uint64_t
+BaseMatterhornServerImpl::get_num_frames() const {
+
+ try {
+ uint32_t num_frames =
+ busCommunication.readRegister(Reg::MH_SM_Frames_Reg);
+ return static_cast(num_frames);
+ } catch (const std::exception &e) {
+ LOG(logERROR) << "Failed to read number of frames from register: "
+ << e.what();
+ throw;
+ }
+}
+
+template
+void BaseMatterhornServerImpl::set_num_frames(
+ const uint64_t num_frames) {
+
+ try {
+ busCommunication.writeRegister(Reg::MH_SM_Frames_Reg,
+ static_cast(num_frames));
+ auto written_num_frames = busCommunication.readRegister(
+ Reg::MH_SM_Frames_Reg); // check if write was successful
+
+ if (num_frames != static_cast(written_num_frames)) {
+ throw std::runtime_error(
+ fmt::format("Requested {} frames, but set {}", num_frames,
+ static_cast(written_num_frames)));
+ }
+ } catch (const std::exception &e) {
+ LOG(logERROR) << "Failed to set number of frames: " << e.what();
+ throw;
+ }
+}
+
+template
+void BaseMatterhornServerImpl::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
+uint32_t
+BaseMatterhornServerImpl::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
+void BaseMatterhornServerImpl::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
+uint32_t
+BaseMatterhornServerImpl::get_counter_mask()
+ const {
+
+ std::vector 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
+void BaseMatterhornServerImpl::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
+slsDetectorDefs::rxParameters
+BaseMatterhornServerImpl::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
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp b/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp
index 8c37df196..6551bf2f8 100644
--- a/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp
+++ b/slsDetectorServers/matterhornServer/src/MatterhornApp.cpp
@@ -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
#include
+#include
#include
#include
#include
@@ -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 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";
diff --git a/slsDetectorServers/matterhornServer/src/MatterhornServer.cpp b/slsDetectorServers/matterhornServer/src/MatterhornServer.cpp
deleted file mode 100644
index 522d5b486..000000000
--- a/slsDetectorServers/matterhornServer/src/MatterhornServer.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "MatterhornServer.h"
-
-namespace sls {
-
-MatterhornServer::MatterhornServer(uint16_t port)
- : BaseMatterhornServer(port) {
-
- // TODO: when do i set the udp mac and ip ?
-
- // should maybe be part of the constructor?
- tcpInterface->startTCPServer();
-
- // need a function to setup detector - e.g. set all registers etc.
-}
-
-ReturnCode MatterhornServer::initial_checks(ServerInterface &socket) {
-
- // TODO: add more checks here, for now just return true to be able to test
- // the should check firmware -client compatibility
- bool initial_checks_passed = true;
- return static_cast(socket.sendResult(initial_checks_passed));
-}
-
-} // namespace sls
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/src/MatterhornServer.hpp b/slsDetectorServers/matterhornServer/src/MatterhornServer.hpp
new file mode 100644
index 000000000..84ba7f96b
--- /dev/null
+++ b/slsDetectorServers/matterhornServer/src/MatterhornServer.hpp
@@ -0,0 +1,41 @@
+#pragma once
+#include "BaseMatterhornServer.hpp"
+#include "MatterhornServerImpl.hpp"
+#include "TCPInterface.hpp"
+#include "sls/sls_detector_defs.h"
+#include
+#include
+
+namespace sls {
+
+template
+class MatterhornServer
+ : public BaseMatterhornServer> {
+
+ public:
+ /**
+ * Constructor
+ * Starts up a Matterhorn server.
+ * Assembles a Matterhorn server using TCP and UDP detector interfaces
+ * throws an exception in case of failure
+ * @param port TCP/IP port number
+ */
+ explicit MatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO);
+
+ ~MatterhornServer() = default;
+};
+
+template
+MatterhornServer::MatterhornServer(uint16_t port)
+ : BaseMatterhornServer>(
+ std::make_unique>(), 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
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/src/MatterhornServerImpl.hpp b/slsDetectorServers/matterhornServer/src/MatterhornServerImpl.hpp
new file mode 100644
index 000000000..9640a273c
--- /dev/null
+++ b/slsDetectorServers/matterhornServer/src/MatterhornServerImpl.hpp
@@ -0,0 +1,70 @@
+#pragma once
+#include "BaseMatterhornServerImpl.hpp"
+
+namespace sls {
+
+template
+class MatterhornServerImpl
+ : public BaseMatterhornServerImpl> {
+
+ public:
+ MatterhornServerImpl() = default;
+ ~MatterhornServerImpl() = default;
+
+ slsDetectorDefs::runStatus get_run_status() const; // TODO: impement
+
+ void set_module_position_and_update_srcudpmac(
+ const std::array &position_info);
+
+ void set_source_udp_mac([[maybe_unused]] const uint64_t src_mac);
+};
+
+template
+slsDetectorDefs::runStatus
+MatterhornServerImpl::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
+void MatterhornServerImpl::
+ set_module_position_and_update_srcudpmac(
+ const std::array &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
+void MatterhornServerImpl::set_source_udp_mac(
+ const uint64_t src_mac) {
+
+ throw RuntimeError(
+ "Cannot overwrite vendor specific source UDP MAC address.");
+}
+
+} // end namespace sls
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.cpp b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.cpp
deleted file mode 100644
index 8c82da20c..000000000
--- a/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-#include "VirtualMatterhornServer.h"
-
-namespace sls {
-
-VirtualMatterhornServer::VirtualMatterhornServer(uint16_t port)
- : BaseMatterhornServer(port) {
-
- udpDetails[0].srcip = LOCALHOSTIP_INT;
-
- // should maybe be part of the constructor?
- tcpInterface->startTCPServer();
-
- // need a function to setup detector - e.g. set all registers etc.
-}
-
-ReturnCode VirtualMatterhornServer::initial_checks(ServerInterface &socket) {
-
- // TODO: add more checks here, for now just return true to be able to test
- // the should check firmware -client compatibility
- bool initial_checks_passed = true;
- return static_cast(socket.sendResult(initial_checks_passed));
-}
-
-} // namespace sls
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.hpp b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.hpp
new file mode 100644
index 000000000..a3c86123e
--- /dev/null
+++ b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServer.hpp
@@ -0,0 +1,39 @@
+
+#include "BaseMatterhornServer.hpp"
+#include "VirtualMatterhornServerImpl.hpp"
+
+namespace sls {
+
+template
+class VirtualMatterhornServer
+ : public BaseMatterhornServer> {
+
+ public:
+ /**
+ * Constructor
+ * Starts up a virtual Matterhorn server.
+ * Assembles a virtual Matterhorn server using TCP and UDP detector
+ * interfaces throws an exception in case of failure
+ * @param port TCP/IP port number
+ */
+ explicit VirtualMatterhornServer(uint16_t port = DEFAULT_TCP_CNTRL_PORTNO);
+
+ ~VirtualMatterhornServer() = default;
+};
+
+template
+VirtualMatterhornServer::VirtualMatterhornServer(uint16_t port)
+ : BaseMatterhornServer>(
+ std::make_unique>(), 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
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/src/VirtualMatterhornServerImpl.hpp b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServerImpl.hpp
new file mode 100644
index 000000000..e442abf6b
--- /dev/null
+++ b/slsDetectorServers/matterhornServer/src/VirtualMatterhornServerImpl.hpp
@@ -0,0 +1,87 @@
+#pragma once
+#include "BaseMatterhornServerImpl.hpp"
+#include "sls/ToString.h"
+
+namespace sls {
+
+template
+class VirtualMatterhornServerImpl
+ : public BaseMatterhornServerImpl<
+ VirtualMatterhornServerImpl> {
+
+ public:
+ VirtualMatterhornServerImpl();
+ ~VirtualMatterhornServerImpl() = default;
+
+ slsDetectorDefs::runStatus get_run_status() const;
+
+ void set_module_position_and_update_srcudpmac(
+ const std::array &position_info);
+
+ void set_source_udp_mac(const uint64_t newsrcudpMac);
+};
+
+template
+VirtualMatterhornServerImpl::VirtualMatterhornServerImpl() {
+ this->set_source_udp_ip(LOCALHOSTIP_INT);
+}
+
+template
+slsDetectorDefs::runStatus
+VirtualMatterhornServerImpl::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
+void VirtualMatterhornServerImpl::
+ set_module_position_and_update_srcudpmac(
+ const std::array &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
+void VirtualMatterhornServerImpl::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
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/src/communication/SPICommunication.cpp b/slsDetectorServers/matterhornServer/src/communication/SPICommunication.cpp
new file mode 100644
index 000000000..8feaffa07
--- /dev/null
+++ b/slsDetectorServers/matterhornServer/src/communication/SPICommunication.cpp
@@ -0,0 +1,120 @@
+#include "SPICommunication.hpp"
+#include
+#include
+#include
+#include
+#include
+
+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
+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::max()) {
+ throw RuntimeError("SPI read size overflow");
+ }
+
+ std::vector 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(((chip_id & 0xF) << 4) | (register_id & 0xF));
+
+ // allocate data buffer to read out data into
+ std::vector 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(dummy_data.data());
+ send_cmd.rx_buf = reinterpret_cast(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 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 &data) {
+
+ const size_t n_bytes = data.size();
+
+ if (n_bytes == std::numeric_limits::max()) {
+ throw RuntimeError("SPI read size overflow");
+ }
+
+ // First byte of the message is 4 bits chip_id then 4 bits register_id
+ std::vector write_data(n_bytes + 1); // +1 for the command byte
+
+ write_data[0] =
+ static_cast(((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(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
\ No newline at end of file
diff --git a/slsDetectorServers/matterhornServer/src/communication/SPICommunication.hpp b/slsDetectorServers/matterhornServer/src/communication/SPICommunication.hpp
new file mode 100644
index 000000000..f74805db9
--- /dev/null
+++ b/slsDetectorServers/matterhornServer/src/communication/SPICommunication.hpp
@@ -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