Exemple #1
0
class IPM(InOutRecordPositioner):
    """
    Standard intensity position monitor.

    This is an `InOutRecordPositioner` that moves
    the target position to any of the four set positions, or out. Valid states
    are (1, 2, 3, 4, 5) or the equivalent (T1, T2, T3, T4, OUT).

    IPMs each also have a diode, which is implemented as the diode attribute of
    this class. This can easily be controlled using ``ipm.diode.insert()`` or
    ``ipm.diode.remove()``.
    """
    __doc__ += basic_positioner_init

    state = Cmp(EpicsSignal, ':TARGET', write_pv=':TARGET:GO')
    diode = Cmp(InOutRecordPositioner, ":DIODE")

    states_list = ['T1', 'T2', 'T3', 'T4', 'OUT']
    _states_alias = {
        'T1': ['T1', 'TARGET1', 't1', 'target1'],
        'T2': ['T2', 'TARGET2', 't2', 'target2'],
        'T3': ['T3', 'TARGET3', 't3', 'target3'],
        'T4': ['T4', 'TARGET4', 't4', 'target4']
    }

    in_states = ['T1', 'T2', 'T3', 'T4']

    # Assume that having any target in gives transmission 0.8
    _transmission = {'T' + str(n): 0.8 for n in range(1, 5)}
Exemple #2
0
class ChannelCutTower(TowerBase):
    """
    Channel Cut tower.

    Components

    th : RotationAero
        Rotation stage of the channel cut crystal

    x : LinearAero
        Translation stage of the tower
    """
    # Rotation
    th = Cmp(RotationAero, ":TH", desc="TH")

    # Translation
    x = Cmp(LinearAero, ":X", desc="X")

    def __init__(self, prefix, *args, **kwargs):
        super().__init__(prefix, *args, **kwargs)
        self._energy_motors = [self.th]

    @property
    def position(self):
        """
        Returns the theta position of the crystal (th) in degrees.

        Returns
        -------
        position : float
            Position of the arm in degrees.
        """
        return self.th.position

    def set_energy(self, E, wait=False, check_status=True):
        """
        Sets the angles of the crystals in the channel cut line to maximize the
        inputted energy.        
    
        Parameters
        ---------
        E : float
            Energy to use for the system.

        wait : bool, optional
            Wait for motion to complete before returning the console.

        check_status : bool, optional
            Check if the motors are in a valid state to move.
        """
        # Convert to theta
        theta = bragg_angle(E=E)

        # Check to make sure the motors are in a valid state to move
        if check_status:
            self.check_status(E)

        # Perform the move
        status = self.th.move(theta, wait=wait)
        return status
Exemple #3
0
class MovableStand(InOutPVStatePositioner):
    """
    Stand that can be moved.

    Parameters
    ----------
    prefix: ``str``

    name: ``str``, required keyword
    """
    in_limit = Cmp(EpicsSignalRO, ':IN_DI')
    out_limit = Cmp(EpicsSignalRO, ':OUT_DO')

    _state_logic = {
        "in_limit": {
            0: "defer",
            1: "IN"
        },
        "out_limit": {
            0: "defer",
            1: "OUT"
        }
    }

    def set(self, *args, **kwargs):
        raise NotImplementedError('Stand not motorized')
Exemple #4
0
class PressureSwitch(PneuBase):
    """
    Pressure switch.

    Components
    ----------
    pressure : EpicsSignalRO
        Pressure readbac signal.
    """
    tab_whitelist = ['bad', 'good', 'position']
    pressure = Cmp(EpicsSignalRO, ":GPS")

    @property
    def position(self):
        """
        Returns the position of the valve.

        Returns
        -------
        position : str
            String saying the current position of the valve. Can be "OPEN" or
            "CLOSED".
        """
        if self.pressure.get() == 0:
            return "GOOD"
        elif self.pressure.get() == 1:
            return "BAD"
        else:
            return "UNKNOWN"

    @property
    def good(self):
        """
        Returns if the pressure is in the 'good' state.

        Returns
        -------
        good : bool
            True if the pressure switch is in the 'good' state.
        """
        return (self.position == "GOOD")

    @property
    def bad(self):
        """
        Returns if the pressure is in the 'bad' state.

        Returns
        -------
        bad : bool
            True if the pressure switch is in the 'bad' state.
        """
        return (self.position == "BAD")
Exemple #5
0
class EccController(SndDevice):
    """
    ECC Controller
    """
    _firm_day = Cmp(EpicsSignalRO, ":CALC:FIRMDAY")
    _firm_month = Cmp(EpicsSignalRO, ":CALC:FIRMMONTH")
    _firm_year = Cmp(EpicsSignalRO, ":CALC:FIRMYEAR")
    _flash = Cmp(EpicsSignal, ":RDB:FLASH", write_pv=":CMD:FLASH")

    @property
    def firmware(self):
        """
        Returns the firmware in the same date format as the EDM screen.
        """
        return "{0}/{1}/{2}".format(self._firm_day.value,
                                    self._firm_month.value,
                                    self._firm_year.value)

    @property
    def flash(self):
        """
        Saves the current configuration of the controller.
        """
        return self._flash.set(1, timeout=self.set_timeout)
Exemple #6
0
class PIMPulnixDetector(PulnixDetector):
    """
    Pulnix detector that is used in the PIM.

    Plugins should be added on an as needed basis here.
    """
    image1 = Cmp(ImagePlugin, ":IMAGE1:", read_attrs=['array_data'])
    image2 = Cmp(ImagePlugin, ":IMAGE2:", read_attrs=['array_data'])
    stats2 = Cmp(StatsPlugin, ":Stats2:", read_attrs=['centroid',
                                                      'mean_value'])

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.image1.stage_sigs[self.image1.nd_array_port] = 'CAM'
        self.image1.stage_sigs[self.image1.blocking_callbacks] = 'Yes'
        self.image1.stage_sigs[self.image1.enable] = 1

        self.stats2.stage_sigs[self.stats2.nd_array_port] = 'CAM'
        self.stats2.stage_sigs[self.stats2.blocking_callbacks] = 'Yes'
        self.stats2.stage_sigs[self.stats2.compute_statistics] = 'Yes'
        self.stats2.stage_sigs[self.stats2.compute_centroid] = 'Yes'
        self.stats2.stage_sigs[self.stats2.centroid_threshold] = 200
        self.stats2.stage_sigs[self.stats2.min_callback_time] = 0.0
        self.stats2.stage_sigs[self.stats2.enable] = 1
Exemple #7
0
class SeqBase(SndDevice):
    """
    Base sequencer class.
    """
    state_control = Cmp(EpicsSignal, ":PLYCTL")
    
    def start(self):
        """
        Start the sequencer.
        """
        status = self.state_control.set(1, timeout=self.timeout)
        status_wait(status)
        
    def stop(self):
        """
        Stop the sequencer.
        """
        status = self.state_control.set(0, timeout=self.timeout)
        status_wait(status)        
Exemple #8
0
class AeroBase(SndEpicsMotor):
    """
    Base Aerotech motor class.

    Components

    power : EpicsSignal, ".CNEN"
        Enables or disables power to the axis.

    retries : EpicsSignalRO, ".RCNT"
        Number of retries attempted.

    retries_max : EpicsSignal, ".RTRY"
        Maximum number of retries.

    retries_deadband : EpicsSignal, ".RDBD"
        Tolerance of each retry.

    axis_fault : EpicsSignalRO, ":AXIS_FAULT"
        Fault readback for the motor.

    axis_status : EpicsSignalRO, ":AXIS_STATUS"
        Status readback for the motor.

    clear_error : EpicsSignal, ":CLEAR"
        Clear error signal.

    config : EpicsSignal, ":CONFIG"
        Signal to reconfig the motor.
    """
    # Remove when these have been figured out
    low_limit_switch = Cmp(Signal)
    high_limit_switch = Cmp(Signal)

    power = Cmp(EpicsSignal, ".CNEN")
    retries = Cmp(EpicsSignalRO, ".RCNT")
    retries_max = Cmp(EpicsSignal, ".RTRY")
    retries_deadband = Cmp(EpicsSignal, ".RDBD")
    axis_status = Cmp(EpicsSignalRO, ":AXIS_STATUS")
    axis_fault = Cmp(EpicsSignalRO, ":AXIS_FAULT")
    clear_error = Cmp(EpicsSignal, ":CLEAR")
    config = Cmp(EpicsSignal, ":CONFIG")
    zero_all_proc = Cmp(EpicsSignal, ":ZERO_P.PROC")
    home_forward = Cmp(EpicsSignal, ".HOMF")
    home_reverse = Cmp(EpicsSignal, ".HOMR")
    dial = Cmp(EpicsSignalRO, ".DRBV")
    state_component = Cmp(EpicsSignal, ".SPMG")

    def __init__(self, prefix, name=None, *args, **kwargs):
        super().__init__(prefix, name=name, *args, **kwargs)
        self.motor_done_move.unsubscribe(self._move_changed)
        self.user_readback.unsubscribe(self._pos_changed)
        self.configuration_attrs.append("power")
        self._state_list = ["Stop", "Pause", "Move", "Go"]

    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

    @stop_on_keyboardinterrupt
    def homf(self, ret_status=False, print_set=True, check_status=True):
        """
        Home the motor forward.

        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.
        
        check_status : bool, optional
            Check if the motors are in a valid state to move.

        Returns
        -------
        Status : StatusObject
            Status of the set.
        """
        # Check the motor status
        if check_status:
            self.check_status()
        status = self.home_forward.set(1, timeout=self.set_timeout)
        return self._status_print(status,
                                  "Homing '{0}' forward.".format(self.desc),
                                  print_set=print_set,
                                  ret_status=ret_status)

    @stop_on_keyboardinterrupt
    def homr(self, ret_status=False, print_set=True, check_status=True):
        """
        Home the motor in reverse.
        
        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        check_status : bool, optional
            Check if the motors are in a valid state to move.

        Returns
        -------
        Status : StatusObject
            Status of the set.
        """
        # Check the motor status
        if check_status:
            self.check_status()
        status = self.home_reverse.set(1, timeout=self.set_timeout)
        return self._status_print(status,
                                  "Homing '{0}' in reverse.".format(self.desc),
                                  print_set=print_set,
                                  ret_status=ret_status)

    def move(self,
             position,
             wait=False,
             check_status=True,
             timeout=None,
             *args,
             **kwargs):
        """
        Move to a specified position, optionally waiting for motion to
        complete.

        Parameters
        ----------
        position
            Position to move to.

        wait : bool, optional
            Wait for the motor to complete the motion.

        check_status : bool, optional
            Check if the motors are in a valid state to move.

        moved_cb : callable
            Call this callback when movement has finished. This callback must
            accept one keyword argument: 'obj' which will be set to this
            positioner instance.

        timeout : float, optional
            Maximum time to wait for the motion. If None, the default timeout
            for this positioner is used.

        Returns
        -------
        status : MoveStatus        
            Status object for the move.
        
        Raises
        ------
        TimeoutError
            When motion takes longer than `timeout`
        
        ValueError
            On invalid positions
        
        RuntimeError
            If motion fails other than timing out
        """
        # Check the motor status
        if check_status:
            self.check_status(position)
        logger.debug("Moving {0} to {1}".format(self.name, position))
        return super().move(position,
                            wait=wait,
                            timeout=timeout,
                            *args,
                            **kwargs)

    def mv(self, position, wait=True, print_move=True, *args, **kwargs):
        """
        Move to a specified position, optionally waiting for motion to
        complete. mv() is different from move() by catching all the common
        exceptions that this motor can raise and just raises a logger
        warning. Therefore if building higher level functionality, do not
        use this method and use move() instead otherwise none of these
        exceptions will propagate to it.

        Parameters
        ----------
        position
            Position to move to.

        wait : bool, optional
            Wait for the motor to complete the motion.

        check_status : bool, optional
            Check if the motors are in a valid state to move.

        print_move : bool, optional
            Print a short statement about the move.

        Exceptions Caught
        -----------------
        LimitError
            Error raised when the inputted position is beyond the soft limits.
        
        MotorDisabled
            Error raised if the motor is disabled and move is requested.

        MotorFaulted
            Error raised if the motor is disabled and the move is requested.

        MotorStopped
            Error raised if the motor is stopped and the move is requested.

        Returns
        -------
        status : MoveStatus        
            Status object for the move.
        """
        try:
            status = super().mv(position, wait=wait, *args, **kwargs)

            # Notify the user that a motor has completed or the command is sent
            if print_move:
                if wait:
                    logger.info("Move completed for '{0}'.".format(self.desc))
                else:
                    logger.info("Move command sent to '{0}'.".format(
                        self.desc))
            return status

        # Catch all the common motor exceptions
        except LimitError:
            logger.warning("Requested move '{0}' is outside the soft limits "
                           "{1} for motor {2}".format(position, self.limits,
                                                      self.desc))
        except MotorDisabled:
            logger.warning(
                "Cannot move - motor {0} is currently disabled. Try "
                "running 'motor.enable()'.".format(self.desc))
        except MotorFaulted:
            logger.warning("Cannot move - motor {0} is currently faulted. Try "
                           "running 'motor.clear()'.".format(self.desc))
        except MotorStopped:
            logger.warning("Cannot move - motor {0} is currently stopped. Try "
                           "running 'motor.state=\"Go\"'.".format(self.desc))

    def check_status(self, position=None):
        """
        Checks the status of the motor to make sure it is ready to move. Checks
        the current position of the motor, and if a position is provided it also
        checks that position.

        Parameters
        ----------
        position : float, optional
            Position to check for validity.

        Raises
        ------
        MotorDisabled
            If the motor is disabled.
        
        MotorFaulted
            If the motor is faulted.

        MotorStopped
            If the motor is stopped.
        """
        if not self.enabled:
            err = "Motor '{0}' is currently disabled".format(self.desc)
            logger.error(err)
            raise MotorDisabled(err)

        if self.faulted:
            err = "Motor '{0}' is currently faulted.".format(self.desc)
            logger.error(err)
            raise MotorFaulted(err)

        if self.state == "Stop":
            err = "Motor '{0}' is currently stopped.".format(self.desc)
            logger.error(err)
            raise MotorStopped(err)

        # Check if the current position is valid
        self.check_value(self.position)
        # Check if the move position is valid
        if position:
            self.check_value(position)

    def set_position(self, position_des, print_set=True):
        """
        Sets the current position to be the inputted position by changing the 
        offset.
        
        Parameters
        ----------
        position_des : float
            The desired current position.
        """
        # Print to console or just to the log
        if print_set:
            log_level = logger.info
        else:
            log_level = logger.debug

        log_level("'{0}' previous position: {0}, offset: {1}".format(
            self.position, self.offset))
        self.offset += position_des - self.position
        log_level("'{0}' New position: {0}, offset: {1}".format(
            self.position, self.offset))

    def enable(self, ret_status=False, print_set=True):
        """
        Enables the motor power.

        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        Status
            The status object for setting the power signal.
        """
        status = self.power.set(1, timeout=self.set_timeout)
        return self._status_print(status,
                                  "Enabled motor '{0}'.".format(self.desc),
                                  print_set=print_set,
                                  ret_status=ret_status)

    def disable(self, ret_status=False, print_set=True):
        """
        Disables the motor power.

        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        Status
            The status object for setting the power signal.
        """
        status = self.power.set(0, timeout=self.set_timeout)
        return self._status_print(status,
                                  "Disabled motor '{0}'.".format(self.desc),
                                  print_set=print_set,
                                  ret_status=ret_status)

    @property
    def enabled(self):
        """
        Returns if the motor is curently enabled.

        Returns
        -------
        enabled : bool
            True if the motor is powered, False if not.
        """
        return bool(self.power.value)

    def clear(self, ret_status=False, print_set=True):
        """
        Clears the motor error.

        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        Status
            The status object for setting the clear_error signal.
        """
        status = self.clear_error.set(1, timeout=self.set_timeout)
        return self._status_print(status,
                                  "Cleared motor '{0}'.".format(self.desc),
                                  print_set=print_set,
                                  ret_status=ret_status)

    def reconfig(self, ret_status=False, print_set=True):
        """
        Re-configures the motor.

        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        Status
            The status object for setting the config signal.
        """
        status = self.config.set(1, timeout=self.set_timeout)
        return self._status_print(status,
                                  "Reconfigured motor '{0}'.".format(
                                      self.desc),
                                  print_set=print_set,
                                  ret_status=ret_status)

    @property
    def faulted(self):
        """
        Returns the current fault with the motor.
        
        Returns
        -------
        faulted
            Fault enumeration.
        """
        if self.axis_fault.connected:
            return bool(self.axis_fault.value)
        else:
            return None

    def zero_all(self, ret_status=False, print_set=True):
        """
        Sets the current position to be the zero position of the motor.

        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        status : StatusObject        
            Status object for the set.
        """
        status = self.zero_all_proc.set(1, timeout=self.set_timeout)
        return self._status_print(status,
                                  "Zeroed motor '{0}'.".format(self.desc),
                                  print_set=print_set,
                                  ret_status=ret_status)

    @property
    def state(self):
        """
        Returns the state of the motor. State can be one of the following:
            'Stop', 'Pause', 'Move', 'Go'

        Returns
        -------
        state : str
            The current state of the motor
        """
        return self._state_list[self.state_component.get()]

    @state.setter
    def state(self, val, ret_status=False, print_set=True):
        """
        Sets the state of the motor. Inputted state can be one of the following
        states or the index of the desired state:
            'Stop', 'Pause', 'Move', 'Go'            
        Alias for set_state((val, False, True)
        
        Parameters
        ----------
        val : int or str
        
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        Status
            The status object for setting the state signal.        
        """
        try:
            return self.set_state(val, ret_status, print_set)
        except ValueError:
            logger.info("State must be one of the following: {0}".format(
                self._state_list))

    def set_state(self, state, ret_status=True, print_set=False):
        """
        Sets the state of the motor. Inputted state can be one of the following
        states or the index of the desired state:
            'Stop', 'Pause', 'Move', 'Go'            
        
        Parameters
        ----------
        val : int or str
        
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        Status
            The status object for setting the state signal.        
        """
        # Make sure it is in title case if it's a string
        val = state
        if isinstance(state, str):
            val = state.title()

        # Make sure it is a valid state or enum
        if val not in self._state_list + list(range(len(self._state_list))):
            error = "Invalid state inputted: '{0}'.".format(val)
            logger.error(error)
            raise ValueError(error)

        # Lets enforce it's a string or value
        status = self.state_component.set(val, timeout=self.set_timeout)

        return self._status_print(status,
                                  "Changed state of '{0} to '{1}'.".format(
                                      self.desc, val),
                                  print_set=print_set,
                                  ret_status=ret_status)

    def ready_motor(self, ret_status=False, print_set=True):
        """
        Sets the motor to the ready state by clearing any errors, enabling it,
        and setting the state to be 'Go'.

        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        Status
            The status object for all the sets.        
        """
        status = [self.clear(ret_status=True, print_set=False)]
        status.append(self.enable(ret_status=True, print_set=False))
        status.append(self.set_state("Go", ret_status=True, print_set=False))
        return self._status_print(status,
                                  "Motor '{0}' is now ready to move.".format(
                                      self.desc),
                                  print_set=print_set,
                                  ret_status=ret_status)

    def expert_screen(self, print_msg=True):
        """
        Launches the expert screen for the motor.

        Parameters
        ----------
        print_msg : bool, optional
            Prints that the screen is being launched.
        """
        path = absolute_submodule_path(
            "hxrsnd/screens/motor_expert_screens.sh")
        if print_msg:
            logger.info("Launching expert screen.")
        os.system("{0} {1} {2} &".format(path, self.prefix, "aerotech"))

    def status(self,
               status="",
               offset=0,
               print_status=True,
               newline=False,
               short=False):
        """
        Returns the status of the device.
        
        Parameters
        ----------
        status : str, optional
            The string to append the status to.
            
        offset : int, optional
            Amount to offset each line of the status.

        print_status : bool, optional
            Determines whether the string is printed or returned.

        newline : bool, optional
            Adds a new line to the end of the string.

        Returns
        -------
        status : str
            Status string.
        """
        if short:
            status += "\n{0}{1:<16}|{2:^16.3f}|{3:^16.3f}".format(
                " " * offset, self.desc, self.position, self.dial.value)
        else:
            status += "{0}{1}\n".format(" " * offset, self.desc)
            status += "{0}PV: {1:>25}\n".format(" " * (offset + 2),
                                                self.prefix)
            status += "{0}Enabled: {1:>20}\n".format(" " * (offset + 2),
                                                     str(self.enabled))
            status += "{0}Faulted: {1:>20}\n".format(" " * (offset + 2),
                                                     str(self.faulted))
            status += "{0}State: {1:>22}\n".format(" " * (offset + 2),
                                                   str(self.state))
            status += "{0}Position: {1:>19}\n".format(" " * (offset + 2),
                                                      np.round(self.wm(), 6))
            status += "{0}Dial: {1:>23}\n".format(" " * (offset + 2),
                                                  np.round(self.dial.value, 6))
            status += "{0}Limits: {1:>21}\n".format(
                " " * (offset + 2),
                str((int(self.low_limit), int(self.high_limit))))

        if newline:
            status += "\n"
        if print_status is True:
            logger.info(status)
        else:
            return status
Exemple #9
0
class EccBase(SndMotor, PositionerBase):
    """
    ECC Motor Class
    """
    # position
    user_readback = Cmp(EpicsSignalRO, ":POSITION", auto_monitor=True)
    user_setpoint = Cmp(EpicsSignal, ":CMD:TARGET")

    # limits
    upper_ctrl_limit = Cmp(EpicsSignal, ':CMD:TARGET.HOPR')
    lower_ctrl_limit = Cmp(EpicsSignal, ':CMD:TARGET.LOPR')

    # configuration
    motor_egu = Cmp(EpicsSignalRO, ":UNIT")
    motor_amplitude = Cmp(EpicsSignal, ":CMD:AMPL")
    motor_dc = Cmp(EpicsSignal, ":CMD:DC")
    motor_frequency = Cmp(EpicsSignal, ":CMD:FREQ")

    # motor status
    motor_connected = Cmp(EpicsSignalRO, ":ST_CONNECT")
    motor_enabled = Cmp(EpicsSignalRO, ":ST_ENABLED")
    motor_referenced = Cmp(EpicsSignalRO, ":ST_REFVAL")
    motor_error = Cmp(EpicsSignalRO, ":ST_ERROR")
    motor_is_moving = Cmp(EpicsSignalRO, ":RD_MOVING")
    motor_done_move = Cmp(EpicsSignalRO, ":RD_INRANGE")
    high_limit_switch = Cmp(EpicsSignal, ":ST_EOT_FWD")
    low_limit_switch = Cmp(EpicsSignal, ":ST_EOT_BWD")
    motor_reference_position = Cmp(EpicsSignalRO, ":REF_POSITION")

    # commands
    motor_stop = Cmp(EpicsSignal, ":CMD:STOP")
    motor_reset = Cmp(EpicsSignal, ":CMD:RESET.PROC")
    motor_enable = Cmp(EpicsSignal, ":CMD:ENABLE")

    @property
    def position(self):
        """
        Returns the current position of the motor.

        Returns
        -------
        position : float
            Current position of the motor.
        """
        return self.user_readback.value

    @property
    def reference(self):
        """
        Returns the reference position of the motor.
        
        Returns
        -------
        position : float
            Reference position of the motor.
        """
        return self.motor_reference_position.value

    @property
    def egu(self):
        """
        The engineering units (EGU) for a position

        Returns
        -------
        Units : str
            Engineering units for the position.
        """
        return self.motor_egu.get()

    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 enable(self, ret_status=False, print_set=True):
        """
        Enables the motor power.

        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        Status
            The status object for setting the power signal.
        """
        status = self.motor_enable.set(1, timeout=self.set_timeout)
        return self._status_print(status,
                                  "Enabled motor '{0}'".format(self.desc),
                                  ret_status=ret_status,
                                  print_set=print_set)

    def disable(self, ret_status=False, print_set=True):
        """
        Disables the motor power.

        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        Status
            The status object for setting the power signal.
        """
        status = self.motor_enable.set(0, timeout=self.set_timeout)
        return self._status_print(status,
                                  "Disabled motor '{0}'".format(self.desc),
                                  ret_status=ret_status,
                                  print_set=print_set)

    @property
    def enabled(self):
        """
        Returns if the motor is curently enabled.

        Returns
        -------
        enabled : bool
            True if the motor is powered, False if not.
        """
        return bool(self.motor_enable.value)

    @property
    def connected(self):
        """
        Returns if the motor is curently connected.

        Returns
        -------
        connected : bool
            True if the motor is connected, False if not.
        """
        return bool(self.motor_connected.value)

    @property
    def referenced(self):
        """
        Returns if the motor is curently referenced.

        Returns
        -------
        referenced : bool
            True if the motor is referenced, False if not.
        """
        return bool(self.motor_referenced.value)

    @property
    def error(self):
        """
        Returns the current error with the motor.
        
        Returns
        -------
        error : bool
            Error enumeration.
        """
        return bool(self.motor_error.value)

    def reset(self, ret_status=False, print_set=True):
        """
        Sets the current position to be the zero position of the motor.

        Returns
        -------
        status : StatusObject        
            Status object for the set.
        """
        status = self.motor_reset.set(1, timeout=self.set_timeout)
        return self._status_print(status,
                                  "Reset motor '{0}'".format(self.desc),
                                  ret_status=ret_status,
                                  print_set=print_set)

    def move(self, position, check_status=True, timeout=None, *args, **kwargs):
        """
        Move to a specified position.

        Parameters
        ----------
        position
            Position to move to

        check_status : bool, optional
            Check if the motors are in a valid state to move.

        Returns
        -------
        status : MoveStatus        
            Status object for the move.
        
        Raises
        ------
        TimeoutError
            When motion takes longer than `timeout`
        
        ValueError
            On invalid positions
        
        RuntimeError
            If motion fails other than timing out
        """
        # Check the motor status
        if check_status:
            self.check_status(position)
        logger.debug("Moving {0} to {1}".format(self.name, position))
        # Begin the move process
        return self.user_setpoint.set(position, timeout=timeout)

    def mv(self, position, print_move=True, *args, **kwargs):
        """
        Move to a specified position, optionally waiting for motion to
        complete. mv() is different from move() by catching all the common
        exceptions that this motor can raise and just raises a logger
        warning. Therefore if building higher level functionality, do not
        use this method and use move() instead otherwise none of these
        exceptions will propagate to it.

        Parameters
        ----------
        position
            Position to move to.

        print_move : bool, optional
            Print a short statement about the move.

        Exceptions Caught
        -----------------
        LimitError
            Error raised when the inputted position is beyond the soft limits.
        
        MotorDisabled
            Error raised if the motor is disabled and move is requested.

        MotorFaulted
            Error raised if the motor is disabled and the move is requested.

        Returns
        -------
        status : MoveStatus        
            Status object for the move.
        """
        try:
            status = super().mv(position, *args, **kwargs)

            # Notify the user that a motor has completed or the command is sent
            if print_move:
                logger.info("Move command sent to '{0}'.".format(self.desc))
            # Check if a status object is desired
            return status

        # Catch all the common motor exceptions
        except LimitError:
            logger.warning("Requested move '{0}' is outside the soft limits "
                           "{1} for motor {2}".format(position, self.limits,
                                                      self.desc))
        except MotorDisabled:
            logger.warning(
                "Cannot move - motor {0} is currently disabled. Try "
                "running 'motor.enable()'.".format(self.desc))
        except MotorFaulted:
            logger.warning("Cannot move - motor {0} is currently faulted. Try "
                           "running 'motor.clear()'.".format(self.desc))

    def check_status(self, position=None):
        """
        Checks the status of the motor to make sure it is ready to move. Checks
        the current position of the motor, and if a position is provided it also
        checks that position.

        Parameters
        ----------
        position : float
            Position to check for validity.

        Raises
        ------
        MotorDisabled
            If the motor is disabled.

        MotorError
            If the motor has an error.
        """
        if not self.enabled:
            err = "Motor '{0}' is currently disabled.".format(self.desc)
            logger.error(err)
            raise MotorDisabled(err)

        if self.error:
            err = "Motor '{0}' currently has an error.".format(self.desc)
            logger.error(err)
            raise MotorError(err)

        # Check if the current position is valid
        self.check_value(self.position)
        # Check if the move position is valid
        if position:
            self.check_value(position)

    def check_value(self, position):
        """
        Checks to make sure the inputted value is valid.

        Parameters
        ----------
        position : float
            Position to check for validity

        Raises
        ------
        ValueError
            If position is None, NaN or Inf
        LimitError
            If the position is outside the soft limits.
        """
        # Check for invalid positions
        if position is None or np.isnan(position) or np.isinf(position):
            raise ValueError("Invalid value inputted: '{0}'".format(position))

        # Check if it is within the soft limits
        if not (self.low_limit <= position <= self.high_limit):
            err_str = "Requested value {0} outside of range: [{1}, {2}]"
            logger.warn(
                err_str.format(position, self.low_limit, self.high_limit))
            raise LimitError(err_str)

    def stop(self, success=False, ret_status=False, print_set=True):
        """
        Stops the motor.

        Parameters
        ----------
        ret_status : bool, optional
            Return the status object of the set.

        print_set : bool, optional
            Print a short statement about the set.

        Returns
        -------
        Status : StatusObject
            Status of the set.
        """
        status = self.motor_stop.set(1, wait=False, timeout=self.set_timeout)
        super().stop(success=success)
        return self._status_print(status,
                                  "Stopped motor '{0}'".format(self.desc),
                                  ret_status=ret_status,
                                  print_set=print_set)

    def expert_screen(self, print_msg=True):
        """
        Launches the expert screen for the motor.

        Parameters
        ----------
        print_msg : bool, optional
            Prints that the screen is being launched.
        """
        # Get the absolute path to the screen
        path = absolute_submodule_path(
            "hxrsnd/screens/motor_expert_screens.sh")
        if print_msg:
            logger.info("Launching expert screen.")
        os.system("{0} {1} {2} &".format(path, self.prefix, "attocube"))

    def set_limits(self, llm, hlm):
        """
        Sets the limits of the motor. Alias for limits = (llm, hlm).
        
        Parameters
        ----------
        llm : float
            Desired low limit.
            
        hlm : float
            Desired low limit.
        """
        self.limits = (llm, hlm)

    @property
    def high_limit(self):
        """
        Returns the upper limit fot the user setpoint.

        Returns
        -------
        high_limit : float
        """
        return self.upper_ctrl_limit.value

    @high_limit.setter
    def high_limit(self, value):
        """
        Sets the high limit for user setpoint.
        """
        self.upper_ctrl_limit.put(value)

    @property
    def low_limit(self):
        """
        Returns the lower limit fot the user setpoint.

        Returns
        -------
        low_limit : float
        """
        return self.lower_ctrl_limit.value

    @low_limit.setter
    def low_limit(self, value):
        """
        Sets the high limit for user setpoint.
        """
        self.lower_ctrl_limit.put(value)

    @property
    def limits(self):
        """
        Returns the limits of the motor.

        Returns
        -------
        limits : tuple
        """
        return (self.low_limit, self.high_limit)

    @limits.setter
    def limits(self, value):
        """
        Sets the limits for user setpoint.
        """
        self.low_limit = value[0]
        self.high_limit = value[1]

    def status(self,
               status="",
               offset=0,
               print_status=True,
               newline=False,
               short=False):
        """
        Returns the status of the device.
        
        Parameters
        ----------
        status : str, optional
            The string to append the status to.
            
        offset : int, optional
            Amount to offset each line of the status.

        print_status : bool, optional
            Determines whether the string is printed or returned.

        newline : bool, optional
            Adds a new line to the end of the string.

        Returns
        -------
        status : str
            Status string.
        """
        if short:
            status += "\n{0}{1:<16}|{2:^16.3f}|{3:^16.3f}".format(
                " " * offset, self.desc, self.position, self.reference)
        else:
            status += "{0}{1}\n".format(" " * offset, self.desc)
            status += "{0}PV: {1:>25}\n".format(" " * (offset + 2),
                                                self.prefix)
            status += "{0}Enabled: {1:>20}\n".format(" " * (offset + 2),
                                                     str(self.enabled))
            status += "{0}Faulted: {1:>20}\n".format(" " * (offset + 2),
                                                     str(self.error))
            status += "{0}Position: {1:>19}\n".format(" " * (offset + 2),
                                                      np.round(self.wm(), 6))
            status += "{0}Limits: {1:>21}\n".format(
                " " * (offset + 2),
                str((int(self.low_limit), int(self.high_limit))))
        if newline:
            status += "\n"
        if print_status is True:
            logger.info(status)
        else:
            return status
Exemple #10
0
class LODCM(InOutRecordPositioner):
    """
    Large Offset Dual Crystal Monochromator

    This is the device that allows XPP and XCS to multiplex with downstream
    hutches. It contains two crystals that steer/split the beam and a number of
    diagnostic devices between them. Beam can continue onto the main line, onto
    the mono line, onto both, or onto neither.

    This positioner only considers the h1n and diagnostic motors.
%s
    main_line: ``str``, optional
        Name of the main, no-bounce beamline.

    mono_line: ``str``, optional
        Name of the mono, double-bounce beamline.
    """
    __doc__ = __doc__ % basic_positioner_init

    state = Cmp(EpicsSignal, ':H1N', write_pv=':H1N:GO')

    yag = Cmp(YagLom, ":DV")
    dectris = Cmp(Dectris, ":DH")
    diode = Cmp(InOutRecordPositioner, ":DIODE")
    foil = Cmp(Foil, ":FOIL")

    states_list = ['OUT', 'C', 'Si']
    in_states = ['C', 'Si']

    # TBH these are guessed. Please replace if you know better. These don't
    # need to be 100% accurate, but they should reflect a reasonable reduction
    # in transmission.
    _transmission = {'C': 0.8, 'Si': 0.7}

    def __init__(self,
                 prefix,
                 *,
                 name,
                 main_line='MAIN',
                 mono_line='MONO',
                 **kwargs):
        super().__init__(prefix, name=name, **kwargs)
        self.main_line = main_line
        self.mono_line = mono_line

    @property
    def branches(self):
        """
        Returns
        -------
        branches: ``list`` of ``str``
            A list of possible destinations.
        """
        return [self.main_line, self.mono_line]

    @property
    def destination(self):
        """
        Which beamline the light is reaching.

        Indeterminate states will show as blocked.

        Returns
        -------
        destination: ``list`` of ``str``
            ``self.main_line`` if the light continues on the main line.
            ``self.mono_line`` if the light continues on the mono line.
        """
        if self.position == 'OUT':
            dest = [self.main_line]
        elif self.position == 'Si':
            dest = [self.mono_line]
        elif self.position == 'C':
            dest = [self.main_line, self.mono_line]
        else:
            dest = []

        if not self._dia_clear and self.mono_line in dest:
            dest.remove(self.mono_line)

        return dest

    @property
    def _dia_clear(self):
        """
        Check if the diagnostics are clear. All but the diode may prevent beam.

        Returns
        -------
        diag_clear: ``bool``
            ``False`` if the diagnostics will prevent beam.
        """
        yag_clear = self.yag.removed
        dectris_clear = self.dectris.removed
        foil_clear = self.foil.removed
        return all((yag_clear, dectris_clear, foil_clear))

    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

    remove_dia.__doc__ += insert_remove
Exemple #11
0
class NotepadScanStatus(Device):
    istep = Cmp(EpicsSignal, ":ISTEP")
    isscan = Cmp(EpicsSignal, ":ISSCAN")
    nshots = Cmp(EpicsSignal, ":NSHOTS")
    nsteps = Cmp(EpicsSignal, ":NSTEPS")
    var0 = Cmp(EpicsSignal, ":SCANVAR00")
    var1 = Cmp(EpicsSignal, ":SCANVAR01")
    var2 = Cmp(EpicsSignal, ":SCANVAR02")
    var0_max = Cmp(EpicsSignal, ":MAX00")
    var1_max = Cmp(EpicsSignal, ":MAX01")
    var2_max = Cmp(EpicsSignal, ":MAX02")
    var0_min = Cmp(EpicsSignal, ":MIN00")
    var1_min = Cmp(EpicsSignal, ":MIN01")
    var2_min = Cmp(EpicsSignal, ":MIN02")

    def clean_fields(self):
        for sig_name in self.signal_names:
            sig = getattr(self, sig_name)
            val = sig.value
            if isinstance(val, (int, float)):
                sig.put(0)
            elif isinstance(val, str):
                sig.put('')
Exemple #12
0
class DelayTower(TowerBase):
    """
    Delay Tower
    
    Components

    tth : RotationAeroInterlocked
        Rotation axis of the entire delay arm.

    th1 : RotationAero
        Rotation axis of the static crystal.

    th2 : RotationAero
        Rotation axis of the delay crystal.

    x : LinearAero
        Linear stage for insertion/bypass of the tower.

    L : LinearAero
        Linear stage for the delay crystal.

    y1 : TranslationEcc
        Y translation for the static crystal.

    y2 : TranslationEcc
        Y translation for the delay crystal.

    chi1 : GoniometerEcc
        Goniometer on static crystal.

    chi2 : GoniometerEcc
        Goniometer on delay crystal.

    dh : DiodeEcc
        Diode insertion motor.

    diode : HamamatsuDiode
        Diode between the static and delay crystals.

    temp : OmegaRTD
        RTD temperature sensor for the nitrogen.
    """
    # Rotation stages
    tth = Cmp(InterRotationAero, ":TTH", desc="TTH")
    th1 = Cmp(RotationAero, ":TH1", desc="TH1")
    th2 = Cmp(RotationAero, ":TH2", desc="TH2")

    # Linear stages
    x = Cmp(InterLinearAero, ":X", desc="X")
    L = Cmp(InterLinearAero, ":L", desc="L")

    # # Y Crystal motion
    y1 = Cmp(TranslationEcc, ":Y1", desc="Y1")
    y2 = Cmp(TranslationEcc, ":Y2", desc="Y2")

    # Chi motion
    chi1 = Cmp(GoniometerEcc, ":CHI1", desc="CHI1")
    chi2 = Cmp(GoniometerEcc, ":CHI2", desc="CHI2")

    # Diode motion
    dh = Cmp(DiodeEcc, ":DH", desc="DH")

    # # Diode
    # diode = Cmp(HamamatsuDiode, ":DIODE", desc="Tower Diode")

    # # Temperature monitor
    # temp = Cmp(OmegaRTD, ":TEMP", desc="Tower RTD")

    def __init__(self, prefix, *args, **kwargs):
        super().__init__(prefix, *args, **kwargs)
        self._energy_motors = [self.tth, self.th1, self.th2]

    @property
    def position(self):
        """
        Returns the theta position of the arm (tth) in degrees.

        Returns
        -------
        position : float
            Position of the arm in degrees.
        """
        return self.tth.position

    def _get_move_positions(self, E):
        """
        Returns the list of positions that each of the energy motors need to
        move to based on the inputted theta. tth moves to 2*theta while all
        the other motors move to theta.

        Parameters
        ----------
        E : float
            Energy to compute the motor move positions for.

        Returns
        -------
        positions : list
            List of positions each of the energy motors need to move to.
        """
        # Convert to theta
        theta = bragg_angle(E=E)

        # Get the positions
        positions = []
        for motor in self._energy_motors:
            if motor is self.tth:
                positions.append(2 * theta)
            else:
                positions.append(theta)
        return positions

    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 set_length(self, position, wait=False, *args, **kwargs):
        """
        Sets the position of the linear delay stage in mm.

        Parameters
        ----------
        position : float
            Position to move the delay motor to.

        wait : bool, optional
            Wait for motion to complete before returning the console.

        Returns
        -------
        status : MoveStatus
            Status object of the move.
        """
        return self.L.move(position, wait=wait, *args, **kwargs)

    @property
    def length(self):
        """
        Returns the position of the linear delay stage (L) in mm.

        Returns
        -------
        position : float
            Position in mm of the linear delay stage.
        """
        return self.L.position

    @length.setter
    def length(self, position):
        """
        Sets the position of the linear delay stage in mm.

        Parameters
        ----------
        position : float
            Position to move the delay motor to.
        """
        status = self.L.mv(position)

    @property
    def theta(self):
        """
        Bragg angle the tower is currently set to maximize.

        Returns
        -------
        position : float
            Current position of the tower.
        """
        return self.position / 2
Exemple #13
0
class SplitAndDelay(SndDevice):
    """
    Hard X-Ray Split and Delay System.

    Components

    t1 : DelayTower
        Tower 1 in the split and delay system.

    t4 : DelayTower
        Tower 4 in the split and delay system.

    t2 : ChannelCutTower
        Tower 2 in the split and delay system.

    t3 : ChannelCutTower
        Tower 3 in the split and delay system.

    ab : SndPneumatics
        Vacuum device object for the system.

    di : HamamatsuXYMotionCamDiode
        Input diode for the system.

    dd : HamamatsuXYMotionCamDiode
        Diode between the two delay towers.

    do : HamamatsuXYMotionCamDiode
        Output diode for the system.

    dci : HamamatsuXMotionDiode
        Input diode for the channel cut line.

    dcc : HamamatsuXMotionDiode
        Diode between the two channel cut towers.

    dco : HamamatsuXMotionDiode
        Input diode for the channel cut line.

    E1 : Energy1Macro
        Delay energy pseudomotor.

    E2 : Energy2Macro
        Channel cut energy pseudomotor.

    delay : DelayMacro
        Delay pseudomotor.
    """
    tab_component_names = True
    tab_whitelist = [
        'st', 'status', 'diag_status', 'theta1', 'theta2', 'main_screen',
        'status'
    ]
    # Delay Towers
    t1 = Cmp(DelayTower,
             ":T1",
             pos_inserted=21.1,
             pos_removed=0,
             desc="Tower 1")
    t4 = Cmp(DelayTower,
             ":T4",
             pos_inserted=21.1,
             pos_removed=0,
             desc="Tower 4")

    # Channel Cut Towers
    t2 = Cmp(ChannelCutTower,
             ":T2",
             pos_inserted=None,
             pos_removed=0,
             desc="Tower 2")
    t3 = Cmp(ChannelCutTower,
             ":T3",
             pos_inserted=None,
             pos_removed=0,
             desc="Tower 3")

    # Pneumatic Air Bearings
    ab = Cmp(SndPneumatics, "")

    # SnD and Delay line diagnostics
    di = Cmp(HamamatsuXMotionDiode, ":DIA:DI", desc="DI")
    dd = Cmp(HamamatsuXYMotionCamDiode, ":DIA:DD", desc="DD")
    do = Cmp(HamamatsuXMotionDiode, ":DIA:DO", desc="DO")

    # Channel Cut Diagnostics
    dci = Cmp(HamamatsuXMotionDiode, ":DIA:DCI", block_pos=-5, desc="DCI")
    dcc = Cmp(HamamatsuXYMotionCamDiode, ":DIA:DCC", block_pos=-5, desc="DCC")
    dco = Cmp(HamamatsuXMotionDiode, ":DIA:DCO", block_pos=-5, desc="DCO")

    # Macro motors
    E1 = Cmp(Energy1Macro, "", desc="Delay Energy")
    E1_cc = Cmp(Energy1CCMacro, "", desc="CC Delay Energy")
    E2 = Cmp(Energy2Macro, "", desc="CC Energy")
    delay = Cmp(DelayMacro, "", desc="Delay")

    def __init__(self, prefix, name=None, daq=None, RE=None, *args, **kwargs):
        super().__init__(prefix, name=name, *args, **kwargs)
        self.daq = daq
        self.RE = RE
        self._delay_towers = [self.t1, self.t4]
        self._channelcut_towers = [self.t2, self.t3]
        self._towers = self._delay_towers + self._channelcut_towers
        self._delay_diagnostics = [self.di, self.dd, self.do]
        self._channelcut_diagnostics = [self.dci, self.dcc, self.dco]
        self._diagnostics = self._delay_diagnostics + self._channelcut_diagnostics

        # Set the position calculators of dd and dcc
        self.dd.pos_func = lambda: \
            self.E1._get_delay_diagnostic_position()
        self.dcc.pos_func = lambda: \
            self.E2._get_channelcut_diagnostic_position()

    def diag_status(self):
        """
        Prints a string containing the blocking status and the position of the
        motor.
        """
        status = "\n{0}{1:<14}|{2:^16}|{3:^16}\n{4}{5}".format(
            " " * 2, "Diagnostic", "Blocking", "Position", " " * 2, "-" * 50)
        for diag in self._diagnostics:
            status += "\n{0}{1:<14}|{2:^16}|{3:^16.3f}".format(
                " " * 2, diag.desc, str(diag.blocked), diag.x.position)
        logger.info(status)

    @property
    def theta1(self):
        """
        Returns the bragg angle the the delay line is currently set to
        maximize.

        Returns
        -------
        theta1 : float
            The bragg angle the delay line is currently set to maximize
            in degrees.
        """
        return self.t1.theta

    @property
    def theta2(self):
        """
        Returns the bragg angle the the delay line is currently set to
        maximize.

        Returns
        -------
        theta2 : float
            The bragg angle the channel cut line is currently set to maximize
            in degrees.
        """
        return self.t2.theta

    def main_screen(self, print_msg=True):
        """
        Launches the main SnD screen.
        """
        # Get the absolute path to the screen
        path = absolute_submodule_path("hxrsnd/screens/snd_main")
        if print_msg:
            logger.info("Launching expert screen.")
        logger.error('TODO - not yet implemented (path: %s)', path)
        # NOTE: this was the command ,where `p` and `axis` were not defined:
        # os.system("{0} {1} {2} &".format(path, p, axis))

    def status(self, print_status=True):
        """
        Returns the status of the split and delay system.

        Returns
        -------
        Status : str
        """
        status = "Split and Delay System Status\n"
        status += "-----------------------------"
        status = self.E1.status(status, 0, print_status=False)
        status = self.E2.status(status, 0, print_status=False)
        status = self.delay.status(status, 0, print_status=False, newline=True)
        status = self.t1.status(status, 0, print_status=False, newline=True)
        status = self.t2.status(status, 0, print_status=False, newline=True)
        status = self.t3.status(status, 0, print_status=False, newline=True)
        status = self.t4.status(status, 0, print_status=False, newline=True)
        status = self.ab.status(status, 0, print_status=False, newline=False)

        if print_status:
            logger.info(status)
        else:
            logger.debug(status)
            return status
Exemple #14
0
class ProportionalValve(PneuBase):
    """
    Class for the proportional pneumatic valves.

    Components
    ----------
    valve : EpicsSignal
        Valve control and readback pv.
    """
    tab_whitelist = ['close', 'closed', 'open', 'opened', 'position']
    valve = Cmp(EpicsSignal, ":VGP")

    def open(self):
        """
        Closes the valve.
        """
        if self.opened:
            logger.info("Valve currently open.")
        else:
            return self.valve.set(1, timeout=self.set_timeout)

    def close(self):
        """
        Closes the valve.
        """
        if self.closed:
            logger.info("Valve currently closed.")
        else:
            return self.valve.set(0, timeout=self.set_timeout)

    @property
    def position(self):
        """
        Returns the position of the valve.

        Returns
        -------
        position : str
            String saying the current position of the valve. Can be "OPEN" or
            "CLOSED".
        """
        if self.valve.get() == 1:
            return "OPEN"
        elif self.valve.get() == 0:
            return "CLOSED"
        else:
            return "UNKNOWN"

    @property
    def opened(self):
        """
        Returns if the valve is in the opened state.

        Returns
        -------
        opened : bool
            True if the valve is currently in the 'opened' position.
        """
        return (self.position == "OPEN")

    @property
    def closed(self):
        """
        Returns if the valve is in the closed state.

        Returns
        -------
        closed : bool
            True if the valve is currently in the 'closed' position.
        """
        return (self.position == "CLOSED")
Exemple #15
0
class SndPneumatics(SndDevice):
    """
    Class that contains the various pneumatic components of the system.

    Components
    ----------
    t1_valve : ProportionalValve
        Proportional valve on T1.

    t4_valve : ProportionalValve
        Proportional valve on T4.

    vac_valve : ProportionalValve
        Proportional valve on the overall system.

    t1_pressure : PressureSwitch
        Pressure switch on T1.

    t4_pressure : PressureSwitch
        Pressure switch on T4.

    vac_pressure : PressureSwitch
        Pressure switch on the overall system.
    """
    tab_whitelist = ['close', 'open', 'pressures', 'status', 'valves']

    t1_valve = Cmp(ProportionalValve, ":N2:T1", desc="T1 Valve")
    t4_valve = Cmp(ProportionalValve, ":N2:T4", desc="T4 Valve")
    vac_valve = Cmp(ProportionalValve, ":VAC", desc="Vacuum Valve")

    t1_pressure = Cmp(PressureSwitch, ":N2:T1", desc="T1 Pressure")
    t4_pressure = Cmp(PressureSwitch, ":N2:T4", desc="T4 Pressure")
    vac_pressure = Cmp(PressureSwitch, ":VAC", desc="Vacuum Pressure")

    def __init__(self, prefix, name=None, *args, **kwargs):
        super().__init__(prefix, name=name, *args, **kwargs)
        self._valves = [self.t1_valve, self.t4_valve, self.vac_valve]
        self._pressure_switches = [
            self.t1_pressure, self.t4_pressure, self.vac_pressure
        ]

    def status(self, status="", offset=0, print_status=True, newline=False):
        """
        Returns the status of the vacuum system.

        Parameters
        ----------
        status : str, optional
            The string to append the status to.

        offset : int, optional
            Amount to offset each line of the status.

        print_status : bool, optional
            Determines whether the string is printed or returned.

        newline : bool, optional
            Adds a new line to the end of the string.

        Returns
        -------
        status : str
            Status string.
        """
        status += "\n{0}Pneumatics".format(" " * offset)
        status += "\n{0}{1}\n{0}{2:^16}|{3:^16}\n{0}{4}\n".format(
            " " * (offset + 2), "-" * 34, "Device", "State", "-" * 34)
        for valve in self._valves:
            status += valve.status(offset=offset + 2, print_status=False)
        for pressure in self._pressure_switches:
            status += pressure.status(offset=offset + 2, print_status=False)

        if newline:
            status += "\n"
        if print_status is True:
            logger.info(status)
        else:
            return status

    def open(self):
        """
        Opens all the valves in the vacuum system.
        """
        logging.info("Opening valves in SnD system.")
        for valve in self._valves:
            valve.open()

    def close(self):
        """
        Opens all the valves in the vacuum system.
        """
        logging.info("Closing valves in SnD system.")
        for valve in self._valves:
            valve.close()

    @property
    def valves(self):
        """
        Prints the positions of all the valves in the system.
        """
        status = ""
        for valve in self._valves:
            status += valve.status(print_status=False)
        logger.info(status)

    @property
    def pressures(self):
        """
        Prints the pressures of all the pressure switches in the system.
        """
        status = ""
        for pressure in self._pressure_switches:
            status += pressure.status(print_status=False)
        logger.info(status)

    def __repr__(self):
        """
        Returns the status of the device. Alias for status().

        Returns
        -------
        status : str
            Status string.
        """
        return self.status(print_status=False)