From 6929c4e084fb00f7a4e08fff5d36e12cbd3b99be Mon Sep 17 00:00:00 2001
From: David Hammer <dhammer@mailbox.org>
Date: Thu, 28 Oct 2021 19:32:09 +0200
Subject: [PATCH] Adding correction step control to scenes

---
 src/calng/base_correction.py |  20 +------
 src/calng/scenes.py          | 105 ++++++++++++++++++++++++++++++++---
 2 files changed, 99 insertions(+), 26 deletions(-)

diff --git a/src/calng/base_correction.py b/src/calng/base_correction.py
index a6bb0069..1d971b93 100644
--- a/src/calng/base_correction.py
+++ b/src/calng/base_correction.py
@@ -52,7 +52,6 @@ class BaseCorrection(PythonDevice):
         "preview.enable",
         "preview.pulse",
         "preview.trainIdModulo",
-        "corrections.disableAll",
         "loadMostRecentConstants",
     ]  # subclass must extend this and put it in schema
     _schema_cache_fields = {
@@ -412,27 +411,12 @@ class BaseCorrection(PythonDevice):
             .commit(),
         )
 
+        # this node will be filled out later
         (
             NODE_ELEMENT(expected)
             .key("corrections")
             .displayedName("Correction steps")
             .commit(),
-            BOOL_ELEMENT(expected)
-            .key("corrections.disableAll")
-            .displayedName("Disable corrections for dataOutput")
-            .description(
-                "Toggle for disabling all corrections for dataOutput at once. This "
-                "overrides the individual flags under corrections.enabled (does not "
-                "affect corrected preview). When corrections are disabled, the device "
-                "still reshapes data to output shape, applies the pulse filter, and "
-                "casts to output dtype. Disabling all corrections can be useful if "
-                "constants are missing / bad, or if user software does not want "
-                "corrected data."
-            )
-            .assignmentOptional()
-            .defaultValue(False)
-            .reconfigurable()
-            .commit(),
         )
 
     def __init__(self, config):
@@ -642,8 +626,6 @@ class BaseCorrection(PythonDevice):
                 preview |= flag
         enabled &= available
         preview &= available
-        if self.get("corrections.disableAll"):
-            enabled &= self._correction_flag_class.NONE
         self._correction_flag_enabled = enabled
         self._correction_flag_preview = preview
         self.log.DEBUG(f"Corrections for dataOutput: {str(enabled)}")
diff --git a/src/calng/scenes.py b/src/calng/scenes.py
index 73cd358e..a3ec9ce3 100644
--- a/src/calng/scenes.py
+++ b/src/calng/scenes.py
@@ -2,6 +2,7 @@ import textwrap
 
 import karathon
 from karabo.common.scenemodel.api import (
+    CheckBoxModel,
     ColorBoolModel,
     DisplayCommandModel,
     DisplayLabelModel,
@@ -236,7 +237,6 @@ class ConstantNode:
 @boxed
 class FoundConstantsColumn:
     def __init__(self, device_id, schema_hash, prefix="foundConstants"):
-        self.device_id = device_id
         self.rows = [
             ConstantNode(device_id, f"{prefix}.{constant_name}")
             for constant_name in schema_hash.get(prefix).getKeys()
@@ -257,6 +257,84 @@ class FoundConstantsColumn:
         return max(row.width() for row in self.rows)
 
 
+class CorrectionStepNode:
+    def __init__(self, device_id, step_path):
+        self.label = LabelModel(
+            text=step_path.split(".")[-1], width=7 * BASE_INC, height=BASE_INC
+        )
+        self.checkbox_main = CheckBoxModel(
+            keys=[f"{device_id}.{step_path}.enable"],
+            height=BASE_INC,
+            width=BASE_INC,
+            klass="EditableCheckBox",
+        )
+        self.checkbox_preview = CheckBoxModel(
+            keys=[f"{device_id}.{step_path}.preview"],
+            height=BASE_INC,
+            width=BASE_INC,
+            klass="EditableCheckBox",
+        )
+
+    def render(self, x, y):
+        self.label.x = x
+        self.label.y = y
+        x += 5 * BASE_INC
+        self.checkbox_main.x = x
+        self.checkbox_main.y = y
+        x += 3 * BASE_INC
+        self.checkbox_preview.x = x
+        self.checkbox_preview.y = y
+        return [self.label, self.checkbox_main, self.checkbox_preview]
+
+    def height(self):
+        return BASE_INC
+
+    def width(self):
+        return 10 * BASE_INC
+
+
+@titled("Correction steps", width=6)
+@boxed
+class CorrectionStepsColumn:
+    def __init__(
+        self, device_id, schema_hash, prefix="corrections", control_prefix=None
+    ):
+        # separate control prefix allows iterating device schema, but setting on manager
+        if control_prefix is None:
+            control_prefix = prefix
+
+        self.header_main = LabelModel(
+            text="enable", width=3 * BASE_INC, height=BASE_INC
+        )
+        self.header_preview = LabelModel(
+            text="preview", width=3 * BASE_INC, height=BASE_INC
+        )
+        self.steps = [
+            CorrectionStepNode(device_id, f"{control_prefix}.{step_path}")
+            for step_path in schema_hash.get(prefix).getKeys()
+        ]
+
+    def render(self, x, y):
+        self.header_main.x = x + 5 * BASE_INC
+        self.header_main.y = y
+        self.header_preview.x = x + 8 * BASE_INC
+        self.header_preview.y = y
+        res = [self.header_main, self.header_preview]
+        y += BASE_INC
+        for step in self.steps:
+            step.x = x
+            step.y = y
+            res.extend(step.render(x, y))
+            y += step.height()
+        return res
+
+    def width(self):
+        return max(step.width() for step in self.steps)
+
+    def height(self):
+        return sum(step.height() for step in self.steps) + BASE_INC
+
+
 class ConstantLoadedAmpeln:
     def __init__(self, device_id, schema_hash, prefix="foundConstants"):
         self.keys = [
@@ -408,23 +486,31 @@ def correction_device_overview_scene(device_id, schema):
     status_overview = CorrectionDeviceStatus(device_id)
     cpc = ConstantParameterColumn(device_id, schema_hash)
     fcc = FoundConstantsColumn(device_id, schema_hash)
+    csc = CorrectionStepsColumn(device_id, schema_hash)
 
     subscenes = []
     x = PADDING
     y = PADDING
     subscenes.extend(status_overview.render(x, y))
-    x += status_overview.width() + BASE_INC
+    x += status_overview.width() + PADDING
     subscenes.extend(cpc.render(x, y))
-    x += cpc.width() + BASE_INC
+    x += cpc.width() + PADDING
     subscenes.extend(fcc.render(x, y))
+    y += fcc.height() + PADDING
+    subscenes.extend(csc.render(x, y))
 
     scene = SceneModel(
-        height=max(status_overview.height(), cpc.height()) + 2 * PADDING,
+        height=max(
+            status_overview.height(),
+            cpc.height(),
+            fcc.height() + PADDING + csc.height(),
+        )
+        + 2 * PADDING,
         width=2 * PADDING
-        + 2 * BASE_INC
+        + 2 * PADDING
         + status_overview.width()
         + cpc.width()
-        + fcc.width(),
+        + csc.width(),
         children=subscenes,
     )
     return write_scene(scene)
@@ -441,13 +527,18 @@ def manager_device_overview_scene(
     x = PADDING
     y = PADDING
     subscenes = []
+    correction_steps = CorrectionStepsColumn(
+        manager_device_id, schema_hash, control_prefix="managed.corrections"
+    )
+    subscenes.extend(correction_steps.render(x, y))
+    y += correction_steps.height() + PADDING
     for device_id in correction_device_ids:
         ccdo = CompactCorrectionDeviceOverview(device_id, schema_hash)
         subscenes.extend(ccdo.render(x, y))
         y += ccdo.height()
 
     scene = SceneModel(
-        height=y + 2 * PADDING,
+        height=y + PADDING,
         width=ccdo.width() + 2 * PADDING,
         children=subscenes,
     )
-- 
GitLab