def addDeviceDataAsStream(devices, label): """ plan: add an ophyd Device as an additional document stream Use this within a custom plan, such as this example:: from apstools.plans import addDeviceStream ... yield from bps.open_run() # ... yield from addDeviceDataAsStream(prescanDeviceList, "metadata_prescan") # ... yield from custom_scan_procedure() # ... yield from addDeviceDataAsStream(postscanDeviceList, "metadata_postscan") # ... yield from bps.close_run() """ yield from bps.create(name=label) if not isinstance(devices, list): # just in case... devices = [devices] for d in devices: yield from bps.read(d) yield from bps.save()
def fly_body(): yield from bps.trigger_and_read([E_centers], name='energy_bins') for y in range(num_scans): # go to start of row yield from bps.mv(mono.linear, l_start) # set the fly speed yield from bps.mv(mono.linear.velocity, flyspeed) yield from bps.trigger_and_read([mono], name='row_ends') for v in ['p1600=0', 'p1600=1']: yield from bps.mv(dtt, v) yield from bps.sleep(0.1) # arm the struck yield from bps.trigger(sclr, group=f'fly_energy_{y}') # fly the motor yield from bps.abs_set(mono.linear, l_stop + a_l_step_size, group=f'fly_energy_{y}') yield from bps.wait(group=f'fly_energy_{y}') yield from bps.trigger_and_read([mono], name='row_ends') yield from bps.mv(mono.linear.velocity, 0.5) # hard coded to let the sclr count its fingers and toes yield from bps.sleep(.1) # read and save the struck yield from bps.create(name='primary') yield from bps.read(sclr) yield from bps.save()
def create_rand_selection_params(motors, population, intensities, bounds): if motors is not None and population is None: # hardware positions = [] change_indx = intensities.index(np.min(intensities)) indv = {} for elem, param in motors.items(): indv[elem] = {} for param_name, elem_obj in param.items(): indv[elem][param_name] = (yield from bps.read(elem_obj))[elem]['value'] positions.append(indv) indv = {} for elem, param in bounds.items(): indv[elem] = {} for param_name, bound in param.items(): indv[elem][param_name] = random.uniform(bound[0], bound[1]) positions.append(indv) return positions, change_indx elif motors is None and population is not None: # sirepo simulation positions = [] change_indx = intensities.index(np.min(intensities)) positions.append(population[0]) indv = {} for elem, param in bounds.items(): indv[elem] = {} for param_name, bound in param.items(): indv[elem][param_name] = random.uniform(bound[0], bound[1]) positions.append(indv) return positions, change_indx
def tester(obj): nonlocal called direct_read = yield from bps.read(obj) rd_read = yield from bps.rd(obj) assert rd_read == direct_read["det"]["value"] called = True
def _plan(self): yield from bps.open_run() # stash numcapture and shutter_enabled num_capture = yield from bps.rd(self.device.hdf5.num_capture) shutter_enabled = yield from bps.rd(self.device.dg1.shutter_enabled) # set to 1 temporarily self.device.hdf5.num_capture.put(1) # Restage to ensure that dark frames goes into a separate file. yield from bps.stage(self.device) yield from bps.mv(self.device.dg1.shutter_enabled, 2) # The `group` parameter passed to trigger MUST start with # bluesky-darkframes-trigger. yield from bps.trigger_and_read([self.device], name='dark') # Restage. yield from bps.unstage(self.device) # restore numcapture and shutter_enabled yield from bps.mv(self.device.hdf5.num_capture, num_capture) yield from bps.mv(self.device.dg1.shutter_enabled, shutter_enabled) # Dark frames finished, moving on to data yield from bps.stage(self.device) status = yield from bps.trigger(self.device, group='primary-trigger') while not status.done: yield from bps.trigger_and_read(self.async_poll_devices, name='labview') yield from bps.sleep(1) yield from bps.create('primary') yield from bps.read(self.device) yield from bps.save() yield from bps.unstage(self.device)
def _snap(md=None): yield from bps.open_run(md) yield from bps.create(name=stream) for obj in objects: # passive observation: DO NOT TRIGGER, only read yield from bps.read(obj) yield from bps.save() yield from bps.close_run()
def fly_row(): # go to start of row target_y = ystart + y * a_ystep_size yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, target_y) yield from bps.mv(y_centers, np.ones(num_xpixels) * target_y) # set the fly speed ret = yield from bps.read(xy_fly_stage.z.user_readback) # (in mm) zpos = (ret[xy_fly_stage.z.user_readback.name]["value"] if ret is not None else 0) yield from bps.mov(z_centers, np.ones(num_xpixels) * zpos) yield from bps.mv(xy_fly_stage.x.velocity, flyspeed) yield from bps.trigger_and_read([xy_fly_stage], name="row_ends") for v in ["p1600=0", "p1600=1"]: yield from bps.mv(dtt, v) yield from bps.sleep(0.1) # arm the struck yield from bps.trigger(sclr, group=f"fly_row_{y}") # maybe start the xspress3 if xspress3 is not None: yield from bps.trigger(xspress3, group=f"fly_row_{y}") yield from bps.sleep(0.1) # fly the motor yield from bps.abs_set(xy_fly_stage.x, xstop + a_xstep_size, group=f"fly_row_{y}") yield from bps.wait(group=f"fly_row_{y}") yield from bps.trigger_and_read([xy_fly_stage], name="row_ends") yield from bps.mv(xy_fly_stage.x.velocity, 5.0) yield from bps.sleep(0.1) # read and save the struck yield from bps.create(name="primary") # yield from bps.read(sclr) yield from bps.read(mono) yield from bps.read(x_centers) yield from bps.read(y_centers) yield from bps.read(z_centers) yield from bps.read(xy_fly_stage.y) yield from bps.read(xy_fly_stage.z) # and maybe the xspress3 if xspress3 is not None: yield from bps.read(xspress3) yield from bps.save()
def change_epu_flt_link(new_target): v = (yield from bps.read(epu1.flt.input_pv)) if v is None: return n = epu1.flt.input_pv.name cur_pv = v[n]['value'] pts = cur_pv.split(' ', maxsplit=1) new_pv = ' '.join([new_target] + pts[1:]) yield from bps.abs_set(epu1.flt.input_pv, new_pv)
def change_epu_flt_link(new_target): v = (yield from bps.read(epu1.flt.input_pv)) if v is None: return n = epu1.flt.input_pv.name cur_pv = v[n]['value'] pts = cur_pv.split(' ', maxsplit=1) new_pv = ' '.join([new_target] + pts[1:]) yield from bps.abs_set(epu1.flt.input_pv, new_pv)
def inner_dark_frame_aware_plan(): tmp = yield from bps.read(shutter.status) init_shutter_state = tmp[shutter.status.name]['value'] if tmp is not None else None yield from bps.mv(shutter, 'Open') for _ in range(num_images): yield from check_and_take_darks() yield from bpp.trigger_and_read([cam], name='primary') yield from bps.mv(shutter, init_shutter_state)
def _scan(md=None): yield from bps.open_run(md) position_list = np.linspace(start, finish, num) signal_list = list(self.signals) signal_list += [ self.axis, ] for pos in position_list: yield from bps.mv(self.axis, pos) yield from bps.trigger_and_read(signal_list) final_position = initial_position if self.peak_detected(): self.tune_ok = True if self.peak_choice == "cen": final_position = self.peaks.cen elif self.peak_choice == "com": final_position = self.peaks.com else: final_position = None self.center = final_position # add stream with results # yield from add_results_stream() stream_name = "PeakStats" results = Results(name=stream_name) results.tune_ok.put(self.tune_ok) results.center.put(self.center) results.final_position.put(final_position) results.initial_position.put(initial_position) for key in results.peakstats_attrs: v = getattr(self.peaks, key) if key in ("crossings", "min", "max"): v = np.array(v) getattr(results, key).put(v) if results.tune_ok.get(): yield from bps.create(name=stream_name) try: yield from bps.read(results) except ValueError as ex: separator = " " * 8 + "-" * 12 print(separator) print(f"Error saving stream {stream_name}:\n{ex}") print(separator) yield from bps.save() yield from bps.mv(self.axis, final_position) self.stats.append(self.peaks) yield from bps.close_run() results.report(stream_name)
def configure_area_det(det, exposure): '''Configure an area detector in "continuous mode"''' def _check_mini_expo(exposure, acq_time): if exposure < acq_time: raise ValueError( "WARNING: total exposure time: {}s is shorter " "than frame acquisition time {}s\n" "you have two choices:\n" "1) increase your exposure time to be at least" "larger than frame acquisition time\n" "2) increase the frame rate, if possible\n" " - to increase exposure time, simply resubmit" " the ScanPlan with a longer exposure time\n" " - to increase frame-rate/decrease the" " frame acquisition time, please use the" " following command:\n" " >>> {} \n then rerun your ScanPlan definition" " or rerun the xrun.\n" "Note: by default, xpdAcq recommends running" "the detector at its fastest frame-rate\n" "(currently with a frame-acquisition time of" "0.1s)\n in which case you cannot set it to a" "lower value.".format( exposure, acq_time, ">>> glbl['frame_acq_time'] = 0.5 #set" " to 0.5s", ) ) # todo make ret = yield from bps.read(det.cam.acquire_time) if ret is None: acq_time = 1 else: acq_time = ret[det.cam.acquire_time.name]["value"] _check_mini_expo(exposure, acq_time) if hasattr(det, "images_per_set"): # compute number of frames num_frame = np.ceil(exposure / acq_time) yield from bps.mov(det.images_per_set, num_frame) else: # The dexela detector does not support `images_per_set` so we just # use whatever the user asks for as the thing # TODO: maybe put in warnings if the exposure is too long? num_frame = 1 computed_exposure = num_frame * acq_time # print exposure time print( "INFO: requested exposure time = {} - > computed exposure time" "= {}".format(exposure, computed_exposure) ) return num_frame, acq_time, computed_exposure
def _read_the_first_key(obj): """Helper to get 'the right' reading.""" reading = yield from bps.read(obj) if reading is None: return None hints = obj.hints.get("fields", []) if len(hints): key, *_ = hints else: key, *_ = list(reading) return reading[key]["value"]
def fly_once(y): # for y in range(num_scans): # go to start of row yield from bps.checkpoint() yield from bps.mv(mono.linear.velocity, 1) yield from bps.mv(mono.linear, l_start) # set the fly speed yield from bps.mv(mono.linear.velocity, flyspeed) yield from bps.trigger_and_read([mono], name="row_ends") for v in ["p1600=0", "p1600=1"]: yield from bps.mv(dtt, v) yield from bps.sleep(0.1) # arm the Struck yield from bps.trigger(sclr, group=f"fly_energy_{y}") if xspress3 is not None: yield from bps.trigger(xspress3, group=f"fly_energy_{y}") # fly the motor yield from bps.abs_set(mono.linear, l_stop + a_l_step_size, group=f"fly_energy_{y}") yield from bps.wait(group=f"fly_energy_{y}") yield from bps.trigger_and_read([mono], name="row_ends") yield from bps.mv(mono.linear.velocity, flyspeed) # hard coded to let the sclr count its fingers and toes yield from bps.sleep(0.1) # read and save the struck yield from bps.create(name="primary") yield from bps.read(sclr) if xspress3 is not None: yield from bps.read(xspress3) yield from bps.save()
def fly_row(): # go to start of row yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, ystart + y * ystep_size) # set the fly speed yield from bps.mv(xy_fly_stage.x.velocity, flyspeed) yield from bps.trigger_and_read([xy_fly_stage], name='row_ends') for v in ['p1600=0', 'p1600=1']: yield from bps.mv(dtt, v) yield from bps.sleep(0.1) # arm the struck yield from bps.trigger(sclr, group=f'fly_row_{y}') # maybe start the xspress3 if xspress3 is not None: yield from bps.trigger(xspress3, group=f'fly_row_{y}') yield from bps.sleep(0.1) # fly the motor yield from bps.abs_set(xy_fly_stage.x, xstop + a_xstep_size, group=f'fly_row_{y}') yield from bps.wait(group=f'fly_row_{y}') yield from bps.trigger_and_read([xy_fly_stage], name='row_ends') yield from bps.mv(xy_fly_stage.x.velocity, 5.0) yield from bps.sleep(.1) # read and save the struck yield from bps.create(name='primary') yield from bps.read(sclr) # and maybe the xspress3 if xspress3 is not None: yield from bps.read(xspress3) yield from bps.save()
def _scan(md=None): yield from bps.open_run(md) position_list = np.linspace(start, finish, num) signal_list = list(self.signals) signal_list += [ self.axis, ] for pos in position_list: yield from bps.mv(self.axis, pos) yield from bps.trigger_and_read(signal_list) final_position = initial_position if self.peak_detected(): self.tune_ok = True if self.peak_choice == "cen": final_position = self.peaks.cen elif self.peak_choice == "com": final_position = self.peaks.com else: final_position = None self.center = final_position # add stream with results # yield from add_results_stream() stream_name = "PeakStats" results = Results(name=stream_name) for key in "tune_ok center".split(): getattr(results, key).put(getattr(self, key)) results.final_position.put(final_position) results.initial_position.put(initial_position) for key in results.peakstats_attrs: v = getattr(self.peaks, key) if key in ("crossings", "min", "max"): v = np.array(v) getattr(results, key).put(v) if results.tune_ok.value: yield from bps.create(name=stream_name) yield from bps.read(results) yield from bps.save() yield from bps.mv(self.axis, final_position) self.stats.append(self.peaks) yield from bps.close_run() results.report()
def dark_plan(cam, dark_frame_cache, obsolete_secs, shutter): if (dark_frame_cache.just_started or # first run after instantiation (dark_frame_cache.last_collected is not None and time.monotonic() - dark_frame_cache.last_collected > obsolete_secs)): tmp = yield from bps.read(shutter.status) init_shutter_state = tmp[shutter.status.name]['value'] if tmp is not None else None yield from bps.mv(shutter, 'Close') yield from bps.trigger(cam, group='cam') yield from bps.wait('cam') yield from bps.mv(shutter, init_shutter_state) teleport(cam, dark_frame_cache) dark_frame_cache.just_started = False dark_frame_cache.update_done = True else: dark_frame_cache.update_done = False
def create_selection_params(motors, population, cross_indv): if motors is not None and population is None: # hardware positions = [elm for elm in cross_indv] indv = {} for elem, param in motors.items(): indv[elem] = {} for param_name, elem_obj in param.items(): indv[elem][param_name] = (yield from bps.read(elem_obj))[elem]['value'] positions.insert(0, indv) return positions if motors is None and population is not None: # sirepo simulation positions = [elm for elm in cross_indv] positions.insert(0, population[0]) return positions
def dull_scan(mot, count, sig=None, sleep_time=0): if sig: thread = threading.Thread(target=sig_sequence, args=(sig, )) thread.start() for i in range(count): yield from checkpoint() try: yield from mv(mot, i) except: pass # make every step take 1s extra yield from sleep(sleep_time) yield from checkpoint() yield from create() yield from read(mot) yield from save() yield from checkpoint()
def configure_area_det(det: typing.Any, exposure: float, acq_time: float): """Configure exposure time of a detector in continuous acquisition mode. Parameters ---------- det : The area detector to be configured. exposure : The total exposure time. One exposure contains one or multiple frames. acq_time : The frame acquisition time. The exposure time per frame. Returns ------- num_frame : Number of frames. real_acq_time: The real frame acquisition time after set. computed_exposure : The real exposure time after set. """ xb._check_mini_expo(exposure, acq_time) yield from bps.mv(det.cam.acquire_time, acq_time) res = yield from bps.read(det.cam.acquire_time) real_acq_time = res[det.cam.acquire_time.name]["value"] if res else 1 if hasattr(det, "images_per_set"): # compute number of frames num_frame = math.ceil(exposure / real_acq_time) yield from bps.mv(det.images_per_set, num_frame) else: # The dexela detector does not support `images_per_set` so we just # use whatever the user asks for as the thing num_frame = 1 computed_exposure = num_frame * real_acq_time # print exposure time print("INFO: requested exposure time = {} - > computed exposure time" "= {}".format(exposure, computed_exposure)) return num_frame, real_acq_time, computed_exposure
def inner_trigger_and_read(): nonlocal group if group is None: group = short_uid('trigger') no_wait = True for obj in devices: if hasattr(obj, 'trigger'): no_wait = False yield from bps.trigger(obj, group=group) # Skip 'wait' if none of the devices implemented a trigger method. if not no_wait: yield from bps.wait(group=group) yield from bps.create(name) ret = {} # collect and return readings to give plan access to them for obj in devices: reading = (yield from bps.read(obj)) if reading is not None: ret.update(reading) yield from bps.save() return ret
def documentation_run(words, md=None, stream=None): """ Save text as a bluesky run. """ text = Signal(value=words, name="text") stream = stream or "primary" _md = dict( purpose=f"save text as bluesky run", plan_name="documentation_run", ) _md.update(md or {}) bec.disable_plots() bec.disable_table() uid = yield from bps.open_run(md=_md) yield from bps.create(stream) yield from bps.read(text) yield from bps.save() yield from bps.close_run() bec.enable_table() bec.enable_plots() return uid
def configure_area_det(det, exposure, acq_time): """Configure exposure time of a detector in continuous acquisition mode""" _check_mini_expo(exposure, acq_time) yield from bps.mv(det.cam.acquire_time, acq_time) res = yield from bps.read(det.cam.acquire_time) real_acq_time = res[det.cam.acquire_time.name]["value"] if res else 1 if hasattr(det, "images_per_set"): # compute number of frames num_frame = np.ceil(exposure / real_acq_time) yield from bps.mv(det.images_per_set, num_frame) else: # The dexela detector does not support `images_per_set` so we just # use whatever the user asks for as the thing num_frame = 1 computed_exposure = num_frame * real_acq_time # print exposure time print( "INFO: requested exposure time = {} - > computed exposure time" "= {}".format(exposure, computed_exposure) ) return num_frame, real_acq_time, computed_exposure
def _run_E_ramp(dets, start, stop, velocity, deadband, *, streamname='primary', md=None): if md is None: md = {} md = ChainMap( md, { 'plan_args': { 'dets': list(map(repr, dets)), 'start': start, 'stop': stop, 'velocity': velocity, 'deadband': deadband }, 'plan_name': 'E_ramp', 'motors': [pgm.energy.name] }) # put the energy at the starting value yield from bps.abs_set(pgm.energy, start, wait=True) yield from bps.abs_set(pgm.fly.start_sig, start, wait=True) yield from bps.abs_set(pgm.fly.stop_sig, stop, wait=True) yield from bps.abs_set(pgm.fly.velocity, velocity, wait=True) if specs in dets: specs.stage() # TODO do this with stage old_db = epu1.flt.output_deadband.get() yield from bps.abs_set(epu1.flt.output_deadband, deadband) # get the old vlaue v = (yield from bps.read(epu1.flt.input_pv)) if v is None: old_link = '' else: n = epu1.flt.input_pv.name old_link = v[n]['value'] # define a clean up plan def clean_up(): # move the energy setpoint to where the energy really is yield from bps.abs_set(pgm.energy, pgm.energy.position, wait=True) # set the interpolator to look at what it was looking at before # the scan. This should be the energy set point. yield from bps.abs_set(epu1.flt.input_pv, old_link, wait=True) yield from bps.abs_set(epu1.flt.output_deadband, old_db, wait=True) if specs in dets: specs.unstage() # change to track the readout energy yield from change_epu_flt_link(pgm_energy.readback.pvname) def go_plan(): ret = (yield from bps.abs_set(pgm.fly.fly_start, 1)) st = StatusBase() enum_map = pgm.fly.scan_status.describe()[ pgm.fly.scan_status.name]['enum_strs'] def _done_cb(value, old_value, **kwargs): print(f'Old value {old_value} -> new value {value}') print( f'Old value type {type(old_value)} -> new value {type(value)}') try: old_value = enum_map[int(old_value)] except (TypeError, ValueError): ... try: value = enum_map[int(value)] except (TypeError, ValueError): ... if old_value != value and value == 'Ready': st._finished() pgm.fly.scan_status.clear_sub(_done_cb) if ret is not None: pgm.fly.scan_status.subscribe(_done_cb, run=False) else: st._finished() print('SIM MODE') return st def inner_plan(): yield from trigger_and_read(dets, name=streamname) print(md) rp = ramp_plan(go_plan(), pgm.energy, inner_plan, period=None, md=md) return (yield from bpp.finalize_wrapper(rp, clean_up()))
def _run_E_ramp(dets, start, stop, velocity, deadband, *, md=None): if md is None: md = {} md = ChainMap(md, {'plan_args': {'dets': list(map(repr, dets)), 'start': start, 'stop': stop, 'velocity': velocity, 'deadband': deadband}, 'plan_name': 'E_ramp', 'motors': [pgm.energy.name]}) # put the energy at the starting value yield from bps.abs_set(pgm.energy, start, wait=True) yield from bps.abs_set(pgm.fly.start_sig, start, wait=True) yield from bps.abs_set(pgm.fly.stop_sig, stop, wait=True) yield from bps.abs_set(pgm.fly.velocity, velocity, wait=True) # TODO do this with stage old_db = epu1.flt.output_deadband.get() yield from bps.abs_set(epu1.flt.output_deadband, deadband) # get the old vlaue v = (yield from bps.read(epu1.flt.input_pv)) if v is None: old_link = '' else: n = epu1.flt.input_pv.name old_link = v[n]['value'] # define a clean up plan def clean_up(): # move the energy setpoint to where the energy really is yield from bps.abs_set(pgm.energy, pgm.energy.position, wait=True) # set the interpolator to look at what it was looking at before # the scan. This should be the energy set point. yield from bps.abs_set(epu1.flt.input_pv, old_link, wait=True) yield from bps.abs_set(epu1.flt.output_deadband, old_db, wait=True) # change to track the readout energy yield from change_epu_flt_link(pgm_energy.readback.pvname) def go_plan(): ret = (yield from bps.abs_set(pgm.fly.fly_start, 1)) st = StatusBase() enum_map = pgm.fly.scan_status.describe()[pgm.fly.scan_status.name]['enum_strs'] def _done_cb(value, old_value, **kwargs): old_value = enum_map[int(old_value)] value = enum_map[int(value)] if old_value != value and value == 'Ready': st._finished() pgm.fly.scan_status.clear_sub(_done_cb) if ret is not None: pgm.fly.scan_status.subscribe(_done_cb, run=False) else: st._finished() print('SIM MODE') return st def inner_plan(): yield from trigger_and_read(dets) print(md) rp = ramp_plan(go_plan(), pgm.energy, inner_plan, period=None, md=md) return (yield from bpp.finalize_wrapper(rp, clean_up()))
def xy_fly(scan_title, *, dwell_time, xstart, xstop, xstep_size, ystart, ystop, ystep_size=None, xspress3=None): """Do a x-y fly scan. The x-motor is the 'fast' direction. Parameters ---------- dwell_time : float Target time is s on each pixel xstart, xstop : float The start and stop values in the fast direction in mm xstep_size : xstep_size is step of x movement ystart, ystop : float The start and stop values in the slow direction in mm ystep_size : ystep_size use xstep_size if it isn't passed in scan_title : str Title of scan, required. """ xy_fly_stage = xy_stage _validate_motor_limits(xy_fly_stage.x, xstart, xstop, 'x') _validate_motor_limits(xy_fly_stage.y, ystart, ystop, 'y') ystep_size = ystep_size if ystep_size is not None else xstep_size assert dwell_time > 0, f'dwell_time ({dwell_time}) must be more than 0' assert xstep_size > 0, f'xstep_size ({xstep_size}) must be more than 0' assert ystep_size > 0, f'ystep_size ({ystep_size}) must be more than 0' ret = yield from bps.read(xy_fly_stage.x.mres) # (in mm) xmres = (ret[xy_fly_stage.x.mres.name]['value'] if ret is not None else .0003125) ret = yield from bps.read(xy_fly_stage.y.mres) # (in mm) ymres = (ret[xy_fly_stage.y.mres.name]['value'] if ret is not None else .0003125) prescale = int(np.floor((xstep_size / (5 * xmres)))) a_xstep_size = prescale * (5 * xmres) a_ystep_size = int(np.floor((ystep_size / (ymres)))) * ymres num_xpixels = int(np.floor((xstop - xstart) / a_xstep_size)) num_ypixels = int(np.floor((ystop - ystart) / a_ystep_size)) flyspeed = a_xstep_size / dwell_time # this is in mm/s try: xy_fly_stage.x.velocity.check_value(flyspeed) except LimitError as e: raise LimitError(f'You requested a range of {xstop - xstart} with ' f'{num_xpixels} pixels and a dwell time of ' f'{dwell_time}. This requires a ' f'motor velocity of {flyspeed} which ' 'is out of range.') from e # set up delta-tau trigger to fast motor for v in ['p1600=0', 'p1607=1', 'p1600=1']: yield from bps.mv(dtt, v) yield from bps.sleep(0.1) # TODO make this a message? sclr.set_mode('flying') # poke the struck settings yield from bps.mv(sclr.mcas.prescale, prescale) yield from bps.mv(sclr.mcas.nuse, num_xpixels) if xspress3 is not None: yield from bps.mov(xs.external_trig, True) yield from mov(xspress3.total_points, num_xpixels) yield from mov(xspress3.hdf5.num_capture, num_xpixels) yield from mov(xspress3.settings.num_images, num_xpixels) @bpp.reset_positions_decorator([xy_fly_stage.x, xy_fly_stage.y]) @bpp.stage_decorator([sclr]) @bpp.baseline_decorator([mono, xy_fly_stage]) # TODO put is other meta data @bpp.run_decorator(md={'scan_title': scan_title}) def fly_body(): yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, ystart) @bpp.stage_decorator([x for x in [xspress3] if x is not None]) def fly_row(): # go to start of row yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, ystart + y * ystep_size) # set the fly speed yield from bps.mv(xy_fly_stage.x.velocity, flyspeed) yield from bps.trigger_and_read([xy_fly_stage], name='row_ends') for v in ['p1600=0', 'p1600=1']: yield from bps.mv(dtt, v) yield from bps.sleep(0.1) # arm the struck yield from bps.trigger(sclr, group=f'fly_row_{y}') # maybe start the xspress3 if xspress3 is not None: yield from bps.trigger(xspress3, group=f'fly_row_{y}') yield from bps.sleep(0.1) # fly the motor yield from bps.abs_set(xy_fly_stage.x, xstop + a_xstep_size, group=f'fly_row_{y}') yield from bps.wait(group=f'fly_row_{y}') yield from bps.trigger_and_read([xy_fly_stage], name='row_ends') yield from bps.mv(xy_fly_stage.x.velocity, 5.0) yield from bps.sleep(.1) # read and save the struck yield from bps.create(name='primary') yield from bps.read(sclr) # and maybe the xspress3 if xspress3 is not None: yield from bps.read(xspress3) yield from bps.save() for y in range(num_ypixels): if xspress3 is not None: yield from bps.mov(xspress3.fly_next, True) yield from fly_row() yield from fly_body()
def sscan_1D( sscan, poll_delay_s=0.001, phase_timeout_s = 60.0, running_stream="primary", final_array_stream=None, device_settings_stream="settings", md=None): """ simple 1-D scan using EPICS synApps sscan record assumes the sscan record has already been setup properly for a scan PARAMETERS sscan : Device one EPICS sscan record (instance of `apstools.synApps.sscanRecord`) running_stream : str or `None` (default: ``"primary"``) Name of document stream to write positioners and detectors data made available while the sscan is running. This is typically the scan data, row by row. If set to `None`, this stream will not be written. final_array_stream : str or `None` (default: ``None``) Name of document stream to write positioners and detectors data posted *after* the sscan has ended. If set to `None`, this stream will not be written. device_settings_stream : str or `None` (default: ``"settings"``) Name of document stream to write *settings* of the sscan device. This is all the information returned by ``sscan.read()``. If set to `None`, this stream will not be written. poll_delay_s : float (default: 0.001 seconds) How long to sleep during each polling loop while collecting interim data values and waiting for sscan to complete. Must be a number between zero and 0.1 seconds. phase_timeout_s : float (default: 60 seconds) How long to wait after last update of the ``sscan.FAZE``. When scanning, we expect the scan phase to update regularly as positioners move and detectors are triggered. If the scan hangs for some reason, this is a way to end the plan early. To cancel this feature, set it to ``None``. NOTE about the document stream names Make certain the names for the document streams are different from each other. If you make them all the same (such as ``primary``), you will have difficulty when reading your data later on. *Don't cross the streams!* EXAMPLE Assume that the chosen sscan record has already been setup. from apstools.devices import sscanDevice scans = sscanDevice(P, name="scans") from apstools.plans import sscan_1D RE(sscan_1D(scans.scan1), md=dict(purpose="demo")) """ global new_data, inactive_deadline if not (0 <= poll_delay_s <= 0.1): raise ValueError( "poll_delay_s must be a number between 0 and 0.1," f" received {poll_delay_s}") t0 = time.time() sscan_status = DeviceStatus(sscan.execute_scan) started = False new_data = False inactive_deadline = time.time() if phase_timeout_s is not None: inactive_deadline += phase_timeout_s def execute_cb(value, timestamp, **kwargs): """watch for sscan to complete""" if started and value in (0, "IDLE"): sscan_status._finished() sscan.execute_scan.unsubscribe_all() sscan.scan_phase.unsubscribe_all() def phase_cb(value, timestamp, **kwargs): """watch for new data""" global new_data, inactive_deadline if phase_timeout_s is not None: inactive_deadline = time.time() + phase_timeout_s if value in (15, "RECORD SCALAR DATA"): new_data = True # set flag for main plan # acquire only the channels with non-empty configuration in EPICS sscan.select_channels() # pre-identify the configured channels sscan_data_objects = _get_sscan_data_objects(sscan) # watch for sscan to complete sscan.execute_scan.subscribe(execute_cb) # watch for new data to be read out sscan.scan_phase.subscribe(phase_cb) _md = dict(plan_name="sscan_1D") _md.update(md or {}) yield from bps.open_run(_md) # start data collection yield from bps.mv(sscan.execute_scan, 1) # start sscan started = True # collect and emit data, wait for sscan to end while not sscan_status.done or new_data: if new_data and running_stream is not None: yield from bps.create(running_stream) for _k, obj in sscan_data_objects.items(): yield from bps.read(obj) yield from bps.save() new_data = False if phase_timeout_s is not None and time.time() > inactive_deadline: print(f"No change in sscan record for {phase_timeout_s} seconds.") print("ending plan early as unsuccessful") sscan_status._finished(success=False) yield from bps.sleep(poll_delay_s) # dump the complete data arrays if final_array_stream is not None: yield from bps.create(final_array_stream) # we have to search for the arrays since they have ``kind="omitted"`` # (which means they do not get reported by the ``.read()`` method) for part in (sscan.positioners, sscan.detectors): for nm in part.read_attrs: if "." not in nm: # TODO: write just the acquired data, not the FULL arrays! yield from bps.read(getattr(part, nm).array) yield from bps.save() # dump the entire sscan record into another stream if device_settings_stream is not None: yield from bps.create(device_settings_stream) yield from bps.read(sscan) yield from bps.save() yield from bps.close_run() elapsed = time.time() - t0 print(f"total time for sscan_1D: {elapsed} s") return sscan_status
def _check_signal(): val = yield from bps.read(done_signal) if val is None: return True val = val[signal_key]['value'] return bool(val)
def xy_fly( scan_title, *, beamline_operator, dwell_time, xstart, xstop, xstep_size, ystart, ystop, ystep_size=None, xspress3=None, ): """Do a x-y fly scan. The x-motor is the 'fast' direction. Parameters ---------- scan_title : str A name for the scan. beamline_operator : str The individual responsible for this scan. Appears in output directory path. dwell_time : float Target time is s on each pixel xstart, xstop : float The start and stop values in the fast direction in mm xstep_size : xstep_size is step of x movement ystart, ystop : float The start and stop values in the slow direction in mm ystep_size : ystep_size use xstep_size if it isn't passed in scan_title : str Title of scan, required. """ xy_fly_stage = xy_stage _validate_motor_limits(xy_fly_stage.x, xstart, xstop, "x") _validate_motor_limits(xy_fly_stage.y, ystart, ystop, "y") ystep_size = ystep_size if ystep_size is not None else xstep_size assert dwell_time > 0, f"dwell_time ({dwell_time}) must be more than 0" assert xstep_size > 0, f"xstep_size ({xstep_size}) must be more than 0" assert ystep_size > 0, f"ystep_size ({ystep_size}) must be more than 0" ret = yield from bps.read(xy_fly_stage.x.mres) # (in mm) xmres = ret[ xy_fly_stage.x.mres.name]["value"] if ret is not None else 0.0003125 ret = yield from bps.read(xy_fly_stage.y.mres) # (in mm) ymres = ret[ xy_fly_stage.y.mres.name]["value"] if ret is not None else 0.0003125 prescale = int(np.floor((xstep_size / (5 * xmres)))) a_xstep_size = prescale * (5 * xmres) a_ystep_size = int(np.floor((ystep_size / (ymres)))) * ymres num_xpixels = int(np.floor((xstop - xstart) / a_xstep_size)) num_ypixels = int(np.floor((ystop - ystart) / a_ystep_size)) yield from bps.mv( x_centers, a_xstep_size / 2 + xstart + np.arange(num_xpixels) * a_xstep_size) # SRX original roi_key = getattr(xs.channel1.rois, roi_name).value.name roi_livegrid_key = xs.channel1.rois.roi01.value.name fig = plt.figure("xs") fig.clf() roi_livegrid = LiveGrid( (num_ypixels + 1, num_xpixels + 1), roi_livegrid_key, clim=None, cmap="inferno", xlabel="x (mm)", ylabel="y (mm)", extent=[xstart, xstop, ystart, ystop], x_positive="right", y_positive="down", ax=fig.gca(), ) flyspeed = a_xstep_size / dwell_time # this is in mm/s try: xy_fly_stage.x.velocity.check_value(flyspeed) except LimitError as e: raise LimitError(f"You requested a range of {xstop - xstart} with " f"{num_xpixels} pixels and a dwell time of " f"{dwell_time}. This requires a " f"motor velocity of {flyspeed} which " "is out of range.") from e # set up delta-tau trigger to fast motor for v in ["p1600=0", "p1607=1", "p1600=1"]: yield from bps.mv(dtt, v) yield from bps.sleep(0.1) # TODO make this a message? sclr.set_mode("flying") # poke the struck settings yield from bps.mv(sclr.mcas.prescale, prescale) yield from bps.mv(sclr.mcas.nuse, num_xpixels) if xspress3 is not None: yield from bps.mv(xs.external_trig, True) yield from bps.mv(xspress3.total_points, num_xpixels) yield from bps.mv(xspress3.hdf5.num_capture, num_xpixels) yield from bps.mv(xspress3.settings.num_images, num_xpixels) @bpp.reset_positions_decorator([xy_fly_stage.x, xy_fly_stage.y]) @bpp.subs_decorator({"all": [roi_livegrid]}) @bpp.monitor_during_decorator([xs.channel1.rois.roi01.value]) @bpp.stage_decorator([sclr]) @bpp.baseline_decorator([mono, xy_fly_stage]) # TODO put is other meta data @bpp.run_decorator( md={ "scan_title": scan_title, "operator": beamline_operator, "user_input": { "dwell_time": dwell_time, "xstart": xstart, "xstop": xstop, "xstep_size": xstep_size, "ystart": ystart, "ystep_size": ystep_size, }, "derived_input": { "actual_ystep_size": a_ystep_size, "actual_xstep_size": a_xstep_size, "fly_velocity": flyspeed, "xpixels": num_xpixels, "ypixels": num_ypixels, "prescale": prescale, }, }) def fly_body(): yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, ystart) @bpp.stage_decorator([x for x in [xspress3] if x is not None]) def fly_row(): # go to start of row target_y = ystart + y * a_ystep_size yield from bps.mv(xy_fly_stage.x, xstart, xy_fly_stage.y, target_y) yield from bps.mv(y_centers, np.ones(num_xpixels) * target_y) # set the fly speed ret = yield from bps.read(xy_fly_stage.z.user_readback) # (in mm) zpos = (ret[xy_fly_stage.z.user_readback.name]["value"] if ret is not None else 0) yield from bps.mov(z_centers, np.ones(num_xpixels) * zpos) yield from bps.mv(xy_fly_stage.x.velocity, flyspeed) yield from bps.trigger_and_read([xy_fly_stage], name="row_ends") for v in ["p1600=0", "p1600=1"]: yield from bps.mv(dtt, v) yield from bps.sleep(0.1) # arm the struck yield from bps.trigger(sclr, group=f"fly_row_{y}") # maybe start the xspress3 if xspress3 is not None: yield from bps.trigger(xspress3, group=f"fly_row_{y}") yield from bps.sleep(0.1) # fly the motor yield from bps.abs_set(xy_fly_stage.x, xstop + a_xstep_size, group=f"fly_row_{y}") yield from bps.wait(group=f"fly_row_{y}") yield from bps.trigger_and_read([xy_fly_stage], name="row_ends") yield from bps.mv(xy_fly_stage.x.velocity, 5.0) yield from bps.sleep(0.1) # read and save the struck yield from bps.create(name="primary") # yield from bps.read(sclr) yield from bps.read(mono) yield from bps.read(x_centers) yield from bps.read(y_centers) yield from bps.read(z_centers) yield from bps.read(xy_fly_stage.y) yield from bps.read(xy_fly_stage.z) # and maybe the xspress3 if xspress3 is not None: yield from bps.read(xspress3) yield from bps.save() for y in range(num_ypixels): if xspress3 is not None: yield from bps.mv(xspress3.fly_next, True) yield from fly_row() yield from fly_body() # save the start document to a file for the benefit of the user start = db[-1].start dt = datetime.datetime.fromtimestamp(start["time"]) filepath = os.path.expanduser( f"~/Users/Data/{start['operator']}/{dt.date().isoformat()}/xy_fly/" f"{start['scan_title']}-{start['scan_id']}-{start['operator']}-{dt.time().isoformat()}.log" ) os.makedirs(os.path.dirname(filepath), exist_ok=True) with open(filepath, "wt") as output_file: output_file.write(pprint.pformat(start))
def _get_v_with_dflt(sig, dflt): ret = yield from bps.read(sig) return ret[sig.name]["value"] if ret is not None else dflt
def custom(detector): for i in range(3): yield from create() yield from read(detector) yield from save()
def add_daq_read(msg): if msg.obj is not daq: yield from bps.read(daq) return (yield msg)