From 8620be2481f8a21b0864b49228332a9dda0f2efa Mon Sep 17 00:00:00 2001
From: David Hammer <dhammer@mailbox.org>
Date: Tue, 5 Oct 2021 17:02:40 +0200
Subject: [PATCH] Start adding scenes

---
 src/calng/agipd_gpu.py       |   6 --
 src/calng/base_correction.py |  39 ++++++--
 src/calng/base_gpu.py        |   7 +-
 src/calng/calcat_utils.py    |  12 +--
 src/calng/scenes.py          | 171 +++++++++++++++++++++++++++++++++++
 5 files changed, 211 insertions(+), 24 deletions(-)
 create mode 100644 src/calng/scenes.py

diff --git a/src/calng/agipd_gpu.py b/src/calng/agipd_gpu.py
index b8fbc6c7..b2914013 100644
--- a/src/calng/agipd_gpu.py
+++ b/src/calng/agipd_gpu.py
@@ -202,15 +202,9 @@ class AgipdGpuRunner(base_gpu.BaseGpuRunner):
 
         if override_flags_to_use is not None:
             self.override_bad_pixel_flags_to_use(override_flags_to_use)
-        print(
-            f"Percentage of bad pixels now: {cupy.count_nonzero(self.bad_pixel_map_gpu) / self.bad_pixel_map_gpu.size * 100:.02f}"
-        )
 
     def override_bad_pixel_flags_to_use(self, override_value):
         self.bad_pixel_map_gpu &= cupy.uint32(override_value)
-        print(
-            f"Percentage of bad pixels now: {cupy.count_nonzero(self.bad_pixel_map_gpu) / self.bad_pixel_map_gpu.size * 100:.02f}"
-        )
 
     def set_bad_pixel_mask_value(self, mask_value):
         self.bad_pixel_mask_value = cupy.float32(mask_value)
diff --git a/src/calng/base_correction.py b/src/calng/base_correction.py
index fc6b5005..14632431 100644
--- a/src/calng/base_correction.py
+++ b/src/calng/base_correction.py
@@ -27,9 +27,10 @@ from karabo.bound import (
     Trainstamp,
     Unit,
 )
+from karabo.common.api import KARABO_SCHEMA_DISPLAY_TYPE_SCENES as DT_SCENES
 from karabo.common.states import State
 
-from . import shmem_utils, utils
+from . import scenes, shmem_utils, utils
 from ._version import version as deviceVersion
 
 
@@ -113,6 +114,12 @@ class BaseCorrection(PythonDevice):
             .assignmentOptional()
             .defaultValue(10)
             .commit(),
+            VECTOR_STRING_ELEMENT(expected)
+            .key("availableScenes")
+            .setSpecialDisplayType(DT_SCENES)
+            .readOnly()
+            .initialValue(["overview", "constants"])
+            .commit(),
         )
 
         (
@@ -198,11 +205,7 @@ class BaseCorrection(PythonDevice):
             .commit(),
         )
 
-        (
-            SLOT_ELEMENT(expected)
-            .key("loadMostRecentConstantsWrap")
-            .commit()
-        )
+        (SLOT_ELEMENT(expected).key("loadMostRecentConstantsWrap").commit())
 
         preview_schema = Schema()
         (
@@ -420,6 +423,7 @@ class BaseCorrection(PythonDevice):
             callback=self._update_actual_rate,
         )
         self._buffer_lock = threading.Lock()
+        self.KARABO_SLOT(self.requestScene)
 
     def preReconfigure(self, config):
         if config.has("performance.rateUpdateInterval"):
@@ -474,6 +478,29 @@ class BaseCorrection(PythonDevice):
         self.gpu_runner.flush_buffers()
         self._update_correction_flags()
 
+    def requestScene(self, params):
+        payload = Hash()
+        scene_name = params.get("name", default="")
+        payload["name"] = scene_name
+        payload["success"] = True
+        if scene_name == "overview":
+            payload["data"] = scenes.correction_device_overview_scene(
+                device_id=self.getInstanceId()
+            )
+        elif scene_name == "constants":
+            payload["data"] = scenes.correction_device_constants_scene(
+                device_id=self.getInstanceId(),
+                node_prefix="constantParameters",
+                schema=self.getFullSchema(),
+            )
+        else:
+            payload["success"] = False
+        response = Hash()
+        response["type"] = "deviceScene"
+        response["origin"] = self.getInstanceId()
+        response["payload"] = payload
+        self.reply(response)
+
     def _write_output(self, data, old_metadata):
         metadata = ChannelMetaData(
             old_metadata.get("source"),
diff --git a/src/calng/base_gpu.py b/src/calng/base_gpu.py
index 1ee0a49d..1b1e9bc1 100644
--- a/src/calng/base_gpu.py
+++ b/src/calng/base_gpu.py
@@ -1,7 +1,6 @@
 import pathlib
 
 import cupy
-import cupyx
 import jinja2
 import numpy as np
 
@@ -71,10 +70,8 @@ class BaseGpuRunner:
             self.processed_shape, dtype=output_data_dtype
         )
         self.reshaped_data_gpu = cupy.empty(self.output_shape, dtype=output_data_dtype)
-        self.preview_raw = cupyx.empty_pinned(self.preview_shape, dtype=np.float32)
-        self.preview_corrected = cupyx.empty_pinned(
-            self.preview_shape, dtype=np.float32
-        )
+        self.preview_raw = np.empty(self.preview_shape, dtype=np.float32)
+        self.preview_corrected = np.empty(self.preview_shape, dtype=np.float32)
 
     # functions to get data from respective buffers to cell, x, y shape for preview computation
     # TODO: handle shape juggling programmatically, removing need for these two helpers
diff --git a/src/calng/calcat_utils.py b/src/calng/calcat_utils.py
index 93040855..a24fea6e 100644
--- a/src/calng/calcat_utils.py
+++ b/src/calng/calcat_utils.py
@@ -18,7 +18,7 @@ from calibration_client.modules import (
     Parameter,
     PhysicalDetectorUnit,
 )
-from karabo.bound import DOUBLE_ELEMENT, NODE_ELEMENT, STRING_ELEMENT
+from karabo.bound import DOUBLE_ELEMENT, NODE_ELEMENT, STRING_ELEMENT, UINT32_ELEMENT
 
 from . import utils
 
@@ -102,9 +102,7 @@ class BaseCalcatFriend:
         AgipdCalcatFriend.dark_condition.
         """
 
-        (
-            NODE_ELEMENT(schema).key(prefix).commit(),
-        )
+        (NODE_ELEMENT(schema).key(prefix).commit(),)
 
         # Parameters which any detector would probably have (extend this in subclass)
         # TODO: probably switch to floating point for everything, including mem cells
@@ -128,18 +126,18 @@ class BaseCalcatFriend:
             .assignmentOptional()
             .defaultValue("")
             .commit(),
-            DOUBLE_ELEMENT(schema)
+            UINT32_ELEMENT(schema)
             .key(f"{prefix}.memoryCells")
             .assignmentOptional()
             .defaultValue(352)
             .reconfigurable()
             .commit(),
-            DOUBLE_ELEMENT(schema)
+            UINT32_ELEMENT(schema)
             .key(f"{prefix}.pixelsX")
             .assignmentOptional()
             .defaultValue(512)
             .commit(),
-            DOUBLE_ELEMENT(schema)
+            UINT32_ELEMENT(schema)
             .key(f"{prefix}.pixelsY")
             .assignmentOptional()
             .defaultValue(512)
diff --git a/src/calng/scenes.py b/src/calng/scenes.py
new file mode 100644
index 00000000..f12ab19e
--- /dev/null
+++ b/src/calng/scenes.py
@@ -0,0 +1,171 @@
+from karabo.bound import Types
+from karabo.common.scenemodel.api import (
+    DisplayLabelModel,
+    DisplayStateColorModel,
+    DisplayTextLogModel,
+    DoubleLineEditModel,
+    IntLineEditModel,
+    LabelModel,
+    SceneModel,
+    write_scene,
+)
+
+BASE_INC = 30
+PADDING = 10
+RECONFIGURABLE = 4  # TODO: look up proper enum
+
+
+def correction_device_constants_scene(device_id, node_prefix, schema):
+    # TODO: are there layout models in scene model somewhere? like gridbox?
+    subscenes = []
+    max_height = 0
+    # first column: parameters
+    row_offset = PADDING
+    col_offset = PADDING
+    subscenes.append(
+        LabelModel(
+            text="Parameters used for CalCat queries",
+            width=12 * BASE_INC,
+            x=col_offset,
+            y=row_offset,
+        )
+    )
+    row_offset += BASE_INC
+    for key in schema.getKeys(node_prefix):
+        key_path = f"{node_prefix}.{key}"
+        subscenes.append(
+            LabelModel(
+                text=schema.getDisplayedName(key_path)
+                if schema.hasDisplayedName(key_path)
+                else key,
+                width=5 * BASE_INC,
+                height=BASE_INC,
+                x=col_offset,
+                y=row_offset,
+            )
+        )
+        subscenes.append(
+            DisplayLabelModel(
+                keys=[f"{device_id}.{node_prefix}.{key}"],
+                width=7 * BASE_INC,
+                height=BASE_INC,
+                x=col_offset + 5 * BASE_INC,
+                y=row_offset,
+            )
+        )
+        if schema.getAccessMode(key_path) == RECONFIGURABLE:
+            value_type = schema.getValueType(key_path)
+            if value_type in (Types.DOUBLE, Types.FLOAT):
+                subscenes.append(
+                    DoubleLineEditModel(
+                        keys=[f"{device_id}.{node_prefix}.{key}"],
+                        width=7 * BASE_INC,
+                        height=BASE_INC,
+                        x=col_offset + 12 * BASE_INC,
+                        y=row_offset,
+                    )
+                )
+            elif value_type in (Types.INT32, Types.UINT32, Types.INT64, Types.UINT64):
+                subscenes.append(
+                    IntLineEditModel(
+                        keys=[f"{device_id}.{node_prefix}.{key}"],
+                        width=7 * BASE_INC,
+                        height=BASE_INC,
+                        x=col_offset + 12 * BASE_INC,
+                        y=row_offset,
+                    )
+                )
+            else:
+                subscenes.append(
+                    LabelModel(
+                        text=f"Not yet supported: editing {key} of type {value_type}",
+                        width=7 * BASE_INC,
+                        height=BASE_INC,
+                        x=col_offset + 12 * BASE_INC,
+                        y=row_offset,
+                    )
+                )
+
+        row_offset += BASE_INC
+    max_height = max(max_height, row_offset)
+
+    # second column: constants as currently loaded
+    row_offset = PADDING
+    col_offset += 19 * BASE_INC  # width of first column
+    col_offset += PADDING  # and padding
+    subscenes.append(
+        LabelModel(
+            text="Constants found and loaded",
+            width=10 * BASE_INC,
+            x=col_offset,
+            y=row_offset,
+        )
+    )
+    row_offset += BASE_INC
+    max_height = max(max_height, row_offset)
+
+    scene = SceneModel(
+        height=max_height + 2 * PADDING,
+        width=col_offset + 10 * BASE_INC + PADDING,
+        children=subscenes,
+    )
+    return write_scene(scene)
+
+
+def correction_device_overview_scene(device_id):
+    subscenes = []
+    row_offset = 0
+    # device class and name
+    subscenes.append(
+        LabelModel(
+            text="Correction device",
+            width=5 * BASE_INC,
+            height=BASE_INC,
+            x=0,
+            y=row_offset,
+        )
+    )
+    subscenes.append(
+        DisplayLabelModel(
+            keys=[f"{device_id}.deviceId"],
+            width=10 * BASE_INC,
+            height=BASE_INC,
+            x=5 * BASE_INC,
+            y=row_offset,
+        )
+    )
+    row_offset += BASE_INC
+    # device state
+    subscenes.append(
+        DisplayStateColorModel(
+            keys=[f"{device_id}.state"],
+            width=5 * BASE_INC,
+            height=BASE_INC,
+            # parent_component="DisplayComponent",
+            x=0,
+            y=row_offset,
+        )
+    )
+    subscenes.append(
+        DisplayLabelModel(
+            keys=[f"{device_id}.state"],
+            width=5 * BASE_INC,
+            height=BASE_INC,
+            x=0,
+            y=row_offset,
+        )
+    )
+    row_offset += BASE_INC
+    # log of status
+    subscenes.append(
+        DisplayTextLogModel(
+            keys=[f"{device_id}.status"],
+            width=10 * BASE_INC,
+            height=10 * BASE_INC,
+            x=0,
+            y=row_offset,
+        )
+    )
+    row_offset += BASE_INC * 10
+    scene = SceneModel(height=845.0, width=742.0, children=subscenes)
+    return write_scene(scene)
-- 
GitLab