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