Merge branch 'developer' into dev/ctb_clocks
Build on RHEL8 docker image / build (push) Failing after 0s
Build on RHEL9 docker image / build (push) Failing after 0s
Run Simulator Tests on local RHEL9 / build (push) Successful in 14m42s
Run Simulator Tests on local RHEL8 / build (push) Successful in 17m12s

This commit is contained in:
2026-04-22 16:36:13 +02:00
72 changed files with 43083 additions and 36506 deletions
+2 -2
View File
@@ -3,8 +3,8 @@
# from .detector import Detector, DetectorError, free_shared_memory
from .eiger import Eiger
from .ctb import Ctb
from .dacs import DetectorDacs, Dac
from .powers import DetectorPowers, Power
from .dacs import NamedDacs, DetectorDacs, Dac
from .powers import NamedPowers, Power
from .detector import Detector
from .jungfrau import Jungfrau
from .mythen3 import Mythen3
+68 -3
View File
@@ -2,12 +2,15 @@
# Copyright (C) 2021 Contributors to the SLS Detector Package
from .detector import Detector, freeze
from .utils import element_if_equal
from .dacs import DetectorDacs, NamedDacs
from .powers import DetectorPowers, NamedPowers
from .dacs import NamedDacs
from .powers import NamedPowers
from .proxy import SlowAdcProxy
from . import _slsdet
dacIndex = _slsdet.slsDetectorDefs.dacIndex
from .detector_property import DetectorProperty
import numpy as np
from .utils import element
@freeze
@@ -24,4 +27,66 @@ class Ctb(Detector):
@property
def powers(self):
return self._powers
return self._powers
@property
def powerlist(self):
return self.getPowerNames()
@powerlist.setter
def powerlist(self, value):
self.setPowerNames(value)
@property
def adclist(self):
return self.getAdcNames()
@adclist.setter
def adclist(self, value):
self.setAdcNames(value)
@property
def signallist(self):
return self.getSignalNames()
@signallist.setter
def signallist(self, value):
self.setSignalNames(value)
@property
def slowadc(self):
"""
[Ctb] Slow ADC channel in uV of all channels or specific ones from 0-7.
Example
-------
>>> d.slowadc
0: 0 uV
1: 0 uV
2: 0 uV
3: 0 uV
4: 0 uV
5: 0 uV
6: 0 uV
7: 0 uV
>>> d.slowadc[3]
0
"""
return SlowAdcProxy(self)
@property
def slowadclist(self):
return self.getSlowADCNames()
@slowadclist.setter
def slowadclist(self, value):
self.setSlowADCNames(value)
@property
def slowadcvalues(self):
"""[Chiptestboard][Xilinx CTB] Gets the slow adc values for every slow adc for this detector."""
return {
slowadc.name.lower(): element_if_equal(np.array(self.getSlowADC(slowadc)))
for slowadc in self.getSlowADCList()
}
+20 -27
View File
@@ -41,40 +41,33 @@ class NamedDacs:
New implementation of the detector dacs. Used at the moment for
Ctb but should replace the old one for all detectors
"""
_frozen = False
_direct_access = ['_detector', '_current', '_dacnames']
_direct_access = ['_detector', '_current']
def __init__(self, detector):
self._frozen = False
self._detector = detector
self._current = 0
#only get the dacnames if we have modules attached
if detector.size() == 0:
self._dacnames = [f"dac{i}" for i in range(18)]
else:
self._dacnames = [n.replace(" ", "") for n in detector.getDacNames()]
# Populate the dacs
for i,name in enumerate(self._dacnames):
#name, enum, low, high, default, detector
setattr(self, name, Dac(name, dacIndex(i), 0, 4000, 1000, detector))
self._frozen = True
# def __getattr__(self, name):
# return self.__getattribute__('_' + name)
@property
def _dacnames(self):
if self._detector.size() == 0:
raise RuntimeError("No modules added")
return [n.replace(" ", "") for n in self._detector.daclist]
def __getattr__(self, name):
if name in self._dacnames:
idx = self._dacnames.index(name)
return Dac(name, dacIndex(idx), 0, 4096, -100, self._detector)
raise AttributeError(f'Dac not found: {name}')
def __setattr__(self, name, value):
if not self._frozen:
#durning init we need to be able to set up the class
if name in ('_detector', '_current', '_frozen'):
super().__setattr__(name, value)
elif name in self._dacnames:
return getattr(self, name).__setitem__(slice(None, None, None), value)
else:
#Later we restrict us to manipulate dacs and a few fields
if name in self._direct_access:
super().__setattr__(name, value)
elif name in self._dacnames:
return self.__getattribute__(name).__setitem__(slice(None, None, None), value)
else:
raise AttributeError(f'Dac not found: {name}')
raise AttributeError(f'Dac not found: {name}')
def __next__(self):
if self._current >= len(self._dacnames):
@@ -82,10 +75,10 @@ class NamedDacs:
raise StopIteration
else:
self._current += 1
return self.__getattribute__(self._dacnames[self._current-1])
# return self.__getattr__(self._dacnames[self._current-1])
return getattr(self, self._dacnames[self._current-1])
def __iter__(self):
self._current = 0
return self
def __repr__(self):
+11 -152
View File
@@ -10,6 +10,7 @@ runStatus = slsDetectorDefs.runStatus
timingMode = slsDetectorDefs.timingMode
speedLevel = slsDetectorDefs.speedLevel
dacIndex = slsDetectorDefs.dacIndex
powerIndex = slsDetectorDefs.powerIndex
detectorType = slsDetectorDefs.detectorType
streamingInterface = slsDetectorDefs.streamingInterface
@@ -20,7 +21,7 @@ from .utils import Geometry, to_geo, element, reduce_time, is_iterable, hostname
from ._slsdet import xy, freeSharedMemory, getUserDetails
from .gaincaps import Mythen3GainCapsWrapper
from . import utils as ut
from .proxy import JsonProxy, SlowAdcProxy, ClkDivProxy, MaxPhaseProxy, ClkFreqProxy, PatLoopProxy, PatNLoopProxy, PatWaitProxy, PatWaitTimeProxy
from .proxy import JsonProxy, ClkDivProxy, MaxPhaseProxy, ClkFreqProxy, PatLoopProxy, PatNLoopProxy, PatWaitProxy, PatWaitTimeProxy
from .registers import Register, Adc_register
import datetime as dt
@@ -516,13 +517,12 @@ class Detector(CppDetectorApi):
@element
def powerchip(self):
"""
[Jungfrau][Moench][Mythen3][Gotthard2][Xilinx Ctb] Power the chip.
[Jungfrau][Moench][Mythen3][Gotthard2] Power the chip.
Note
----
[Jungfrau][Moench] Default is disabled. Get will return power status. Can be off if temperature event occured (temperature over temp_threshold with temp_control enabled. Will configure chip (only chip v1.1).\n
[Mythen3][Gotthard2] Default is 1. If module not connected or wrong module, powerchip will fail.
[Xilinx Ctb] Default is 0. Also configures the chip if powered on.
"""
return self.getPowerChip()
@@ -1987,26 +1987,7 @@ class Detector(CppDetectorApi):
return super().getBit(resolved)
@property
def slowadc(self):
"""
[Ctb] Slow ADC channel in uV of all channels or specific ones from 0-7.
Example
-------
>>> d.slowadc
0: 0 uV
1: 0 uV
2: 0 uV
3: 0 uV
4: 0 uV
5: 0 uV
6: 0 uV
7: 0 uV
>>> d.slowadc[3]
0
"""
return SlowAdcProxy(self)
@property
def daclist(self):
@@ -2022,52 +2003,7 @@ class Detector(CppDetectorApi):
def daclist(self, value):
self.setDacNames(value)
@property
def adclist(self):
"""
[Chiptestboard] List of names for every adc for this board. 32 adcs
"""
return self.getAdcNames()
@adclist.setter
def adclist(self, value):
self.setAdcNames(value)
@property
def signallist(self):
"""
[Chiptestboard] List of names for every io signal for this board. 64 signals
"""
return self.getSignalNames()
@signallist.setter
def signallist(self, value):
self.setSignalNames(value)
@property
def powerlist(self):
"""
[Chiptestboard] List of names for every power for this board. 5 power supply
"""
return self.getPowerNames()
@powerlist.setter
def powerlist(self, value):
self.setPowerNames(value)
@property
def slowadclist(self):
"""
[Chiptestboard] List of names for every slowadc for this board. 8 slowadc
"""
return self.getSlowADCNames()
@slowadclist.setter
def slowadclist(self, value):
self.setSlowADCNames(value)
@property
def dacvalues(self):
"""Gets the dac values for every dac for this detector."""
@@ -2076,21 +2012,6 @@ class Detector(CppDetectorApi):
for dac in self.getDacList()
}
@property
def powervalues(self):
"""[Chiptestboard] Gets the power values for every power for this detector."""
return {
power.name.lower(): element_if_equal(np.array(self.getPower(power)))
for power in self.getPowerList()
}
@property
def slowadcvalues(self):
"""[Chiptestboard] Gets the slow adc values for every slow adc for this detector."""
return {
slowadc.name.lower(): element_if_equal(np.array(self.getSlowADC(slowadc)))
for slowadc in self.getSlowADCList()
}
@property
def timinglist(self):
@@ -4189,77 +4110,15 @@ class Detector(CppDetectorApi):
n = ut.merge_args(2, n)
ut.set_using_dict(self.setPatternLoopCycles, *n)
@property
@element
def v_a(self):
"""[Ctb][Xilinx Ctb] Power supply a in mV."""
return self.getPower(dacIndex.V_POWER_A)
@v_a.setter
def v_a(self, value):
value = ut.merge_args(dacIndex.V_POWER_A, value)
ut.set_using_dict(self.setPower, *value)
@property
@element
def v_b(self):
"""[Ctb][Xilinx Ctb] Power supply b in mV."""
return self.getPower(dacIndex.V_POWER_B)
@v_b.setter
def v_b(self, value):
value = ut.merge_args(dacIndex.V_POWER_B, value)
ut.set_using_dict(self.setPower, *value)
@property
@element
def v_c(self):
"""[Ctb][Xilinx Ctb] Power supply c in mV."""
return self.getPower(dacIndex.V_POWER_C)
@v_c.setter
def v_c(self, value):
value = ut.merge_args(dacIndex.V_POWER_C, value)
ut.set_using_dict(self.setPower, *value)
@property
@element
def v_d(self):
"""[Ctb][Xilinx Ctb] Power supply d in mV."""
return self.getPower(dacIndex.V_POWER_D)
@v_d.setter
def v_d(self, value):
value = ut.merge_args(dacIndex.V_POWER_D, value)
ut.set_using_dict(self.setPower, *value)
@property
@element
def v_io(self):
"""[Ctb][Xilinx Ctb] Power supply io in mV. Minimum 1200 mV.
Note
----
Must be the first power regulator to be set after fpga reset (on-board detector server start up).
"""
return self.getPower(dacIndex.V_POWER_IO)
@v_io.setter
def v_io(self, value):
value = ut.merge_args(dacIndex.V_POWER_IO, value)
ut.set_using_dict(self.setPower, *value)
@property
@element
def v_limit(self):
"""[Ctb][Xilinx Ctb] Soft limit for power supplies (ctb only) and DACS in mV."""
return self.getPower(dacIndex.V_LIMIT)
return self.getVoltageLimit()
@v_limit.setter
def v_limit(self, value):
value = ut.merge_args(dacIndex.V_LIMIT, value)
ut.set_using_dict(self.setPower, *value)
ut.set_using_dict(self.setVoltageLimit, value)
@property
@element
@@ -4268,7 +4127,7 @@ class Detector(CppDetectorApi):
:setter: Not implemented
"""
return self.getMeasuredCurrent(dacIndex.I_POWER_A)
return self.getMeasuredCurrent(powerIndex.I_POWER_A)
@property
@element
@@ -4277,7 +4136,7 @@ class Detector(CppDetectorApi):
:setter: Not implemented
"""
return self.getMeasuredCurrent(dacIndex.I_POWER_B)
return self.getMeasuredCurrent(powerIndex.I_POWER_B)
@property
@element
@@ -4286,7 +4145,7 @@ class Detector(CppDetectorApi):
:setter: Not implemented
"""
return self.getMeasuredCurrent(dacIndex.I_POWER_C)
return self.getMeasuredCurrent(powerIndex.I_POWER_C)
@property
@element
@@ -4295,7 +4154,7 @@ class Detector(CppDetectorApi):
:setter: Not implemented
"""
return self.getMeasuredCurrent(dacIndex.I_POWER_D)
return self.getMeasuredCurrent(powerIndex.I_POWER_D)
@property
@element
@@ -4304,7 +4163,7 @@ class Detector(CppDetectorApi):
:setter: Not implemented
"""
return self.getMeasuredCurrent(dacIndex.I_POWER_IO)
return self.getMeasuredCurrent(powerIndex.I_POWER_IO)
@property
def clkphase(self):
+93 -145
View File
@@ -1,77 +1,125 @@
# SPDX-License-Identifier: LGPL-3.0-or-other
# Copyright (C) 2021 Contributors to the SLS Detector Package
from .detector_property import DetectorProperty
from functools import partial
import numpy as np
from . import _slsdet
from .detector import freeze
dacIndex = _slsdet.slsDetectorDefs.dacIndex
class Power(DetectorProperty):
powerIndex = _slsdet.slsDetectorDefs.powerIndex
class Power:
"""
This class represents a power on the Chip Test Board. One instance handles all
powers with the same name for a multi detector instance. (TODO: Not needed for CTB)
This class represents a power supply on the Chip Test Board.
.. note ::
This class is used to build up DetectorPowers and is in general
This class is used to build up NamedPowers and is in general
not directly accessible to the user.
"""
_direct_access = ['_detector']
def __init__(self, name, enum, default, detector):
super().__init__(partial(detector.getPower, enum),
lambda x, y : detector.setPower(enum, x, y),
detector.size,
name)
self._frozen = False
self.__name__ = name
self.enum = enum
self.default = default
self.detector = detector
self._frozen = True
def enable(self):
" Enable this power supply."
self.detector.setPowerEnabled([self.enum], True)
def disable(self):
" Disable this power supply."
self.detector.setPowerEnabled([self.enum], False)
@property
def dac(self):
" Returns the dac value for this power supply in mV."
return self.detector.getPowerDAC(self.enum)
@property
def enabled(self):
" Returns whether this power supply is enabled."
return self.detector.isPowerEnabled(self.enum)
# prevent unknown attributes
def __setattr__(self, name, value):
if not getattr(self, "_frozen", False) or name in ("_frozen", "__name__", "enum", "default", "detector"):
super().__setattr__(name, value)
else:
raise AttributeError(f"Cannot set attribute '{name}' on Power.")
def __eq__(self, other):
if isinstance(other, Power):
return (
self.detector == other.detector and
self.enum == other.enum
)
if isinstance(other, int):
return self.dac == other
return NotImplemented
def __repr__(self):
"""String representation for a single power in all modules"""
powerstr = ''.join([f'{item:5d}' for item in self.get()])
return f'{self.__name__:15s}:{powerstr}'
"String representation for a single power supply"
return f'{self.__name__:15s}: {str(self.enabled):5s}, {self.dac:5d} mV'
class NamedPowers:
"""
New implementation of the detector powers.
List implementation of the all the power supplies with its names.
d.powers gives you list of all powers with their DAC values and enables.
Example
--------
# print all powers with DAC and enables
d.powers
# set DAC or enables
d.powers.VA = 1200
d.powers.VA.enable()
d.powers.VA.disable()
# get
d.powers.VA.enabled
d.powers.VA.dac
d.powers.VA # print both enabled and dac
"""
_frozen = False
_direct_access = ['_detector', '_current', '_powernames']
_direct_access = ['_detector', '_current']
def __init__(self, detector):
self._frozen = False
self._detector = detector
self._current = 0
#only get the powernames if we have modules attached
if detector.size() == 0:
self._powernames = ["VA", "VB", "VC", "VD", "VIO"]
else:
self._powernames = [n.replace(" ", "") for n in detector.getPowerNames()]
# Populate the powers
for i,name in enumerate(self._powernames):
#name, enum, low, high, default, detector
k = dacIndex(i + int(dacIndex.V_POWER_A))
setattr(self, name, Power(name, k, 0, detector))
self._frozen = True
# def __getattr__(self, name):
# return self.__getattribute__('_' + name)
@property
def _powernames(self):
if self._detector.size() == 0:
raise RuntimeError("No modules added")
# always get the latest list
if hasattr(self._detector, 'powerlist'):
return [n.replace(" ", "") for n in self._detector.powerlist]
else:
raise RuntimeError("Detector does not have powerlist attribute")
def __getattr__(self, name):
if name in self._powernames:
idx = self._powernames.index(name)
return Power(name, powerIndex(idx), 0, self._detector)
raise AttributeError(f'Power not found: {name}')
def __setattr__(self, name, value):
if not self._frozen:
#durning init we need to be able to set up the class
if name in ("_detector", "_current", "_frozen"):
super().__setattr__(name, value)
else:
#Later we restrict us to manipulate powers and a few fields
if name in self._direct_access:
super().__setattr__(name, value)
elif name in self._powernames:
return self.__getattribute__(name).__setitem__(slice(None, None), value)
elif name in self._powernames:
if isinstance(value, int):
idx = self._powernames.index(name)
self._detector.setPowerDAC(powerIndex(idx), value)
else:
raise AttributeError(f'Power not found: {name}')
raise AttributeError(f"Can only set DAC (int) for '{name}' on Power.")
else:
raise AttributeError(f'Power not found: {name}')
def __next__(self):
if self._current >= len(self._powernames):
@@ -79,83 +127,11 @@ class NamedPowers:
raise StopIteration
else:
self._current += 1
return self.__getattribute__(self._powernames[self._current-1])
return getattr(self, self._powernames[self._current-1])
# return self.__getattr__(self._powernames[self._current-1])
def __iter__(self):
return self
def __repr__(self):
r_str = ['========== POWERS =========']
r_str += [repr(power) for power in self]
return '\n'.join(r_str)
def get_asarray(self):
"""
Read the powers into a numpy array with dimensions [npowers, nmodules]
"""
power_array = np.zeros((len(self._powernames), len(self._detector)))
for i, _d in enumerate(self):
power_array[i,:] = _d[:]
return power_array
def to_array(self):
return self.get_asarray()
def set_from_array(self, power_array):
"""
Set the power from an numpy array with power values. [npowers, nmodules]
"""
power_array = power_array.astype(np.int)
for i, _d in enumerate(self):
_d[:] = power_array[i]
def from_array(self, power_array):
self.set_from_array(power_array)
class DetectorPowers:
_powers = []
_powernames = [_d[0] for _d in _powers]
_allowed_attr = ['_detector', '_current']
_frozen = False
def __init__(self, detector):
# We need to at least initially know which detector we are connected to
self._detector = detector
# Index to support iteration
self._current = 0
# Name the attributes?
for _d in self._powers:
setattr(self, '_'+_d[0], Power(*_d, detector))
self._frozen = True
def __getattr__(self, name):
return self.__getattribute__('_' + name)
@property
def powernames(self):
return [_d[0] for _d in _powers]
def __setattr__(self, name, value):
if name in self._powernames:
return self.__getattribute__('_' + name).__setitem__(slice(None, None), value)
else:
if self._frozen == True and name not in self._allowed_attr:
raise AttributeError(f'Power not found: {name}')
super().__setattr__(name, value)
def __next__(self):
if self._current >= len(self._powers):
self._current = 0
raise StopIteration
else:
self._current += 1
return self.__getattr__(self._powernames[self._current-1])
def __iter__(self):
return self
def __repr__(self):
@@ -163,33 +139,5 @@ class DetectorPowers:
r_str += [repr(power) for power in self]
return '\n'.join(r_str)
def get_asarray(self):
"""
Read the powers into a numpy array with dimensions [npowers, nmodules]
"""
power_array = np.zeros((len(self._powers), len(self._detector)))
for i, _d in enumerate(self):
power_array[i,:] = _d[:]
return power_array
def to_array(self):
return self.get_asarray()
def set_from_array(self, power_array):
"""
Set the powers from an numpy array with power values. [npowers, nmodules]
"""
power_array = power_array.astype(np.int)
for i, _d in enumerate(self):
_d[:] = power_array[i]
def from_array(self, power_array):
self.set_from_array(power_array)
def set_default(self):
"""
Set all powers to their default values
"""
for _d in self:
_d[:] = _d.default
def __dir__(self):
return super().__dir__() + self._powernames
+40 -29
View File
@@ -1549,21 +1549,46 @@ void init_det(py::module &m) {
Detector::getSYNCClock,
py::arg() = Positions{});
CppDetectorApi.def("getPowerList",
(std::vector<defs::dacIndex>(Detector::*)() const) &
(std::vector<defs::powerIndex>(Detector::*)() const) &
Detector::getPowerList);
CppDetectorApi.def("getPowerDAC",
(int (Detector::*)(defs::powerIndex) const) &
Detector::getPowerDAC,
py::arg());
CppDetectorApi.def("setPowerDAC",
(void (Detector::*)(defs::powerIndex, int)) &
Detector::setPowerDAC,
py::arg(), py::arg());
CppDetectorApi.def("isPowerEnabled",
(bool (Detector::*)(defs::powerIndex) const) &
Detector::isPowerEnabled,
py::arg());
CppDetectorApi.def(
"setPowerEnabled",
(void (Detector::*)(const std::vector<defs::powerIndex> &, bool)) &
Detector::setPowerEnabled,
py::arg(), py::arg());
CppDetectorApi.def("getMeasuredPower",
(int (Detector::*)(defs::powerIndex) const) &
Detector::getMeasuredPower,
py::arg());
CppDetectorApi.def("getMeasuredCurrent",
(int (Detector::*)(defs::powerIndex) const) &
Detector::getMeasuredCurrent,
py::arg());
CppDetectorApi.def("getVoltageLimit",
(int (Detector::*)() const) & Detector::getVoltageLimit);
CppDetectorApi.def(
"setVoltageLimit",
(void (Detector::*)(const int)) & Detector::setVoltageLimit, py::arg());
CppDetectorApi.def("getSlowADCList",
(std::vector<defs::dacIndex>(Detector::*)() const) &
Detector::getSlowADCList);
CppDetectorApi.def(
"getPower",
"getSlowADC",
(Result<int>(Detector::*)(defs::dacIndex, sls::Positions) const) &
Detector::getPower,
Detector::getSlowADC,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def(
"setPower",
(void (Detector::*)(defs::dacIndex, int, sls::Positions)) &
Detector::setPower,
py::arg(), py::arg(), py::arg() = Positions{});
CppDetectorApi.def("getADCVpp",
(Result<int>(Detector::*)(bool, sls::Positions) const) &
Detector::getADCVpp,
@@ -1629,21 +1654,6 @@ void init_det(py::module &m) {
(void (Detector::*)(int, sls::Positions)) &
Detector::setDBITClock,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def(
"getMeasuredPower",
(Result<int>(Detector::*)(defs::dacIndex, sls::Positions) const) &
Detector::getMeasuredPower,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def(
"getMeasuredCurrent",
(Result<int>(Detector::*)(defs::dacIndex, sls::Positions) const) &
Detector::getMeasuredCurrent,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def(
"getSlowADC",
(Result<int>(Detector::*)(defs::dacIndex, sls::Positions) const) &
Detector::getSlowADC,
py::arg(), py::arg() = Positions{});
CppDetectorApi.def("getExternalSamplingSource",
(Result<int>(Detector::*)(sls::Positions) const) &
Detector::getExternalSamplingSource,
@@ -1766,18 +1776,19 @@ void init_det(py::module &m) {
Detector::getPowerNames);
CppDetectorApi.def(
"getPowerIndex",
(defs::dacIndex(Detector::*)(const std::string &) const) &
(defs::powerIndex(Detector::*)(const std::string &) const) &
Detector::getPowerIndex,
py::arg());
CppDetectorApi.def(
"setPowerName",
(void (Detector::*)(const defs::dacIndex, const std::string &)) &
(void (Detector::*)(const defs::powerIndex, const std::string &)) &
Detector::setPowerName,
py::arg(), py::arg());
CppDetectorApi.def("getPowerName",
(std::string(Detector::*)(const defs::dacIndex) const) &
Detector::getPowerName,
py::arg());
CppDetectorApi.def(
"getPowerName",
(std::string(Detector::*)(const defs::powerIndex) const) &
Detector::getPowerName,
py::arg());
CppDetectorApi.def("setSlowADCNames",
(void (Detector::*)(const std::vector<std::string>)) &
Detector::setSlowADCNames,
+20 -12
View File
@@ -29,6 +29,12 @@ void init_enums(py::module &m) {
slsDetectorDefs::detectorType::XILINX_CHIPTESTBOARD)
.export_values();
py::enum_<slsDetectorDefs::boolFormat>(Defs, "boolFormat")
.value("TrueFalse", slsDetectorDefs::boolFormat::TrueFalse)
.value("OnOff", slsDetectorDefs::boolFormat::OnOff)
.value("OneZero", slsDetectorDefs::boolFormat::OneZero)
.export_values();
py::enum_<slsDetectorDefs::runStatus>(Defs, "runStatus")
.value("IDLE", slsDetectorDefs::runStatus::IDLE)
.value("ERROR", slsDetectorDefs::runStatus::ERROR)
@@ -175,18 +181,6 @@ void init_enums(py::module &m) {
.value("TEMPERATURE_FPGA3",
slsDetectorDefs::dacIndex::TEMPERATURE_FPGA3)
.value("TRIMBIT_SCAN", slsDetectorDefs::dacIndex::TRIMBIT_SCAN)
.value("V_POWER_A", slsDetectorDefs::dacIndex::V_POWER_A)
.value("V_POWER_B", slsDetectorDefs::dacIndex::V_POWER_B)
.value("V_POWER_C", slsDetectorDefs::dacIndex::V_POWER_C)
.value("V_POWER_D", slsDetectorDefs::dacIndex::V_POWER_D)
.value("V_POWER_IO", slsDetectorDefs::dacIndex::V_POWER_IO)
.value("V_POWER_CHIP", slsDetectorDefs::dacIndex::V_POWER_CHIP)
.value("I_POWER_A", slsDetectorDefs::dacIndex::I_POWER_A)
.value("I_POWER_B", slsDetectorDefs::dacIndex::I_POWER_B)
.value("I_POWER_C", slsDetectorDefs::dacIndex::I_POWER_C)
.value("I_POWER_D", slsDetectorDefs::dacIndex::I_POWER_D)
.value("I_POWER_IO", slsDetectorDefs::dacIndex::I_POWER_IO)
.value("V_LIMIT", slsDetectorDefs::dacIndex::V_LIMIT)
.value("SLOW_ADC0", slsDetectorDefs::dacIndex::SLOW_ADC0)
.value("SLOW_ADC1", slsDetectorDefs::dacIndex::SLOW_ADC1)
.value("SLOW_ADC2", slsDetectorDefs::dacIndex::SLOW_ADC2)
@@ -198,6 +192,20 @@ void init_enums(py::module &m) {
.value("SLOW_ADC_TEMP", slsDetectorDefs::dacIndex::SLOW_ADC_TEMP)
.export_values();
py::enum_<slsDetectorDefs::powerIndex>(Defs, "powerIndex")
.value("V_POWER_A", slsDetectorDefs::powerIndex::V_POWER_A)
.value("V_POWER_B", slsDetectorDefs::powerIndex::V_POWER_B)
.value("V_POWER_C", slsDetectorDefs::powerIndex::V_POWER_C)
.value("V_POWER_D", slsDetectorDefs::powerIndex::V_POWER_D)
.value("V_POWER_IO", slsDetectorDefs::powerIndex::V_POWER_IO)
.value("V_POWER_CHIP", slsDetectorDefs::powerIndex::V_POWER_CHIP)
.value("I_POWER_A", slsDetectorDefs::powerIndex::I_POWER_A)
.value("I_POWER_B", slsDetectorDefs::powerIndex::I_POWER_B)
.value("I_POWER_C", slsDetectorDefs::powerIndex::I_POWER_C)
.value("I_POWER_D", slsDetectorDefs::powerIndex::I_POWER_D)
.value("I_POWER_IO", slsDetectorDefs::powerIndex::I_POWER_IO)
.export_values();
py::enum_<slsDetectorDefs::detectorSettings>(Defs, "detectorSettings")
.value("STANDARD", slsDetectorDefs::detectorSettings::STANDARD)
.value("FAST", slsDetectorDefs::detectorSettings::FAST)
+321
View File
@@ -394,3 +394,324 @@ def test_patternstart(session_simulator, request):
assert "not implemented" in str(exc_info.value)
Log(LogLevel.INFOGREEN, f"{request.node.name} passed")
@pytest.mark.detectorintegration
def test_v_limit(session_simulator, request):
"""Test v_limit."""
det_type, num_interfaces, num_mods, d = session_simulator
assert d is not None
if det_type in ['ctb', 'xilinx_ctb']:
# save previous value
prev_val = d.getVoltageLimit()
from slsdet import dacIndex, powerIndex
prev_dac_val = d.getDAC(dacIndex.DAC_0, False)
prev_power_dac_val = d.getPowerDAC(powerIndex.V_POWER_A)
with pytest.raises(Exception):
d.v_limit = (1200, 'mV') #mV unit not supported, should be 'no unit'
with pytest.raises(Exception):
d.v_limit = -100 # previously worked but not allowing now
# setting dac and power dac with no vlimit should work
d.v_limit = 0
assert d.v_limit == 0
d.setDAC(dacIndex.DAC_0, 1200, True, [0])
d.setPowerDAC(powerIndex.V_POWER_A, 1200)
# setting vlimit should throw setting values above vlimit
d.v_limit = 1500
assert d.v_limit == 1500
with pytest.raises(Exception):
d.setDAC(dacIndex.DAC_0, 1501, True, [0])
with pytest.raises(Exception):
d.setPowerDAC(powerIndex.V_POWER_A, 1501)
# setting dac and power dac below vlimit should still work
d.setDAC(dacIndex.DAC_0, 1210, True, [0])
d.setPowerDAC(powerIndex.V_POWER_A, 1210)
# restore previous value
d.setVoltageLimit(prev_val)
d.setPowerDAC(powerIndex.V_POWER_A, prev_power_dac_val)
for i in range(len(d)):
d.setDAC(dacIndex.DAC_0, prev_dac_val[i], False, [i])
else:
with pytest.raises(Exception) as exc_info:
d.v_limit
assert "not implemented" in str(exc_info.value)
Log(LogLevel.INFOGREEN, f"{request.node.name} passed")
@pytest.mark.detectorintegration
def test_v_abcd(session_simulator, request):
"""Test v_a, v_b, v_c, v_d, v_io are deprecated comands."""
det_type, num_interfaces, num_mods, d = session_simulator
assert d is not None
with pytest.raises(Exception):
d.v_a
with pytest.raises(Exception):
d.v_b
with pytest.raises(Exception):
d.v_c
with pytest.raises(Exception):
d.v_d
with pytest.raises(Exception):
d.v_io
Log(LogLevel.INFOGREEN, f"{request.node.name} passed")
@pytest.mark.detectorintegration
def test_powers(session_simulator, request):
"""Test powers and powerlist."""
det_type, num_interfaces, num_mods, d = session_simulator
assert d is not None
from slsdet import Ctb
c = Ctb()
if det_type in ['ctb', 'xilinx_ctb']:
c.powerlist
# save previous value
from slsdet import powerIndex
prev_val_dac = {power: c.getPowerDAC(power) for power in c.getPowerList()}
prev_val = {power: c.isPowerEnabled(power) for power in c.getPowerList()}
# invalid
invalid_assignments = [
(c.powers, "random", True), # set random power
(c.powers, "random", True), # set random attribute of power
(c.powers.VA, "dac", "1200"),
(c.powers.VA, "enabled", "True"),
(c.powers, "VA", "-100"),
(c.powers, "VA", "-1"),
(c.powers, "VA", "4096")
]
for obj, attr, value in invalid_assignments:
with pytest.raises(Exception):
setattr(obj, attr, value)
# vchip power can only be accessed via pybindings because it cannot be enabled/disabled
with pytest.raises(Exception):
c.powers.VCHIP
# valid
c.powers
c.powers.VA = 1200
assert c.powers.VA == 1200
assert c.powers.VA.dac == 1200
c.powers.VA.enable()
assert c.powers.VA.enabled == True
c.setPowerEnabled([powerIndex.V_POWER_B, powerIndex.V_POWER_C], True)
assert c.powers.VB.enabled == True
assert c.powers.VC.enabled == True
c.powers.VA = 1500
assert c.powers.VA == 1500
assert c.powers.VA.dac == 1500
# change power name and test same value
temp = c.powers.VB
c.powerlist = ["VA", "m_VB", "VC", "VD", "VIO"]
assert c.powers.m_VB.enabled == True
assert c.powers.m_VB == temp
# restore previous value
for power in c.getPowerList():
c.setPowerDAC(power, prev_val_dac[power])
c.setPowerEnabled([power], prev_val[power])
else:
with pytest.raises(Exception) as exc_info:
c.powerlist
assert "only for CTB" in str(exc_info.value)
Log(LogLevel.INFOGREEN, f"{request.node.name} passed")
@pytest.mark.detectorintegration
def test_adclist(session_simulator, request):
"""Test ADC list."""
det_type, num_interfaces, num_mods, d = session_simulator
assert d is not None
from slsdet import Ctb
c = Ctb()
if det_type in ['ctb', 'xilinx_ctb']:
c.adclist
c.adclist = ["1", "2", "3", "test", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32"]
c.adclist
else:
with pytest.raises(Exception) as exc_info:
c.adclist
assert "only for CTB" in str(exc_info.value)
Log(LogLevel.INFOGREEN, f"{request.node.name} passed")
@pytest.mark.detectorintegration
def test_signallist(session_simulator, request):
"""Test signal list."""
det_type, num_interfaces, num_mods, d = session_simulator
assert d is not None
from slsdet import Ctb
c = Ctb()
if det_type in ['ctb', 'xilinx_ctb']:
c.signallist
c.signallist = ["1", "2", "3", "test", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "60", "61", "62", "63", "64"]
c.signallist
else:
with pytest.raises(Exception) as exc_info:
c.signallist
assert "only for CTB" in str(exc_info.value)
Log(LogLevel.INFOGREEN, f"{request.node.name} passed")
@pytest.mark.detectorintegration
def test_slowadc(session_simulator, request):
"""Test slow ADC and slow adc list."""
det_type, num_interfaces, num_mods, d = session_simulator
assert d is not None
from slsdet import Ctb
c = Ctb()
if det_type in ['ctb', 'xilinx_ctb']:
c.slowadc
c.slowadc.SLOWADC5
c.slowadclist = ["1", "2", "3", "test", "5", "6", "7", "8"]
c.slowadc.test
else:
with pytest.raises(Exception) as exc_info:
c.signallist
assert "only for CTB" in str(exc_info.value)
Log(LogLevel.INFOGREEN, f"{request.node.name} passed")
@pytest.mark.detectorintegration
def test_dac(session_simulator, request):
"""Test dac."""
det_type, num_interfaces, num_mods, d = session_simulator
assert d is not None
from slsdet import dacIndex
if det_type in ['ctb', 'xilinx_ctb']:
from slsdet import Ctb
c = Ctb()
# valid
c.daclist
c.dacvalues
# save previous value
prev_val = {dac: c.getDAC(dac, False) for dac in c.getDacList()}
prev_dac_list = c.daclist
# invalid
invalid_assignments = [
(c.dacs, "vb_comp", "1200"), # set random dac
(c.dacs, "DAC18", "1200"), # set dac 18
(c.dacs, "DAC0", "-1"),
(c.dacs, "DAC0", "4096")
]
for obj, attr, value in invalid_assignments:
with pytest.raises(Exception):
setattr(obj, attr, value)
# valid
c.dacs.DAC0 = 1200
assert c.getDAC(dacIndex.DAC_0, False)[0] == 1200
c.dacs.DAC0 = 0
assert c.dacs.DAC0[0] == 0
# restore previous value
for dac in c.getDacList():
c.setDAC(dac, prev_val[dac][0], False)
c.daclist = prev_dac_list
else:
with pytest.raises(Exception):
d.dacs.DAC0
# valid
d.daclist
d.dacvalues
# remember first dac name and index to test later
dacname = d.daclist[0]
assert dacname
dacIndex = d.getDacList()[0]
# save previous value
prev_val = d.getDAC(dacIndex, False)
if det_type == 'eiger':
from slsdet import Eiger
c = Eiger()
elif det_type == 'jungfrau':
from slsdet import Jungfrau
c = Jungfrau()
elif det_type == 'gotthard2':
from slsdet import Gotthard2
c = Gotthard2()
elif det_type == 'mythen3':
from slsdet import Mythen3
c = Mythen3()
elif det_type == 'moench':
from slsdet import Moench
c = Moench()
else:
raise RuntimeError("Unknown detector type to test dac: " + det_type)
# invalid checks
invalid_assignments = [
(c.dacs, "random", "1200"), # set random dac
(c.dacs, "DAC0", "1200"), # set random dac
(c.dacs, dacname, "-1"),
(c.dacs, dacname, "4096")
]
for obj, attr, value in invalid_assignments:
with pytest.raises(Exception):
setattr(obj, attr, value)
# valid, have to use setattr because c is different for each detector
# and we cannot hardcode the dac name
setattr(c.dacs, dacname, 1200)
assert c.getDAC(dacIndex, False)[0] == 1200
setattr(c.dacs, dacname, 0)
assert getattr(c.dacs, dacname)[0] == 0
# restore previous value
for i in range(len(d)):
d.setDAC(dacIndex, prev_val[i], False, [i])
Log(LogLevel.INFOGREEN, f"{request.node.name} passed")