mx-bluesky 1.4.6__py3-none-any.whl → 1.4.7__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 (62) hide show
  1. mx_bluesky/_version.py +2 -2
  2. mx_bluesky/beamlines/aithre_lasershaping/__init__.py +13 -0
  3. mx_bluesky/beamlines/aithre_lasershaping/check_goniometer_performance.py +29 -0
  4. mx_bluesky/beamlines/aithre_lasershaping/goniometer_controls.py +18 -0
  5. mx_bluesky/beamlines/i04/redis_to_murko_forwarder.py +31 -25
  6. mx_bluesky/beamlines/i04/thawing_plan.py +10 -1
  7. mx_bluesky/beamlines/i24/serial/extruder/EX-gui-edm/DiamondExtruder-I24-py3v1.edl +12 -12
  8. mx_bluesky/beamlines/i24/serial/extruder/i24ssx_Extruder_Collect_py3v2.py +30 -29
  9. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Collect_py3v1.py +10 -11
  10. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_Chip_Manager_py3v1.py +8 -10
  11. mx_bluesky/beamlines/i24/serial/fixed_target/i24ssx_moveonclick.py +10 -3
  12. mx_bluesky/beamlines/i24/serial/log.py +1 -0
  13. mx_bluesky/beamlines/i24/serial/set_visit_directory.sh +1 -1
  14. mx_bluesky/beamlines/i24/serial/setup_beamline/pv.py +16 -16
  15. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_beamline.py +47 -48
  16. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_detector.py +1 -1
  17. mx_bluesky/beamlines/i24/serial/setup_beamline/setup_zebra_plans.py +9 -7
  18. mx_bluesky/beamlines/i24/serial/write_nexus.py +3 -2
  19. mx_bluesky/common/device_setup_plans/xbpm_feedback.py +45 -0
  20. mx_bluesky/common/external_interaction/callbacks/common/ispyb_callback_base.py +2 -4
  21. mx_bluesky/common/external_interaction/callbacks/common/ispyb_mapping.py +1 -1
  22. mx_bluesky/common/external_interaction/callbacks/common/zocalo_callback.py +18 -15
  23. mx_bluesky/{hyperion → common}/external_interaction/callbacks/sample_handling/sample_handling_callback.py +16 -4
  24. mx_bluesky/common/external_interaction/callbacks/xray_centre/ispyb_callback.py +41 -5
  25. mx_bluesky/common/external_interaction/callbacks/xray_centre/nexus_callback.py +2 -1
  26. mx_bluesky/common/external_interaction/ispyb/data_model.py +1 -0
  27. mx_bluesky/common/external_interaction/ispyb/ispyb_store.py +14 -1
  28. mx_bluesky/common/external_interaction/nexus/nexus_utils.py +1 -1
  29. mx_bluesky/common/parameters/constants.py +2 -0
  30. mx_bluesky/common/parameters/gridscan.py +1 -1
  31. mx_bluesky/common/plans/write_sample_status.py +46 -0
  32. mx_bluesky/common/preprocessors/__init__.py +0 -0
  33. mx_bluesky/common/preprocessors/preprocessors.py +105 -0
  34. mx_bluesky/common/protocols/__init__.py +0 -0
  35. mx_bluesky/common/protocols/protocols.py +10 -0
  36. mx_bluesky/hyperion/__main__.py +3 -9
  37. mx_bluesky/hyperion/baton_handler.py +84 -0
  38. mx_bluesky/hyperion/device_setup_plans/setup_panda.py +5 -1
  39. mx_bluesky/hyperion/experiment_plans/__init__.py +0 -4
  40. mx_bluesky/hyperion/experiment_plans/change_aperture_then_move_plan.py +10 -25
  41. mx_bluesky/hyperion/experiment_plans/experiment_registry.py +0 -7
  42. mx_bluesky/hyperion/experiment_plans/flyscan_xray_centre_plan.py +11 -10
  43. mx_bluesky/hyperion/experiment_plans/load_centre_collect_full_plan.py +5 -1
  44. mx_bluesky/hyperion/experiment_plans/oav_snapshot_plan.py +2 -2
  45. mx_bluesky/hyperion/experiment_plans/pin_tip_centring_plan.py +5 -3
  46. mx_bluesky/hyperion/experiment_plans/robot_load_then_centre_plan.py +0 -26
  47. mx_bluesky/hyperion/experiment_plans/rotation_scan_plan.py +23 -18
  48. mx_bluesky/hyperion/experiment_plans/set_energy_plan.py +25 -6
  49. mx_bluesky/hyperion/external_interaction/agamemnon.py +148 -10
  50. mx_bluesky/hyperion/external_interaction/callbacks/__main__.py +12 -6
  51. mx_bluesky/hyperion/external_interaction/callbacks/snapshot_callback.py +107 -0
  52. mx_bluesky/hyperion/parameters/gridscan.py +2 -2
  53. mx_bluesky/hyperion/parameters/rotation.py +1 -1
  54. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info}/METADATA +7 -7
  55. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info}/RECORD +60 -51
  56. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info}/WHEEL +1 -1
  57. mx_bluesky/common/external_interaction/callbacks/common/aperture_change_callback.py +0 -22
  58. mx_bluesky/hyperion/device_setup_plans/xbpm_feedback.py +0 -103
  59. /mx_bluesky/{hyperion → common}/external_interaction/callbacks/sample_handling/__init__.py +0 -0
  60. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info}/entry_points.txt +0 -0
  61. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info/licenses}/LICENSE +0 -0
  62. {mx_bluesky-1.4.6.dist-info → mx_bluesky-1.4.7.dist-info}/top_level.txt +0 -0
@@ -258,22 +258,22 @@ step09_pmac_response = "BL24I-MO-IOC-09:ASYN9.AINP"
258
258
  step10_pmac_response = "BL24I-MO-IOC-10:ASYN10.AINP"
259
259
  step11_pmac_response = "BL24I-MO-IOC-11:ASYN11.AINP"
260
260
  step12_pmac_response = "BL24I-MO-IOC-12:ASYN12.AINP"
261
- # General Purpose PV
262
- ioc12_gp1 = "BL24I-EA-IOC-12:GP1"
263
- ioc12_gp2 = "BL24I-EA-IOC-12:GP2"
264
- ioc12_gp3 = "BL24I-EA-IOC-12:GP3"
265
- ioc12_gp4 = "BL24I-EA-IOC-12:GP4"
266
- ioc12_gp5 = "BL24I-EA-IOC-12:GP5"
267
- ioc12_gp6 = "BL24I-EA-IOC-12:GP6"
268
- ioc12_gp7 = "BL24I-EA-IOC-12:GP7"
269
- ioc12_gp8 = "BL24I-EA-IOC-12:GP8"
270
- ioc12_gp9 = "BL24I-EA-IOC-12:GP9"
271
- ioc12_gp10 = "BL24I-EA-IOC-12:GP10"
272
- ioc12_gp11 = "BL24I-EA-IOC-12:GP11"
273
- ioc12_gp12 = "BL24I-EA-IOC-12:GP12"
274
- ioc12_gp13 = "BL24I-EA-IOC-12:GP13"
275
- ioc12_gp14 = "BL24I-EA-IOC-12:GP14"
276
- ioc12_gp15 = "BL24I-EA-IOC-12:GP15"
261
+ # General Purpose PV. Now use STEP13
262
+ ioc13_gp1 = "BL24I-MO-IOC-13:GP1"
263
+ ioc13_gp2 = "BL24I-MO-IOC-13:GP2"
264
+ ioc13_gp3 = "BL24I-MO-IOC-13:GP3"
265
+ ioc13_gp4 = "BL24I-MO-IOC-13:GP4"
266
+ ioc13_gp5 = "BL24I-MO-IOC-13:GP5"
267
+ ioc13_gp6 = "BL24I-MO-IOC-13:GP6"
268
+ ioc13_gp7 = "BL24I-MO-IOC-13:GP7"
269
+ ioc13_gp8 = "BL24I-MO-IOC-13:GP8"
270
+ ioc13_gp9 = "BL24I-MO-IOC-13:GP9"
271
+ ioc13_gp10 = "BL24I-MO-IOC-13:GP10"
272
+ ioc13_gp11 = "BL24I-MO-IOC-13:GP11"
273
+ ioc13_gp12 = "BL24I-MO-IOC-13:GP12"
274
+ ioc13_gp13 = "BL24I-MO-IOC-13:GP13"
275
+ ioc13_gp14 = "BL24I-MO-IOC-13:GP14"
276
+ ioc13_gp15 = "BL24I-MO-IOC-13:GP15"
277
277
  # ME14E
278
278
  me14e_pmac_str = "ME14E-MO-CHIP-01:PMAC_STRING"
279
279
  me14e_stage_x = "ME14E-MO-CHIP-01:X"
@@ -1,5 +1,4 @@
1
1
  from pathlib import Path
2
- from time import sleep
3
2
 
4
3
  import bluesky.plan_stubs as bps
5
4
  from dodal.beamlines import i24
@@ -149,9 +148,9 @@ def modechange(action):
149
148
  caput(pv.vgon_pinzs, 0)
150
149
  caput(pv.fluo_trans, "OUT")
151
150
  caput(pv.bs_roty, 0)
152
- sleep(0.5)
151
+ yield from bps.sleep(0.5)
153
152
  caput(pv.bs_mp_select, "Data Collection")
154
- sleep(2.3)
153
+ yield from bps.sleep(2.3)
155
154
  caput(pv.bl_mp_select, "In")
156
155
  SSX_LOGGER.debug("Pin Data Collection Done")
157
156
 
@@ -168,9 +167,9 @@ def modechange(action):
168
167
  caput(pv.vgon_pinyh, 0)
169
168
  caput(pv.vgon_pinzs, 0)
170
169
  caput(pv.fluo_trans, "OUT")
171
- sleep(0.1)
170
+ yield from bps.sleep(0.1)
172
171
  caput(pv.bs_roty, 0)
173
- sleep(2.6)
172
+ yield from bps.sleep(2.6)
174
173
  caput(pv.bl_mp_select, "In")
175
174
  caput(pv.bs_mp_select, "Data Collection")
176
175
  SSX_LOGGER.debug("RT Data Collection Done")
@@ -182,11 +181,11 @@ def modechange(action):
182
181
  caput(pv.hgon_omega, 0.0)
183
182
  caput(pv.fluo_trans, "OUT")
184
183
  caput(pv.bl_mp_select, "Out")
185
- sleep(1)
184
+ yield from bps.sleep(1)
186
185
  caput(pv.aptr1_mp_select, "Manual Mounting")
187
186
  caput(pv.bs_mp_select, "Tray Mount")
188
187
  while float(caget(pv.ttab_x + ".RBV")) > 3:
189
- sleep(1)
188
+ yield from bps.sleep(1)
190
189
  SSX_LOGGER.debug("Tray Hand Mount Done")
191
190
 
192
191
  # Tray Robot Load. This action needs to be reviewed and revised
@@ -199,15 +198,15 @@ def modechange(action):
199
198
  caput(pv.fluo_trans, "OUT")
200
199
  caput(pv.aptr1_mp_select, "In")
201
200
  caput(pv.bl_mp_select, "Out")
202
- sleep(1)
201
+ yield from bps.sleep(1)
203
202
  caput(pv.bs_roty, 0)
204
- sleep(1)
203
+ yield from bps.sleep(1)
205
204
  caput(pv.bs_mp_select, "Robot")
206
- sleep(1)
205
+ yield from bps.sleep(1)
207
206
  caput(pv.bs_mp_select, "Data Collection Far")
208
- sleep(1)
207
+ yield from bps.sleep(1)
209
208
  caput(pv.bs_roty, 0)
210
- sleep(4)
209
+ yield from bps.sleep(4)
211
210
  caput(pv.bl_mp_select, "In")
212
211
  SSX_LOGGER.debug("Tray Robot Mount Done")
213
212
 
@@ -220,15 +219,15 @@ def modechange(action):
220
219
  caput(pv.aptr1_mp_select, "In")
221
220
  caput(pv.fluo_trans, "OUT")
222
221
  caput(pv.bl_mp_select, "Out")
223
- sleep(1)
222
+ yield from bps.sleep(1)
224
223
  caput(pv.bs_roty, 0)
225
- sleep(1)
224
+ yield from bps.sleep(1)
226
225
  caput(pv.bs_mp_select, "Robot")
227
- sleep(1)
226
+ yield from bps.sleep(1)
228
227
  caput(pv.bs_mp_select, "Data Collection")
229
- sleep(1)
228
+ yield from bps.sleep(1)
230
229
  caput(pv.bs_roty, 0)
231
- sleep(4)
230
+ yield from bps.sleep(4)
232
231
  caput(pv.bl_mp_select, "In")
233
232
  SSX_LOGGER.debug("Tray Data Collection Done")
234
233
 
@@ -252,17 +251,17 @@ def modechange(action):
252
251
  caput(pv.vgon_pinzs, 0)
253
252
  while float(caget(pv.ttab_x + ".RBV")) > 1:
254
253
  SSX_LOGGER.debug(f"moving ttab_x {caget(pv.ttab_x)}")
255
- sleep(0.1)
254
+ yield from bps.sleep(0.1)
256
255
  while caget(pv.fluo_out_limit) == "OFF":
257
256
  SSX_LOGGER.debug("waiting on fluorescence detector")
258
- sleep(0.1)
257
+ yield from bps.sleep(0.1)
259
258
  while caget(pv.bl_mp_select) != "Out":
260
259
  SSX_LOGGER.debug("waiting on back light to move to out")
261
- sleep(0.1)
260
+ yield from bps.sleep(0.1)
262
261
  caput(pv.bs_mp_select, "Robot")
263
262
  caput(pv.bs_roty, 0)
264
263
  while float(caget(pv.ptab_y + ".RBV")) > -89.0:
265
- sleep(1)
264
+ yield from bps.sleep(1)
266
265
  SSX_LOGGER.debug("Switch To Tray Done")
267
266
 
268
267
  # Tray Switch to Pin
@@ -272,11 +271,11 @@ def modechange(action):
272
271
  caput(pv.hgon_trayys, 0.0)
273
272
  caput(pv.hgon_trayzs, 0.0)
274
273
  while float(caget(pv.ttab_x + ".RBV")) > 1.0:
275
- sleep(1)
274
+ yield from bps.sleep(1)
276
275
  caput(pv.ptab_y, 0)
277
276
  while float(caget(pv.ptab_y + ".RBV")) < -1.0:
278
- sleep(1)
279
- modechange("Pin_data_collection")
277
+ yield from bps.sleep(1)
278
+ yield from modechange("Pin_data_collection")
280
279
  SSX_LOGGER.debug("Switch To Pin Done")
281
280
  else:
282
281
  SSX_LOGGER.debug(f"Unknown action: {action}")
@@ -323,9 +322,9 @@ def pilatus(action, args_list):
323
322
  [filepath, filename, num_imgs, exptime] = args_list
324
323
  rampath = filepath.replace("dls/i24/data", "ramdisk")
325
324
  caput(pv.pilat_filepath, rampath)
326
- sleep(0.1)
325
+ yield from bps.sleep(0.1)
327
326
  caput(pv.pilat_filename, filename)
328
- sleep(0.1)
327
+ yield from bps.sleep(0.1)
329
328
  acqtime = float(exptime) - 0.001
330
329
  caput(pv.pilat_acquiretime, str(acqtime))
331
330
  caput(pv.pilat_acquireperiod, str(exptime))
@@ -336,21 +335,21 @@ def pilatus(action, args_list):
336
335
  SSX_LOGGER.debug(f"Exposure time set as {exptime} s")
337
336
  SSX_LOGGER.debug(f"Acquire time set as {acqtime} s")
338
337
  SSX_LOGGER.debug("Pilatus takes time apprx 2sec")
339
- sleep(2)
338
+ yield from bps.sleep(2)
340
339
  caput(pv.pilat_delaytime, 0.00)
341
340
  caput(pv.pilat_numimages, str(num_imgs))
342
341
  caput(pv.pilat_imagemode, "Continuous")
343
342
  caput(pv.pilat_triggermode, "Ext. Trigger")
344
- sleep(0.2)
343
+ yield from bps.sleep(0.2)
345
344
 
346
345
  elif action == "quickshot-internaltrig":
347
346
  SSX_LOGGER.debug("quickshot-internaltrig")
348
347
  [filepath, filename, num_imgs, exptime] = args_list
349
348
  rampath = filepath.replace("dls/i24/data", "ramdisk")
350
349
  caput(pv.pilat_filepath, rampath)
351
- sleep(0.1)
350
+ yield from bps.sleep(0.1)
352
351
  caput(pv.pilat_filename, filename)
353
- sleep(0.1)
352
+ yield from bps.sleep(0.1)
354
353
  acqtime = float(exptime) - 0.001
355
354
  caput(pv.pilat_acquiretime, str(acqtime))
356
355
  caput(pv.pilat_acquireperiod, str(exptime))
@@ -361,12 +360,12 @@ def pilatus(action, args_list):
361
360
  SSX_LOGGER.debug(f"Exposure time set as {exptime} s")
362
361
  SSX_LOGGER.debug(f"Acquire time set as {acqtime} s")
363
362
  SSX_LOGGER.debug("Pilatus takes time apprx 2sec")
364
- sleep(2)
363
+ yield from bps.sleep(2)
365
364
  caput(pv.pilat_delaytime, 0.00)
366
365
  caput(pv.pilat_numimages, str(num_imgs))
367
366
  caput(pv.pilat_imagemode, "Continuous")
368
367
  caput(pv.pilat_triggermode, "Internal")
369
- sleep(0.2)
368
+ yield from bps.sleep(0.2)
370
369
 
371
370
  # Put it all back to GDA acceptable defaults
372
371
  elif action == "return to normal":
@@ -374,7 +373,7 @@ def pilatus(action, args_list):
374
373
  caput(pv.pilat_triggermode, "Ext. Trigger")
375
374
  caput(pv.pilat_numexpimage, 1)
376
375
  SSX_LOGGER.debug("***** leaving pilatus")
377
- sleep(0.1)
376
+ yield from bps.sleep(0.1)
378
377
  return 0
379
378
 
380
379
 
@@ -388,7 +387,7 @@ def eiger(action, args_list):
388
387
  caput(pv.eiger_detdist, str(float(caget(pv.det_z)) / 1000))
389
388
  caput(pv.eiger_wavelength, caget(pv.dcm_lambda))
390
389
  caput(pv.eiger_omegaincr, 0.0)
391
- sleep(0.1)
390
+ yield from bps.sleep(0.1)
392
391
  # Setup common to all collections ###
393
392
  caput(pv.eiger_filewriter, "No")
394
393
  caput(pv.eiger_stream, "Yes")
@@ -409,9 +408,9 @@ def eiger(action, args_list):
409
408
  [filepath, filename, num_imgs, exptime] = args_list
410
409
  filename = filename + "_" + str(caget(pv.eiger_seqID))
411
410
  caput(pv.eiger_ODfilepath, filepath)
412
- sleep(0.1)
411
+ yield from bps.sleep(0.1)
413
412
  caput(pv.eiger_ODfilename, filename)
414
- sleep(0.1)
413
+ yield from bps.sleep(0.1)
415
414
  acqtime = float(exptime) - 0.0000001
416
415
  caput(pv.eiger_acquiretime, str(acqtime))
417
416
  SSX_LOGGER.debug(f"Filepath was set as {filepath}")
@@ -425,7 +424,7 @@ def eiger(action, args_list):
425
424
  caput(pv.eiger_triggermode, "Internal Series")
426
425
  caput(pv.eiger_numtriggers, 1)
427
426
  caput(pv.eiger_manualtrigger, "Yes")
428
- sleep(1.0)
427
+ yield from bps.sleep(1.0)
429
428
  # ODIN setup
430
429
  SSX_LOGGER.info("Setting up Odin")
431
430
  caput(pv.eiger_ODfilename, filename)
@@ -435,7 +434,7 @@ def eiger(action, args_list):
435
434
  eigerbdrbv = "UInt" + str(caget(pv.eiger_bitdepthrbv))
436
435
  caput(pv.eiger_ODdatatype, eigerbdrbv)
437
436
  caput(pv.eiger_ODcompress, "BSL24")
438
- sleep(1.0)
437
+ yield from bps.sleep(1.0)
439
438
  # All done. Now get Odin to wait for data and start Eiger
440
439
  SSX_LOGGER.info("Done: Odin waiting for data")
441
440
  caput(pv.eiger_ODcapture, "Capture")
@@ -451,9 +450,9 @@ def eiger(action, args_list):
451
450
  [filepath, filename, num_imgs, exptime] = args_list
452
451
  filename = filename + "_" + str(caget(pv.eiger_seqID))
453
452
  caput(pv.eiger_ODfilepath, filepath)
454
- sleep(0.1)
453
+ yield from bps.sleep(0.1)
455
454
  caput(pv.eiger_ODfilename, filename)
456
- sleep(0.1)
455
+ yield from bps.sleep(0.1)
457
456
  acqtime = float(exptime) - 0.0000001
458
457
  caput(pv.eiger_acquiretime, str(acqtime))
459
458
  SSX_LOGGER.debug(f"Filepath was set as {filepath}")
@@ -467,7 +466,7 @@ def eiger(action, args_list):
467
466
  caput(pv.eiger_triggermode, "External Enable")
468
467
  caput(pv.eiger_numtriggers, str(num_imgs))
469
468
  caput(pv.eiger_manualtrigger, "Yes")
470
- sleep(1.0)
469
+ yield from bps.sleep(1.0)
471
470
  # ODIN setup #
472
471
  SSX_LOGGER.info("Setting up Odin")
473
472
  caput(pv.eiger_ODfilename, filename)
@@ -477,7 +476,7 @@ def eiger(action, args_list):
477
476
  eigerbdrbv = "UInt" + str(caget(pv.eiger_bitdepthrbv))
478
477
  caput(pv.eiger_ODdatatype, eigerbdrbv)
479
478
  caput(pv.eiger_ODcompress, "BSL24")
480
- sleep(1.0)
479
+ yield from bps.sleep(1.0)
481
480
  # All done. Now get Odin to wait for data and start Eiger
482
481
  SSX_LOGGER.info("Done: Odin waiting for data")
483
482
  caput(pv.eiger_ODcapture, "Capture")
@@ -492,7 +491,7 @@ def eiger(action, args_list):
492
491
  caput(pv.eiger_manualtrigger, "No")
493
492
  # caput(pv.eiger_seqID, int(caget(pv.eiger_seqID))+1)
494
493
  SSX_LOGGER.debug("***** leaving Eiger")
495
- sleep(0.1)
494
+ yield from bps.sleep(0.1)
496
495
  return 0
497
496
 
498
497
 
@@ -510,10 +509,10 @@ def xspress3(action, args_list):
510
509
  caput(pv.xsp3_acquiretime, exp_time)
511
510
  caput(pv.xsp3_c1_mca_roi1_llm, lo)
512
511
  caput(pv.xsp3_c1_mca_roi1_hlm, hi)
513
- sleep(0.2)
512
+ yield from bps.sleep(0.2)
514
513
  caput(pv.xsp3_c1_mca_roi1_llm, lo)
515
514
  caput(pv.xsp3_c1_mca_roi1_hlm, hi)
516
- sleep(0.2)
515
+ yield from bps.sleep(0.2)
517
516
  caput(pv.xsp3_erase, 0)
518
517
 
519
518
  elif action == "on-the-fly":
@@ -522,10 +521,10 @@ def xspress3(action, args_list):
522
521
  caput(pv.xsp3_numimages, num_frms)
523
522
  caput(pv.xsp3_c1_mca_roi1_llm, lo)
524
523
  caput(pv.xsp3_c1_mca_roi1_hlm, hi)
525
- sleep(0.2)
524
+ yield from bps.sleep(0.2)
526
525
  caput(pv.xsp3_c1_mca_roi1_llm, lo)
527
526
  caput(pv.xsp3_c1_mca_roi1_hlm, hi)
528
- sleep(0.2)
527
+ yield from bps.sleep(0.2)
529
528
  caput(pv.xsp3_erase, 0)
530
529
 
531
530
  elif action == "return-to-normal":
@@ -539,6 +538,6 @@ def xspress3(action, args_list):
539
538
  else:
540
539
  SSX_LOGGER.error("Unknown action for xspress3 method:", action)
541
540
 
542
- sleep(0.1)
541
+ yield from bps.sleep(0.1)
543
542
  SSX_LOGGER.debug("***** leaving xspress3")
544
543
  return 1
@@ -22,7 +22,7 @@ from mx_bluesky.beamlines.i24.serial.setup_beamline.pv_abstract import (
22
22
 
23
23
  EXPT_TYPE_DETECTOR_PVS = {
24
24
  SSXType.FIXED: pv.me14e_gp101,
25
- SSXType.EXTRUDER: pv.ioc12_gp15,
25
+ SSXType.EXTRUDER: pv.ioc13_gp15,
26
26
  }
27
27
 
28
28
 
@@ -272,7 +272,7 @@ def setup_zebra_for_fastchip_plan(
272
272
  det_type: str,
273
273
  num_gates: int,
274
274
  num_exposures: int,
275
- exposure_time: float,
275
+ exposure_time_s: float,
276
276
  start_time_offset: float = 0.0,
277
277
  group: str = "setup_zebra_for_fastchip",
278
278
  wait: bool = True,
@@ -303,7 +303,7 @@ def setup_zebra_for_fastchip_plan(
303
303
  det_type (str): Detector in use, current choices are Eiger or Pilatus.
304
304
  num_gates (int): Number of apertures to visit in a chip.
305
305
  num_exposures (int): Number of times data is collected in each aperture.
306
- exposure_time (float): Exposure time for each shot.
306
+ exposure_time_s (float): Exposure time for each shot.
307
307
  start_time_offset (float): Delay on the start of the position compare. \
308
308
  Defaults to 0.0 (standard chip collection).
309
309
  """
@@ -341,10 +341,12 @@ def setup_zebra_for_fastchip_plan(
341
341
  )
342
342
 
343
343
  # Square wave - needs a small drop to make it work for eiger
344
- pulse_width = exposure_time - 0.0001 if det_type == "eiger" else exposure_time / 2
344
+ pulse_width = (
345
+ exposure_time_s - 0.0001 if det_type == "eiger" else exposure_time_s / 2
346
+ )
345
347
 
346
348
  # 100us buffer needed to avoid missing some of the triggers
347
- exptime_buffer = exposure_time + 0.0001
349
+ exptime_buffer = exposure_time_s + 0.0001
348
350
 
349
351
  # Number of gates is the number of windows collected
350
352
  yield from bps.abs_set(zebra.pc.num_gates, num_gates, group=group)
@@ -362,7 +364,7 @@ def setup_zebra_for_fastchip_plan(
362
364
  def open_fast_shutter_at_each_position_plan(
363
365
  zebra: Zebra,
364
366
  num_exposures: int,
365
- exposure_time: float,
367
+ exposure_time_s: float,
366
368
  group: str = "fast_shutter_control",
367
369
  wait: bool = True,
368
370
  ):
@@ -384,7 +386,7 @@ def open_fast_shutter_at_each_position_plan(
384
386
  Args:
385
387
  zebra (Zebra): The zebra ophyd device.
386
388
  num_exposures (int): Number of times data is collected in each aperture.
387
- exposure_time (float): Exposure time for each shot.
389
+ exposure_time_s (float): Exposure time for each shot.
388
390
  """
389
391
  SSX_LOGGER.info(
390
392
  "ZEBRA setup for fastchip collection with long delays between exposures."
@@ -395,7 +397,7 @@ def open_fast_shutter_at_each_position_plan(
395
397
  zebra.output.pulse_2.input, zebra.mapping.sources.PC_GATE, group=group
396
398
  )
397
399
  yield from bps.abs_set(zebra.output.pulse_2.delay, 0.0, group=group)
398
- pulse2_width = num_exposures * exposure_time + SHUTTER_OPEN_TIME
400
+ pulse2_width = num_exposures * exposure_time_s + SHUTTER_OPEN_TIME
399
401
  yield from bps.abs_set(zebra.output.pulse_2.width, pulse2_width, group=group)
400
402
 
401
403
  # Fast shutter
@@ -4,6 +4,7 @@ import pprint
4
4
  import time
5
5
  from datetime import datetime
6
6
 
7
+ import bluesky.plan_stubs as bps
7
8
  import requests
8
9
 
9
10
  from mx_bluesky.beamlines.i24.serial.fixed_target.ft_utils import ChipType, MappingType
@@ -61,10 +62,10 @@ def call_nexgen(
61
62
  while time.time() - t0 < max_wait:
62
63
  if meta_h5.exists():
63
64
  SSX_LOGGER.info(f"Found {meta_h5} after {time.time() - t0:.1f} seconds")
64
- time.sleep(5)
65
+ yield from bps.sleep(5)
65
66
  break
66
67
  SSX_LOGGER.debug(f"Waiting for {meta_h5}")
67
- time.sleep(1)
68
+ yield from bps.sleep(1)
68
69
  if not meta_h5.exists():
69
70
  SSX_LOGGER.warning(f"Giving up waiting for {meta_h5} after {max_wait} seconds")
70
71
  return
@@ -0,0 +1,45 @@
1
+ from bluesky import plan_stubs as bps
2
+ from dodal.devices.attenuator.attenuator import BinaryFilterAttenuator
3
+ from dodal.devices.xbpm_feedback import Pause, XBPMFeedback
4
+
5
+ from mx_bluesky.common.utils.log import LOGGER
6
+
7
+
8
+ def unpause_xbpm_feedback_and_set_transmission_to_1(
9
+ xbpm_feedback: XBPMFeedback, attenuator: BinaryFilterAttenuator
10
+ ):
11
+ """Turns the XBPM feedback back on and sets transmission to 1 so that it keeps the
12
+ beam aligned whilst not collecting.
13
+
14
+ Args:
15
+ xbpm_feedback (XBPMFeedback): The XBPM device that is responsible for keeping
16
+ the beam in position
17
+ attenuator (BinaryFilterAttenuator): The attenuator used to set transmission
18
+ """
19
+ yield from bps.mv(xbpm_feedback.pause_feedback, Pause.RUN, attenuator, 1.0) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
20
+
21
+
22
+ def check_and_pause_feedback(
23
+ xbpm_feedback: XBPMFeedback,
24
+ attenuator: BinaryFilterAttenuator,
25
+ desired_transmission_fraction: float,
26
+ ):
27
+ """Checks that the xbpm is in position before then turning it off and setting a new
28
+ transmission.
29
+
30
+ Args:
31
+ xbpm_feedback (XBPMFeedback): The XBPM device that is responsible for keeping
32
+ the beam in position
33
+ attenuator (BinaryFilterAttenuator): The attenuator used to set transmission
34
+ desired_transmission_fraction (float): The desired transmission to set after
35
+ turning XBPM feedback off.
36
+
37
+ """
38
+ yield from bps.mv(attenuator, 1.0) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
39
+ LOGGER.info("Waiting for XBPM feedback to be stable")
40
+ yield from bps.trigger(xbpm_feedback, wait=True)
41
+ LOGGER.info(
42
+ f"XPBM feedback in position, pausing and setting transmission to {desired_transmission_fraction}"
43
+ )
44
+ yield from bps.mv(xbpm_feedback.pause_feedback, Pause.PAUSE) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
45
+ yield from bps.mv(attenuator, desired_transmission_fraction) # type: ignore # See: https://github.com/bluesky/bluesky/issues/1809
@@ -121,10 +121,8 @@ class BaseISPyBCallback(PlanReactiveCallback):
121
121
  ISPYB_ZOCALO_CALLBACK_LOGGER.info(
122
122
  "ISPyB handler received event from read hardware"
123
123
  )
124
- assert isinstance(
125
- synchrotron_mode := doc["data"]["synchrotron-synchrotron_mode"],
126
- SynchrotronMode,
127
- )
124
+ synchrotron_mode = doc["data"]["synchrotron-synchrotron_mode"]
125
+ assert isinstance(synchrotron_mode, SynchrotronMode)
128
126
 
129
127
  hwscan_data_collection_info = DataCollectionInfo(
130
128
  undulator_gap1=doc["data"]["undulator-current_gap"],
@@ -35,7 +35,7 @@ def populate_remaining_data_collection_info(
35
35
  data_collection_info.detector_id = I03_EIGER_DETECTOR
36
36
  data_collection_info.comments = comment
37
37
  data_collection_info.detector_distance = params.detector_params.detector_distance
38
- data_collection_info.exp_time = params.detector_params.exposure_time
38
+ data_collection_info.exp_time = params.detector_params.exposure_time_s
39
39
  data_collection_info.imgdir = params.detector_params.directory
40
40
  data_collection_info.imgprefix = params.detector_params.prefix
41
41
  data_collection_info.imgsuffix = EIGER_FILE_SUFFIX
@@ -18,10 +18,11 @@ if TYPE_CHECKING:
18
18
 
19
19
  class ZocaloCallback(CallbackBase):
20
20
  """Callback class to handle the triggering of Zocalo processing.
21
- Sends zocalo a run_start signal on receiving a start document for the specified
22
- sub-plan, and sends a run_end signal on receiving a stop document for the same plan.
21
+ Will start listening for collections when {triggering_plan} has been started.
23
22
 
24
- The metadata of the sub-plan this starts on must include a zocalo_environment.
23
+ For every ispyb deposition that occurs inside this run the callback will send zocalo
24
+ a run_start signal. Once the {triggering_plan} has ended the callback will send a
25
+ run_end signal for all collections.
25
26
 
26
27
  Shouldn't be subscribed directly to the RunEngine, instead should be passed to the
27
28
  `emit` argument of an ISPyB callback which appends DCIDs to the relevant start doc.
@@ -30,7 +31,9 @@ class ZocaloCallback(CallbackBase):
30
31
  def _reset_state(self):
31
32
  self.run_uid: str | None = None
32
33
  self.zocalo_info: list[ZocaloStartInfo] = []
34
+ self._started_zocalo_collections: list[ZocaloStartInfo] = []
33
35
  self.descriptors: dict[str, EventDescriptor] = {}
36
+ self.start_frame = 0
34
37
 
35
38
  def __init__(self, triggering_plan: str, zocalo_environment: str):
36
39
  super().__init__()
@@ -42,26 +45,21 @@ class ZocaloCallback(CallbackBase):
42
45
  ISPYB_ZOCALO_CALLBACK_LOGGER.info("Zocalo handler received start document.")
43
46
  if self.triggering_plan and doc.get("subplan_name") == self.triggering_plan:
44
47
  self.run_uid = doc.get("uid")
45
- assert isinstance(scan_points := doc.get("scan_points"), list)
48
+ if self.run_uid:
46
49
  if (
47
- isinstance(ispyb_ids := doc.get("ispyb_dcids"), tuple)
50
+ isinstance(scan_points := doc.get("scan_points"), list)
51
+ and isinstance(ispyb_ids := doc.get("ispyb_dcids"), tuple)
48
52
  and len(ispyb_ids) > 0
49
53
  ):
50
54
  ISPYB_ZOCALO_CALLBACK_LOGGER.info(f"Zocalo triggering for {ispyb_ids}")
51
55
  ids_and_shape = list(zip(ispyb_ids, scan_points, strict=False))
52
- start_frame = 0
53
- self.zocalo_info = []
54
56
  for idx, id_and_shape in enumerate(ids_and_shape):
55
57
  id, shape = id_and_shape
56
58
  num_frames = number_of_frames_from_scan_spec(shape)
57
59
  self.zocalo_info.append(
58
- ZocaloStartInfo(id, None, start_frame, num_frames, idx)
60
+ ZocaloStartInfo(id, None, self.start_frame, num_frames, idx)
59
61
  )
60
- start_frame += num_frames
61
- else:
62
- raise ISPyBDepositionNotMade(
63
- f"No ISPyB IDs received by the start of {self.triggering_plan=}"
64
- )
62
+ self.start_frame += num_frames
65
63
 
66
64
  def descriptor(self, doc: EventDescriptor):
67
65
  self.descriptors[doc["uid"]] = doc
@@ -73,6 +71,8 @@ class ZocaloCallback(CallbackBase):
73
71
  for start_info in self.zocalo_info:
74
72
  start_info.filename = filename
75
73
  self.zocalo_interactor.run_start(start_info)
74
+ self._started_zocalo_collections.append(start_info)
75
+ self.zocalo_info = []
76
76
  return doc
77
77
 
78
78
  def stop(self, doc: RunStop):
@@ -80,7 +80,10 @@ class ZocaloCallback(CallbackBase):
80
80
  ISPYB_ZOCALO_CALLBACK_LOGGER.info(
81
81
  f"Zocalo handler received stop document, for run {doc.get('run_start')}."
82
82
  )
83
- assert self.zocalo_interactor is not None
84
- for info in self.zocalo_info:
83
+ if not self._started_zocalo_collections:
84
+ raise ISPyBDepositionNotMade(
85
+ f"No ISPyB IDs received by the end of {self.triggering_plan=}"
86
+ )
87
+ for info in self._started_zocalo_collections:
85
88
  self.zocalo_interactor.run_end(info.ispyb_dcid)
86
89
  self._reset_state()
@@ -15,12 +15,15 @@ class SampleHandlingCallback(PlanReactiveCallback):
15
15
  """Intercepts exceptions from experiment plans and updates the ISPyB BLSampleStatus
16
16
  field according to the type of exception raised."""
17
17
 
18
- def __init__(self):
18
+ def __init__(self, record_loaded_on_success=False):
19
19
  super().__init__(log=ISPYB_ZOCALO_CALLBACK_LOGGER)
20
20
  self._sample_id: int | None = None
21
21
  self._descriptor: str | None = None
22
22
  self._run_id: str | None = None
23
23
 
24
+ # Record 'sample loaded' if document successfully stops
25
+ self.record_loaded_on_success = record_loaded_on_success
26
+
24
27
  def activity_gated_start(self, doc: RunStart):
25
28
  if not self._sample_id and self.active:
26
29
  sample_id = doc.get("metadata", {}).get("sample_id")
@@ -30,6 +33,7 @@ class SampleHandlingCallback(PlanReactiveCallback):
30
33
 
31
34
  def activity_gated_stop(self, doc: RunStop) -> RunStop:
32
35
  if self._run_id == doc.get("run_start"):
36
+ expeye = ExpeyeInteraction()
33
37
  if doc["exit_status"] != "success":
34
38
  exception_type, message = SampleException.type_and_message_from_reason(
35
39
  doc.get("reason", "")
@@ -37,13 +41,17 @@ class SampleHandlingCallback(PlanReactiveCallback):
37
41
  self.log.info(
38
42
  f"Sample handling callback intercepted exception of type {exception_type}: {message}"
39
43
  )
40
- self._record_exception(exception_type)
44
+ self._record_exception(exception_type, expeye)
45
+
46
+ elif self.record_loaded_on_success:
47
+ self._record_loaded(expeye)
48
+
41
49
  self._sample_id = None
42
50
  self._run_id = None
51
+
43
52
  return doc
44
53
 
45
- def _record_exception(self, exception_type: str):
46
- expeye = ExpeyeInteraction()
54
+ def _record_exception(self, exception_type: str, expeye: ExpeyeInteraction):
47
55
  assert self._sample_id, "Unable to record exception due to no sample ID"
48
56
  sample_status = self._decode_sample_status(exception_type)
49
57
  expeye.update_sample_status(self._sample_id, sample_status)
@@ -53,3 +61,7 @@ class SampleHandlingCallback(PlanReactiveCallback):
53
61
  case SampleException.__name__ | CrystalNotFoundException.__name__:
54
62
  return BLSampleStatus.ERROR_SAMPLE
55
63
  return BLSampleStatus.ERROR_BEAMLINE
64
+
65
+ def _record_loaded(self, expeye: ExpeyeInteraction):
66
+ assert self._sample_id, "Unable to record loaded state due to no sample ID"
67
+ expeye.update_sample_status(self._sample_id, BLSampleStatus.LOADED)