From 4d5b3072c0bb1450f73339986cd06c3d27052b37 Mon Sep 17 00:00:00 2001
From: David Hammer <dhammer@mailbox.org>
Date: Thu, 30 Sep 2021 17:07:39 +0200
Subject: [PATCH] WIP replace calibrationBase with CalCat friends

---
 src/calng/AgipdCorrection.py    | 79 ++++++++++++++----------------
 src/calng/CalibrationManager.py | 19 +++----
 src/calng/DsscCorrection.py     |  4 +-
 src/calng/base_correction.py    | 87 +++++++++------------------------
 src/calng/calcat_utils.py       | 65 +++++++++++++++---------
 src/tests/test_calcat_utils.py  | 15 +++---
 6 files changed, 117 insertions(+), 152 deletions(-)

diff --git a/src/calng/AgipdCorrection.py b/src/calng/AgipdCorrection.py
index 70fca7bf..a9d9e3a4 100644
--- a/src/calng/AgipdCorrection.py
+++ b/src/calng/AgipdCorrection.py
@@ -17,6 +17,7 @@ from . import utils
 from ._version import version as deviceVersion
 from .agipd_gpu import AgipdGainMode, AgipdGpuRunner, BadPixelValues, CorrectionFlags
 from .base_correction import BaseCorrection
+from .calcat_utils import AgipdCalcatFriend, AgipdConstants
 
 
 @KARABO_CLASSINFO("AgipdCorrection", deviceVersion)
@@ -35,40 +36,9 @@ class AgipdCorrection(BaseCorrection):
 
     @staticmethod
     def expectedParameters(expected):
-        AgipdCorrection.addConstant(
-            "ThresholdsDark",
-            "Dark",
-            expected,
-            optional=False,
-            mandatoryForIteration=True,
-        )
-        AgipdCorrection.addConstant(
-            "Offset", "Dark", expected, optional=False, mandatoryForIteration=True
-        )
-        AgipdCorrection.addConstant(
-            "SlopesPC", "Dark", expected, optional=True, mandatoryForIteration=True
-        )
-        AgipdCorrection.addConstant(
-            "SlopesFF",
-            "Illuminated",
-            expected,
-            optional=True,
-            mandatoryForIteration=True,
-        )
-        AgipdCorrection.addConstant(
-            "BadPixelsDark", "Dark", expected, optional=True, mandatoryForIteration=True
-        )
-        AgipdCorrection.addConstant(
-            "BadPixelsPC", "Dark", expected, optional=True, mandatoryForIteration=True
-        )
-        AgipdCorrection.addConstant(
-            "BadPixelsFF",
-            "Illuminated",
-            expected,
-            optional=True,
-            mandatoryForIteration=True,
-        )
         super(AgipdCorrection, AgipdCorrection).expectedParameters(expected)
+        AgipdCalcatFriend.add_schema(expected)
+        # TODO: encapsulate correction configuration subschema
         for slot_name, _ in AgipdCorrection._correction_slot_names:
             (
                 BOOL_ELEMENT(expected)
@@ -129,7 +99,7 @@ class AgipdCorrection(BaseCorrection):
             .commit(),
         )
         # TODO: hook this up to actual correction done
-        # NOTE: wanted to configure this as table, could not make reaonly table with reconfigurable bools in rows
+        # NOTE: wanted as table, could not make readonly rows of reconfigurable bools
         (
             NODE_ELEMENT(expected)
             .key("corrections.badPixelFlagsToUse")
@@ -189,6 +159,8 @@ class AgipdCorrection(BaseCorrection):
         )
 
     def __init__(self, config):
+        super().__init__(config)
+        self.calibration_constant_manager = AgipdCalcatFriend(self)
         # TODO: different gpu runner for fixed gain mode
         self.gain_mode = AgipdGainMode[config.get("gainMode")]
         self.bad_pixel_mask_value = eval(config.get("corrections.badPixelMaskValue"))
@@ -197,7 +169,6 @@ class AgipdCorrection(BaseCorrection):
             "bad_pixel_mask_value": self.bad_pixel_mask_value,
         }
 
-        super().__init__(config)
         self._output_transpose = {
             "pixels-fast": None,
             "memorycells-fast": (2, 1, 0),
@@ -218,6 +189,7 @@ class AgipdCorrection(BaseCorrection):
         self._update_bad_pixel_selection()
 
         self.updateState(State.ON)
+        self.KARABO_SLOT(self.loadMostRecentConstantsWrap)
 
     def process_input(self, data, metadata):
         """Registered for dataInput, handles all processing and sending"""
@@ -272,7 +244,7 @@ class AgipdCorrection(BaseCorrection):
                 self._schema_cache["processingStateTimeout"]
             )
 
-        correction_cell_num = self._schema_cache["dataFormat.constantMemoryCells"]
+        correction_cell_num = self._schema_cache["constantParameters.memoryCells"]
         do_generate_preview = (
             train_id % self._schema_cache["preview.trainIdModulo"] == 0
             and self._schema_cache["preview.enable"]
@@ -334,15 +306,36 @@ class AgipdCorrection(BaseCorrection):
 
         # update rate etc.
         self._buffered_status_update.set("trainId", train_id)
-        self._rate_tracker.update()
+        # self._rate_tracker.update()
         time_spent = timeit.default_timer() - time_start
         self._buffered_status_update.set(
             "performance.lastProcessingDuration", time_spent * 1000
         )
 
-    def _load_constant_to_gpu(self, constant_name, constant_data):
+    def loadMostRecentConstantsWrap(self):
+        self.flush_constants()
+        for constant in AgipdConstants:
+            try:
+                metadata, data = self.calibration_constant_manager.get_constant_version(constant)
+            except Exception as e:
+                self.log.WARN(f"Failed getting {constant} with {e}")
+            else:
+                self.log.INFO(f"Retrieved something for {constant}")
+                print(data)
+                self._load_constant_to_gpu(constant, (metadata, data))
+        """
+        threads = [
+            self.calibration_constant_manager.get_constant_version_and_call_me_back(
+                constant, self._load_constant_to_gpu
+            )
+            for constant in AgipdConstants
+        ]
+        """
+
+    def _load_constant_to_gpu(self, constant, found):
         # TODO: encode correction / constant dependencies in a clever way
-        if constant_name == "ThresholdsDark":
+        metadata, constant_data = found
+        if constant is AgipdConstants.ThresholdsDark:
             if self.gain_mode is not AgipdGainMode.ADAPTIVE_GAIN:
                 self.log.INFO("Loaded ThresholdsDark ignored due to fixed gain mode")
                 return
@@ -351,13 +344,13 @@ class AgipdCorrection(BaseCorrection):
                 self.set("corrections.available.thresholding", True)
                 self.set("corrections.enabled.thresholding", True)
                 self.set("corrections.preview.thresholding", True)
-        elif constant_name == "Offset":
+        elif constant is AgipdConstants.Offset:
             self.gpu_runner.load_offset_map(constant_data)
             if not self.get("corrections.available.offset"):
                 self.set("corrections.available.offset", True)
                 self.set("corrections.enabled.offset", True)
                 self.set("corrections.preview.offset", True)
-        elif constant_name == "SlopesPC":
+        elif constant is AgipdConstants.SlopesPC:
             self.gpu_runner.load_rel_gain_pc_map(constant_data)
             if not self.get("corrections.available.relGainPc"):
                 self.set("corrections.available.relGainPc", True)
@@ -367,13 +360,13 @@ class AgipdCorrection(BaseCorrection):
                 self.gpu_runner.md_additional_offset_gpu.fill(
                     self._override_md_additional_offset
                 )
-        elif constant_name == "SlopesFF":
+        elif constant is AgipdConstants.SlopesFF:
             self.gpu_runner.load_rel_gain_ff_map(constant_data)
             if not self.get("corrections.available.relGainXray"):
                 self.set("corrections.available.relGainXray", True)
                 self.set("corrections.enabled.relGainXray", True)
                 self.set("corrections.preview.relGainXray", True)
-        elif "BadPixels" in constant_name:
+        elif "BadPixels" in constant.name:
             self.gpu_runner.load_bad_pixels_map(
                 constant_data, override_flags_to_use=self._override_bad_pixel_flags
             )
diff --git a/src/calng/CalibrationManager.py b/src/calng/CalibrationManager.py
index c5ab67cd..ce05c5a0 100644
--- a/src/calng/CalibrationManager.py
+++ b/src/calng/CalibrationManager.py
@@ -292,7 +292,6 @@ class ManagedKeysCloneFactory(ProxyFactory):
 
 class CalibrationManager(DeviceClientBase, Device):
     __version__ = deviceVersion
-    _conditions_to_set = {}
 
     interfaces = VectorString(
         displayedName='Device interfaces',
@@ -380,7 +379,13 @@ class CalibrationManager(DeviceClientBase, Device):
         displayedName='CalCat URL',
         description='[NYI] URL to CalCat API to use for constant retrieval, '
                     'set by local secrets file',
-        accessMode=AccessMode.READONLY)
+        accessMode=AccessMode.INITONLY,
+        assignment=Assignment.MANDATORY)
+
+    calcatClientId = String(
+        displayedName='calibration client ID',
+        accessMode=AccessMode.INITONLY,
+        assignment=Assignment.MANDATORY)
 
     webserverApi = Node(
         WebserverApiNode,
@@ -1083,16 +1088,6 @@ class CalibrationManager(DeviceClientBase, Device):
             ):
                 return
 
-        # get device schema sneakily
-        self.logger.debug(f'Trying to figure out which constant parameters to set later')
-        # note: this should "obviously" happen earlier, but also more generally
-        correction_device_schema, _, _ = await call(server_by_group[group], "slotGetClassSchema", class_ids["correction"])
-        self._conditions_to_set = {
-            constant_name: constant_schema.getAttribute('detector_condition', 'defaultValue')
-            for constant_name, constant_schema in correction_device_schema.hash['constants'].items()
-        }
-        self.logger.debug(str(self._conditions_to_set))
-
         # Instantiate group matchers and bridges.
         for row in self.moduleGroups.value:
             group, server, with_matcher, with_bridge, bridge_port, \
diff --git a/src/calng/DsscCorrection.py b/src/calng/DsscCorrection.py
index b1dc274a..93009796 100644
--- a/src/calng/DsscCorrection.py
+++ b/src/calng/DsscCorrection.py
@@ -7,6 +7,7 @@ from karabo.common.states import State
 from . import utils
 from ._version import version as deviceVersion
 from .base_correction import BaseCorrection
+from .calcat_utils import DsscCalcatFriend, DsscConstants
 from .dssc_gpu import DsscGpuRunner, CorrectionFlags
 
 
@@ -19,9 +20,6 @@ class DsscCorrection(BaseCorrection):
 
     @staticmethod
     def expectedParameters(expected):
-        DsscCorrection.addConstant(
-            "Offset", "Dark", expected, optional=True, mandatoryForIteration=True
-        )
         super(DsscCorrection, DsscCorrection).expectedParameters(expected)
         for slot_name, _ in DsscCorrection._correction_slot_names:
             (
diff --git a/src/calng/base_correction.py b/src/calng/base_correction.py
index 29451fca..4a6a2e3e 100644
--- a/src/calng/base_correction.py
+++ b/src/calng/base_correction.py
@@ -1,6 +1,5 @@
 import threading
 
-import calibrationBase
 import hashToSchema
 import numpy as np
 from karabo.bound import (
@@ -8,9 +7,11 @@ from karabo.bound import (
     FLOAT_ELEMENT,
     INPUT_CHANNEL,
     INT32_ELEMENT,
+    KARABO_CLASSINFO,
     NDARRAY_ELEMENT,
     NODE_ELEMENT,
     OUTPUT_CHANNEL,
+    SLOT_ELEMENT,
     STRING_ELEMENT,
     UINT32_ELEMENT,
     UINT64_ELEMENT,
@@ -20,6 +21,7 @@ from karabo.bound import (
     Epochstamp,
     Hash,
     MetricPrefix,
+    PythonDevice,
     Schema,
     Timestamp,
     Trainstamp,
@@ -28,16 +30,18 @@ from karabo.bound import (
 from karabo.common.states import State
 
 from . import shmem_utils, utils
+from ._version import version as deviceVersion
 
 
-class BaseCorrection(calibrationBase.CalibrationReceiverBaseDevice):
+@KARABO_CLASSINFO("BaseCorrection", deviceVersion)
+class BaseCorrection(PythonDevice):
     _correction_flag_class = None  # subclass must override this with some enum class
     _gpu_runner_class = None  # subclass must set this
     _gpu_runner_init_args = {}  # subclass can set this (TODO: remove, design better)
     _schema_cache_slots = {
         "doAnything",
         "dataFormat.memoryCells",
-        "dataFormat.constantMemoryCells",
+        "constantParameters.memoryCells",
         "dataFormat.pixelsX",
         "dataFormat.pixelsY",
         "preview.enable",
@@ -166,20 +170,6 @@ class BaseCorrection(calibrationBase.CalibrationReceiverBaseDevice):
             .assignmentOptional()
             .defaultValue("pixels-fast")
             .commit(),
-            UINT32_ELEMENT(expected)
-            .key("dataFormat.constantMemoryCells")
-            .displayedName("Memory cells in correction map")
-            .description(
-                "Number of memory cells in loaded or expected constants. "
-                "May exceed memory cell number in input if veto is on. "
-                "This value should be updated (will be done by manager) before "
-                "requesting constants with different number of cells. "
-                "Will in future versions get better integrated into constant loading."
-            )
-            .assignmentOptional()
-            .defaultValue(0)
-            .reconfigurable()
-            .commit(),
             VECTOR_UINT32_ELEMENT(expected)
             .key("dataFormat.inputDataShape")
             .displayedName("Input data shape")
@@ -204,6 +194,12 @@ class BaseCorrection(calibrationBase.CalibrationReceiverBaseDevice):
             .commit(),
         )
 
+        (
+            SLOT_ELEMENT(expected)
+            .key("loadMostRecentConstantsWrap")
+            .commit()
+        )
+
         preview_schema = Schema()
         (
             NODE_ELEMENT(expected).key("preview").displayedName("Preview").commit(),
@@ -399,9 +395,10 @@ class BaseCorrection(calibrationBase.CalibrationReceiverBaseDevice):
         self._shmem_buffer = None
         self._has_set_output_schema = False
         self._has_updated_shapes = False
-        self._rate_tracker = calibrationBase.utils.UpdateRate(
-            interval=config.get("performance.rateBufferSpan")
-        )
+        # TODO: replace
+        #self._rate_tracker = calibrationBase.utils.UpdateRate(
+            #interval=config.get("performance.rateBufferSpan")
+        #)
         self._state_reset_timer = None
 
         self._buffered_status_update = Hash(
@@ -429,9 +426,10 @@ class BaseCorrection(calibrationBase.CalibrationReceiverBaseDevice):
             )
 
         if config.has("performance.rateBufferSpan"):
-            self._rate_tracker = calibrationBase.utils.UpdateRate(
-                interval=config.get("performance.rateBufferSpan")
-            )
+            #self._rate_tracker = calibrationBase.utils.UpdateRate(
+                #interval=config.get("performance.rateBufferSpan")
+            #)
+            ...
 
         for path in config.getPaths():
             if path in self._schema_cache_slots:
@@ -444,7 +442,7 @@ class BaseCorrection(calibrationBase.CalibrationReceiverBaseDevice):
                 "dataFormat.pixelsX",
                 "dataFormat.pixelsY",
                 "dataFormat.memoryCells",
-                "dataFormat.constantMemoryCells",
+                "constantParameters.memoryCells",
             )
         ):
             # will make postReconfigure handle shape update after merging schema
@@ -464,30 +462,9 @@ class BaseCorrection(calibrationBase.CalibrationReceiverBaseDevice):
                 self._schema_cache[key] = value
         super().set(*args)
 
-    def requestConstant(self, name, mostRecent=False, tryRemote=True):
-        """Wrapper around method from CalibrationReceiverBaseDevice
-
-        The superclass provides the constantLoaded hook, but it gets called without
-        arguments, losing the name of the freshly loaded constant. To handle individual
-        constants correctly, we set up our own hook instead.
-        """
-        # TODO: remove when revamping constant retrieval
-        if name in self._cached_constants:
-            del self._cached_constants[name]
-        super().requestConstant(name, mostRecent, tryRemote)
-        constant = self.getConstant(name)
-        if constant is None:
-            return
-
-        # TODO: remaining constants, DRY
-        self._cached_constants[name] = constant
-        self._load_constant_to_gpu(name, constant)
-        self._update_correction_flags()
-
     def flush_constants(self):
         """Override from CalibrationReceiverBaseDevice to also flush GPU buffers"""
         # TODO: update when revamping constant retrieval
-        super().flush_constants()
         for correction_step, _ in self._correction_slot_names:
             self.set(f"corrections.available.{correction_step}", False)
         self.gpu_runner.flush_buffers()
@@ -593,7 +570,7 @@ class BaseCorrection(calibrationBase.CalibrationReceiverBaseDevice):
             self.get("dataFormat.pixelsX"),
             self.get("dataFormat.pixelsY"),
             self.get("dataFormat.memoryCells"),
-            self.get("dataFormat.constantMemoryCells"),
+            int(self.get("constantParameters.memoryCells")),
             output_transpose=self._output_transpose,
             input_data_dtype=self.input_data_dtype,
             output_data_dtype=self.output_data_dtype,
@@ -616,7 +593,7 @@ class BaseCorrection(calibrationBase.CalibrationReceiverBaseDevice):
         if not self.get("state") is State.PROCESSING:
             self._rate_update_timer.delay()
             return
-        self._buffered_status_update.set("performance.rate", self._rate_tracker.rate())
+        #self._buffered_status_update.set("performance.rate", self._rate_tracker.rate())
         last_processing = self._buffered_status_update.get(
             "performance.lastProcessingDuration"
         )
@@ -632,19 +609,3 @@ class BaseCorrection(calibrationBase.CalibrationReceiverBaseDevice):
         self._has_set_output_schema = False
         self.updateState(State.ON)
         self.signalEndOfStream("dataOutput")
-
-    def getConstant(self, name):
-        """Wrapper around getConstant to return None on failure
-
-        Full function is from CalibrationReceiverBaseDevice
-        """
-
-        # TODO: remove when revamping constant retrieval
-        const = super().getConstant(name)
-        if const is not None and len(const.shape) == 1:
-            self.log.WARN(
-                f"Constant {name} should probably be None, but is array"
-                f" of size {const.size}, shape {const.shape}"
-            )
-            const = None
-        return const
diff --git a/src/calng/calcat_utils.py b/src/calng/calcat_utils.py
index 880ed1d8..2d1997cd 100644
--- a/src/calng/calcat_utils.py
+++ b/src/calng/calcat_utils.py
@@ -17,7 +17,7 @@ from calibration_client.modules import (
     Parameter,
     PhysicalDetectorUnit,
 )
-from karabo.bound import FLOAT_ELEMENT, NODE_ELEMENT, STRING_ELEMENT, UINT32_ELEMENT
+from karabo.bound import DOUBLE_ELEMENT, NODE_ELEMENT, STRING_ELEMENT
 
 from . import utils
 
@@ -89,7 +89,7 @@ class BaseCalcatFriend:
     _constants_need_conditions = None  # subclass should set
 
     @staticmethod
-    def add_schema(schema, prefix="constantParameters"):
+    def add_schema(schema, detector_type, prefix="constantParameters"):
         """Add elements needed by this object to device's schema (expectedSchema)
 
         All elements added to schema go under prefix which should end with name of
@@ -136,30 +136,38 @@ class BaseCalcatFriend:
         (
             STRING_ELEMENT(schema)
             .key(f"{prefix}.detectorType")
-            .assignmentMandatory()
+            .readOnly()
+            .initialValue(detector_type)
             .commit(),
             STRING_ELEMENT(schema)
             .key(f"{prefix}.detectorName")
             .assignmentMandatory()
             .commit(),
-            UINT32_ELEMENT(schema)
-            .key(f"{prefix}.memoryCells")
+            STRING_ELEMENT(schema)
+            .key(f"{prefix}.karaboDa")
             .assignmentMandatory()
             .commit(),
-            UINT32_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
+            .key(f"{prefix}.memoryCells")
+            .assignmentOptional()
+            .defaultValue(352)
+            .reconfigurable()
+            .commit(),
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.pixelsX")
             .assignmentOptional()
             .defaultValue(512)
             .commit(),
-            UINT32_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.pixelsY")
             .assignmentOptional()
             .defaultValue(512)
             .commit(),
-            UINT32_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.biasVoltage")
             .assignmentOptional()
             .defaultValue(300)
+            .reconfigurable()
             .commit(),
         )
 
@@ -188,6 +196,7 @@ class BaseCalcatFriend:
             scope="public",
             session_token=None,
         )
+        self.device.log.INFO("calibration_client initialized")
 
     def _get(self, key):
         """Helper to get value from attached device schema"""
@@ -238,6 +247,7 @@ class BaseCalcatFriend:
         # modifying condition parameter messes with cache
         condition_with_detector = copy.copy(condition)
         condition_with_detector["Detector UUID"] = pdu
+        self.device.log.INFO(f"Look for condition: {condition_with_detector}")
         resp = self.client.search_possible_conditions_from_dict(
             "", condition_with_detector.encode()
         )
@@ -255,10 +265,10 @@ class BaseCalcatFriend:
         _check_resp(resp)
         return resp["data"]["id"]
 
-    def get_constant_version(self, karabo_da, constant, snapshot_at=None):
+    def get_constant_version(self, constant, snapshot_at=None):
         # TODO: support snapshot
         # TODO: support creation time
-        # TODO: move karabo_da into device config (no need to be able to get constants for other modules)
+        karabo_da = self._get("karaboDa")
         self.device.log.DEBUG(f"Going looking for {constant} for {karabo_da}")
         if karabo_da not in self._karabo_da_to_float_uuid:
             raise ModuleNotFound(f"Module map did not include {karabo_da}")
@@ -294,7 +304,7 @@ class BaseCalcatFriend:
         return timestamp, ary
 
     def get_constant_version_and_call_me_back(
-        self, karabo_da, constant, callback, snapshot_at=None
+        self, constant, callback, snapshot_at=None
     ):
         """WIP. Callback function will get constant name, constant data, and hopefully
         soon also some metadata or whatever.
@@ -302,7 +312,7 @@ class BaseCalcatFriend:
         # TODO: thread safe caching throughout this class
         # TODO: do we want to use asyncio / "modern" async?
         def aux():
-            data = self.get_constant_version(karabo_da, constant, snapshot_at)
+            data = self.get_constant_version(constant, snapshot_at)
             callback(constant, data)
 
         thread = threading.Thread(target=aux)
@@ -341,33 +351,40 @@ class AgipdCalcatFriend(BaseCalcatFriend):
 
     @staticmethod
     def add_schema(schema, prefix="constantParameters"):
-        super(AgipdCalcatFriend, AgipdCalcatFriend).add_schema(schema, prefix)
+        super(AgipdCalcatFriend, AgipdCalcatFriend).add_schema(
+            schema, "AGIPD-Type", prefix
+        )
 
         (
-            FLOAT_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.acquisitionRate")
             .assignmentOptional()
             .defaultValue(1.1)
+            .reconfigurable()
             .commit(),
-            FLOAT_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.gainSetting")
             .assignmentOptional()
             .defaultValue(0)
+            .reconfigurable()
             .commit(),
-            FLOAT_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.photonEnergy")
             .assignmentOptional()
-            .defaultValue(9.3)
+            .defaultValue(9.2)
+            .reconfigurable()
             .commit(),
-            FLOAT_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.gainMode")
             .assignmentOptional()
             .defaultValue(0)
+            .reconfigurable()
             .commit(),
-            FLOAT_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.integrationtime")
             .assignmentOptional()
             .defaultValue(12)
+            .reconfigurable()
             .commit(),
         )
 
@@ -411,19 +428,21 @@ class DsscCalcatFriend(BaseCalcatFriend):
 
     @staticmethod
     def add_schema(schema, prefix="constantParameters"):
-        super(DsscCalcatFriend, DsscCalcatFriend).add_schema(schema, prefix)
+        super(DsscCalcatFriend, DsscCalcatFriend).add_schema(
+            schema, "DSSC-Type", prefix
+        )
         (
-            FLOAT_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.pulseIdChecksum")
             .assignmentOptional()
             .defaultValue(2.8866323107820637e-36)
             .commit(),
-            FLOAT_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.acquisitionRate")
             .assignmentOptional()
             .defaultValue(4.5)
             .commit(),
-            FLOAT_ELEMENT(schema)
+            DOUBLE_ELEMENT(schema)
             .key(f"{prefix}.encodedGain")
             .assignmentOptional()
             .defaultValue(67328)
diff --git a/src/tests/test_calcat_utils.py b/src/tests/test_calcat_utils.py
index 7aaa0fb5..36f2b322 100644
--- a/src/tests/test_calcat_utils.py
+++ b/src/tests/test_calcat_utils.py
@@ -42,12 +42,12 @@ class DummyAgipdDevice:
         calcat_utils.AgipdCalcatFriend.add_schema(expected, "constantParameters")
 
     def __init__(self, config):
+        self.log = DummyLogger()
         self.schema = config
         self.calibration_constant_manager = calcat_utils.AgipdCalcatFriend(
             self,
             "constantParameters",
         )
-        self.log = DummyLogger()
 
     def get(self, key):
         return self.schema.get(key)
@@ -65,13 +65,13 @@ class DummyDsscDevice:
         calcat_utils.DsscCalcatFriend.add_schema(expected, "constantParameters")
 
     def __init__(self, config):
+        self.log = DummyLogger()
         # TODO: check config against schema (as Karabo would)
         self.schema = config
         self.calibration_constant_manager = calcat_utils.DsscCalcatFriend(
             self,
             "constantParameters",
         )
-        self.log = DummyLogger()
 
     def get(self, key):
         return self.schema.get(key)
@@ -91,6 +91,7 @@ def test_agipd_constants_and_caching_and_async():
 
     conf["constantParameters.detectorType"] = "AGIPD-Type"
     conf["constantParameters.detectorName"] = "SPB_DET_AGIPD1M-1"
+    conf["constantParameters.karaboDa"] = "AGIPD00"
     conf["constantParameters.pixelsX"] = 512
     conf["constantParameters.pixelsY"] = 128
     conf["constantParameters.memoryCells"] = 352
@@ -110,7 +111,7 @@ def test_agipd_constants_and_caching_and_async():
         threads = []
         for constant in calcat_utils.AgipdConstants:
             thread = device.calibration_constant_manager.get_constant_version_and_call_me_back(
-                "AGIPD00", constant, backcall
+                constant, backcall
             )
             threads.append(thread)
         for thread in threads:
@@ -120,7 +121,7 @@ def test_agipd_constants_and_caching_and_async():
         threads = []
         for constant in calcat_utils.AgipdConstants:
             thread = device.calibration_constant_manager.get_constant_version_and_call_me_back(
-                "AGIPD00", constant, backcall
+                constant, backcall
             )
             threads.append(thread)
         for thread in threads:
@@ -129,7 +130,6 @@ def test_agipd_constants_and_caching_and_async():
     with Stopwatch() as timer_sync_warm:
         for constant in calcat_utils.AgipdConstants:
             ts, ary = device.calibration_constant_manager.get_constant_version(
-                "AGIPD00",
                 constant,
             )
             assert ts is not None, "Some constants should be found"
@@ -154,6 +154,7 @@ def test_dssc_constants():
 
     conf["constantParameters.detectorType"] = "DSSC-Type"
     conf["constantParameters.detectorName"] = "SCS_DET_DSSC1M-1"
+    conf["constantParameters.karaboDa"] = "DSSC00"
     conf["constantParameters.memoryCells"] = 400
     conf["constantParameters.biasVoltage"] = 100
     conf["constantParameters.pixelsX"] = 512
@@ -162,8 +163,6 @@ def test_dssc_constants():
     # conf["constantParameters.acquisitionRate"] = 4.5
     # conf["constantParameters.encodedGain"] = 67328
     device = DummyDsscDevice(conf)
-    ts, offset_map = device.calibration_constant_manager.get_constant_version(
-        "DSSC00", "Offset"
-    )
+    ts, offset_map = device.calibration_constant_manager.get_constant_version("Offset")
 
     assert ts is not None
-- 
GitLab