From d3bfcf1a51d39f6eba14bc2456dd96c3fa021c03 Mon Sep 17 00:00:00 2001
From: David Hammer <david.hammer@xfel.eu>
Date: Tue, 7 Feb 2023 13:16:16 +0100
Subject: [PATCH] LPD memory cell constant parameter and condition checker

---
 setup.py                                    |  1 +
 src/calng/base_correction.py                |  1 +
 src/calng/conditions/LpdCondition.py        | 31 +++++++++++++
 src/calng/corrections/JungfrauCorrection.py | 49 ++++++++-------------
 src/calng/corrections/LpdCorrection.py      | 45 +++++++++++++++++--
 src/calng/utils.py                          |  4 ++
 6 files changed, 97 insertions(+), 34 deletions(-)
 create mode 100644 src/calng/conditions/LpdCondition.py

diff --git a/setup.py b/setup.py
index 12f4ff59..cd8c3851 100644
--- a/setup.py
+++ b/setup.py
@@ -40,6 +40,7 @@ setup(name='calng',
               'CalibrationManager = calng.CalibrationManager:CalibrationManager',
               'AgipdCondition = calng.conditions:AgipdCondition.AgipdCondition',
               'JungfrauCondition = calng.conditions.JungfrauCondition:JungfrauCondition',
+              'LpdCondition = calng.conditions.LpdCondition:LpdCondition',
               'Agipd1MGeometry = calng.geometries.Agipd1MGeometry:Agipd1MGeometry',
               'Dssc1MGeometry = calng.geometries:Dssc1MGeometry.Dssc1MGeometry',
               'Epix100Geometry = calng.geometries:Epix100Geometry.Epix100Geometry',
diff --git a/src/calng/base_correction.py b/src/calng/base_correction.py
index 0073ee26..0ace7697 100644
--- a/src/calng/base_correction.py
+++ b/src/calng/base_correction.py
@@ -50,6 +50,7 @@ class FramefilterSpecType(enum.Enum):
 class WarningLampType(enum.Enum):
     FRAME_FILTER = enum.auto()
     MEMORY_CELL_RANGE = enum.auto()
+    CONSTANT_OPERATING_PARAMETERS = enum.auto()
     PREVIEW_SETTINGS = enum.auto()
     CORRECTION_RUNNER = enum.auto()
     OUTPUT_BUFFER = enum.auto()
diff --git a/src/calng/conditions/LpdCondition.py b/src/calng/conditions/LpdCondition.py
new file mode 100644
index 00000000..6d467fd1
--- /dev/null
+++ b/src/calng/conditions/LpdCondition.py
@@ -0,0 +1,31 @@
+from karabo.middlelayer import (
+    AccessMode,
+    Assignment,
+    Slot,
+    State,
+    String,
+    getDevice,
+    get_array_data,
+    sleep,
+)
+from ..corrections import LpdCorrection
+from .. import base_condition, utils
+
+
+class LpdCondition(base_condition.ConditionBase):
+    someCorrectionDeviceId = String(
+        displayedName="Correction device",
+        description="Correction devices expose the cell table as a property. Specify "
+        "the name of one of the correction devices in the pipeline to allow monitoring "
+        "of the memoryCellOrder parameter.",
+        assignment=Assignment.MANDATORY,
+        accessMode=AccessMode.INITONLY,
+    )
+
+    @property
+    def keys_to_get(self):
+        return {
+            self.someCorrectionDeviceId.value: [
+                ("dataFormat.cellId", "memoryCellOrder", utils.cell_table_to_string)
+            ]
+        }
diff --git a/src/calng/corrections/JungfrauCorrection.py b/src/calng/corrections/JungfrauCorrection.py
index 9d3234b7..e25add50 100644
--- a/src/calng/corrections/JungfrauCorrection.py
+++ b/src/calng/corrections/JungfrauCorrection.py
@@ -34,6 +34,12 @@ class Constants(enum.Enum):
     RelativeGain10Hz = enum.auto()
 
 
+bad_pixel_constants = {
+    Constants.BadPixelsDark10Hz,
+    Constants.BadPixelsFF10Hz,
+}
+
+
 # from pycalibration (TOOD: move to common shared lib)
 class GainModes(enum.Enum):
     DYNAMIC_GAIN = "dynamicgain"
@@ -119,12 +125,10 @@ class JungfrauBaseRunner(base_kernel_runner.BaseKernelRunner):
             self.offset_map[:] = self._xp.asarray(constant_data, dtype=np.float32)
         elif constant is Constants.RelativeGain10Hz:
             self.rel_gain_map[:] = self._xp.asarray(constant_data, dtype=np.float32)
-        elif constant in (
-            Constants.BadPixelsDark10Hz,
-            Constants.BadPixelsFF10Hz,
-        ):
+        elif constant in bad_pixel_constants:
             self.bad_pixel_map |= self._xp.asarray(constant_data, dtype=np.uint32)
-            self.override_bad_pixel_flags_to_use(self._override_bad_pixel_flags)
+        else:
+            raise ValueError(f"Unexpected constant type {constant}")
 
     @property
     def preview_data_views(self):
@@ -698,6 +702,8 @@ class JungfrauCorrection(base_correction.BaseCorrection):
             )
 
     def _load_constant_to_runner(self, constant, constant_data):
+        if constant in bad_pixel_constants:
+            constant_data &= self._override_bad_pixel_flags
         self.kernel_runner.load_constant(constant, constant_data)
 
     def postReconfigure(self):
@@ -713,31 +719,14 @@ class JungfrauCorrection(base_correction.BaseCorrection):
 
         if update.has("corrections.badPixels.subsetToUse"):
             self.log_status_info("Updating bad pixel maps based on subset specified")
-            if any(
-                update.get(
-                    f"corrections.badPixels.subsetToUse.{field.name}", default=False
-                )
-                for field in utils.BadPixelValues
-            ):
-                self.log_status_info(
-                    "Some fields reenabled, reloading cached bad pixel constants"
-                )
-                with self.calcat_friend.cached_constants_lock:
-                    self.kernel_runner.flush_buffers(
-                        {
-                            Constants.BadPixelsDark10Hz,
-                            Constants.BadPixelsFF10Hz,
-                        }
-                    )
-                    for (
-                        constant,
-                        data,
-                    ) in self.calcat_friend.cached_constants.items():
-                        if "BadPixels" in constant.name:
-                            self._load_constant_to_runner(constant, data)
-            self.kernel_runner.override_bad_pixel_flags_to_use(
-                self._override_bad_pixel_flags
-            )
+            # note: now just always reloading from cache for convenience
+            with self.calcat_friend.cached_constants_lock:
+                self.kernel_runner.flush_buffers(bad_pixel_constants)
+                for constant in bad_pixel_constants:
+                    if constant in self.calcat_friend.cached_constants:
+                        self._load_constant_to_runner(
+                            constant, self.calcat_friend.cached_constants[constant]
+                        )
 
         if self._preview_friend is not None:
             self._preview_friend.reconfigure(update)
diff --git a/src/calng/corrections/LpdCorrection.py b/src/calng/corrections/LpdCorrection.py
index ffae49dd..31c25ca0 100644
--- a/src/calng/corrections/LpdCorrection.py
+++ b/src/calng/corrections/LpdCorrection.py
@@ -2,6 +2,7 @@ import enum
 
 import numpy as np
 from karabo.bound import (
+    BOOL_ELEMENT,
     DOUBLE_ELEMENT,
     KARABO_CLASSINFO,
     OUTPUT_CHANNEL,
@@ -188,8 +189,8 @@ class LpdCalcatFriend(base_calcat.BaseCalcatFriend):
     @property
     def _constants_need_conditions(self):
         return {
-            Constants.Offset: self.dark_condition,
-            Constants.BadPixelsDark: self.dark_condition,
+            Constants.Offset: self.with_cell_condition,
+            Constants.BadPixelsDark: self.with_cell_condition,
             Constants.GainAmpMap: self.category_condition,
             Constants.FFMap: self.category_condition,
             Constants.RelativeGain: self.category_condition,
@@ -249,14 +250,32 @@ class LpdCalcatFriend(base_calcat.BaseCalcatFriend):
             .defaultValue(0)
             .reconfigurable()
             .commit(),
+
+            BOOL_ELEMENT(schema)
+            .key("constantParameters.useMemoryCellOrder")
+            .displayedName("Use memory cell order parameter")
+            .assignmentOptional()
+            .defaultValue(True)
+            .reconfigurable()
+            .commit(),
+
+            STRING_ELEMENT(schema)
+            .key("constantParameters.memoryCellOrder")
+            .displayedName("Memory cell order")
+            .assignmentOptional()
+            .defaultValue("")
+            .reconfigurable()
+            .commit(),
         )
+        managed_keys.add("constantParameters.useMemoryCellOrder")
+        managed_keys.add("constantParameters.memoryCellOrder")
         managed_keys.add("constantParameters.feedbackCapacitor")
         managed_keys.add("constantParameters.photonEnergy")
         managed_keys.add("constantParameters.category")
 
         base_calcat.add_status_schema_from_enum(schema, Constants)
 
-    def dark_condition(self):
+    def basic_condition(self):
         res = base_calcat.OperatingConditions()
         res["Memory cells"] = self._get_param("memoryCells")
         res["Sensor Bias Voltage"] = self._get_param("biasVoltage")
@@ -266,8 +285,15 @@ class LpdCalcatFriend(base_calcat.BaseCalcatFriend):
 
         return res
 
+    def with_cell_condition(self):
+        res = self.basic_condition()
+        if self._get_param("useMemoryCellOrder"):
+            res["Memory cell order"] = self._get_param("memoryCellOrder")
+
+        return res
+
     def illuminated_condition(self):
-        res = self.dark_condition()
+        res = self.basic_condition()
         res["Source Energy"] = self._get_param("photonEnergy")
         return res
 
@@ -425,6 +451,17 @@ class LpdCorrection(BaseCorrection):
         cell_table,
         pulse_table,
     ):
+        with self.warning_context(
+            "processingState", WarningLampType.CONSTANT_OPERATING_PARAMETERS
+        ) as warn:
+            if (
+                cell_table_string := utils.cell_table_to_string(cell_table)
+            ) != self.unsafe_get(
+                "constantParameters.memoryCellOrder"
+            ) and self.unsafe_get(
+                "constantParameters.useMemoryCellOrder"
+            ):
+                warn(f"Cell order does not match input; input: {cell_table_string}")
         if self._frame_filter is not None:
             try:
                 cell_table = cell_table[self._frame_filter]
diff --git a/src/calng/utils.py b/src/calng/utils.py
index 464a1958..7c48ebf3 100644
--- a/src/calng/utils.py
+++ b/src/calng/utils.py
@@ -554,3 +554,7 @@ def quadrant_views(A):
     for row in np.vsplit(A, 2):
         res.extend(np.hsplit(row, 2))
     return res
+
+
+def cell_table_to_string(cell_table):
+    return ",".join(map(str, cell_table.ravel())) + ","
-- 
GitLab