From 93089028c65e27a3f5b1b34264cd9af257f2f6e7 Mon Sep 17 00:00:00 2001
From: David Hammer <david.hammer@xfel.eu>
Date: Tue, 7 Feb 2023 10:11:50 +0100
Subject: [PATCH] Add cell and pulse tables as properties

---
 src/calng/base_correction.py                 | 49 +++++++++++++++++---
 src/calng/corrections/AgipdCorrection.py     |  3 +-
 src/calng/corrections/DsscCorrection.py      |  2 +-
 src/calng/corrections/Epix100Correction.py   |  2 +
 src/calng/corrections/Gotthard2Correction.py |  2 +
 src/calng/corrections/JungfrauCorrection.py  |  2 +
 src/calng/corrections/LpdCorrection.py       |  2 +-
 7 files changed, 52 insertions(+), 10 deletions(-)

diff --git a/src/calng/base_correction.py b/src/calng/base_correction.py
index e2cd56cf..0073ee26 100644
--- a/src/calng/base_correction.py
+++ b/src/calng/base_correction.py
@@ -84,6 +84,7 @@ class BaseCorrection(PythonDevice):
     }  # subclass can extend this, /must/ put it in schema as managedKeys
     _image_data_path = "image.data"  # customize for *some* subclasses
     _cell_table_path = "image.cellId"
+    _pulse_table_path = "image.pulseId"
     _warn_memory_cell_range = True  # can be disabled for some detectors
     _cuda_pin_buffers = False
 
@@ -355,6 +356,18 @@ class BaseCorrection(PythonDevice):
             .initialValue([])
             .commit(),
 
+            VECTOR_UINT32_ELEMENT(expected)
+            .key("dataFormat.cellId")
+            .readOnly()
+            .initialValue([])
+            .commit(),
+
+            VECTOR_UINT32_ELEMENT(expected)
+            .key("dataFormat.pulseId")
+            .readOnly()
+            .initialValue([])
+            .commit(),
+
             NODE_ELEMENT(expected)
             .key("workarounds")
             .displayedName("Workarounds")
@@ -601,6 +614,8 @@ class BaseCorrection(PythonDevice):
         self._buffer_lock = threading.Lock()
         self._last_processing_started = 0  # used for processing time and timeout
         self._last_train_id_processed = 0  # used to keep track (and as fallback)
+        self._last_cell_table = None  # used to check whether to update property
+        self._last_pulse_table = None  # ditto - though not all detectors have this
 
         # register slots
         def constant_override_fun(friend_fun, constant, preserve_fields):
@@ -1098,17 +1113,22 @@ class BaseCorrection(PythonDevice):
             ) as warn:
                 try:
                     image_data = np.asarray(data_hash.get(self._image_data_path))
-                    if self._cell_table_path is None:
-                        cell_table = None
-                    else:
-                        cell_table = np.asarray(
-                            data_hash.get(self._cell_table_path)
-                        ).ravel()
+                    cell_table = (
+                        np.asarray(data_hash.get(self._cell_table_path)).ravel()
+                        if self._cell_table_path is not None
+                        else None
+                    )
+                    pulse_table = (
+                        np.asarray(data_hash.get(self._pulse_table_path)).ravel()
+                        if self._pulse_table_path is not None
+                        else None
+                    )
                 except RuntimeError as err:
                     warn(
                         "Failed to load image data; "
                         f"probably empty hash from DAQ: {err}"
                     )
+                    self._maybe_update_cell_and_pulse_tables(None, None)
                     continue
 
             # no more common reasons to skip input, so go to processing
@@ -1117,6 +1137,7 @@ class BaseCorrection(PythonDevice):
                 self.updateState(State.PROCESSING)
                 self.log_status_info("Processing data")
 
+            self._maybe_update_cell_and_pulse_tables(cell_table, pulse_table)
             timestamp = Timestamp.fromHashAttributes(metadata.getAttributes("timestamp"))
             train_id = timestamp.getTrainId()
 
@@ -1206,6 +1227,7 @@ class BaseCorrection(PythonDevice):
                     train_id,
                     image_data,
                     cell_table,
+                    pulse_table,
                 )
             self._last_train_id_processed = train_id
             self._buffered_status_update.set("trainId", train_id)
@@ -1235,6 +1257,21 @@ class BaseCorrection(PythonDevice):
                     f"No new train in {PROCESSING_STATE_TIMEOUT} s, switching state."
                 )
                 self._train_ratio_tracker.reset()
+                self._maybe_update_cell_and_pulse_tables(None, None)
+
+    def _maybe_update_cell_and_pulse_tables(self, cell_table, pulse_table):
+        if not np.array_equal(cell_table, self._last_cell_table):
+            self._last_cell_table = cell_table
+            if cell_table is None:
+                self.set("dataFormat.cellId", [])
+            else:
+                self.set("dataFormat.cellId", list(map(int, cell_table)))
+        if not np.array_equal(pulse_table, self._last_pulse_table):
+            self._last_pulse_table = pulse_table
+            if pulse_table is None:
+                self.set("dataFormat.pulseId", [])
+            else:
+                self.set("dataFormat.pulseId", list(map(int, pulse_table)))
 
     def handle_eos(self, channel):
         self.updateState(State.ON)
diff --git a/src/calng/corrections/AgipdCorrection.py b/src/calng/corrections/AgipdCorrection.py
index 7857c8ed..7d0803b5 100644
--- a/src/calng/corrections/AgipdCorrection.py
+++ b/src/calng/corrections/AgipdCorrection.py
@@ -737,12 +737,11 @@ class AgipdCorrection(base_correction.BaseCorrection):
         train_id,
         image_data,
         cell_table,
+        pulse_table,
     ):
         """Called by input_handler for each data hash. Should correct data, optionally
         compute preview, write data output, and optionally write preview outputs."""
         # original shape: frame, data/raw_gain, x, y
-
-        pulse_table = data_hash.get("image.pulseId").ravel()
         if self._frame_filter is not None:
             try:
                 cell_table = cell_table[self._frame_filter]
diff --git a/src/calng/corrections/DsscCorrection.py b/src/calng/corrections/DsscCorrection.py
index 2ee61b84..3ae2ec2a 100644
--- a/src/calng/corrections/DsscCorrection.py
+++ b/src/calng/corrections/DsscCorrection.py
@@ -275,8 +275,8 @@ class DsscCorrection(base_correction.BaseCorrection):
         train_id,
         image_data,
         cell_table,
+        pulse_table,
     ):
-        pulse_table = np.ravel(data_hash.get("image.pulseId"))
         if self._frame_filter is not None:
             try:
                 cell_table = cell_table[self._frame_filter]
diff --git a/src/calng/corrections/Epix100Correction.py b/src/calng/corrections/Epix100Correction.py
index 6705142c..2a052e58 100644
--- a/src/calng/corrections/Epix100Correction.py
+++ b/src/calng/corrections/Epix100Correction.py
@@ -307,6 +307,7 @@ class Epix100Correction(base_correction.BaseCorrection):
     _managed_keys = base_correction.BaseCorrection._managed_keys.copy()
     _image_data_path = "data.image.pixels"
     _cell_table_path = None
+    _pulse_table_path = None
     _warn_memory_cell_range = False
 
     @staticmethod
@@ -416,6 +417,7 @@ class Epix100Correction(base_correction.BaseCorrection):
         train_id,
         image_data,
         cell_table,  # will be None
+        pulse_table,  # ditto
     ):
         self.kernel_runner.load_data(image_data)
 
diff --git a/src/calng/corrections/Gotthard2Correction.py b/src/calng/corrections/Gotthard2Correction.py
index 5043e9b6..c07efd58 100644
--- a/src/calng/corrections/Gotthard2Correction.py
+++ b/src/calng/corrections/Gotthard2Correction.py
@@ -258,6 +258,7 @@ class Gotthard2Correction(base_correction.BaseCorrection):
     _managed_keys = base_correction.BaseCorrection._managed_keys.copy()
     _image_data_path = "data.adc"
     _cell_table_path = "data.memoryCell"
+    _pulse_table_path = None
     _warn_memory_cell_range = False  # for now, receiver always writes 255
     _cuda_pin_buffers = False
 
@@ -410,6 +411,7 @@ class Gotthard2Correction(base_correction.BaseCorrection):
         train_id,
         image_data,
         cell_table,
+        pulse_table,
     ):
         # cell table currently not used for GOTTHARD2 (assume alternating)
         gain_map = np.asarray(data_hash.get("data.gain"))
diff --git a/src/calng/corrections/JungfrauCorrection.py b/src/calng/corrections/JungfrauCorrection.py
index 79314a48..9d3234b7 100644
--- a/src/calng/corrections/JungfrauCorrection.py
+++ b/src/calng/corrections/JungfrauCorrection.py
@@ -448,6 +448,7 @@ class JungfrauCorrection(base_correction.BaseCorrection):
     _managed_keys = base_correction.BaseCorrection._managed_keys.copy()
     _image_data_path = "data.adc"
     _cell_table_path = "data.memoryCell"
+    _pulse_table_path = None
 
     @staticmethod
     def expectedParameters(expected):
@@ -626,6 +627,7 @@ class JungfrauCorrection(base_correction.BaseCorrection):
         train_id,
         image_data,
         cell_table,
+        pulse_table,
     ):
         if len(cell_table.shape) == 0:
             cell_table = cell_table[np.newaxis]
diff --git a/src/calng/corrections/LpdCorrection.py b/src/calng/corrections/LpdCorrection.py
index 1e8ab80b..ffae49dd 100644
--- a/src/calng/corrections/LpdCorrection.py
+++ b/src/calng/corrections/LpdCorrection.py
@@ -423,8 +423,8 @@ class LpdCorrection(BaseCorrection):
         train_id,
         image_data,
         cell_table,
+        pulse_table,
     ):
-        pulse_table = np.ravel(data_hash.get("image.pulseId"))
         if self._frame_filter is not None:
             try:
                 cell_table = cell_table[self._frame_filter]
-- 
GitLab