mx-bluesky 1.4.1a0__py3-none-any.whl → 1.4.3__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 (105) hide show
  1. mx_bluesky/_version.py +2 -2
  2. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +178 -0
  3. mx_bluesky/beamlines/i24/serial/__init__.py +0 -6
  4. mx_bluesky/beamlines/i24/serial/dcid.py +125 -151
  5. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +1 -1
  6. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +88 -43
  7. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/DiamondChipI24-py3v1.edl +1 -1
  8. mx_bluesky/beamlines/i24/serial/fixed_target/FT-gui-edm/MappingLite-oxford_py3v1.edl +2 -46
  9. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +85 -122
  10. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +58 -66
  11. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_StartUp_py3v1.py +1 -19
  12. mx_bluesky/beamlines/i24/serial/parameters/__init__.py +11 -2
  13. mx_bluesky/beamlines/i24/serial/parameters/constants.py +16 -2
  14. mx_bluesky/beamlines/i24/serial/parameters/experiment_parameters.py +94 -19
  15. mx_bluesky/beamlines/i24/serial/parameters/utils.py +19 -0
  16. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +2 -0
  17. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +61 -8
  18. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +81 -40
  19. mx_bluesky/beamlines/i24/serial/write_nexus.py +66 -67
  20. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/aperture_change_callback.py +1 -1
  21. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/grid_detection_callback.py +19 -1
  22. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/ispyb_callback_base.py +40 -34
  23. mx_bluesky/{hyperion → common}/external_interaction/callbacks/common/ispyb_mapping.py +4 -4
  24. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/logging_callback.py +1 -1
  25. mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/zocalo_callback.py +14 -9
  26. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/ispyb_callback.py +46 -38
  27. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/ispyb_mapping.py +2 -2
  28. mx_bluesky/{hyperion → common}/external_interaction/callbacks/xray_centre/nexus_callback.py +20 -15
  29. mx_bluesky/common/external_interaction/config_server.py +11 -0
  30. mx_bluesky/common/external_interaction/ispyb/__init__.py +0 -0
  31. mx_bluesky/{hyperion → common}/external_interaction/ispyb/data_model.py +2 -0
  32. mx_bluesky/{hyperion → common}/external_interaction/ispyb/exp_eye_store.py +67 -17
  33. mx_bluesky/{hyperion → common}/external_interaction/ispyb/ispyb_store.py +20 -18
  34. mx_bluesky/{hyperion → common}/external_interaction/ispyb/ispyb_utils.py +2 -2
  35. mx_bluesky/common/external_interaction/nexus/__init__.py +0 -0
  36. mx_bluesky/{hyperion → common}/external_interaction/nexus/nexus_utils.py +21 -6
  37. mx_bluesky/{hyperion → common}/external_interaction/nexus/write_nexus.py +5 -5
  38. mx_bluesky/common/external_interaction/test_config_server.py +38 -0
  39. mx_bluesky/common/parameters/components.py +10 -8
  40. mx_bluesky/common/parameters/constants.py +6 -0
  41. mx_bluesky/common/parameters/gridscan.py +102 -53
  42. mx_bluesky/common/plans/do_fgs.py +4 -4
  43. mx_bluesky/{hyperion → common/utils}/exceptions.py +27 -1
  44. mx_bluesky/common/utils/log.py +17 -7
  45. mx_bluesky/hyperion/__main__.py +15 -14
  46. mx_bluesky/hyperion/device_setup_plans/check_beamstop.py +27 -0
  47. mx_bluesky/hyperion/device_setup_plans/dcm_pitch_roll_mirror_adjuster.py +34 -37
  48. mx_bluesky/hyperion/device_setup_plans/manipulate_sample.py +7 -7
  49. mx_bluesky/hyperion/device_setup_plans/position_detector.py +1 -1
  50. mx_bluesky/hyperion/device_setup_plans/read_hardware_for_setup.py +3 -3
  51. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +21 -4
  52. mx_bluesky/hyperion/device_setup_plans/setup_zebra.py +62 -36
  53. mx_bluesky/hyperion/device_setup_plans/smargon.py +3 -3
  54. mx_bluesky/hyperion/device_setup_plans/utils.py +4 -0
  55. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +8 -8
  56. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +28 -17
  57. mx_bluesky/hyperion/experiment_plans/common/xrc_result.py +10 -1
  58. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +9 -9
  59. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +54 -58
  60. mx_bluesky/hyperion/experiment_plans/grid_detect_then_xray_centre_plan.py +22 -31
  61. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +57 -40
  62. mx_bluesky/hyperion/experiment_plans/oav_grid_detection_plan.py +3 -3
  63. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +8 -2
  64. mx_bluesky/hyperion/experiment_plans/optimise_attenuation_plan.py +6 -14
  65. mx_bluesky/hyperion/experiment_plans/pin_centre_then_xray_centre_plan.py +12 -11
  66. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +4 -4
  67. mx_bluesky/hyperion/experiment_plans/robot_load_and_change_energy.py +39 -30
  68. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +36 -18
  69. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +33 -21
  70. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +10 -9
  71. mx_bluesky/hyperion/external_interaction/callbacks/__init__.py +0 -4
  72. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +31 -20
  73. mx_bluesky/hyperion/external_interaction/callbacks/common/callback_util.py +46 -30
  74. mx_bluesky/hyperion/external_interaction/callbacks/robot_load/ispyb_callback.py +39 -24
  75. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_callback.py +25 -24
  76. mx_bluesky/hyperion/external_interaction/callbacks/rotation/ispyb_mapping.py +1 -1
  77. mx_bluesky/hyperion/external_interaction/callbacks/rotation/nexus_callback.py +13 -9
  78. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/__init__.py +0 -0
  79. mx_bluesky/hyperion/external_interaction/callbacks/sample_handling/sample_handling_callback.py +50 -0
  80. mx_bluesky/hyperion/external_interaction/config_server.py +15 -1
  81. mx_bluesky/hyperion/parameters/components.py +3 -2
  82. mx_bluesky/hyperion/parameters/constants.py +1 -0
  83. mx_bluesky/hyperion/parameters/gridscan.py +56 -89
  84. mx_bluesky/hyperion/parameters/load_centre_collect.py +51 -6
  85. mx_bluesky/hyperion/parameters/robot_load.py +40 -0
  86. mx_bluesky/hyperion/parameters/rotation.py +28 -3
  87. mx_bluesky/hyperion/utils/context.py +1 -1
  88. mx_bluesky/hyperion/utils/validation.py +5 -3
  89. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/METADATA +6 -6
  90. mx_bluesky-1.4.3.dist-info/RECORD +155 -0
  91. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/WHEEL +1 -1
  92. mx_bluesky/common/parameters/robot_load.py +0 -16
  93. mx_bluesky/hyperion/external_interaction/exceptions.py +0 -13
  94. mx_bluesky/hyperion/log.py +0 -15
  95. mx_bluesky-1.4.1a0.dist-info/RECORD +0 -150
  96. /mx_bluesky/{hyperion/external_interaction/callbacks/xray_centre → common/external_interaction}/__init__.py +0 -0
  97. /mx_bluesky/{hyperion/external_interaction/ispyb → common/external_interaction/callbacks/common}/__init__.py +0 -0
  98. /mx_bluesky/{hyperion → common}/external_interaction/callbacks/common/abstract_event.py +0 -0
  99. /mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/log_uid_tag_callback.py +0 -0
  100. /mx_bluesky/{hyperion/external_interaction/callbacks → common/external_interaction/callbacks/common}/plan_reactive_callback.py +0 -0
  101. /mx_bluesky/{hyperion/external_interaction/nexus → common/external_interaction/callbacks/xray_centre}/__init__.py +0 -0
  102. /mx_bluesky/{hyperion → common}/utils/utils.py +0 -0
  103. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/LICENSE +0 -0
  104. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/entry_points.txt +0 -0
  105. {mx_bluesky-1.4.1a0.dist-info → mx_bluesky-1.4.3.dist-info}/top_level.txt +0 -0
@@ -15,22 +15,30 @@ import bluesky.plan_stubs as bps
15
15
  import numpy as np
16
16
  from bluesky.utils import MsgGenerator
17
17
  from dodal.common import inject
18
+ from dodal.devices.attenuator.attenuator import ReadOnlyAttenuator
18
19
  from dodal.devices.i24.beamstop import Beamstop, BeamstopPositions
19
20
  from dodal.devices.i24.dual_backlight import BacklightPositions, DualBacklight
20
21
  from dodal.devices.i24.i24_detector_motion import DetectorMotion
21
22
  from dodal.devices.i24.pmac import PMAC, EncReset, LaserSettings
22
23
 
23
- from mx_bluesky.beamlines.i24.serial.fixed_target.ft_utils import ChipType, Fiducials
24
+ from mx_bluesky.beamlines.i24.serial.fixed_target.ft_utils import (
25
+ ChipType,
26
+ Fiducials,
27
+ MappingType,
28
+ )
24
29
  from mx_bluesky.beamlines.i24.serial.log import (
25
30
  SSX_LOGGER,
26
31
  _read_visit_directory_from_file,
27
32
  log_on_entry,
28
33
  )
29
- from mx_bluesky.beamlines.i24.serial.parameters import get_chip_format
34
+ from mx_bluesky.beamlines.i24.serial.parameters import (
35
+ FixedTargetParameters,
36
+ get_chip_format,
37
+ get_chip_map,
38
+ )
30
39
  from mx_bluesky.beamlines.i24.serial.parameters.constants import (
31
40
  CS_FILES_PATH,
32
41
  LITEMAP_PATH,
33
- PARAM_FILE_NAME,
34
42
  PARAM_FILE_PATH_FT,
35
43
  PVAR_FILE_PATH,
36
44
  )
@@ -46,6 +54,8 @@ CHIP_MOVES = {
46
54
  ChipType.Custom: 25.40,
47
55
  ChipType.Minichip: 25.40,
48
56
  }
57
+ OXFORD_CHIP_WIDTH = 8
58
+ PVAR_TEMPLATE = f"P3%0{2}d1"
49
59
  CHIPTYPE_PV = pv.me14e_gp1
50
60
  MAPTYPE_PV = pv.me14e_gp2
51
61
  NUM_EXPOSURES_PV = pv.me14e_gp3
@@ -106,21 +116,35 @@ def initialise_stages(
106
116
 
107
117
 
108
118
  @log_on_entry
109
- def write_parameter_file(
119
+ def read_parameters(
110
120
  detector_stage: DetectorMotion,
121
+ attenuator: ReadOnlyAttenuator,
111
122
  ) -> MsgGenerator:
112
- param_path: Path = PARAM_FILE_PATH_FT
113
- # Create directory if it doesn't yet exist.
114
- param_path.mkdir(parents=True, exist_ok=True)
123
+ """ Read the parameters from user input and create the parameter model for a fixed \
124
+ target collection.
115
125
 
116
- SSX_LOGGER.info(
117
- f"Writing Parameter File: {(param_path / PARAM_FILE_NAME).as_posix()}"
118
- )
126
+ Args:
127
+ detector_stage (DetectorMotion): The detector stage device.
128
+ attenuator (ReadOnlyAttenuator): A read-only attenuator device to get the \
129
+ transmission value.
130
+
131
+ Returns:
132
+ FixedTargetParameters: Parameter model for fixed target collections
133
+
134
+ """
135
+ SSX_LOGGER.info("Creating parameter model from input.")
119
136
 
120
137
  filename = caget(pv.me14e_chip_name)
121
138
  det_type = yield from get_detector_type(detector_stage)
122
139
  chip_params = get_chip_format(ChipType(int(caget(CHIPTYPE_PV))))
123
140
  map_type = int(caget(MAPTYPE_PV))
141
+ if map_type == MappingType.Lite and chip_params.chip_type in [
142
+ ChipType.Oxford,
143
+ ChipType.OxfordInner,
144
+ ]:
145
+ chip_map = get_chip_map()
146
+ else:
147
+ chip_map = []
124
148
  pump_repeat = int(caget(PUMP_REPEAT_PV))
125
149
 
126
150
  # If file name ends in a digit this causes processing/pilatus pain.
@@ -136,6 +160,8 @@ def write_parameter_file(
136
160
  f"Requested filename ends in a number. Appended dash: {filename}"
137
161
  )
138
162
 
163
+ transmission = yield from bps.rd(attenuator.actual_transmission)
164
+
139
165
  params_dict = {
140
166
  "visit": _read_visit_directory_from_file().as_posix(), # noqa
141
167
  "directory": caget(pv.me14e_filepath),
@@ -144,24 +170,24 @@ def write_parameter_file(
144
170
  "detector_distance_mm": caget(pv.me14e_dcdetdist),
145
171
  "detector_name": str(det_type),
146
172
  "num_exposures": int(caget(NUM_EXPOSURES_PV)),
147
- "chip": chip_params.dict(),
173
+ "transmission": transmission,
174
+ "chip": chip_params.model_dump(),
148
175
  "map_type": map_type,
149
176
  "pump_repeat": pump_repeat,
150
177
  "checker_pattern": bool(caget(pv.me14e_gp111)),
151
- "laser_dwell_s": float(caget(pv.me14e_gp103)) if pump_repeat != 0 else None,
152
- "laser_delay_s": float(caget(pv.me14e_gp110)) if pump_repeat != 0 else None,
178
+ "chip_map": chip_map,
179
+ "laser_dwell_s": float(caget(pv.me14e_gp103)) if pump_repeat != 0 else 0.0,
180
+ "laser_delay_s": float(caget(pv.me14e_gp110)) if pump_repeat != 0 else 0.0,
153
181
  "pre_pump_exposure_s": float(caget(pv.me14e_gp109))
154
182
  if pump_repeat != 0
155
183
  else None,
156
184
  }
157
185
 
158
- with open(param_path / PARAM_FILE_NAME, "w") as f:
159
- json.dump(params_dict, f, indent=4)
160
-
161
- SSX_LOGGER.info("Information written to file \n")
186
+ SSX_LOGGER.info("Parameters for I24 serial collection: \n")
162
187
  SSX_LOGGER.info(pformat(params_dict))
163
188
 
164
189
  yield from bps.null()
190
+ return FixedTargetParameters(**params_dict)
165
191
 
166
192
 
167
193
  def scrape_pvar_file(fid: str, pvar_dir: Path = PVAR_FILE_PATH):
@@ -216,59 +242,25 @@ def define_current_chip(
216
242
 
217
243
 
218
244
  @log_on_entry
219
- def save_screen_map() -> MsgGenerator:
220
- litemap_path: Path = LITEMAP_PATH
221
- litemap_path.mkdir(parents=True, exist_ok=True)
222
-
223
- SSX_LOGGER.info(f"Saving {litemap_path.as_posix()} currentchip.map")
224
- with open(litemap_path / "currentchip.map", "w") as f:
225
- SSX_LOGGER.debug("Printing only blocks with block_val == 1")
226
- for x in range(1, 82):
227
- block_str = f"ME14E-MO-IOC-01:GP{x + 10:d}"
228
- block_val = int(caget(block_str))
229
- if block_val == 1:
230
- SSX_LOGGER.info(f"{block_str} {block_val:d}")
231
- line = f"{x:02d}status P3{x:02d}1 \t{block_val}\n"
232
- f.write(line)
233
- yield from bps.null()
245
+ def upload_chip_map_to_geobrick(pmac: PMAC, chip_map: list[int]) -> MsgGenerator:
246
+ """Upload the map parameters for an Oxford-type chip (width=8) to the geobrick.
234
247
 
248
+ Args:
249
+ pmac (PMAC): The PMAC device.
250
+ chip_map (list[int]): A list of selected blocks to be collected.
235
251
 
236
- @log_on_entry
237
- def upload_parameters(pmac: PMAC = inject("pmac")) -> MsgGenerator:
252
+ """
238
253
  SSX_LOGGER.info("Uploading Parameters for Oxford Chip to the GeoBrick")
239
- caput(CHIPTYPE_PV, 0)
240
- width = 8
241
-
242
- map_file: Path = LITEMAP_PATH / "currentchip.map"
243
- if not map_file.exists():
244
- raise FileNotFoundError(f"The file {map_file} has not yet been created")
245
-
246
- with open(map_file) as f:
247
- SSX_LOGGER.info(f"Chipid {ChipType.Oxford}")
248
- SSX_LOGGER.info(f"width {width}")
249
- x = 1
250
- for line in f.readlines()[: width**2]:
251
- cols = line.split()
252
- pvar = cols[1]
253
- value = cols[2]
254
- s = pvar + "=" + value
255
- if value != "1":
256
- s2 = pvar + " "
257
- sys.stdout.write(s2)
258
- else:
259
- sys.stdout.write(s + " ")
260
- sys.stdout.flush()
261
- if x == width:
262
- print()
263
- x = 1
264
- else:
265
- x += 1
266
- yield from bps.abs_set(pmac.pmac_string, s, wait=True)
267
- sleep(0.02)
268
-
269
- SSX_LOGGER.warning("Automatic Setting Mapping Type to Lite has been disabled")
254
+ SSX_LOGGER.info(f"Chipid {ChipType.Oxford}, width {OXFORD_CHIP_WIDTH}")
255
+ for block in range(1, 65):
256
+ value = 1 if block in chip_map else 0
257
+ pvar = PVAR_TEMPLATE % block
258
+ pvar_str = f"{pvar}={value}"
259
+ SSX_LOGGER.debug(f"Set {pvar_str} for block {block}")
260
+ yield from bps.abs_set(pmac.pmac_string, pvar_str, wait=True)
261
+ # Wait for PMAC to be done processing PVAR string
262
+ sleep(0.02)
270
263
  SSX_LOGGER.debug("Upload parameters done.")
271
- yield from bps.null()
272
264
 
273
265
 
274
266
  @log_on_entry
@@ -3,28 +3,10 @@ Startup utilities for chip
3
3
  """
4
4
 
5
5
  import string
6
- from pathlib import Path
7
6
 
8
7
  from mx_bluesky.beamlines.i24.serial.fixed_target.ft_utils import ChipType
9
8
  from mx_bluesky.beamlines.i24.serial.log import SSX_LOGGER, log_on_entry
10
- from mx_bluesky.beamlines.i24.serial.parameters import (
11
- FixedTargetParameters,
12
- get_chip_format,
13
- )
14
- from mx_bluesky.beamlines.i24.serial.parameters.constants import (
15
- PARAM_FILE_NAME,
16
- PARAM_FILE_PATH_FT,
17
- )
18
-
19
-
20
- def read_parameter_file(
21
- param_path: Path | str = PARAM_FILE_PATH_FT,
22
- ) -> FixedTargetParameters:
23
- if not isinstance(param_path, Path):
24
- param_path = Path(param_path)
25
- params_file = param_path / PARAM_FILE_NAME
26
- params = FixedTargetParameters.from_file(params_file)
27
- return params
9
+ from mx_bluesky.beamlines.i24.serial.parameters import get_chip_format
28
10
 
29
11
 
30
12
  @log_on_entry
@@ -1,15 +1,24 @@
1
- from mx_bluesky.beamlines.i24.serial.parameters.constants import SSXType
1
+ from mx_bluesky.beamlines.i24.serial.parameters.constants import DetectorName, SSXType
2
2
  from mx_bluesky.beamlines.i24.serial.parameters.experiment_parameters import (
3
+ BeamSettings,
3
4
  ChipDescription,
4
5
  ExtruderParameters,
5
6
  FixedTargetParameters,
7
+ SerialAndLaserExperiment,
8
+ )
9
+ from mx_bluesky.beamlines.i24.serial.parameters.utils import (
10
+ get_chip_format,
11
+ get_chip_map,
6
12
  )
7
- from mx_bluesky.beamlines.i24.serial.parameters.utils import get_chip_format
8
13
 
9
14
  __all__ = [
10
15
  "SSXType",
16
+ "DetectorName",
17
+ "BeamSettings",
11
18
  "ExtruderParameters",
12
19
  "ChipDescription",
13
20
  "FixedTargetParameters",
21
+ "SerialAndLaserExperiment",
14
22
  "get_chip_format",
23
+ "get_chip_map",
15
24
  ]
@@ -1,15 +1,29 @@
1
- from enum import Enum
1
+ from enum import StrEnum
2
2
  from os import environ
3
3
  from pathlib import Path
4
4
 
5
5
  from mx_bluesky.beamlines.i24.serial.log import _read_visit_directory_from_file
6
6
 
7
7
 
8
- class SSXType(Enum):
8
+ class SSXType(StrEnum):
9
9
  FIXED = "Serial Fixed"
10
10
  EXTRUDER = "Serial Jet"
11
11
 
12
12
 
13
+ class DetectorName(StrEnum):
14
+ EIGER = "eiger"
15
+ PILATUS = "pilatus"
16
+
17
+
18
+ # TODO figue sth out for tests
19
+ LUT_FILES_PATH = Path("/dls_sw/i24/software/daq_configuration/lookup")
20
+
21
+ BEAM_CENTER_LUT_FILES = {
22
+ DetectorName.EIGER: LUT_FILES_PATH / "DetDistToBeamXYConverterE9M.txt",
23
+ DetectorName.PILATUS: LUT_FILES_PATH / "DetDistToBeamXYConverterP6M.txt",
24
+ }
25
+
26
+
13
27
  OAV_CONFIG_FILES = {
14
28
  "zoom_params_file": "/dls_sw/i24/software/gda_versions/gda_9_34/config/xml/jCameraManZoomLevels.xml",
15
29
  "oav_config_json": "/dls_sw/i24/software/daq_configuration/json/OAVCentring.json",
@@ -1,14 +1,24 @@
1
1
  import json
2
+ from abc import abstractmethod
2
3
  from pathlib import Path
3
- from typing import Literal
4
4
 
5
- from pydantic import BaseModel, field_validator
5
+ import numpy as np
6
+ from dodal.devices.detector.det_dim_constants import (
7
+ EIGER2_X_9M_SIZE,
8
+ PILATUS_6M_SIZE,
9
+ DetectorSizeConstants,
10
+ )
11
+ from pydantic import BaseModel, ConfigDict, computed_field, field_validator
6
12
 
7
13
  from mx_bluesky.beamlines.i24.serial.fixed_target.ft_utils import (
8
14
  ChipType,
9
15
  MappingType,
10
16
  PumpProbeSetting,
11
17
  )
18
+ from mx_bluesky.beamlines.i24.serial.parameters.constants import (
19
+ DetectorName,
20
+ SSXType,
21
+ )
12
22
 
13
23
 
14
24
  class SerialExperiment(BaseModel):
@@ -19,7 +29,8 @@ class SerialExperiment(BaseModel):
19
29
  filename: str
20
30
  exposure_time_s: float
21
31
  detector_distance_mm: float
22
- detector_name: Literal["eiger", "pilatus"]
32
+ detector_name: DetectorName
33
+ transmission: float
23
34
 
24
35
  @field_validator("visit", mode="before")
25
36
  @classmethod
@@ -30,29 +41,58 @@ class SerialExperiment(BaseModel):
30
41
 
31
42
  @property
32
43
  def collection_directory(self) -> Path:
33
- return Path(self.visit) / self.directory
44
+ directory = Path(self.visit) / self.directory
45
+ return directory
46
+
47
+ @property
48
+ def detector_size_constants(self) -> DetectorSizeConstants:
49
+ return (
50
+ EIGER2_X_9M_SIZE
51
+ if self.detector_name is DetectorName.EIGER
52
+ else PILATUS_6M_SIZE
53
+ )
34
54
 
35
55
 
36
56
  class LaserExperiment(BaseModel):
37
57
  """Laser settings for pump probe serial collections."""
38
58
 
39
- laser_dwell_s: float | None = None # pump exposure time
40
- laser_delay_s: float | None = None # pump delay
59
+ laser_dwell_s: float = 0.0 # pump exposure time
60
+ laser_delay_s: float = 0.0 # pump delay
41
61
  pre_pump_exposure_s: float | None = None # Pre illumination, just for chip
42
62
 
43
63
 
44
- class ExtruderParameters(SerialExperiment, LaserExperiment):
45
- """Extruder parameter model."""
46
-
47
- num_images: int
48
- pump_status: bool
49
-
64
+ class SerialAndLaserExperiment(SerialExperiment, LaserExperiment):
50
65
  @classmethod
51
66
  def from_file(cls, filename: str | Path):
52
67
  with open(filename) as fh:
53
68
  raw_params = json.load(fh)
54
69
  return cls(**raw_params)
55
70
 
71
+ @property
72
+ @abstractmethod
73
+ def nexgen_experiment_type(self) -> str:
74
+ pass
75
+
76
+ @property
77
+ @abstractmethod
78
+ def ispyb_experiment_type(self) -> SSXType:
79
+ pass
80
+
81
+
82
+ class ExtruderParameters(SerialAndLaserExperiment):
83
+ """Extruder parameter model."""
84
+
85
+ num_images: int
86
+ pump_status: bool
87
+
88
+ @property
89
+ def nexgen_experiment_type(self) -> str:
90
+ return "extruder"
91
+
92
+ @property
93
+ def ispyb_experiment_type(self) -> SSXType:
94
+ return SSXType.EXTRUDER
95
+
56
96
 
57
97
  class ChipDescription(BaseModel):
58
98
  """Parameters defining the chip in use for FT collection."""
@@ -85,8 +125,12 @@ class ChipDescription(BaseModel):
85
125
  else:
86
126
  return ((self.y_num_steps - 1) * self.y_step_size) + self.b2b_vert
87
127
 
128
+ @property
129
+ def tot_num_blocks(self) -> int:
130
+ return self.x_blocks * self.y_blocks
131
+
88
132
 
89
- class FixedTargetParameters(SerialExperiment, LaserExperiment):
133
+ class FixedTargetParameters(SerialAndLaserExperiment):
90
134
  """Fixed target parameter model."""
91
135
 
92
136
  num_exposures: int
@@ -94,10 +138,41 @@ class FixedTargetParameters(SerialExperiment, LaserExperiment):
94
138
  map_type: MappingType
95
139
  pump_repeat: PumpProbeSetting
96
140
  checker_pattern: bool = False
97
- total_num_images: int = 0 # Calculated in the code for now
141
+ chip_map: list[int]
98
142
 
99
- @classmethod
100
- def from_file(cls, filename: str | Path):
101
- with open(filename) as fh:
102
- raw_params = json.load(fh)
103
- return cls(**raw_params)
143
+ @property
144
+ def nexgen_experiment_type(self) -> str:
145
+ return "fixed-target"
146
+
147
+ @property
148
+ def ispyb_experiment_type(self) -> SSXType:
149
+ return SSXType.FIXED
150
+
151
+ @computed_field # type: ignore # Mypy doesn't like it
152
+ @property
153
+ def total_num_images(self) -> int:
154
+ match self.map_type:
155
+ case MappingType.NoMap:
156
+ if self.chip.chip_type is ChipType.Custom:
157
+ num_images = (
158
+ self.chip.x_num_steps
159
+ * self.chip.y_num_steps
160
+ * self.num_exposures
161
+ )
162
+ else:
163
+ chip_format = self.chip.chip_format[:4]
164
+ num_images = int(np.prod(chip_format) * self.num_exposures)
165
+ case MappingType.Lite:
166
+ chip_format = self.chip.chip_format[2:4]
167
+ block_count = len(self.chip_map) # type: ignore
168
+ num_images = int(
169
+ np.prod(chip_format) * block_count * self.num_exposures
170
+ )
171
+ return num_images
172
+
173
+
174
+ class BeamSettings(BaseModel):
175
+ model_config = ConfigDict(frozen=True)
176
+ wavelength_in_a: float
177
+ beam_size_in_um: tuple[float, float]
178
+ beam_center_in_mm: tuple[float, float]
@@ -6,6 +6,12 @@ from mx_bluesky.beamlines.i24.serial.parameters.experiment_parameters import (
6
6
  )
7
7
  from mx_bluesky.beamlines.i24.serial.setup_beamline import caget, pv
8
8
 
9
+ OXFORD_BLOCKS_PVS = [f"ME14E-MO-IOC-01:GP{i}" for i in range(11, 75)]
10
+
11
+
12
+ class EmptyMapError(Exception):
13
+ pass
14
+
9
15
 
10
16
  def get_chip_format(chip_type: ChipType) -> ChipDescription:
11
17
  """Default parameter values."""
@@ -40,3 +46,16 @@ def get_chip_format(chip_type: ChipType) -> ChipDescription:
40
46
  defaults["b2b_horz"] = defaults["b2b_vert"] = 0.0
41
47
  chip_params: dict[str, Any] = {"chip_type": chip_type, **defaults}
42
48
  return ChipDescription(**chip_params)
49
+
50
+
51
+ def get_chip_map() -> list[int]:
52
+ """Return a list of blocks (the 'chip map') to be collected on an Oxford type chip \
53
+ when using lite mapping."""
54
+ chipmap = []
55
+ for n, block_pv in enumerate(OXFORD_BLOCKS_PVS):
56
+ block_val = int(caget(block_pv))
57
+ if block_val == 1:
58
+ chipmap.append(n + 1)
59
+ if len(chipmap) == 0:
60
+ raise EmptyMapError("No blocks selected for Lite map.")
61
+ return chipmap
@@ -23,6 +23,8 @@ def __which__():
23
23
  print("path to pv.py: ")
24
24
 
25
25
 
26
+ requested_transmission = "BL24I-OP-ATTN-01:T2A:SETVAL1"
27
+
26
28
  # PILATUS
27
29
  pilat_filepath = "BL24I-EA-PILAT-01:cam1:FilePath"
28
30
  pilat_filename = "BL24I-EA-PILAT-01:cam1:FileName"
@@ -1,16 +1,60 @@
1
+ from pathlib import Path
1
2
  from time import sleep
2
3
 
3
4
  import bluesky.plan_stubs as bps
5
+ from dodal.beamlines import i24
6
+ from dodal.devices.detector.det_dim_constants import DetectorSizeConstants
4
7
  from dodal.devices.i24.aperture import Aperture, AperturePositions
8
+ from dodal.devices.i24.beam_center import DetectorBeamCenter
5
9
  from dodal.devices.i24.beamstop import Beamstop, BeamstopPositions
6
10
  from dodal.devices.i24.dual_backlight import BacklightPositions, DualBacklight
7
11
  from dodal.devices.i24.i24_detector_motion import DetectorMotion
12
+ from dodal.devices.util.lookup_tables import (
13
+ linear_interpolation_lut,
14
+ parse_lookup_table,
15
+ )
8
16
 
9
17
  from mx_bluesky.beamlines.i24.serial.log import SSX_LOGGER
10
18
  from mx_bluesky.beamlines.i24.serial.setup_beamline import pv
11
19
  from mx_bluesky.beamlines.i24.serial.setup_beamline.ca import caget, caput
12
20
 
13
21
 
22
+ def get_beam_center_device(detector_in_use: str) -> DetectorBeamCenter:
23
+ if detector_in_use == "eiger":
24
+ return i24.eiger_beam_center()
25
+ else:
26
+ return i24.pilatus_beam_center()
27
+
28
+
29
+ def compute_beam_center_position_from_lut(
30
+ lut_path: Path,
31
+ detector_distance_mm: float,
32
+ det_size_constants: DetectorSizeConstants,
33
+ ) -> tuple[float, float]:
34
+ """Calculate the beam center position for the detector distance \
35
+ using the values in the lookup table for the conversion.
36
+ """
37
+ lut_values = parse_lookup_table(lut_path.as_posix())
38
+
39
+ calc_x = linear_interpolation_lut(lut_values[0], lut_values[1])
40
+ beam_x_mm = calc_x(detector_distance_mm)
41
+ beam_x = (
42
+ beam_x_mm
43
+ * det_size_constants.det_size_pixels.width
44
+ / det_size_constants.det_dimension.width
45
+ )
46
+
47
+ calc_y = linear_interpolation_lut(lut_values[0], lut_values[2])
48
+ beam_y_mm = calc_y(detector_distance_mm)
49
+ beam_y = (
50
+ beam_y_mm
51
+ * det_size_constants.det_size_pixels.height
52
+ / det_size_constants.det_dimension.height
53
+ )
54
+
55
+ return beam_x, beam_y
56
+
57
+
14
58
  def setup_beamline_for_collection_plan(
15
59
  aperture: Aperture,
16
60
  backlight: DualBacklight,
@@ -43,6 +87,23 @@ def move_detector_stage_to_position_plan(
43
87
  yield from bps.mv(detector_stage.z, detector_distance) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
44
88
 
45
89
 
90
+ def set_detector_beam_center_plan(
91
+ beam_center_device: DetectorBeamCenter,
92
+ beam_center_pixels: tuple[float, float],
93
+ group: str = "set_beamcenter",
94
+ wait: bool = True,
95
+ ):
96
+ """A small temporary plan to set up the beam center on the detector in use."""
97
+ # NOTE This will be removed once the detectors are using ophyd_async devices
98
+ # See https://github.com/DiamondLightSource/mx-bluesky/issues/62
99
+ beam_position_x, beam_position_y = beam_center_pixels
100
+ SSX_LOGGER.info(f"Setting beam center to: {beam_position_x}, {beam_position_y}")
101
+ yield from bps.abs_set(beam_center_device.beam_x, beam_position_x, group=group)
102
+ yield from bps.abs_set(beam_center_device.beam_y, beam_position_y, group=group)
103
+ if wait:
104
+ yield from bps.wait(group=group)
105
+
106
+
46
107
  def modechange(action):
47
108
  """Mode Change"""
48
109
  # Pin Hand Mount
@@ -232,11 +293,6 @@ def pilatus(action, args_list):
232
293
  # caput(pv.pilat_wavelength, caget(pv.dcm_lambda))
233
294
  caput(pv.pilat_detdist, caget(pv.det_z))
234
295
  caput(pv.pilat_filtertrasm, caget(pv.attn_match))
235
- SSX_LOGGER.warning("WARNING: Have you set beam X and Y?")
236
- # 16 Fed 2022 last change DA
237
- caput(pv.pilat_beamx, 1284.7)
238
- caput(pv.pilat_beamy, 1308.6)
239
- sleep(0.1)
240
296
 
241
297
  # Fixed Target stage (very fast start and stop w/ triggering from GeoBrick
242
298
  if action == "fastchip":
@@ -330,11 +386,8 @@ def eiger(action, args_list):
330
386
  SSX_LOGGER.debug(f"Argument: {arg}")
331
387
  # caput(pv.eiger_wavelength, caget(pv.dcm_lambda))
332
388
  caput(pv.eiger_detdist, str(float(caget(pv.det_z)) / 1000))
333
- SSX_LOGGER.warning("WARNING: Have you set header info?")
334
389
  caput(pv.eiger_wavelength, caget(pv.dcm_lambda))
335
390
  caput(pv.eiger_omegaincr, 0.0)
336
- caput(pv.eiger_beamx, 1600.0)
337
- caput(pv.eiger_beamy, 1697.4)
338
391
  sleep(0.1)
339
392
  # Setup common to all collections ###
340
393
  caput(pv.eiger_filewriter, "No")