From 9e192a9a6f09bfcd9ebeae6b9f15d1633f1c85d5 Mon Sep 17 00:00:00 2001
From: David Hammer <dhammer@mailbox.org>
Date: Sat, 5 Feb 2022 19:16:06 +0100
Subject: [PATCH] Add preview scene for SimpleAssembler

---
 src/calng/SimpleAssembler.py |  15 ++---
 src/calng/scenes.py          | 112 +++++++++++++++++++++++++++++++++--
 2 files changed, 110 insertions(+), 17 deletions(-)

diff --git a/src/calng/SimpleAssembler.py b/src/calng/SimpleAssembler.py
index f9df6a2a..847265f6 100644
--- a/src/calng/SimpleAssembler.py
+++ b/src/calng/SimpleAssembler.py
@@ -53,7 +53,7 @@ class SimpleAssembler(TrainMatcher.TrainMatcher):
         (
             OVERWRITE_ELEMENT(expected)
             .key("availableScenes")
-            .setNewDefaultValue(["scene", "assemblerOverview"])
+            .setNewDefaultValue(["overview", "trainMatcherScene"])
             .commit(),
 
             FLOAT_ELEMENT(expected)
@@ -135,7 +135,7 @@ class SimpleAssembler(TrainMatcher.TrainMatcher):
     def requestScene(self, params):
         # TODO: unify with TrainMatcher overview
         scene_name = params.get("name", default="")
-        if scene_name == "assemblerOverview":
+        if scene_name == "overview":
             payload = Hash("name", scene_name, "success", True)
             payload["data"] = scenes.simple_assembler_overview(
                 device_id=self.getInstanceId(),
@@ -153,7 +153,8 @@ class SimpleAssembler(TrainMatcher.TrainMatcher):
                     payload,
                 )
             )
-        else:
+        elif scene_name == "trainMatcherScene":
+            params["name"] = "scene"
             return super().requestScene(params)
 
     def receive_geometry(self, data, metadata):
@@ -238,14 +239,6 @@ class SimpleAssembler(TrainMatcher.TrainMatcher):
         channel.update()
         self.rate_out.update()
 
-    def _update_rate(self):
-        self._buffered_status_update.set(
-            "processingTime", self._processing_time_ema.get()
-        )
-        self._buffered_status_update.set("timeOfFlight", self._time_of_flight_ema.get())
-        self._buffered_status_update.set("rate", self._rate_tracker.get())
-        self.set(self._buffered_status_update)
-
     @functools.lru_cache()
     def _source_to_index(self, source):
         # note: cache means warning only shows up once (also not performance-critical)
diff --git a/src/calng/scenes.py b/src/calng/scenes.py
index 1373e022..d59dd95e 100644
--- a/src/calng/scenes.py
+++ b/src/calng/scenes.py
@@ -5,6 +5,7 @@ from karabo.common.scenemodel.api import (
     CheckBoxModel,
     ColorBoolModel,
     DeviceSceneLinkModel,
+    DetectorGraphModel,
     DisplayCommandModel,
     DisplayLabelModel,
     DisplayStateColorModel,
@@ -20,8 +21,10 @@ from karabo.common.scenemodel.api import (
     write_scene,
 )
 
+
 # section: common setup
 
+
 BASE_INC = 25
 NARROW_INC = 20
 PADDING = 5
@@ -115,6 +118,11 @@ def boxed(component_class):
 # section: useful layout and utility classes
 
 
+def DisplayRoundedFloat(*args, decimals=2, **kwargs):
+    # note: naive subclass breaks as registry looks for writer based on exact class
+    return EvaluatorModel(*args, expression=f"f'{{x:{decimals}}}'", **kwargs)
+
+
 class Space:
     def __init__(self, width, height):
         self.width = width
@@ -124,6 +132,27 @@ class Space:
         return []
 
 
+def dummy_wrap(model_class):
+    class Wrapper:
+        def __init__(self, *args, **kwargs):
+            self.thing = model_class(*args, **kwargs)
+
+        def render(self, x, y):
+            self.thing.x = x
+            self.thing.y = y
+            return [self.thing]
+
+        @property
+        def width(self):
+            return self.thing.width
+
+        @property
+        def height(self):
+            return self.thing.height
+
+    return Wrapper
+
+
 class HorizontalLayout:
     def __init__(self, *arg_children, children=None, padding=PADDING):
         self.children = list(arg_children)
@@ -374,14 +403,12 @@ class CorrectionDeviceStatus(VerticalLayout):
             width=7 * BASE_INC,
             height=BASE_INC,
         )
-        rate = EvaluatorModel(
-            expression="f'{x:.02f}'",
+        rate = DisplayRoundedFloat(
             keys=[f"{device_id}.performance.rate"],
             width=7 * BASE_INC,
             height=BASE_INC,
         )
-        processing_time = EvaluatorModel(
-            expression="f'{x:.02f}'",
+        processing_time = DisplayRoundedFloat(
             keys=[f"{device_id}.performance.processingTime"],
             width=7 * BASE_INC,
             height=BASE_INC,
@@ -433,8 +460,7 @@ class CompactCorrectionDeviceOverview(HorizontalLayout):
                     width=6 * BASE_INC,
                     height=BASE_INC,
                 ),
-                EvaluatorModel(
-                    expression="f'{x:.02f}'",
+                DisplayRoundedFloat(
                     keys=[f"{device_id}.performance.rate"],
                     width=4 * BASE_INC,
                     height=BASE_INC,
@@ -476,6 +502,47 @@ class CompactDeviceLinkList(VerticalLayout):
         )
 
 
+@titled("Assembler status", width=8 * NARROW_INC)
+@boxed
+class AssemblerDeviceStatus(VerticalLayout):
+    def __init__(self, device_id):
+        super().__init__(padding=0)
+        name = DisplayLabelModel(
+            keys=[f"{device_id}.deviceId"],
+            width=14 * BASE_INC,
+            height=BASE_INC,
+        )
+        state = DisplayStateColorModel(
+            show_string=True,
+            keys=[f"{device_id}.state"],
+            width=7 * BASE_INC,
+            height=BASE_INC,
+        )
+        train_id = DisplayLabelModel(
+            keys=[f"{device_id}.trainId"],
+            width=7 * BASE_INC,
+            height=BASE_INC,
+        )
+        self.children.extend(
+            [
+                name,
+                HorizontalLayout(
+                    state,
+                    train_id,
+                    padding=0,
+                ),
+                DeviceSceneLinkModel(
+                    text="I'm actually a TrainMatcher",
+                    keys=[f"{device_id}.availableScenes"],
+                    target="trainMatcherScene",
+                    target_window=SceneTargetWindow.Dialog,
+                    width=14 * BASE_INC,
+                    height=BASE_INC,
+                ),
+            ]
+        )
+
+
 # section: generating actual scenes
 
 
@@ -583,6 +650,39 @@ def manager_device_overview_scene(
         ),
     )
 
+@scene_generator
+def simple_assembler_overview(device_id, geometry_device_id):
+    return VerticalLayout(
+        HorizontalLayout(
+            AssemblerDeviceStatus(device_id),
+            titled("My geometry device")(boxed(VerticalLayout))(
+                DeviceSceneLinkModel(
+                    text=f"Geometry device: {geometry_device_id}",
+                    keys=[f"{geometry_device_id}.availableScenes"],
+                    target="overview",
+                    target_window=SceneTargetWindow.Dialog,
+                    frame_width=1,
+                    width=14 * BASE_INC,
+                    height=BASE_INC,
+                ),
+                DisplayCommandModel(
+                    keys=[f"{geometry_device_id}.pleaseSendYourGeometry"],
+                    width=14 * BASE_INC,
+                    height=BASE_INC,
+                ),
+                padding=0,
+            ),
+        ),
+        titled("Preview image")(boxed(dummy_wrap(DetectorGraphModel)))(
+            keys=[f"{device_id}.preview.output.schema.image"],
+            colormap="viridis",
+            width=30 * BASE_INC,
+            height=30 * BASE_INC,
+            x=PADDING,
+            y=PADDING,
+        ),
+    )
+
 
 # section: here be monsters
 
-- 
GitLab