diff --git a/src/calng/mdl_geometry_base.py b/src/calng/mdl_geometry_base.py index 4115e6038263295886a40f498d86c2f3b1724b0c..4b4dad872f4aa1fbeaa21b90e5e733f03e2b0213 100644 --- a/src/calng/mdl_geometry_base.py +++ b/src/calng/mdl_geometry_base.py @@ -20,6 +20,7 @@ from karabo.middlelayer import ( Slot, State, String, + Unit, VectorChar, VectorHash, ) @@ -53,7 +54,8 @@ class GeometryFileNode(Configurable): displayedName="Update manual settings", description="If this flag is on, the manual settings on this device will be " "updated according to the loaded geometry file. This is useful when you want " - "to load a file and then tweak the geometry a bit.", + "to load a file and then tweak the geometry a bit. This will zero current " + "offset.", assignment=Assignment.OPTIONAL, accessMode=AccessMode.RECONFIGURABLE, ) @@ -187,20 +189,40 @@ class ManualGeometryBase(Device): self.logger.log(level, text) -def makeXYCoordinateNode(default_x, default_y): +def makeXYCoordinateNode( + default_x, default_y, x_args=None, y_args=None, node_args=None +): class XYCoordinate(Configurable): x = Double( defaultValue=default_x, accessMode=AccessMode.RECONFIGURABLE, assignment=Assignment.OPTIONAL, + **({} if x_args is None else x_args), ) y = Double( defaultValue=default_y, accessMode=AccessMode.RECONFIGURABLE, assignment=Assignment.OPTIONAL, + **({} if y_args is None else y_args), ) - return Node(XYCoordinate) + return Node(XYCoordinate, **({} if node_args is None else node_args)) + + +def makeXYOffsetNode(): + return makeXYCoordinateNode( + 0, + 0, + x_args={"unitSymbol": Unit.METER}, + y_args={"unitSymbol": Unit.METER}, + node_args={ + "displayedName": "Offset", + "description": "See EXtra-geom documentation for details. This offset is " + "applied to entire detector after initial geometry is created from manual " + "parameters. Example: To move entire geometry up by 2 mm relative to beam, " + "set offset.y to 2e-3.", + }, + ) def makeQuadrantCornersNode(default_values): @@ -212,6 +234,7 @@ def makeQuadrantCornersNode(default_values): Q2 = makeXYCoordinateNode(*default_values[1]) Q3 = makeXYCoordinateNode(*default_values[2]) Q4 = makeXYCoordinateNode(*default_values[3]) + offset = makeXYOffsetNode() return Node(QuadrantCornersNode) @@ -237,15 +260,23 @@ class ManualQuadrantsGeometryBase(ManualGeometryBase): with self.push_state(State.CHANGING): geometry = self.geometry_class.from_quad_positions( [(Q.x.value, Q.y.value) for Q in self._quadrant_corners] + ).offset( + ( + self.quadrantCorners.offset.x.value, + self.quadrantCorners.offset.y.value, + ) ) await self._set_geometry(geometry) def _update_manual_from_current(self): + # TODO: consider what to do about offset for corner, (x, y) in zip( self._quadrant_corners, self.geometry.quad_positions() ): corner.x = x corner.y = y + self.quadrantCorners.offset.x = 0 + self.quadrantCorners.offset.y = 0 class MdlAgipd1MGeometry(ManualQuadrantsGeometryBase): @@ -293,6 +324,7 @@ class ModuleListItem(Configurable): class ManualModuleListGeometryBase(ManualGeometryBase): moduleList = None # subclass must define (with nice defaults) + offset = makeXYOffsetNode() @Slot( displayedName="Set from device", @@ -307,7 +339,7 @@ class ManualModuleListGeometryBase(ManualGeometryBase): (orient_x, orient_y) for (_, _, orient_x, orient_y) in self.moduleList.value ], - ) + ).offset((self.offset.x.value, self.offset.y.value)) self._pickled_geometry = pickle.dumps(self._geometry) def _update_manual_from_current(self):