diff --git a/eco/bernina/config.py b/eco/bernina/config.py index 7ce7d87..2f801e5 100755 --- a/eco/bernina/config.py +++ b/eco/bernina/config.py @@ -818,6 +818,14 @@ components = [ "type": "eco.xdiagnostics.bsen_usd:Bsen", "kwargs": {}, }, + { + "args": [], + "name": "dsd", + "z_und": 146, + "desc": "downstream diagnostics", + "type": "eco.xdiagnostics.dsd:DownstreamDiagnostic", + "kwargs": {}, + }, { "args": [], "name": "att_usd", diff --git a/eco/devices_general/cameras_swissfel.py b/eco/devices_general/cameras_swissfel.py index c2cdd49..83c1ee8 100644 --- a/eco/devices_general/cameras_swissfel.py +++ b/eco/devices_general/cameras_swissfel.py @@ -1,25 +1,46 @@ -from cam_server import CamClient +from cam_server import CamClient,PipelineClient from ..aliases import Alias, append_object_to_object from .adjustable import PvRecord, PvEnum, AdjustableGetSet, AdjustableVirtual from ..elements import Assembly from .motors import MotorRecord +CAM_CLIENT = None +PIPELINE_CLIENT = None + +def get_camclient(): + global CAM_CLIENT + if not CAM_CLIENT: + CAM_CLIENT = CamClient() + return CAM_CLIENT + +def get_pipelineclient(): + global PIPELINE_CLIENT + if not PIPELINE_CLIENT: + PIPELINE_CLIENT = PipelineClient() + return PIPELINE_CLIENT class CamserverConfig(Assembly): - def __init__(self, camname, camserver_alias=None, name=None): + def __init__(self, cam_id, camserver_alias=None, name=None): super().__init__(name=name) - self.camname = camname + self.cam_id = cam_id self.camserver_alias = camserver_alias - self.cc = CamClient() + + @property + def cc(self): + return get_camclient() + + @property + def pc(self): + return get_pipelineclient() def get_current_value(self): - return self.cc.get_camera_config(self.camname) + return self.cc.get_camera_config(self.cam_id) def set_config_fields(self, fields): """fields is a dictionary containing the keys and values that should be updated, e.g. fields={'group': ['Laser', 'Bernina']}""" config = self.get_current_value() config.update(fields) - self.cc.set_camera_config(self.camname, config) + self.cc.set_camera_config(self.cam_id, config) ### convenience functions ### def set_alias(self, alias=None): @@ -29,7 +50,7 @@ class CamserverConfig(Assembly): self.set_config_fields({"alias": [alias]}) def stop(self): - self.cc.stop_instance(self.camname) + self.cc.stop_instance(self.cam_id) def set_cross(self, x, y, x_um_per_px=None, y_um_per_px=None): """set x and y position of the refetence marker on a camera px/um calibration is conserved if no new value is given""" diff --git a/eco/elements/assembly.py b/eco/elements/assembly.py index 39061c3..15ca7a4 100644 --- a/eco/elements/assembly.py +++ b/eco/elements/assembly.py @@ -51,7 +51,7 @@ class Assembly: settings[ts.alias.get_full_name(base=base)] = ts.get_current_value() for ts in self.status_indicators: if (not (ts is self)) and hasattr(ts, "get_status"): - tstat = ts.get_status() + tstat = ts.get_status(base=base) settings.update(tstat["settings"]) status_indicators.update(tstat["status_indicators"]) else: diff --git a/eco/elements/memory.py b/eco/elements/memory.py index 11be7ce..eb6177a 100644 --- a/eco/elements/memory.py +++ b/eco/elements/memory.py @@ -138,6 +138,8 @@ class Memory: continue table.append([n, tselstr, key, present_value, comp_indicator, recall_value]) + if len(table)==0: + return "No changes compared to memory!" return tabulate( table, headers=[ diff --git a/eco/xdiagnostics/dsd.py b/eco/xdiagnostics/dsd.py new file mode 100644 index 0000000..5041b61 --- /dev/null +++ b/eco/xdiagnostics/dsd.py @@ -0,0 +1,22 @@ +import sys + +sys.path.append("..") + +from ..devices_general.motors import MotorRecord +from ..devices_general.smaract import SmarActRecord +from ..devices_general.adjustable import PvRecord +from ..aliases import Alias, append_object_to_object +from ..elements.assembly import Assembly +from .profile_monitors import Pprm_dsd +from .intensity_monitors import SolidTargetDetectorPBPS_new_assembly + +class DownstreamDiagnostic(Assembly): + def __init__( + self, + name=None, + ): + super().__init__(name=name) + self._append(MotorRecord, "SARES20-DSD:MOTOR_DSDX", name="xbase", is_setting=True) + self._append(MotorRecord, "SARES20-DSD:MOTOR_DSDY", name="ybase", is_setting=True) + self._append(Pprm_dsd, "SARES20-DSDPPRM", "SARES20-DSDPPRM", name="prof_dsd", is_setting=True, view_toplevel_only = False) + self._append(SolidTargetDetectorPBPS_new_assembly, pvname= "SARES20-DSDPBPS", name="mon_dsd", is_setting = True, view_toplevel_only=False) diff --git a/eco/xdiagnostics/intensity_monitors.py b/eco/xdiagnostics/intensity_monitors.py index 58df276..24887cb 100755 --- a/eco/xdiagnostics/intensity_monitors.py +++ b/eco/xdiagnostics/intensity_monitors.py @@ -314,3 +314,170 @@ class SolidTargetDetectorPBPS: print("No diodes configured, can not change any gain!") # SAROP21-CVME-PBPS:Lnk10Ch15-WD-gain +from ..elements.assembly import Assembly +class SolidTargetDetectorPBPS_new_assembly(Assembly): + def __init__( + self, + pvname, + VME_crate=None, + link=None, + channels={}, + ch_up=12, + ch_down=13, + ch_left=15, + ch_right=14, + elog=None, + name=None, + calc=None, + calc_calib={}, + ): + super().__init__(name=name) + self.pvname = pvname + self._append(MotorRecord, "SARES20-DSD:MOTOR_DSDX", name="xbase", is_setting=True) + self._append(MotorRecord, pvname + ":MOTOR_X1", name="x_diodes", is_setting=True) + self._append(MotorRecord, pvname + ":MOTOR_Y1", name="y_diodes", is_setting=True) + self._append(MotorRecord, pvname + ":MOTOR_PROBE", name="target_y", is_setting=True) + self._append(PvEnum, pvname + ":PROBE_SP", name="target", is_setting=True) + if VME_crate: + self.diode_up = FeDigitizer("%s:Lnk%dCh%d" % (VME_crate, link, ch_up)) + self.diode_down = FeDigitizer("%s:Lnk%dCh%d" % (VME_crate, link, ch_down)) + self.diode_left = FeDigitizer("%s:Lnk%dCh%d" % (VME_crate, link, ch_left)) + self.diode_right = FeDigitizer("%s:Lnk%dCh%d" % (VME_crate, link, ch_right)) + + if channels: + self._append(PvDataStream, channels["up"], name="signal_up", is_setting=False) + self._append(PvDataStream, channels["down"], name="signal_down", is_setting=False) + self._append(PvDataStream, channels["left"], name="signal_left", is_setting=False) + self._append(PvDataStream, channels["right"], name="signal_right", is_setting=False) + + if calc: + self._append(PvDataStream, calc["itot"], name="intensity", is_setting=False) + self._append(PvDataStream, calc["xpos"], name="xpos", is_setting=False) + self._append(PvDataStream, calc["ypos"], name="ypos", is_setting=False) + + def get_calibration_values(self, seconds=5): + self.x_diodes.set_target_value(0).wait() + self.y_diodes.set_target_value(0).wait() + ds = [self.signal_up, self.signal_down, self.signal_left, self.signal_right] + aqs = [d.acquire(seconds=seconds) for d in ds] + data = [aq.wait() for aq in aqs] + mean = [np.mean(td) for td in data] + std = [np.std(td) for td in data] + norm_diodes = [1 / tm / 4 for tm in mean] + return norm_diodes + + def set_calibration_values(self, norm_diodes): + # this is now only for bernina when using the ioxos from sla + channels = [ + "SLAAR21-LTIM01-EVR0:CALCI.INPG", + "SLAAR21-LTIM01-EVR0:CALCI.INPH", + "SLAAR21-LTIM01-EVR0:CALCI.INPF", + "SLAAR21-LTIM01-EVR0:CALCI.INPE", + ] + for tc, tv in zip(channels, norm_diodes): + PV(tc).put(bytes(str(tv), "utf8")) + channels = ["SLAAR21-LTIM01-EVR0:CALCX.INPE", "SLAAR21-LTIM01-EVR0:CALCX.INPF"] + for tc, tv in zip(channels, norm_diodes[2:4]): + PV(tc).put(bytes(str(tv), "utf8")) + channels = ["SLAAR21-LTIM01-EVR0:CALCY.INPE", "SLAAR21-LTIM01-EVR0:CALCY.INPF"] + for tc, tv in zip(channels, norm_diodes[0:2]): + PV(tc).put(bytes(str(tv), "utf8")) + + def get_calibration_values_position( + self, calib_intensities, seconds=5, motion_range=0.2 + ): + self.x_diodes.set_limits(-motion_range / 2 - 0.1, +motion_range / 2 + 0.1) + self.y_diodes.set_limits(-motion_range / 2 - 0.1, +motion_range / 2 + 0.1) + self.x_diodes.set_target_value(0).wait() + self.y_diodes.set_target_value(0).wait() + raw = [] + for pos in [motion_range / 2, -motion_range / 2]: + print(pos) + self.x_diodes.set_target_value(pos).wait() + aqs = [ + ts.acquire(seconds=seconds) + for ts in [self.signal_left, self.signal_right] + ] + vals = [ + np.mean(aq.wait()) * calib + for aq, calib in zip(aqs, calib_intensities[0:2]) + ] + raw.append((vals[0] - vals[1]) / (vals[0] + vals[1])) + grad = motion_range / np.diff(raw)[0] + # xcalib = [np.diff(calib_intensities[0:2])[0]/np.sum(calib_intensities[0:2]), grad] + xcalib = [0, grad] + self.x_diodes.set_target_value(0).wait() + raw = [] + for pos in [motion_range / 2, -motion_range / 2]: + self.y_diodes.set_target_value(pos).wait() + aqs = [ + ts.acquire(seconds=seconds) for ts in [self.signal_up, self.signal_down] + ] + vals = [ + np.mean(aq.wait()) * calib + for aq, calib in zip(aqs, calib_intensities[2:4]) + ] + raw.append((vals[0] - vals[1]) / (vals[0] + vals[1])) + grad = motion_range / np.diff(raw)[0] + # ycalib = [np.diff(calib_intensities[2:4])[0]/np.sum(calib_intensities[2:4]), grad] + ycalib = [0, grad] + self.y_diodes.set_target_value(0).wait() + return xcalib, ycalib + + def set_calibration_values_position(self, xcalib, ycalib): + channels = ["SLAAR21-LTIM01-EVR0:CALCX.INPJ", "SLAAR21-LTIM01-EVR0:CALCX.INPI"] + # txcalib = [-1*xcalib[0],-1*xcalib[1]] + for tc, tv in zip(channels, xcalib): + PV(tc).put(bytes(str(tv), "utf8")) + channels = ["SLAAR21-LTIM01-EVR0:CALCY.INPJ", "SLAAR21-LTIM01-EVR0:CALCY.INPI"] + for tc, tv in zip(channels, ycalib): + PV(tc).put(bytes(str(tv), "utf8")) + + def calibrate(self, seconds=5): + c = self.get_calibration_values(seconds=seconds) + self.set_calibration_values(c) + xc, yc = self.get_calibration_values_position(c, seconds=seconds) + self.set_calibration_values_position(xc, yc) + + + def set_gains(self, value): + try: + self.diode_up.gain.set(value) + self.diode_down.gain.set(value) + self.diode_left.gain.set(value) + self.diode_right.gain.set(value) + except: + print("No diodes configured, can not change any gain!") + + def get_available_gains(self): + try: + nu = self.diode_up.gain.names + nd = self.diode_down.gain.names + nl = self.diode_left.gain.names + nr = self.diode_right.gain.names + assert ( + nu == nd == nl == nr + ), "NB: the gain options of the four diodes are not equal!!!" + return nu + except: + print("No diodes configured, can not change any gain!") + + def get_gains(self): + try: + gains = dict() + gains["up"] = (self.diode_up.gain.get_name(), self.diode_up.gain.get()) + gains["down"] = ( + self.diode_down.gain.get_name(), + self.diode_down.gain.get(), + ) + gains["left"] = ( + self.diode_left.gain.get_name(), + self.diode_left.gain.get(), + ) + gains["right"] = ( + self.diode_right.gain.get_name(), + self.diode_right.gain.get(), + ) + return gains + except: + print("No diodes configured, can not change any gain!") diff --git a/eco/xdiagnostics/profile_monitors.py b/eco/xdiagnostics/profile_monitors.py index 107d6e7..b74b0fa 100755 --- a/eco/xdiagnostics/profile_monitors.py +++ b/eco/xdiagnostics/profile_monitors.py @@ -26,7 +26,7 @@ class Pprm(Assembly): is_setting=True, ) self.camCA = CameraCA(pvname_camera) - self._append(CameraBasler, pvname_camera, name="camera") + self._append(CameraBasler, pvname_camera, name="camera", is_setting=True) self._append(PvEnum, self.pvname + ":LED", name="led", is_setting=True) self._append(PvEnum, self.pvname + ":PROBE_SP", name="target", is_setting=True) @@ -42,6 +42,32 @@ class Pprm(Assembly): return s + +class Pprm_dsd(Assembly): + def __init__(self, pvname, pvname_camera, name=None): + super().__init__(name=name) + self.pvname = pvname + self._append( + MotorRecord, + pvname_camera + ":MOTOR_PROBE", + name="target_pos", + is_setting=True, + ) + self.camCA = CameraCA(pvname_camera) + self._append(CameraBasler, pvname_camera, name="camera", is_setting=False) + self._append(MotorRecord, self.pvname + ":MOTOR_ZOOM", name="zoom", is_setting=True) + self._append(PvEnum, self.pvname + ":PROBE_SP", name="target", is_setting=True) + + def movein(self, target=1): + self.target.set_target_value(target) + + def moveout(self, target=0): + self.target.set_target_value(target) + + + + + class Pprmold: def __init__(self, Id, name=None): self.Id = Id