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
@@ -7,10 +7,24 @@ from bluesky.callbacks.zmq import Proxy, RemoteDispatcher
7
7
  from dodal.log import LOGGER as dodal_logger
8
8
  from dodal.log import set_up_all_logging_handlers
9
9
 
10
- from mx_bluesky.common.utils.log import _get_logging_dir, tag_filter
11
- from mx_bluesky.hyperion.external_interaction.callbacks.log_uid_tag_callback import (
10
+ from mx_bluesky.common.external_interaction.callbacks.common.log_uid_tag_callback import (
12
11
  LogUidTaggingCallback,
13
12
  )
13
+ from mx_bluesky.common.external_interaction.callbacks.common.zocalo_callback import (
14
+ ZocaloCallback,
15
+ )
16
+ from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
17
+ GridscanISPyBCallback,
18
+ )
19
+ from mx_bluesky.common.external_interaction.callbacks.xray_centre.nexus_callback import (
20
+ GridscanNexusFileCallback,
21
+ )
22
+ from mx_bluesky.common.utils.log import (
23
+ ISPYB_ZOCALO_CALLBACK_LOGGER,
24
+ NEXUS_LOGGER,
25
+ _get_logging_dir,
26
+ tag_filter,
27
+ )
14
28
  from mx_bluesky.hyperion.external_interaction.callbacks.robot_load.ispyb_callback import (
15
29
  RobotLoadISPyBCallback,
16
30
  )
@@ -20,21 +34,15 @@ from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_callback
20
34
  from mx_bluesky.hyperion.external_interaction.callbacks.rotation.nexus_callback import (
21
35
  RotationNexusFileCallback,
22
36
  )
23
- from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callback import (
24
- GridscanISPyBCallback,
25
- )
26
- from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.nexus_callback import (
27
- GridscanNexusFileCallback,
28
- )
29
- from mx_bluesky.hyperion.external_interaction.callbacks.zocalo_callback import (
30
- ZocaloCallback,
31
- )
32
- from mx_bluesky.hyperion.log import (
33
- ISPYB_LOGGER,
34
- NEXUS_LOGGER,
37
+ from mx_bluesky.hyperion.external_interaction.callbacks.sample_handling.sample_handling_callback import (
38
+ SampleHandlingCallback,
35
39
  )
36
40
  from mx_bluesky.hyperion.parameters.cli import parse_callback_dev_mode_arg
37
41
  from mx_bluesky.hyperion.parameters.constants import CONST
42
+ from mx_bluesky.hyperion.parameters.gridscan import (
43
+ GridCommonWithHyperionDetectorParams,
44
+ HyperionSpecifiedThreeDGridScan,
45
+ )
38
46
 
39
47
  LIVENESS_POLL_SECONDS = 1
40
48
  ERROR_LOG_BUFFER_LINES = 5000
@@ -43,18 +51,21 @@ ERROR_LOG_BUFFER_LINES = 5000
43
51
  def setup_callbacks():
44
52
  zocalo = ZocaloCallback()
45
53
  return [
46
- GridscanNexusFileCallback(),
47
- GridscanISPyBCallback(emit=zocalo),
54
+ GridscanNexusFileCallback(param_type=HyperionSpecifiedThreeDGridScan),
55
+ GridscanISPyBCallback(
56
+ param_type=GridCommonWithHyperionDetectorParams, emit=zocalo
57
+ ),
48
58
  RotationNexusFileCallback(),
49
59
  RotationISPyBCallback(emit=zocalo),
50
60
  LogUidTaggingCallback(),
51
61
  RobotLoadISPyBCallback(),
62
+ SampleHandlingCallback(),
52
63
  ]
53
64
 
54
65
 
55
66
  def setup_logging(dev_mode: bool):
56
67
  for logger, filename in [
57
- (ISPYB_LOGGER, "hyperion_ispyb_callback.log"),
68
+ (ISPYB_ZOCALO_CALLBACK_LOGGER, "hyperion_ispyb_callback.log"),
58
69
  (NEXUS_LOGGER, "hyperion_nexus_callback.log"),
59
70
  ]:
60
71
  if logger.handlers == []:
@@ -70,7 +81,7 @@ def setup_logging(dev_mode: bool):
70
81
  log_info(f"Loggers initialised with dev_mode={dev_mode}")
71
82
  nexgen_logger = logging.getLogger("nexgen")
72
83
  nexgen_logger.parent = NEXUS_LOGGER
73
- dodal_logger.parent = ISPYB_LOGGER
84
+ dodal_logger.parent = ISPYB_ZOCALO_CALLBACK_LOGGER
74
85
  log_debug("nexgen logger added to nexus logger")
75
86
 
76
87
 
@@ -90,12 +101,12 @@ def setup_threads():
90
101
 
91
102
 
92
103
  def log_info(msg, *args, **kwargs):
93
- ISPYB_LOGGER.info(msg, *args, **kwargs)
104
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(msg, *args, **kwargs)
94
105
  NEXUS_LOGGER.info(msg, *args, **kwargs)
95
106
 
96
107
 
97
108
  def log_debug(msg, *args, **kwargs):
98
- ISPYB_LOGGER.debug(msg, *args, **kwargs)
109
+ ISPYB_ZOCALO_CALLBACK_LOGGER.debug(msg, *args, **kwargs)
99
110
  NEXUS_LOGGER.debug(msg, *args, **kwargs)
100
111
 
101
112
 
@@ -2,6 +2,15 @@ from collections.abc import Callable
2
2
 
3
3
  from bluesky.callbacks import CallbackBase
4
4
 
5
+ from mx_bluesky.common.external_interaction.callbacks.common.zocalo_callback import (
6
+ ZocaloCallback,
7
+ )
8
+ from mx_bluesky.common.external_interaction.callbacks.xray_centre.ispyb_callback import (
9
+ GridscanISPyBCallback,
10
+ )
11
+ from mx_bluesky.common.external_interaction.callbacks.xray_centre.nexus_callback import (
12
+ GridscanNexusFileCallback,
13
+ )
5
14
  from mx_bluesky.hyperion.external_interaction.callbacks.robot_load.ispyb_callback import (
6
15
  RobotLoadISPyBCallback,
7
16
  )
@@ -11,54 +20,61 @@ from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_callback
11
20
  from mx_bluesky.hyperion.external_interaction.callbacks.rotation.nexus_callback import (
12
21
  RotationNexusFileCallback,
13
22
  )
14
- from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.ispyb_callback import (
15
- GridscanISPyBCallback,
23
+ from mx_bluesky.hyperion.external_interaction.callbacks.sample_handling.sample_handling_callback import (
24
+ SampleHandlingCallback,
16
25
  )
17
- from mx_bluesky.hyperion.external_interaction.callbacks.xray_centre.nexus_callback import (
18
- GridscanNexusFileCallback,
19
- )
20
- from mx_bluesky.hyperion.external_interaction.callbacks.zocalo_callback import (
21
- ZocaloCallback,
26
+ from mx_bluesky.hyperion.parameters.gridscan import (
27
+ GridCommonWithHyperionDetectorParams,
28
+ HyperionSpecifiedThreeDGridScan,
22
29
  )
23
30
 
24
31
  CallbacksFactory = Callable[[], tuple[CallbackBase, ...]]
25
32
 
26
33
 
27
- def create_robot_load_and_centre_callbacks() -> (
28
- tuple[GridscanNexusFileCallback, GridscanISPyBCallback, RobotLoadISPyBCallback]
29
- ):
34
+ def create_robot_load_and_centre_callbacks() -> tuple[
35
+ GridscanNexusFileCallback, GridscanISPyBCallback, RobotLoadISPyBCallback
36
+ ]:
30
37
  return (
31
- GridscanNexusFileCallback(),
32
- GridscanISPyBCallback(emit=ZocaloCallback()),
38
+ GridscanNexusFileCallback(param_type=HyperionSpecifiedThreeDGridScan),
39
+ GridscanISPyBCallback(
40
+ param_type=GridCommonWithHyperionDetectorParams, emit=ZocaloCallback()
41
+ ),
33
42
  RobotLoadISPyBCallback(),
34
43
  )
35
44
 
36
45
 
37
- def create_gridscan_callbacks() -> (
38
- tuple[GridscanNexusFileCallback, GridscanISPyBCallback]
39
- ):
40
- return (GridscanNexusFileCallback(), GridscanISPyBCallback(emit=ZocaloCallback()))
46
+ def create_gridscan_callbacks() -> tuple[
47
+ GridscanNexusFileCallback, GridscanISPyBCallback
48
+ ]:
49
+ return (
50
+ GridscanNexusFileCallback(param_type=HyperionSpecifiedThreeDGridScan),
51
+ GridscanISPyBCallback(
52
+ param_type=GridCommonWithHyperionDetectorParams, emit=ZocaloCallback()
53
+ ),
54
+ )
41
55
 
42
56
 
43
- def create_rotation_callbacks() -> (
44
- tuple[RotationNexusFileCallback, RotationISPyBCallback]
45
- ):
57
+ def create_rotation_callbacks() -> tuple[
58
+ RotationNexusFileCallback, RotationISPyBCallback
59
+ ]:
46
60
  return (RotationNexusFileCallback(), RotationISPyBCallback(emit=ZocaloCallback()))
47
61
 
48
62
 
49
- def create_load_centre_collect_callbacks() -> (
50
- tuple[
51
- GridscanNexusFileCallback,
52
- GridscanISPyBCallback,
53
- RobotLoadISPyBCallback,
54
- RotationNexusFileCallback,
55
- RotationISPyBCallback,
56
- ]
57
- ):
63
+ def create_load_centre_collect_callbacks() -> tuple[
64
+ GridscanNexusFileCallback,
65
+ GridscanISPyBCallback,
66
+ RobotLoadISPyBCallback,
67
+ RotationNexusFileCallback,
68
+ RotationISPyBCallback,
69
+ SampleHandlingCallback,
70
+ ]:
58
71
  return (
59
- GridscanNexusFileCallback(),
60
- GridscanISPyBCallback(emit=ZocaloCallback()),
72
+ GridscanNexusFileCallback(param_type=HyperionSpecifiedThreeDGridScan),
73
+ GridscanISPyBCallback(
74
+ param_type=GridCommonWithHyperionDetectorParams, emit=ZocaloCallback()
75
+ ),
61
76
  RobotLoadISPyBCallback(),
62
77
  RotationNexusFileCallback(),
63
78
  RotationISPyBCallback(emit=ZocaloCallback()),
79
+ SampleHandlingCallback(),
64
80
  )
@@ -2,19 +2,18 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from event_model.documents import EventDescriptor
6
-
7
- from mx_bluesky.hyperion.external_interaction.callbacks.common.ispyb_mapping import (
5
+ from mx_bluesky.common.external_interaction.callbacks.common.ispyb_mapping import (
8
6
  get_proposal_and_session_from_visit_string,
9
7
  )
10
- from mx_bluesky.hyperion.external_interaction.callbacks.plan_reactive_callback import (
8
+ from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
11
9
  PlanReactiveCallback,
12
10
  )
13
- from mx_bluesky.hyperion.external_interaction.ispyb.exp_eye_store import (
11
+ from mx_bluesky.common.external_interaction.ispyb.exp_eye_store import (
12
+ BLSampleStatus,
14
13
  ExpeyeInteraction,
15
14
  RobotActionID,
16
15
  )
17
- from mx_bluesky.hyperion.log import ISPYB_LOGGER
16
+ from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER
18
17
  from mx_bluesky.hyperion.parameters.constants import CONST
19
18
 
20
19
  if TYPE_CHECKING:
@@ -23,28 +22,35 @@ if TYPE_CHECKING:
23
22
 
24
23
  class RobotLoadISPyBCallback(PlanReactiveCallback):
25
24
  def __init__(self) -> None:
26
- ISPYB_LOGGER.debug("Initialising ISPyB Robot Load Callback")
27
- super().__init__(log=ISPYB_LOGGER)
25
+ ISPYB_ZOCALO_CALLBACK_LOGGER.debug("Initialising ISPyB Robot Load Callback")
26
+ super().__init__(log=ISPYB_ZOCALO_CALLBACK_LOGGER)
27
+ self._metadata: dict | None = None
28
+
28
29
  self.run_uid: str | None = None
29
30
  self.descriptors: dict[str, EventDescriptor] = {}
30
31
  self.action_id: RobotActionID | None = None
31
32
  self.expeye = ExpeyeInteraction()
32
33
 
33
34
  def activity_gated_start(self, doc: RunStart):
34
- ISPYB_LOGGER.debug("ISPyB robot load callback received start document.")
35
+ ISPYB_ZOCALO_CALLBACK_LOGGER.debug(
36
+ "ISPyB robot load callback received start document."
37
+ )
35
38
  if doc.get("subplan_name") == CONST.PLAN.ROBOT_LOAD:
36
- ISPYB_LOGGER.debug(f"ISPyB robot load callback received: {doc}")
39
+ ISPYB_ZOCALO_CALLBACK_LOGGER.debug(
40
+ f"ISPyB robot load callback received: {doc}"
41
+ )
37
42
  self.run_uid = doc.get("uid")
38
- assert isinstance(metadata := doc.get("metadata"), dict)
43
+ self._metadata = doc.get("metadata")
44
+ assert isinstance(self._metadata, dict)
39
45
  proposal, session = get_proposal_and_session_from_visit_string(
40
- metadata["visit"]
46
+ self._metadata["visit"]
41
47
  )
42
48
  self.action_id = self.expeye.start_load(
43
49
  proposal,
44
50
  session,
45
- metadata["sample_id"],
46
- metadata["sample_puck"],
47
- metadata["sample_pin"],
51
+ self._metadata["sample_id"],
52
+ self._metadata["sample_puck"],
53
+ self._metadata["sample_pin"],
48
54
  )
49
55
  return super().activity_gated_start(doc)
50
56
 
@@ -58,9 +64,9 @@ class RobotLoadISPyBCallback(PlanReactiveCallback):
58
64
  event_descriptor
59
65
  and event_descriptor.get("name") == CONST.DESCRIPTORS.ROBOT_LOAD
60
66
  ):
61
- assert (
62
- self.action_id is not None
63
- ), "ISPyB Robot load callback event called unexpectedly"
67
+ assert self.action_id is not None, (
68
+ "ISPyB Robot load callback event called unexpectedly"
69
+ )
64
70
  barcode = doc["data"]["robot-barcode"]
65
71
  oav_snapshot = doc["data"]["oav-snapshot-last_saved_path"]
66
72
  webcam_snapshot = doc["data"]["webcam-last_saved_path"]
@@ -72,15 +78,24 @@ class RobotLoadISPyBCallback(PlanReactiveCallback):
72
78
  return super().activity_gated_event(doc)
73
79
 
74
80
  def activity_gated_stop(self, doc: RunStop) -> RunStop | None:
75
- ISPYB_LOGGER.debug("ISPyB robot load callback received stop document.")
81
+ ISPYB_ZOCALO_CALLBACK_LOGGER.debug(
82
+ "ISPyB robot load callback received stop document."
83
+ )
76
84
  if doc.get("run_start") == self.run_uid:
77
- assert (
78
- self.action_id is not None
79
- ), "ISPyB Robot load callback stop called unexpectedly"
80
- exit_status = (
81
- doc.get("exit_status") or "Exit status not available in stop document!"
85
+ assert self.action_id is not None, (
86
+ "ISPyB Robot load callback stop called unexpectedly"
82
87
  )
88
+ exit_status = doc.get("exit_status")
89
+ assert exit_status, "Exit status not available in stop document!"
90
+ assert self._metadata, "Metadata not received before stop document."
83
91
  reason = doc.get("reason") or "OK"
92
+
84
93
  self.expeye.end_load(self.action_id, exit_status, reason)
94
+ self.expeye.update_sample_status(
95
+ self._metadata["sample_id"],
96
+ BLSampleStatus.LOADED
97
+ if exit_status == "success"
98
+ else BLSampleStatus.ERROR_BEAMLINE,
99
+ )
85
100
  self.action_id = None
86
101
  return super().activity_gated_stop(doc)
@@ -3,28 +3,27 @@ from __future__ import annotations
3
3
  from collections.abc import Callable, Sequence
4
4
  from typing import TYPE_CHECKING, Any, cast
5
5
 
6
- from mx_bluesky.common.parameters.components import IspybExperimentType
7
- from mx_bluesky.common.utils.log import set_dcgid_tag
8
- from mx_bluesky.hyperion.external_interaction.callbacks.common.ispyb_mapping import (
9
- populate_data_collection_group,
10
- populate_remaining_data_collection_info,
11
- )
12
- from mx_bluesky.hyperion.external_interaction.callbacks.ispyb_callback_base import (
6
+ from mx_bluesky.common.external_interaction.callbacks.common.ispyb_callback_base import (
13
7
  BaseISPyBCallback,
14
8
  )
15
- from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_mapping import (
16
- populate_data_collection_info_for_rotation,
9
+ from mx_bluesky.common.external_interaction.callbacks.common.ispyb_mapping import (
10
+ populate_data_collection_group,
11
+ populate_remaining_data_collection_info,
17
12
  )
18
- from mx_bluesky.hyperion.external_interaction.ispyb.data_model import (
13
+ from mx_bluesky.common.external_interaction.ispyb.data_model import (
19
14
  DataCollectionInfo,
20
15
  DataCollectionPositionInfo,
21
16
  ScanDataInfo,
22
17
  )
23
- from mx_bluesky.hyperion.external_interaction.ispyb.ispyb_store import (
18
+ from mx_bluesky.common.external_interaction.ispyb.ispyb_store import (
24
19
  IspybIds,
25
20
  StoreInIspyb,
26
21
  )
27
- from mx_bluesky.hyperion.log import ISPYB_LOGGER
22
+ from mx_bluesky.common.parameters.components import IspybExperimentType
23
+ from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER, set_dcgid_tag
24
+ from mx_bluesky.hyperion.external_interaction.callbacks.rotation.ispyb_mapping import (
25
+ populate_data_collection_info_for_rotation,
26
+ )
28
27
  from mx_bluesky.hyperion.parameters.constants import CONST
29
28
  from mx_bluesky.hyperion.parameters.rotation import RotationScan
30
29
 
@@ -58,10 +57,10 @@ class RotationISPyBCallback(BaseISPyBCallback):
58
57
 
59
58
  def activity_gated_start(self, doc: RunStart):
60
59
  if doc.get("subplan_name") == CONST.PLAN.ROTATION_OUTER:
61
- ISPYB_LOGGER.info(
60
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
62
61
  "ISPyB callback received start document with experiment parameters."
63
62
  )
64
- hyperion_params = doc.get("hyperion_parameters")
63
+ hyperion_params = doc.get("mx_bluesky_parameters")
65
64
  assert isinstance(hyperion_params, str)
66
65
  self.params = RotationScan.model_validate_json(hyperion_params)
67
66
  dcgid = (
@@ -73,16 +72,18 @@ class RotationISPyBCallback(BaseISPyBCallback):
73
72
  self.params.ispyb_experiment_type
74
73
  == IspybExperimentType.CHARACTERIZATION
75
74
  ):
76
- ISPYB_LOGGER.info("Screening collection - using new DCG")
75
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
76
+ "Screening collection - using new DCG"
77
+ )
77
78
  dcgid = None
78
79
  self.last_sample_id = None
79
80
  else:
80
- ISPYB_LOGGER.info(
81
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info(
81
82
  f"Collection is {self.params.ispyb_experiment_type} - storing sampleID to bundle images"
82
83
  )
83
84
  self.last_sample_id = self.params.sample_id
84
85
  self.ispyb = StoreInIspyb(self.ispyb_config)
85
- ISPYB_LOGGER.info("Beginning ispyb deposition")
86
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info("Beginning ispyb deposition")
86
87
  data_collection_group_info = populate_data_collection_group(self.params)
87
88
  data_collection_info = populate_data_collection_info_for_rotation(
88
89
  cast(RotationScan, self.params)
@@ -100,7 +101,7 @@ class RotationISPyBCallback(BaseISPyBCallback):
100
101
  self.ispyb_ids = self.ispyb.begin_deposition(
101
102
  data_collection_group_info, [scan_data_info]
102
103
  )
103
- ISPYB_LOGGER.info("ISPYB handler received start document.")
104
+ ISPYB_ZOCALO_CALLBACK_LOGGER.info("ISPYB handler received start document.")
104
105
  if doc.get("subplan_name") == CONST.PLAN.ROTATION_MAIN:
105
106
  self.uid_to_finalize_on = doc.get("uid")
106
107
  return super().activity_gated_start(doc)
@@ -111,9 +112,9 @@ class RotationISPyBCallback(BaseISPyBCallback):
111
112
  event_sourced_position_info: DataCollectionPositionInfo | None,
112
113
  params,
113
114
  ) -> Sequence[ScanDataInfo]:
114
- assert (
115
- self.ispyb_ids.data_collection_ids
116
- ), "Expect an existing DataCollection to update"
115
+ assert self.ispyb_ids.data_collection_ids, (
116
+ "Expect an existing DataCollection to update"
117
+ )
117
118
 
118
119
  return [
119
120
  ScanDataInfo(
@@ -131,9 +132,9 @@ class RotationISPyBCallback(BaseISPyBCallback):
131
132
  doc["data"]["smargon-y"],
132
133
  doc["data"]["smargon-z"],
133
134
  ]
134
- assert (
135
- self.params
136
- ), "handle_ispyb_hardware_read triggered before activity_gated_start"
135
+ assert self.params, (
136
+ "handle_ispyb_hardware_read triggered before activity_gated_start"
137
+ )
137
138
  motor_positions_um = [position * 1000 for position in motor_positions_mm]
138
139
  comment = f"Sample position (µm): ({motor_positions_um[0]:.0f}, {motor_positions_um[1]:.0f}, {motor_positions_um[2]:.0f}) {self.params.comment} "
139
140
  scan_data_infos[0].data_collection_info.comments = comment
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from mx_bluesky.hyperion.external_interaction.ispyb.data_model import DataCollectionInfo
3
+ from mx_bluesky.common.external_interaction.ispyb.data_model import DataCollectionInfo
4
4
  from mx_bluesky.hyperion.parameters.rotation import RotationScan
5
5
 
6
6
 
@@ -2,20 +2,22 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING
4
4
 
5
- from mx_bluesky.hyperion.external_interaction.callbacks.plan_reactive_callback import (
5
+ from mx_bluesky.common.external_interaction.callbacks.common.logging_callback import (
6
+ format_doc_for_log,
7
+ )
8
+ from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
6
9
  PlanReactiveCallback,
7
10
  )
8
- from mx_bluesky.hyperion.external_interaction.nexus.nexus_utils import (
11
+ from mx_bluesky.common.external_interaction.nexus.nexus_utils import (
12
+ AxisDirection,
9
13
  create_beam_and_attenuator_parameters,
10
14
  vds_type_based_on_bit_depth,
11
15
  )
12
- from mx_bluesky.hyperion.external_interaction.nexus.write_nexus import NexusWriter
13
- from mx_bluesky.hyperion.log import NEXUS_LOGGER
16
+ from mx_bluesky.common.external_interaction.nexus.write_nexus import NexusWriter
17
+ from mx_bluesky.common.utils.log import NEXUS_LOGGER
14
18
  from mx_bluesky.hyperion.parameters.constants import CONST
15
19
  from mx_bluesky.hyperion.parameters.rotation import RotationScan
16
20
 
17
- from ..logging_callback import format_doc_for_log
18
-
19
21
  if TYPE_CHECKING:
20
22
  from event_model.documents import Event, EventDescriptor, RunStart
21
23
 
@@ -64,7 +66,7 @@ class RotationNexusFileCallback(PlanReactiveCallback):
64
66
  self.writer.attenuator,
65
67
  ) = create_beam_and_attenuator_parameters(
66
68
  data["dcm-energy_in_kev"],
67
- data["flux_flux_reading"],
69
+ data["flux-flux_reading"],
68
70
  data["attenuator-actual_transmission"],
69
71
  )
70
72
  vds_data_type = vds_type_based_on_bit_depth(doc["data"]["eiger_bit_depth"])
@@ -78,7 +80,7 @@ class RotationNexusFileCallback(PlanReactiveCallback):
78
80
  self.meta_data_run_number = doc.get("meta_data_run_number")
79
81
  if doc.get("subplan_name") == CONST.PLAN.ROTATION_OUTER:
80
82
  self.run_uid = doc.get("uid")
81
- hyperion_params = doc.get("hyperion_parameters")
83
+ hyperion_params = doc.get("mx_bluesky_parameters")
82
84
  assert isinstance(hyperion_params, str)
83
85
  NEXUS_LOGGER.info(
84
86
  f"Nexus writer received start document with experiment parameters {hyperion_params}"
@@ -100,5 +102,7 @@ class RotationNexusFileCallback(PlanReactiveCallback):
100
102
  vds_start_index=parameters.nexus_vds_start_img,
101
103
  full_num_of_images=self.full_num_of_images,
102
104
  meta_data_run_number=self.meta_data_run_number,
103
- rotation_direction=parameters.rotation_direction,
105
+ axis_direction=AxisDirection.NEGATIVE
106
+ if parameters.features.omega_flip
107
+ else AxisDirection.POSITIVE,
104
108
  )
@@ -0,0 +1,50 @@
1
+ from event_model import RunStart, RunStop
2
+
3
+ from mx_bluesky.common.external_interaction.callbacks.common.plan_reactive_callback import (
4
+ PlanReactiveCallback,
5
+ )
6
+ from mx_bluesky.common.external_interaction.ispyb.exp_eye_store import (
7
+ BLSampleStatus,
8
+ ExpeyeInteraction,
9
+ )
10
+ from mx_bluesky.common.utils.exceptions import CrystalNotFoundException, SampleException
11
+ from mx_bluesky.common.utils.log import ISPYB_ZOCALO_CALLBACK_LOGGER
12
+
13
+
14
+ class SampleHandlingCallback(PlanReactiveCallback):
15
+ """Intercepts exceptions from experiment plans and updates the ISPyB BLSampleStatus
16
+ field according to the type of exception raised."""
17
+
18
+ def __init__(self):
19
+ super().__init__(log=ISPYB_ZOCALO_CALLBACK_LOGGER)
20
+ self._sample_id: int | None = None
21
+ self._descriptor: str | None = None
22
+
23
+ def activity_gated_start(self, doc: RunStart):
24
+ if not self._sample_id:
25
+ sample_id = doc.get("metadata", {}).get("sample_id")
26
+ self.log.info(f"Recording sample ID at run start {sample_id}")
27
+ self._sample_id = sample_id
28
+
29
+ def activity_gated_stop(self, doc: RunStop) -> RunStop:
30
+ if doc["exit_status"] != "success":
31
+ exception_type, message = SampleException.type_and_message_from_reason(
32
+ doc.get("reason", "")
33
+ )
34
+ self.log.info(
35
+ f"Sample handling callback intercepted exception of type {exception_type}: {message}"
36
+ )
37
+ self._record_exception(exception_type)
38
+ return doc
39
+
40
+ def _record_exception(self, exception_type: str):
41
+ expeye = ExpeyeInteraction()
42
+ assert self._sample_id, "Unable to record exception due to no sample ID"
43
+ sample_status = self._decode_sample_status(exception_type)
44
+ expeye.update_sample_status(self._sample_id, sample_status)
45
+
46
+ def _decode_sample_status(self, exception_type: str) -> BLSampleStatus:
47
+ match exception_type:
48
+ case SampleException.__name__ | CrystalNotFoundException.__name__:
49
+ return BLSampleStatus.ERROR_SAMPLE
50
+ return BLSampleStatus.ERROR_BEAMLINE
@@ -3,11 +3,24 @@ from functools import cache
3
3
  from daq_config_server.client import ConfigServer
4
4
 
5
5
  from mx_bluesky.common.external_interaction.config_server import FeatureFlags
6
- from mx_bluesky.hyperion.log import LOGGER
6
+ from mx_bluesky.common.utils.log import LOGGER
7
7
  from mx_bluesky.hyperion.parameters.constants import CONST
8
8
 
9
9
 
10
10
  class HyperionFeatureFlags(FeatureFlags):
11
+ """
12
+ Feature flags specific to Hyperion.
13
+
14
+ Attributes:
15
+ use_panda_for_gridscan: If True then the PandA is used for gridscans, otherwise the zebra is used
16
+ compare_cpu_and_gpu_zocalo: If True then GPU result processing is enabled alongside CPU, if False then
17
+ CPU only is used.
18
+ set_stub_offsets: If True then set the stub offsets after moving to the crystal (ignored for
19
+ multi-centre)
20
+ omega_flip: If True then invert the smargon omega motor rotation commands with respect to
21
+ the hyperion request.
22
+ """
23
+
11
24
  @staticmethod
12
25
  @cache
13
26
  def get_config_server() -> ConfigServer:
@@ -16,3 +29,4 @@ class HyperionFeatureFlags(FeatureFlags):
16
29
  use_panda_for_gridscan: bool = CONST.I03.USE_PANDA_FOR_GRIDSCAN
17
30
  compare_cpu_and_gpu_zocalo: bool = CONST.I03.COMPARE_CPU_AND_GPU_ZOCALO
18
31
  set_stub_offsets: bool = CONST.I03.SET_STUB_OFFSETS
32
+ omega_flip: bool = CONST.I03.OMEGA_FLIP
@@ -1,7 +1,8 @@
1
- from pydantic import BaseModel, Field
1
+ from pydantic import Field
2
2
 
3
+ from mx_bluesky.common.parameters.components import WithPandaGridScan
3
4
  from mx_bluesky.hyperion.external_interaction.config_server import HyperionFeatureFlags
4
5
 
5
6
 
6
- class WithHyperionFeatures(BaseModel):
7
+ class WithHyperionUDCFeatures(WithPandaGridScan):
7
8
  features: HyperionFeatureFlags = Field(default=HyperionFeatureFlags())
@@ -28,6 +28,7 @@ class I03Constants:
28
28
  SHUTTER_TIME_S = 0.06
29
29
  USE_PANDA_FOR_GRIDSCAN = False
30
30
  SET_STUB_OFFSETS = False
31
+ OMEGA_FLIP = True
31
32
 
32
33
  # Turns on GPU processing for zocalo and logs a comparison between GPU and CPU-
33
34
  # processed results. GPU results never used in analysis for now