diff --git a/startup/10-area-detector.py b/startup/10-area-detector.py index 4397640..7aa62d5 100755 --- a/startup/10-area-detector.py +++ b/startup/10-area-detector.py @@ -1,5 +1,9 @@ from ophyd import Component as Cpt -from ophyd.areadetector.filestore_mixins import FileStoreHDF5IterativeWrite +from ophyd.areadetector.filestore_mixins import ( + FileStoreHDF5IterativeWrite, + FileStoreIterativeWrite, + FileStorePluginBase, +) from ophyd import EpicsSignal, AreaDetector from ophyd import ( ImagePlugin, @@ -40,9 +44,22 @@ def ensure_nonblocking(self): cpt.ensure_nonblocking() -class HDF5PluginWithFileStore(HDF5Plugin, FileStoreHDF5IterativeWrite): +class HDF5PluginWithFileStore(HDF5Plugin, FileStorePluginBase): # AD v2.2.0 (at least) does not have this. It is present in v1.9.1. file_number_sync = None + filestore_spec = "AD_HDF5_v1" + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + # can not be class level because parent sets this in __init__ :( + self.stage_sigs.update( + [ + ("file_template", "%s%s_%6.6d.h5"), + ("file_write_mode", "Stream"), + ("capture", 1), + ] + ) + self._current_index = 0 def get_frames_per_point(self): return self.parent.cam.num_images.get() @@ -52,6 +69,22 @@ def make_filename(self): self._ret = super().make_filename() return self._ret + def stage(self): + super().stage() + self._current_index = 0 + res_kwargs = {} + self._generate_resource(res_kwargs) + + def generate_datum(self, key, timestamp, datum_kwargs): + step = self.get_frames_per_point() + + datum_kwargs = datum_kwargs or {} + datum_kwargs.update({"offset": self._current_index, "num_frames": step}) + + self._current_index += step + + return super().generate_datum(key, timestamp, datum_kwargs) + class AndorKlass(SingleTriggerV33, DetectorBase): cam = Cpt(AndorCam, "cam1:") @@ -267,6 +300,7 @@ def resume(self): for det in [detA1, Andor]: + det.cam.num_images.kind = 'config' det.stats1.total.kind = "hinted" # It does not work since it's not defined in the class, commenting out: # det.stats5.total.kind = 'hinted' diff --git a/startup/20-global_param.py b/startup/20-global_param.py index a44a39f..d121847 100755 --- a/startup/20-global_param.py +++ b/startup/20-global_param.py @@ -9,7 +9,7 @@ # ZONE_DIAMETER = 200 # 200 um ### xridia zone plate OUT_ZONE_WIDTH = ZONE_PLATE["OUT_ZONE_WIDTH"] # 30 nm ZONE_DIAMETER = ZONE_PLATE["ZONE_DIAMETER"] # new commercial zone plate - +zp.wait_for_connection() GLOBAL_VLM_MAG = 10 # vlm magnification GLOBAL_MAG = np.round((DetU.z.position / zp.z.position - 1) * GLOBAL_VLM_MAG, 2) CURRENT_MAG_1 = GLOBAL_MAG diff --git a/startup/40-scans.py b/startup/40-scans.py index 2469172..19e26da 100755 --- a/startup/40-scans.py +++ b/startup/40-scans.py @@ -128,9 +128,16 @@ def _set_andor_param(exposure_time=0.1, period=0.1, chunk_size=1): yield from abs_set(Andor.cam.acquire, 0, wait=True) yield from abs_set(Andor.cam.image_mode, 0, wait=True) - yield from abs_set(Andor.cam.num_images, chunk_size, wait=True) - yield from abs_set(Andor.cam.acquire_time, exposure_time, wait=True) - Andor.cam.acquire_period.put(period) + + yield from bps.configure( + Andor.cam, + { + Andor.cam.num_images.attr_name: chunk_size, + # Andor.cam.acquire_time.attr_name: exposure_time, + # Andor.cam.acquire_period.attr_name: period, + }, + ) + yield from bps.configure(Andor, {}) def _set_rotation_speed(rs=1): @@ -914,7 +921,7 @@ def eng_scan( detectors: list, detector list, e.g.[ic3, ic4, Andor] delay_time: float, delay time after moving motors, in sec - + """ det = [det.name for det in detectors] @@ -992,7 +999,7 @@ def eng_scan_delay( delay_time: float, delay time after moving motors, in sec - note: string + note: string """ global ZONE_PLATE @@ -1234,7 +1241,7 @@ def fly_scan( ------- exposure_time: float, in unit of sec - relative_rot_angle: float, + relative_rot_angle: float, total rotation angles start from current rotary stage (zps.pi_r) position period: float, in unit of sec @@ -1268,7 +1275,7 @@ def fly_scan( simu: Bool, default is False True: will simulate closing/open shutter without really closing/opening False: will really close/open shutter - + """ global ZONE_PLATE motor_x_ini = zps.sx.position @@ -1497,7 +1504,7 @@ def grid2D_rel_inner(): def delay_count(detectors, num=1, delay=None, *, note="", plot_flag=0, md=None): """ - same function as the default "count", + same function as the default "count", re_write it in order to add auto-logging """ global ZONE_PLATE @@ -1591,7 +1598,7 @@ def delay_scan( if 0: not plot note: string - + """ global ZONE_PLATE if Andor in detectors: @@ -1683,7 +1690,7 @@ def xanes_3d_scan(eng_list, exposure_time, relative_rot_angle, period, chunk_siz insert_text(txt) print(txt) - + for eng in eng_list: RE(move_zp_ccd(eng)) RE(fly_scan(exposure_time, relative_rot_angle, period, chunk_size, out_x, out_y, rs, parkpos, note)) @@ -1721,7 +1728,7 @@ def raster_2D_scan( Inputs: ------- - x_range: two-elements list, e.g., [-1, 1], in unit of horizontal screen size + x_range: two-elements list, e.g., [-1, 1], in unit of horizontal screen size y_range: two-elements list, e.g., [-1, 1], in unit of horizontal screen size @@ -1742,7 +1749,7 @@ def raster_2D_scan( out_r: float, default is 0 relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - + img_sizeX: int, default is 2560, it is the pixel number for Andor camera horizontal img_sizeY: int, default is 2160, it is the pixel number for Andor camera vertical @@ -1758,7 +1765,7 @@ def raster_2D_scan( simu: Bool, default is False True: will simulate closing/open shutter without really closing/opening False: will really close/open shutter - + """ global ZONE_PLATE motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] @@ -1926,7 +1933,7 @@ def raster_2D_scan2( Inputs: ------- - x_range: two-elements list, e.g., [-1, 1], in unit of horizontal screen size + x_range: two-elements list, e.g., [-1, 1], in unit of horizontal screen size y_range: two-elements list, e.g., [-1, 1], in unit of horizontal screen size @@ -1947,7 +1954,7 @@ def raster_2D_scan2( out_r: float, default is 0 relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - + img_sizeX: int, default is 2560, it is the pixel number for Andor camera horizontal img_sizeY: int, default is 2160, it is the pixel number for Andor camera vertical @@ -1963,7 +1970,7 @@ def raster_2D_scan2( simu: Bool, default is False True: will simulate closing/open shutter without really closing/opening False: will really close/open shutter - + """ global ZONE_PLATE motor = [zps.sx, zps.sy, zps.sz, zps.pi_r] @@ -2188,7 +2195,7 @@ def multipos_2D_xanes_scan2( For example: RE(multipos_2D_xanes_scan2(Ni_eng_list, x_list=[0,1,2], y_list=[2,3,4], z_list=[0,0,0], r_list=[0,0,0], out_x=1000, out_y=0, out_z=0, out_r=90, repeat_num=2, exposure_time=0.1, sleep_time=60, chunk_size=5, relative_move_flag=True, note='sample') - + Inputs: -------- eng_list: list or numpy array, @@ -2221,7 +2228,7 @@ def multipos_2D_xanes_scan2( out_r: float, default is 0 relative movement of sample by rotating "out_r" degrees, using zps.pi_r to move out sample NOTE: BE CAUSION THAT IT WILL ROTATE SAMPLE BY "out_r" FIRST, AND THEN MOVE X, Y, Z - + repeat_num: integer, default is 1 repeating multiposition xanes scans @@ -2233,13 +2240,13 @@ def multipos_2D_xanes_scan2( chunk_size: int number of background images == num of dark images == num of image for each energy - + relative_move_flag: if 1: relative movement of out_x, out_y, out_z, and out_r if 0: set absolute position of x, y, z, r to move out sample note: string - + """ print(eng_list) print(x_list) @@ -2427,7 +2434,7 @@ def multipos_2D_xanes_scan3( For example: RE(multipos_2D_xanes_scan3(Ni_eng_list, x_list=[0,1,2], y_list=[2,3,4], z_list=[0,0,0], r_list=[0,0,0], out_x=1000, out_y=0, out_z=0, out_r=90, repeat_num=2, sleep_time=60, note='sample') - + Inputs: -------- eng_list: list or numpy array, @@ -2471,7 +2478,7 @@ def multipos_2D_xanes_scan3( number of background images == num of dark images == num of image for each energy note: string - + """ global ZONE_PLATE txt = "starting multipos_2D_xanes_scan3" @@ -2736,7 +2743,7 @@ def repeat_multipos_2D_xanes_scan2(eng_list, x_list, y_list, z_list, r_list, out txt = f'starting "repeat_multipos_2D_xanes_scan2", consists of following scans:' print(txt) - insert_text(txt) + insert_text(txt) for i in range(repeat_num): print(f'repeat #{i}:\n ') yield from multipos_2D_xanes_scan2(eng_list, x_list, y_list, z_list, r_list, out_x, out_y, out_z, out_r, exposure_time, chunk_size, simu, relative_move_flag, note, md) diff --git a/startup/93-load_scan.py b/startup/93-load_scan.py index 91b1064..bb029c5 100755 --- a/startup/93-load_scan.py +++ b/startup/93-load_scan.py @@ -177,8 +177,9 @@ def export_fly_scan(h): imgs = imgs[1:-1] s1 = imgs.shape imgs = imgs.reshape([s1[0] * s1[1], s1[2], s1[3]]) - - with db.reg.handler_context({"AD_HDF5": AreaDetectorHDF5TimestampHandler}): + from area_detector_handlers.handlers import HDF5VariableFramesHandlerTS + with db.reg.handler_context({"AD_HDF5": AreaDetectorHDF5TimestampHandler, + "AD_HDF5_v1": HDF5VariableFramesHandlerTS}): chunked_timestamps = list(h.data("Andor_image")) chunked_timestamps = chunked_timestamps[1:-1]