Example #1
0
class ZebraINP(Device):
    use = FC(EpicsSignal, '{self.prefix}_ENA:B{self._bindex}')
    source_addr = FC(EpicsSignalWithRBV, '{self.prefix}_INP{self.index}')
    source_str = FC(EpicsSignalRO,
                    '{self.prefix}_INP{self.index}:STR',
                    string=True)
    source_status = FC(EpicsSignalRO, '{self.prefix}_INP{self.index}:STA')
    invert = FC(EpicsSignal, '{self.prefix}_INV:B{self._bindex}')

    def __init__(self,
                 prefix,
                 *,
                 index,
                 read_attrs=None,
                 configuration_attrs=None,
                 **kwargs):
        if read_attrs is None:
            read_attrs = []
        if configuration_attrs is None:
            configuration_attrs = [
                'use', 'source_addr', 'source_str', 'invert'
            ]
        self.index = index
        self._bindex = index - 1
        super().__init__(prefix,
                         read_attrs=read_attrs,
                         configuration_attrs=configuration_attrs,
                         **kwargs)
class ZebraPositionCaptureArm(ZebraPositionCaptureDeviceBase):
    class ZebraArmSignalWithRBV(EpicsSignal):
        def __init__(self, prefix, **kwargs):
            super().__init__(prefix + 'ARM_OUT',
                             write_pv=prefix + 'ARM',
                             **kwargs)

    class ZebraDisarmSignalWithRBV(EpicsSignal):
        def __init__(self, prefix, **kwargs):
            super().__init__(prefix + 'ARM_OUT',
                             write_pv=prefix + 'DISARM',
                             **kwargs)

    arm = FC(ZebraArmSignalWithRBV, '{self._parent_prefix}')
    disarm = FC(ZebraDisarmSignalWithRBV, '{self._parent_prefix}')

    def __init__(self,
                 prefix,
                 *,
                 parent=None,
                 configuration_attrs=None,
                 read_attrs=None,
                 **kwargs):

        self._parent_prefix = parent.prefix

        super().__init__(prefix,
                         read_attrs=read_attrs,
                         configuration_attrs=configuration_attrs,
                         parent=parent,
                         **kwargs)
class ZebraEncoder(Device):
    motor_pos = FC(EpicsSignalRO, '{self._zebra_prefix}M{self.index}:RBV')
    zebra_pos = FC(EpicsSignal, '{self._zebra_prefix}POS{self.index}_SET')
    encoder_res = FC(EpicsSignal, '{self._zebra_prefix}M{self.index}:MRES')
    encoder_off = FC(EpicsSignal, '{self._zebra_prefix}M{self.index}:OFF')
    _copy_pos_signal = FC(EpicsSignal,
                          '{self._zebra_prefix}M{self.index}:SETPOS.PROC')

    def __init__(self,
                 prefix,
                 *,
                 index=None,
                 parent=None,
                 configuration_attrs=None,
                 read_attrs=None,
                 **kwargs):
        if read_attrs is None:
            read_attrs = []
        if configuration_attrs is None:
            configuration_attrs = ['encoder_res', 'encoder_off']

        self.index = index
        self._zebra_prefix = parent.prefix

        super().__init__(prefix,
                         read_attrs=read_attrs,
                         configuration_attrs=configuration_attrs,
                         parent=parent,
                         **kwargs)

    def copy_position(self):
        self._copy_pos_signal.put(1, wait=True)
Example #4
0
class SlitPositioner(FltMvInterface, PVPositioner, Device):
    """
    Abstraction of Slit Axis

    Each adjustable parameter of the slit (center, width) can be modeled as a
    motor in itself, even though each controls two different actual motors in
    reality, this gives a convienent interface for adjusting the aperture size
    and location with out backwards calculating motor positions

    Parameters
    ----------
    prefix : ``str``
        The prefix location of the slits, i.e MFX:DG2

    slit_type : ``'XWIDTH'``, ``'YWIDTH'``, ``'XCENTER'``, ``'YCENTER'``
        The aspect of the slit position you would like to control with this
        specific motor

    name : ``str``
        Alias for the axis

    limits : ``tuple``, optional
        Limits on the motion of the positioner. By default, the limits on the
        setpoint PV are used if None is given.

    See Also
    --------
    ``ophyd.PVPositioner``
        ``SlitPositioner`` inherits directly from ``PVPositioner``.
    """
    setpoint = FC(EpicsSignal, "{self.prefix}:{self._dirshort}_REQ")
    readback = FC(EpicsSignalRO, "{self.prefix}:ACTUAL_{self._dirlong}")
    done = C(EpicsSignalRO, ":DMOV")

    _default_read_attrs = ['readback']

    def __init__(self,
                 prefix,
                 *,
                 slit_type="",
                 name=None,
                 limits=None,
                 **kwargs):
        # Private PV names to deal with complex naming schema
        self._dirlong = slit_type
        self._dirshort = slit_type[:4]
        # Initalize PVPositioner
        super().__init__(prefix, limits=limits, name=name, **kwargs)

    @property
    def egu(self):
        """Engineering units"""
        return self._egu or self.readback._read_pv.units

    def _setup_move(self, position):
        # This is subclassed because we need `wait` to be set to False unlike
        # the default PVPositioner method. `wait` set to True will not return
        # until the move has completed
        logger.debug('%s.setpoint = %s', self.name, position)
        self.setpoint.put(position, wait=False)
Example #5
0
class LaserShutter(InOutPositioner):
    """Controls shutter controlled by Analog Output"""
    # EpicsSignals
    voltage = C(EpicsSignal, '')
    state = FC(AttributeSignal, 'voltage_check')
    # Constants
    out_voltage = 5.0
    in_voltage = 0.0
    barrier_voltage = 1.4

    @property
    def voltage_check(self):
        """Return the position we believe shutter based on the channel"""
        if self.voltage.get() >= self.barrier_voltage:
            return 'OUT'
        else:
            return 'IN'

    def _do_move(self, state):
        """Override to just put to the channel"""
        if state.name == 'IN':
            self.voltage.put(self.in_voltage)
        elif state.name == 'OUT':
            self.voltage.put(self.out_voltage)
        else:
            raise ValueError("%s is in an invalid state", state)
Example #6
0
def mps_factory(clsname, cls, *args, mps_prefix, veto=False, **kwargs):
    """
    Create a new object of arbitrary class capable of storing MPS information

    A new class identical to the provided one is created, but with additional
    attribute ``mps`` that relies upon the provided ``mps_prefix``. All other
    information is passed through to the class constructor as args and kwargs

    Parameters
    ----------
    clsname : ``str``
        Name of new class to create

    cls : ``type``
        Device class to add ``mps``

    mps_prefix : ``str``
        Prefix for MPS subcomponent

    veto : ``bool``, optional
        Whether the MPS bit is capable of veto

    args :
        Passed to device constructor

    kwargs:
        Passed to device constructor
    """
    comp = FC(MPS, mps_prefix, veto=veto)
    cls = type(clsname, (cls, ), {'mps': comp})
    return cls(*args, **kwargs)
Example #7
0
class ZebraPulse(Device):
    width = Cpt(ZebraSignalWithRBV, 'WID')
    input_addr = Cpt(ZebraSignalWithRBV, 'INP')
    input_str = Cpt(EpicsSignalRO, 'INP:STR', string=True)
    input_status = Cpt(EpicsSignalRO, 'INP:STA')
    delay = Cpt(ZebraSignalWithRBV, 'DLY')
    delay_sync = Cpt(EpicsSignal, 'DLY:SYNC')
    time_units = Cpt(ZebraSignalWithRBV, 'PRE', string=True)
    output = Cpt(EpicsSignal, 'OUT')

    input_edge = FC(EpicsSignal,
                    '{self._zebra_prefix}POLARITY:{self._edge_addr}')

    _edge_addrs = {1: 'BC',
                   2: 'BD',
                   3: 'BE',
                   4: 'BF',
                   }

    def __init__(self, prefix, *, index=None, parent=None,
                 configuration_attrs=None, read_attrs=None, **kwargs):
        if read_attrs is None:
            read_attrs = ['input_status', 'output']
        if configuration_attrs is None:
            configuration_attrs = list(self.component_names) + ['input_edge']

        zebra = parent
        self.index = index
        self._zebra_prefix = zebra.prefix
        self._edge_addr = self._edge_addrs[index]

        super().__init__(prefix, configuration_attrs=configuration_attrs,
                         read_attrs=read_attrs, parent=parent, **kwargs)
Example #8
0
class ZebraGateInput(Device):
    addr = Cpt(ZebraSignalWithRBV, '')
    string = Cpt(EpicsSignalRO, ':STR', string=True)
    status = Cpt(EpicsSignalRO, ':STA')
    sync = Cpt(EpicsSignal, ':SYNC')
    write_input = Cpt(EpicsSignal, ':SET')

    # Input edge index depends on the gate number (these are set in __init__)
    edge = FC(EpicsSignal,
              '{self._zebra_prefix}POLARITY:B{self._input_edge_idx}')

    def __init__(self, prefix, *, index=None, parent=None,
                 configuration_attrs=None, read_attrs=None, **kwargs):
        if read_attrs is None:
            read_attrs = ['status']
        if configuration_attrs is None:
            configuration_attrs = ['addr', 'edge']

        gate = parent
        zebra = gate.parent

        self.index = index
        self._zebra_prefix = zebra.prefix
        self._input_edge_idx = gate._input_edge_idx[self.index]

        super().__init__(prefix, read_attrs=read_attrs,
                         configuration_attrs=configuration_attrs,
                         parent=parent, **kwargs)
Example #9
0
class Gantry(OMMotor):
    """
    Gantry Axis

    The horizontal and vertical motion of the OffsetMirror are controlled by
    two coupled stepper motors. Instructions are sent to both by simply
    requesting a move on the primary so they are represented here as a single
    motor with additional diagnostics and interlock

    Parameters
    ----------
    prefix : str
        Base prefix for both stepper motors i.e XRT:M1H. Do not include the "P"
        or "S" to indicate primary or secondary steppers

    gantry_prefix : str, optional
        Prefix for the shared gantry diagnostics if it is different than the
        stepper motor prefix
    """
    # Readbacks for gantry information
    gantry_difference = FC(EpicsSignalRO, "{self.gantry_prefix}:GDIF")
    decoupled = FC(EpicsSignalRO, "{self.gantry_prefix}:DECOUPLE")
    # Readbacks for the secondary motor
    follower_readback = FC(EpicsSignalRO, "{self.follow_prefix}:RBV")
    follower_low_limit_switch = FC(EpicsSignalRO, "{self.follow_prefix}:LLS")
    follower_high_limit_switch = FC(EpicsSignalRO, "{self.follow_prefix}:HLS")

    _default_read_attrs = ['readback', 'setpoint', 'gantry_difference']

    def __init__(self, prefix, *, gantry_prefix=None, **kwargs):
        self.gantry_prefix = gantry_prefix or 'GANTRY:' + prefix
        self.follow_prefix = prefix + ':S'
        super().__init__(prefix + ':P', **kwargs)

    def check_value(self, pos):
        """
        Add additional check for the gantry coupling

        This is not a safety measure, but instead just here largely
        for bookkeeping and to give the operator further feedback on why the
        requested move is not completed.
        """
        # Check that the gantry is not decoupled
        if self.decoupled.get():
            raise PermissionError("The gantry is not currently coupled")
        # Allow OMMotor to check the value
        super().check_value(pos)
Example #10
0
class MockDevice(Device):
    # Device signals
    readback = C(RandomSignal)
    noise = C(RandomSignal)
    transmorgifier = C(SignalRO, value=4)
    setpoint = C(Signal, value=0)
    velocity = C(Signal, value=1)
    flux = C(RandomSignal)
    modified_flux = C(RandomSignal)
    capacitance = C(RandomSignal)
    acceleration = C(Signal, value=3)
    limit = C(Signal, value=4)
    inductance = C(RandomSignal)
    transformed_inductance = C(SignalRO, value=3)
    core_temperature = C(RandomSignal)
    resolution = C(Signal, value=5)
    duplicator = C(Signal, value=6)

    # Component Motors
    x = FC(ConfiguredSynAxis, name='X Axis')
    y = FC(ConfiguredSynAxis, name='Y Axis')
    z = FC(ConfiguredSynAxis, name='Z Axis')

    # Default Signal Sorting
    _default_read_attrs = [
        'readback', 'setpoint', 'transmorgifier', 'noise', 'inductance'
    ]
    _default_configuration_attrs = [
        'flux', 'modified_flux', 'capacitance', 'velocity', 'acceleration'
    ]

    def insert(self,
               width: float = 2.0,
               height: float = 2.0,
               fast_mode: bool = False):
        """Fake insert function to display"""
        pass

    def remove(self, height: float, fast_mode: bool = False):
        """Fake remove function to display"""
        pass

    @property
    def hints(self):
        return {'fields': [self.name + '_readback']}
Example #11
0
class Pitch(OMMotor):
    """
    HOMS Pitch Mechanism

    The axis is actually a piezo actuator and a stepper motor in series, and
    this is reflected in the PV naming
    """
    __doc__ += basic_positioner_init

    piezo_volts = FC(EpicsSignalRO, "{self._piezo}:VRBV")
    stop_signal = FC(EpicsSignal, "{self._piezo}:STOP")

    # TODO: Limits will be added soon, but not present yet

    def __init__(self, prefix, **kwargs):
        # Predict the prefix of all piezo pvs
        self._piezo = prefix.replace('MIRR', 'PIEZO')
        super().__init__(prefix, **kwargs)
Example #12
0
class MockDevice(Device):
    # Device signals
    readback = Cpt(RandomSignal, kind='normal')
    noise = Cpt(RandomSignal, kind='normal')
    transmorgifier = Cpt(SignalRO, value=4, kind='normal')
    setpoint = Cpt(Signal, value=0, kind='normal')

    velocity = Cpt(Signal, value=1, kind='config')
    flux = Cpt(RandomSignal, kind='config')
    modified_flux = Cpt(RandomSignal, kind='config')
    capacitance = Cpt(RandomSignal, kind='config')
    acceleration = Cpt(Signal, value=3, kind='config')
    limit = Cpt(Signal, value=4, kind='config')
    inductance = Cpt(RandomSignal, kind='normal')

    transformed_inductance = Cpt(SignalRO, value=3, kind='omitted')
    core_temperature = Cpt(RandomSignal, kind='omitted')
    resolution = Cpt(Signal, value=5, kind='omitted')
    duplicator = Cpt(Signal, value=6, kind='omitted')

    # Component Motors
    x = FC(ConfiguredSynAxis, name='X Axis')
    y = FC(ConfiguredSynAxis, name='Y Axis')
    z = FC(ConfiguredSynAxis, name='Z Axis')

    def insert(self,
               width: float = 2.0,
               height: float = 2.0,
               fast_mode: bool = False):
        """Fake insert function to display"""
        pass

    def remove(self, height: float, fast_mode: bool = False):
        """Fake remove function to display"""
        pass

    @property
    def hints(self):
        return {'fields': [self.name + '_readback']}
Example #13
0
class MockDevice(Device):
    # Device signals
    read1 = C(EpicsSignalRO, ':READ1')
    read2 = C(EpicsSignalRO, ':READ2')
    read3 = C(EpicsSignalRO, ':READ3')
    read4 = C(EpicsSignal, ':READ4')
    read5 = C(EpicsSignal, ':READ5', write_pv=':WRITE5')
    config1 = C(EpicsSignalRO, ':CFG1')
    config2 = C(EpicsSignalRO, ':CFG2')
    config3 = C(EpicsSignalRO, ':CFG3')
    config4 = C(EpicsSignal, ':CFG4')
    config5 = C(EpicsSignal, ':CFG5', write_pv=':CFGWRITE5')
    misc1 = C(EpicsSignalRO, ':MISC1')
    misc2 = C(EpicsSignalRO, ':MISC2')
    misc3 = C(EpicsSignalRO, ':MISC3')
    misc4 = C(EpicsSignal, ':MISC4')
    misc5 = C(EpicsSignal, ':MISC5', write_pv=':MISCWRITE5')

    # Component Motors
    x = FC(EpicsMotor, 'Tst:MMS:X', name='X Axis')
    y = FC(EpicsMotor, 'Tst:MMS:Y', name='Y Axis')
    z = FC(EpicsMotor, 'Tst:MMS:Z', name='Z Axis')

    # Default Signal Sorting
    _default_read_attrs = ['read1', 'read2', 'read3', 'read4', 'read5']
    _default_configuration_attrs = [
        'config1', 'config2', 'config3', 'config4', 'config5'
    ]

    def insert(self,
               width: float = 2.0,
               height: float = 2.0,
               fast_mode: bool = False):
        """Fake insert function to display"""
        pass

    def remove(self, height: float, fast_mode: bool = False):
        """Fake remove function to display"""
        pass
Example #14
0
class Lens(InOutRecordPositioner):
    """
    Data structure for basic Lens object

    Parameters
    ----------
    prefix : str
        Name of the state record that controls the PV

    prefix_lens : str
        Prefix for the PVs that contain focusing information
    """
    _sig_radius = FC(EpicsSignalRO, "{self.prefix_lens}:RADIUS",
                     auto_monitor=True)
    _sig_z = FC(EpicsSignalRO, "{self.prefix_lens}:Z",
                auto_monitor=True)
    _sig_focus = FC(EpicsSignalRO, "{self.prefix_lens}:FOCUS",
                    auto_monitor=True)
    # Default configuration attributes. Read attributes are set correctly by
    # InOutRecordPositioner
    _default_configuration_attrs = ['_sig_radius', '_sig_z']

    def __init__(self, prefix, prefix_lens, **kwargs):
        self.prefix_lens = prefix_lens
        super().__init__(prefix, **kwargs)

    @property
    def radius(self):
        """
        Method converts the EPICS lens radius signal into a float that can be
        used for calculations.

        Returns
        -------
        float
            Returns the radius of the lens
        """
        return self._sig_radius.value

    @property
    def z(self):
        """
        Method converts the z position EPICS signal into a float.

        Returns
        -------
        float
            Returns the z position of the lens in meters along the beamline
        """
        return self._sig_z.value

    @property
    def focus(self):
        """
        Method converts the EPICS focal length signal of the lens into a float

        Returns
        -------
        float
            Returns the focal length of the lens in meters
        """
        return self._sig_focus.value

    def image_from_obj(self, z_obj):
        """
        Method calculates the image distance in meters along the beam pipeline
        from a point of origin given the focal length of the lens, location of
        lens, and location of object.

        Parameters
        ----------
        z_obj
            Location of object along the beamline in meters (m)

        Returns
        -------
        image
            Returns the distance z_im of the image along the beam pipeline from
            a point of origin in meters (m)
        Note
        ----
        If the location of the object (z_obj) is equal to the focal length of
        the lens, this function will return infinity.
        """
        # Check if the lens object is at the focal length
        # If this happens, then the image location will be infinity.
        # Note, this should not effect the recursive calculations that occur
        # later in the code
        if z_obj == self.focus:
            return np.inf
        # Find the object location for the lens
        obj = self.z - z_obj
        # Calculate the location of the focal plane
        plane = 1/(1/self.focus - 1/obj)
        # Find the position in accelerator coordinates
        return plane + self.z
class DualAdcFS(TriggerAdc):
    '''
    Adc Device, when read, returns references to data in filestore.
        This is for a dual device. It defines one ADC which
            uses a shared triggering mechanism for file writing.
        The adc is either a 'master' or 'slave'. If 'master', then kickoff()
            should trigger the 'Ena-Sel' PV (which starts collecting data to file).
        If 'slave', then it should check that the 'Ena-Sel' PV is set.
        TODO : Need to add a good waiting mechanism for this.
    '''
    # these are for the dual ADC FS
    # column is the column and enable_sel is what triggers the collection
    # rename because of existing children pv's
    chunk_size = 2**20
    write_path_template = '/nsls2/xf07bm/data/pizza_box_data/%Y/%m/%d/'
    volt = FC(EpicsSignal, '{self._adc_read}}}E-I')
    offset = FC(EpicsSignal, '{self._adc_read}}}Offset')
    dev_name = FC(EpicsSignal, '{self._adc_read}}}DevName')

    def __init__(self,
                 *args,
                 adc_column,
                 adc_read_name,
                 twin_adc=None,
                 reg,
                 **kwargs):
        '''
            This is for a dual ADC system.
            adc_column : the column
            adc_trigger: the adc pv used for triggering for this dual channel
            mode : {'master', 'slave'}
                The mode. Master will create a new file and resource during kickoff
                    Slave assumes the new file and resource are already created.
                    Slave should check if acquisition has been triggered first,
                    else return an error.

            Notes:
                The adc master and adc for triggering are not necessarily the same
                    (for ex, adc6 and adc7 triggered using adc1, but adc6 is
                     what will put to adc1's trigger)
        '''
        self._twin_adc = twin_adc
        self._reg = reg
        self._column = adc_column
        self._adc_read = adc_read_name
        self._staged_adc = False
        self._kickoff_adc = False
        self._complete_adc = False
        super().__init__(*args, **kwargs)

    def stage(self):
        "Set the filename and record it in a 'resource' document in the filestore database."

        if self.connected:
            # NOTE: master MUST be done before slave. need to fix this later
            if self._twin_adc is None:
                raise ValueError("Error a twin ADC must be given")

            # if twin didnt stage yet, stage
            if not self._twin_adc._staged_adc:
                self._staged_adc = True

                print(self.name, 'stage')
                DIRECTORY = datetime.now().strftime(self.write_path_template)
                #DIRECTORY = "/nsls2/xf07bm/data/pb_data"

                filename = 'an_' + str(uuid.uuid4())[:6]
                self._full_path = os.path.join(
                    DIRECTORY, filename)  # stash for future reference
                print(">>>>>>>>>>>>>>> writing to {}".format(self._full_path))

                self.filepath.put(self._full_path)
                self.resource_uid = self._reg.register_resource(
                    'PIZZABOX_AN_FILE_TXT', DIRECTORY, self._full_path,
                    {'chunk_size': self.chunk_size})

                super().stage()
            else:
                # don't stage, use twin's info
                self.resource_uid = self._twin_adc.resource_uid
                self._full_path = self._twin_adc._full_path
                # reset twin
                self._twin_adc._staged_adc = False
                print(
                    "ACD {}'s twin {} already staged. File path already set to {}"
                    .format(self.name, self._twin_adc.name,
                            self.filepath.get()))
        else:
            msg = "Error, adc {} not ready for acquiring\n".format(self.name)
            raise ValueError(msg)
        time.sleep(.1)

    def unstage(self):
        if (self.connected):
            set_and_wait(self.enable_sel, 1)
            # either master or slave can unstage if needed, safer
            self._staged_adc = False
            self._kickoff_adc = False
            self._complete_adc = False
            return super().unstage()

    def kickoff(self):
        print('kickoff', self.name)
        if self._twin_adc is None:
            raise ValueError("ADC must have a twin")

        if self._twin_adc._kickoff_adc is False:
            self._ready_to_collect = True
            "Start writing data into the file."

            # set_and_wait(self.enable_sel, 0)
            st = self.enable_sel.set(0)
            self._kickoff_adc = True
            return st
        else:
            print("ADC {} was kicked off by {} already".format(
                self.name, self._twin_adc.name))
            self._ready_to_collect = True
            #reset kickoff
            self._kickoff_adc = False
            #reset twin
            self._twin_adc._kickoff_adc = False
            st = Status()
            st._finished()
            return st

    def complete(self):
        print('complete', self.name, '| filepath', self._full_path)
        if not self._ready_to_collect:
            raise RuntimeError(
                "must called kickoff() method before calling complete()")
        if self._twin_adc._complete_adc is False:
            # Stop adding new data to the file.
            #set_and_wait(self.enable_sel, 1)
            self.enable_sel.put(1)
            self._complete_adc = True
        else:
            print("Device already stopped by {}".format(self._twin_adc.name))
            self._twin_adc._complete_adc = False
            self._complete_adc = False

        return NullStatus()

    def collect(self):
        """
        Record a 'datum' document in the filestore database for each encoder.

        Return a dictionary with references to these documents.
        """
        print('collect', self.name)
        self._ready_to_collect = False

        # Create an Event document and a datum record in filestore for each line
        # in the text file.
        now = ttime.time()
        ttime.sleep(1)  # wait for file to be written by pizza box
        if os.path.isfile(self._full_path):
            with open(self._full_path, 'r') as f:
                linecount = 0
                for ln in f:
                    linecount += 1

            chunk_count = linecount // self.chunk_size + int(
                linecount % self.chunk_size != 0)
            for chunk_num in range(chunk_count):
                datum_uid = self._reg.register_datum(self.resource_uid, {
                    'chunk_num': chunk_num,
                    'column': self._column
                })
                data = {self.name: datum_uid}

                yield {
                    'data': data,
                    'timestamps': {key: now
                                   for key in data},
                    'time': now
                }
        else:
            print('collect {}: File was not created'.format(self.name))
            print("filename : {}".format(self._full_path))

    def describe_collect(self):
        # TODO Return correct shape (array dims)
        now = ttime.time()
        return {
            self.name: {
                self.name: {
                    'filename': self._full_path,
                    'devname': self.dev_name.value,
                    'source': 'pizzabox-adc-file',
                    'external': 'FILESTORE:',
                    'shape': [
                        5,
                    ],
                    'dtype': 'array'
                }
            }
        }
Example #16
0
class Xspress3ROI(ADBase):
    '''A configurable Xspress3 EPICS ROI'''

    # prefix: C{channel}_   MCA_ROI{self.roi_num}
    bin_low = FC(SignalWithRBV, '{self.channel.prefix}{self.bin_suffix}_LLM')
    bin_high = FC(SignalWithRBV, '{self.channel.prefix}{self.bin_suffix}_HLM')

    # derived from the bin signals, low and high electron volt settings:
    ev_low = Cpt(EvSignal, parent_attr='bin_low')
    ev_high = Cpt(EvSignal, parent_attr='bin_high')

    # C{channel}_  ROI{self.roi_num}
    value = Cpt(EpicsSignalRO, 'Value_RBV')
    value_sum = Cpt(EpicsSignalRO, 'ValueSum_RBV')

    enable = Cpt(SignalWithRBV, 'EnableCallbacks')
    # ad_plugin = Cpt(Xspress3ROISettings, '')

    @property
    def ad_root(self):
        root = self.parent
        while True:
            if not isinstance(root.parent, ADBase):
                return root
            root = root.parent

    def __init__(self,
                 prefix,
                 *,
                 roi_num=0,
                 use_sum=False,
                 read_attrs=None,
                 configuration_attrs=None,
                 parent=None,
                 bin_suffix=None,
                 **kwargs):

        if read_attrs is None:
            if use_sum:
                read_attrs = ['value_sum']
            else:
                read_attrs = ['value', 'value_sum']

        if configuration_attrs is None:
            configuration_attrs = ['ev_low', 'ev_high', 'enable']

        rois = parent
        channel = rois.parent
        self._channel = channel
        self._roi_num = roi_num
        self._use_sum = use_sum
        self._ad_plugin = getattr(rois, 'ad_attr{:02d}'.format(roi_num))

        if bin_suffix is None:
            bin_suffix = 'MCA_ROI{}'.format(roi_num)

        self.bin_suffix = bin_suffix

        super().__init__(prefix,
                         parent=parent,
                         read_attrs=read_attrs,
                         configuration_attrs=configuration_attrs,
                         **kwargs)

    @property
    def settings(self):
        '''Full areaDetector settings'''
        return self._ad_plugin

    @property
    def channel(self):
        '''The Xspress3Channel instance associated with the ROI'''
        return self._channel

    @property
    def channel_num(self):
        '''The channel number associated with the ROI'''
        return self._channel.channel_num

    @property
    def roi_num(self):
        '''The ROI number'''
        return self._roi_num

    def clear(self):
        '''Clear and disable this ROI'''
        self.configure(0, 0)

    def configure(self, ev_low, ev_high):
        '''Configure the ROI with low and high eV

        Parameters
        ----------
        ev_low : int
            low electron volts for ROI
        ev_high : int
            high electron volts for ROI
        '''
        ev_low = int(ev_low)
        ev_high = int(ev_high)

        enable = 1 if ev_high > ev_low else 0
        changed = any([
            self.ev_high.get() != ev_high,
            self.ev_low.get() != ev_low,
            self.enable.get() != enable
        ])

        if not changed:
            return

        logger.debug(
            'Setting up EPICS ROI: name=%s ev=(%s, %s) '
            'enable=%s prefix=%s channel=%s', self.name, ev_low, ev_high,
            enable, self.prefix, self._channel)
        if ev_high <= self.ev_low.get():
            self.ev_low.put(0)

        self.ev_high.put(ev_high)
        self.ev_low.put(ev_low)
        self.enable.put(enable)
Example #17
0
class BlockWatch(Device):
    """
    Class to monitor Sequencer and Analog Input

    The BlockWatch has a constant monitor callback on both the sequencer and
    the analog input calling :meth:`.process_event` whenever one of these
    changes. The handling of these signals is as follows; if the analog input
    is below a certain threshold and the sequencer is off a soft trip will be
    reported. This is simply to alert the operator, but no preventative measure
    will be taken. If the sequencer is running, and the signal is below the
    :attr:`.threshold`, we report a hard trip, and close the filter to protect
    our detector.

    In order to give the operator some control and readback, the status of
    the watcher is reported into a few soft PVS. In addition to the soft and
    hard trips, there is a string pv so that the status can be displayed.
    Finally, there is a quick enable toggle. If this is set to False, the logic
    will process the same, but motion of the attenuator will be prevented

    Parameters
    ----------
    prefix : str
        Base of the PV Notepad PVs

    ai : str
        Analog input channel of RoadRunner monitor

    fltr : str
        Base name of attenuator filter

    sequencer : str
        Base name of sequencer

    threshold : float, optional
        Minimum threshold to allow RoadRunner signal before tripping the watch
    """
    #Soft PVs
    soft_trip = C(EpicsSignal, ':SOFT_TRIP')
    hard_trip = C(EpicsSignal, ':HARD_TRIP')
    enabled = C(EpicsSignalRO, ':ENABLE', auto_monitor=True)
    #Real PVs
    seq_run = FC(EpicsSignalRO, '{self._sequencer}:PLSTAT', auto_monitor=True)
    seq_cnt = FC(EpicsSignalRO, '{self._sequencer}:CURSTP', auto_monitor=True)
    ai = FC(EpicsSignal, '{self._ai}', auto_monitor=True)
    #Blocker
    blocker = FC(Filter, '{self._filter}')

    def __init__(self, prefix, ai, fltr, sequencer, threshold=1):
        self.threshold = threshold
        #Store EPICS prefixes
        self._filter = fltr
        self._ai = ai
        self._sequencer = sequencer
        #Initialize ophyd device
        super().__init__(prefix)

    @property
    def sequencer_running(self):
        """
        Return whether the sequencer is running

        The play status of the sequencer is modified as well as the current
        step. This is done to avoid race conditions with the start of the
        sequencer and the beginning of the roadrunnner scan
        """
        return self.seq_run.value == 2 and self.seq_cnt.value > 25

    @property
    def signal_present(self):
        """
        Assert whether we have a signal over :attr:`.threshold`
        """
        return self.ai.value > self.threshold

    def remove(self):
        """
        Remove the blocker if it is not removed from the beam
        """
        if self.enabled.value == 1:
            self.blocker.move_out()

    def block(self):
        """
        Block the beam if the filter is not already inserted
        """
        if self.enabled.value == 1:
            self.blocker.move_in()

    def process_event(self, *args, **kwargs):
        """
        Process a change in state if either the sequencer or ai changes state
        """
        #If we are not receiving signal
        if not self.signal_present:
            #If the sequencer is running, hard-fault
            if self.sequencer_running:
                self.hard_trip.put(1)
                self.block()
            #Otherwise, alert the soft trip
            else:
                self.soft_trip.put(1)
        #Otherwise if we are seeing a signal
        else:
            #Remove our blocker
            self.remove()
            #Reset our faults
            self.soft_trip.put(0)
            self.hard_trip.put(0)

    def run(self):
        """
        Repeatedly run :func:`.process_event`
        """
        while True:
            try:
                self.process_event()
            except KeyboardInterrupt:
                print("Watch has been manually interrupted!")
                break
Example #18
0
class OffsetMirror(Device):
    """
    X-Ray offset mirror class.

    This is for each individual mirror system used in the FEE
    and XRT. Controls for the pitch, and primary gantry x and y motors are
    included.

    When controlling the pitch motor, if the piezo is set to 'PID' mode, then
    the pitch mechanism is setup to first move the stepper as close to the
    desired position, then the piezo will kick in to constantly try and correct
    any positional changes.

    Parameters
    ----------
    prefix : str
        The EPICS base PV of the pitch motor

    prefix_xy : str
        The EPICS base PV of the gantry x and y gantry motors

    xgantry_prefix : str
        The name of the horizontal gantry if not identical to the prefix

    name : str
        The name of the offset mirror
    """
    # Pitch Motor
    pitch = FC(Pitch, "MIRR:{self.prefix}")
    # Gantry motors
    xgantry = FC(Gantry,
                 "{self._prefix_xy}:X",
                 gantry_prefix="{self._xgantry}",
                 add_prefix=['suffix', 'gantry_prefix'])
    ygantry = FC(Gantry,
                 "{self._prefix_xy}:Y",
                 gantry_prefix='GANTRY:{self.prefix}:Y',
                 add_prefix=['suffix', 'gantry_prefix'])
    # Transmission for Lightpath Interface
    transmission = 1.0

    _default_read_attrs = [
        'pitch.readback', 'xgantry.readback', 'xgantry.gantry_difference'
    ]

    _default_configuration_attrs = ['ygantry.setpoint']

    def __init__(self,
                 prefix,
                 *,
                 prefix_xy=None,
                 xgantry_prefix=None,
                 **kwargs):
        # Handle prefix mangling
        self._prefix_xy = prefix_xy or prefix
        self._xgantry = xgantry_prefix or 'GANTRY:' + prefix + ':X'
        super().__init__(prefix, **kwargs)

    @property
    def inserted(self):
        """
        Treat OffsetMirror as always inserted
        """
        return True

    @property
    def removed(self):
        """
        Treat OffsetMirror as always inserted
        """
        return False