class SlitDownstream(MotorBundle): """Downstream slit that controls the four blades that form the beam""" h_ib = Component(EpicsMotor, "6idhedm:m35", name='h_ib') h_ob = Component(EpicsMotor, "6idhedm:m36", name='h_ob') # h_size = Component(EpicsMotor, "1ide1:Kohzu_E_dnHsize.VAL", name='h_size' ) # horizontal size !!!!!need to check this v_tp = Component(EpicsMotor, "6idhedm:m33", name='v_tp') v_bt = Component(EpicsMotor, "6idhedm:m34", name='v_bt')
class KepkoController(Device): voltage = Component(LocalPositioner, '', progtype='V') current = Component(LocalPositioner, '', progtype='C') mode = Component( EpicsSignal, 'setMode', kind='config', string=True, auto_monitor=True, labels=('kepko', 'magnet') ) remote = Component( EpicsSignal, 'setRemote', kind='config', string=True, auto_monitor=True, labels=('kepko', 'magnet') ) enable = Component( EpicsSignal, 'Enable.VAL', kind='omitted', string=True, labels=('kepko', 'magnet') ) @mode.sub_value def mode_change(self, value=None, **kwargs): if value == 'Current': self.current.readback.kind = Kind.hinted self.voltage.readback.kind = Kind.normal if value == 'Voltage': self.current.readback.kind = Kind.normal self.voltage.readback.kind = Kind.hinted
class TomoStage(MotorBundle): #rotation preci = Component(EpicsMotor, "6bmpreci:m1", name='preci') samX = Component(EpicsMotor, "6bma1:m19", name='samX') ksamX = Component(EpicsMotor, "6bma1:m11", name='ksamX') ksamZ = Component(EpicsMotor, "6bma1:m12", name='ksamZ') samY = Component(EpicsMotor, "6bma1:m18", name="samY")
class MyKohzu(KohzuSeqCtl_Monochromator): m_theta = Component(EpicsMotor, "m45", kind="normal") m_y = Component(EpicsMotor, "m46", kind="normal") m_z = Component(EpicsMotor, "m47", kind="normal") def into_control_range(self, p_theta=2, p_y=-15, p_z=90): """ Move the Kohzu motors into range so the energy controls will work. Written as a bluesky plan so that all motors can be moved simultaneously. Return early if the motors are already in range. USAGE:: RE(dcm.into_control_range()) """ args = [] if self.m_theta.position < p_theta: args += [self.m_theta, p_theta] if self.m_y.position > p_y: args += [self.m_y, p_y] if self.m_z.position < p_z: args += [self.m_z, p_z] if (len(args) == 0): # all motors in range, no work to do, MUST yield something yield from bps.null() return yield from bps.sleep(1) # allow IOC to react yield from bps.mv(self.operator_acknowledge, 1, self.mode, "Auto") def stop(self): self.m_theta.stop() self.m_y.stop() self.m_z.stop()
class ROIDevice(Device): # TODO: Using locals() feels like cheating... # Make 32 ROIs --> same number as in EPICS support. for i in range(1, 33): locals()['roi{:02d}'.format(i)] = Component(Xspress3ROI, f'{i}:') num_rois = Component(Signal, value=32, kind='config')
class FilterBank(Device): """ Filter bank with 4 slots """ slot1 = Component(OneSlot, 'bo', num=1) slot2 = Component(OneSlot, 'bo', num=2) slot3 = Component(OneSlot, 'bo', num=3) slot4 = Component(OneSlot, 'bo', num=4)
class SampleRotatorHoming(Device): forward = Component(EpicsSignal, ".HOMF", kind='omitted', auto_monitor=True) reverse = Component(EpicsSignal, ".HOMR", kind='omitted', auto_monitor=True) def set(self, value, timeout=10): """Find the Home pulse in either forward or reverse direction.""" if not hasattr(self, value): raise KeyError("either 'forward' or 'reverse'" f", not: '{value}'") signal = getattr(self, value) st = Status(self, timeout=timeout) def put_cb(**kwargs): st._finished(success=True) signal.put(1, use_complete=True, callback=put_cb) st.wait(timeout=timeout) return st
class MyHDF5Plugin(HDF5Plugin, FileStoreHDF5IterativeWrite): create_directory_depth = Component(EpicsSignalWithRBV, suffix="CreateDirectory") array_callbacks = Component(EpicsSignalWithRBV, suffix="ArrayCallbacks") pool_max_buffers = None file_number_sync = None file_number_write = None
class Trajectories(Device): """fly scan trajectories""" ar = Component(EpicsSignal, "9idcLAX:traj1:M1Traj") ay = Component(EpicsSignal, "9idcLAX:traj3:M1Traj") dy = Component(EpicsSignal, "9idcLAX:traj2:M1Traj") num_pulse_positions = Component(EpicsSignal, "9idcLAX:traj1:NumPulsePositions")
class UserCalc(Device): channels = DynamicDeviceComponent(_sc_chans('chan', list(ascii_uppercase)[:12]), kind=Kind.config) scan = Component(EpicsSignal, '.SCAN', kind=Kind.omitted) expression = Component(EpicsSignal, '.CALC$', string=True, kind=Kind.config) calc_value = Component(EpicsSignal, '.VAL', kind=Kind.hinted) enable = Component(EpicsSignal, '.DESC', string=True, kind=Kind.config) # Dummy signal for compatibility preset_monitor = Component(Signal, value=0, kind='omitted') def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._read_attrs = ['calc_value'] @property def read_attrs(self): return self._read_attrs @read_attrs.setter def read_attrs(self, value): if type(value) is not list: raise TypeError('read_attrs has to be a list!') else: self._read_attrs = value def SetCountTimePlan(self, value, **kwargs): yield from mv(self.preset_monitor, value, **kwargs)
class DCM_Feedback(Device): """ monochromator EPID-record-based feedback program: fb_epid """ control = Component(EpicsSignal, "") on = Component(EpicsSignal, ":on") drvh = Component(EpicsSignal, ".DRVH") drvl = Component(EpicsSignal, ".DRVL") oval = Component(EpicsSignal, ".OVAL") @property def is_on(self): return self.on.get() == 1 @run_in_thread def _send_emails(self, subject, message): email_notices.send(subject, message) def check_position(self): diff_hi = self.drvh.get() - self.oval.get() diff_lo = self.oval.get() - self.drvl.get() if min(diff_hi, diff_lo) < 0.2: subject = "USAXS Feedback problem" message = "Feedback is very close to its limits." if email_notices.notify_on_feedback: self._send_emails(subject, message) logger.warning("!" * 15) logger.warning(subject, message) logger.warning("!" * 15)
class TomoStage(MotorBundle): #rotation preci = Component(EpicsMotor, _devices['tomo_stage']['preci'], name='preci') samX = Component(EpicsMotor, _devices['tomo_stage']['samX' ], name='samX' ) ksamX = Component(EpicsMotor, _devices['tomo_stage']['ksamX'], name='ksamX') ksamZ = Component(EpicsMotor, _devices['tomo_stage']['ksamZ'], name='ksamZ') samY = Component(EpicsMotor, _devices['tomo_stage']["samY" ], name="samY" )
class MainShutter6IDD(ShutterBase): """ Ophyd class for the main shutter at 6IDD NOTE: There is some dealy (>2s) during the opening and closing of the main shutter, therefore frequent toggling of the main shutter is highly discouraged. """ open_signal = Component(EpicsSignal, "AcO8") close_signal = Component(EpicsSignal, "AcO7") state_signal = EpicsSignalRO("PA:06ID:STA_D_SDS_OPEN_PL.VAL") delay_time = 1.5 # seconds @property def state(self): return self.state_signal.get() def open(self, timeout=10): self.open_signal.put(1) # send out open request if self.delay_time > 0: import time time.sleep(self.delay_time) if self.open_signal.get() == 1: self.open_signal.put( 0) # cancel the open request, shutter is open now def close(self, timeout=10): self.close_signal.put(1) # send out the close request if self.delay_time > 0: import time time.sleep(self.delay_time) if self.close_signal.get() == 1: self.close_signal.put( 0) # cancle the close request, shutter is closed now
class UsaxsAnalyzerSideReflectionStageDevice(MotorBundle): """USAXS Analyzer side-reflection stage""" #r = Component(UsaxsMotor, '9idcLAX:xps:c0:m6', labels=("analyzer",)) #t = Component(UsaxsMotor, '9idcLAX:xps:c0:m4', labels=("analyzer",)) y = Component(UsaxsMotor, '9idcLAX:m58:c1:m4', labels=("analyzer", )) rp = Component(UsaxsMotorTunable, '9idcLAX:pi:c0:m4', labels=("analyzer", "tunable"))
class Detector(Device): flat_image = Component(Signal, value=np.ones(12)) width = Component(Signal, value=4) height = Component(Signal, value=3) ndims = Component(Signal, value=2) shaped_image = Component(NDDerivedSignal, 'flat_image', shape=('width', 'height'), num_dimensions='ndims')
class SynApps_Record_asub(Device): """asub record, just the fields used here""" # https://wiki-ext.aps.anl.gov/epics/index.php/RRM_3-14_Array_Subroutine proc = Component(EpicsSignal, ".PROC") a = Component(EpicsSignal, ".A") b = Component(EpicsSignal, ".B") vale = Component(EpicsSignal, ".VALE")
class StageTable(Device): """ table for the sample stage """ # horizontal translation transverse to incoming beam x = Component(MyMotor, 'sky:m13', labels=["motor", "table"]) # horizontal translation parallel to incoming beam z = Component(MyMotor, 'sky:m14', labels=["motor", "table"])
class Xspress3Vortex4Ch(Xspress3VortexBase): # Channels Ch1 = Component(Xspress3Channel, '', chnum=1) Ch2 = Component(Xspress3Channel, '', chnum=2) Ch3 = Component(Xspress3Channel, '', chnum=3) Ch4 = Component(Xspress3Channel, '', chnum=4) _num_channels = 4
class MyHdf5Detector(SimDetector, SingleTrigger): image = Component(ImagePlugin, suffix="image1:") hdf1 = Component( MyHDF5Plugin, suffix='HDF1:', root='/', # for databroker write_path_template=image_file_path, # for EPICS AD, reg=db.reg)
class SubDevice(Device): cpt1 = Component(FakeSignal, '1') cpt2 = Component(FakeSignal, '2') cpt3 = Component(FakeSignal, '3') subsub = Component(SubSubDevice, '') def stop(self): self.stop_called = True super().stop()
class MySingleTriggerHdf5SimDetector(SingleTrigger, SimDetector): image = Component(ImagePlugin, suffix="image1:") hdf1 = Component( myHDF5FileNames, suffix='HDF1:', root='/', # for databroker write_path_template=image_file_path, # for EPICS AD )
class AMIZones(Device): high_field = Component(EpicsSignal, "Field.VAL", write_pv="FieldSet", kind="config") ramp_rate = Component(EpicsSignal, "Rate", write_pv="RateSet", kind="config")
def create_attr(self, attr_name): try: cls, suffix, kwargs = self.defn[attr_name] inst = Component(cls, suffix, **kwargs) except ValueError: cls, kwargs = self.defn[attr_name] inst = Component(cls, **kwargs) inst.attr = attr_name return inst
class GSlitDevice(MotorBundle): """ guard slit * aperture: (h_size, v_size) """ bot = Component(GuardSlitMotor, '9idcLAX:mxv:c0:m6', labels=("gslit", )) inb = Component(GuardSlitMotor, '9idcLAX:mxv:c0:m4', labels=("gslit", )) outb = Component(GuardSlitMotor, '9idcLAX:mxv:c0:m3', labels=("gslit", )) top = Component(GuardSlitMotor, '9idcLAX:mxv:c0:m5', labels=("gslit", )) x = Component(UsaxsMotor, '9idcLAX:m58:c1:m5', labels=("gslit", )) y = Component(UsaxsMotor, '9idcLAX:m58:c0:m6', labels=("gslit", )) h_size = Component(EpicsSignal, '9idcLAX:GSlit1H:size') v_size = Component(EpicsSignal, '9idcLAX:GSlit1V:size') h_sync_proc = Component(EpicsSignal, '9idcLAX:GSlit1H:sync.PROC') v_sync_proc = Component(EpicsSignal, '9idcLAX:GSlit1V:sync.PROC') gap_tolerance = 0.02 # actual must be this close to desired scale_factor = 1.2 # 1.2x the size of the beam should be good guess for guard slits. h_step_away = 0.2 # 0.2mm step away from beam v_step_away = 0.1 # 0.1mm step away from beam h_step_into = 1.1 # 1.1mm step into the beam (blocks the beam) v_step_into = 0.4 # 0.4mm step into the beam (blocks the beam) tuning_intensity_threshold = 500 def set_size(self, *args, h=None, v=None): """move the slits to the specified size""" if h is None: raise ValueError("must define horizontal size") if v is None: raise ValueError("must define vertical size") move_motors(self.h_size, h, self.v_size, v) @property def h_gap_ok(self): gap = self.outb.position - self.inb.position return abs(gap - terms.SAXS.guard_h_size.get()) <= self.gap_tolerance @property def v_h_gap_ok(self): gap = self.top.position - self.bot.position return abs(gap - terms.SAXS.guard_v_size.get()) <= self.gap_tolerance @property def gap_ok(self): return self.h_gap_ok and self.v_h_gap_ok def process_motor_records(self): yield from bps.mv(self.top.process_record, 1) yield from bps.mv(self.outb.process_record, 1) yield from bps.sleep(0.05) yield from bps.mv(self.bot.process_record, 1) yield from bps.mv(self.inb.process_record, 1) yield from bps.sleep(0.05)
class Mirror1_A(Device): """ Mirror 1 in the 2BM-A station A_mirror1 = Mirror1_A("2bma:M1", name="A_mirror1") A_mirror1.angle.put(Mirr_Ang) A_mirror1.average.put(Mirr_YAvg) """ angle = Component(EpicsSignal, "angl") average = Component(EpicsSignal, "avg")
class RubyDevice(Device): """ Ruby system """ focus = Component(EpicsMotor, 'm37', labels=('motor', 'ruby')) y = Component(EpicsMotor, 'm38', labels=('motor', 'ruby')) z = Component(EpicsMotor, 'm39', labels=('motor', 'ruby')) zoom = Component(EpicsMotor, 'm40', labels=('motor', 'ruby')) led = FormattedComponent(DAC, '4idd:DAC1_1', labels=('ruby', )) laser = FormattedComponent(DAC, '4idd:DAC1_2', labels=('ruby', ))
class SubDevice(Device): cpt1 = Component(FakeSignal, '1') cpt2 = Component(FakeSignal, '2') cpt3 = Component(FakeSignal, '3') subsub = Component(SubSubDevice, '') def stop(self, *, success=False): self.stop_called = True self.success = success super().stop(success=success)
class SampleStage(Device): """ table for the sample stage """ # horizontal translation transverse to incoming beam x = Component(MyMotor, 'sky:m15', labels=["motor", "sample"]) # vertical translation transverse to incoming beam y = Component(MyMotor, 'sky:m16', labels=["motor", "sample"]) # support table table = Component(StageTable)
class NeatStage_3IDD(Device): x = Component(EpicsMotor, "m37", labels=[ "NEAT stage", ]) y = Component(EpicsMotor, "m38", labels=[ "NEAT stage", ]) theta = Component(EpicsMotor, "m3", labels=[ "NEAT stage", ])
class PreUsaxsTuneParameters(Device): """preUSAXStune handling""" num_scans_last_tune = Component(EpicsSignal, "9idcLAX:NumScansFromLastTune") epoch_last_tune = Component(EpicsSignal, "9idcLAX:EPOCHTimeOfLastTune") req_num_scans_between_tune = Component(EpicsSignal, "9idcLAX:ReqNumScansBetweenTune") req_time_between_tune = Component(EpicsSignal, "9idcLAX:ReqTimeBetweenTune") run_tune_on_qdo = Component(EpicsSignal, "9idcLAX:RunPreUSAXStuneOnQdo") run_tune_next = Component(EpicsSignal, "9idcLAX:RunPreUSAXStuneNext") sx = Component(EpicsSignal, "9idcLAX:preUSAXStuneSX") sy = Component(EpicsSignal, "9idcLAX:preUSAXStuneSY") use_specific_location = Component(EpicsSignal, "9idcLAX:UseSpecificTuneLocation") @property def needed(self): """ is a tune needed? EXAMPLE:: if terms.preUSAXStune.needed: yield from preUSAXStune() # TODO: and then reset terms as approriate """ result = self.run_tune_next.get() # TODO: next test if not in SAXS or WAXS mode result = result or self.num_scans_last_tune.get() > self.req_num_scans_between_tune.get() time_limit = self.epoch_last_tune.get() + self.req_time_between_tune.get() result = result or time.time() > time_limit self.run_tune_next.put(0) return result