def laser_in(wait=False, timeout=10): """ Insert the Reference Laser and clear the beampath Parameters ---------- wait: bool, optional Wait and check that motion is properly completed timeout: float, optional Time to wait for motion completion if requested to do so Operations ---------- * Insert the Reference Laser * Set the Wave8 out (35 mm) * Set the DG1 slits to 6 mm x 6 mm * Set the DG2 upstream slits to 6 mm x 6 mm * Set the DG2 midstream slits to 1 mm x 1 mm * Set the DG2 downstream slits to 1 mm x 1 mm """ # Command motion and collect status objects ref = mfx_reflaser.insert(wait=False) w8 = mfx_dg1_wave8_motor.move(35, wait=False) dg1 = mfx_dg1_slits.move(6., wait=False) dg2_us = mfx_dg2_upstream_slits.move(6., wait=False) dg2_ms = mfx_dg2_midstream_slits.move(1., wait=False) dg2_ds = mfx_dg2_downstream_slits.move(1., wait=False) # Combine status and wait for completion if wait: status_wait(ref & w8 & dg1 & dg2_us & dg2_ms & dg2_ds, timeout=timeout)
def home(self, direction=None, wait=True, **kwargs): """ Perform the configured homing function. This is set on the controller. Unlike other kinds of axes, only the single pre-programmed homing routine can be used, so the ``direction`` argument has no effect. """ self._run_subs(sub_type=self._SUB_REQ_DONE, success=False) self._reset_sub(self._SUB_REQ_DONE) status = MoveStatus(self, self.plc.home_pos.get(), timeout=None, settle_time=self._settle_time) self.plc.cmd_home.put(1) self.subscribe(status._finished, event_type=self._SUB_REQ_DONE, run=False) try: if wait: status_wait(status) except KeyboardInterrupt: self.stop() raise return status
def wait(self, status=None): """Wait for the inputted status to complete.""" try: status = status or self.status status_wait(status) except KeyboardInterrupt: pass
def set_energy(self, E, wait=False, check_status=True): """ Sets the angles of the crystals in the delay line to maximize the inputted energy. Parameters --------- E : float Energy to use for the system. wait : bool, optional Wait for each motor to complete the motion. check_status : bool, optional Check if the motors are in a valid state to move. """ # Check to make sure the motors are in a valid state to move if check_status: self.check_status(energy=E) # Perform the move status = [ motor.move(pos, wait=False, check_status=False) for motor, pos in zip(self._energy_motors, self._get_move_positions(E)) ] # Wait for the motions to finish if wait: for s in status: logger.info("Waiting for {} to finish move ...".format( s.device.name)) status_wait(s) return status
def move(self, position, moved_cb=None, timeout=None, wait=False): """ Move to the desired state and return completion information. Parameters ---------- position: ``int`` or ``str`` The enumerate state or the corresponding integer moved_cb: ``function``, optional moved_cb(obj=self) will be called at the end of motion timeout: ``int`` or ``float``, optional Move timeout in seconds wait: ``bool``, optional If True, do not return until the motion has completed. Returns ------- status: ``StateStatus`` ``Status`` object that represents the move's progress. """ status = self.set(position, moved_cb=moved_cb, timeout=timeout) if wait: status_wait(status) return status
def move(self, position, moved_cb=None, timeout=None, wait=False): """ Move to the desired state and return completion information. Parameters ---------- position : int or str The enumerate state or the corresponding integer. moved_cb : callable, optional Function to call at the end of motion. i.e. ``moved_cb(obj=self)`` will be called when move is complete. timeout : int or float, optional Move timeout in seconds. wait : bool, optional If `True`, do not return until the motion has completed. Returns ------- status : StateStatus `Status` object that represents the move's progress. """ status = self.set(position, moved_cb=moved_cb, timeout=timeout) if wait: status_wait(status) return status
def home(self, direction, wait=True, **kwargs): '''Perform the default homing function in the desired direction Parameters ---------- direction : HomeEnum Direction in which to perform the home search. ''' direction = HomeEnum(direction) self._started_moving = False position = (self.low_limit+self.high_limit)/2 status = super().move(position, **kwargs) if direction == HomeEnum.forward: self.home_forward.put(1, wait=False) else: self.home_reverse.put(1, wait=False) try: if wait: status_wait(status) except KeyboardInterrupt: self.stop() raise return status
def set(self, position, *, wait=False, timeout=None, settle_time=None, moved_cb=None): # In case nothing needs to be moved, just create a finished status status = Status() status.set_finished() # Mono if abs(self.get()-position) > mono.energy.tolerance.get(): mono_status = mono.energy.set( position, wait=wait, timeout=timeout, moved_cb=moved_cb ) status = AndStatus(status, mono_status) # Phase retarders for pr in [pr1, pr2, pr3]: if pr.tracking.get(): pr_status = pr.energy.move( position, wait=wait, timeout=timeout, moved_cb=moved_cb ) status = AndStatus(status, pr_status) # Undulator if undulator.downstream.tracking.get(): und_pos = position + undulator.downstream.energy.offset.get() und_status = undulator.downstream.energy.move( und_pos, wait=wait, timeout=timeout, moved_cb=moved_cb ) status = AndStatus(status, und_status) if wait: status_wait(status) return status
def reinitialize(self, wait=False): """ Reinitialize the IMS motor. Parameters ---------- wait : bool Wait for the motor to be fully intialized. Returns ------- :class:`~ophyd.status.SubscriptionStatus` Status object reporting the initialization state of the motor. """ logger.info('Reinitializing motor') # Issue command self.reinit_command.put(1) # Check error def initialize_complete(value=None, **kwargs): return value != 3 # Generate a status st = SubscriptionStatus(self.error_severity, initialize_complete, settle_time=0.5) # Wait on status if requested if wait: status_wait(st) return st
def _clear_flag(self, flag, wait=False, timeout=10): """Clear flag whose information is in :attr:`._bit_flags`""" # Gather our flag information flag_info = self._bit_flags[flag] bit = flag_info['readback'] mask = flag_info.get('mask', 1) # Create a callback function to check for bit def flag_is_cleared(value=None, **kwargs): return not bool((int(value) >> bit) & mask) # Check that we need to actually set the flag if flag_is_cleared(value=self.bit_status.get()): logger.debug("%s flag is not currently active", flag) st = DeviceStatus(self) st.set_finished() return st # Issue our command logger.info('Clearing %s flag ...', flag) self.seq_seln.put(flag_info['clear']) # Generate a status st = SubscriptionStatus(self.bit_status, flag_is_cleared) if wait: status_wait(st, timeout=timeout) return st
def laser_out(wait=False, timeout=10): """ Remove the Reference Laser and configure the beamline Parameters ---------- wait: bool, optional Wait and check that motion is properly completed timeout: float, optional Time to wait for motion completion if requested to do so Operations ---------- * Remove the Reference Laser * Set the Wave8 Target 3 In (5.5 mm) * Set the DG1 slits to 0.7 mm x 0.7 mm * Set the DG2 upstream slits to 0.7 mm x 0.7 mm * Set the DG2 midstream slits to 0.7 mm x 0.7 mm * Set the DG2 downstream slits to 0.7 mm x 0.7 mm """ # Command motion and collect status objects ref = mfx_reflaser.remove(wait=False) w8 = mfx_dg1_wave8_motor.move(5.5, wait=False) dg1 = mfx_dg1_slits.move(0.7, wait=False) dg2_us = mfx_dg2_upstream_slits.move(0.7, wait=False) dg2_ms = mfx_dg2_midstream_slits.move(0.7, wait=False) dg2_ds = mfx_dg2_downstream_slits.move(0.7, wait=False) # Combine status and wait for completion if wait: status_wait(ref & w8 & dg1 & dg2_us & dg2_ms & dg2_ds, timeout=timeout)
def test_slit_motion(): slits = fake_slits() # Uneven motion status = slits.move((5.0, 10.0)) assert slits.xwidth.setpoint._write_pv.get() == 5.0 assert slits.ywidth.setpoint._write_pv.get() == 10.0 assert not status.done # Manually complete move slits.xwidth.readback._read_pv.put(5.0) slits.xwidth.done._read_pv.put(1) slits.ywidth.readback._read_pv.put(10.0) slits.ywidth.done._read_pv.put(1) status_wait(status, timeout=5.0) assert status.done and status.success # Reset DMOV flags slits.xwidth.done._read_pv.put(0) slits.ywidth.done._read_pv.put(0) time.sleep(1.0) status = slits.remove(40.0) # Command was registered assert slits.xwidth.setpoint._write_pv.get() == 40.0 assert slits.ywidth.setpoint._write_pv.get() == 40.0 # Status object reports done at correct moment assert not status.done # Manually complete move slits.xwidth.readback._read_pv.put(40.0) slits.xwidth.done._read_pv.put(1) slits.ywidth.readback._read_pv.put(40.0) slits.ywidth.done._read_pv.put(1) status_wait(status, timeout=1) assert status.done and status.success
def _status_print(self, status, msg=None, ret_status=False, print_set=True, timeout=None, wait=True, reraise=False): """ Internal method that optionally returns the status object and optionally prints a message about the set. If a message is passed but print_set is False then the message is logged at the debug level. Parameters ---------- status : StatusObject or list The inputted status object. msg : str or None, optional Message to print if print_set is True. ret_status : bool, optional Return the status object of the set. print_set : bool, optional Print a short statement about the set. wait : bool, optional Wait for the status to complete. reraise : bool, optional Raise the RuntimeError in the except. Returns ------- Status Inputted status object. """ try: # Wait for the status to complete if wait: for s in as_list(status): status_wait(s, timeout) # Notify the user if msg is not None: if print_set: logger.info(msg) else: logger.debug(msg) if ret_status: return status # The operation failed for some reason except RuntimeError: error = "Operation completed, but reported an error." logger.error(error) if reraise: raise
def move(self, width, height=None, *, wait=False, moved_cb=None, timeout=None): """ Set the dimensions of the width/height of the slits gap. Parameters --------- size : float or tuple of float Target size for slits in both x and y axis. If a square gap is desired, a single value can be entered. Otherwise, the width and height can both be entered, either as separate arguments or as a tuple. wait : bool, optional If `True`, block until move is completed. Defaults to `False`. timeout : float, optional Maximum time for the motion. If `None` is given, the default value of `xwidth` and `ywidth` positioners is used. moved_cb : callable, optional Function to be run when the operation finishes. This callback should not expect any arguments or keywords. Returns ------- status : AndStatus Logical combination of the request to both horizontal and vertical motors. """ # Check for missing size if width is None and height is None: raise TypeError("move() missing 1 required positional " "argument: 'width'") elif width is None: width = height # Check for rectangular setpoint elif isinstance(width, tuple): (width, height) = width elif height is None: height = width # Instruct both width and height then combine the output status x_stat = self.xwidth.move(width, wait=False, timeout=timeout) y_stat = self.ywidth.move(height, wait=False, timeout=timeout) status = x_stat & y_stat # Add our callback if one was given if moved_cb is not None: status.add_callback(moved_cb) # Wait if instructed to do so. Stop the motors if interrupted if wait: try: status_wait(status) except KeyboardInterrupt: self.xwidth.stop() self.ywidth.stop() raise return status
def ascan(motor, start, stop, num, events_per_point=360, record=False, controls=None, **kwargs): """ Quick re-implementation of old python for the transition """ daq = snd_devices.daq events = events_per_point status = notepad_scan_status status.clean_fields() if controls is None: controls = {} start_pos = motor.position def get_controls(motor, extra_controls): out_arr = {motor.name: motor} out_arr.update(extra_controls) return out_arr try: scan_controls = get_controls(motor, controls) daq.configure(record=record, controls=scan_controls) status.isscan.put(1) status.nshots.put(events_per_point) status.nsteps.put(num) status.var0.put(motor.name) status.var0_max.put(max((start, stop))) status.var0_min.put(min((start, stop))) for i, step in enumerate(np.linspace(start, stop, num)): logger.info('Beginning step {}'.format(step)) try: mstat = motor.set(step, verify_move=False, **kwargs) except TypeError: mstat = motor.set(step, **kwargs) status.istep.put(i) status_wait(mstat) scan_controls = get_controls(motor, controls) daq.begin(events=events, controls=scan_controls) logger.info('Waiting for {} events ...'.format(events)) daq.wait() finally: logger.info('DONE!') status.clean_fields() daq.end_run() daq.disconnect() try: motor.set(start_pos, verify_move=False, **kwargs) except TypeError: motor.set(start_pos, **kwargs)
def _wait(self, sig, *goals): """ Helper function to wait for a signal to reach a value. This is used here because most commands are only valid when mode is IDLE. """ def cb(value, *args, **kwargs): logger.debug((value, goals)) return value in goals status = SubscriptionStatus(sig, cb) status_wait(status)
def move(self, *args, wait=None, moved_cb=None, timeout=None, **kwargs): st = self.set(*args, **kwargs) import functools from ophyd.status import wait as status_wait if moved_cb is not None: st.add_callback(functools.partial(moved_cb, obj=self)) if wait: status_wait(st) return st
def test_stopper_motion(): stopper = fake_stopper() # Check the status object status = stopper.close(wait=False) stopper.open_limit._read_pv.put(0) stopper.closed_limit._read_pv.put(1) status_wait(status, timeout=1) assert status.done and status.success # Remove stopper.open(wait=False) # Check write PV assert stopper.command.value == stopper.commands.open_valve.value
def focus_at(self, value=None, wait=False, timeout=None, **kwargs): """ Calculate a combination and insert the lenses Parameters ---------- value: float, optional Chosen focal plane. Nominal sample by default wait : bool, optional Wait for the motion of the transfocator to complete timeout: float, optional Timeout for motion kwargs: All passed to :meth:`.find_best_combo` Returns ------- StateStatus Status that represents whether the move is complete """ # Find the best combination of lenses to match the target image plane = value or self.nominal_sample best_combo = self.find_best_combo(target=plane, **kwargs) # Collect status to combine statuses = list() # Only tell one XRT lens to insert prefocused = False for lens in self.xrt_lenses: if lens in best_combo.lenses: statuses.append(lens.insert(timeout=timeout)) prefocused = True break # If we have no XRT lenses one remove will do if not prefocused: statuses.append(self.xrt_lenses[0].remove(timeout=timeout)) # Ensure all Transfocator lenses are correct for lens in self.tfs_lenses: if lens in best_combo.lenses: statuses.append(lens.insert(timeout=timeout)) else: statuses.append(lens.remove(timeout=timeout)) # Conglomerate all status objects status = statuses.pop(0) for st in statuses: status = status & st # Wait if necessary if wait: status_wait(status, timeout=timeout) return status
def wait(self, timeout=None): """ Pause the thread until the DAQ is done aquiring. Parameters ---------- timeout: ``float`` Maximum time to wait in seconds. """ logger.debug('Daq.wait()') if self.state == 'Running': status = self._get_end_status() status_wait(status, timeout=timeout)
def move(self, size, wait=False, moved_cb=None, timeout=None): """ Set the dimensions of the width/height of the gap to width paramater. Parameters --------- size : ``float``, tuple Target size for slits in both x and y axis. Either specify as a tuple for a rectangular aperture (width, height) or set both with single floating point value to use set a square width wait : ``bool`` If true, block until move is completed timeout: ``float``, optional Maximum time for the motion. If None is given, the default value of `xwidth` and `ywidth` positioners is used. moved_cb: ``callable``, optional Function to be run when the operation finishes. This callback should not expect any arguments or keywords Returns ------- status : ``AndStatus`` Logical combination of the request to both horizontal and vertical motors """ # Check for rectangular setpoint if isinstance(size, tuple): (width, height) = size else: width, height = size, size # Instruct both width and height then combine the output status x_stat = self.xwidth.move(width, wait=False, timeout=timeout) y_stat = self.ywidth.move(height, wait=False, timeout=timeout) status = x_stat & y_stat # Add our callback if one was given if moved_cb is not None: status.add_callback(moved_cb) # Wait if instructed to do so. Stop the motors if interrupted if wait: try: status_wait(status) except KeyboardInterrupt: self.xwidth.stop() self.ywidth.stop() raise return status
def wait(self, status=None): """ Waits for the status objects to complete the motions. Parameters ---------- status : list or None, optional List of status objects to wait on. If None, will wait on the internal status list. """ status = status or self._status logger.info("Waiting for the motors to finish moving...") status_wait(status) logger.info("Move completed.")
def remove_dia(self, moved_cb=None, timeout=None, wait=False): """Remove all diagnostic components.""" logger.debug('Removing %s diagnostics', self.name) status = NullStatus() for dia in (self.yag, self.dectris, self.diode, self.foil): status = status & dia.remove(timeout=timeout, wait=False) if moved_cb is not None: status.add_callback(functools.partial(moved_cb, obj=self)) if wait: status_wait(status) return status
def clear(self, wait=False, timeout=None, passive=False, ignore=None): """ Clear the beampath of all obstructions Parameters ---------- wait : bool Wait for all devices to complete their motion timeout : float, optional Duration to wait for device movements ignore: device or iterable, optional Leave devices in their current state without removing them passive : bool, optional If False, devices that are inserted but don't attenuate the beam below :attr:`.minimum_threshold` are ignored Returns ------- statuses : Returns list of status objects returned by :meth:`.LightInterface.remove` """ logger.info('Clearing beampath %s ...', self) # Assemble device list target_devices, ignored = self._ignore(ignore, passive=passive) # Remove devices logger.info('Removing devices along the beampath ...') status = [ device.remove(timeout=timeout) for device in target_devices if find_device_state(device) in ( DeviceState.Inserted, DeviceState.Unknown) and hasattr(device, 'remove') ] # Wait parameters if wait: logger.info( 'Waiting for all devices to be ' 'removed from the beampath %s ...', self) # Wait consecutively for statuses, this can be done by combining # statuses in the future for s in status: logger.debug('Waiting for %s to be done ...', s) status_wait(s, timeout=timeout) logger.info('Completed') return status
def test_misc(daq, sig): """ Blatant coverage-grab """ logger.debug('test_misc') daq.configure(controls=dict(sig=sig)) daq_module.pydaq = None with pytest.raises(ImportError): daq_module.Daq() end_status = daq._get_end_status() status_wait(end_status) daq.begin(duration=10) # Interrupt run with new run and we don't time out daq.begin(events=12) daq.wait()
def fake_move_transition(att, status, goal): """ Set to the PVs sort of like it would happen in the real world and check the status """ # Set status to "MOVING" att.done.sim_put(1) # Set transmission to the goal att.readback.sim_put(goal) # Set status to "OK" att.done.sim_put(0) # Check that the object responded properly status_wait(status, timeout=1) assert status.done assert status.success
def move_3d(self, x, y, z, *, timeout=None, wait=False): """Move to given (x,y,z) coordinate.""" # Move each motor to the corresponding position and collect the status # objects in a list status_list = [ motor.move(val, timeout=timeout, wait=False) for motor, val in zip(self.motors, (x, y, z)) ] # Reduce the list to a single AndStatus status = reduce(lambda s1, s2: s1 & s2, status_list) if wait: status_wait(status) return status
def move(self, position, **kwargs): # Just changes the sample temperature if not tracking vaporizer. if self.parent.track_vaporizer.get() is False: return super().move(position, **kwargs) wait = kwargs.pop('wait', True) sample_status = super().move(position, wait=False, **kwargs) vaporizer_position = self._get_vaporizer_position(position) # Will not wait for vaporizer. _ = self.root.loop1.move(vaporizer_position, wait=False, **kwargs) if wait: status_wait(sample_status) return sample_status
def wait(self, timeout=None): """ Pause the thread until the DAQ is done aquiring. Parameters ---------- timeout: ``float`` Maximum time to wait in seconds. """ logger.debug('Daq.wait()') if self.state == 'Running': if self._events or self._duration: status = self._get_end_status() status_wait(status, timeout=timeout) else: raise RuntimeError('Cannot wait, daq configured to run ' 'forever.')
def test_ims_reinitialize(fake_ims): logger.debug('test_ims_reinitialize') m = fake_ims # Do not reinitialize on auto-setup m.auto_setup() assert m.reinit_command.get() == 0 # Check that we reinitialize m.error_severity.sim_put(3) st = m.reinitialize(wait=False) assert m.reinit_command.get() == 1 # Status should not be complete until reinitialize is done assert not st.done assert not st.success # When error severity is no longer 3 we are reinitialized m.error_severity.sim_put(0) status_wait(st) assert st.done assert st.success