diff --git a/eco/acquisition/daq_client.py b/eco/acquisition/daq_client.py index 535a059..4ac3513 100644 --- a/eco/acquisition/daq_client.py +++ b/eco/acquisition/daq_client.py @@ -226,24 +226,24 @@ class Daq(Assembly): def get_next_run_number(self, pgroup=None): if pgroup is None: pgroup = self.pgroup - res = requests.get( - f"{self.broker_address}/get_next_run_number", + res = requests.post( + f"{self.broker_address}/advance_run_number", json={"pgroup": pgroup}, timeout=self.timeout, ) assert res.ok, f"Getting last run number failed {res.raise_for_status()}" - return int(res.json()["message"]) + return int(res.json()["run_number"]) def get_last_run_number(self, pgroup=None): if pgroup is None: pgroup = self.pgroup res = requests.get( - f"{self.broker_address}/get_last_run_number", + f"{self.broker_address}/get_current_run_number", json={"pgroup": pgroup}, timeout=self.timeout, ) assert res.ok, f"Getting last run number failed {res.raise_for_status()}" - return int(res.json()["message"]) + return int(res.json()["run_number"]) def get_detector_frequency(self): return self._event_master.event_codes[ @@ -251,12 +251,12 @@ class Daq(Assembly): ].frequency.get_current_value() def get_JFs_available(self): - return requests.get(f"{self.broker_address}/get_allowed_detectors_list").json()[ + return requests.get(f"{self.broker_address}/get_allowed_detectors").json()[ "detectors" ] def get_JFs_running(self): - return requests.get(f"{self.broker_address}/get_running_detectors_list").json()[ + return requests.get(f"{self.broker_address}/get_running_detectors").json()[ "detectors" ] diff --git a/eco/acquisition/epics_data.py b/eco/acquisition/epics_data.py index fa5830f..23dd6ca 100644 --- a/eco/acquisition/epics_data.py +++ b/eco/acquisition/epics_data.py @@ -9,23 +9,29 @@ from time import sleep from pathlib import Path from .utilities import Acquisition import time - +from ..elements.adjustable import AdjustableFS class EpicsDaq: def __init__( self, - default_file_path="%s", elog=None, name=None, channel_list=None, ): self.name = name - self._default_file_path = default_file_path + self.default_file_path = AdjustableFS(f'/sf/bernina/config/eco/reference_values/{name}_default_file_path.json', default_value="~/data/", name="default_file_path") self._elog = elog self.channels = {} self.pulse_id = PV("SLAAR11-LTIM01-EVR0:RX-PULSEID") self.channel_list = channel_list - + self.update_channels() + @property + def _default_file_path(self): + return self.default_file_path() + @_default_file_path.setter + def _default_file_path(self, val): + self.default_file_path(val) + def update_channels(self): channels = self.channel_list.get_current_value() for channel in channels: @@ -33,7 +39,10 @@ class EpicsDaq: self.channels[channel] = PV(channel, auto_monitor=True) def h5(self, fina=None, channel_list=None, N_pulses=None, queue_size=100): - channel_list = self.channel_list + if channel_list is None: + channel_list = self.channel_list + if not channel_list.get_current_value() == list(self.channels.keys()): + self.update_channels() if os.path.isfile(fina): print("!!! File %s already exists, would you like to delete it?" % fina) @@ -43,10 +52,10 @@ class EpicsDaq: else: return - data = [] - counters = [] + data = {} + counters = {} channels = self.channels - for channel in channels: + for k, channel in channels.items(): channelval = channel.value if type(channelval) == np.ndarray: shape = (N_pulses,) + channelval.shape @@ -54,26 +63,26 @@ class EpicsDaq: else: shape = (N_pulses,) dtype = type(channelval) - data.append(np.ndarray(shape, dtype=dtype)) - counters.append(0) + data[k]=np.ndarray(shape, dtype=dtype) + counters[k]=0 - def cb_getdata(ch=None, m=0, *args, **kwargs): - data[m][counters[m]] = kwargs["value"] - counters[m] = counters[m] + 1 - if counters[m] == N_pulses: + def cb_getdata(ch=None, k="", *args, **kwargs): + data[k][counters[k]] = kwargs["value"] + counters[k] = counters[k] + 1 + if counters[k] == N_pulses: ch.clear_callbacks() - for (m, channel) in enumerate(channels): - channel.add_callback(callback=cb_getdata, ch=channel, m=m) + for k, channel in channels.items(): + channel.add_callback(callback=cb_getdata, ch=channel, k=k) while True: sleep(0.005) - if np.mean(counters) == N_pulses: + if np.mean(list(counters.values())) == N_pulses: break f = h5py.File(name=fina, mode="w") - for (n, channel) in enumerate(channel_list): - dat = f.create_group(name=channel) - dat.create_dataset(name="data", data=data[n]) + for k in channels.keys(): + dat = f.create_group(name=k) + dat.create_dataset(name="data", data=data[k]) dat.create_dataset( name="pulse_id", data=np.arange(N_pulses) + round(time.time() * 100) ) diff --git a/eco/bernina/bernina.py b/eco/bernina/bernina.py index 894f5b5..f95d170 100644 --- a/eco/bernina/bernina.py +++ b/eco/bernina/bernina.py @@ -341,7 +341,7 @@ namespace.append_obj( # }, name="mon_mono", module_name="eco.xdiagnostics.intensity_monitors", - # pipeline_computation="SAROP21-PBPS103_proc", + pipeline_computation="SAROP21-PBPS103_proc", lazy=True, ) @@ -648,6 +648,7 @@ namespace.append_obj( "right": "SARES21-PBPS141:Lnk9Ch0-PP_VAL_PD3", }, module_name="eco.xdiagnostics.intensity_monitors", + pipeline_computation="SARES21-PBPS141_proc", name="mon_kb", lazy=True, ) @@ -1160,14 +1161,13 @@ namespace.append_obj( # ) ### draft new epics daq ### -# namespace.append_obj( -# "EpicsDaq", -# default_file_path=f"/sf/bernina/data/{config_berninamesp['pgroup']}/res/epics_daq/", -# channels_list=channels_CA_epicsdaq, -# name="daq_epics_local", -# module_name="eco.acquisition.epics_data", -# lazy=True, -# ) +namespace.append_obj( + "EpicsDaq", + channel_list=channels_CA_epicsdaq, + name="daq_epics_local", + module_name="eco.acquisition.epics_data", + lazy=True, +) ### old epics daq ### # namespace.append_obj( # "ChannelList", @@ -1184,17 +1184,18 @@ namespace.append_obj( # module_name="eco.acquisition.epics_data", # ) -# namespace.append_obj( -# "Scans", -# name="scans_epics", -# module_name="eco.acquisition.scan", -# data_base_dir="scan_data", -# scan_info_dir=f"/sf/bernina/data/{config_berninamesp['pgroup']}/res/scan_info", -# default_counters=[epics_daq], -# checker=checker_epics, -# scan_directories=True, -# run_table=run_table, -# ) +namespace.append_obj( + "Scans", + name="scans_epics", + module_name="eco.acquisition.scan", + data_base_dir=f"{config_bernina.pgroup()}/scan_data", + scan_info_dir=f"{daq_epics_local.default_file_path()}/{config_bernina.pgroup()}/scan_info", + default_counters=[daq_epics_local], + checker=None, + scan_directories=True, + run_table=None, + lazy=True, +) # # ##### standard DAQ ####### @@ -1861,17 +1862,17 @@ namespace.append_obj( "CameraBasler", "SARES20-CAMS142-M2", lazy=True, + name="samplecam_sideview_45", + module_name="eco.devices_general.cameras_swissfel", +) + +namespace.append_obj( + "CameraBasler", + "SARES20-CAMS142-C1", + lazy=True, name="samplecam_sideview", module_name="eco.devices_general.cameras_swissfel", ) -#namespace.append_obj( -# "CameraBasler", -# "SARES20-CAMS142-C2", -# lazy=True, -# name="samplecam_inline", -# module_name="eco.devices_general.cameras_swissfel", -#) - namespace.append_obj( "OxygenSensor", @@ -2112,7 +2113,21 @@ namespace.append_obj( "name": "helium_control_valve", }, # configuration=["ottifant"], - configuration=[], + configuration=["cube"], +) + +namespace.append_obj( + "Organic_crystal_breadboard", + lazy=True, + name="ocb", + module_name="eco.endstations.bernina_sample_environments", +) + +namespace.append_obj( + "Electro_optic_sampling", + lazy=True, + name="eos", + module_name="eco.endstations.bernina_sample_environments", ) # class Sample_stages(Assembly): @@ -2499,14 +2514,14 @@ namespace.append_obj( module_name="eco.xoptics.dcm_new", ) -namespace.append_obj( - "AramisDcmFeedback", - mono=mono, - xbpm=mon_opt, - name="mono_feedback", - lazy=True, - module_name="eco.xoptics.xopt_feedback", -) +# namespace.append_obj( +# "AramisDcmFeedback", +# mono=mono, +# xbpm=mon_opt, +# name="mono_feedback", +# lazy=True, +# module_name="eco.xoptics.xopt_feedback", +# ) # namespace.append_obj( @@ -2600,7 +2615,7 @@ from eco.loptics.bernina_laser import Stage_LXT_Delay namespace.append_obj( "StageLxtDelay", - pumpdelay.pdelay, + las.delay_pump, las, lazy=True, name="lxt", @@ -2627,7 +2642,8 @@ try: pgroup_eco_path = TimeoutPath( f"/sf/bernina/data/{config_bernina.pgroup()}/res/eco" ) - pgroup_eco_path.mkdir(mode=775, exist_ok=True) + pgroup_eco_path.mkdir(mode=0o775, exist_ok=True) + pgroup_eco_path.chmod(mode=0o775) sys.path.append(pgroup_eco_path.as_posix()) else: print( @@ -2744,10 +2760,10 @@ class IlluminatorsLasers(Assembly): def __init__(self, name="sample_illumination"): super().__init__(name=name) self._append( - MpodChannel, - pvbase="SARES21-CPCL-PS7071", - channel_number=5, - name="illumination_inline", + MpodChannel, + pvbase="SARES21-CPCL-PS7071", + channel_number=5, + name="illumination_inline", ) self._append( MpodChannel, @@ -2755,18 +2771,18 @@ class IlluminatorsLasers(Assembly): channel_number=2, name="illumination_side", ) - self._append( - MpodChannel, - pvbase="SARES21-CPCL-PS7071", - channel_number=6, - name="illumination_top", - ) - self._append( - MpodChannel, - pvbase="SARES21-CPCL-PS7071", - channel_number=4, - name="flattening_laser", - ) + #self._append( + # MpodChannel, + # pvbase="SARES21-CPCL-PS7071", + # channel_number=6, + # name="illumination_top", + #) + #self._append( + # MpodChannel, + # pvbase="SARES21-CPCL-PS7071", + # channel_number=4, + # name="flattening_laser", + #) namespace.append_obj(IlluminatorsLasers, name="sample_illumination", lazy=True) @@ -3052,17 +3068,25 @@ def name2pgroups(name, beamline="bernina"): return eq + ni -namespace.append_obj( - "Jungfrau", - "JF03T01V02", - name="det_i0", - pgroup_adj=config_bernina.pgroup, - module_name="eco.detector", -) -# namespace.append_obj( -# "Jungfrau", -# "JF01T03V01", -# name="det_diff", -# pgroup_adj=config_bernina.pgroup, -# module_name="eco.detector", -# ) + + +def timetool_data_monitor(warning_threshold=1000, loopsleep=5): + dir(bs_worker) + tt_kb.spectrum_signal.stream.accumulate(do_accumulate=True) + print('Monitoring timetool data ...') + + while True: + + eid_diff= int(event_system.pulse_id.get_current_value() - tt_kb.spectrum_signal.stream.eventIds[-1][-1]) + if eid_diff> warning_threshold: + message = f"Last timetool data {eid_diff} pulses ago!" + print(message) + try: + e = pyttsx3.init() + e.say(message) + e.runAndWait() + e.stop() + except: + pass + time.sleep(loopsleep) + diff --git a/eco/detector/jungfrau.py b/eco/detector/jungfrau.py index c899ae2..3752fd7 100644 --- a/eco/detector/jungfrau.py +++ b/eco/detector/jungfrau.py @@ -3,6 +3,7 @@ from tkinter import W from eco.base.adjustable import Adjustable from eco.devices_general.therm import ChillerThermotek +from eco.elements.adj_obj import AdjustableObject from eco.elements.detector import DetectorGet from ..elements.adjustable import AdjustableFS, AdjustableVirtual, AdjustableGetSet from ..epics.adjustable import AdjustablePv @@ -32,6 +33,7 @@ class Jungfrau(Assembly): trigger_on=254, trigger_off=255, broker_address="http://sf-daq:10002", + broker_address_aux="http://sf-daq:10003", pgroup_adj=None, config_adj=None, chiller_thermotek="SARES20-CHIL", @@ -42,6 +44,7 @@ class Jungfrau(Assembly): self.pgroup = pgroup_adj self.jf_id = jf_id self.broker_address = broker_address + self.broker_address_aux = broker_address_aux self._append( DetectorGet, lambda: f"http://{self.get_vis_url()}", name="visulization_url" ) @@ -107,6 +110,21 @@ class Jungfrau(Assembly): name="gain_file_in_run", is_display=True, ) + self._append( + AdjustableGetSet, + self.get_dap_settings, + self.set_dap_settings, + name="_dap_settings", + is_display=False, + is_setting=False, + ) + self._append( + AdjustableObject, + self._dap_settings, + is_setting_children=True, + name="settings_dap", + ) + if config_adj: self._append( JungfrauDaqConfig, @@ -118,7 +136,12 @@ class Jungfrau(Assembly): is_display="recursive", ) if chiller_thermotek: - self._append(ChillerThermotek,pvbase=chiller_thermotek,name="chiller",is_display="recursive") + self._append( + ChillerThermotek, + pvbase=chiller_thermotek, + name="chiller", + is_display="recursive", + ) def _set_trigger_enable(self, value): if value: @@ -139,8 +162,14 @@ class Jungfrau(Assembly): dest = Path( f"/sf/bernina/data/{self.pgroup()}/res/tmp/gainmaps_{self.jf_id}.h5" ) - if not dest.exists(): - shutil.copyfile(f, dest) + + try: + if not dest.exists(): + dest.parent.mkdir(parents=True, exist_ok=True, mode=0o775) + shutil.copyfile(f, dest) + except PermissionError: + return "No permissions to res directory!" + if intempdir: return dest.as_posix() else: @@ -157,14 +186,38 @@ class Jungfrau(Assembly): dest = Path( f"/sf/bernina/data/{self.pgroup()}/res/tmp/pedestal_{self.jf_id}_{f.stem}.h5" ) - if not dest.exists(): - shutil.copyfile(f, dest) + try: + if not dest.exists(): + dest.parent.mkdir(parents=True, exist_ok=True, mode=0o775) + dest.parent.chmod(0o775) + shutil.copyfile(f, dest) + except PermissionError: + return "No poermissions to res directory!" + if intempdir: return dest.as_posix() else: return f"aux/{dest.name}" + def get_dap_settings(self): + m = requests.get( + f"{self.broker_address_aux}/get_dap_settings", + json={"detector_name": self.jf_id}, + ).json() + if m["status"] == "ok": + return m["parameters"] + + def set_dap_settings(self, dap_setting_dict): + # print("Setting not implmented yet!") + # return + m = requests.post( + f"{self.broker_address_aux}/set_dap_settings", + json={"detector_name": self.jf_id, "parameters": dap_setting_dict}, + ).json() + if m["status"] == "ok": + return m + def get_detector_frequency(self): return self._event_master.event_codes[ self._detectors_event_code @@ -173,21 +226,21 @@ class Jungfrau(Assembly): def get_availability(self): is_available = ( self.jf_id - in requests.get(f"{self.broker_address}/get_allowed_detectors_list").json()[ + in requests.get(f"{self.broker_address}/get_allowed_detectors").json()[ "detectors" ] ) return is_available def get_vis_url(self): - tmp = requests.get(f"{self.broker_address}/get_allowed_detectors_list").json() + tmp = requests.get(f"{self.broker_address}/get_allowed_detectors").json() ix = tmp["detectors"].index(self.jf_id) return tmp["visualisation_address"][ix] def get_isrunning(self): is_running = ( self.jf_id - in requests.get(f"{self.broker_address}/get_running_detectors_list").json()[ + in requests.get(f"{self.broker_address}/get_running_detectors").json()[ "detectors" ] ) diff --git a/eco/devices_general/motors.py b/eco/devices_general/motors.py index cc6977c..591b9f4 100755 --- a/eco/devices_general/motors.py +++ b/eco/devices_general/motors.py @@ -554,7 +554,7 @@ class PshellMotor(Assembly): # # s += "\tuser limits (low,high) : {:1.6g},{1.6g}".format(self.get_limits()) else: s += ( - f"\t@ {colorama.Style.BRIGHT}{'NOT CONECTED'}{colorama.Style.RESET_ALL}" + f"\t@ {colorama.Style.BRIGHT}{'NOT CONNECTED'}{colorama.Style.RESET_ALL}" ) return s @@ -563,7 +563,156 @@ class PshellMotor(Assembly): print(str(self)) return object.__repr__(self) +class AdjustablePiHex(AdjustablePv): + def __init__(self, pvname=None, pvreadbackname=None, accuracy=None, unit=None, name=None): + super().__init__(pvname, pvreadbackname=pvreadbackname, accuracy=accuracy, unit=unit, name=name) + self.limit_high = AdjustableFS(f'/sf/bernina/config/eco/reference_values/hex_pi_{name}_limit_high.json', default_value=0) + self.limit_low = AdjustableFS(f'/sf/bernina/config/eco/reference_values/hex_pi_{name}_limit_low.json', default_value=0) + def move(self, value, check=False): + if check: + if self.limit_low: + if value < self.limit_low(): + raise Exception( + f"Target value of {self.name} is smaller than limit value!" + ) + if self.limit_high: + if self.limit_high() < value: + raise Exception( + f"Target value of {self.name} is higher than limit value!" + ) + self._pv.put(value) + time.sleep(0.1) + while self.get_change_done() == 0: + time.sleep(0.1) + def change(self, value): + return self.move(value) + + def get_limits(self): + return (self.limit_low(), self.limit_high()) + + def set_limits(self, limit_low, limit_high): + self.limit_low(limit_low) + self.limit_high(limit_high) + + + +@spec_convenience +@update_changes +@get_from_archive +@value_property +class ThorlabsPiezoRecord(Assembly): + def __init__(self, pvname=None, accuracy=0.1, unit=None, name=None): + super().__init__(name=name) + self.pvname=pvname + self._cb = None + self._append( + AdjustablePv, + self.pvname + ":DRIVE", + pvreadbackname=self.pvname+":MOTRBV", + accuracy = accuracy, + unit=unit, + name="pos", + is_setting=True, + is_display=True, + ) + self._append( + AdjustablePv, + self.pvname+":HLM", + name="limit_high", + is_setting=True, + is_status=False, + is_display=False, + ) + self._append( + AdjustablePv, + self.pvname+":LLM", + name="limit_low", + is_setting=True, + is_status=False, + is_display=False, + ) + self._append( + AdjustablePv, + self.pvname + ":FRM_FORW.PROC", + name="home_forward", + is_setting=False, + is_status=False, + is_display=False, + ) + self._append( + AdjustablePv, + self.pvname + ":FRM_BACK.PROC", + name="home_backward", + is_setting=False, + is_status=False, + is_display=False, + ) + self._append( + AdjustablePv, + self.pvname + ":STOP.PROC", + name="_stop_pv", + is_setting=False, + is_status=False, + is_display=False, + ) + def stop(self): + self._stop_pv(1) + + def move(self, value, check=True, wait=False): + if check: + if self.limit_low: + if value < self.limit_low(): + raise Exception( + f"Target value of {self.name} is smaller than limit value!" + ) + if self.limit_high: + if self.limit_high() < value: + raise Exception( + f"Target value of {self.name} is higher than limit value!" + ) + self.pos(value) + if wait: + time.sleep(0.02) + while self.pos.get_change_done() == 0: + time.sleep(0.02) + + def get_limits(self): + return (self.limit_low(), self.limit_high()) + + def set_limits(self, limit_low, limit_high): + self.limit_low(limit_low) + self.limit_high(limit_high) + + def get_current_value(self): + return self.pos() + + def set_target_value(self, value, hold=False, check=True): + changer = lambda value: self.move(value, check=check, wait=True) + return Changer( + target=value, + parent=self, + changer=changer, + hold=hold, + stopper=self.stop, + ) + # return string with motor value as variable representation + def __str__(self): + # """ return short info for the current motor""" + s = f"{self.name}" + # s += f"\t@ {colorama.Style.BRIGHT}{self.get_current_value():1.6g}{colorama.Style.RESET_ALL} stat: {self.status_flag().name}" + s += f"\t@ {colorama.Style.BRIGHT}{self.get_current_value():1.6g}{colorama.Style.RESET_ALL}" + # # s += "\tuser limits (low,high) : {:1.6g},{:1.6g}\n".format(*self.get_limits()) + s += f"\n{colorama.Style.DIM}low limit {colorama.Style.RESET_ALL}" + s += ValueInRange(*self.get_limits()).get_str(self.get_current_value()) + s += f" {colorama.Style.DIM}high limit{colorama.Style.RESET_ALL}" + # # s += "\tuser limits (low,high) : {:1.6g},{1.6g}".format(self.get_limits()) + return s + + def __repr__(self): + print(str(self)) + return object.__repr__(self) + @spec_convenience @update_changes @get_from_archive @@ -1222,7 +1371,7 @@ class SmaractRecord(Assembly): self._append( AdjustablePv, - self.pvname + ".HOMR", + self.pvname + ".HOMF", name="home_forward", is_setting=False, is_status=False, diff --git a/eco/elements/assembly.py b/eco/elements/assembly.py index f9561a4..cf1e966 100644 --- a/eco/elements/assembly.py +++ b/eco/elements/assembly.py @@ -380,7 +380,11 @@ class Assembly: tab.append( [".".join([main_name, name]), value, unit, typechar, description] ) - s = tabulate(tab, tablefmt=tablefmt, maxcolwidths=maxcolwidths) + if tab: + s = tabulate(tab, tablefmt=tablefmt, maxcolwidths=maxcolwidths) + else: + s='' + return s def status_to_elog( diff --git a/eco/endstations/bernina_robots.py b/eco/endstations/bernina_robots.py index 585cd44..feffe7d 100644 --- a/eco/endstations/bernina_robots.py +++ b/eco/endstations/bernina_robots.py @@ -156,7 +156,7 @@ class StaeubliTx200(Assembly): def stop(self): try: - self.z_lin.stop() + self.cartesian.z_lin.stop() except: print("Failed to stop linear axis") self.get_eval_result("robot.stop()") @@ -213,6 +213,10 @@ class StaeubliTx200(Assembly): vals = {k: v for k, v in zip(["gamma", "delta", "r"], [gamma,delta,t_det]) if not v is None} return self.get_eval_result(cmd=f"robot.sph2cart(**{vals})") + def remote_connection_to_server(self, resolution="2048x1280"): + cmd = f"xfreerdp /v:PC14742 /size:{resolution} /u:gac-bernina@psich" + return self._run_cmd(cmd) + ######## Motion recording ########## def record_motion(self, **kwargs): """ @@ -275,7 +279,7 @@ class StaeubliTx200(Assembly): Simulated stored commands on the controller. """ sim = np.array(self.get_eval_result(f"robot.simulate_stored_commands()")) - lin = np.array([self.z_lin()]*len(sim)) + lin = np.array([self.cartesian.z_lin()]*len(sim)) sim = np.vstack([lin,sim.T]).T if plot: if self._urdf is not None: @@ -303,7 +307,7 @@ class StaeubliTx200(Assembly): coordinates is returned. Setting coordinates only has an effect, when the motion is simulated. """ sim = np.array(self.get_eval_result(f"robot.move_spherical(r={t_det}, gamma={gamma}, delta={delta}, simulate=True, coordinates='{coordinates}')")) - lin = np.array([self.z_lin()]*len(sim)) + lin = np.array([self.cartesian.z_lin()]*len(sim)) sim = np.vstack([lin,sim.T]).T if plot: if self._urdf is not None: @@ -323,7 +327,7 @@ class StaeubliTx200(Assembly): Simulated motion in the joint coordinate system. """ sim = np.array(self.get_eval_result(f"robot.move_joint(j1={j1}, j2={j2}, j3={j3}, j4={j4}, j5={j5}, j6={j6}, simulate=True")) - lin = np.array([self.z_lin()]*11) + lin = np.array([self.cartesian.z_lin()]*11) sim = np.vstack([lin,sim.T]).T if plot: if self._urdf is not None: @@ -350,7 +354,7 @@ class StaeubliTx200(Assembly): coordinates is returned. Setting coordinates only has an effect, when the motion is simulated. """ sim = np.array(self.get_eval_result(f"robot.move_cartesian(x={x}, y={y}, z={z}, rx={rx}, ry={ry}, rz={rz}, simulate=True, coordinates='{coordinates}')")) - lin = np.array([self.z_lin()]*11) + lin = np.array([self.cartesian.z_lin()]*11) sim = np.vstack([lin,sim.T]).T if plot: if self._urdf is not None: diff --git a/eco/endstations/bernina_sample_environments.py b/eco/endstations/bernina_sample_environments.py index 31ad3d4..2c29203 100644 --- a/eco/endstations/bernina_sample_environments.py +++ b/eco/endstations/bernina_sample_environments.py @@ -2,7 +2,7 @@ from eco.devices_general.powersockets import MpodChannel import sys sys.path.append("..") -from ..devices_general.motors import MotorRecord, SmaractRecord +from ..devices_general.motors import MotorRecord, SmaractRecord, ThorlabsPiezoRecord from ..epics.adjustable import AdjustablePv import numpy as np from epics import PV @@ -90,6 +90,53 @@ class High_field_thz_chamber(Assembly): }, } + + self.motor_configuration_cube = { + "inc_rz": { + "id": "SARES23-USR:MOT_5", + "pv_descr": "Module2:2 THz Inc Cube Rz", + "direction": 1, + "sensor": 53, + "speed": 250, + "home_direction": "back", + "kwargs": {"accuracy": 0.01}, + }, + "inc_z": { + "id": "SARES23-USR:MOT_4", + "pv_descr": "Module2:1 THz Inc Cube z ", + "direction": 1, + "sensor": 42, + "speed": 250, + "home_direction": "back", + }, + "inc_x": { + "id": "SARES23-USR:MOT_6", + "pv_descr": "Module2:3 THz Inc Cube x ", + "direction": 1, + "sensor": 42, + "speed": 250, + "home_direction": "forward", + }, + "inc_ry": { + "id": "SARES23-USR:MOT_18", + "pv_descr": "Module6:3 THz Inc Cube Ry ", + "direction": 0, + "sensor": 2, + "speed": 250, + "home_direction": "forward", + }, + } + + self.motor_configuration_ocb = { + "inc_x": { + "id": "SARES23-USR:MOT_6", + "pv_descr": "Module2:3 THz Inc Cube x ", + "direction": 1, + "sensor": 42, + "speed": 250, + "home_direction": "forward", + }, + } ### lakeshore temperatures #### self._append( AdjustablePv, @@ -136,6 +183,25 @@ class High_field_thz_chamber(Assembly): name=name, is_setting=True, ) + + if "cube" in configuration: + for name, config in self.motor_configuration_cube.items(): + self._append( + SmaractRecord, + pvname=config["id"], + name=name, + is_setting=True, + ) + def home_smaract_stages_cube(): + return self.home_smaract_stages(motor_configuration=self.motor_configuration_cube) + def set_stage_config_cube(): + return self.set_stage_config(cfg=self.motor_configuration_cube) + self.home_smaract_stages_cube = home_smaract_stages_cube + self.set_stage_config_cube = set_stage_config_cube + + if "ocb" in configuration: + pass + self._append( AdjustableFS, "/sf/bernina/config/eco/reference_values/thc_par_in_pos", @@ -180,6 +246,7 @@ class High_field_thz_chamber(Assembly): if illumination_mpod: for illu in illumination_mpod: self._append(MpodChannel,illu['pvbase'], illu['channel_number'], module_string=illu['module_string'], name=illu['name']) + if helium_control_valve: self._append(MpodChannel,helium_control_valve['pvbase'], helium_control_valve['channel_number'], module_string=helium_control_valve['module_string'], name="_helium_valve_mpod_ch", is_display=True, is_setting=True) @@ -198,7 +265,6 @@ class High_field_thz_chamber(Assembly): else: voltage = val*(5.5-2.9)/100+2.9 return voltage - self._append(AdjustableVirtual, [self._helium_valve_mpod_ch.voltage], get_valve, set_valve, name=helium_control_valve["name"], is_display=True, is_setting=False) def moveout(self): @@ -224,8 +290,10 @@ class High_field_thz_chamber(Assembly): self.x.set_target_value(self.par_in_pos()[0]).wait() self.z.set_target_value(self.par_in_pos()[1]) - def set_stage_config(self): - for name, config in self.motor_configuration.items(): + def set_stage_config(self, cfg=None): + if cfg is None: + cfg = self.motor_configuration + for name, config in cfg.items(): mot = self.__dict__[name] mot.description(config["pv_descr"]) #mot.stage_type(config["type"]) @@ -235,13 +303,14 @@ class High_field_thz_chamber(Assembly): sleep(0.5) mot.calibrate_sensor() - def home_smaract_stages(self, stages=None): - if stages == None: - stages = self.motor_configuration.keys() + def home_smaract_stages(self, motor_configuration=None): + if motor_configuration == None: + motor_configuration= self.motor_configuration + stages = motor_configuration.keys() print("#### Positions before homing ####") print(self.__repr__()) for name in stages: - config = self.motor_configuration[name] + config = motor_configuration[name] mot = self.__dict__[name] print( "#### Homing {} in {} direction ####".format( @@ -272,6 +341,7 @@ class High_field_thz_chamber(Assembly): ) mot.home_reverse(1) + def calc_otti( self, otti_nu=None, otti_del=None, otti_det=None, plotit=True, **kwargs ): @@ -291,136 +361,173 @@ class High_field_thz_chamber(Assembly): class Organic_crystal_breadboard(Assembly): - def __init__(self, name=None, Id=None, alias_namespace=None): + def __init__(self, name=None, alias_namespace=None): super().__init__(name=name) - self.Id = Id - self.name = name - self.alias = Alias(name) - self.motor_configuration = { - "mir_x": { - # "id": "-LIC17", - "id": "-USR:MOT_8", - "pv_descr": "Motor8:2 THz mirror x ", - "type": 1, - "sensor": 13, - "speed": 250, - "home_direction": "back", - }, - "mir_rz": { - # "id": "-LIC18", - "id": "-USR:MOT_9", - "pv_descr": "Motor8:3 THz mirror rz ", - "type": 1, - "sensor": 13, - "speed": 250, - "home_direction": "back", - }, - "mir_ry": { - # "id": "-ESB1", - "id": "-LIC:MOT_18", - "pv_descr": "Motor3:1 THz mirror ry ", - "type": 2, - "sensor": 1, - "speed": 250, - "home_direction": "forward", - }, - "mir_z": { - # "id": "-LIC16", - "id": "-USR:MOT_7", - "pv_descr": "Motor8:1 THz mirror z", - "type": 1, - "sensor": 13, - "speed": 250, - "home_direction": "back", - }, - "par_x": { - # "id": "-ESB3", - "id": "-LIC:MOT_17", - "pv_descr": "Motor3:3 THz parabola2 x", - "type": 1, - "sensor": 0, - "speed": 250, - "home_direction": "back", - }, - "delaystage_thz": { - # "id": "-ESB18", - "id": "-USR:MOT_1", - "pv_descr": "Motor8:3 NIR delay stage", - "type": 1, - "sensor": 0, - "speed": 100, - "home_direction": "back", - }, + #"mir_x": { + # # "id": "-LIC17", + # "id": "-USR:MOT_8", + # "pv_descr": "Motor8:2 THz mirror x ", + # "type": 1, + # "sensor": 13, + # "speed": 250, + # "home_direction": "back", + #}, + #"mir_rz": { + # # "id": "-LIC18", + # "id": "-USR:MOT_9", + # "pv_descr": "Motor8:3 THz mirror rz ", + # "type": 1, + # "sensor": 13, + # "speed": 250, + # "home_direction": "back", + #}, + #"mir_ry": { + # # "id": "-ESB1", + # "id": "-LIC:MOT_18", + # "pv_descr": "Motor3:1 THz mirror ry ", + # "type": 2, + # "sensor": 1, + # "speed": 250, + # "home_direction": "forward", + #}, + #"mir_z": { + # # "id": "-LIC16", + # "id": "-USR:MOT_7", + # "pv_descr": "Motor8:1 THz mirror z", + # "type": 1, + # "sensor": 13, + # "speed": 250, + # "home_direction": "back", + #}, + #"par_x": { + # # "id": "-ESB3", + # "id": "-LIC:MOT_17", + # "pv_descr": "Motor3:3 THz parabola2 x", + # "type": 1, + # "sensor": 0, + # "speed": 250, + # "home_direction": "back", + #}, + #"delaystage_thz": { + # "id": "-USR:MOT_1", + # "pv_descr": "Motor8:3 NIR delay stage", + # "type": 1, + # "sensor": 0, + # "speed": 100, + # "home_direction": "back", + #}, "nir_m1_ry": { - # "id": "-ESB17", - "id": "-USR:MOT_3", - "pv_descr": "Motor8:2 near IR mirror 1 ry", - "type": 2, - "sensor": 1, + "id": "SARES23-LIC:MOT_18", + "pv_descr": "Module6:3 NIR Mirr1 Ry", + "sensor": 2, + "direction": 0, "speed": 250, "home_direction": "back", }, "nir_m1_rx": { - "id": "-USR:MOT_16", - "pv_descr": "Motor8:1 near IR mirror 1 rx", - "type": 2, - "sensor": 1, + "id": "SARES23-USR:MOT_10", + "pv_descr": "Module4:1 NIR Mirr1 Rx", + "sensor": 53, "speed": 250, + "direction": 1, "home_direction": "back", }, "nir_m2_ry": { - # "id": "-ESB9", - "id": "-USR:MOT_14", - "pv_descr": "Motor5:3 near IR mirror 2 ry", - "type": 2, - "sensor": 1, + "id": "SARES23-USR:MOT_1", + "pv_descr": "Module1:1 NIR Mirr2 Ry", + "sensor":2, "speed": 250, + "direction": 1, "home_direction": "back", }, "nir_m2_rx": { # "id": "-USR:MOT_4", - "id": "-USR:MOT_12", - "pv_descr": "Motor4:1 near IR mirror 2 rx", - "type": 1, - "sensor": 13, + "id": "SARES23-USR:MOT_7", + "pv_descr": "Module3:1 NIR Mirr2 rx", + "sensor": 53, + "direction": 0, "speed": 250, "home_direction": "back", }, - "crystal": { - "id": "-USR:MOT_2", - "pv_descr": "Motor3:2 crystal rotation", - "type": 2, - "sensor": 1, + "delay_400nm": { + # "id": "-USR:MOT_4", + "id": "SARES23-LIC:MOT_17", + "pv_descr": "Module6:2 400nm_stage", + "sensor": 2, + "direction": 0, "speed": 250, "home_direction": "back", }, - "wp": { - "id": "-USR:MOT_7", - "pv_descr": "Motor5:1 waveplate rotation", - "type": 2, - "sensor": 1, - "speed": 250, - "home_direction": "back", - "direction": 1, - }, + #"crystal": { + # "id": "-USR:MOT_2", + # "pv_descr": "Motor3:2 crystal rotation", + # "type": 2, + # "sensor": 1, + # "speed": 250, + # "home_direction": "back", + #}, + #"wp": { + # "id": "-USR:MOT_7", + # "pv_descr": "Motor5:1 waveplate rotation", + # "type": 2, + # "sensor": 1, + # "speed": 250, + # "home_direction": "back", + # "direction": 1, + #}, } - + self.motor_configuration_thorlabs = { + "polarizer": { + "pvname": "SLAAR21-LMOT-ELL1", + }, + "waveplate_ir": { + "pvname": "SLAAR21-LMOT-ELL2", + }, + "crystal": { + "pvname": "SLAAR21-LMOT-ELL3", + }, + "waveplate_thz": { + "pvname": "SLAAR21-LMOT-ELL4", + }, + } ### smaract motors ### for name, config in self.motor_configuration.items(): self._append( SmaractRecord, - pvname=Id + config["id"], + pvname=config["id"], name=name, is_setting=True, ) - - self.delay_thz = DelayTime(self.delaystage_thz, name="delay_thz") - + + ### thorlabs piezo motors ### + for name, config in self.motor_configuration_thorlabs.items(): + self._append( + ThorlabsPiezoRecord, + pvname=config["pvname"], + name=name, + is_setting=True, + ) + self._append( + MotorRecord, + pvname = "SLAAR21-LMOT-M522:MOTOR_1", + name="delaystage_thz", + is_setting=True, + is_display=False, + is_status=True, + ) + self._append( + DelayTime, + self.delaystage_thz, + name="delay_thz", + is_setting=False, + is_display=True, + is_status=True, + ) self.thz_polarization = AdjustableVirtual( - [self.crystal, self.wp], + [self.crystal, self.waveplate_ir], self.thz_pol_get, self.thz_pol_set, name="thz_polarization", @@ -432,25 +539,27 @@ class Organic_crystal_breadboard(Assembly): def thz_pol_get(self, val, val2): return 1.0 * val - def set_stage_config(self): - for name, config in self.motor_configuration.items(): + def set_stage_config(self, cfg=None): + if cfg is None: + cfg = self.motor_configuration + for name, config in cfg.items(): mot = self.__dict__[name] - mot.caqtdm_name(config["pv_descr"]) - mot.stage_type(config["type"]) - mot.sensor_type(config["sensor"]) - mot.speed(config["speed"]) - if "direction" in config.keys(): - mot.direction(config["direction"]) + mot.description(config["pv_descr"]) + #mot.stage_type(config["type"]) + mot.motor_parameters.sensor_type_num(config["sensor"]) + mot.direction(config["direction"]) + mot.motor_parameters.max_frequency(config["speed"]) sleep(0.5) - mot.calibrate_sensor(1) + mot.calibrate_sensor() - def home_smaract_stages(self, stages=None): - if stages == None: - stages = self.motor_configuration.keys() + def home_smaract_stages(self, motor_configuration=None): + if motor_configuration == None: + motor_configuration= self.motor_configuration + stages = motor_configuration.keys() print("#### Positions before homing ####") print(self.__repr__()) for name in stages: - config = self.motor_configuration[name] + config = motor_configuration[name] mot = self.__dict__[name] print( "#### Homing {} in {} direction ####".format( @@ -459,25 +568,27 @@ class Organic_crystal_breadboard(Assembly): ) sleep(1) if config["home_direction"] == "back": - mot.home_backward(1) - while mot.status_channel().value == 7: + mot.home_reverse(1) + sleep(.5) + while not mot.flags.motion_complete(): sleep(1) - if mot.is_homed() == 0: + if not mot.flags.is_homed(): print( "Homing failed, try homing {} in forward direction".format(name) ) mot.home_forward(1) elif config["home_direction"] == "forward": mot.home_forward(1) - while mot.status_channel().value == 7: + sleep(.5) + while not mot.flags.motion_complete(): sleep(1) - if mot.is_homed() == 0: + if not mot.flags.is_homed(): print( "Homing failed, try homing {} in backward direction".format( name ) ) - mot.home_backward(1) + mot.home_reverse(1) def get_adjustable_positions_str(self): ostr = "*****Organic Crystal Breadboard positions******\n" @@ -595,64 +706,85 @@ class LiNbO3_crystal_breadboard: return self.get_adjustable_positions_str() -class Electro_optic_sampling: +class Electro_optic_sampling(Assembly): def __init__( - self, name=None, Id=None, alias_namespace=None, pgroup=None, diode_channels=None + self, name=None, diode_channels=None ): - self.Id = Id + super().__init__(name=name) self.name = name self.alias = Alias(name) self.diode_channels = diode_channels self.basepath = f"/sf/bernina/data/p18915/res/scan_info/" self.motor_configuration = { "ry": { - "id": "-ESB16", - "pv_descr": "Motor8:1 EOS prism ry ", - "type": 2, - "sensor": 1, + "id": "SARES23-USR:MOT_3", + "pv_descr": "Module1:3 EOS Ry", + "sensor": 2, + "direction": 0, "speed": 250, "home_direction": "back", }, "rx": { - "id": "-ESB5", - "pv_descr": "Motor4:1 EOS prism rx ", - "type": 1, - "sensor": 0, + "id": "SARES23-USR:MOT_11", + "pv_descr": "Motor4:2 EOS Rx", + "sensor": 53, "speed": 250, + "direction": 0, "home_direction": "back", }, "x": { - "id": "-ESB4", - "pv_descr": "Motor4:2 EOS prism x ", - "type": 1, - "sensor": 0, + "id": "SARES23-USR:MOT_12", + "pv_descr": "Module4:3 EOS x", + "sensor":42, "speed": 250, + "direction": 0, "home_direction": "back", }, } + self._append( + MotorRecord, + "SLAAR21-LMOT-M521:MOTOR_1", + name="delaystage_pump", + is_setting=True, + ) + self._append( + DelayTime, + self.delaystage_pump, + name="delay_pump", + is_setting=True, + ) ### in vacuum smaract motors ### for name, config in self.motor_configuration.items(): - addSmarActRecordToSelf(self, Id=Id + config["id"], name=name) + self._append( + SmaractRecord, + pvname=config["id"], + name=name, + is_setting=True, + ) - def set_stage_config(self): - for name, config in self.motor_configuration.items(): - mot = self.__dict__[name]._device - mot.put("NAME", config["pv_descr"]) - mot.put("STAGE_TYPE", config["type"]) - mot.put("SET_SENSOR_TYPE", config["sensor"]) - mot.put("CL_MAX_FREQ", config["speed"]) + def set_stage_config(self, cfg=None): + if cfg is None: + cfg = self.motor_configuration + for name, config in cfg.items(): + mot = self.__dict__[name] + mot.description(config["pv_descr"]) + #mot.stage_type(config["type"]) + mot.motor_parameters.sensor_type_num(config["sensor"]) + mot.direction(config["direction"]) + mot.motor_parameters.max_frequency(config["speed"]) sleep(0.5) - mot.put("CALIBRATE.PROC", 1) + mot.calibrate_sensor() - def home_smaract_stages(self, stages=None): - if stages == None: - stages = self.motor_configuration.keys() + def home_smaract_stages(self, motor_configuration=None): + if motor_configuration == None: + motor_configuration= self.motor_configuration + stages = motor_configuration.keys() print("#### Positions before homing ####") print(self.__repr__()) for name in stages: - config = self.motor_configuration[name] - mot = self.__dict__[name]._device + config = motor_configuration[name] + mot = self.__dict__[name] print( "#### Homing {} in {} direction ####".format( name, config["home_direction"] @@ -660,25 +792,27 @@ class Electro_optic_sampling: ) sleep(1) if config["home_direction"] == "back": - mot.put("FRM_BACK.PROC", 1) - while mot.get("STATUS") == 7: + mot.home_reverse(1) + sleep(.5) + while not mot.flags.motion_complete(): sleep(1) - if mot.get("GET_HOMED") == 0: + if not mot.flags.is_homed(): print( "Homing failed, try homing {} in forward direction".format(name) ) - mot.put("FRM_FORW.PROC", 1) + mot.home_forward(1) elif config["home_direction"] == "forward": - mot.put("FRM_FORW.PROC", 1) - while mot.get("STATUS") == 7: + mot.home_forward(1) + sleep(.5) + while not mot.flags.motion_complete(): sleep(1) - if mot.get("GET_HOMED") == 0: + if not mot.flags.is_homed(): print( "Homing failed, try homing {} in backward direction".format( name ) ) - mot.put("FRM_BACK.PROC", 1) + mot.home_reverse(1) def fit_funvction(self, t, t0, w, tau): from scipy.special import erf diff --git a/eco/endstations/hexapod.py b/eco/endstations/hexapod.py index 3fe2234..275335e 100644 --- a/eco/endstations/hexapod.py +++ b/eco/endstations/hexapod.py @@ -8,7 +8,7 @@ from ..aliases import append_object_to_object, Alias from scipy.spatial.transform import Rotation import datetime from ..elements.assembly import Assembly - +from eco.devices_general.motors import AdjustablePiHex class Hexapod_PI: def __init__(self, Id): @@ -26,37 +26,6 @@ class Hexapod_PI: for i in "RST" ] -class AdjustablePiHex(AdjustablePv): - def __init__(self, pvname=None, pvreadbackname=None, accuracy=None, unit=None, name=None): - super().__init__(pvname, pvreadbackname=pvreadbackname, accuracy=accuracy, unit=unit, name=name) - self.limit_high = AdjustableFS(f'/sf/bernina/config/eco/reference_values/hex_pi_{name}_limit_high.json', default_value=0) - self.limit_low = AdjustableFS(f'/sf/bernina/config/eco/reference_values/hex_pi_{name}_limit_low.json', default_value=0) - - - def change(self, value): - if self.limit_low: - if value < self.limit_low(): - raise Exception( - f"Target value of {self.name} is smaller than limit value!" - ) - if self.limit_high: - if self.limit_high() < value: - raise Exception( - f"Target value of {self.name} is higher than limit value!" - ) - self._pv.put(value) - time.sleep(0.1) - while self.get_change_done() == 0: - time.sleep(0.1) - - def get_limits(self): - return (self.limit_low(), self.limit_high()) - - def set_limits(self, limit_low, limit_high): - self.limit_low(limit_low) - self.limit_high(limit_high) - - class HexapodPI(Assembly): def __init__(self, pvname, name=None, fina_angle_offset=None): super().__init__(name=name) diff --git a/eco/timing/timing_diag.py b/eco/timing/timing_diag.py index 239fdf2..f91de67 100644 --- a/eco/timing/timing_diag.py +++ b/eco/timing/timing_diag.py @@ -48,11 +48,17 @@ class TimetoolBerninaUSD(Assembly): self._append(DelayTime, self.delaystage_tt_usd, name="delay", is_setting=True) self.proc_client = PipelineClient() - self.proc_pipeline = processing_pipeline - self._append(Pipeline,self.proc_pipeline, name='pipeline_projection', is_setting=True) - self.proc_instance = processing_instance - self.proc_pipeline_edge = edge_finding_pipeline - self._append(Pipeline,self.proc_pipeline_edge, name='pipeline_edgefinding', is_setting=True) + try: + self.proc_pipeline = processing_pipeline + self._append(Pipeline,self.proc_pipeline, name='pipeline_projection', is_setting=True) + self.proc_instance = processing_instance + except Exception as e: + print(f"Timetool projection pipeline initialization failed with: \n{e}") + try: + self.proc_pipeline_edge = edge_finding_pipeline + self._append(Pipeline,self.proc_pipeline_edge, name='pipeline_edgefinding', is_setting=True) + except Exception as e: + print(f"Timetool edge finding pipeline initialization failed with: \n{e}") self.spectrometer_camera_channel = spectrometer_camera_channel self._append( Target_xyz, @@ -190,7 +196,10 @@ class TimetoolBerninaUSD(Assembly): is_display=True, ) if andor_spectrometer: - self._append(SpectrometerAndor,andor_spectrometer, name='spectrometer', is_setting=True, is_display='recursive') + try: + self._append(SpectrometerAndor,andor_spectrometer, name='spectrometer', is_setting=True, is_display='recursive') + except Exception as e: + print(f"Andor spectrometer initialization failed with: \n{e}") def get_online_data(self): diff --git a/eco/xdiagnostics/intensity_monitors.py b/eco/xdiagnostics/intensity_monitors.py index 6123a2c..0ebd840 100755 --- a/eco/xdiagnostics/intensity_monitors.py +++ b/eco/xdiagnostics/intensity_monitors.py @@ -1156,6 +1156,7 @@ class SolidTargetDetectorBerninaUSD(Assembly): name=None, # calc=None, # calc_calib={}, + pipeline_computation = None, ): super().__init__(name=name) @@ -1266,6 +1267,8 @@ class SolidTargetDetectorBerninaUSD(Assembly): # is_setting=True, # is_display=False, # ) + if pipeline_computation: + self._append(Pipeline, pipeline_computation,name='pipeline_comp', is_setting=True, is_display=False) def get_calibration_values(self, seconds=5): self.x_diodes.set_target_value(0).wait()