示例#1
0
    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 trigger(self):
     "Trigger one acquisition."
     if not self._staged:
         raise RuntimeError("This detector is not ready to trigger."
                            "Call the stage() method before triggering.")
     self._save_started = False
     self._status = DeviceStatus(self)
     self._desired_number_of_sets = self.number_of_sets.get()
     self._plugin.num_capture.put(self._desired_number_of_sets)
     self.dispatch(self._image_name, ttime.time())
     # reset the proc buffer, this needs to be generalized
     self.proc.reset_filter.put(1)
     self._plugin.capture.put(1)  # Now the TIFF plugin is capturing.
     return self._status
示例#3
0
    def kickoff(self):
        '''Start collection

        Returns
        -------
        DeviceStatus
            This will be set to done when acquisition has begun
        '''
        # Put scaler in fly mode
        # self.scaler_mode.put(1, wait=True)

        # Set monochromator speed
        self.fly_motor_speed.put(self._speed, wait=True)

        # Set encoder step size for accumulating counts
        self.encoder_steps.put(self._encoder_step_counts, wait=True)

        # Reset scaler array waveform
        self.scaler_reset.put(1, wait=True)

        # Reset scaler counter
        self.scaler_preset.put(0, wait=True)

        # Wait For HC10E scaler reset
        ttime.sleep(.2)

        # Indicators
        self._start_time = ttime.time()
        self._acquiring = True
        self._paused = False

        # Put scaler in fly mode
        self.scaler_mode.put(1, wait=True)

        # Start motor motion to target_position asynchronously
        _th = self.energyToTheta(self._target_energy)
        self.fly_motor.put(_th, wait=False)
        ttime.sleep(0.2)

        thread = threading.Thread(target=self.checkMotor, daemon=True)
        thread.start()

        # make status object, Indicate flying has started
        self.kickoff_status = DeviceStatus(self)
        self.kickoff_status._finished(success=True)

        self.complete_status = DeviceStatus(self)

        return self.kickoff_status
示例#4
0
    def move(self, position, wait=True, timeout=None, moved_cb=None):
        """
        Move to a specified position, optionally waiting for motion to
        complete.

        Checks for the motor step, and ask the user for confirmation if
        movement step is greater than default one.
        """
        try:
            return super().move(position, wait=wait, timeout=timeout,
                                moved_cb=moved_cb)
        except KappaMoveAbort as exc:
            logger.warning('Aborting moving for safety.')
            status = DeviceStatus(self)
            status.set_exception(exc)
            return status
示例#5
0
    def trigger(self):
        """
        Trigger the EventSequencer

        This method reconfigures the EventSequencer to take a new reading. This
        means:

            * Stopping the EventSequencer if it is already running
            * Restarting the EventSequencer

        The returned status object will indicate different behavior based on
        the configuration of the EventSequencer itself. If set to "Run
        Forever", the status object merely indicates that we have succesfully
        started our sequence. Otherwise, the status object will be completed
        when the sequence we have set it to play is complete.
        """
        # Stop the Sequencer if it is already running
        self.stop()
        # Fire the EventSequencer
        self.start()
        # If we are running forever, count this is as triggered
        if self.play_mode.get() == 2:
            logger.debug("EventSequencer is set to run forever, "
                         "trigger is complete")
            return DeviceStatus(self, done=True, success=True)

        # Create our status
        def done(*args, value=None, old_value=None, **kwargs):
            return value == 2 and old_value == 0

        # Create our status object
        return SubscriptionStatus(self.play_status, done, run=True)
示例#6
0
 def move(self, new_position, moved_cb=None, **kwargs):
     print(np.abs(self.position - new_position), .2, self.position,
           new_position)
     if np.abs(self.position - new_position) < .2:
         if moved_cb is not None:
             moved_cb(obj=self)
         return DeviceStatus(self, done=True, success=True)
     return super().move(new_position, moved_cb=moved_cb, **kwargs)
示例#7
0
 def trigger_internal(self):
     if self._staged != Staged.yes:
         raise RuntimeError("not staged")
     self._status = DeviceStatus(self)
     self.settings.erase.put(1)
     self._acquisition_signal.put(1, wait=False)
     self._dispatch_channels(trigger_time=time.time())
     return self._status
示例#8
0
def test_status_with_name(hw):
    from ophyd.status import DeviceStatus
    st = DeviceStatus(device=hw.det)
    pbar = ProgressBar([st])
    st._finished()

    st = DeviceStatus(device=hw.det)
    pbar = ProgressBar([st])
    assert pbar.delay_draw == 0.2
    time.sleep(0.25)
    st._finished()
示例#9
0
 def insert(self, timeout=None, finished_cb=None):
     """
     Insert the device into the beampath
     """
     # Complete request
     self.status = Status.inserted
     # Run subscriptions to device state
     self._run_subs(obj=self, sub_type=self._default_sub)
     # Return complete status object
     return DeviceStatus(self, done=True, success=True)
示例#10
0
 def remove(self, wait=False, timeout=None, finished_cb=None):
     """
     Remove the device from the beampath
     """
     # Complete request
     self.status = Status.removed
     # Run subscriptions to device state
     self._run_subs(obj=self, sub_type=self._default_sub)
     # Return complete status object
     return DeviceStatus(self, done=True, success=True)
示例#11
0
    def move(self, position, wait=True, timeout=None, moved_cb=None):
        """
        Move to a specified position, optionally waiting for motion to
        complete.

        Checks for the motor step, and ask the user for confirmation if
        movement step is greater than default one.
        """
        if self.check_motor_step(position.e_eta, position.e_chi,
                                 position.e_phi):
            return super().move(position,
                                wait=wait,
                                timeout=timeout,
                                moved_cb=moved_cb)
        else:
            logger.warning('Aborting moving for safety.')
            status = DeviceStatus(self)
            status.set_exception(ValueError('Unsafe Kappa move aborted!'))
            return status
示例#12
0
    def set(self, val):
        if self._set_st is not None:
            raise RuntimeError('trying to set while a set is in progress')

        cmd_map = {
            self.open_str: self.open_cmd,
            self.close_str: self.close_cmd
        }
        target_map = {
            self.open_str: self.open_val,
            self.close_str: self.close_val
        }

        cmd_sig = cmd_map[val]
        target_val = target_map[val]

        st = self._set_st = DeviceStatus(self)
        enums = self.status.enum_strs

        def shutter_cb(value, timestamp, **kwargs):
            value = enums[int(value)]
            if value == target_val:
                self._set_st._finished()
                self._set_st = None
                self.status.clear_sub(shutter_cb)

        cmd_enums = cmd_sig.enum_strs
        count = 0

        def cmd_retry_cb(value, timestamp, **kwargs):
            nonlocal count
            value = cmd_enums[int(value)]
            # ts = datetime.datetime.fromtimestamp(timestamp).strftime(_time_fmtstr)
            # print('sh', ts, val, st)
            count += 1
            if count > 5:
                cmd_sig.clear_sub(cmd_retry_cb)
                st._finished(success=False)
            if value == 'None':
                if not st.done:
                    time.sleep(.5)
                    cmd_sig.set(1)
                    ts = datetime.datetime.fromtimestamp(timestamp).strftime(
                        _time_fmtstr)
                    print(
                        '** ({}) Had to reactuate shutter while {}ing'.format(
                            ts, val))
                else:
                    cmd_sig.clear_sub(cmd_retry_cb)

        cmd_sig.subscribe(cmd_retry_cb, run=False)
        cmd_sig.set(1)
        self.status.subscribe(shutter_cb)

        return st
示例#13
0
    def trigger_external(self):
        if self._staged != Staged.yes:
            raise RuntimeError("not staged")

        self._status = DeviceStatus(self)
        self._status._finished()
        if self.mode_settings.scan_type.get() != 'fly':
            self._dispatch_channels(trigger_time=time.time())
            # fly-scans take care of dispatching on their own

        return self._status
示例#14
0
    def new_acquire_status(self):
        """
        Create, remember, and return a Status object that will be marked
        as `finished` when acquisition is done (see _acquire_changed). The
        intention is that this Status will be used by another object,
        for example a RunEngine.

        Returns
        -------
        DeviceStatus
        """

        self._acquire_status = DeviceStatus(self)
        return self._acquire_status
示例#15
0
    def trigger(self):
        if self._staged != Staged.yes:
            raise RuntimeError("not staged")

        self._status = DeviceStatus(self)
        self._acquisition_signal.put(1, wait=False)
        trigger_time = ttime.time()

        for sn in self.read_attrs:
            if sn.startswith('channel') and '.' not in sn:
                ch = getattr(self, sn)
                self.dispatch(ch.name, trigger_time)

        self._abs_trigger_count += 1
        return self._status
示例#16
0
def test_status_with_name(hw):
    from ophyd.status import DeviceStatus
    st = DeviceStatus(device=hw.det)
    pbar = ProgressBar([st])
    st._finished()

    st = DeviceStatus(device=hw.det)
    pbar = ProgressBar([st])
    assert pbar.delay_draw == 0.2
    time.sleep(0.25)
    st._finished()
示例#17
0
    def set(self, value, **kwargs):
        """interface to use bps.mv()"""
        if value != 1:
            return

        working_status = DeviceStatus(self)
        started = False

        def execute_scan_cb(value, timestamp, **kwargs):
            value = int(value)
            if started and value == 0:
                working_status._finished()

        self.execute_scan.subscribe(execute_scan_cb)
        self.execute_scan.set(1)
        started = True
        return working_status
示例#18
0
    def trigger(self):
        if self._staged != Staged.yes:
            raise RuntimeError("not staged")

        self._status = DeviceStatus(self)
        self.settings.erase.put(1)
        self._acquisition_signal.put(1, wait=False)
        trigger_time = ttime.time()
        if self._mode is TESMode.step:
            for sn in self.read_attrs:
                if sn.startswith("channel") and "." not in sn:
                    ch = getattr(self, sn)
                    self.dispatch(ch.name, trigger_time)
        elif self._mode is TESMode.fly:
            self.dispatch("fluor", trigger_time)
        else:
            raise Exception(f"unexpected mode {self._mode}")
        self._abs_trigger_count += 1
        return self._status
示例#19
0
    def complete(self):
        """
        Complete the EventSequencer's current sequence.

        The result of this method varies on the mode that the EventSequencer is
        configured. If the EventSequencer is either set to 'Run Once' or 'Run N
        Times' this method allows the current sequence to complete and returns
        a status object that indicates a successful completion. However, this
        mode of operation does not make sense if the EventSequencer is in
        'Run Forever' mode. In this case, the EventSequencer is stopped
        immediately and a completed status object is returned.

        Returns
        -------
        status : DeviceStatus or SubscriptionStatus
            Status indicating completion of sequence.

        Notes
        -----
        If you want to stop the sequence from running regardless of
        configuration use the :meth:`.stop` command.
        """

        # Clear the monitor subscriptions
        super().complete()
        # If we are running forever we can stop whenever
        if self.play_mode.get() == 2:
            logger.debug("EventSequencer is set to run forever, "
                         "stopping immediately")
            self.stop()
            return DeviceStatus(self, done=True, success=True)

        # Otherwise we should wait for the sequencer to end
        def done(*args, value=None, old_value=None, timestamp=0, **kwargs):
            return all((value == 0, old_value == 2,
                        timestamp > self.play_control.timestamp))

        # Create a SubscriptionStatus
        logger.debug("EventSequencer has a determined stopping point, "
                     " waiting for sequence to complete")
        st = SubscriptionStatus(self.play_status, done, run=True)
        return st
示例#20
0
    def trigger_hide(self):
        if self._staged != Staged.yes:
            raise RuntimeError("not staged")

        import epics
        #t = '{:%H:%M:%S.%f}'.format(datetime.datetime.now())
        #print('tr1 {} '.format(t))
        self._status = DeviceStatus(self)
        self.cam.acquire.put(1, wait=False)
        trigger_time = ttime.time()
        #t = '{:%H:%M:%S.%f}'.format(datetime.datetime.now())
        #print('tr2 {} '.format(t))

        for sn in self.read_attrs:
            if sn.startswith('channel') and '.' not in sn:
                ch = getattr(self, sn)
                self.dispatch(ch.name, trigger_time)
        #t = '{:%H:%M:%S.%f}'.format(datetime.datetime.now())
        #print('tr3 {} '.format(t))

        self._abs_trigger_count += 1
        return self._status
示例#21
0
    def set(self, value):
        #self.done_signal.value=1
        st = DeviceStatus(self)
        # these arg sames matter
        #def am_done(old_value, value, **kwargs):
        def am_done(old_value,value, **kwargs):
            #if old_value == 1 and value == 0:
            #print("running",old_value,value,self.done_signal.value)
            #print("done_signal: ",smtr.done_signal.value)
            """
            if self.done_signal.value==1:
                if self.last_value==0:
                    st._finished()
                self.last_value=1
            else:
                self.last_value=0
            """
            if old_value==0 and value==1:
                st._finished()

        self.done_signal.subscribe(am_done, run=False)
        self.user_setpoint.set(value)
        return st
示例#22
0
 def set(self, new_position, **kwargs):
     if np.abs(self.position - new_position) < .2:
         return DeviceStatus(self, done=True, success=True)
     return super().set(new_position, **kwargs)
示例#23
0
 def test_others(self):
     DeviceStatus(None)
示例#24
0
 def trigger(self):
     status = DeviceStatus(self)
     self._thread = threading.Thread(target=self.take_image, args=[status])
     self._thread.start()
     return status
示例#25
0
class HxnXspressTrigger(HxnModalBase, BlueskyInterface):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._status = None
        self._acquisition_signal = self.settings.acquire
        self._abs_trigger_count = 0

    def unstage(self):
        ret = super().unstage()
        try:
            self._acquisition_signal.clear_sub(self._acquire_changed)
        except KeyError:
            pass

        return ret

    def _acquire_changed(self, value=None, old_value=None, **kwargs):
        "This is called when the 'acquire' signal changes."
        if self._status is None:
            return
        if (old_value == 1) and (value == 0):
            # Negative-going edge means an acquisition just finished.
            self._status._finished()

    def mode_internal(self):
        self._abs_trigger_count = 0
        self.stage_sigs[self.external_trig] = False
        self.stage_sigs[self.settings.acquire_time] = self.count_time.get()
        self._acquisition_signal.subscribe(self._acquire_changed)

    def mode_external(self):
        ms = self.mode_settings
        # NOTE: these are used in Xspress3Filestore.stage
        self.stage_sigs[self.external_trig] = True
        self.stage_sigs[self.total_points] = ms.total_points.get()
        self.stage_sigs[self.spectra_per_point] = 1
        self.stage_sigs[self.settings.acquire_time] = 0.001

        # NOTE: these are taken care of in Xspress3Filestore
        # self.stage_sigs[self.settings.trigger_mode] = 'TTL Veto Only'
        # self.stage_sigs[self.settings.num_images] = total_capture

    def _dispatch_channels(self, trigger_time):
        self._abs_trigger_count += 1
        channels = self._channels.values()
        for sn in self.read_attrs:
            ch = getattr(self, sn)
            if ch in channels:
                self.dispatch(ch.name, trigger_time)

    def trigger_internal(self):
        if self._staged != Staged.yes:
            raise RuntimeError("not staged")
        self._status = DeviceStatus(self)
        self.settings.erase.put(1)
        self._acquisition_signal.put(1, wait=False)
        self._dispatch_channels(trigger_time=time.time())
        return self._status

    def trigger_external(self):
        if self._staged != Staged.yes:
            raise RuntimeError("not staged")

        self._status = DeviceStatus(self)
        self._status._finished()
        if self.mode_settings.scan_type.get() != 'fly':
            self._dispatch_channels(trigger_time=time.time())
            # fly-scans take care of dispatching on their own

        return self._status

    def stage(self):
        staged = super().stage()
        mode = self.mode_settings.mode.get()
        if mode == 'external':
            # In external triggering mode, the devices is only triggered once
            # at stage
            self.settings.erase.put(1, wait=True)
            self._acquisition_signal.put(1, wait=False)
        return staged
示例#26
0
class DCMFlyer(Device):
    """DCM flyer with HC10E counter board

    :PV fly_motor : monochromator theta1
    :PV fly_motor_speed : monochromator speed [deg/sec]
    :PV fly_motor_stop : Stop monochromator theta
    :PV fly_motor_done_move : Done moving when value is '1', '0' : moving
    :PV scaler_mode : change mode. '0' : normal mode, '1' : fly(trigger) mode
    :PV encoder_steps : Flyer will accumulate counts during specified encoder steps
    :PV scaler_preset : set to '0' will result in measured values to reset
    :PV scaler_reset : reset array waveform

    :PV enc_waveform : encoder waveform
    :PV I0_waveform : I0 waveform
    :PV It_waveform : It waveform
    :PV If_waveform : If waveform
    :PV Ir_waveform : Ir waveform

    """
    fly_motor = Cpt(EpicsSignal, pv_names['DCM']["mono_theta"])
    fly_motor_speed = Cpt(EpicsSignal, pv_names['DCM']["mono_theta_speed"])
    fly_motor_stop = Cpt(EpicsSignal, pv_names['DCM']["mono_theta_stop"])
    fly_motor_done_move = Cpt(EpicsSignalRO,
                              pv_names['DCM']["mono_theta_dmov"])
    fly_motor_eres = Cpt(EpicsSignalRO, pv_names['DCM']["mono_enc_resolution"])

    scaler_mode = Cpt(EpicsSignal, pv_names['Scaler']["HC10E_Mode"])
    encoder_steps = Cpt(EpicsSignal, pv_names['Scaler']["HC10E_TrigStep"])
    scaler_preset = Cpt(EpicsSignal, pv_names['Scaler']["HC10E_Preset"])
    scaler_reset = Cpt(EpicsSignal, pv_names['Scaler']["HC10E_Reset"])

    enc_waveform = Cpt(EpicsSignalRO, pv_names['Scaler']["HC10E_ENC_WF"])
    I0_waveform = Cpt(EpicsSignalRO, pv_names['Scaler']["HC10E_I0_WF"])
    It_waveform = Cpt(EpicsSignalRO, pv_names['Scaler']["HC10E_It_WF"])
    If_waveform = Cpt(EpicsSignalRO, pv_names['Scaler']["HC10E_If_WF"])
    Ir_waveform = Cpt(EpicsSignalRO, pv_names['Scaler']["HC10E_Ir_WF"])

    def __init__(self,
                 *args,
                 target_energy=None,
                 speed=None,
                 encoder_steps=None,
                 stream_names=None,
                 **kwargs):
        super().__init__(*args, **kwargs)

        self.complete_status = None
        self._acquiring = False
        self._paused = False

        self.stream_names = stream_names
        self._collected_data = None
        self._start_energy = 8333

        # Flyer motor target position
        if target_energy is None:
            self._target_energy = 9300
        else:
            self._target_energy = target_energy

        # Flyer motor speed
        self._speed = speed

        # Encoder step size for accumulating counts
        self._encoder_step_counts = encoder_steps

        # Number of scan points to read
        self._num_of_counts = int(100)

    def energyToTheta(self, energy):
        _th = np.rad2deg(np.arcsin(_hc / (2. * _si_111 * energy)))
        return _th

    def pause(self):
        # Stop motor motion
        self.fly_motor_stop.put(1, wait=True)

    def resume(self):
        # Resume without erasing
        _th = self.energyToTheta(self._target_energy)
        self.fly_motor.put(_th, wait=True)

    def setStartEnergy(self, energy):
        self._start_energy = energy

    def setTargetEnergy(self, energy):
        self._target_energy = energy

    def setSpeed(self, speed):
        """Set Scan Speed"""
        self._speed = speed

    def setEncSteps(self, steps):
        self._encoder_step_counts = steps

    def setNumOfCounts(self, counts):
        self._num_of_counts = int(counts)

    def checkMotor(self):
        """check motor motion on a separated thread"""

        # Wait for completion
        while True:
            if self.fly_motor_done_move.get() == 0:
                # logger.error("fly_motor : {}".format(self.fly_motor_done_move.get()))
                ttime.sleep(0.1)
            else:
                # logger.error("fly_motor : {}".format(self.fly_motor_done_move.get()))
                break

        # logger.error("Done fly")
        # After the wait, we declare success
        self._acquiring = False
        self.complete_status._finished(success=True)

    def kickoff(self):
        '''Start collection

        Returns
        -------
        DeviceStatus
            This will be set to done when acquisition has begun
        '''
        # Put scaler in fly mode
        # self.scaler_mode.put(1, wait=True)

        # Set monochromator speed
        self.fly_motor_speed.put(self._speed, wait=True)

        # Set encoder step size for accumulating counts
        self.encoder_steps.put(self._encoder_step_counts, wait=True)

        # Reset scaler array waveform
        self.scaler_reset.put(1, wait=True)

        # Reset scaler counter
        self.scaler_preset.put(0, wait=True)

        # Wait For HC10E scaler reset
        ttime.sleep(.2)

        # Indicators
        self._start_time = ttime.time()
        self._acquiring = True
        self._paused = False

        # Put scaler in fly mode
        self.scaler_mode.put(1, wait=True)

        # Start motor motion to target_position asynchronously
        _th = self.energyToTheta(self._target_energy)
        self.fly_motor.put(_th, wait=False)
        ttime.sleep(0.2)

        thread = threading.Thread(target=self.checkMotor, daemon=True)
        thread.start()

        # make status object, Indicate flying has started
        self.kickoff_status = DeviceStatus(self)
        self.kickoff_status._finished(success=True)

        self.complete_status = DeviceStatus(self)

        return self.kickoff_status

    def complete(self):
        '''Wait for flying to be complete'''
        if self.complete_status is None:
            raise RuntimeError('No collection in progress')

        return self.complete_status

    def describe_collect(self):
        d = dict(source="HC10E", dtype="number", shape=(1, ))

        return {'primary': {"ENC": d, "I0": d, "It": d, "If": d, "Ir": d}}

    def collect(self):
        # Put scaler in normal mode
        self.scaler_mode.put(0, wait=False)

        # '''Retrieve all collected data'''
        if self._acquiring:
            raise RuntimeError('Acquisition still in progress. Call complete()'
                               ' first.')

        self.complete_status = None

        # Retrive waveforms from epics PVs
        ENC = self.enc_waveform.get()
        if isinstance(ENC, type(None)):
            ENC = self.enc_waveform.get()

        I0 = self.I0_waveform.get()
        if isinstance(I0, type(None)):
            I0 = self.I0_waveform.get()

        It = self.It_waveform.get()
        if isinstance(It, type(None)):
            It = self.It_waveform.get()

        If = self.If_waveform.get()
        if isinstance(If, type(None)):
            If = self.If_waveform.get()

        Ir = self.Ir_waveform.get()
        if isinstance(Ir, type(None)):
            Ir = self.Ir_waveform.get()

        if self._num_of_counts > len(ENC):
            self._num_of_counts = len(ENC)

        # logger.error("ENC : {}".format(ENC))
        # logger.error("num_of_counts : {}".format(self._num_of_counts))
        # for idx in range(len(ENC)):
        for idx in range(int(self._num_of_counts)):
            t = ttime.time()
            enc = ENC[idx]
            _i0 = I0[idx]
            _it = It[idx]
            _if = If[idx]
            _ir = Ir[idx]

            d = dict(time=t,
                     data=dict(ENC=enc, I0=_i0, It=_it, If=_if, Ir=_ir),
                     timestamps=dict(ENC=t, I0=t, It=t, If=t, Ir=t))
            yield d
示例#27
0
def test_status_others():
    DeviceStatus(None)
示例#28
0
 def trigger_external(self):
     self._status = DeviceStatus(self)
     self._status._finished()
     return self._status
示例#29
0
class HxnTriggeringScaler(HxnModalBase, StruckScaler):
    def __init__(self, prefix, *, scan_type_triggers=None, **kwargs):
        super().__init__(prefix, **kwargs)

        if scan_type_triggers is None:
            scan_type_triggers = {'step': [],
                                  'fly': [],
                                  }

        self.scan_type_triggers = dict(scan_type_triggers)

        # Scaler 1 should be in output mode 1 to properly trigger
        self.stage_sigs[self.output_mode] = 'Mode 1'
        # Ensure that the scaler isn't counting in mcs mode for any reason
        self.stage_sigs[self.stop_all] = 1
        # Ensure that the scaler isn't counting
        self.stage_sigs[self.count] = 0

        self.external_signals = [self.erase_start,
                                 self.start_all,
                                 self.input_mode,
                                 self.channel_advance,
                                 self.nuse_all,
                                 self.preset_real,
                                 self.dwell]

    def mode_internal(self):
        super().mode_internal()

        settings = self.mode_settings

        # ensure that no external signals are in the stage sigs list:
        for ext_sig in self.external_signals:
            if ext_sig in self.stage_sigs:
                del self.stage_sigs[ext_sig]

        scan_type = settings.scan_type.get()

        triggers = self.scan_type_triggers[scan_type]
        settings.triggers.put(list(triggers))

        self.stop_all.put(1, wait=True)
        self.count.put(0, wait=True)
        self.stage_sigs[self.preset_time] = self.count_time.get()

    def mode_external(self):
        super().mode_external()

        if self.preset_time in self.stage_sigs:
            del self.stage_sigs[self.preset_time]

        self.stage_sigs[self.preset_real] = 0.0
        self.stage_sigs[self.dwell] = 0.0
        self.stage_sigs[self.channel_advance] = 'External'
        self.stage_sigs[self.input_mode] = 'Mode 2'
        self.stage_sigs[self.nuse_all] = self.mode_settings.total_points.get()

        self.stage_sigs[self.erase_start] = 1
        self.stage_sigs.move_to_end(self.erase_start)

    def trigger(self):
        if self._staged != Staged.yes:
            raise RuntimeError("This detector is not ready to trigger."
                               "Call the stage() method before triggering.")

        mode = self.mode_settings.mode.get()
        mode_trigger = getattr(self, 'trigger_{}'.format(mode))
        return mode_trigger()

    def trigger_internal(self):
        return super().trigger()

    def trigger_external(self):
        self._status = DeviceStatus(self)
        self._status._finished()
        return self._status
class ContinuousAcquisitionTrigger(BlueskyInterface):
    """
    This trigger mixin class records images when it is triggered.

    It expects the detector to *already* be acquiring, continously.
    """
    def __init__(self, *args, plugin_name=None, image_name=None, **kwargs):
        if plugin_name is None:
            raise ValueError("plugin name is a required keyword argument")
        super().__init__(*args, **kwargs)
        self._plugin = getattr(self, plugin_name)
        if image_name is None:
            image_name = '_'.join([self.name, 'image'])
        self._plugin.stage_sigs[self._plugin.auto_save] = 'No'
        self.cam.stage_sigs[self.cam.image_mode] = 'Continuous'
        self._plugin.stage_sigs[self._plugin.file_write_mode] = 'Capture'
        self._image_name = image_name
        self._status = None
        self._num_captured_signal = self._plugin.num_captured
        self._num_captured_signal.subscribe(self._num_captured_changed)
        self._save_started = False

    def stage(self):
        if self.cam.acquire.get() != 1:
            raise RuntimeError("The ContinuousAcuqisitionTrigger expects "
                               "the detector to already be acquiring.")
        return super().stage()
        # put logic to look up proper dark frame
        # die if none is found

    def trigger(self):
        "Trigger one acquisition."
        if not self._staged:
            raise RuntimeError("This detector is not ready to trigger."
                               "Call the stage() method before triggering.")
        self._save_started = False
        self._status = DeviceStatus(self)
        self._desired_number_of_sets = self.number_of_sets.get()
        self._plugin.num_capture.put(self._desired_number_of_sets)
        self.dispatch(self._image_name, ttime.time())
        # reset the proc buffer, this needs to be generalized
        self.proc.reset_filter.put(1)
        self._plugin.capture.put(1)  # Now the TIFF plugin is capturing.
        return self._status

    def _num_captured_changed(self, value=None, old_value=None, **kwargs):
        "This is called when the 'acquire' signal changes."
        if self._status is None:
            return
        if value == self._desired_number_of_sets:
            # This is run on a thread, so exceptions might pass silently.
            # Print and reraise so they are at least noticed.
            try:
                self.tiff.write_file.put(1)
            except Exception as e:
                print(e)
                raise
            self._save_started = True
        if value == 0 and self._save_started:
            self._status._finished()
            self._status = None
            self._save_started = False