diff --git a/setup.py b/setup.py index 94b98d0da6a06754b9221906afb1fd947e521031..74bd68d34d3a73245b4b6cbf60da8491ffe8f5cb 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,11 @@ setup(name='calng', 'AgipdCorrection = calng.AgipdCorrection:AgipdCorrection', 'DsscCorrection = calng.DsscCorrection:DsscCorrection', 'ModuleStacker = calng.ModuleStacker:ModuleStacker', + 'ManualAgipdGeometry = calng.ManualAgipdGeometry:ManualAgipdGeometry', + 'ManualDsscGeometry = calng.ManualDsscGeometry:ManualDsscGeometry', + 'ManualJungfrauGeometry = calng.ManualJungfrauGeometry:ManualJungfrauGeometry', 'ShmemToZMQ = calng.ShmemToZMQ:ShmemToZMQ', + 'SimpleAssembler = calng.SimpleAssembler:SimpleAssembler', ], 'karabo.middlelayer_device': [ diff --git a/src/calng/ManualAgipdGeometry.py b/src/calng/ManualAgipdGeometry.py index b01e96bc43bfad767fc02690156244768a77ab10..20428d6bdbfe38709305c6fc90820445e25e14f2 100644 --- a/src/calng/ManualAgipdGeometry.py +++ b/src/calng/ManualAgipdGeometry.py @@ -2,11 +2,11 @@ import extra_geom from karabo.bound import KARABO_CLASSINFO from ._version import version as deviceVersion -from .manual_geometry_base import ManualGeometryBase +from .manual_geometry_base import ManualQuadrantsGeometryBase @KARABO_CLASSINFO("ManualAgipdGeometry", deviceVersion) -class ManualAgipdGeometry(ManualGeometryBase): +class ManualAgipdGeometry(ManualQuadrantsGeometryBase): geometry_class = extra_geom.AGIPD_1MGeometry @staticmethod diff --git a/src/calng/ManualDsscGeometry.py b/src/calng/ManualDsscGeometry.py index 27f951d126e42fad23c252e44453d18df5ff8ff6..5b3d9b2e4d2394bf67b78c7fed2cc084d384612f 100644 --- a/src/calng/ManualDsscGeometry.py +++ b/src/calng/ManualDsscGeometry.py @@ -2,11 +2,11 @@ import extra_geom from karabo.bound import KARABO_CLASSINFO from ._version import version as deviceVersion -from .manual_geometry_base import ManualGeometryBase +from .manual_geometry_base import ManualQuadrantsGeometryBase @KARABO_CLASSINFO("ManualDsscGeometry", deviceVersion) -class ManualDsscGeometry(ManualGeometryBase): +class ManualDsscGeometry(ManualQuadrantsGeometryBase): geometry_class = extra_geom.DSSC_1MGeometry @staticmethod diff --git a/src/calng/ManualJungfrauGeometry.py b/src/calng/ManualJungfrauGeometry.py new file mode 100644 index 0000000000000000000000000000000000000000..57f7da914e77cb7aee28ffd078fe64c13231c259 --- /dev/null +++ b/src/calng/ManualJungfrauGeometry.py @@ -0,0 +1,28 @@ +import extra_geom +from karabo.bound import KARABO_CLASSINFO, OVERWRITE_ELEMENT, Hash + +from ._version import version as deviceVersion +from .manual_geometry_base import ManualModulesGeometryBase + + +@KARABO_CLASSINFO("ManualJungfrauGeometry", deviceVersion) +class ManualJungfrauGeometry(ManualModulesGeometryBase): + geometry_class = extra_geom.JUNGFRAUGeometry + + @staticmethod + def expectedParameters(expected): + # TODO: come up with some sweet defaults (this is two modules from docs 4M) + ( + OVERWRITE_ELEMENT(expected) + .key("modules") + .setNewDefaultValue( + [ + Hash( + "posX", 95, "posY", 564, "orientationX", -1, "orientationY", -1 + ), + Hash( + "posX", 95, "posY", 17, "orientationX", -1, "orientationY", -1 + ), + ] + ) + ) diff --git a/src/calng/SimpleAssembler.py b/src/calng/SimpleAssembler.py index 9f7d752326624f4da283bbc43db189476a01895d..dbd191fb6968b08dba54641457c557f5c3ed37aa 100644 --- a/src/calng/SimpleAssembler.py +++ b/src/calng/SimpleAssembler.py @@ -3,6 +3,7 @@ import pickle import re import numpy as np +from calng import utils from karabo.bound import ( FLOAT_ELEMENT, IMAGEDATA_ELEMENT, @@ -25,20 +26,21 @@ from karabo.bound import ( Unit, ) from karabo.common.api import KARABO_SCHEMA_DISPLAY_TYPE_SCENES as DT_SCENES -from TrainMatcher import TrainMatcher, scenes as trainmatcher_scenes +from TrainMatcher import TrainMatcher +from TrainMatcher import scenes as trainmatcher_scenes from . import scenes from ._version import version as deviceVersion -from calng import utils - preview_schema = Schema() ( IMAGEDATA_ELEMENT(preview_schema).key("image").commit(), + UINT64_ELEMENT(preview_schema).key("trainId").readOnly().commit(), ) xtdf_source_re = re.compile(r".*\/DET\/(\d+)CH0:xtdf") +daq_source_re = re.compile(r".*\/DET\/.*?(\d+):daqOutput") # TODO: merge scene with TrainMatcher's nice overview @KARABO_CLASSINFO("SimpleAssembler", deviceVersion) @@ -48,9 +50,9 @@ class SimpleAssembler(TrainMatcher.TrainMatcher): ( OVERWRITE_ELEMENT(expected) .key("availableScenes") - # TODO: add assemblerOverview scene .setNewDefaultValue(["scene", "assemblerOverview"]) .commit(), + FLOAT_ELEMENT(expected) .key("processingTime") .unit(Unit.SECOND) @@ -61,6 +63,7 @@ class SimpleAssembler(TrainMatcher.TrainMatcher): .info("Cannot keep up with GUI limit") .needsAcknowledging(False) .commit(), + FLOAT_ELEMENT(expected) .key("timeOfFlight") .unit(Unit.SECOND) @@ -71,15 +74,18 @@ class SimpleAssembler(TrainMatcher.TrainMatcher): .info("Time of flight exceeding 1 s") .needsAcknowledging(False) .commit(), + STRING_ELEMENT(expected) .key("pathToStack") .assignmentOptional() .defaultValue("image.data") .commit(), + INPUT_CHANNEL(expected) .key("geometryInput") .displayedName("Geometry input") .commit(), + OUTPUT_CHANNEL(expected) # can OVERWRITE_ELEMENT even do this? .key("output") .dataSchema(preview_schema) @@ -145,7 +151,7 @@ class SimpleAssembler(TrainMatcher.TrainMatcher): module_indices_unfilled.discard(module_index) for unfilled_module in module_indices_unfilled: - self.input_buffer[module_index].fill(0) + self.input_buffer[unfilled_module].fill(0) # TODO: configurable treatment of missing modules # TODO: reusable output buffer to save on allocation @@ -179,8 +185,15 @@ class SimpleAssembler(TrainMatcher.TrainMatcher): @functools.lru_cache def _source_to_index(self, source): # note: cache means warning only shows up once (also not performance-critical) + # TODO: allow user to inspect, modify the mapping + match = xtdf_source_re.match(source) - if match is None: - self.log.WARN(f"Couldn't figure out index for source {source}") - return 0 - return int(match.group(1)) + if match is not None: + return int(match.group(1)) + + match = daq_source_re.match(source) + if match is not None: + return int(match.group(1)) - 1 + + self.log.WARN(f"Couldn't figure out index for source {source}") + return 0 diff --git a/src/calng/manual_geometry_base.py b/src/calng/manual_geometry_base.py index e7629f3a390c7a1cdea969ac5e681993f8f9f479..2d2827b8e39c35eb3fe2b5e558d942d1e6d00207 100644 --- a/src/calng/manual_geometry_base.py +++ b/src/calng/manual_geometry_base.py @@ -1,12 +1,16 @@ import pickle +import matplotlib.pyplot as plt +import numpy as np from karabo.bound import ( DOUBLE_ELEMENT, - KARABO_CLASSINFO, IMAGEDATA_ELEMENT, + INT32_ELEMENT, + KARABO_CLASSINFO, NODE_ELEMENT, OUTPUT_CHANNEL, SLOT_ELEMENT, + TABLE_ELEMENT, VECTOR_CHAR_ELEMENT, VECTOR_STRING_ELEMENT, Encoding, @@ -17,14 +21,11 @@ from karabo.bound import ( State, ) from karabo.common.api import KARABO_SCHEMA_DISPLAY_TYPE_SCENES as DT_SCENES -import numpy as np from matplotlib.backends.backend_agg import FigureCanvasAgg -import matplotlib.pyplot as plt from . import scenes from ._version import version as deviceVersion - geometry_schema = Schema() ( VECTOR_CHAR_ELEMENT(geometry_schema) @@ -38,6 +39,37 @@ geometry_schema = Schema() preview_schema = Schema() (IMAGEDATA_ELEMENT(preview_schema).key("overview").commit()) +ModuleColumn = Schema() +( + DOUBLE_ELEMENT(ModuleColumn) + .key("posX") + .assignmentOptional() + .defaultValue(95) + .reconfigurable() + .commit(), + + DOUBLE_ELEMENT(ModuleColumn) + .key("posY") + .assignmentOptional() + .defaultValue(564) + .reconfigurable() + .commit(), + + INT32_ELEMENT(ModuleColumn) + .key("orientationX") + .assignmentOptional() + .defaultValue(-1) + .reconfigurable() + .commit(), + + INT32_ELEMENT(ModuleColumn) + .key("orientationY") + .assignmentOptional() + .defaultValue(-1) + .reconfigurable() + .commit(), +) + @KARABO_CLASSINFO("ManualGeometryBase", deviceVersion) class ManualGeometryBase(PythonDevice): @@ -49,37 +81,20 @@ class ManualGeometryBase(PythonDevice): .key("geometryOutput") .dataSchema(geometry_schema) .commit(), + SLOT_ELEMENT(expected).key("pleaseSendYourGeometry").commit(), + SLOT_ELEMENT(expected).key("reloadScenes").commit(), + OUTPUT_CHANNEL(expected) .key("previewOutput") .dataSchema(preview_schema) .commit(), + IMAGEDATA_ELEMENT(expected).key("layoutPreview").commit(), ) - # configuring this one manually - # subclasses should set better defaults - (NODE_ELEMENT(expected).key("quadrantCorners").commit(),) - for q in range(1, 5): - ( - NODE_ELEMENT(expected).key(f"quadrantCorners.Q{q}").commit(), - DOUBLE_ELEMENT(expected) - .key(f"quadrantCorners.Q{q}.x") - .assignmentOptional() - .defaultValue(0) - .reconfigurable() - .commit(), - DOUBLE_ELEMENT(expected) - .key(f"quadrantCorners.Q{q}.y") - .assignmentOptional() - .defaultValue(0) - .reconfigurable() - .commit(), - ) - # scenes are fun - # TODO: add the scene ( VECTOR_STRING_ELEMENT(expected) .key("availableScenes") @@ -89,6 +104,9 @@ class ManualGeometryBase(PythonDevice): .commit(), ) + def update_geom(self): + raise NotImplementedError() + def __init__(self, config): super().__init__(config) @@ -116,22 +134,6 @@ class ManualGeometryBase(PythonDevice): response["payload"] = payload self.reply(response) - def update_geom(self): - self.quadrant_corners = tuple( - (self.get(f"quadrantCorners.Q{q}.x"), self.get(f"quadrantCorners.Q{q}.y")) - for q in range(1, 5) - ) - self.geom = self.geometry_class.from_quad_positions(self.quadrant_corners) - self.pickled = pickle.dumps(self.geom) - # TODO: send to anyone who asks? make slot for that? - self.writeChannel("geometryOutput", Hash("pickledGeometry", self.pickled)) - - def reloadScenes(self): - global scenes - import importlib - - scenes = importlib.reload(scenes) - def pleaseSendYourGeometry(self): self.writeChannel("geometryOutput", Hash("pickledGeometry", self.pickled)) axis = self.geom.inspect() @@ -148,7 +150,6 @@ class ManualGeometryBase(PythonDevice): "layoutPreview", ImageData(image_buffer, encoding=Encoding.RGBA, bitsPerPixel=3 * 8), ) - # self.writeChannel("previewOutput", Hash("overview", ImageData())) def preReconfigure(self, config): self._prereconfigure_update_hash = config @@ -159,3 +160,68 @@ class ManualGeometryBase(PythonDevice): for path in self._prereconfigure_update_hash.getPaths() ): self.update_geom() + del self._prereconfigure_update_hash + + +@KARABO_CLASSINFO("ManualQuadrantsGeometryBase", deviceVersion) +class ManualQuadrantsGeometryBase(ManualGeometryBase): + @staticmethod + def expectedParameters(expected): + # note: subclasses should set better defaults + (NODE_ELEMENT(expected).key("quadrantCorners").commit(),) + for q in range(1, 5): + ( + NODE_ELEMENT(expected).key(f"quadrantCorners.Q{q}").commit(), + DOUBLE_ELEMENT(expected) + .key(f"quadrantCorners.Q{q}.x") + .assignmentOptional() + .defaultValue(0) + .reconfigurable() + .commit(), + + DOUBLE_ELEMENT(expected) + .key(f"quadrantCorners.Q{q}.y") + .assignmentOptional() + .defaultValue(0) + .reconfigurable() + .commit(), + ) + + def update_geom(self): + self.quadrant_corners = tuple( + (self.get(f"quadrantCorners.Q{q}.x"), self.get(f"quadrantCorners.Q{q}.y")) + for q in range(1, 5) + ) + self.geom = self.geometry_class.from_quad_positions(self.quadrant_corners) + self.pickled = pickle.dumps(self.geom) + # TODO: send to anyone who asks? make slot for that? send on connect? + self.writeChannel("geometryOutput", Hash("pickledGeometry", self.pickled)) + + +@KARABO_CLASSINFO("ManualModulesGeometryBase", deviceVersion) +class ManualModulesGeometryBase(ManualGeometryBase): + @staticmethod + def expectedParameters(expected): + ( + TABLE_ELEMENT(expected) + .key("modules") + .setColumns(ModuleColumn) + .assignmentOptional() + .defaultValue([]) + .reconfigurable() + .commit(), + ) + + def update_geom(self): + modules = self.get("modules") + module_pos = [(module.get("posX"), module.get("posY")) for module in modules] + orientations = [ + (module.get("orientationX"), module.get("orientationY")) + for module in modules + ] + self.geom = self.geometry_class.from_module_positions( + module_pos, orientations=orientations + ) + self.pickled = pickle.dumps(self.geom) + # TODO: send to anyone who asks? make slot for that? + self.writeChannel("geometryOutput", Hash("pickledGeometry", self.pickled))