dls-dodal 1.61.0__py3-none-any.whl → 1.63.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. {dls_dodal-1.61.0.dist-info → dls_dodal-1.63.0.dist-info}/METADATA +1 -1
  2. {dls_dodal-1.61.0.dist-info → dls_dodal-1.63.0.dist-info}/RECORD +81 -73
  3. dls_dodal-1.63.0.dist-info/entry_points.txt +3 -0
  4. dodal/_version.py +2 -2
  5. dodal/beamlines/__init__.py +1 -0
  6. dodal/beamlines/adsim.py +5 -3
  7. dodal/beamlines/b21.py +3 -1
  8. dodal/beamlines/i02_2.py +32 -0
  9. dodal/beamlines/i03.py +9 -0
  10. dodal/beamlines/i04.py +1 -1
  11. dodal/beamlines/i09.py +10 -3
  12. dodal/beamlines/i09_1.py +9 -3
  13. dodal/beamlines/i10.py +7 -69
  14. dodal/beamlines/i10_1.py +35 -0
  15. dodal/beamlines/i10_optics.py +205 -0
  16. dodal/beamlines/i15_1.py +5 -5
  17. dodal/beamlines/i17.py +50 -1
  18. dodal/beamlines/i18.py +15 -9
  19. dodal/beamlines/i19_1.py +3 -3
  20. dodal/beamlines/i19_2.py +12 -2
  21. dodal/beamlines/i19_optics.py +4 -1
  22. dodal/beamlines/i24.py +3 -3
  23. dodal/cli.py +4 -4
  24. dodal/common/visit.py +4 -4
  25. dodal/devices/aperturescatterguard.py +6 -4
  26. dodal/devices/apple2_undulator.py +211 -114
  27. dodal/devices/attenuator/filter_selections.py +6 -6
  28. dodal/devices/common_dcm.py +62 -15
  29. dodal/devices/controllers.py +8 -6
  30. dodal/devices/current_amplifiers/femto.py +4 -4
  31. dodal/devices/current_amplifiers/sr570.py +3 -3
  32. dodal/devices/fast_grid_scan.py +97 -21
  33. dodal/devices/fast_shutter.py +69 -0
  34. dodal/devices/i02_1/fast_grid_scan.py +1 -1
  35. dodal/devices/i02_2/__init__.py +0 -0
  36. dodal/devices/i03/dcm.py +4 -2
  37. dodal/devices/i04/murko_results.py +35 -14
  38. dodal/devices/i09/__init__.py +1 -2
  39. dodal/devices/i10/__init__.py +29 -0
  40. dodal/devices/i10/diagnostics.py +37 -5
  41. dodal/devices/i10/i10_apple2.py +125 -229
  42. dodal/devices/i10/slits.py +38 -6
  43. dodal/devices/i15/dcm.py +6 -44
  44. dodal/devices/i17/__init__.py +0 -0
  45. dodal/devices/i17/i17_apple2.py +51 -0
  46. dodal/devices/i19/access_controlled/__init__.py +0 -0
  47. dodal/devices/i19/{shutter.py → access_controlled/shutter.py} +7 -4
  48. dodal/devices/i19/mapt_configuration.py +38 -0
  49. dodal/devices/i19/pin_col_stages.py +170 -0
  50. dodal/devices/i22/dcm.py +2 -2
  51. dodal/devices/i24/dcm.py +2 -2
  52. dodal/devices/oav/oav_detector.py +1 -1
  53. dodal/devices/oav/oav_parameters.py +4 -4
  54. dodal/devices/oav/oav_to_redis_forwarder.py +4 -4
  55. dodal/devices/oav/pin_image_recognition/__init__.py +3 -3
  56. dodal/devices/oav/pin_image_recognition/utils.py +1 -1
  57. dodal/devices/oav/snapshots/snapshot.py +1 -1
  58. dodal/devices/oav/snapshots/snapshot_image_processing.py +12 -12
  59. dodal/devices/oav/snapshots/snapshot_with_grid.py +1 -1
  60. dodal/devices/oav/utils.py +2 -2
  61. dodal/devices/pgm.py +3 -3
  62. dodal/devices/robot.py +5 -5
  63. dodal/devices/tetramm.py +9 -5
  64. dodal/devices/thawer.py +0 -4
  65. dodal/devices/v2f.py +2 -2
  66. dodal/devices/zebra/zebra_constants_mapping.py +2 -2
  67. dodal/devices/zocalo/__init__.py +4 -4
  68. dodal/devices/zocalo/zocalo_results.py +4 -4
  69. dodal/log.py +9 -9
  70. dodal/plan_stubs/motor_utils.py +4 -4
  71. dodal/plans/configure_arm_trigger_and_disarm_detector.py +29 -7
  72. dodal/plans/save_panda.py +7 -7
  73. dodal/plans/verify_undulator_gap.py +2 -2
  74. dls_dodal-1.61.0.dist-info/entry_points.txt +0 -3
  75. dodal/beamlines/i10-1.py +0 -25
  76. dodal/devices/i09/dcm.py +0 -26
  77. {dls_dodal-1.61.0.dist-info → dls_dodal-1.63.0.dist-info}/WHEEL +0 -0
  78. {dls_dodal-1.61.0.dist-info → dls_dodal-1.63.0.dist-info}/licenses/LICENSE +0 -0
  79. {dls_dodal-1.61.0.dist-info → dls_dodal-1.63.0.dist-info}/top_level.txt +0 -0
  80. /dodal/devices/areadetector/plugins/{CAM.py → cam.py} +0 -0
  81. /dodal/devices/areadetector/plugins/{MJPG.py → mjpg.py} +0 -0
  82. /dodal/devices/i18/{KBMirror.py → kb_mirror.py} +0 -0
  83. /dodal/devices/i19/{blueapi_device.py → access_controlled/blueapi_device.py} +0 -0
  84. /dodal/devices/i19/{hutch_access.py → access_controlled/hutch_access.py} +0 -0
@@ -1,9 +1,10 @@
1
+ import asyncio
1
2
  from abc import ABC, abstractmethod
2
3
  from typing import Generic, TypeVar
3
4
 
4
5
  import numpy as np
5
- from bluesky.plan_stubs import mv
6
- from bluesky.protocols import Flyable
6
+ from bluesky.plan_stubs import prepare
7
+ from bluesky.protocols import Flyable, Preparable
7
8
  from numpy import ndarray
8
9
  from ophyd_async.core import (
9
10
  AsyncStatus,
@@ -13,6 +14,7 @@ from ophyd_async.core import (
13
14
  SignalRW,
14
15
  StandardReadable,
15
16
  derived_signal_r,
17
+ set_and_wait_for_value,
16
18
  soft_signal_r_and_setter,
17
19
  wait_for_value,
18
20
  )
@@ -29,6 +31,10 @@ from dodal.log import LOGGER
29
31
  from dodal.parameters.experiment_parameter_base import AbstractExperimentWithBeamParams
30
32
 
31
33
 
34
+ class GridScanInvalidError(RuntimeError):
35
+ """Raised when the gridscan parameters are not valid."""
36
+
37
+
32
38
  @dataclass
33
39
  class GridAxis:
34
40
  start: float
@@ -144,7 +150,7 @@ class GridScanParamsThreeD(GridScanParamsCommon):
144
150
  return GridAxis(self.z2_start_mm, self.z_step_size_mm, self.z_steps)
145
151
 
146
152
 
147
- ParamType = TypeVar("ParamType", bound=GridScanParamsCommon, covariant=True)
153
+ ParamType = TypeVar("ParamType", bound=GridScanParamsCommon)
148
154
 
149
155
 
150
156
  class WithDwellTime(BaseModel):
@@ -190,7 +196,9 @@ class MotionProgram(Device):
190
196
  self.program_number = soft_signal_r_and_setter(float, -1)[0]
191
197
 
192
198
 
193
- class FastGridScanCommon(StandardReadable, Flyable, ABC, Generic[ParamType]):
199
+ class FastGridScanCommon(
200
+ StandardReadable, Flyable, ABC, Preparable, Generic[ParamType]
201
+ ):
194
202
  """Device containing the minimal signals for a general fast grid scan.
195
203
 
196
204
  When the motion program is started, the goniometer will move in a snake-like grid trajectory,
@@ -231,8 +239,9 @@ class FastGridScanCommon(StandardReadable, Flyable, ABC, Generic[ParamType]):
231
239
  self.KICKOFF_TIMEOUT: float = 5.0
232
240
 
233
241
  self.COMPLETE_STATUS: float = 60.0
242
+ self.VALIDITY_CHECK_TIMEOUT = 0.5
234
243
 
235
- self.movable_params: dict[str, Signal] = {
244
+ self._movable_params: dict[str, Signal] = {
236
245
  "x_steps": self.x_steps,
237
246
  "y_steps": self.y_steps,
238
247
  "x_step_size_mm": self.x_step_size,
@@ -284,6 +293,45 @@ class FastGridScanCommon(StandardReadable, Flyable, ABC, Generic[ParamType]):
284
293
  self, motion_controller_prefix: str
285
294
  ) -> MotionProgram: ...
286
295
 
296
+ @AsyncStatus.wrap
297
+ async def prepare(self, value: ParamType):
298
+ """
299
+ Submit the gridscan parameters to the device for validation prior to
300
+ gridscan kickoff
301
+ Args:
302
+ value: the gridscan parameters
303
+
304
+ Raises:
305
+ GridScanInvalidError: if the gridscan parameters were not valid
306
+ """
307
+ set_statuses = []
308
+
309
+ LOGGER.info("Applying gridscan parameters...")
310
+ # Create arguments for bps.mv
311
+ for key, signal in self._movable_params.items():
312
+ param_value = value.__dict__[key]
313
+ set_statuses.append(await set_and_wait_for_value(signal, param_value)) # type: ignore
314
+
315
+ # Counter should always start at 0
316
+ set_statuses.append(await set_and_wait_for_value(self.position_counter, 0))
317
+
318
+ LOGGER.info("Gridscan parameters applied, waiting for sets to complete...")
319
+
320
+ # wait for parameter sets to complete
321
+ await asyncio.gather(*set_statuses)
322
+
323
+ LOGGER.info("Sets confirmed, waiting for validity checks to pass...")
324
+ try:
325
+ await wait_for_value(
326
+ self.scan_invalid, 0.0, timeout=self.VALIDITY_CHECK_TIMEOUT
327
+ )
328
+ except TimeoutError as e:
329
+ raise GridScanInvalidError(
330
+ f"Gridscan parameters not validated after {self.VALIDITY_CHECK_TIMEOUT}s"
331
+ ) from e
332
+
333
+ LOGGER.info("Gridscan validity confirmed, gridscan is now prepared.")
334
+
287
335
 
288
336
  class FastGridScanThreeD(FastGridScanCommon[ParamType]):
289
337
  """Device for standard 3D FGS.
@@ -309,10 +357,10 @@ class FastGridScanThreeD(FastGridScanCommon[ParamType]):
309
357
 
310
358
  super().__init__(full_prefix, prefix, name)
311
359
 
312
- self.movable_params["z_step_size_mm"] = self.z_step_size
313
- self.movable_params["z2_start_mm"] = self.z2_start
314
- self.movable_params["y2_start_mm"] = self.y2_start
315
- self.movable_params["z_steps"] = self.z_steps
360
+ self._movable_params["z_step_size_mm"] = self.z_step_size
361
+ self._movable_params["z2_start_mm"] = self.z2_start
362
+ self._movable_params["y2_start_mm"] = self.y2_start
363
+ self._movable_params["z_steps"] = self.z_steps
316
364
 
317
365
  def _create_expected_images_signal(self):
318
366
  return derived_signal_r(
@@ -329,7 +377,35 @@ class FastGridScanThreeD(FastGridScanCommon[ParamType]):
329
377
  return first_grid + second_grid
330
378
 
331
379
  def _create_scan_invalid_signal(self, prefix: str) -> SignalR[float]:
332
- return epics_signal_r(float, f"{prefix}SCAN_INVALID")
380
+ self.x_scan_valid = epics_signal_r(float, f"{prefix}X_SCAN_VALID")
381
+ self.y_scan_valid = epics_signal_r(float, f"{prefix}Y_SCAN_VALID")
382
+ self.z_scan_valid = epics_signal_r(float, f"{prefix}Z_SCAN_VALID")
383
+ self.device_scan_invalid = epics_signal_r(float, f"{prefix}SCAN_INVALID")
384
+
385
+ def compute_derived_value(
386
+ x_scan_valid: float,
387
+ y_scan_valid: float,
388
+ z_scan_valid: float,
389
+ device_scan_invalid: float,
390
+ ) -> float:
391
+ return (
392
+ 1.0
393
+ if not (
394
+ x_scan_valid
395
+ and y_scan_valid
396
+ and z_scan_valid
397
+ and not device_scan_invalid
398
+ )
399
+ else 0.0
400
+ )
401
+
402
+ return derived_signal_r(
403
+ compute_derived_value,
404
+ x_scan_valid=self.x_scan_valid,
405
+ y_scan_valid=self.y_scan_valid,
406
+ z_scan_valid=self.z_scan_valid,
407
+ device_scan_invalid=self.device_scan_invalid,
408
+ )
333
409
 
334
410
  def _create_motion_program(self, motion_controller_prefix: str):
335
411
  return MotionProgram(motion_controller_prefix)
@@ -349,7 +425,7 @@ class ZebraFastGridScanThreeD(FastGridScanThreeD[ZebraGridScanParamsThreeD]):
349
425
  self.dwell_time_ms = epics_signal_rw_rbv(float, f"{full_prefix}DWELL_TIME")
350
426
  self.x_counter = epics_signal_r(int, f"{full_prefix}X_COUNTER")
351
427
  super().__init__(prefix, infix, name)
352
- self.movable_params["dwell_time_ms"] = self.dwell_time_ms
428
+ self._movable_params["dwell_time_ms"] = self.dwell_time_ms
353
429
 
354
430
  def _create_position_counter(self, prefix: str):
355
431
  return epics_signal_rw(
@@ -380,20 +456,20 @@ class PandAFastGridScan(FastGridScanThreeD[PandAGridScanParams]):
380
456
  )
381
457
  super().__init__(prefix, infix, name)
382
458
 
383
- self.movable_params["run_up_distance_mm"] = self.run_up_distance_mm
459
+ self._movable_params["run_up_distance_mm"] = self.run_up_distance_mm
384
460
 
385
461
  def _create_position_counter(self, prefix: str):
386
462
  return epics_signal_rw(int, f"{prefix}Y_COUNTER")
387
463
 
388
464
 
389
465
  def set_fast_grid_scan_params(scan: FastGridScanCommon[ParamType], params: ParamType):
390
- to_move = []
391
-
392
- # Create arguments for bps.mv
393
- for key in scan.movable_params.keys():
394
- to_move.extend([scan.movable_params[key], params.__dict__[key]])
395
-
396
- # Counter should always start at 0
397
- to_move.extend([scan.position_counter, 0])
466
+ """
467
+ Apply the fast grid scan parameters to the grid scan device and validate them
468
+ Args:
469
+ scan: The fast grid scan device
470
+ params: The parameters to set
398
471
 
399
- yield from mv(*to_move)
472
+ Raises:
473
+ GridScanInvalidError: if the grid scan parameters are not valid
474
+ """
475
+ yield from prepare(scan, params, wait=True)
@@ -0,0 +1,69 @@
1
+ from typing import TypeVar
2
+
3
+ from bluesky.protocols import Movable
4
+ from ophyd_async.core import (
5
+ AsyncStatus,
6
+ EnumTypes,
7
+ StandardReadable,
8
+ )
9
+ from ophyd_async.epics.core import epics_signal_rw
10
+
11
+ StrictEnumT = TypeVar("StrictEnumT", bound=EnumTypes)
12
+
13
+
14
+ class GenericFastShutter(StandardReadable, Movable[StrictEnumT]):
15
+ """
16
+ Basic enum device specialised for a fast shutter with configured open_state and
17
+ close_state so it is generic enough to be used with any device or plan without
18
+ knowing the specific enum to use.
19
+
20
+ For example:
21
+ await shutter.set(shutter.open_state)
22
+ await shutter.set(shutter.close_state)
23
+ OR
24
+ run_engine(bps.mv(shutter, shutter.open_state))
25
+ run_engine(bps.mv(shutter, shutter.close_state))
26
+ """
27
+
28
+ def __init__(
29
+ self,
30
+ pv: str,
31
+ open_state: StrictEnumT,
32
+ close_state: StrictEnumT,
33
+ name: str = "",
34
+ ):
35
+ """
36
+ Arguments:
37
+ pv: The pv to connect to the shutter device.
38
+ open_state: The enum value that corresponds with opening the shutter.
39
+ close_state: The enum value that corresponds with closing the shutter.
40
+ """
41
+ self.open_state = open_state
42
+ self.close_state = close_state
43
+ with self.add_children_as_readables():
44
+ self.state = epics_signal_rw(type(self.open_state), pv)
45
+ super().__init__(name)
46
+
47
+ @AsyncStatus.wrap
48
+ async def set(self, value: StrictEnumT) -> None:
49
+ await self.state.set(value)
50
+
51
+ async def is_open(self) -> bool:
52
+ """
53
+ Checks to see if shutter is in open_state. Should not be used directly inside a
54
+ plan. A user should use the following instead in a plan:
55
+
56
+ from bluesky import plan_stubs as bps
57
+ is_open = yield from bps.rd(shutter.state) == shutter.open_state
58
+ """
59
+ return await self.state.get_value() == self.open_state
60
+
61
+ async def is_closed(self) -> bool:
62
+ """
63
+ Checks to see if shutter is in close_state. Should not be used directly inside a
64
+ plan. A user should use the following instead in a plan:
65
+
66
+ from bluesky import plan_stubs as bps
67
+ is_closed = yield from bps.rd(shutter.state) == shutter.close_state
68
+ """
69
+ return await self.state.get_value() == self.close_state
@@ -35,7 +35,7 @@ class ZebraFastGridScanTwoD(FastGridScanCommon[ZebraGridScanParamsTwoD]):
35
35
  # See https://github.com/DiamondLightSource/mx-bluesky/issues/1203
36
36
  self.dwell_time_ms = epics_signal_rw_rbv(float, f"{full_prefix}EXPOSURE_TIME")
37
37
 
38
- self.movable_params["dwell_time_ms"] = self.dwell_time_ms
38
+ self._movable_params["dwell_time_ms"] = self.dwell_time_ms
39
39
 
40
40
  def _create_expected_images_signal(self):
41
41
  return derived_signal_r(
File without changes
dodal/devices/i03/dcm.py CHANGED
@@ -9,13 +9,15 @@ from dodal.common.crystal_metadata import (
9
9
  make_crystal_metadata_from_material,
10
10
  )
11
11
  from dodal.devices.common_dcm import (
12
- BaseDCM,
12
+ DoubleCrystalMonochromatorWithDSpacing,
13
13
  PitchAndRollCrystal,
14
14
  StationaryCrystal,
15
15
  )
16
16
 
17
17
 
18
- class DCM(BaseDCM[PitchAndRollCrystal, StationaryCrystal]):
18
+ class DCM(
19
+ DoubleCrystalMonochromatorWithDSpacing[PitchAndRollCrystal, StationaryCrystal]
20
+ ):
19
21
  """
20
22
  A double crystal monochromator (DCM), used to select the energy of the beam.
21
23
 
@@ -43,7 +43,7 @@ class Coord(Enum):
43
43
 
44
44
  @dataclass
45
45
  class MurkoResult:
46
- centre_px: tuple
46
+ chosen_point_px: tuple[int, int]
47
47
  x_dist_mm: float
48
48
  y_dist_mm: float
49
49
  omega: float
@@ -51,7 +51,7 @@ class MurkoResult:
51
51
  metadata: MurkoMetadata
52
52
 
53
53
 
54
- class NoResultsFound(ValueError):
54
+ class NoResultsFoundError(ValueError):
55
55
  pass
56
56
 
57
57
 
@@ -73,6 +73,7 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
73
73
 
74
74
  TIMEOUT_S = 2
75
75
  PERCENTAGE_TO_USE = 25
76
+ LEFTMOST_PIXEL_TO_USE = 10
76
77
  NUMBER_OF_WRONG_RESULTS_TO_LOG = 5
77
78
 
78
79
  def __init__(
@@ -129,7 +130,7 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
129
130
  await self.process_batch(message, sample_id)
130
131
 
131
132
  if not self._results:
132
- raise NoResultsFound("No results retrieved from Murko")
133
+ raise NoResultsFoundError("No results retrieved from Murko")
133
134
 
134
135
  for result in self._results:
135
136
  LOGGER.debug(result)
@@ -186,16 +187,16 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
186
187
  else:
187
188
  shape = result["original_shape"] # Dimensions of image in pixels
188
189
  # Murko returns coords as y, x
189
- centre_px = (coords[1] * shape[1], coords[0] * shape[0])
190
+ chosen_point_px = (coords[1] * shape[1], coords[0] * shape[0])
190
191
 
191
192
  beam_dist_px = calculate_beam_distance(
192
193
  (metadata["beam_centre_i"], metadata["beam_centre_j"]),
193
- centre_px[0],
194
- centre_px[1],
194
+ chosen_point_px[0],
195
+ chosen_point_px[1],
195
196
  )
196
197
  self._results.append(
197
198
  MurkoResult(
198
- centre_px=centre_px,
199
+ chosen_point_px=chosen_point_px,
199
200
  x_dist_mm=beam_dist_px[0] * metadata["microns_per_x_pixel"] / 1000,
200
201
  y_dist_mm=beam_dist_px[1] * metadata["microns_per_y_pixel"] / 1000,
201
202
  omega=omega,
@@ -209,10 +210,27 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
209
210
  """Whilst murko is not fully trained it often gives us poor results.
210
211
  When it is wrong it usually picks up the base of the pin, rather than the tip,
211
212
  meaning that by keeping only a percentage of the results with the smallest X we
212
- remove many of the outliers.
213
+ remove many of the outliers. Murko also occasionally picks a point in the bottom
214
+ left corner, which can be removed by filtering results with a small x pixel.
213
215
  """
216
+
214
217
  LOGGER.info(f"Number of results before filtering: {len(self._results)}")
215
- sorted_results = sorted(self._results, key=lambda item: item.centre_px[0])
218
+ sorted_results = sorted(self._results, key=lambda item: item.chosen_point_px[0])
219
+
220
+ results_without_tiny_x = [
221
+ result
222
+ for result in sorted_results
223
+ if result.chosen_point_px[0] >= self.LEFTMOST_PIXEL_TO_USE
224
+ ]
225
+ result_uuids_with_tiny_x = [
226
+ result.uuid
227
+ for result in sorted_results
228
+ if result not in results_without_tiny_x
229
+ ]
230
+
231
+ LOGGER.info(
232
+ f"Results with tiny x have been removed: {result_uuids_with_tiny_x}"
233
+ )
216
234
 
217
235
  worst_results = [
218
236
  r.uuid for r in sorted_results[-self.NUMBER_OF_WRONG_RESULTS_TO_LOG :]
@@ -221,14 +239,17 @@ class MurkoResultsDevice(StandardReadable, Triggerable, Stageable):
221
239
  LOGGER.info(
222
240
  f"Worst {self.NUMBER_OF_WRONG_RESULTS_TO_LOG} murko results were {worst_results}"
223
241
  )
242
+
224
243
  cutoff = max(1, int(len(sorted_results) * self.PERCENTAGE_TO_USE / 100))
225
- for i, result in enumerate(sorted_results):
226
- result.metadata["used_for_centring"] = i < cutoff
244
+ cutoff = min(cutoff, len(results_without_tiny_x))
245
+
246
+ best_x = results_without_tiny_x[:cutoff]
227
247
 
228
- smallest_x = sorted_results[:cutoff]
248
+ for result in sorted_results:
249
+ result.metadata["used_for_centring"] = result in best_x
229
250
 
230
- LOGGER.info(f"Number of results after filtering: {len(smallest_x)}")
231
- return smallest_x
251
+ LOGGER.info(f"Number of results after filtering: {len(best_x)}")
252
+ return best_x
232
253
 
233
254
 
234
255
  def get_yz_least_squares(vertical_dists: list, omegas: list) -> tuple[float, float]:
@@ -1,4 +1,3 @@
1
- from dodal.devices.i09.dcm import DCM
2
1
  from dodal.devices.i09.enums import Grating, LensMode, PassEnergy, PsuMode
3
2
 
4
- __all__ = ["DCM", "Grating", "LensMode", "PsuMode", "PassEnergy"]
3
+ __all__ = ["Grating", "LensMode", "PsuMode", "PassEnergy"]
@@ -0,0 +1,29 @@
1
+ from .diagnostics import (
2
+ I10Diagnostic,
3
+ I10Diagnostic5ADet,
4
+ I10JDiagnostic,
5
+ I10PneumaticStage,
6
+ I10SharedDiagnostic,
7
+ )
8
+ from .mirrors import PiezoMirror
9
+ from .slits import (
10
+ I10JSlits,
11
+ I10SharedSlits,
12
+ I10SharedSlitsDrainCurrent,
13
+ I10Slits,
14
+ I10SlitsDrainCurrent,
15
+ )
16
+
17
+ __all__ = [
18
+ "I10Diagnostic",
19
+ "I10Diagnostic5ADet",
20
+ "I10PneumaticStage",
21
+ "PiezoMirror",
22
+ "I10Slits",
23
+ "I10SlitsDrainCurrent",
24
+ "I10SharedDiagnostic",
25
+ "I10SharedSlits",
26
+ "I10JSlits",
27
+ "I10SharedSlitsDrainCurrent",
28
+ "I10JDiagnostic",
29
+ ]
@@ -28,7 +28,7 @@ class D3Position(StrictEnum):
28
28
  GRID = "Grid"
29
29
 
30
30
 
31
- class D5Position(StrictEnum):
31
+ class CellPosition(StrictEnum):
32
32
  CELL_IN = "Cell In"
33
33
  CELL_OUT = "Cell Out"
34
34
 
@@ -68,6 +68,21 @@ class InStateReadBackTable(StrictEnum):
68
68
  OUT_OF_BEAM = "Out of Beam"
69
69
 
70
70
 
71
+ class D2jPosition(StrictEnum):
72
+ OUT_OF_THE_BEAM = "Out of the beam"
73
+ DIODE = "Diode"
74
+ BLADE = "Blade"
75
+ LA = "La ref"
76
+ GD = "Gd ref"
77
+ YB = "Yb ref"
78
+
79
+
80
+ class D3jPosition(StrictEnum):
81
+ OUT_OF_THE_BEAM = "Out of the beam"
82
+ DIODE_IN = "Diode In"
83
+ DIAMOND_WINDOW = "Diamond window"
84
+
85
+
71
86
  class I10PneumaticStage(StandardReadable):
72
87
  """Pneumatic stage only has two real positions in or out.
73
88
  Use for fluorescent screen which can be insert into the x-ray beam.
@@ -138,9 +153,7 @@ class FullDiagnostic(Device):
138
153
  super().__init__(name)
139
154
 
140
155
 
141
- class I10Diagnostic(Device):
142
- """Collection of all the diagnostic stage on i10."""
143
-
156
+ class I10SharedDiagnostic(Device):
144
157
  def __init__(self, prefix, name: str = "") -> None:
145
158
  self.d1 = ScreenCam(prefix=prefix + "PHDGN-01:")
146
159
  self.d2 = ScreenCam(prefix=prefix + "PHDGN-02:")
@@ -149,8 +162,15 @@ class I10Diagnostic(Device):
149
162
  positioner_enum=D3Position,
150
163
  positioner_suffix="DET:X",
151
164
  )
165
+ super().__init__(name)
166
+
167
+
168
+ class I10Diagnostic(Device):
169
+ """Collection of all the diagnostic stage on i10."""
170
+
171
+ def __init__(self, prefix, name: str = "") -> None:
152
172
  self.d4 = ScreenCam(prefix=prefix + "PHDGN-04:")
153
- self.d5 = create_positioner(D5Position, f"{prefix}IONC-01:Y")
173
+ self.d5 = create_positioner(CellPosition, f"{prefix}IONC-01:Y")
154
174
  self.d5A = create_positioner(D5APosition, f"{prefix}PHDGN-06:DET:X")
155
175
  self.d6 = FullDiagnostic(f"{prefix}PHDGN-05:", D6Position, "DET:X")
156
176
  self.d7 = create_positioner(D7Position, f"{prefix}PHDGN-07:Y")
@@ -158,6 +178,18 @@ class I10Diagnostic(Device):
158
178
  super().__init__(name)
159
179
 
160
180
 
181
+ class I10JDiagnostic(Device):
182
+ """Collection of all the diagnostic stage on i10-1."""
183
+
184
+ def __init__(self, prefix, name: str = "") -> None:
185
+ self.dj1 = ScreenCam(prefix=prefix + "PHDGN-01:")
186
+ self.dj2 = create_positioner(CellPosition, f"{prefix}IONC-01:Y")
187
+ self.dj2A = create_positioner(D2jPosition, f"{prefix}PHDGN-03:DET:X")
188
+ self.dj3 = FullDiagnostic(f"{prefix}PHDGN-02:", D3jPosition, "DET:X")
189
+
190
+ super().__init__(name)
191
+
192
+
161
193
  class I10Diagnostic5ADet(Device):
162
194
  """Diagnostic 5a detection with drain current and photo diode"""
163
195