diff --git a/adhoc.py b/adhoc.py index 2182636..641d832 100644 --- a/adhoc.py +++ b/adhoc.py @@ -3,20 +3,25 @@ from time import sleep from epics import PV -from slic.core.adjustable import PVAdjustable +from slic.core.adjustable import Adjustable, PVAdjustable from slic.devices.device import Device from slic.devices.simpledevice import SimpleDevice from slic.devices.general.motor import Motor from slic.devices.general.delay_stage import DelayStage from slic.devices.general.smaract import SmarActAxis +from slic.devices.general.shutter import Shutter from slic.devices.xoptics.dcm import CoupledDoubleCrystalMonoEnergyWithTimeCorrection +from undulator import Undulators + + laser_pitch = SmarActAxis("SARES11-XICM125:ROX1") laser_trans = SmarActAxis("SARES11-XICM125:TRX1") laser_yaw = SmarActAxis("SARES11-XICM125:ROY1") microscope_pitch = SmarActAxis("SARES11-XMI125:ROY1") microscope_yaw = SmarActAxis("SARES11-XMI125:ROZ1") +laser_mod = PVAdjustable("SIN-TIMAST-TMA:Evt-23-Off-SP", process_time=1, name="Laser Mod") CDCMEWTC = CoupledDoubleCrystalMonoEnergyWithTimeCorrection(limit_low=2450, limit_high=2520) @@ -24,6 +29,252 @@ CDCMEWTC = CoupledDoubleCrystalMonoEnergyWithTimeCorrection(limit_low=2450, limi PSSS = Motor("SARFE10-PSSS059:MOTOR_Y3", name="PSSS XTAL y") opo_delay = PVAdjustable("SLAAR03-LTIM-PDLY:DELAY", name="OPO Delay") +XrayShutter = Shutter("SARFE10-OPSH059") + + + +import requests + +class UndulatorEnergy(PVAdjustable): + + def __init__(self, process_time=1): + + self.process_time = process_time + super().__init__("SARUN:USER-DELTA", "SARUN03-UIND030:FELPHOTENE", process_time=0.2) # process_time here is only to insert the delta into the panel +# assert self.units == "keV" + print('units={}'.format(self.units)) + self.units = "eV" + + def set_target_value(self, value): + #value /= 1000 + current = self.get_current_value() + delta = (value - current) + print(f"delta: {value} - {current} = {delta}") + delta /= 1000 + task = super().set_target_value(delta) + sleep(0.1) +# task.wait() # make sure the delta is set correctly + #TODO: the following should be a task? + url_go = "http://sf-daq-mgmt.psi.ch:8090/run/Undulators/K_AR_scale" + reply = requests.get(url_go)#.json() + print('Pressing "GO" produced:', reply) + sleep(self.process_time) # wait for undulator change + return task + + def get_current_value(self): + value = super().get_current_value() - 0.02037 + value *= 1000 + return value + + def stop(self): + url_abort = "http://sf-daq-mgmt.psi.ch:8090/abort" + reply = requests.get(url_abort)#.json() + print('Pressing "Abort" produced:', reply) + + + + + +class UndulatorCoupledDoubleCrystalMonoEnergy(Adjustable): + + def __init__(self, ID, name=None, process_time=1): +# self.und = UndulatorEnergy(process_time=process_time) + self.und = Undulators() + + pvname_setvalue = "SAROP11-ARAMIS:ENERGY_SP" + pvname_readback = "SAROP11-ARAMIS:ENERGY" + pvname_moving = "SAROP11-ODCM105:MOVING" + + # This is the WRONG coupling, we need to make sure it is disabled! + pvname_ebeam_coupling = "SGE-OP2E-ARAMIS:MODE_SP" + + pv_setvalue = PV(pvname_setvalue) + pv_readback = PV(pvname_readback) + pv_moving = PV(pvname_moving) + pv_ebeam_coupling = PV(pvname_ebeam_coupling) + + units = pv_readback.units + super().__init__(ID, name=name, units=units) + + self.pvnames = SimpleNamespace( + setvalue = pvname_setvalue, + readback = pvname_readback, + moving = pvname_moving, + ebeam_coupling = pvname_ebeam_coupling + ) + + self.pvs = SimpleNamespace( + setvalue = pv_setvalue, + readback = pv_readback, + moving = pv_moving, + ebeam_coupling = pv_ebeam_coupling + ) + + + def get_current_value(self): + return self.pvs.readback.get() + + def set_target_value(self, value, hold=False): + changer = lambda: self.move_and_wait(value) + return self._as_task(changer, hold=hold, stopper=self.stop) + + def move_and_wait(self, value, wait_time=0.1): + self.disable_ebeam_coupling() + self.pvs.setvalue.put(value) + self.und.set_target_value(value) + sleep(3) #TODO: is this correct? + while self.is_moving(): + sleep(wait_time) + + def is_moving(self): + moving = self.pvs.moving.get() + return bool(moving) + +# def enable_ebeam_coupling(self): +# self.pvs.ebeam_coupling.put(1) + + def disable_ebeam_coupling(self): + self.pvs.ebeam_coupling.put(0) + +# @property +# def ebeam_coupling(self): +# return self.pvs.ebeam_coupling.get(as_string=True) + + + + + +class UndulatorCoupledDoubleCrystalMonoEnergyWithTimeCorrection(Adjustable): + + def __init__(self, ID="UCDCMEWTC", name="Alvra DCM Undulator-coupled energy with time correction", limit_low=None, limit_high=None, process_time=1): +# self.und = UndulatorEnergy(process_time=process_time) + self.und = Undulators() + + self.wait_time = 0.1 + + self.limit_low = limit_low + self.limit_high = limit_high + + pvname_setvalue = "SAROP11-ARAMIS:ENERGY_SP" + pvname_readback = "SAROP11-ARAMIS:ENERGY" + pvname_moving = "SAROP11-ODCM105:MOVING" + + # This is the WRONG coupling, we need to make sure it is disabled! + pvname_ebeam_coupling = "SGE-OP2E-ARAMIS:MODE_SP" + + pv_setvalue = PV(pvname_setvalue) + pv_readback = PV(pvname_readback) + pv_moving = PV(pvname_moving) + pv_ebeam_coupling = PV(pvname_ebeam_coupling) + + self.timing = Motor("SLAAR11-LMOT-M452:MOTOR_1") +# self.electron_energy_rb = PV("SARCL02-MBND100:P-READ") +# self.electron_energy_sv = PV("SGE-OP2E-ARAMIS:E_ENERGY_SP") + + units = pv_readback.units + super().__init__(ID, name=name, units=units) + + self.pvnames = SimpleNamespace( + setvalue = pvname_setvalue, + readback = pvname_readback, + moving = pvname_moving, + ebeam_coupling = pvname_ebeam_coupling + ) + + self.pvs = SimpleNamespace( + setvalue = pv_setvalue, + readback = pv_readback, + moving = pv_moving, + ebeam_coupling = pv_ebeam_coupling + ) + + + def get_current_value(self): + return self.pvs.readback.get() + + def set_target_value(self, value, hold=False): + ll = self.limit_low + if ll is not None: + if value < ll: + msg = f"requested value is outside the allowed range: {value} < {ll}" + print(msg) + raise KeyboardInterrupt(msg) + lh = self.limit_high + if lh is not None: + if value > lh: + msg = f"requested value is outside the allowed range: {value} > {lh}" + print(msg) + raise KeyboardInterrupt(msg) + + wait_time = self.wait_time + + self.disable_ebeam_coupling() + + current_energy = self.get_current_value() + delta_energy = value - current_energy + + timing = self.timing + current_delay = timing.get_current_value() + delta_delay = convert_E_to_distance(delta_energy) + target_delay = current_delay + delta_delay + + print(f"Energy = {current_energy} -> delta = {delta_energy} -> {value}") + print(f"Delay = {current_delay} -> delta = {delta_delay} -> {target_delay}") + + timing.set_target_value(target_delay).wait() + + self.move_and_wait(value) + sleep(3) # wait so that the set value has changed + + print("start waiting for DCM") + while self.is_moving(): #TODO: moving PV seems broken + sleep(wait_time) +# print("start waiting for electron beam") +# while abs(self.electron_energy_rb.get() - self.electron_energy_sv.get()) > 0.25: +# sleep(wait_time) + sleep(wait_time) + + + def move_and_wait(self, value, wait_time=0.1): + self.disable_ebeam_coupling() + self.pvs.setvalue.put(value) + self.und.set_target_value(value) + sleep(3) #TODO: is this correct? + while self.is_moving(): + sleep(wait_time) + + + def is_moving(self): + moving = self.pvs.moving.get() + return bool(moving) + +# def enable_ebeam_coupling(self): +# self.pvs.ebeam_coupling.put(1) + + def disable_ebeam_coupling(self): + self.pvs.ebeam_coupling.put(0) + +# @property +# def ebeam_coupling(self): +# return self.pvs.ebeam_coupling.get(as_string=True) + + + +# adjust this function!! +# be careful, CDCMEWTC has its own conversion!!! +def convert_E_to_distance(E): ### returns a value in mm! + #return 0.0061869 * E ### calibration May 2021, S k-edge, 2450 eV, corresponds to 41.2 fs/eV + #return 0.00524244 * E ### calibration Nov 2021, Rh L-edge, 3000 eV, corresponds to 35 fs/eV + return 0.0005445 * E ### calibration Mar 2022, Mn K-edge, 6500 eV, corresponds to 3.6 fs/eV + + + +#und = UndulatorEnergy() +mono_und = UndulatorCoupledDoubleCrystalMonoEnergy("MONO_UND", name="Alvra DCM Undulator-coupled energy") +UCDCMEWTC = UndulatorCoupledDoubleCrystalMonoEnergyWithTimeCorrection(limit_low=2450, limit_high=2520) + + + class TXS(Device):