def acquire(streams, settings_obs=None): """ Start an acquisition task for the given streams. It will decide in which order the stream must be acquired. ..Note: It is highly recommended to not have any other acquisition going on. :param streams: [Stream] the streams to acquire :param settings_obs: [SettingsObserver or None] class that contains a list of all VAs that should be saved as metadata :return: (ProgressiveFuture) an object that represents the task, allow to know how much time before it is over and to cancel it. It also permits to receive the result of the task, which is a tuple: (list of model.DataArray): the raw acquisition data (Exception or None): exception raised during the acquisition """ # create a future future = model.ProgressiveFuture() # create a task task = AcquisitionTask(streams, future, settings_obs) future.task_canceller = task.cancel # let the future cancel the task # connect the future to the task and run in a thread executeAsyncTask(future, task.run) # return the interface to manipulate the task return future
def cryoTiltSample(rx, rz=0): """ Provide the ability to switch between imaging and tilted position, withing bumping into anything. Imaging position is considered when rx and rz are equal 0, otherwise it's considered tilting :param rx: (float) rotation movement in x axis :param rz: (float) rotation movement in z axis :return (CancellableFuture -> None): cancellable future of the move to observe the progress, and control raising the ValueError exception """ # Get the stage and align components from the backend components stage = model.getComponent(role='stage') align = model.getComponent(role='align') f = model.CancellableFuture() f.task_canceller = _cancelCryoMoveSample f._task_state = RUNNING f._task_lock = threading.Lock() f._running_subf = model.InstantaneousFuture() # Run in separate thread executeAsyncTask(f, _doCryoTiltSample, args=( f, stage, align, rx, rz, )) return f
def AlignSpot(ccd, stage, escan, focus, type=OBJECTIVE_MOVE, dfbkg=None, rng_f=None): """ Wrapper for DoAlignSpot. It provides the ability to check the progress of spot mode procedure or even cancel it. ccd (model.DigitalCamera): The CCD stage (model.Actuator): The stage escan (model.Emitter): The e-beam scanner focus (model.Actuator): The optical focus type (string): Type of move in order to align dfbkg (model.DataFlow): dataflow of se- or bs- detector for background subtraction rng_f (tuple of floats): range to apply Autofocus on if needed returns (model.ProgressiveFuture): Progress of DoAlignSpot, whose result() will return: returns (float): Final distance to the center (m) """ # Create ProgressiveFuture and update its state to RUNNING est_start = time.time() + 0.1 f = model.ProgressiveFuture(start=est_start, end=est_start + estimateAlignmentTime(ccd.exposureTime.value)) f._task_state = RUNNING # Task to run f.task_canceller = _CancelAlignSpot f._alignment_lock = threading.Lock() f._done = threading.Event() # Create autofocus and centerspot module f._autofocusf = model.InstantaneousFuture() f._centerspotf = model.InstantaneousFuture() # Run in separate thread executeAsyncTask(f, _DoAlignSpot, args=(f, ccd, stage, escan, focus, type, dfbkg, rng_f)) return f
def CenterSpot(ccd, stage, escan, mx_steps, type=OBJECTIVE_MOVE, dfbkg=None): """ Wrapper for _DoCenterSpot. ccd (model.DigitalCamera): The CCD stage (model.Actuator): The stage escan (model.Emitter): The e-beam scanner mx_steps (int): Maximum number of steps to reach the center type (*_MOVE or BEAM_SHIFT): Type of move in order to align dfbkg (model.DataFlow or None): If provided, will be used to start/stop the e-beam emission (it must be the dataflow of se- or bs-detector) in order to do background subtraction. If None, no background subtraction is performed. returns (model.ProgressiveFuture): Progress of _DoCenterSpot, whose result() will return: (float): Final distance to the center #m (2 floats): vector to the spot from the center (m, m) """ # Create ProgressiveFuture and update its state to RUNNING est_start = time.time() + 0.1 f = model.ProgressiveFuture(start=est_start, end=est_start + estimateCenterTime(ccd.exposureTime.value)) f._spot_center_state = RUNNING f.task_canceller = _CancelCenterSpot f._center_lock = threading.Lock() # Run in separate thread executeAsyncTask(f, _DoCenterSpot, args=(f, ccd, stage, escan, mx_steps, type, dfbkg)) return f
def SEMCCDAcquisition(escan, ccd, detector, light): f = model.ProgressiveFuture() f._acq_state = RUNNING # Task to run doAcquisition = _DoAcquisition f.task_canceller = _CancelAcquisition # Run in separate thread executeAsyncTask(f, doAcquisition, args=(f, escan, ccd, detector, light)) return f
def wrapSimpleStreamIntoFuture(stream): """ Starts one stream acquisition and return a Future Works with streams having only .is_active and .image . returns (Future that returns list of DataArray): the acquisition task """ # Create a Future, not started yet future = SimpleStreamFuture(stream) # run in a separate thread executeAsyncTask(future, future._run) return future
def do_progressive_long(self, duration=5): """ return a ProgressiveFuture, which will have the estimated time shorten """ # First estimate the time to 10s, and then it will be shorten start = time.time() + 1 end = start + 10 f = model.ProgressiveFuture(start, end) # run in a separate thread executeAsyncTask(f, self._long_pessimistic_task, args=(f, duration)) return f
def acquire(self): """ Runs the acquisition returns Future that will have as a result a DataArray with the 3D data """ # Make sure the stream is prepared (= optical path set) # TODO: move the optical path change done in the plugin.acquire() to here # self.prepare().result() # Hard coded optical path (as the OPM doesn't know about this special mode) logging.info("Preparing optical path") # Configure the optical path for the CCD we need mvs = self._opm.selectorsToPath(self._detector.name) # On Odemis 2.9-, mvs is just a list of futures # On Odemis 2.10+, mvs is a list of tuples(future, comp, pos) => only keep the futures fs = [m[0] if isinstance(m, tuple) else m for m in mvs] # move lens 2 into position for p, n in self._lsw.axes["x"].choices.items(): if n == "on": f = self._lsw.moveAbs({"x": p}) fs.append(f) break # move big slit into position for p, n in self._bigslit.axes["x"].choices.items(): if n == "off": f = self._bigslit.moveAbs({"x": p}) fs.append(f) break # wait for all the moves to be over for f in fs: f.result() logging.debug("Optical path configured") est_start = time.time() + 0.1 # Create a "Future", which is an object that can be used to follow the # task completion while it's going on, and get the result. f = model.ProgressiveFuture(start=est_start, end=est_start + self.estimateAcquisitionTime()) f.task_canceller = self._cancelAcquisition f._acq_state = RUNNING f._acq_lock = threading.Lock() f._acq_done = threading.Event() # run task in separate thread executeAsyncTask(f, self._runAcquisition, args=(f, )) return f
def cryoSwitchSamplePosition(target): """ Provide the ability to switch between loading, imaging and coating position, without bumping into anything. :param target: (int) target position either one of the constants LOADING, IMAGING, COATING AND ALIGNMENT :return (CancellableFuture -> None): cancellable future of the move to observe the progress, and control raising the ValueError exception """ f = model.CancellableFuture() f.task_canceller = _cancelCryoMoveSample f._task_state = RUNNING f._task_lock = threading.Lock() f._running_subf = model.InstantaneousFuture() # Run in separate thread executeAsyncTask(f, _doCryoSwitchSamplePosition, args=(f, target)) return f
def FindOverlay(repetitions, dwell_time, max_allowed_diff, escan, ccd, detector, skew=False, bgsub=False): """ Wrapper for DoFindOverlay. It provides the ability to check the progress of overlay procedure or even cancel it. repetitions (tuple of ints): The number of CL spots are used dwell_time (float): Time to scan each spot #s max_allowed_diff (float): Maximum allowed difference in electron coordinates #m escan (model.Emitter): The e-beam scanner ccd (model.DigitalCamera): The CCD detector (model.Detector): The electron detector skew (boolean): If True, also compute skew bgsub (boolean): If True, apply background substraction in grid scanning returns (model.ProgressiveFuture): Progress of DoFindOverlay, whose result() will return: tuple: Transformation parameters translation (Tuple of 2 floats) scaling (Float) rotation (Float) dict : Transformation metadata """ # Create ProgressiveFuture and update its state to RUNNING est_start = time.time() + 0.1 f = model.ProgressiveFuture(start=est_start, end=est_start + estimateOverlayTime(dwell_time, repetitions)) f._find_overlay_state = RUNNING # Task to run f.task_canceller = _CancelFindOverlay f._overlay_lock = threading.Lock() f._done = threading.Event() # Create scanner for scan grid f._gscanner = GridScanner(repetitions, dwell_time, escan, ccd, detector, bgsub) # Run in separate thread executeAsyncTask(f, _DoFindOverlay, args=(f, repetitions, dwell_time, max_allowed_diff, escan, ccd, detector, skew)) return f
def turnOnLight(bl, ccd): """ Turn on a light and wait until it detects a signal change on the ccd bl (Light): the light, which should initially be turned off. ccd (DigitalCamera): detector to use to check for the signal returns (CancellableFuture): a future that will finish when a significant signal increase is detected. Can also be used to stop the procedure. """ f = CancellableFuture() f.task_canceller = _cancelTurnOnLight f._task_state = RUNNING f._task_lock = threading.Lock() f._was_stopped = False # if cancel was successful # Run in separate thread executeAsyncTask(f, _doTurnOnLight, args=(f, bl, ccd)) return f
def acquire(self): """ Runs the acquisition returns Future that will have as a result a DataArray with the 3D data """ # Make sure the stream is prepared (= optical path set) # TODO: move the optical path change done in the plugin.acquire() to here # self.prepare().result() # Hard coded optical path (as the OPM doesn't know about this special mode) logging.info("Preparing optical path") # Configure the optical path for the CCD we need fs = self._opm.selectorsToPath(self._detector.name) # move lens 2 into position for p, n in self._lsw.axes["x"].choices.items(): if n == "on": f = self._lsw.moveAbs({"x": p}) fs.append(f) break # move big slit into position for p, n in self._bigslit.axes["x"].choices.items(): if n == "off": f = self._bigslit.moveAbs({"x": p}) fs.append(f) break # wait for all the moves to be over for f in fs: f.result() logging.debug("Optical path configured") est_start = time.time() + 0.1 # Create a "Future", which is an object that can be used to follow the # task completion while it's going on, and get the result. f = model.ProgressiveFuture(start=est_start, end=est_start + self.estimateAcquisitionTime()) f.task_canceller = self._cancelAcquisition f._acq_state = RUNNING f._acq_lock = threading.Lock() f._acq_done = threading.Event() # run task in separate thread executeAsyncTask(f, self._runAcquisition, args=(f,)) return f
def cryoSwitchAlignPosition(target): """ Provide the ability to switch between loading, imaging and alignment position, without bumping into anything. :param target: (int) target position either one of the constants LOADING, IMAGING or ALIGNMENT :return (CancellableFuture -> None): cancellable future of the move to observe the progress, and control the raise ValueError exception """ # Get the aligner from backend components align = model.getComponent(role='align') f = model.CancellableFuture() f.task_canceller = _cancelCryoMoveSample f._task_state = RUNNING f._task_lock = threading.Lock() f._running_subf = model.InstantaneousFuture() # Run in separate thread executeAsyncTask(f, _doCryoSwitchAlignPosition, args=(f, align, target)) return f
def test_execute(self): f = CancellableFuture() t = executeAsyncTask(f, self.long_task, args=(42,), kwargs={"d": 3}) self.assertFalse(f.done()) time.sleep(0.1) self.assertTrue(f.running()) r = f.result() self.assertEqual(r, -1) self.assertEqual(self._v, 42) self.assertEqual(self._d, 3)
def cryoLoadSample(target): """ Provide the ability to switch between loading position and imaging position, without bumping into anything. :param target: (int) target position either one of the constants LOADING or IMAGING :return (CancellableFuture -> None): cancellable future of the move to observe the progress, and control the raise ValueError exception """ # Get the stage and focus components from the backend components stage = model.getComponent(role='stage') focus = model.getComponent(role='focus') f = model.CancellableFuture() f.task_canceller = _cancelCryoMoveSample f._task_state = RUNNING f._task_lock = threading.Lock() f._running_subf = model.InstantaneousFuture() # Run in separate thread executeAsyncTask(f, _doCryoLoadSample, args=(f, stage, focus, target)) return f
def test_execute(self): f = CancellableFuture() t = executeAsyncTask(f, self.long_task, args=(42, ), kwargs={"d": 3}) self.assertFalse(f.done()) time.sleep(0.1) self.assertTrue(f.running()) r = f.result() self.assertEqual(r, -1) self.assertEqual(self._v, 42) self.assertEqual(self._d, 3)
def acquire(self): """ Runs the acquisition returns Future that will have as a result a DataArray with the spectrum """ # Make sure every stream is prepared, not really necessary to check _prepared f = self.prepare() f.result() est_start = time.time() + 0.1 f = model.ProgressiveFuture(start=est_start, end=est_start + self.estimateAcquisitionTime()) f.task_canceller = self._cancelAcquisition f._acq_state = RUNNING f._acq_lock = threading.Lock() f._acq_done = threading.Event() # run task in separate thread executeAsyncTask(f, self._runAcquisition, args=(f,)) return f
def AutoFocus(detector, emt, focus, dfbkg=None, good_focus=None, rng_focus=None, method=MTD_BINARY): """ Wrapper for DoAutoFocus. It provides the ability to check the progress of autofocus procedure or even cancel it. detector (model.DigitalCamera or model.Detector): Detector on which to improve the focus quality emt (None or model.Emitter): In case of a SED this is the scanner used focus (model.Actuator): The focus actuator dfbkg (model.DataFlow or None): If provided, will be used to start/stop the e-beam emission (it must be the dataflow of se- or bs-detector) in order to do background subtraction. If None, no background subtraction is performed. good_focus (float): if provided, an already known good focus position to be taken into consideration while autofocusing rng_focus (tuple): if provided, the search of the best focus position is limited within this range method (MTD_*): focusing method, if BINARY we follow a dichotomic method while in case of EXHAUSTIVE we iterate through the whole provided range returns (model.ProgressiveFuture): Progress of DoAutoFocus, whose result() will return: Focus position (m) Focus level """ # Create ProgressiveFuture and update its state to RUNNING est_start = time.time() + 0.1 f = model.ProgressiveFuture(start=est_start, end=est_start + estimateAutoFocusTime(detector, emt)) f._autofocus_state = RUNNING f._autofocus_lock = threading.Lock() f.task_canceller = _CancelAutoFocus # Run in separate thread if method == MTD_EXHAUSTIVE: autofocus_fn = _DoExhaustiveFocus elif method == MTD_BINARY: autofocus_fn = _DoBinaryFocus else: raise ValueError("Unknown autofocus method") executeAsyncTask(f, autofocus_fn, args=(f, detector, emt, focus, dfbkg, good_focus, rng_focus)) return f
def acquire(self): """ Runs the acquisition returns Future that will have as a result a DataArray with the spectrum """ # Make sure every stream is prepared, not really necessary to check _prepared f = self.prepare() f.result() est_start = time.time() + 0.1 f = model.ProgressiveFuture(start=est_start, end=est_start + self.estimateAcquisitionTime()) f.task_canceller = self._cancelAcquisition f._acq_state = RUNNING f._acq_lock = threading.Lock() f._acq_done = threading.Event() # run task in separate thread executeAsyncTask(f, self._runAcquisition, args=(f, )) return f
def AutoFocusSpectrometer(spectrograph, focuser, detectors, selector=None, streams=None): """ Run autofocus for a spectrograph. It will actually run autofocus on each gratings, and for each detectors. The input slit should already be in a good position (typically, almost closed), and a light source should be active. Note: it's currently tailored to the Andor Shamrock SR-193i. It's recommended to put the detector on the "direct" output as first detector. spectrograph (Actuator): should have grating and wavelength. focuser (Actuator): should have a z axis detectors (Detector or list of Detectors): all the detectors available on the spectrometer. selector (Actuator or None): must have a rx axis with each position corresponding to one of the detectors. If there is only one detector, selector can be None. return (ProgressiveFuture -> dict((grating, detector)->focus position)): a progressive future which will eventually return a map of grating/detector -> focus position. """ if not isinstance(detectors, collections.Iterable): detectors = [detectors] if not detectors: raise ValueError("At least one detector must be provided") if len(detectors) > 1 and selector is None: raise ValueError("No selector provided, but multiple detectors") if streams is None: streams=[] # Create ProgressiveFuture and update its state to RUNNING est_start = time.time() + 0.1 #calculate the time for the AutoFocusSpectrometer procedure to be completed a_time = _totalAutoFocusTime(spectrograph, detectors) f = model.ProgressiveFuture(start=est_start, end=est_start + a_time) f.task_canceller = _CancelAutoFocusSpectrometer # Extra info for the canceller f._autofocus_state = RUNNING f._autofocus_lock = threading.Lock() f._subfuture = InstantaneousFuture() # Run in separate thread executeAsyncTask(f, _DoAutoFocusSpectrometer, args=(f, spectrograph, focuser, detectors, selector, streams)) return f
def acquire(self): """ Runs the acquisition returns Future that will have as a result a DataArray with the 3D data """ # Make sure the stream is prepared (= optical path set) # TODO: on the SPARCv1, which doesn't support time-correlator mode, # this fails => need to use a different (and supported) mode. self.prepare().result() est_start = time.time() + 0.1 # Create a "Future", which is an object that can be used to follow the # task completion while it's going on, and get the result. f = model.ProgressiveFuture(start=est_start, end=est_start + self.estimateAcquisitionTime()) f.task_canceller = self._cancelAcquisition f._acq_state = RUNNING f._acq_lock = threading.Lock() f._acq_done = threading.Event() # run task in separate thread executeAsyncTask(f, self._runAcquisition, args=(f,)) return f
def acquireZStack(streams, zlevels, settings_obs=None): """ The acquisition manager of a zstack streams (list of Stream): the streams to be acquired zlevels (dict Stream -> list of floats): a dictionary containing the streams and the corresponding lists of actuator z positions settings_obs (SettingsObserver): VAs to be integrated in the acquired data as metadata return (ProgressiveFuture): the future that will be executing the task """ # create future future = model.ProgressiveFuture() # create acquisition task acqui_task = ZStackAcquisitionTask(future, streams, zlevels, settings_obs) # add the ability of cancelling the future during execution future.task_canceller = acqui_task.cancel # set the progress of the future total_duration = acqui_task.estimate_total_duration() future.set_end_time(time.time() + total_duration) # assign the acquisition task to the future executeAsyncTask(future, acqui_task.run) return future
def Sparc2AutoFocus(align_mode, opm, streams=None, start_autofocus=True): """ It provides the ability to check the progress of the complete Sparc2 autofocus procedure in a Future or even cancel it. Pick the hardware components Turn on the light and wait for it to be complete Change the optical path (closing the slit) Run AutoFocusSpectrometer Acquire one last image Turn off the light align_mode (str): OPM mode, spec-focus or spec-fiber-focus, streak-focus opm: OpticalPathManager streams: list of streams return (ProgressiveFuture -> dict((grating, detector)->focus position)): a progressive future which will eventually return a map of grating/detector -> focus position, the same as AutoFocusSpectrometer raises: CancelledError if cancelled LookupError if procedure failed """ focuser = None if align_mode in ("spec-focus", "streak-focus"): focuser = model.getComponent(role='focus') elif align_mode == "spec-fiber-focus": # The "right" focuser is the one which affects the same detectors as the fiber-aligner aligner = model.getComponent(role='fiber-aligner') aligner_affected = aligner.affects.value # List of component names for f in ("spec-ded-focus", "focus"): try: focus = model.getComponent(role=f) except LookupError: logging.debug("No focus component %s found", f) continue focuser_affected = focus.affects.value # Does the focus affects _at least_ one component also affected by the fiber-aligner? if set(focuser_affected) & set(aligner_affected): focuser = focus break else: raise ValueError("Unknown align_mode %s", align_mode) if focuser is None: raise LookupError("Failed to find the focuser for align mode %s", align_mode) if streams is None: streams = [] for s in streams: if s.focuser is None: logging.debug("Stream %s has no focuser, will assume it's fine", s) elif s.focuser != focuser: logging.warning("Stream %s has focuser %s, while expected %s", s, s.focuser, focuser) # Get all the detectors, spectrograph and selectors affected by the focuser try: spgr, dets, selector = _getSpectrometerFocusingComponents(focuser) # type: (object, List[Any], Optional[Any]) except LookupError as ex: # TODO: just run the standard autofocus procedure instead? raise LookupError("Failed to focus in mode %s: %s" % (align_mode, ex)) for s in streams: if s.detector.role not in (d.role for d in dets): logging.warning("The detector of the stream is not found to be one of the picked detectors %s") # Create ProgressiveFuture and update its state to RUNNING est_start = time.time() + 0.1 # Rough approximation of the times of each action: # * 5 s to turn on the light # * 5 s to close the slit # * af_time s for the AutoFocusSpectrometer procedure to be completed # * 0.2 s to acquire one last image # * 0.1 s to turn off the light if start_autofocus: # calculate the time needed for the AutoFocusSpectrometer procedure to be completed af_time = _totalAutoFocusTime(spgr, dets) autofocus_loading_times = (5, 5, af_time, 0.2, 5) # a list with the time that each action needs else: autofocus_loading_times = (5, 5) f = model.ProgressiveFuture(start=est_start, end=est_start + sum(autofocus_loading_times)) f._autofocus_state = RUNNING # Time for each action left f._actions_time = list(autofocus_loading_times) f.task_canceller = _CancelSparc2AutoFocus f._autofocus_lock = threading.Lock() f._running_subf = model.InstantaneousFuture() # Run in separate thread executeAsyncTask(f, _DoSparc2AutoFocus, args=(f, streams, align_mode, opm, dets, spgr, selector, focuser, start_autofocus)) return f
def _prepare_opm(self): # Return a future which calls the OPM _and_ updates the "special" axes f = futures.Future() executeAsyncTask(f, self._set_optical_path) return f