class Part: def __init__(self, part): # Let errors propagate self._name = part.get('name') self._vset = part.get('vset') self._base = part.get('base') self._scale = part.get('scale') self._ramping = EpicsSignalRO(f'{self._base}{RAMPING}') self._set_voltage = EpicsSignal(f'{self._base}{VSET}') @property def name(self): return self._name @property def vset(self): return self._vset @property def base(self): return self._base def set_voltage(self, zero=False): if zero: self._set_voltage.put(0) else: self._set_voltage.put(self.vset) @property def ramping(self): return self._ramping.get()
class HxnScanStatus: '''Broadcast if a scan is in progress via PV Processed on every start/end document ''' def __init__(self, running_pv): self._last_start = None self.running_signal = EpicsSignal(running_pv) self._running = False @property def running(self): return self._running @running.setter def running(self, running): self._running = running if running is None: running = 0 try: self.running_signal.put(running) except DisconnectedError: logger.error('ScanRunning PV disconnected. Is the hxntools IOC running?') def __call__(self, name, doc): '''Bluesky callback with document info''' if name == 'start': self._last_start = doc if self._last_start and name in ('start', 'stop'): self.running = (name == 'start')
def stop_turbo(): turbo_onoff = EpicsSignal('XF:12IDC-VA:2{Det:300KW-TMP:1}OnOff', name='turbo_onoff') turbo_onoff.put(0) iv1 = EpicsSignal('XF:12IDC-VA:2{Det:300KW-IV:1}Cmd:Cls-Cmd', name='iv1') iv1.put(1)
class SolenoidValve: """ this works for both gate valves and solenoid valves status of the valve is given by PV [deviceName]Pos-Sts open the valve by setting PV [deviceName]Cmd:Opn-Cmd to 1 close the valve by setting PV [deviceName]Cmd:Cls-Cmd to 1 """ def __init__(self, devName): """ for soft pump valves devName should be a list some valves have """ if isinstance(devName, list): # soft pump valves if len(devName) != 2: raise Exception( "2 device names must be given for a soft pump valve.") self.has_soft = True self.soft_valve = SolenoidValve(devName[1]) devName = devName[0] self.devName = devName else: self.has_soft = False self.devName = devName self.sig_status = EpicsSignal(devName + 'Pos-Sts') self.sig_open = EpicsSignal(devName + 'Cmd:Opn-Cmd') self.sig_close = EpicsSignal(devName + 'Cmd:Cls-Cmd') def open(self, softOpen=False): print("request to open valve: ", self.devName) if sim_on: print("simulation mode on, the valve state will not change.") return if softOpen and self.has_soft: self.close() self.soft_valve.open() else: if self.has_soft: self.soft_valve.close() self.sig_open.put(1) check_sig_status(self.sig_status, 1) def close(self): print("request to close valve: ", self.devName) if sim_on: return if self.has_soft: if self.soft_valve.status == 1: self.soft_valve.close() if self.status == 1: self.sig_close.put(1) check_sig_status(self.sig_status, 0) @property def status(self): if self.has_soft: return 0.5 * self.soft_valve.status + self.sig_status.get() else: return self.sig_status.get()
def test_write_pv_timestamp_no_monitor(motor): sp = EpicsSignal(motor.user_setpoint.pvname, name='test') sp_value0 = sp.get() ts0 = sp.timestamp sp.put(sp_value0 + 0.1, wait=True) time.sleep(0.1) sp_value1 = sp.get() ts1 = sp.timestamp assert ts1 > ts0 assert_almost_equal(sp_value0 + 0.1, sp_value1) sp.put(sp.value - 0.1, wait=True)
def test_read_pv_timestamp_no_monitor(motor): sp = EpicsSignal(motor.user_setpoint.pvname, name='test') rbv = EpicsSignalRO(motor.user_readback.pvname, name='test') rbv_value0 = rbv.get() ts0 = rbv.timestamp sp.put(sp.value + 0.1, wait=True) time.sleep(0.1) rbv_value1 = rbv.get() ts1 = rbv.timestamp assert ts1 > ts0 assert_almost_equal(rbv_value0 + 0.1, rbv_value1) sp.put(sp.value - 0.1, wait=True)
def gv_temp_open(sysdev, duration): """ gv_temp_open("XF:21IDB-VA{BT:10-GV:10_D_1}", 1.5 (seconds)) """ open_pv_str = sysdev + "Cmd:Opn-Cmd" close_pv_str = sysdev + "Cmd:Cls-Cmd" open_cmd = EpicsSignal(name="open_cmd", read_pv=open_pv_str, write_pv=open_pv_str) close_cmd = EpicsSignal(name="close_cmd", read_pv=close_pv_str, write_pv=close_pv_str) open_cmd.put(1) time.sleep(duration) close_cmd.put(1)
def test_write_pv_timestamp_monitor(self): mtr = EpicsMotor(config.motor_recs[0]) mtr.wait_for_connection() sp = EpicsSignal(mtr.user_setpoint.pvname, auto_monitor=True) sp_value0 = sp.get() ts0 = sp.timestamp sp.put(sp_value0 + 0.1, wait=True) time.sleep(0.1) sp_value1 = sp.get() ts1 = sp.timestamp self.assertGreater(ts1, ts0) self.assertAlmostEqual(sp_value0 + 0.1, sp_value1) sp.put(sp.value - 0.1, wait=True)
def test_write_pv_timestamp_no_monitor(self): mtr = EpicsMotor(config.motor_recs[0], name='test') mtr.wait_for_connection() sp = EpicsSignal(mtr.user_setpoint.pvname, name='test') sp_value0 = sp.get() ts0 = sp.timestamp sp.put(sp_value0 + 0.1, wait=True) time.sleep(0.1) sp_value1 = sp.get() ts1 = sp.timestamp self.assertGreater(ts1, ts0) self.assertAlmostEqual(sp_value0 + 0.1, sp_value1) sp.put(sp.value - 0.1, wait=True)
def test_read_pv_timestamp_monitor(self): mtr = EpicsMotor(config.motor_recs[0]) mtr.wait_for_connection() sp = EpicsSignal(mtr.user_setpoint.pvname, auto_monitor=True) rbv = EpicsSignalRO(mtr.user_readback.pvname, auto_monitor=True) rbv_value0 = rbv.get() ts0 = rbv.timestamp sp.put(sp.value + 0.1, wait=True) time.sleep(0.1) rbv_value1 = rbv.get() ts1 = rbv.timestamp self.assertGreater(ts1, ts0) self.assertAlmostEqual(rbv_value0 + 0.1, rbv_value1) sp.put(sp.value - 0.1, wait=True)
def test_read_pv_timestamp_no_monitor(self): mtr = EpicsMotor(config.motor_recs[0]) mtr.wait_for_connection() sp = EpicsSignal(mtr.user_setpoint.pvname) rbv = EpicsSignalRO(mtr.user_readback.pvname) rbv_value0 = rbv.get() ts0 = rbv.timestamp sp.put(sp.value + 0.1, wait=True) time.sleep(0.1) rbv_value1 = rbv.get() ts1 = rbv.timestamp self.assertGreater(ts1, ts0) self.assertAlmostEqual(rbv_value0 + 0.1, rbv_value1) sp.put(sp.value - 0.1, wait=True)
class MKSGauge: """ can read pressure, status of the gauge "OFF", "NO GAUGE" turn on and off """ def __init__(self, devName): self.pres_sig = EpicsSignal(devName + 'P:Raw-I') self.power_ctrl = EpicsSignal(devName + 'Pwr-Cmd') def pressure(self): """ raise exception if the gause if not present or off """ Pr = self.pres_sig.get() if Pr == "NO_GAUGE" or Pr == "OFF": raise Exception(Pr, self.pres_sig.name) elif Pr == "LO<E-03": P0 = 1.0e-3 elif Pr == ">1.0E+03": P0 = 1.1e3 else: P0 = np.float(Pr) return P0 def power(self, state, max_try=10): """ turn the gauge on or off """ if max_try <= 0: raise Exception("could not change gauge state to ", state) self.power_on = self.power_ctrl.get() if state == "ON" or state == "on": if self.power_on: return else: self.power_ctrl.put(1) time.sleep(1.5) return self.power(state, max_try - 1) else: if self.power_on: self.power_ctrl.put(0) time.sleep(1.5) return self.power(state, max_try - 1) else: return
class TimingChannel(object): def __init__(self, setpoint_PV, readback_PV, name): self.control_PV = EpicsSignal(setpoint_PV) # DG/Vitara PV self.storage_PV = EpicsSignal(readback_PV) # Notepad PV self.name = name def save_t0(self, val=None): """ Set the t0 value directly, or save the current value as t0. """ if not val: # Take current value to be t0 if not provided val = self.control_PV.get() self.storage_PV.put(val) # Save t0 value def restore_t0(self): """ Restore the t0 value from the current saved value for t0. """ val = self.storage_PV.get() self.control_PV.put(val) # write t0 value def mvr(self, relval): """ Move the control PV relative to it's current value. """ currval = self.control_PV.get() self.control_PV.put(currval - relval) def mv(self, val): t0 = self.storage_PV.get() self.control_PV.put(t0 - val) def get_delay(self, verbose=False): delay = self.control_PV.get() - self.storage_PV.get() if delay > 0: print("X-rays arrive {} s before the optical laser".format( abs(delay))) elif delay < 0: print("X-rays arrive {} s after the optical laser".format( abs(delay))) else: # delay is 0 print("X-rays arrive at the same time as the optical laser") if verbose: control_data = (self.name, self.control_PV.pvname, self.control_PV.get()) storage_data = (self.name, self.storage_PV.pvname, self.storage_PV.get()) print("{} Control PV: {}, Control Value: {}".format(*control_data)) print("{} Storage PV: {}, Storage Value: {}".format(*storage_data))
class User: """Generic User Object""" def __init__(self): self.sc1_mesh_raw = EpicsSignal(name='sc1_mesh_raw', read_pv='CXI:SC1:AIO:04:RAW:ANALOGIN', write_pv='CXI:SC1:AIO:04:ANALOGOUT') self.sc1_mesh_scale = EpicsSignal(name='sc1_mesh_scale', read_pv='CXI:SC1:AIO:04:SCALE') def get_sc1_mesh_voltage(self): """ Get the current power supply voltage """ return self.sc1_mesh_raw.get() def set_sc1_mesh_voltage(self, sigIn, wait=True, do_print=True): """ Set voltage on power supply to an absolute value Parameters ---------- sigIn: int or float Power supply voltage in Volts """ if do_print: print('Setting voltage...') sigInScaled = sigIn / self.sc1_mesh_scale.get() # in V self.sc1_mesh_raw.put(sigInScaled) if wait: time.sleep(2.5) finalVolt = self.sc1_mesh_raw.get() finalVoltSupply = finalVolt * self.sc1_mesh_scale.get() if do_print: print('Power Supply Setpoint: %s V' % sigIn) print('Power Supply Voltage: %s V' % finalVoltSupply) def set_sc1_rel_mesh_voltage(self, deltaVolt, wait=True, do_print=True): """ Increase/decrease power supply voltage by a specified amount Parameters ---------- deltaVolt: int or float Amount to increase/decrease voltage (in Volts) from its current value. Use positive value to increase and negative value to decrease """ if do_print: print('Setting voltage...') curr_set = self.sc1_mesh_raw.get_setpoint() curr_set_supply = curr_set * self.sc1_mesh_scale.get() if do_print: print('Previous Power Supply Setpoint: %s V' % curr_set_supply) new_voltage = round(curr_set_supply + deltaVolt) self.set_sc1_mesh_voltage(new_voltage, wait=wait, do_print=do_print) def tweak_mesh_voltage(self, deltaVolt): """ Continuously Increase/decrease power supply voltage by specifed amount using arrow keys Parameters ---------- deltaVolt: int or float Amount to change voltage (in Volts) from its current value at each step. After calling with specified step size, use arrow keys to keep changing ^C: exits tweak mode """ print('Use arrow keys (left, right) to step voltage (-, +)') while True: key = key_press.get_input() if key in ('q', None): return elif key == key_press.arrow_right: self.set_sc1_rel_mesh_voltage(deltaVolt, wait=False, do_print=False) elif key == key_press.arrow_left: self.set_sc1_rel_mesh_voltage(-deltaVolt, wait=False, do_print=False)
class PilatusGrabber(): '''Crude tool for grabbing images from the Pilatus. Largely following the standard BlueSky AreaDetector interface, but monkey-patching functionality for the bits that Bruce is too dim to figure out. Define the Pilatus Detector pilatus = MyDetector('XF:06BMB-ES{Det:PIL100k}:', name='Pilatus') Make an PilatusGrabber opbject pil = PilatusGrabber(pilatus) Take an exposure pil.snap() Show the image (and maybe copy it elsewhere) pil.fetch() Properties: path: AreaDetector's file path (cannot have spaces) fname: file name template: substitution template for constructing the resolved file name fullname: AreaDetector's fully resolved file name number: file extension (auto increments) threshold: detector energy threshold in keV time: exposure time, sets the exposure time and acquire time ready: flag with a simple check to see if camera is ready to take a picture ''' def __init__(self, source): self.source = source self.image = EpicsSignal(self.source.prefix + 'image1:ArrayData', name=self.source.name + ' image') self._path = EpicsSignal(self.source.prefix + 'cam1:FilePath') self._fname = EpicsSignal(self.source.prefix + 'cam1:FileName') self._number = EpicsSignal(self.source.prefix + 'cam1:FileNumber') self._template = EpicsSignal(self.source.prefix + 'cam1:FileTemplate') self._threshold = EpicsSignal(self.source.prefix + 'cam1:ThresholdEnergy') self._fullname = EpicsSignalRO(self.source.prefix + 'cam1:FullFileName_RBV') self._statusmsg = EpicsSignalRO(self.source.prefix + 'cam1:StatusMessage_RBV') self._busy = EpicsSignalRO(self.source.prefix + 'cam1:AcquireBusy') @property def path(self): return (''.join( [chr(x) for x in self._path.value[self._path.value.nonzero()]])) @path.setter def path(self, path): a = numpy.pad(array([ord(x) for x in path]), (0, 256 - len(path)), mode='constant') self._path.put(a) @property def fname(self): return (''.join( [chr(x) for x in self._fname.value[self._fname.value.nonzero()]])) @fname.setter def fname(self, fname): a = numpy.pad(array([ord(x) for x in fname]), (0, 256 - len(fname)), mode='constant') self._fname.put(a) @property def template(self): return (''.join([ chr(x) for x in self._template.value[self._template.value.nonzero()] ])) @template.setter def template(self, template): a = numpy.pad(array([ord(x) for x in template]), (0, 256 - len(template)), mode='constant') self._template.put(a) @property def fullname(self): return (''.join([ chr(x) for x in self._fullname.value[self._fullname.value.nonzero()] ])) @property def statusmsg(self): return (''.join([ chr(x) for x in self._statusmsg.value[self._statusmsg.value.nonzero()] ])) @property def number(self): return (self._number.value) @number.setter def number(self, number): self._number.put(number) @property def threshold(self): return (self._threshold.value) @threshold.setter def threshold(self, threshold): self._threshold.put(threshold) @property def time(self): return (self.source.cam.acquire_time.value) @time.setter def time(self, exposure_time): self.source.cam.acquire_time.put(exposure_time) self.source.cam.acquire_period.put(exposure_time + 0.004) @property def numimages(self): return (self.source.cam.num_images.value) @time.setter def numimages(self, numimages): self.source.cam.num_images.put(numimages) @property def ready(self): if self._busy.value == 1: return False elif 'Error' in self.statusmsg: return False else: return True def snap(self): self.source.stage() st = self.source.trigger() while not st.done: time.sleep(.1) ret = self.source.read() desc = self.source.describe() self.source.unstage() def fetch(self, fname='/home/xf06bm/test.tif'): array = self.image.get() size = (self.source.image.height.value, self.source.image.width.value) img = np.reshape(array, size).astype('float') #Image.fromarray(img).save(fname, "TIFF") ## symlink to file on /nist ??? copy??? fig, ax = plt.subplots(1) # Create figure and axes rotated_img = ndimage.rotate(img, 90) plt.imshow( rotated_img, cmap='bone' ) ## https://matplotlib.org/3.1.0/tutorials/colors/colormaps.html imfile = os.path.basename(self.fullname) plt.title(imfile) plt.colorbar() plt.clim(0, array.max()) plt.show()
class MirrorScreen(Display): def __init__(self, parent=None, args=None, macros=None): super(MirrorScreen, self).__init__(parent=parent, args=args, macros=macros) if macros != None: self.x_stop = EpicsSignal(macros['XAXIS'] + ".STOP") self.y_stop = EpicsSignal(macros['YAXIS'] + ".STOP") self.p_stop = EpicsSignal(macros['PITCH'] + ".STOP") self.alarm_box_x = QHBoxLayout() self.alarm_box_y = QHBoxLayout() self.alarm_box_p = QHBoxLayout() self.alarm_box_gantry_x = QHBoxLayout() self.alarm_box_gantry_y = QHBoxLayout() self.ghost = QHBoxLayout() self.alarm_x = TyphosAlarmCircle() self.alarm_x.channel = "ca://" + macros['XAXIS'] self.alarm_x.setMaximumHeight(35) self.alarm_x.setMaximumWidth(35) self.alarm_y = TyphosAlarmCircle() self.alarm_y.channel = "ca://" + macros['YAXIS'] self.alarm_y.setMaximumHeight(35) self.alarm_y.setMaximumWidth(35) self.alarm_p = TyphosAlarmCircle() self.alarm_p.channel = "ca://" + macros['PITCH'] self.alarm_p.setMaximumHeight(35) self.alarm_p.setMaximumWidth(35) label_font = QFont() label_font.setBold(True) self.x_label = QLabel("x") #self.x_label.setFont(label_font) self.y_label = QLabel("y") #self.y_label.setFont(label_font) self.p_label = QLabel("pitch") #self.p_label.setFont(label_font) self.gantry_x_label = QLabel("gantry x") #self.gantry_x_label.setFont(label_font) self.gantry_y_label = QLabel("gantry y") #self.gantry_y_label.setFont(label_font) self.alarm_gantry_x = TyphosAlarmCircle() self.alarm_gantry_x.channel = "ca://" + macros['MIRROR'] + ":HOMS:ALREADY_COUPLED_X_RBV" self.alarm_gantry_x.setMaximumHeight(35) self.alarm_gantry_x.setMaximumWidth(35) self.alarm_gantry_y = TyphosAlarmCircle() self.alarm_gantry_y.channel = "ca://" + macros['MIRROR'] + ":HOMS:ALREADY_COUPLED_Y_RBV" self.alarm_gantry_y.setMaximumHeight(35) self.alarm_gantry_y.setMaximumWidth(35) self.alarm_box_x.addWidget(self.x_label) self.alarm_box_x.setAlignment(self.x_label, Qt.AlignCenter) self.alarm_box_x.addWidget(self.alarm_x) self.alarm_box_y.addWidget(self.y_label) self.alarm_box_y.setAlignment(self.y_label, Qt.AlignCenter) self.alarm_box_y.addWidget(self.alarm_y) self.alarm_box_p.addWidget(self.p_label) self.alarm_box_p.setAlignment(self.p_label, Qt.AlignCenter) self.alarm_box_p.addWidget(self.alarm_p) self.alarm_box_gantry_x.addWidget(self.gantry_x_label) self.alarm_box_gantry_x.setAlignment(self.gantry_x_label, Qt.AlignCenter) self.alarm_box_gantry_x.addWidget(self.alarm_gantry_x) self.alarm_box_gantry_y.addWidget(self.gantry_y_label) self.alarm_box_gantry_y.setAlignment(self.gantry_y_label, Qt.AlignCenter) self.alarm_box_gantry_y.addWidget(self.alarm_gantry_y) self.stop_button = PyDMPushButton(label="Stop") self.stop_button.setMaximumHeight(120) self.stop_button.setMaximumWidth(120) self.stop_button.clicked.connect(self.stop_motors) self.stop_button.setStyleSheet("background: rgb(255,0,0)") self.advanced_button = TyphosRelatedSuiteButton() self.advanced_button.happi_names = [macros['MIRROR'].lower() + "_homs"] self.advanced_button.setText("Advanced") self.ui.horizontalLayout_8.addLayout(self.alarm_box_x) self.ui.horizontalLayout_8.addLayout(self.alarm_box_y) self.ui.horizontalLayout_8.addLayout(self.alarm_box_p) self.ui.horizontalLayout_8.addLayout(self.alarm_box_gantry_x) self.ui.horizontalLayout_8.addLayout(self.alarm_box_gantry_y) #self.ui.horizontalLayout_8.addWidget(self.alarm_x) #self.ui.horizontalLayout_8.addWidget(self.alarm_y) #self.ui.horizontalLayout_8.addWidget(self.alarm_p) #self.ui.horizontalLayout_8.addWidget(self.stop_button) #self.ui.verticalLayout_6.setAlignment(Qt.AlignCenter) self.ui.horizontalLayout_14.addWidget(self.advanced_button) self.ui.horizontalLayout_14.addSpacing(50) self.ui.horizontalLayout_14.addWidget(self.stop_button) self.ui.horizontalLayout_14.addSpacing(160) self.ui.setGeometry(QtCore.QRect(0,0, 360, 385)) def stop_motors(self): self.x_stop.put(1) self.y_stop.put(1) self.p_stop.put(1) def ui_filename(self): # Point to our UI file return 'mirrorScreen.ui' def ui_filepath(self): # Return the full path to the UI file return path.join(path.dirname(path.realpath(__file__)), self.ui_filename())
def peakup_dcm(correct_roll=True, plot=False, shutter=True, use_calib=False): """ Scan the HDCM fine pitch and, optionally, roll against the ion chamber in the D Hutch correct_roll <Bool> If True, align the beam in the vertical (roll) plot <Bool> If True, plot the intensity as a function of pitch/roll shutter <Bool> If True, the shutter will be automatically opened/closed use_calib <Bool> If True, use a previous calibration as an initial guess """ e_value = energy.energy.get()[1] pitch_old = dcm.c2_pitch.position roll_old = dcm.c1_roll.position det = [sclr1] ps = PeakStats(dcm.c2_pitch.name, im.name) ps1 = PeakStats(dcm.c1_roll.name, im.name) if (shutter == True): RE(mv(shut_b, 'Open')) c2pitch_kill = EpicsSignal("XF:05IDA-OP:1{Mono:HDCM-Ax:P2}Cmd:Kill-Cmd") # Turn off the ePID loop for the pitch motor # 'XF:05IDD-CT{FbPid:02}PID:on' c2_pid = EpicsSignal("XF:05IDD-CT{FbPid:02}PID:on") c2_pid.put(0) # Turn off the ePID loop c2_V = EpicsSignal("XF:05ID-BI{EM:BPM1}DAC1") c2_V.put(3.0) # Reset the piezo voltage to 3 V # pitch_lim = (-19.320, -19.370) # pitch_num = 51 # roll_lim = (-4.9, -5.14) # roll_num = 45 # pitch_lim = (-19.375, -19.425) # pitch_num = 51 # roll_lim = (-4.9, -5.6) # roll_num = 51 pitch_lim = (pitch_old - 0.025, pitch_old + 0.025) roll_lim = (roll_old - 0.2, roll_old + 0.2) pitch_num = 51 roll_num = 51 if (use_calib): # Factor to convert eV to keV K = 1 if (e_value > 1000): K = 1 / 1000 # Pitch calibration pitch_guess = -0.00055357 * K * e_value - 19.39382381 dcm.c2_pitch.move(pitch_guess, wait=True) # Roll calibration roll_guess = -0.01124286 * K * e_value - 4.93568571 dcm.c1_roll.move(roll_guess, wait=True) # Output guess print('\nMoving to guess:') print('\tC2 Pitch: %f' % (pitch_guess)) print('\tC1 Roll: %f\n' % (roll_guess)) #if e_value < 10.: # sclr1.preset_time.put(0.1) # RE(scan([sclr1], dcm.c2_pitch, -19.335, -19.305, 31), [ps]) #else: # sclr1.preset_time.put(1.) # RE(scan([sclr1], dcm.c2_pitch, -19.355, -19.310, 46), [ps]) if e_value < 14.: # sclr1.preset_time.put(0.1) sclr1.preset_time.put(1.0) else: sclr1.preset_time.put(1.0) if (plot == True): sclr1.preset_time.put( 1.0) # Let's collect a longer scan since we're plotting it RE(scan(det, dcm.c2_pitch, pitch_lim[0], pitch_lim[1], pitch_num), [ps]) print('Pitch: Centroid at %f\n\n' % (ps.cen)) plt.figure() plt.plot(ps.x_data, ps.y_data, label='Data') plt.plot((ps.cen, ps.cen), (np.amin(ps.y_data), np.amax(ps.y_data)), '--k', label='Centroid') plt.xlabel('HDCM C2 PITCH') plt.ylabel('Counts') plt.legend() else: RE(scan(det, dcm.c2_pitch, pitch_lim[0], pitch_lim[1], pitch_num), [ps]) time.sleep(0.5) dcm.c2_pitch.move(ps.cen, wait=True) time.sleep(0.5) if (np.abs(dcm.c2_pitch.position - ps.cen) > 0.001): print('The pitch motor did not move on the first try. Trying again...', end='') dcm.c2_pitch.move(ps.cen, wait=True) if (np.abs(dcm.c2_pitch.position - ps.cen) > 0.001): print('FAIL! Check motor location.\n') else: print('OK\n') logscan('peakup_pitch') c2pitch_kill.put(1) if correct_roll == True: if (plot == True): sclr1.preset_time.put( 1.0) # If we are debugging, let's collect a longer scan RE(scan(det, dcm.c1_roll, roll_lim[0], roll_lim[1], roll_num), [ps1]) # print('Roll: Maximum flux at %f' % (ps1.max[0])) print('Roll: Centroid at %f\n\n' % (ps1.cen)) plt.figure() plt.plot(ps1.x_data, ps1.y_data, label='Data') plt.plot((ps1.cen, ps1.cen), (np.amin(ps1.y_data), np.amax(ps1.y_data)), '--k', label='Centroid') plt.xlabel('HDCM ROLL') plt.ylabel('Counts') plt.legend() else: RE(scan(det, dcm.c1_roll, roll_lim[0], roll_lim[1], roll_num), [ps1]) time.sleep(0.5) dcm.c1_roll.move(ps1.cen, wait=True) time.sleep(0.5) if (np.abs(dcm.c1_roll.position - ps1.cen) > 0.001): print( 'The roll motor did not move on the first try. Trying again...', end='') dcm.c1_roll.move(ps1.cen, wait=True) if (np.abs(dcm.c1_roll.position - ps1.cen) > 0.001): print('FAIL! Check motor location.\n') else: print('OK\n') logscan('peakup_roll') # Output old/new values print('Old pitch value:\t%f' % pitch_old) print('New pitch value:\t%f' % ps.cen) print('Current pitch value:\t%f' % dcm.c2_pitch.position) print('Old roll value: \t%f' % roll_old) print('New roll value: \t%f' % ps1.cen) print('Current roll value: \t%f\n' % dcm.c1_roll.position) if (shutter == True): RE(mv(shut_b, 'Close')) #for some reason we now need to kill the pitch motion to keep it from overheating. 6/8/17 #this need has disappeared mysteriously after the shutdown - gjw 2018/01/19 # This has now reappeared - amk 2018/06/06 time.sleep(1) c2pitch_kill.put(1) c2_pid.put(1) # Turn on the ePID loop
class RamperIOC(PVGroup): """ An IOC for executing ramps of pv_setpoints """ pv_sp = pvproperty( value=25.0, doc='pv setpoint', ) pv_sp_rate = pvproperty( value=0.0, doc='pv setpoint change rate', ) go = pvproperty( value=0, doc='flag indicating whether ramping is actually taking place') time_elapsed = pvproperty( value=0.0, doc= 'timer for measuring the elapsed time since the beginning of the program' ) pause = pvproperty(value=0, doc='flag indicating whether ramping is paused') time_paused = pvproperty(value=0.0, doc='timer for measuring the paused time') tprog = pvproperty(value=[0.0, 60.0], doc='time array of the ramp program', max_length=25) pvprog = pvproperty(value=[25.0, 25.0], doc='pv setpoint array of the ramp program', max_length=25) dwell = pvproperty(value=0.05, doc="dwell time for the pv setpoint update") safety_timer = pvproperty( value=0.0, doc='timer for stopping the program in case if ipython session dies') safety_thresh = pvproperty(value=10.0, doc='time threshold for turning the heater off') pid_enable_name = pvproperty(value='', dtype=ChannelType.STRING, doc='pv name for pid enable') pid_output_name = pvproperty( value='', dtype=ChannelType.STRING, # dtype=_LongStringChannelType.LONG_STRING, doc='pv name for pid output') # pid_output_name_ext = pvproperty( # value='', # dtype=ChannelType.STRING, # # dtype=_LongStringChannelType.LONG_STRING, # doc='pv name for pid output (extension)' # ) pv_test = pvproperty(value=0, doc='pv for output testing') _previous_elapsed_time = None time_start = None pid_enable = None pid_output = None @pv_sp.startup async def pv_sp(self, instance, async_lib): """This is a startup hook which periodically updates the value.""" rate = 0 while True: await self.pv_sp_rate.write(rate) await async_lib.sleep(self.dwell.value) if self.go.value == 1: if self.time_start is None: self.time_start = ttime.time() if self.pause.value == 0: await self.time_elapsed.write(ttime.time() - self.time_start) dt = self.time_elapsed.value - self.time_paused.value if dt < np.max(self.tprog.value): pv_sp = np.interp(dt, self.tprog.value, self.pvprog.value) else: pv_sp = self.pvprog.value[-1] if self._previous_elapsed_time is not None: rate = (pv_sp - self.pv_sp.value) / ( dt - self._previous_elapsed_time) * 60 self._previous_elapsed_time = dt await instance.write(value=pv_sp) else: rate = 0 else: self.time_start = None await self.time_elapsed.write(0.0) await self.time_paused.write(0.0) rate = 0 @pid_enable_name.startup async def pid_enable_name(self, instance, async_lib): while self.pid_enable_name.value == '': await async_lib.sleep(1) self.pid_enable = EpicsSignal(self.pid_enable_name.value, name='pid_enable') def subscription(value, **kwargs): if value == 0: if self.pid_output is not None: if self.pid_output.get() != 0: self.pid_output.put(0) self.pid_enable.subscribe(subscription) @pid_output_name.startup async def pid_output_name(self, instance, async_lib): while (self.pid_output_name.value == ''): await async_lib.sleep(1) self.pid_output = EpicsSignal(self.pid_output_name.value, name='pid_output') def subscription(value, **kwargs): if value != 0: if self.pid_enable is not None: if self.pid_enable.get() == 0: self.pid_output.put(0) self.pid_output.subscribe(subscription) @safety_timer.startup async def safety_timer(self, instance, async_lib): while self.safety_timer.value < self.safety_thresh.value: safety_timer = self.safety_timer.value + 1 await instance.write(value=safety_timer) await async_lib.sleep(1) if self.pid_enable is not None: self.pid_enable.put(0) @time_paused.startup async def time_paused(self, instance, async_lib): while True: cur_time = ttime.time() if self.pause.value == 1: while True: if self.pause.value == 0: break await async_lib.sleep(self.dwell.value / 2) time_paused = self.time_paused.value + (ttime.time() - cur_time) await instance.write(value=time_paused) await async_lib.sleep(self.dwell.value)
class ICAmplifier(Device): """Keithely 428 Current Amplifier Ophyd device""" def __init__(self, *args, gain_set_pv, gain_get_pv, autoFilter_set_pv, autoFilter_get_pv, riseTime_set_pv, riseTime_get_pv, zcheck_set_pv, zcheck_get_pv, filter_set_pv, filter_get_pv, x10_set_pv, x10_get_pv, autoSupEnable_set_pv, autoSupEnable_get_pv, suppression_enable_set_pv, suppression_enable_get_pv, suppressionValue_set_pv, suppressionExponent_set_pv, suppression_set_pv, suppression_get_pv, overload_get_pv, **kwargs): super().__init__(*args, **kwargs) self.gain = EpicsSignal(write_pv=self.prefix + gain_set_pv, read_pv=self.prefix + gain_get_pv, auto_monitor=True, name=self.name) self.autoFilter = EpicsSignal(write_pv=self.prefix + autoFilter_set_pv, read_pv=self.prefix + autoFilter_get_pv, auto_monitor=True, name=self.name) self.filter = EpicsSignal(write_pv=self.prefix + filter_set_pv, read_pv=self.prefix + filter_get_pv, auto_monitor=True, name=self.name) self.riseTime = EpicsSignal(write_pv=self.prefix + riseTime_set_pv, read_pv=self.prefix + riseTime_get_pv, auto_monitor=True, name=self.name) self.zeroCheck = EpicsSignal(write_pv=self.prefix + zcheck_set_pv, read_pv=self.prefix + zcheck_get_pv, auto_monitor=True, name=self.name) self.x10 = EpicsSignal(write_pv=self.prefix + x10_set_pv, read_pv=self.prefix + x10_get_pv, auto_monitor=True, name=self.name) self.suppressionValue = EpicsSignal( write_pv=self.prefix + suppressionValue_set_pv, read_pv=self.prefix + suppressionValue_set_pv, auto_monitor=True, name=self.name) self.suppressionExponent = EpicsSignal( write_pv=self.prefix + suppressionExponent_set_pv, read_pv=self.prefix + suppressionExponent_set_pv, auto_monitor=True, name=self.name) self.autoSupEnable = EpicsSignal( write_pv=self.prefix + autoSupEnable_set_pv, read_pv=self.prefix + autoSupEnable_get_pv, auto_monitor=True, name=self.name) self.suppression = EpicsSignal( write_pv=self.prefix + suppression_set_pv, read_pv=self.prefix + suppression_get_pv, auto_monitor=True, name=self.name) self.overload = EpicsSignalRO(read_pv=self.prefix + overload_get_pv, auto_monitor=True, name=self.name) def set_gain(self, value: int): val = int(value) self.gain.put(val) def get_gain(self): return self.gain.get() def set_suppress(self, value: int): """Set current suppression depending on gain""" if value == 0: # Suppression : -0.05E-3 self.suppressionValue.set(-0.05) self.suppressionExponent.set(6) elif value == 1: # Suppression : -5E-6 self.suppressionValue.set(-5) self.suppressionExponent.set(3) elif value == 2: # Suppression : -0.5E-6 self.suppressionValue.set(-0.5) self.suppressionExponent.set(3) elif value == 3: # Suppression : -0.05E-6 self.suppressionValue.set(-0.05) self.suppressionExponent.set(3) elif value == 4: # Suppression : -5E-9 self.suppressionValue.set(-5) self.suppressionExponent.set(0) elif value == 5: # Suppression : -0.5E-9 self.suppressionValue.set(-0.5) self.suppressionExponent.set(0) elif value == 6: # Suppression : -0.05E-9 self.suppressionValue.set(-0.05) self.suppressionExponent.set(0) elif value == 7: # Suppression : -0.005E-9 self.suppressionValue.set(-0.005) self.suppressionExponent.set(0) else: pass def do_correct(self): self.zeroCheck.put(2) def set_zcheck(self, value: int): val = int(value) self.zeroCheck.put(val) def get_zcheck(self): return self.zeroCheck.enum_strs[self.zeroCheck.get()]
class ICAmplifier(Device): #low_noise_gain = Cpt(EpicsSignal, 'LN}I0') def __init__(self, *args, gain_0, gain_1, gain_2, hspeed_bit, bw_10mhz_bit, bw_1mhz_bit, lnoise, hspeed, bwidth, par = None, **kwargs): super().__init__(*args, **kwargs) self.gain_0 = EpicsSignal(self.prefix + gain_0, name=self.name + '_gain_0') self.gain_1 = EpicsSignal(self.prefix + gain_1, name=self.name + '_gain_1') self.gain_2 = EpicsSignal(self.prefix + gain_2, name=self.name + '_gain_2') self.hspeed_bit = EpicsSignal(self.prefix + hspeed_bit, name=self.name + '_hspeed_bit') self.bw_10mhz_bit = EpicsSignal(self.prefix + bw_10mhz_bit, name=self.name + '_bw_10mhz_bit') self.bw_1mhz_bit = EpicsSignal(self.prefix + bw_1mhz_bit, name=self.name + '_bw_1mhz_bit') self.low_noise_gain = EpicsSignal(self.prefix + lnoise, name=self.name + '_lnoise') self.high_speed_gain = EpicsSignal(self.prefix + hspeed, name=self.name + '_hspeed') self.band_width = EpicsSignal(self.prefix + bwidth, name=self.name + '_bwidth') self.par = par def set_gain(self, value: int, high_speed: bool): val = int(value) - 2 if not ((high_speed and (1 <= val < 7)) or (not high_speed and (0 <= val < 6))): print('{} invalid value. Ignored...'.format(self.name)) return 'Aborted' if high_speed: val -= 1 self.low_noise_gain.put(0) self.high_speed_gain.put(val + 1) self.hspeed_bit.put(1) else: self.low_noise_gain.put(val + 1) self.high_speed_gain.put(0) self.hspeed_bit.put(0) self.gain_0.put((val >> 0) & 1) self.gain_1.put((val >> 1) & 1) self.gain_2.put((val >> 2) & 1) def set_gain_plan(self, value: int, high_speed: bool): val = int(value) - 2 if not ((high_speed and (1 <= val < 7)) or (not high_speed and (0 <= val < 6))): print('{} invalid value. Ignored...'.format(self.name)) return 'Aborted' if high_speed: val -= 1 yield from bps.abs_set(self.low_noise_gain, 0) yield from bps.abs_set(self.high_speed_gain, val + 1) yield from bps.abs_set(self.hspeed_bit, 1) else: yield from bps.abs_set(self.low_noise_gain, val + 1) yield from bps.abs_set(self.high_speed_gain, 0) yield from bps.abs_set(self.hspeed_bit, 0) yield from bps.abs_set(self.gain_0, (val >> 0) & 1) yield from bps.abs_set(self.gain_1, (val >> 1) & 1) yield from bps.abs_set(self.gain_2, (val >> 2) & 1) def get_gain(self): if self.low_noise_gain.value == 0: return [self.high_speed_gain.enum_strs[self.high_speed_gain.value], 1] elif self.high_speed_gain.value == 0: return [self.low_noise_gain.enum_strs[self.low_noise_gain.value], 0] else: return ['0', 0]
class User: SCALE = 1000.0 """Generic User Object""" sequencer = sequencer def __init__(self): self.mesh_raw = EpicsSignal(name='mesh_raw', read_pv='MFX:USR:ai1:0', write_pv='MFX:USR:ao1:0') @property def current_rate(self): """Current configured EventSequencer rate""" return sequencer.sync_marker.get(as_string=True) def configure_sequencer(self, rate='30Hz', readout_all=False): """ Setup EventSequencer (30Hz default) with event codes to tag the Rayonix readout and each of the preceeding 3 shots. Parameters ---------- rate : str, optional default is "30Hz", or optionally "10Hz" readout_all : bool, optional Readout all events with ec198 if True otherwise readout only ec198 at same time as Rayonix ec210 """ logger.info("Configure EventSequencer ...") # Setup sequencer sequencer.sync_marker.put(rate) if rate == '10Hz': delta0 = 120 / 10 elif rate == '30Hz': delta0 = 120 / 30 else: delta0 = 120 / 30 # Set sequence # delta0 is the number of shots until the next event code # for the desired rate, e.g., ec42 for 30 Hz and ec43 for 10 Hz # seqstep = 0 seq_steps[seqstep].configure({ 'eventcode': 213, 'delta_beam': delta0 - 3, 'fiducial': 0, 'comment': 'Rayonix-3' }) seqstep += 1 seq_steps[seqstep].configure({ 'eventcode': 197, 'delta_beam': 1, 'fiducial': 0, 'comment': 'PulsePicker' }) if readout_all: seqstep += 1 seq_steps[seqstep].configure({ 'eventcode': 198, 'delta_beam': 0, 'fiducial': 0, 'comment': 'DAQ Readout' }) seqstep += 1 seq_steps[seqstep].configure({ 'eventcode': 212, 'delta_beam': 0, 'fiducial': 0, 'comment': 'Rayonix-2' }) if readout_all: seqstep += 1 seq_steps[seqstep].configure({ 'eventcode': 198, 'delta_beam': 0, 'fiducial': 0, 'comment': 'DAQ Readout' }) seqstep += 1 seq_steps[seqstep].configure({ 'eventcode': 211, 'delta_beam': 1, 'fiducial': 0, 'comment': 'Rayonix-1' }) if readout_all: seqstep += 1 seq_steps[seqstep].configure({ 'eventcode': 198, 'delta_beam': 0, 'fiducial': 0, 'comment': 'DAQ Readout' }) seqstep += 1 seq_steps[seqstep].configure({ 'eventcode': 210, 'delta_beam': 1, 'fiducial': 0, 'comment': 'Rayonix' }) if readout_all: seqstep += 1 seq_steps[seqstep].configure({ 'eventcode': 198, 'delta_beam': 0, 'fiducial': 0, 'comment': 'DAQ Readout' }) seqstep += 1 seq_steps[seqstep].configure({ 'eventcode': 198, 'delta_beam': 0, 'fiducial': 0, 'comment': 'DAQ Readout' }) # Clear other sequence steps seqstep += 1 sequencer.sequence_length.put(seqstep) for i in range(seqstep, 20): seq_steps[i].clear() ###################### # Scanning Functions # ###################### def perform_run(self, events, record=True, comment='', post=True, **kwargs): """ Perform a single run of the experiment Parameters ---------- events: int Number of events to include in run record: bool, optional Whether to record the run comment : str, optional Comment for ELog post: bool, optional Whether to post to the experimental ELog or not. Will not post if not recording """ # time.sleep(3) / a leftover from original script # Create descriptive message comment = comment or '' # Start recording try: logger.info("Starting DAQ run, -> record=%s", record) daq.begin(events=events, record=record) time.sleep( 2) # Wait for the DAQ to get spinnign before sending events logger.debug("Starting EventSequencer ...") sequencer.start() time.sleep(1) # Post to ELog if desired runnum = daq._control.runnumber() info = [runnum, comment, events, self.current_rate] post_msg = post_template.format(*info) print(post_msg) if post and record: elog.post(post_msg, run=runnum) # Wait for the DAQ to finish logger.info("Waiting or DAQ to complete %s events ...", events) daq.wait() logger.info("Run complete!") daq.end_run() logger.debug("Stopping Sequencer ...") sequencer.stop() # allow short time after sequencer stops time.sleep(0.5) except KeyboardInterrupt: logger.warning("Scan interrupted by user request!") logger.info("Stopping DAQ ...") daq.stop() # Return the DAQ to the original state finally: logger.info("Disconnecting from DAQ ...") daq.disconnect() logger.info("Closing all laser shutters ...") self.configure_shutters(pulse1=False, pulse2=False, pulse3=False, opo=False) logger.info("Restarting the EventSequencer ...") sequencer.start() def get_mesh_voltage(self): """ Get the current power supply voltage """ return self.mesh_raw.get() def set_mesh_voltage(self, sigIn, wait=True, do_print=True): """ Set voltage on power supply to an absolute value Parameters ---------- sigIn: int or float Power supply voltage in Volts """ if do_print: print('Setting voltage...') sigInScaled = sigIn / self.SCALE # in V self.mesh_raw.put(sigInScaled) if wait: time.sleep(2.5) finalVolt = self.mesh_raw.get() finalVoltSupply = finalVolt * self.SCALE if do_print: print('Power Supply Setpoint: %s V' % sigIn) print('Power Supply Voltage: %s V' % finalVoltSupply) def set_rel_mesh_voltage(self, deltaVolt, wait=True, do_print=True): """ Increase/decrease power supply voltage by a specified amount Parameters ---------- deltaVolt: int or float Amount to increase/decrease voltage (in Volts) from its current value. Use positive value to increase and negative value to decrease """ if do_print: print('Setting voltage...') curr_set = self.mesh_raw.get_setpoint() curr_set_supply = curr_set * self.SCALE if do_print: print('Previous Power Supply Setpoint: %s V' % curr_set_supply) new_voltage = round(curr_set_supply + deltaVolt) self.set_mesh_voltage(new_voltage, wait=wait, do_print=do_print) def tweak_mesh_voltage(self, deltaVolt): """ Continuously Increase/decrease power supply voltage by specifed amount using arrow keys Parameters ---------- deltaVolt: int or float Amount to change voltage (in Volts) from its current value at each step. After calling with specified step size, use arrow keys to keep changing ^C: exits tweak mode """ print('Use arrow keys (left, right) to step voltage (-, +)') while True: key = key_press.get_input() if key in ('q', None): return elif key == key_press.arrow_right: self.set_rel_mesh_voltage(deltaVolt, wait=False, do_print=False) elif key == key_press.arrow_left: self.set_rel_mesh_voltage(-deltaVolt, wait=False, do_print=False)
class PilatusExtTrigger(PilatusDetector): armed = Cpt(EpicsSignal, "cam1:Armed") # Use self._image_name as in SingleTrigger? def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._num_images = 1 self._acquisition_signal = self.cam.acquire self._counter_signal = self.cam.array_counter #self._trigger_signal = EpicsSignal('XF:16ID-TS{EVR:C1-Out:FP3}Src:Scale-SP') self._trigger_signal = EpicsSignal('XF:16IDC-ES{Zeb:1}:SOFT_IN:B0') self._status = None self.first = True self.acq_t = 0 def set_num_images(self, num_images): self._num_images = num_images def stage(self, multitrigger=True): if self._staged == Staged.yes: return print(self.name, "staging") #self.stage_sigs.update([ # ('cam.trigger_mode', 3), # 2 DOESN'T WORK! # ('cam.num_images', self._num_images) #]) acq_t = self.cam.acquire_period.get() if multitrigger: self.cam.trigger_mode.put(3, wait=True) # ExtMTrigger in camserver self.trig_wait = acq_t + 0.02 else: self.cam.trigger_mode.put(2, wait=True) # ExtTrigger in camserver self.trig_wait = acq_t * self._num_images + 0.02 time.sleep(.1) self.cam.num_images.put(self._num_images, wait=True) print(self.name, "stage sigs updated") super().stage() print(self.name, "super staged") self._counter_signal.put(0) print(self.name, "checking armed status") self._acquisition_signal.put(1) #, wait=True) while self.armed.get() != 1: time.sleep(0.1) print(self.name, "staged") def unstage(self): if self._staged == Staged.no: return self._status = None self._acquisition_signal.put(0) time.sleep(.1) self.cam.trigger_mode.put(0, wait=True) time.sleep(.1) self.cam.num_images.put(1, wait=True) super().unstage() def trigger(self): print(self.name + " trigger") if self._staged != Staged.yes: raise RuntimeError("This detector is not ready to trigger." "Call the stage() method before triggering.") status = DeviceStatus(self) # Only one Pilatus has to send the trigger if self.name == first_PilatusExt(): while pilatus_trigger_lock.locked(): time.sleep(0.005) print("triggering") self._trigger_signal.put(1, wait=True) self._trigger_signal.put(0, wait=True) ##set up callback to clear status after the end-of-exposure threading.Timer(self.trig_wait, status._finished, ()).start() else: status._finished() self.dispatch(f'{self.name}_image', ttime.time()) return status