From a8eae094eb60d1951e26cf58715832c3b42e5eb8 Mon Sep 17 00:00:00 2001 From: Klaus Wakonig Date: Fri, 9 Dec 2022 11:36:05 +0100 Subject: [PATCH] refactor: export from bec repo --- .gitignore | 8 + bec_client/.DS_Store | Bin 0 -> 6148 bytes bec_client/hli/spec_hli.py | 245 ++ bec_client/plugins/.DS_Store | Bin 0 -> 8196 bytes bec_client/plugins/LamNI/LamNI_logo.png | Bin 0 -> 49460 bytes bec_client/plugins/LamNI/__init__.py | 2 + bec_client/plugins/LamNI/bl_show_all.mac | 269 ++ .../plugins/LamNI/lamni_optics_mixin.py | 154 ++ .../LamNI/load_additional_correction.py | 23 + bec_client/plugins/LamNI/x_ray_eye_align.py | 888 +++++++ bec_client/plugins/cSAXS/__init__.py | 1 + bec_client/plugins/cSAXS/cSAXS_beamline.py | 28 + scan_server/.DS_Store | Bin 0 -> 6148 bytes scan_server/plugins/LamNIFermatScan.py | 451 ++++ scibec/lamni_config.py | 150 ++ scibec/test_config_cSAXS.yaml | 2326 +++++++++++++++++ 16 files changed, 4545 insertions(+) create mode 100644 .gitignore create mode 100644 bec_client/.DS_Store create mode 100644 bec_client/hli/spec_hli.py create mode 100644 bec_client/plugins/.DS_Store create mode 100644 bec_client/plugins/LamNI/LamNI_logo.png create mode 100644 bec_client/plugins/LamNI/__init__.py create mode 100644 bec_client/plugins/LamNI/bl_show_all.mac create mode 100644 bec_client/plugins/LamNI/lamni_optics_mixin.py create mode 100755 bec_client/plugins/LamNI/load_additional_correction.py create mode 100644 bec_client/plugins/LamNI/x_ray_eye_align.py create mode 100644 bec_client/plugins/cSAXS/__init__.py create mode 100644 bec_client/plugins/cSAXS/cSAXS_beamline.py create mode 100644 scan_server/.DS_Store create mode 100644 scan_server/plugins/LamNIFermatScan.py create mode 100644 scibec/lamni_config.py create mode 100644 scibec/test_config_cSAXS.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0814ce9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +**/venv +**/.idea +*.log +**/__pycache__ +.DS_Store +**/out +**/.vscode +**/.pytest_cache diff --git a/bec_client/.DS_Store b/bec_client/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..fae008cfdae4b6118ad2f0cec2de2a2666ad4354 GIT binary patch literal 6148 zcmeHK!EO^V5FIyxq^%G+6ofdf)N3WO3J9TIwrThQ7J(40(rh+LRygZbcGDc9Nc#cw z!XMDj=((q=XMThqV8))3q}lXV1jr+epY55k$4^>&2LMEK9J&BC0H~r8mbb7e5VDi5 z$Og|mMijJ0ngmh>IJyXu6>mFX8L$leZ4A)2TY>>dh=R%D_ba0`tJi;sjZ*o+!wSlF z<=7vniJx_{aoX&RKGW#EQZl&dcY+U5Z_=*q9I80$L~(DV1ER2pE=M1uI8>9S8pmO( zV|`=TL}}NaPp7XNZ}uFw;m-D)sr#z_5(o80V>a8YKG}V-|E~Q-4rBGz5Ge4z(ejA# z2F|d<=aGHa@2cVAO~jU=YPI;Ls(!jU$j-lC9?av0D!jv*4A!lB>W^Zn;y$iK*;ODx zR>#nX2x3UXv?~K-tH4x{=&j(zgrCamfD>NvnkT;AtKa|?NTCZOOf-QE(!zX}JQnl$ z`R#o0JD*!#^L*BO7372LS-F1(_#3Kj-~U#22g`tE;2&dv?gs~z&|}OE>Z=1AT>>DM zaas%Zsg|G^htXrq4Wb1_m{3F$D!9ZDCLHG-_VXBXgC-mV7axLm7F?kSy*sY&Xm=2v zLEBmeECb67l+3V3@BiEN_5X5^9a#n}1OF8RqTKRZO*|66tt*G4x7I;DK_wx-+@KD@ khM!~Ip{Mv6sur|4)IszZbAvd8Vt)iQ4Ysii{80ve0VuVsVE_OC literal 0 HcmV?d00001 diff --git a/bec_client/hli/spec_hli.py b/bec_client/hli/spec_hli.py new file mode 100644 index 0000000..5d699c2 --- /dev/null +++ b/bec_client/hli/spec_hli.py @@ -0,0 +1,245 @@ +from bec_client.scan_manager import ScanReport +from bec_utils.devicemanager import Device + +# pylint:disable=undefined-variable +# pylint: disable=too-many-arguments + + +def dscan( + motor1: Device, m1_from: float, m1_to: float, steps: int, exp_time: float, **kwargs +) -> ScanReport: + """Relative line scan with one device. + + Args: + motor1 (Device): Device that should be scanned. + m1_from (float): Start position relative to the current position. + m1_to (float): End position relative to the current position. + steps (int): Number of steps. + exp_time (float): Exposure time. + + Returns: + ScanReport: Status object. + + Examples: + >>> dscan(dev.motor1, -5, 5, 10, 0.1) + """ + return scans.line_scan( + motor1, m1_from, m1_to, steps=steps, exp_time=exp_time, relative=True, **kwargs + ) + + +def d2scan( + motor1: Device, + m1_from: float, + m1_to: float, + motor2: Device, + m2_from: float, + m2_to: float, + steps: int, + exp_time: float, + **kwargs +) -> ScanReport: + """Relative line scan with two devices. + + Args: + motor1 (Device): First device that should be scanned. + m1_from (float): Start position of the first device relative to its current position. + m1_to (float): End position of the first device relative to its current position. + motor2 (Device): Second device that should be scanned. + m2_from (float): Start position of the second device relative to its current position. + m2_to (float): End position of the second device relative to its current position. + steps (int): Number of steps. + exp_time (float): Exposure time + + Returns: + ScanReport: Status object. + + Examples: + >>> d2scan(dev.motor1, -5, 5, dev.motor2, -8, 8, 10, 0.1) + """ + return scans.line_scan( + motor1, + m1_from, + m1_to, + motor2, + m2_from, + m2_to, + steps=steps, + exp_time=exp_time, + relative=True, + **kwargs + ) + + +def ascan(motor1, m1_from, m1_to, steps, exp_time, **kwargs): + """Absolute line scan with one device. + + Args: + motor1 (Device): Device that should be scanned. + m1_from (float): Start position. + m1_to (float): End position. + steps (int): Number of steps. + exp_time (float): Exposure time. + + Returns: + ScanReport: Status object. + + Examples: + >>> ascan(dev.motor1, -5, 5, 10, 0.1) + """ + return scans.line_scan( + motor1, m1_from, m1_to, steps=steps, exp_time=exp_time, relative=False, **kwargs + ) + + +def a2scan(motor1, m1_from, m1_to, motor2, m2_from, m2_to, steps, exp_time, **kwargs): + """Absolute line scan with two devices. + + Args: + motor1 (Device): First device that should be scanned. + m1_from (float): Start position of the first device. + m1_to (float): End position of the first device. + motor2 (Device): Second device that should be scanned. + m2_from (float): Start position of the second device. + m2_to (float): End position of the second device. + steps (int): Number of steps. + exp_time (float): Exposure time + + Returns: + ScanReport: Status object. + + Examples: + >>> a2scan(dev.motor1, -5, 5, dev.motor2, -8, 8, 10, 0.1) + """ + return scans.line_scan( + motor1, + m1_from, + m1_to, + motor2, + m2_from, + m2_to, + steps=steps, + exp_time=exp_time, + relative=False, + **kwargs + ) + + +def dmesh(motor1, m1_from, m1_to, m1_steps, motor2, m2_from, m2_to, m2_steps, exp_time, **kwargs): + """Relative mesh scan (grid scan) with two devices. + + Args: + motor1 (Device): First device that should be scanned. + m1_from (float): Start position of the first device relative to its current position. + m1_to (float): End position of the first device relative to its current position. + m1_steps (int): Number of steps for motor1. + motor2 (Device): Second device that should be scanned. + m2_from (float): Start position of the second device relative to its current position. + m2_to (float): End position of the second device relative to its current position. + m2_steps (int): Number of steps for motor2. + exp_time (float): Exposure time + + Returns: + ScanReport: Status object. + + Examples: + >>> dmesh(dev.motor1, -5, 5, 10, dev.motor2, -8, 8, 10, 0.1) + """ + return scans.grid_scan( + motor1, + m1_from, + m1_to, + m1_steps, + motor2, + m2_from, + m2_to, + m2_steps, + exp_time=exp_time, + relative=True, + ) + + +def amesh(motor1, m1_from, m1_to, m1_steps, motor2, m2_from, m2_to, m2_steps, exp_time, **kwargs): + """Absolute mesh scan (grid scan) with two devices. + + Args: + motor1 (Device): First device that should be scanned. + m1_from (float): Start position of the first device. + m1_to (float): End position of the first device. + m1_steps (int): Number of steps for motor1. + motor2 (Device): Second device that should be scanned. + m2_from (float): Start position of the second device. + m2_to (float): End position of the second device. + m2_steps (int): Number of steps for motor2. + exp_time (float): Exposure time + + Returns: + ScanReport: Status object. + + Examples: + >>> amesh(dev.motor1, -5, 5, 10, dev.motor2, -8, 8, 10, 0.1) + """ + return scans.grid_scan( + motor1, + m1_from, + m1_to, + m1_steps, + motor2, + m2_from, + m2_to, + m2_steps, + exp_time=exp_time, + relative=False, + ) + + +def umv(*args) -> ScanReport: + """Updated absolute move (i.e. blocking) for one or more devices. + + Returns: + ScanReport: Status object. + + Examples: + >>> umv(dev.samx, 1) + >>> umv(dev.samx, 1, dev.samy, 2) + """ + return scans.umv(*args, relative=False) + + +def umvr(*args) -> ScanReport: + """Updated relative move (i.e. blocking) for one or more devices. + + Returns: + ScanReport: Status object. + + Examples: + >>> umvr(dev.samx, 1) + >>> umvr(dev.samx, 1, dev.samy, 2) + """ + return scans.umv(*args, relative=True) + + +def mv(*args) -> ScanReport: + """Absolute move for one or more devices. + + Returns: + ScanReport: Status object. + + Examples: + >>> mv(dev.samx, 1) + >>> mv(dev.samx, 1, dev.samy, 2) + """ + return scans.mv(*args, relative=False) + + +def mvr(*args) -> ScanReport: + """Relative move for one or more devices. + + Returns: + ScanReport: Status object. + + Examples: + >>> mvr(dev.samx, 1) + >>> mvr(dev.samx, 1, dev.samy, 2) + """ + return scans.mv(*args, relative=True) diff --git a/bec_client/plugins/.DS_Store b/bec_client/plugins/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..33b8cab6977acbc02b75cb53485ebe03d309fdf6 GIT binary patch literal 8196 zcmeHLO^nr46h5bKhG{z>cletr;bCFqe}D%A1Vr#LKQhSAAaDLbAAPSCDxG$C?RyU; z6DPq15EXUd#>Bski6;Imj6oN!*r?IO5S0iE7rJrdMgwm2+w)|+Z`2RY zNY^ml9$>~lTs4lX}>1Qfc^Ye$;PgTh9$eG@;$PN5( zz$t|!^K52FPVXuP{{E7~=Ld^{S044lJZmaiUa{<1k+Z||a_%!uI21W2B7Qa!2BoM| z80Hx(ht2_q?A(geMb_bvOPAwj47K;C&rL60x+=5r!G}9{?%98F#(guHuFsw`SaCzQ z;5oxqSaf{b^^f#dT{}8DLLu}c_es|&XjYOUE!%rzAZSVQlt9ykOJjKL{*9$tiV z@H)H$AHaF|9KL`r;VbwCF2WV~8GeP|;Sab9*HFPmoQ^YaCN97wxD=ORJ3fTla64wP z8~0%^?#BZ-h=;g5J3 zui!6u6|do+N=lihG%GENp)6LGDX|+$EoX+O=8QuB2WfAZ%o$3KcoR@YZj~JAu5Nwj z|C1b-rt@@Ynl*dQ+=Z>nSFfienPiOQSdCUU(p4~Y3a#+RL)g$CEpADBc!ddbSfYj-FXC3657J+|L)uW z|G)DE5@ZsABm#GR1hB3v*VRF{dAlu^*V-l>hw0#r+f7I*Ak=Xat{3Gv$+#RBIy0Wj h#&SYR0VRxpl9ZMFfBqxj`sZr${!iZjoQn5k`x{XeA=dx^ literal 0 HcmV?d00001 diff --git a/bec_client/plugins/LamNI/LamNI_logo.png b/bec_client/plugins/LamNI/LamNI_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..965195cffb7ce55bced133089bc0f9ebaf0906ab GIT binary patch literal 49460 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≶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 8) { + text_bf + } + printf("%.3f mm\n",gap) + text_non_bf + + if (!_id_loss_rate_ok()) { + id_loss_rate + } + + bl_shutter_status + + if (_bl_cvd_filter_open()) { + text_bf + printf("CVD diamond filter : open / out\n") + text_non_bf + } + + if (!_bl_xbox_valve_es1_open()) { + bl_xbox_valve_es1 _show + } + + if (_bl_ln2_non_standard()) { + text_bf + printf("\nNon standard liquid nitrogen cooling-warning parameters occur. Please report this to your local contact.\n") + text_non_bf + printf("The macro bl_ln2_warn can be used to control this e-mail warning feature.\n") + bl_ln2_warn "show" + printf("\n") + } + + printf("\n") + bl_flight_tube_pressure + printf("\n") + + bl_attended _show + + _bl_check_alarm_records(1,1) + + printf("\n") + bl_op_msg +}' + + +def _bl_hall_temperature_ok() '{ + local temp_ok + local stat + + temp_ok = 1 + + # EH T02 average temperature + stat = epics_get("ILUUL-02AV:TEMP") + if ((stat < 23.0) || (stat > 26.0)) { + temp_ok = 0 + } + + # EH T02 temperature at T0204 axis 16 + stat = epics_get("ILUUL-0200-EB104:TEMP") + if ((stat < 23.0) || (stat > 26.0)) { + temp_ok = 0 + } + + # EH T02 temperature at T0205 axis 18 + stat = epics_get("ILUUL-0200-EB105:TEMP") + if ((stat < 23.0) || (stat > 26.0)) { + temp_ok = 0 + } + + return (temp_ok) +}' + + +# ---------------------------------------------------------------------- +def bl_hall_temperature '{ + local stat + + stat = epics_get("ILUUL-02AV:TEMP") + printf("hall T02 average temperature : ") + if ((stat < 23.0) || (stat > 25.0)) { + text_bf + } + printf("%.2f deg.C\n",stat) + text_non_bf + + stat = epics_get("ILUUL-0200-EB104:TEMP") + printf("hall temperature at T0204 axis 16 : ") + if ((stat < 23) || (stat > 25)) { + text_bf + } + printf("%.2f deg.C\n",stat) + text_non_bf + + stat = epics_get("ILUUL-0200-EB105:TEMP") + printf("hall temperature at T0205 axis 18 : ") + if ((stat < 23) || (stat > 25)) { + text_bf + } + printf("%.2f deg.C\n",stat) + text_non_bf + +# stat = epics_get("ILUUL-0300-EB102:TEMP") +# printf("EH T03 temperature at T0302 axis 21: ") +# if ((stat < 23) || (stat > 25)) { +# text_bf +# } +# printf("%.2f deg.C\n",stat) +# text_non_bf + +}' + +def _bl_sls_status_unusual() '{ + local unusual + local stat + + unusual = 0 + + stat = epics_get("X12SA-SR-VAC:SETPOINT") + if (stat != "OK") { + unusual = 1 + } + + stat = epics_get("ACOAU-ACCU:OP-MODE.VAL") + if ((stat != "Light Available") && (stat != "Light-Available")) { + unusual = 1 + } + + stat = epics_get("ALIRF-GUN:INJ-MODE") + if (stat != "TOP-UP") { + unusual = 1 + } + + # current threshold + stat = epics_get("ALIRF-GUN:CUR-LOWLIM") + if (stat < 350) { + unusual = 1 + } + + # current deadband + stat = epics_get("ALIRF-GUN:CUR-DBAND") + if (stat > 2) { + unusual = 1 + } + + # orbit feedback mode + stat = epics_get("ARIDI-BPM:OFB-MODE") + if (stat != "fast") { + unusual = 1 + } + + # fast orbit feedback + stat = epics_get("ARIDI-BPM:FOFBSTATUS-G") + if (stat != "running") { + unusual = 1 + } + + return(unusual) +}' + +def bl_sls_status '{ + local stat + + stat = epics_get("ACOAU-ACCU:OP-MODE.VAL") + printf("SLS status : ") + if ((stat != "Light Available") && (stat != "Light-Available")) { + text_bf + } + printf("%s\n",stat) + text_non_bf + + stat = epics_get("ALIRF-GUN:INJ-MODE") + printf("SLS injection mode : ") + if (stat != "TOP-UP") { + text_bf + } + printf("%s\n",stat) + text_non_bf + + stat = epics_get("ALIRF-GUN:CUR-LOWLIM") + printf("SLS current threshold : ") + if (stat < 350) { + text_bf + } + printf("%7.3f\n",stat) + text_non_bf + + stat = epics_get("ALIRF-GUN:CUR-DBAND") + printf("SLS current deadband : ") + if (stat > 2) { + text_bf + } + printf("%7.3f\n",stat) + text_non_bf + + stat = epics_get("ACORF-FILL:PAT-SELECT") + printf("SLS filling pattern : ") + printf("%s\n",stat) + + bl_ring_current + + stat = epics_get("ARIDI-PCT:TAU-HOUR") + printf("SLS filling life time : ") + printf("%.2f h\n",stat) + + stat = epics_get("ARIDI-BPM:OFB-MODE") + printf("orbit feedback mode : ") + if (stat != "fast") { + text_bf + } + printf("%s\n",stat) + text_non_bf + + stat = epics_get("ARIDI-BPM:FOFBSTATUS-G") + printf("fast orbit feedback : ") + if (stat != "running") { + text_bf + } + printf("%s\n",stat) + text_non_bf + +}' + +def _bl_get_ring_current() '{ + return epics_get("ARIDI-PCT:CURRENT") +}' + + +# ---------------------------------------------------------------------- +def _bl_no_ring_current() '{ + # set an arbitrary current limit of 100mA as no-beam limit + if (_bl_get_ring_current() < 100) { + return 1 + } else { + return 0 + } +}' + + +# ---------------------------------------------------------------------- +def bl_ring_current '{ + local curr + + curr = _bl_get_ring_current() + + if (curr < 300) { + text_bf + } + printf("SLS ring current : %.3f mA\n",curr) + text_non_bf +}' \ No newline at end of file diff --git a/bec_client/plugins/LamNI/lamni_optics_mixin.py b/bec_client/plugins/LamNI/lamni_optics_mixin.py new file mode 100644 index 0000000..19a9059 --- /dev/null +++ b/bec_client/plugins/LamNI/lamni_optics_mixin.py @@ -0,0 +1,154 @@ +import time +from rich.console import Console +from rich.table import Table +from rich import box + +from bec_client.plugins.cSAXS import fshclose + + +class LamNIOpticsMixin: + @staticmethod + def _get_user_param_safe(device, var): + param = dev[device].user_parameter + if not param or param.get(var) is None: + raise ValueError(f"Device {device} has no user parameter definition for {var}.") + return param.get(var) + + def leyey_out(self): + self.loptics_in() + fshclose() + leyey_out = self._get_user_param_safe("leyey", "out") + umv(dev.leyey, leyey_out) + + epics_put("XOMNYI-XEYE-ACQ:0", 2) + umv(dev.dttrz, 5830, dev.fttrz, 3338) + + def leye_in(self): + bec.queue.next_dataset_number += 1 + umv(dev.dttrz, 5830 + 600, dev.fttrz, 3338 + 600) + while True: + moved_out = (input("Did the flight tube move out? (Y/n)") or "y").lower() + if moved_out == "y": + break + if moved_out == "n": + return + leyex_in = self._get_user_param_safe("leyex", "in") + leyey_in = self._get_user_param_safe("leyey", "in") + umv(dev.leyex, leyex_in, dev.leyey, leyey_in) + self.align.update_frame() + + def _lfzp_in(self): + loptx_in = self._get_user_param_safe("loptx", "in") + lopty_in = self._get_user_param_safe("lopty", "in") + umv( + dev.loptx, loptx_in, dev.lopty, lopty_in + ) # for 7.2567 keV and 150 mu, 60 nm fzp, loptz 83.6000 for propagation 1.4 mm + + def lfzp_in(self): + """ + move in the lamni zone plate. + This will disable rt feedback, move the FZP and re-enabled the feedback. + """ + if "rtx" in dev and dev.rtx.enabled: + dev.rtx.feedback_disable() + + self._lfzp_in() + + if "rtx" in dev and dev.rtx.enabled: + dev.rtx.feedback_enable_with_reset() + + def loptics_in(self): + """ + Move in the lamni optics, including the FZP and the OSA. + """ + self.lfzp_in() + self.losa_in() + + def loptics_out(self): + """Move out the lamni optics""" + if "rtx" in dev and dev.rtx.enabled: + dev.rtx.feedback_disable() + + # self.lcs_out() + self.losa_out() + loptx_out = self._get_user_param_safe("loptx", "out") + lopty_out = self._get_user_param_safe("lopty", "out") + umv(dev.loptx, loptx_out, dev.lopty, lopty_out) + + if "rtx" in dev and dev.rtx.enabled: + time.sleep(1) + dev.rtx.feedback_enable_with_reset() + + def lcs_in(self): + # umv lcsx -1.852 lcsy -0.095 + pass + + def lcs_out(self): + umv(dev.lcsy, 3) + + def losa_in(self): + # 6.2 keV, 170 um FZP + # umv(dev.losax, -1.4450000, dev.losay, -0.1800) + # umv(dev.losaz, -1) + # 6.7, 170 + # umv(dev.losax, -1.4850, dev.losay, -0.1930) + # umv(dev.losaz, 1.0000) + # 7.2, 150 + losax_in = self._get_user_param_safe("losax", "in") + losay_in = self._get_user_param_safe("losay", "in") + losaz_in = self._get_user_param_safe("losaz", "in") + umv(dev.losax, losax_in, dev.losay, losay_in) + umv(dev.losaz, losaz_in) + # 11 kev + # umv(dev.losax, -1.161000, dev.losay, -0.196) + # umv(dev.losaz, 1.0000) + + def losa_out(self): + losay_out = self._get_user_param_safe("losay", "out") + losaz_out = self._get_user_param_safe("losaz", "out") + umv(dev.losaz, losaz_out) + umv(dev.losay, losay_out) + + def lfzp_info(self): + loptz_val = dev.loptz.read()["loptz"]["value"] + distance = -loptz_val + 85.6 + 52 + print(f"The sample is in a distance of {distance:.1f} mm from the FZP.") + + diameters = [] + diameters[0] = 80e-6 + diameters[1] = 100e-6 + diameters[2] = 120e-6 + diameters[3] = 150e-6 + diameters[4] = 170e-6 + diameters[5] = 200e-6 + diameters[6] = 220e-6 + diameters[7] = 250e-6 + + mokev_val = dev.mokev.read()["mokev"]["value"] + console = Console() + table = Table( + title=f"At the current energy of {mokev_val:.4f} keV we have following options:", + box=box.SQUARE, + ) + table.add_column("Diameter", justify="center") + table.add_column("Focal distance", justify="center") + table.add_column("Current beam size", justify="center") + + wavelength = 1.2398e-9 / mokev_val + + for diameter in diameters: + outermost_zonewidth = 60e-9 + focal_distance = diameter * outermost_zonewidth / wavelength + beam_size = ( + -diameter / (focal_distance * 1000) * (focal_distance * 1000 - distance) * 1e6 + ) + table.add_row(f"{diameter}", f"{focal_distance:.2f} mm", f"{beam_size:.2f} microns") + + console.print(table) + + print("OSA Information:") + # print(f"Current losaz %.1f\n", A[losaz]) + # print("The OSA will collide with the sample plane at %.1f\n\n", 89.3-A[loptz]) + print( + "The numbers presented here are for a sample in the plane of the lamni sample holder.\n" + ) diff --git a/bec_client/plugins/LamNI/load_additional_correction.py b/bec_client/plugins/LamNI/load_additional_correction.py new file mode 100755 index 0000000..3fd1281 --- /dev/null +++ b/bec_client/plugins/LamNI/load_additional_correction.py @@ -0,0 +1,23 @@ +def lamni_read_additional_correction(): + # "additional_correction_shift" + # [0][] x , [1][] y, [2][] angle, [3][0] number of elements + import numpy as np + + with open("correction_lamni_um_S01405_.txt", "r") as f: + num_elements = f.readline() + int_num_elements = int(num_elements.split(" ")[2]) + print(int_num_elements) + corr_pos_x = [] + corr_pos_y = [] + corr_angle = [] + for j in range(0, int_num_elements * 3): + line = f.readline() + value = line.split(" ")[2] + name = line.split(" ")[0].split("[")[0] + if name == "corr_pos_x": + corr_pos_x.append(value) + elif name == "corr_pos_y": + corr_pos_y.append(value) + elif name == "corr_angle": + corr_angle.append(value) + return (corr_pos_x, corr_pos_y, corr_angle, num_elements) diff --git a/bec_client/plugins/LamNI/x_ray_eye_align.py b/bec_client/plugins/LamNI/x_ray_eye_align.py new file mode 100644 index 0000000..b4010cb --- /dev/null +++ b/bec_client/plugins/LamNI/x_ray_eye_align.py @@ -0,0 +1,888 @@ +import builtins +import datetime +import math +import os +import subprocess +import threading +import time +from collections import defaultdict +from pathlib import Path + +import numpy as np +from bec_utils import bec_logger +from bec_utils.logbook_connector import LogbookMessage +from bec_utils.pdf_writer import PDFWriter +from typeguard import typechecked + +from bec_client.plugins.cSAXS import epics_get, epics_put, fshclose, fshopen + +from .lamni_optics_mixin import LamNIOpticsMixin + +logger = bec_logger.logger + + +class XrayEyeAlign: + # pixel calibration, multiply to get mm + # PIXEL_CALIBRATION = 0.2/209 #.2 with binning + PIXEL_CALIBRATION = 0.2 / 218 # .2 with binning + + def __init__(self, client, lamni) -> None: + self.client = client + self.lamni = lamni + self.device_manager = client.device_manager + self.scans = client.scans + self.xeye = self.device_manager.devices.xeye + self.alignment_values = defaultdict(list) + self.shift_xy = [0, 0] + self._xray_fov_xy = [0, 0] + + def save_frame(self): + epics_put("XOMNYI-XEYE-SAVFRAME:0", 1) + + def update_frame(self): + epics_put("XOMNYI-XEYE-ACQDONE:0", 0) + # start live + epics_put("XOMNYI-XEYE-ACQ:0", 1) + # wait for start live + while epics_get("XOMNYI-XEYE-ACQDONE:0") == 0: + time.sleep(0.5) + print("waiting for live view to start...") + fshopen() + + epics_put("XOMNYI-XEYE-ACQDONE:0", 0) + + while epics_get("XOMNYI-XEYE-ACQDONE:0") == 0: + print("waiting for new frame...") + time.sleep(0.5) + + time.sleep(0.5) + # stop live view + epics_put("XOMNYI-XEYE-ACQ:0", 0) + time.sleep(1) + # fshclose + print("got new frame") + + def _disable_rt_feedback(self): + self.device_manager.devices.rtx.controller.feedback_disable() + + def _enable_rt_feedback(self): + self.device_manager.devices.rtx.controller.feedback_enable_with_reset() + + def tomo_rotate(self, val: float): + # pylint: disable=undefined-variable + umv(self.device_manager.devices.lsamrot, val) + + def get_tomo_angle(self): + return self.device_manager.devices.lsamrot.readback.read()["lsamrot"]["value"] + + def update_fov(self, k: int): + self._xray_fov_xy[0] = max(epics_get(f"XOMNYI-XEYE-XWIDTH_X:{k}"), self._xray_fov_xy[0]) + self._xray_fov_xy[1] = max(0, self._xray_fov_xy[0]) + + @property + def movement_buttons_enabled(self): + return [epics_get("XOMNYI-XEYE-ENAMVX:0"), epics_get("XOMNYI-XEYE-ENAMVY:0")] + + @movement_buttons_enabled.setter + def movement_buttons_enabled(self, enabled: bool): + enabled = int(enabled) + epics_put("XOMNYI-XEYE-ENAMVX:0", enabled) + epics_put("XOMNYI-XEYE-ENAMVY:0", enabled) + + def send_message(self, msg: str): + epics_put("XOMNYI-XEYE-MESSAGE:0.DESC", msg) + + def align(self): + # this makes sure we are in a defined state + self._disable_rt_feedback() + + epics_put("XOMNYI-XEYE-PIXELSIZE:0", self.PIXEL_CALIBRATION) + + self._enable_rt_feedback() + + # initialize + # disable movement buttons + self.movement_buttons_enabled = False + + epics_put("XOMNYI-XEYE-ACQ:0", 0) + self.send_message("please wait...") + + # put sample name + epics_put("XOMNYI-XEYE-SAMPLENAME:0.DESC", "Let us LAMNI...") + + # first step + self._disable_rt_feedback() + k = 0 + + # move zone plate in, eye in to get beam position + self.lamni.lfzp_in() + + self.update_frame() + + # enable submit buttons + self.movement_buttons_enabled = False + epics_put("XOMNYI-XEYE-SUBMIT:0", 0) + epics_put("XOMNYI-XEYE-STEP:0", 0) + self.send_message("Submit center value of FZP.") + + while True: + if epics_get("XOMNYI-XEYE-SUBMIT:0") == 1: + val_x = epics_get(f"XOMNYI-XEYE-XVAL_X:{k}") * self.PIXEL_CALIBRATION # in mm + val_y = epics_get(f"XOMNYI-XEYE-YVAL_Y:{k}") * self.PIXEL_CALIBRATION # in mm + self.alignment_values[k] = [val_x, val_y] + print( + f"Clicked position {k}: x {self.alignment_values[k][0]}, y {self.alignment_values[k][1]}" + ) + + if k == 0: # received center value of FZP + self.send_message("please wait ...") + # perform movement: FZP out, Sample in + self.lamni.loptics_out() + epics_put("XOMNYI-XEYE-SUBMIT:0", -1) # disable submit button + self.movement_buttons_enabled = False + print("Moving sample in, FZP out") + + self._disable_rt_feedback() + time.sleep(0.3) + self._enable_rt_feedback() + time.sleep(0.3) + + # zero is now at the center + self.update_frame() + self.send_message("Go and find the sample") + epics_put("XOMNYI-XEYE-SUBMIT:0", 0) + self.movement_buttons_enabled = True + + elif ( + k == 1 + ): # received sample center value at samroy 0 ie the final base shift values + print( + f"Base shift values from movement are x {self.shift_xy[0]}, y {self.shift_xy[1]}" + ) + self.shift_xy[0] += ( + self.alignment_values[0][0] - self.alignment_values[1][0] + ) * 1000 + self.shift_xy[1] += ( + self.alignment_values[1][1] - self.alignment_values[0][1] + ) * 1000 + print( + f"Base shift values from movement and clicked position are x {self.shift_xy[0]}, y {self.shift_xy[1]}" + ) + + self.scans.lamni_move_to_scan_center( + self.shift_xy[0] / 1000, self.shift_xy[1] / 1000, self.get_tomo_angle() + ).wait() + + self.send_message("please wait ...") + epics_put("XOMNYI-XEYE-SUBMIT:0", -1) # disable submit button + self.movement_buttons_enabled = False + time.sleep(1) + + self.scans.lamni_move_to_scan_center( + self.shift_xy[0] / 1000, self.shift_xy[1] / 1000, self.get_tomo_angle() + ).wait() + + epics_put("XOMNYI-XEYE-ANGLE:0", self.get_tomo_angle()) + self.update_frame() + self.send_message("Submit sample center and FOV (0 deg)") + epics_put("XOMNYI-XEYE-SUBMIT:0", 0) + self.update_fov(k) + + elif 1 < k < 10: # received sample center value at samroy 0 ... 315 + self.send_message("please wait ...") + epics_put("XOMNYI-XEYE-SUBMIT:0", -1) # disable submit button + + # we swtich feedback off before rotating to not have it on and off again later for smooth operation + self._disable_rt_feedback() + self.tomo_rotate((k - 1) * 45 - 45 / 2) + self.scans.lamni_move_to_scan_center( + self.shift_xy[0] / 1000, self.shift_xy[1] / 1000, self.get_tomo_angle() + ).wait() + self._disable_rt_feedback() + self.tomo_rotate((k - 1) * 45) + self.scans.lamni_move_to_scan_center( + self.shift_xy[0] / 1000, self.shift_xy[1] / 1000, self.get_tomo_angle() + ).wait() + + epics_put("XOMNYI-XEYE-ANGLE:0", self.get_tomo_angle()) + self.update_frame() + self.send_message("Submit sample center") + epics_put("XOMNYI-XEYE-SUBMIT:0", 0) + epics_put("XOMNYI-XEYE-ENAMVX:0", 1) + self.update_fov(k) + + elif k == 10: # received sample center value at samroy 270 and done + self.send_message("done...") + epics_put("XOMNYI-XEYE-SUBMIT:0", -1) # disable submit button + self.movement_buttons_enabled = False + self.update_fov(k) + break + + k += 1 + epics_put("XOMNYI-XEYE-STEP:0", k) + if k < 2: + # allow movements, store movements to calculate center + _xrayeyalignmvx = epics_get("XOMNYI-XEYE-MVX:0") + _xrayeyalignmvy = epics_get("XOMNYI-XEYE-MVY:0") + if _xrayeyalignmvx != 0 or _xrayeyalignmvy != 0: + self.shift_xy[0] = self.shift_xy[0] + _xrayeyalignmvx + self.shift_xy[1] = self.shift_xy[1] + _xrayeyalignmvy + self.scans.lamni_move_to_scan_center( + self.shift_xy[0] / 1000, self.shift_xy[1] / 1000, self.get_tomo_angle() + ).wait() + print( + f"Current center horizontal {self.shift_xy[0]} vertical {self.shift_xy[1]}" + ) + epics_put("XOMNYI-XEYE-MVY:0", 0) + epics_put("XOMNYI-XEYE-MVX:0", 0) + self.update_frame() + + time.sleep(0.2) + + self.write_output() + fovx = self._xray_fov_xy[0] * self.PIXEL_CALIBRATION * 1000 / 2 + fovy = self._xray_fov_xy[1] * self.PIXEL_CALIBRATION * 1000 / 2 + print( + f"The largest field of view from the xrayeyealign was \nfovx = {fovx:.0f} microns, fovy = {fovy:.0f} microns" + ) + print("Use matlab routine to fit the current alignment...") + + print( + f"This additional shift is applied to the base shift values\n which are x {self.shift_xy[0]}, y {self.shift_xy[1]}" + ) + + self._disable_rt_feedback() + + self.tomo_rotate(0) + + print( + "\n\nNEXT LOAD ALIGNMENT PARAMETERS\nby running lamni.align.read_alignment_parameters()\n" + ) + + self.client.set_global_var("tomo_fov_offset", self.shift_xy) + + def write_output(self): + with open( + os.path.expanduser("~/Data10/specES1/internal/xrayeye_alignmentvalues"), "w" + ) as alignment_values_file: + alignment_values_file.write(f"angle\thorizontal\tvertical\n") + for k in range(2, 11): + fovx_offset = (self.alignment_values[0][0] - self.alignment_values[k][0]) * 1000 + fovy_offset = (self.alignment_values[k][1] - self.alignment_values[0][1]) * 1000 + print( + f"Writing to file new alignment: number {k}, value x {fovx_offset}, y {fovy_offset}" + ) + alignment_values_file.write(f"{(k-2)*45}\t{fovx_offset}\t{fovy_offset}\n") + + def read_alignment_parameters(self, dir_path=os.path.expanduser("~/Data10/specES1/internal/")): + tomo_fit_xray_eye = np.zeros((2, 3)) + with open(os.path.join(dir_path, "ptychotomoalign_Ax.txt"), "r") as file: + tomo_fit_xray_eye[0][0] = file.readline() + + with open(os.path.join(dir_path, "ptychotomoalign_Bx.txt"), "r") as file: + tomo_fit_xray_eye[0][1] = file.readline() + + with open(os.path.join(dir_path, "ptychotomoalign_Cx.txt"), "r") as file: + tomo_fit_xray_eye[0][2] = file.readline() + + with open(os.path.join(dir_path, "ptychotomoalign_Ay.txt"), "r") as file: + tomo_fit_xray_eye[1][0] = file.readline() + + with open(os.path.join(dir_path, "ptychotomoalign_By.txt"), "r") as file: + tomo_fit_xray_eye[1][1] = file.readline() + + with open(os.path.join(dir_path, "ptychotomoalign_Cy.txt"), "r") as file: + tomo_fit_xray_eye[1][2] = file.readline() + + self.client.set_global_var("tomo_fit_xray_eye", tomo_fit_xray_eye.tolist()) + # x amp, phase, offset, y amp, phase, offset + # 0 0 0 1 0 2 1 0 1 1 1 2 + + print("New alignment parameters loaded from X-ray eye") + print( + f"X Amplitude {tomo_fit_xray_eye[0][0]}," + f"X Phase {tomo_fit_xray_eye[0][1]}, " + f"X Offset {tomo_fit_xray_eye[0][2]}," + f"Y Amplitude {tomo_fit_xray_eye[1][0]}," + f"Y Phase {tomo_fit_xray_eye[1][1]}," + f"Y Offset {tomo_fit_xray_eye[1][2]}" + ) + + +class LamNI(LamNIOpticsMixin): + def __init__(self, client): + super().__init__() + self.client = client + self.align = XrayEyeAlign(client, self) + self.corr_pos_x = [] + self.corr_pos_y = [] + self.corr_angle = [] + self.check_shutter = True + self.check_light_available = True + self.check_fofb = True + self._check_msgs = [] + self.tomo_id = None + self._beam_is_okay = True + self._stop_beam_check_event = None + self.beam_check_thread = None + + def get_beamline_checks_enabled(self): + print( + f"Shutter: {self.check_shutter}\nFOFB: {self.check_fofb}\nLight available: {self.check_light_available}" + ) + + def set_beamline_checks_enabled(self, val: bool): + self.check_shutter = val + self.check_light_available = val + self.check_fofb = val + self.get_beamline_checks_enabled() + + @property + def tomo_fovx_offset(self): + val = self.client.get_global_var("tomo_fov_offset") + if val is None: + return 0.0 + return val[0] / 1000 + + @tomo_fovx_offset.setter + def tomo_fovx_offset(self, val: float): + self.client.set_global_var("tomo_fov_offset", val) + + @property + def tomo_fovy_offset(self): + val = self.client.get_global_var("tomo_fov_offset") + if val is None: + return 0.0 + return val[1] / 1000 + + @tomo_fovy_offset.setter + def tomo_fovy_offset(self, val: float): + self.client.set_global_var("tomo_fov_offset", val) + + @property + def tomo_shellstep(self): + val = self.client.get_global_var("tomo_shellstep") + if val is None: + return 1 + return val + + @tomo_shellstep.setter + def tomo_shellstep(self, val: float): + self.client.set_global_var("tomo_shellstep", val) + + @property + def tomo_circfov(self): + val = self.client.get_global_var("tomo_circfov") + if val is None: + return 0.0 + return val + + @tomo_circfov.setter + def tomo_circfov(self, val: float): + self.client.set_global_var("tomo_circfov", val) + + @property + def tomo_countingtime(self): + val = self.client.get_global_var("tomo_countingtime") + if val is None: + return 0.1 + return val + + @tomo_countingtime.setter + def tomo_countingtime(self, val: float): + self.client.set_global_var("tomo_countingtime", val) + + @property + def manual_shift_x(self): + val = self.client.get_global_var("manual_shift_x") + if val is None: + return 0.0 + return val + + @manual_shift_x.setter + def manual_shift_x(self, val: float): + self.client.set_global_var("manual_shift_x", val) + + @property + def manual_shift_y(self): + val = self.client.get_global_var("manual_shift_y") + if val is None: + return 0.0 + return val + + @manual_shift_y.setter + def manual_shift_y(self, val: float): + self.client.set_global_var("manual_shift_y", val) + + @property + def lamni_piezo_range_x(self): + val = self.client.get_global_var("lamni_piezo_range_x") + if val is None: + return 20 + return val + + @lamni_piezo_range_x.setter + def lamni_piezo_range_x(self, val: float): + if val > 80: + raise ValueError("Piezo range cannot be larger than 80 um.") + self.client.set_global_var("lamni_piezo_range_x", val) + + @property + def lamni_piezo_range_y(self): + val = self.client.get_global_var("lamni_piezo_range_y") + if val is None: + return 20 + return val + + @lamni_piezo_range_y.setter + def lamni_piezo_range_y(self, val: float): + if val > 80: + raise ValueError("Piezo range cannot be larger than 80 um.") + self.client.set_global_var("lamni_piezo_range_y", val) + + @property + def lamni_stitch_x(self): + val = self.client.get_global_var("lamni_stitch_x") + if val is None: + return 0 + return val + + @lamni_stitch_x.setter + @typechecked + def lamni_stitch_x(self, val: int): + self.client.set_global_var("lamni_stitch_x", val) + + @property + def lamni_stitch_y(self): + val = self.client.get_global_var("lamni_stitch_y") + if val is None: + return 0 + return val + + @lamni_stitch_y.setter + @typechecked + def lamni_stitch_y(self, val: int): + self.client.set_global_var("lamni_stitch_y", val) + + @property + def ptycho_reconstruct_foldername(self): + val = self.client.get_global_var("ptycho_reconstruct_foldername") + if val is None: + return "ptycho_reconstruct" + return val + + @ptycho_reconstruct_foldername.setter + def ptycho_reconstruct_foldername(self, val: str): + self.client.set_global_var("ptycho_reconstruct_foldername", val) + + @property + def tomo_angle_stepsize(self): + val = self.client.get_global_var("tomo_angle_stepsize") + if val is None: + return 10.0 + return val + + @tomo_angle_stepsize.setter + def tomo_angle_stepsize(self, val: float): + self.client.set_global_var("tomo_angle_stepsize", val) + + @property + def tomo_stitch_overlap(self): + val = self.client.get_global_var("tomo_stitch_overlap") + if val is None: + return 0.2 + return val + + @tomo_stitch_overlap.setter + def tomo_stitch_overlap(self, val: float): + self.client.set_global_var("tomo_stitch_overlap", val) + + def tomo_scan_projection(self, angle: float): + scans = builtins.__dict__.get("scans") + bec = builtins.__dict__.get("bec") + additional_correction = self.compute_additional_correction(angle) + correction_xeye_mu = self.lamni_compute_additional_correction_xeye_mu(angle) + + self._current_scan_list = [] + + for stitch_x in range(-self.lamni_stitch_x, self.lamni_stitch_x + 1): + for stitch_y in range(-self.lamni_stitch_y, self.lamni_stitch_y + 1): + # pylint: disable=undefined-variable + self._current_scan_list.append(bec.queue.next_scan_number) + logger.info( + f"scans.lamni_fermat_scan(fov_size=[{self.lamni_piezo_range_x},{self.lamni_piezo_range_y}], step={self.tomo_shellstep}, stitch_x={0}, stitch_y={0}, stitch_overlap={1}," + f"center_x={self.tomo_fovx_offset}, center_y={self.tomo_fovy_offset}, " + f"shift_x={self.manual_shift_x+correction_xeye_mu[0]-additional_correction[0]}, " + f"shift_y={self.manual_shift_y+correction_xeye_mu[1]-additional_correction[1]}, " + f"fov_circular={self.tomo_circfov}, angle={angle}, scan_type='fly')" + ) + scans.lamni_fermat_scan( + fov_size=[self.lamni_piezo_range_x, self.lamni_piezo_range_y], + step=self.tomo_shellstep, + stitch_x=stitch_x, + stitch_y=stitch_y, + stitch_overlap=self.tomo_stitch_overlap, + center_x=self.tomo_fovx_offset, + center_y=self.tomo_fovy_offset, + shift_x=( + self.manual_shift_x + correction_xeye_mu[0] - additional_correction[0] + ), + shift_y=( + self.manual_shift_y + correction_xeye_mu[1] - additional_correction[1] + ), + fov_circular=self.tomo_circfov, + angle=angle, + scan_type="fly", + exp_time=self.tomo_countingtime, + ) + + def lamni_compute_additional_correction_xeye_mu(self, angle): + tomo_fit_xray_eye = self.client.get_global_var("tomo_fit_xray_eye") + if tomo_fit_xray_eye is None: + print("Not applying any additional correction. No x-ray eye data available.\n") + return (0, 0) + + # x amp, phase, offset, y amp, phase, offset + # 0 0 0 1 0 2 1 0 1 1 1 2 + correction_x = ( + tomo_fit_xray_eye[0][0] * math.sin(math.radians(angle) + tomo_fit_xray_eye[0][1]) + + tomo_fit_xray_eye[0][2] + ) / 1000 + correction_y = ( + tomo_fit_xray_eye[1][0] * math.sin(math.radians(angle) + tomo_fit_xray_eye[1][1]) + + tomo_fit_xray_eye[1][2] + ) / 1000 + + print(f"Xeye correction x {correction_x}, y {correction_y} for angle {angle}\n") + return (correction_x, correction_y) + + def compute_additional_correction(self, angle): + if not self.corr_pos_x: + print("Not applying any additional correction. No data available.\n") + return (0, 0) + + # find index of closest angle + for j, _ in enumerate(self.corr_pos_x): + newangledelta = math.fabs(self.corr_angle[j] - angle) + if j == 0: + angledelta = newangledelta + additional_correction_shift_x = self.corr_pos_x[j] + additional_correction_shift_y = self.corr_pos_y[j] + continue + + if newangledelta < angledelta: + additional_correction_shift_x = self.corr_pos_x[j] + additional_correction_shift_y = self.corr_pos_y[j] + angledelta = newangledelta + + if additional_correction_shift_x == 0 and angle < self.corr_angle[0]: + additional_correction_shift_x = self.corr_pos_x[0] + additional_correction_shift_y = self.corr_pos_y[0] + + if additional_correction_shift_x == 0 and angle > self.corr_angle[-1]: + additional_correction_shift_x = self.corr_pos_x[-1] + additional_correction_shift_y = self.corr_pos_y[-1] + logger.info( + f"Additional correction shifts: {additional_correction_shift_x} {additional_correction_shift_y}" + ) + return (additional_correction_shift_x, additional_correction_shift_y) + + def lamni_read_additional_correction(self, correction_file: str): + + with open(correction_file, "r") as f: + num_elements = f.readline() + int_num_elements = int(num_elements.split(" ")[2]) + print(int_num_elements) + corr_pos_x = [] + corr_pos_y = [] + corr_angle = [] + for j in range(0, int_num_elements * 3): + line = f.readline() + value = line.split(" ")[2] + name = line.split(" ")[0].split("[")[0] + if name == "corr_pos_x": + corr_pos_x.append(float(value) / 1000) + elif name == "corr_pos_y": + corr_pos_y.append(float(value) / 1000) + elif name == "corr_angle": + corr_angle.append(float(value)) + self.corr_pos_x = corr_pos_x + self.corr_pos_y = corr_pos_y + self.corr_angle = corr_angle + return + + def _run_beamline_checks(self): + msgs = [] + dev = builtins.__dict__.get("dev") + try: + if self.check_shutter: + shutter_val = dev.x12sa_es1_shutter_status.read(cached=True) + if shutter_val["value"].lower() != "open": + self._beam_is_okay = False + msgs.append("Check beam failed: Shutter is closed.") + if self.check_light_available: + machine_status = dev.sls_machine_status.read(cached=True) + if machine_status["value"] not in ["Light Available", "Light-Available"]: + self._beam_is_okay = False + msgs.append("Check beam failed: Light not available.") + if self.check_fofb: + fast_orbit_feedback = dev.sls_fast_orbit_feedback.read(cached=True) + if fast_orbit_feedback["value"] != "running": + self._beam_is_okay = False + msgs.append("Check beam failed: Fast orbit feedback is not running.") + except Exception: + logger.warning("Failed to check beam.") + return msgs + + def _check_beam(self): + while not self._stop_beam_check_event.is_set(): + self._check_msgs = self._run_beamline_checks() + + if not self._beam_is_okay: + self._stop_beam_check_event.set() + time.sleep(1) + + def _start_beam_check(self): + self._beam_is_okay = True + self._stop_beam_check_event = threading.Event() + + self.beam_check_thread = threading.Thread(target=self._check_beam, daemon=True) + self.beam_check_thread.start() + + def _was_beam_okay(self): + self._stop_beam_check_event.set() + self.beam_check_thread.join() + return self._beam_is_okay + + def _print_beamline_checks(self): + for msg in self._check_msgs: + logger.warning(msg) + + def _wait_for_beamline_checks(self): + self._print_beamline_checks() + try: + msg = LogbookMessage(self.client.logbook) + msg.add_text( + f"

Beamline checks failed at {str(datetime.datetime.now())}: {''.join(self._check_msgs)}

" + ).add_tag(["BEC", "beam_check"]) + self.client.logbook.send_logbook_message(msg) + except Exception: + logger.warning("Failed to send update to SciLog.") + + while True: + self._beam_is_okay = True + self._check_msgs = self._run_beamline_checks() + if self._beam_is_okay: + break + self._print_beamline_checks() + time.sleep(1) + + try: + msg = LogbookMessage(self.client.logbook) + msg.add_text( + f"

Operation resumed at {str(datetime.datetime.now())}.

" + ).add_tag(["BEC", "beam_check"]) + self.client.logbook.send_logbook_message(msg) + except Exception: + logger.warning("Failed to send update to SciLog.") + + def add_sample_database( + self, samplename, date, eaccount, scan_number, setup, sample_additional_info, user + ): + """Add a sample to the omny sample database. This also retrieves the tomo id.""" + subprocess.run( + f"wget --user=omny --password=samples -q -O /tmp/currsamplesnr.txt 'https://omny.web.psi.ch/samples/newmeasurement.php?sample={samplename}&date={date}&eaccount={eaccount}&scannr={scan_number}&setup={setup}&additional={sample_additional_info}&user={user}'", + shell=True, + ) + with open("/tmp/currsamplesnr.txt") as tomo_number_file: + tomo_number = int(tomo_number_file.read()) + return tomo_number + + def sub_tomo_scan(self, subtomo_number, start_angle=None): + """start a subtomo""" + dev = builtins.__dict__.get("dev") + bec = builtins.__dict__.get("bec") + + if start_angle is None: + if subtomo_number == 1: + start_angle = 0 + elif subtomo_number == 2: + start_angle = self.tomo_angle_stepsize / 8.0 * 4 + elif subtomo_number == 3: + start_angle = self.tomo_angle_stepsize / 8.0 * 2 + elif subtomo_number == 4: + start_angle = self.tomo_angle_stepsize / 8.0 * 6 + elif subtomo_number == 5: + start_angle = self.tomo_angle_stepsize / 8.0 * 1 + elif subtomo_number == 6: + start_angle = self.tomo_angle_stepsize / 8.0 * 5 + elif subtomo_number == 7: + start_angle = self.tomo_angle_stepsize / 8.0 * 3 + elif subtomo_number == 8: + start_angle = self.tomo_angle_stepsize / 8.0 * 7 + + # _tomo_shift_angles (potential global variable) + _tomo_shift_angles = 0 + angle_end = start_angle + 360 + for angle in np.linspace( + start_angle + _tomo_shift_angles, + angle_end, + num=int(360 / self.tomo_angle_stepsize) + 1, + endpoint=True, + ): + successful = False + if 0 <= angle < 360.05: + print(f"Starting LamNI scan for angle {angle}") + while not successful: + self._start_beam_check() + self.tomo_scan_projection(angle) + + if self._was_beam_okay(): + successful = True + else: + self._wait_for_beamline_checks() + self.tomo_reconstruct() + tomo_id = 0 + with open( + os.path.expanduser("~/Data10/specES1/dat-files/tomography_scannumbers.txt"), + "a+", + ) as out_file: + # pylint: disable=undefined-variable + out_file.write( + f"{bec.queue.next_scan_number-1} {angle} {dev.lsamrot.read()['lsamrot']['value']} {self.tomo_id} {subtomo_number} {0} {'lamni'}\n" + ) + + def tomo_scan(self, subtomo_start=1, start_angle=None): + """start a tomo scan""" + bec = builtins.__dict__.get("bec") + if subtomo_start == 1 and start_angle is None: + # pylint: disable=undefined-variable + self.tomo_id = self.add_sample_database( + "bec_test_sample", + str(datetime.date.today()), + "e20588", + bec.queue.next_scan_number, + "lamni", + "test additional info", + "BEC", + ) + for ii in range(subtomo_start, 9): + self.sub_tomo_scan(ii, start_angle=start_angle) + start_angle = None + + def tomo_parameters(self): + """print and update the tomo parameters""" + print("Current settings:") + print(f"Counting time = {self.tomo_countingtime} s") + print(f"Stepsize microns = {self.tomo_shellstep}") + print( + f"Piezo range (max 80) = {self.lamni_piezo_range_x}, {self.lamni_piezo_range_y}" + ) + print(f"Stitching number x,y = {self.lamni_stitch_x}, {self.lamni_stitch_y}") + print(f"Stitching overlap = {self.tomo_stitch_overlap}") + print(f"Circuilar FOV diam = {self.tomo_circfov}") + print(f"Reconstruction queue name = {self.ptycho_reconstruct_foldername}") + print( + "For information, fov offset is rotating and finding the ROI, manual shift moves rotation center" + ) + print(f" _tomo_fovx_offset = {self.tomo_fovx_offset}") + print(f" _tomo_fovy_offset = {self.tomo_fovy_offset}") + print(f" _manual_shift_x = {self.manual_shift_x}") + print(f" _manual_shift_y = {self.manual_shift_y}") + print(f"Angular step within sub-tomogram: {self.tomo_angle_stepsize} degrees") + print(f"Resulting in number of projections: {360/self.tomo_angle_stepsize*8}\n") + + user_input = input("Are these parameters correctly set for your scan? ") + if user_input == "y": + print("good then") + else: + self.tomo_countingtime = self._get_val(" s", self.tomo_countingtime, float) + self.tomo_shellstep = self._get_val(" um", self.tomo_shellstep, float) + self.lamni_piezo_range_x = self._get_val( + " um", self.lamni_piezo_range_x, float + ) + self.lamni_piezo_range_y = self._get_val( + " um", self.lamni_piezo_range_y, float + ) + self.lamni_stitch_x = self._get_val("", self.lamni_stitch_x, int) + self.lamni_stitch_y = self._get_val("", self.lamni_stitch_y, int) + self.tomo_circfov = self._get_val(" um", self.tomo_circfov, float) + self.ptycho_reconstruct_foldername = self._get_val( + "Reconstruction queue ", self.ptycho_reconstruct_foldername, str + ) + tomo_numberofprojections = self._get_val( + "Number of projections", 360 / self.tomo_angle_stepsize * 8, int + ) + + print(f"The angular step will be {360/tomo_numberofprojections}") + self.tomo_angle_stepsize = 360 / tomo_numberofprojections * 8 + print(f"The angular step in a subtomogram it will be {self.tomo_angle_stepsize}") + + @staticmethod + def _get_val(msg: str, default_value, data_type): + return data_type(input(f"{msg} ({default_value}): ") or default_value) + + def tomo_reconstruct(self, base_path="~/Data10/specES1"): + """write the tomo reconstruct file for the reconstruction queue""" + bec = builtins.__dict__.get("bec") + base_path = os.path.expanduser(base_path) + ptycho_queue_path = Path(os.path.join(base_path, self.ptycho_reconstruct_foldername)) + ptycho_queue_path.mkdir(parents=True, exist_ok=True) + + # pylint: disable=undefined-variable + last_scan_number = bec.queue.next_scan_number - 1 + ptycho_queue_file = os.path.abspath( + os.path.join(ptycho_queue_path, f"scan_{last_scan_number:05d}.dat") + ) + with open(ptycho_queue_file, "w") as queue_file: + scans = " ".join([str(scan) for scan in self._current_scan_list]) + queue_file.write(f"p.scan_number {scans}\n") + queue_file.write(f"p.check_nextscan_started 1\n") + + def write_pdf_report(self): + """create and write the pdf report with the current LamNI settings""" + dev = builtins.__dict__.get("dev") + header = ( + " \n" * 3 + + " ::: ::: ::: ::: :::: ::: ::::::::::: \n" + + " :+: :+: :+: :+:+: :+:+: :+:+: :+: :+: \n" + + " +:+ +:+ +:+ +:+ +:+:+ +:+ :+:+:+ +:+ +:+ \n" + + " +#+ +#++:++#++: +#+ +:+ +#+ +#+ +:+ +#+ +#+ \n" + + " +#+ +#+ +#+ +#+ +#+ +#+ +#+#+# +#+ \n" + + " #+# #+# #+# #+# #+# #+# #+#+# #+# \n" + + " ########## ### ### ### ### ### #### ########### \n" + ) + padding = 20 + piezo_range = f"{self.lamni_piezo_range_x:.2f}/{self.lamni_piezo_range_y:.2f}" + stitching = f"{self.lamni_stitch_x:.2f}/{self.lamni_stitch_y:.2f}" + dataset_id = str(self.client.queue.next_dataset_number) + # pylint: disable=undefined-variable + content = ( + f"{'Sample Name:':<{padding}}{'test':>{padding}}\n", + f"{'Measurement ID:':<{padding}}{str(self.tomo_id):>{padding}}\n", + f"{'Dataset ID:':<{padding}}{dataset_id:>{padding}}\n", + f"{'Sample Info:':<{padding}}{'Sample Info':>{padding}}\n", + f"{'e-account:':<{padding}}{str(self.client.username):>{padding}}\n", + f"{'Number of projections:':<{padding}}{int(360 / self.tomo_angle_stepsize * 8):>{padding}}\n", + f"{'First scan number:':<{padding}}{self.client.queue.next_scan_number:>{padding}}\n", + f"{'Last scan number approx.:':<{padding}}{self.client.queue.next_scan_number + int(360 / self.tomo_angle_stepsize * 8) + 10:>{padding}}\n", + f"{'Current photon energy:':<{padding}}{dev.mokev.read(cached=True)['mokev']['value']:>{padding}.4f}\n", + f"{'Exposure time:':<{padding}}{self.tomo_countingtime:>{padding}.2f}\n", + f"{'Fermat spiral step size:':<{padding}}{self.tomo_shellstep:>{padding}.2f}\n", + f"{'Piezo range (FOV sample plane):':<{padding}}{piezo_range>{padding}}\n", + f"{'Restriction to circular FOV:':<{padding}}{self.tomo_circfov:>{padding}.2f}\n", + f"{'Stitching:':<{padding}}{stitching:>{padding}}\n", + f"{'Number of individual sub-tomograms:':<{padding}}{8:>{padding}}\n", + f"{'Angular step within sub-tomogram:':<{padding}}{self.tomo_angle_stepsize:>{padding}.2f}\n", + ) + + with PDFWriter(os.path.expanduser("~/Data10/")) as file: + file.write(header) + file.write(content) + + msg = LogbookMessage(self.client.logbook) + logo_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lamni_logo.png") + msg.add_file(logo_path).add_text("".join(content).replace("\n", "

")).add_tag( + ["BEC", "tomo_parameters", f"dataset_id_{dataset_id}", "LamNI"] + ) + self.client.logbook.send_logbook_message(msg) diff --git a/bec_client/plugins/cSAXS/__init__.py b/bec_client/plugins/cSAXS/__init__.py new file mode 100644 index 0000000..aff5e6b --- /dev/null +++ b/bec_client/plugins/cSAXS/__init__.py @@ -0,0 +1 @@ +from .cSAXS_beamline import fshopen, fshclose, fshstatus, epics_get, epics_put diff --git a/bec_client/plugins/cSAXS/cSAXS_beamline.py b/bec_client/plugins/cSAXS/cSAXS_beamline.py new file mode 100644 index 0000000..6863e46 --- /dev/null +++ b/bec_client/plugins/cSAXS/cSAXS_beamline.py @@ -0,0 +1,28 @@ +import epics + + +def epics_put(channel, value): + epics.caput(channel, value) + + +def epics_get(channel): + return epics.caget(channel) + + +def fshon(): + pass + + +def fshopen(): + """open the fast shutter""" + epics_put("X12SA-ES1-TTL:OUT_01", 1) + + +def fshclose(): + """close the fast shutter""" + epics_put("X12SA-ES1-TTL:OUT_01", 0) + + +def fshstatus(): + """show the fast shutter status""" + return epics_get("X12SA-ES1-TTL:OUT_01") diff --git a/scan_server/.DS_Store b/scan_server/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d53b2bca08c6cf6407f4aa191719a88c576065de GIT binary patch literal 6148 zcmeHK%}T>S5T32owusP!f_Mse5iHnHMMRKT>kAmsgGz0hQiI!UXj*$Hh1}JL(8uuX zGx$8t{#5LbCGg10mj8pS=;piC?%a0BMcBj@h8z6! zY7~+JqyQ=KTLsL~l3o1mgNRQGkODKOfIJ^8bcv3^M58)7uu&xdVj72)VJvG2$*~MN z1`~}qgT^!!QB#>IF_@;~xMlJjgNa5>2WE;7W>#jVLSbrke7>d9fjJtfB?U- tuple: + """convert from lamni coordinates to stage coordinates""" + y_stage = y / MOVEMENT_SCALE_Y + x_stage = 2 * (x - y_stage * MOVEMENT_SCALE_X) + return (x_stage, y_stage) + + +def lamni_from_stage_coordinates(x_stage: float, y_stage: float) -> tuple: + """convert to lamni coordinates from stage coordinates""" + x = x_stage * 0.5 + y_stage * MOVEMENT_SCALE_X + y = y_stage * MOVEMENT_SCALE_Y + return (x, y) + + +class LamNIMixin: + def _lamni_compute_scan_center(self, x, y, angle_deg): + # assuming a scan point was found at interferometer x,y at zero degrees + # this function computes the new interferometer coordinates of this spot + # at a different rotation angle based on the lamni geometry + alpha = angle_deg / 180 * np.pi + stage_x, stage_y = lamni_to_stage_coordinates(x, y) + stage_x_rot = np.cos(alpha) * stage_x - np.sin(alpha) * stage_y + stage_y_rot = np.sin(alpha) * stage_x + np.cos(alpha) * stage_y + return lamni_from_stage_coordinates(stage_x_rot, stage_y_rot) + + def lamni_new_scan_center_interferometer(self, x, y): + """move to new scan center. xy in mm""" + lsamx_user_params = self.device_manager.devices.lsamx.user_parameter + if lsamx_user_params is None or lsamx_user_params.get("center") is None: + raise RuntimeError("lsamx center is not defined") + lsamy_user_params = self.device_manager.devices.lsamy.user_parameter + if lsamy_user_params is None or lsamy_user_params.get("center") is None: + raise RuntimeError("lsamy center is not defined") + lsamx_center = lsamx_user_params.get("center") + lsamy_center = lsamy_user_params.get("center") + + # could first check if feedback is enabled + yield from self.stubs.send_rpc_and_wait("rtx", "controller.feedback_disable") + time.sleep(0.05) + + rtx_current = yield from self.stubs.send_rpc_and_wait("rtx", "readback.get") + rty_current = yield from self.stubs.send_rpc_and_wait("rty", "readback.get") + lsamx_current = yield from self.stubs.send_rpc_and_wait("lsamx", "readback.get") + lsamy_current = yield from self.stubs.send_rpc_and_wait("lsamy", "readback.get") + + x_stage, y_stage = lamni_to_stage_coordinates(x, y) + + x_center_expect, y_center_expect = lamni_from_stage_coordinates( + lsamx_current - lsamx_center, lsamy_current - lsamy_center + ) + + # in microns + x_drift = x_center_expect * 1000 - rtx_current + y_drift = y_center_expect * 1000 - rty_current + + logger.info(f"Current uncompensated drift of setup is x={x_drift:.3f}, y={y_drift:.3f}") + + move_x = x_stage + lsamx_center + lamni_to_stage_coordinates(x_drift, y_drift)[0] / 1000 + move_y = y_stage + lsamy_center + lamni_to_stage_coordinates(x_drift, y_drift)[1] / 1000 + + coarse_move_req_x = np.abs(lsamx_current - move_x) + coarse_move_req_y = np.abs(lsamy_current - move_y) + + self.device_manager.devices.lsamx.enabled_set = True + self.device_manager.devices.lsamy.enabled_set = True + + if ( + np.abs(y_drift) > 150 + or np.abs(x_drift) > 150 + or (coarse_move_req_y < 0.003 and coarse_move_req_x < 0.003) + ): + logger.info("No drift correction.") + else: + logger.info( + f"Compensating {[val/1000 for val in lamni_to_stage_coordinates(x_drift,y_drift)]}" + ) + yield from self.stubs.set_and_wait( + device=["lsamx", "lsamy"], positions=[move_x, move_y] + ) + + time.sleep(0.01) + rtx_current = yield from self.stubs.send_rpc_and_wait("rtx", "readback.get") + rty_current = yield from self.stubs.send_rpc_and_wait("rty", "readback.get") + + logger.info(f"New scan center interferometer {rtx_current:.3f}, {rty_current:.3f} microns") + + # second iteration + x_center_expect, y_center_expect = lamni_from_stage_coordinates(x_stage, y_stage) + + # in microns + x_drift2 = x_center_expect * 1000 - rtx_current + y_drift2 = y_center_expect * 1000 - rty_current + logger.info( + f"Uncompensated drift of setup after first iteration is x={x_drift2:.3f}, y={y_drift2:.3f}" + ) + + if np.abs(x_drift2) > 5 or np.abs(y_drift2) > 5: + logger.info( + f"Compensating second iteration {[val/1000 for val in lamni_to_stage_coordinates(x_drift2,y_drift2)]}" + ) + move_x = ( + x_stage + + lsamx_center + + lamni_to_stage_coordinates(x_drift, y_drift)[0] / 1000 + + lamni_to_stage_coordinates(x_drift2, y_drift2)[0] / 1000 + ) + move_y = ( + y_stage + + lsamy_center + + lamni_to_stage_coordinates(x_drift, y_drift)[1] / 1000 + + lamni_to_stage_coordinates(x_drift2, y_drift2)[1] / 1000 + ) + yield from self.stubs.set_and_wait( + device=["lsamx", "lsamy"], positions=[move_x, move_y] + ) + time.sleep(0.01) + rtx_current = yield from self.stubs.send_rpc_and_wait("rtx", "readback.get") + rty_current = yield from self.stubs.send_rpc_and_wait("rty", "readback.get") + + logger.info( + f"New scan center interferometer after second iteration {rtx_current:.3f}, {rty_current:.3f} microns" + ) + x_drift2 = x_center_expect * 1000 - rtx_current + y_drift2 = y_center_expect * 1000 - rty_current + logger.info( + f"Uncompensated drift of setup after second iteration is x={x_drift2:.3f}, y={y_drift2:.3f}" + ) + else: + logger.info("No second iteration required") + + self.device_manager.devices.lsamx.enabled_set = False + self.device_manager.devices.lsamy.enabled_set = False + + yield from self.stubs.send_rpc_and_wait("rtx", "controller.feedback_enable_without_reset") + + +class LamNIMoveToScanCenter(RequestBase, LamNIMixin): + scan_name = "lamni_move_to_scan_center" + scan_report_hint = None + scan_type = "step" + required_kwargs = [] + arg_input = [ScanArgType.FLOAT, ScanArgType.FLOAT, ScanArgType.FLOAT] + arg_bundle_size = None + + def __init__(self, *args, parameter=None, **kwargs): + """ + Move LamNI to a new scan center. + + Args: + *args: shift x, shift y, tomo angle in deg + + Examples: + >>> scans.lamni_move_to_scan_center(1.2, 2.8, 12.5) + """ + super().__init__(parameter=parameter, **kwargs) + + def run(self): + center_x, center_y = self._lamni_compute_scan_center(*self.caller_args) + yield from self.lamni_new_scan_center_interferometer(center_x, center_y) + + +class LamNIFermatScan(ScanBase, LamNIMixin): + scan_name = "lamni_fermat_scan" + scan_report_hint = "table" + scan_type = "step" + required_kwargs = ["fov_size", "exp_time", "step", "angle"] + arg_input = [] + arg_bundle_size = None + + def __init__(self, *args, parameter=None, **kwargs): + """ + A LamNI scan following Fermat's spiral. + + Kwargs: + fov_size [um]: Fov in the piezo plane (i.e. piezo range). Max 80 um + step [um]: stepsize + shift_x/y [mm]: extra shift in x/y. The shift is directly applied to the scan. It will not be auto rotated. (default 0). + center_x/center_y [mm]: center position in x/y at 0 deg. This shift is rotated + using the geometry of LamNI + It is determined by the first 'click' in the x-ray eye alignemnt procedure + angle [deg]: rotation angle (will rotate first) + scan_type: fly (i.e. HW triggered step in case of LamNI) or step + stitch_x/y: shift scan to adjacent stitch region + fov_circular [um]: generate a circular field of view in the sample plane. This is an additional cropping to fov_size. + stitch_overlap [um]: overlap of the stitched regions + Returns: + + Examples: + >>> scans.lamni_fermat_scan(fov_size=[20], step=0.5, exp_time=0.1) + >>> scans.lamni_fermat_scan(fov_size=[20, 25], center_x=0.02, center_y=0, shift_x=0, shift_y=0, angle=0, step=0.5, fov_circular=0, exp_time=0.1) + """ + + super().__init__(parameter=parameter, **kwargs) + self.axis = [] + scan_kwargs = parameter.get("kwargs", {}) + self.fov_size = scan_kwargs.get("fov_size") + if len(self.fov_size) == 1: + self.fov_size *= 2 # if we only have one argument, let's assume it's a square + self.step = scan_kwargs.get("step", 0.1) + self.center_x = scan_kwargs.get("center_x", 0) + self.center_y = scan_kwargs.get("center_y", 0) + self.shift_x = scan_kwargs.get("shift_x", 0) + self.shift_y = scan_kwargs.get("shift_y", 0) + self.angle = scan_kwargs.get("angle", 0) + self.scan_type = scan_kwargs.get("scan_type", "fly") + self.stitch_x = scan_kwargs.get("stitch_x", 0) + self.stitch_y = scan_kwargs.get("stitch_y", 0) + self.fov_circular = scan_kwargs.get("fov_circular", 0) + self.stitch_overlap = scan_kwargs.get("stitch_overlap", 1) + # self.keep_plot = scan_kwargs.get("keep_plot", 0) + # self.optim_trajectory = scan_kwargs.get("optim_trajectory", "corridor") + + def initialize(self): + self.scan_motors = ["rtx", "rty"] + + def prepare_positions(self): + self._calculate_positions() + self._optimize_trajectory() + # self._sort_positions() + + self.num_pos = len(self.positions) + + def _lamni_check_pos_in_fov_range_and_circ_fov(self, x, y) -> bool: + # this function checks if positions are reachable in a scan + # these x y intererometer positions are not shifted to the scan center + # so its purpose is to see if the position is reachable by the + # rotated piezo stage. For a scan these positions have to be shifted to + # the current scan center before starting the scan + stage_x, stage_y = lamni_to_stage_coordinates(x, y) + stage_x_with_stitch, stage_y_with_stitch = self._lamni_compute_stitch_center( + self.stitch_x, self.stitch_y, self.angle + ) + stage_x_with_stitch, stage_y_with_stitch = lamni_to_stage_coordinates( + stage_x_with_stitch, stage_y_with_stitch + ) + + # piezo stage is currently rotated to stage_angle_deg in degrees + # rotate positions to the piezo stage system + alpha = (self.angle - 300 + 30.5) / 180 * np.pi + stage_x_rot = np.cos(alpha) * stage_x + np.sin(alpha) * stage_y + stage_y_rot = -np.sin(alpha) * stage_x + np.cos(alpha) * stage_y + + stage_x_rot_with_stitch = ( + np.cos(alpha) * stage_x_with_stitch + np.sin(alpha) * stage_y_with_stitch + ) + stage_y_rot_with_stitch = ( + -np.sin(alpha) * stage_x_with_stitch + np.cos(alpha) * stage_y_with_stitch + ) + + return ( + np.abs(stage_x_rot) <= (self.fov_size[1] / 2) + and np.abs(stage_y_rot) <= (self.fov_size[0] / 2) + and ( + self.fov_circular == 0 + or ( + np.power((stage_x_rot_with_stitch + stage_x_rot), 2) + + np.power((stage_y_rot_with_stitch + stage_y_rot), 2) + ) + <= pow((self.fov_circular / 2), 2) + ) + ) + + def _prepare_setup(self): + yield from self.stubs.send_rpc_and_wait("rtx", "controller.clear_trajectory_generator") + yield from self.lamni_rotation(self.angle) + total_shift_x, total_shift_y = self._compute_total_shift() + yield from self.lamni_new_scan_center_interferometer(total_shift_x, total_shift_y) + # self._plot_target_pos() + if self.scan_type == "fly": + yield from self._transfer_positions_to_LamNI() + + # def _plot_target_pos(self): + # # return + # plt.plot(self.positions[:, 0], self.positions[:, 1], alpha=0.2) + # plt.scatter(self.positions[:, 0], self.positions[:, 1]) + # plt.savefig("mygraph.png") + # if not self.keep_plot: + # plt.clf() + # # plt.show() + + def _transfer_positions_to_LamNI(self): + yield from self.stubs.send_rpc_and_wait( + "rtx", "controller.add_pos_to_scan", (self.positions.tolist(),) + ) + + def _calculate_positions(self): + self.positions = self.get_lamni_fermat_spiral_pos( + -np.abs(self.fov_size[0] / 2), + np.abs(self.fov_size[0] / 2), + -np.abs(self.fov_size[1] / 2), + np.abs(self.fov_size[1] / 2), + step=self.step, + spiral_type=0, + center=False, + ) + + def _lamni_compute_stitch_center(self, xcount, ycount, angle_deg): + alpha = angle_deg / 180 * np.pi + stage_x = xcount * (self.fov_size[0] - self.stitch_overlap) + stage_y = ycount * (self.fov_size[1] - self.stitch_overlap) + x_rot = np.cos(alpha) * stage_x - np.sin(alpha) * stage_y + y_rot = np.sin(alpha) * stage_x + np.cos(alpha) * stage_y + + return lamni_from_stage_coordinates(x_rot, y_rot) + + def _compute_total_shift(self): + _shfitx, _shfity = self._lamni_compute_scan_center(self.center_x, self.center_y, self.angle) + x_stitch_shift, y_stitch_shift = self._lamni_compute_stitch_center( + self.stitch_x, self.stitch_y, self.angle + ) + logger.info( + f"Total shift [mm] {_shfitx+x_stitch_shift/1000+self.shift_x}, {_shfity+y_stitch_shift/1000+self.shift_y}" + ) + return ( + _shfitx + x_stitch_shift / 1000 + self.shift_x, + _shfity + y_stitch_shift / 1000 + self.shift_y, + ) + + def get_lamni_fermat_spiral_pos( + self, m1_start, m1_stop, m2_start, m2_stop, step=1, spiral_type=0, center=False + ): + """[summary] + + Args: + m1_start (float): start position motor 1 + m1_stop (float): end position motor 1 + m2_start (float): start position motor 2 + m2_stop (float): end position motor 2 + step (float, optional): Step size. Defaults to 1. + spiral_type (float, optional): Angular offset in radians that determines the shape of the spiral. + A spiral with spiral_type=2 is the same as spiral_type=0. Defaults to 0. + center (bool, optional): Add a center point. Defaults to False. + + Raises: + TypeError: [description] + TypeError: [description] + TypeError: [description] + + Returns: + [type]: [description] + + Yields: + [type]: [description] + """ + positions = [] + phi = 2 * np.pi * ((1 + np.sqrt(5)) / 2.0) + spiral_type * np.pi + + start = int(not center) + + length_axis1 = np.abs(m1_stop - m1_start) + length_axis2 = np.abs(m2_stop - m2_start) + n_max = int(length_axis1 * length_axis2 * 3.2 / step / step) + + total_shift_x, total_shift_y = self._compute_total_shift() + + for ii in range(start, n_max): + radius = step * 0.57 * np.sqrt(ii) + # FOV is restructed below at check pos in range + # if abs(radius * np.sin(ii * phi)) > length_axis1 / 2: + # continue + # if abs(radius * np.cos(ii * phi)) > length_axis2 / 2: + # continue + x = radius * np.sin(ii * phi) + y = radius * np.cos(ii * phi) + if self._lamni_check_pos_in_fov_range_and_circ_fov(x, y): + positions.extend([(x + total_shift_x * 1000, y + total_shift_y * 1000)]) + # for testing we just shift by center_i and prepare also the setup to center_i + return np.array(positions) + + def lamni_rotation(self, angle): + # get last setpoint (cannot be based on pos get because they will deviate slightly) + lsamrot_current_setpoint = yield from self.stubs.send_rpc_and_wait( + "lsamrot", "user_setpoint.get" + ) + if angle == lsamrot_current_setpoint: + logger.info("No rotation required") + else: + logger.info("Rotating to requested angle") + yield from self.stubs.set_and_wait(device=["lsamrot"], positions=[angle]) + + def scan_core(self): + if self.scan_type == "step": + for ind, pos in self._get_position(): + for self.burst_index in range(self.burst_at_each_point): + yield from self._at_each_point(ind, pos) + self.burst_index = 0 + elif self.scan_type == "fly": + # use a device message to receive the scan number and + # scan ID before sending the message to the device server + yield from self.stubs.kickoff(device="rtx") + while True: + yield from self.stubs.read_and_wait(group="primary", wait_group="readout_primary") + msg = self.device_manager.producer.get(MessageEndpoints.device_status("rt_scan")) + if msg: + status = BECMessage.DeviceStatusMessage.loads(msg) + if status.content.get("status", 1) == 0 and self.metadata.get( + "RID" + ) == status.metadata.get("RID"): + break + + time.sleep(1) + logger.debug("reading monitors") + # yield from self.device_rpc("rtx", "controller.kickoff") + + def run(self): + self.initialize() + yield from self.read_scan_motors() + self.prepare_positions() + yield from self._prepare_setup() + yield from self.open_scan() + yield from self.stage() + yield from self.run_baseline_reading() + yield from self.scan_core() + yield from self.finalize() + yield from self.unstage() + yield from self.cleanup() diff --git a/scibec/lamni_config.py b/scibec/lamni_config.py new file mode 100644 index 0000000..a19fa50 --- /dev/null +++ b/scibec/lamni_config.py @@ -0,0 +1,150 @@ +import yaml + +from .config import DemoConfig, X12SAConfig + + +class LamNIConfig(DemoConfig, X12SAConfig): + def run(self): + # self.write_galil_motors() + # self.write_rt_motors() + # self.write_smaract_motors() + # self.write_eiger1p5m() + self.write_x12sa_status() + self.write_sls_status() + self.load_csaxs_config() + # self.write_sim_user_motors() + # self.write_sim_beamline_motors() + # self.write_sim_beamline_monitors() + + def write_galil_motors(self): + lamni_galil_motors = [ + ("lsamx", "A", -1, 0.5, {"center": 8.768000}), + ("lsamy", "B", 1, 0.5, {"center": 10.041000}), + ("lsamrot", "C", 1, 0.5, {}), + ("loptz", "D", -1, 0.5, {}), + ("loptx", "E", 1, 0.5, {"in": -0.8380, "out": -0.699}), + ("lopty", "F", 1, 0.5, {"in": 3.3540, "out": 3.53}), + ("leyex", "G", -1, 0.001, {"in": 14.117000}), + ("leyey", "H", -1, 0.001, {"in": 48.069000, "out": 0.5}), + ] + out = {} + for m in lamni_galil_motors: + out[m[0]] = dict( + { + "status": {"enabled": True, "enabled_set": True}, + "deviceClass": "GalilMotor", + "deviceConfig": { + "axis_Id": m[1], + "name": m[0], + "labels": m[0], + "host": "mpc2680.psi.ch", + "port": 8081, + "sign": m[2], + "limits": [0, 0], + "tolerance": m[3], + "device_access": True, + "device_mapping": {"rt": "rtx"}, + }, + "acquisitionConfig": { + "schedule": "sync", + "acquisitionGroup": "motor", + "readoutPriority": "baseline", + }, + "deviceTags": ["lamni"], + } + ) + if m[4]: + out[m[0]]["userParameter"] = m[4] + self.write_section(out, "LamNI Galil motors") + + def write_rt_motors(self): + lamni_rt_motors = [ + ("rtx", "A", 1), + ("rty", "B", 1), + ] + out = dict() + for m in lamni_rt_motors: + out[m[0]] = dict( + { + "status": {"enabled": True, "enabled_set": True}, + "deviceClass": "RtLamniMotor", + "deviceConfig": { + "axis_Id": m[1], + "name": m[0], + "labels": m[0], + "host": "mpc2680.psi.ch", + "port": 3333, + "limits": [0, 0], + "sign": m[2], + "device_access": True, + }, + "acquisitionConfig": { + "schedule": "sync", + "acquisitionGroup": "motor", + "readoutPriority": "baseline", + }, + "deviceTags": ["lamni"], + } + ) + self.write_section(out, "LamNI RT") + + def write_smaract_motors(self): + lamni_smaract_motors = [ + ("losax", "A", -1, {"in": -0.848000}), + ("losay", "B", -1, {"in": 0.135000, "out": 3.8}), + ("losaz", "C", 1, {"in": -1, "out": -3}), + ("lcsx", "D", -1, {}), + ("lcsy", "E", -1, {}), + ] + out = dict() + for m in lamni_smaract_motors: + out[m[0]] = dict( + { + "status": {"enabled": True, "enabled_set": True}, + "deviceClass": "SmaractMotor", + "deviceConfig": { + "axis_Id": m[1], + "name": m[0], + "labels": m[0], + "host": "mpc2680.psi.ch", + "port": 8085, + "limits": [0, 0], + "sign": m[2], + "tolerance": 0.05, + }, + "acquisitionConfig": { + "schedule": "sync", + "acquisitionGroup": "motor", + "readoutPriority": "baseline", + }, + "deviceTags": ["lamni"], + } + ) + if m[3]: + out[m[0]]["userParameter"] = m[3] + self.write_section(out, "LamNI SmarAct motors") + + def write_eiger1p5m(self): + out = { + "eiger1p5m": { + "description": "Eiger 1.5M in vacuum detector, in-house developed, PSI", + "status": {"enabled": True, "enabled_set": True}, + "deviceClass": "Eiger1p5MDetector", + "deviceConfig": {"device_access": True, "name": "eiger1p5m"}, + "acquisitionConfig": { + "schedule": "sync", + "acquisitionGroup": "detector", + "readoutPriority": "monitored", + }, + "deviceTags": ["detector"], + } + } + self.write_section(out, "LamNI Eiger 1.5M in vacuum") + + def load_csaxs_config(self): + CONFIG_PATH = "./init_scibec/configs/test_config_cSAXS.yaml" + content = {} + with open(CONFIG_PATH, "r") as csaxs_config_file: + content = yaml.safe_load(csaxs_config_file.read()) + + self.write_section(content, "Default cSAXS config") diff --git a/scibec/test_config_cSAXS.yaml b/scibec/test_config_cSAXS.yaml new file mode 100644 index 0000000..a49d3f8 --- /dev/null +++ b/scibec/test_config_cSAXS.yaml @@ -0,0 +1,2326 @@ +ppth: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: phase plate angle + deviceClass: EpicsMotor + deviceConfig: + name: ppth + prefix: X12SA-ES2-ES23 + deviceTags: + - cSAXS + - lamni + - phase plates + onFailure: buffer + status: + enabled: true + enabled_set: true +ppx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: phase plate + deviceClass: EpicsMotor + deviceConfig: + name: ppx + prefix: X12SA-ES2-ES01 + deviceTags: + - cSAXS + - lamni + - phase plates + onFailure: buffer + status: + enabled: true + enabled_set: true +ppthenc: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: phase plate encoder + deviceClass: EpicsSignalRO + deviceConfig: + name: ppthenc + prefix: X12SA-ES2-EC1.VAL + deviceTags: + - cSAXS + - lamni + - phase plates + onFailure: buffer + status: + enabled: true + enabled_set: false + +FBPMDX: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: FOFB reference + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: FBPMDX + read_pv: X12SA-ID-FBPMD:X + deviceTags: + - cSAXS + - fofb + onFailure: buffer + status: + enabled: true + enabled_set: false +FBPMDY: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: FOFB reference + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: FBPMDY + read_pv: X12SA-ID-FBPMD:Y + deviceTags: + - cSAXS + - fofb + onFailure: buffer + status: + enabled: true + enabled_set: false +FBPMUX: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: FOFB reference + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: FBPMUX + read_pv: X12SA-ID-FBPMU:X + deviceTags: + - cSAXS + - fofb + onFailure: buffer + status: + enabled: true + enabled_set: false +FBPMUY: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: FOFB reference + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: FBPMUY + read_pv: X12SA-ID-FBPMU:Y + deviceTags: + - cSAXS + - fofb + onFailure: buffer + status: + enabled: true + enabled_set: false +XASYM: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: FOFB reference + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: XASYM + read_pv: X12SA-LBB:X-ASYM + deviceTags: + - cSAXS + - fofb + onFailure: buffer + status: + enabled: true + enabled_set: false +XSYM: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: FOFB reference + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: XSYM + read_pv: X12SA-LBB:X-SYM + deviceTags: + - cSAXS + - fofb + onFailure: buffer + status: + enabled: true + enabled_set: false +YASYM: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: FOFB reference + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: YASYM + read_pv: X12SA-LBB:Y-ASYM + deviceTags: + - cSAXS + - fofb + onFailure: buffer + status: + enabled: true + enabled_set: false +YSYM: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: FOFB reference + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: YSYM + read_pv: X12SA-LBB:Y-SYM + deviceTags: + - cSAXS + - fofb + onFailure: buffer + status: + enabled: true + enabled_set: false +aptrx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: ES aperture horizontal movement + deviceClass: EpicsMotor + deviceConfig: + name: aptrx + prefix: X12SA-ES1-PIN1:TRX1 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +aptry: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: ES aperture vertical movement + deviceClass: EpicsMotor + deviceConfig: + name: aptry + prefix: X12SA-ES1-PIN1:TRY1 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +bm1trx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: FrontEnd XBPM 1 horizontal movement + deviceClass: EpicsMotor + deviceConfig: + name: bm1trx + prefix: X12SA-FE-BM1:TRH + deviceTags: + - cSAXS + - bm1 + onFailure: buffer + status: + enabled: true + enabled_set: false +bm1try: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: FrontEnd XBPM 1 vertical movement + deviceClass: EpicsMotor + deviceConfig: + name: bm1try + prefix: X12SA-FE-BM1:TRV + deviceTags: + - cSAXS + - bm1 + onFailure: buffer + status: + enabled: true + enabled_set: false +bm2trx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: FrontEnd XBPM 2 horizontal movement + deviceClass: EpicsMotor + deviceConfig: + name: bm2trx + prefix: X12SA-FE-BM2:TRH + deviceTags: + - cSAXS + - bm2 + onFailure: buffer + status: + enabled: true + enabled_set: false +bm2try: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: FrontEnd XBPM 2 vertical movement + deviceClass: EpicsMotor + deviceConfig: + name: bm2try + prefix: X12SA-FE-BM2:TRV + deviceTags: + - cSAXS + - bm2 + onFailure: buffer + status: + enabled: true + enabled_set: false +bm3trx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch XBPM 1 horizontal movement + deviceClass: EpicsMotor + deviceConfig: + name: bm3trx + prefix: X12SA-OP-BM1:TRX1 + deviceTags: + - cSAXS + - bm3 + onFailure: buffer + status: + enabled: true + enabled_set: false +bm3try: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch XBPM 1 vertical movement + deviceClass: EpicsMotor + deviceConfig: + name: bm3try + prefix: X12SA-OP-BM1:TRY1 + deviceTags: + - cSAXS + - bm3 + onFailure: buffer + status: + enabled: true + enabled_set: false +bm4trx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch XBPM 2 horizontal movement + deviceClass: EpicsMotor + deviceConfig: + name: bm4trx + prefix: X12SA-OP-BM2:TRX1 + deviceTags: + - cSAXS + - bm4 + onFailure: buffer + status: + enabled: true + enabled_set: false +bm4try: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch XBPM 2 vertical movement + deviceClass: EpicsMotor + deviceConfig: + name: bm4try + prefix: X12SA-OP-BM2:TRY1 + deviceTags: + - cSAXS + - bm4 + onFailure: buffer + status: + enabled: true + enabled_set: false +bm5trx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch XBPM 3 horizontal movement + deviceClass: EpicsMotor + deviceConfig: + name: bm5trx + prefix: X12SA-OP-BM3:TRX1 + deviceTags: + - cSAXS + - bm5 + onFailure: buffer + status: + enabled: true + enabled_set: false +bm5try: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch XBPM 3 vertical movement + deviceClass: EpicsMotor + deviceConfig: + name: bm5try + prefix: X12SA-OP-BM3:TRY1 + deviceTags: + - cSAXS + - bm5 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm1: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 1: Somewhere around mono (VME)" + deviceClass: XbpmCsaxsOp + deviceConfig: + name: bpm1 + prefix: "X12SA-OP-BPM2:" + deviceTags: + - cSAXS + - bpm1 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm1i: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Some VME XBPM... + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm1i + read_pv: X12SA-OP-BPM1:SUM + deviceTags: + - cSAXS + - bpm1 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm2: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 2: Somewhere around mono (VME)" + deviceClass: XbpmCsaxsOp + deviceConfig: + name: bpm2 + prefix: "X12SA-OP-BPM2:" + deviceTags: + - cSAXS + - bpm2 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm2i: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Some VME XBPM... + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm2i + read_pv: X12SA-OP-BPM2:SUM + deviceTags: + - cSAXS + - bpm2 + onFailure: buffer + status: + enabled: true + enabled_set: false +# bpm3: +# acquisitionConfig: +# acquisitionGroup: monitor +# readoutPriority: baseline +# schedule: sync +# description: "XBPM 3: White beam AH501 before mono" +# deviceClass: QuadEM +# deviceConfig: +# name: bpm3 +# prefix: "X12SA-OP-BPM3:" +# deviceTags: +# - cSAXS +# - bpm3 +# onFailure: buffer +# status: +# enabled: true +# enabled_set: false +bpm3a: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 3: White beam AH501 before mono" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm3a + read_pv: X12SA-OP-BPM3:Current1:MeanValue_RBV + deviceTags: + - cSAXS + - bpm3 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm3b: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 3: White beam AH501 before mono" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm3b + read_pv: X12SA-OP-BPM3:Current2:MeanValue_RBV + deviceTags: + - cSAXS + - bpm3 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm3c: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 3: White beam AH501 before mono" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm3c + read_pv: X12SA-OP-BPM3:Current3:MeanValue_RBV + deviceTags: + - cSAXS + - bpm3 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm3d: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 3: White beam AH501 before mono" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm3d + read_pv: X12SA-OP-BPM3:Current4:MeanValue_RBV + deviceTags: + - cSAXS + - bpm3 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm4i: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 4: integrated counts" + deviceClass: Bpm4i + deviceConfig: + name: bpm4i + prefix: X12SA-OP1-SCALER. + deviceTags: + - cSAXS + - bpm4 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm4a: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 4: VME between mono and mirror" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm4a + read_pv: X12SA-OP1-SCALER.S2 + deviceTags: + - cSAXS + - bpm4 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm4b: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 4: VME between mono and mirror" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm4b + read_pv: X12SA-OP1-SCALER.S3 + deviceTags: + - cSAXS + - bpm4 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm4c: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 4: VME between mono and mirror" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm4c + read_pv: X12SA-OP1-SCALER.S4 + deviceTags: + - cSAXS + - bpm4 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm4d: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 4: VME between mono and mirror" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm4d + read_pv: X12SA-OP1-SCALER.S5 + deviceTags: + - cSAXS + - bpm4 + onFailure: buffer + status: + enabled: true + enabled_set: false +# bpm5: +# acquisitionConfig: +# acquisitionGroup: monitor +# readoutPriority: baseline +# schedule: sync +# description: "XBPM 5: AH501 past the mirror" +# deviceClass: QuadEM +# deviceConfig: +# name: bpm5 +# prefix: "X12SA-OP-BPM5:" +# deviceTags: +# - cSAXS +# - bpm5 +# onFailure: buffer +# status: +# enabled: true +# enabled_set: false +bpm5a: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 5: AH501 past the mirror" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm5a + read_pv: X12SA-OP-BPM5:Current1:MeanValue_RBV + deviceTags: + - cSAXS + - bpm5 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm5b: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 5: AH501 past the mirror" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm5b + read_pv: X12SA-OP-BPM5:Current2:MeanValue_RBV + deviceTags: + - cSAXS + - bpm5 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm5c: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 5: AH501 past the mirror" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm5c + read_pv: X12SA-OP-BPM5:Current3:MeanValue_RBV + deviceTags: + - cSAXS + - bpm5 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm5d: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 5: AH501 past the mirror" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm5d + read_pv: X12SA-OP-BPM5:Current4:MeanValue_RBV + deviceTags: + - cSAXS + - bpm5 + onFailure: buffer + status: + enabled: true + enabled_set: false +# bpm6: +# acquisitionConfig: +# acquisitionGroup: monitor +# readoutPriority: baseline +# schedule: sync +# description: "XBPM 6: Xbox, not commissioned" +# deviceClass: QuadEM +# deviceConfig: +# name: bpm6 +# prefix: "X12SA-OP-BPM6:" +# deviceTags: +# - cSAXS +# - bpm6 +# onFailure: buffer +# status: +# enabled: true +# enabled_set: false +bpm6a: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 6: Xbox, not commissioned" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm6a + read_pv: X12SA-OP-BPM6:Current1:MeanValue_RBV + deviceTags: + - cSAXS + - bpm6 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm6b: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 6: Xbox, not commissioned" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm6b + read_pv: X12SA-OP-BPM6:Current2:MeanValue_RBV + deviceTags: + - cSAXS + - bpm6 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm6c: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 6: Xbox, not commissioned" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm6c + read_pv: X12SA-OP-BPM6:Current3:MeanValue_RBV + deviceTags: + - cSAXS + - bpm6 + onFailure: buffer + status: + enabled: true + enabled_set: false +bpm6d: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: "XBPM 6: Xbox, not commissioned" + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: bpm6d + read_pv: X12SA-OP-BPM6:Current4:MeanValue_RBV + deviceTags: + - cSAXS + - bpm6 + onFailure: buffer + status: + enabled: true + enabled_set: false +bs1x: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Beam stop 1 x + deviceClass: EpicsMotor + deviceConfig: + name: bs1x + prefix: X12SA-ES1-BS1:TRX1 + deviceTags: + - cSAXS + - beam stop + onFailure: buffer + status: + enabled: true + enabled_set: false +bs1y: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Beam stop 1 y + deviceClass: EpicsMotor + deviceConfig: + name: bs1y + prefix: X12SA-ES1-BS1:TRY1 + deviceTags: + - cSAXS + - beam stop + onFailure: buffer + status: + enabled: true + enabled_set: false +bs2x: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Beam stop 2 x + deviceClass: EpicsMotor + deviceConfig: + name: bs2x + prefix: X12SA-ES1-BS2:TRX1 + deviceTags: + - cSAXS + - beam stop + onFailure: buffer + status: + enabled: true + enabled_set: false +bs2y: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Beam stop 2 y + deviceClass: EpicsMotor + deviceConfig: + name: bs2y + prefix: X12SA-ES1-BS2:TRY1 + deviceTags: + - cSAXS + - beam stop + onFailure: buffer + status: + enabled: true + enabled_set: false +curr: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: SLS ring current + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: curr + read_pv: ARIDI-PCT:CURRENT + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +cyb: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Some scaler... + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: cyb + read_pv: X12SA-ES1-SCALER.S2 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +dettrx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Detector tower motion + deviceClass: EpicsMotor + deviceConfig: + name: dettrx + prefix: X12SA-ES1-DET1:TRX1 + deviceTags: + - cSAXS + - detector table + onFailure: buffer + status: + enabled: true + enabled_set: false +di2trx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: FrontEnd diaphragm 2 horizontal movement + deviceClass: EpicsMotor + deviceConfig: + name: di2trx + prefix: X12SA-FE-DI2:TRX1 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +di2try: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: FrontEnd diaphragm 2 vertical movement + deviceClass: EpicsMotor + deviceConfig: + name: di2try + prefix: X12SA-FE-DI2:TRY1 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +diode: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Some scaler... + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: diode + read_pv: X12SA-ES1-SCALER.S3 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +dtpush: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Detector tower tilt pusher + deviceClass: EpicsMotor + deviceConfig: + name: dtpush + prefix: X12SA-ES1-DETT:ROX1 + deviceTags: + - cSAXS + - detector table + onFailure: buffer + status: + enabled: true + enabled_set: false +dtth: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Detector tower tilt rotation + deviceClass: PmDetectorRotation + deviceConfig: + name: dtth + prefix: X12SA-ES1-DETT:ROX1 + deviceTags: + - cSAXS + - detector table + onFailure: buffer + status: + enabled: true + enabled_set: false +dttrx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Detector tower motion + deviceClass: EpicsMotor + deviceConfig: + name: dttrx + prefix: X12SA-ES1-DETT:TRX1 + deviceTags: + - cSAXS + - detector table + onFailure: buffer + status: + enabled: true + enabled_set: false +dttry: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Detector tower motion, no encoder + deviceClass: EpicsMotor + deviceConfig: + name: dttry + prefix: X12SA-ES1-DETT:TRY1 + deviceTags: + - cSAXS + - detector table + onFailure: buffer + status: + enabled: true + enabled_set: false +dttrz: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Detector tower motion + deviceClass: EpicsMotor + deviceConfig: + name: dttrz + prefix: X12SA-ES1-DETT:TRZ1 + deviceTags: + - cSAXS + - detector table + onFailure: buffer + status: + enabled: true + enabled_set: false +ebtrx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Exposure box 2 horizontal movement + deviceClass: EpicsMotor + deviceConfig: + name: ebtrx + prefix: X12SA-ES1-EB:TRX1 + deviceTags: + - cSAXS + - xbox + onFailure: buffer + status: + enabled: true + enabled_set: false +ebtry: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Exposure box 2 vertical movement + deviceClass: EpicsMotor + deviceConfig: + name: ebtry + prefix: X12SA-ES1-EB:TRY1 + deviceTags: + - cSAXS + - xbox + onFailure: buffer + status: + enabled: true + enabled_set: false +ebtrz: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Exposure box 2 axial movement + deviceClass: EpicsMotor + deviceConfig: + name: ebtrz + prefix: X12SA-ES1-EB:TRZ1 + deviceTags: + - cSAXS + - xbox + onFailure: buffer + status: + enabled: true + enabled_set: false +eyecenx: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: X-ray eye intensit math + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: eyecenx + read_pv: XOMNYI-XEYE-XCEN:0 + deviceTags: + - cSAXS + - xeye + onFailure: buffer + status: + enabled: true + enabled_set: false +eyeceny: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: X-ray eye intensit math + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: eyeceny + read_pv: XOMNYI-XEYE-YCEN:0 + deviceTags: + - cSAXS + - xeye + onFailure: buffer + status: + enabled: true + enabled_set: false +eyefoc: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: X-ray eye focusing motor + deviceClass: EpicsMotor + deviceConfig: + name: eyefoc + prefix: X12SA-ES2-ES25 + deviceTags: + - cSAXS + - xeye + onFailure: buffer + status: + enabled: true + enabled_set: false +eyeint: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: X-ray eye intensit math + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: eyeint + read_pv: XOMNYI-XEYE-INT_MEAN:0 + deviceTags: + - cSAXS + - xeye + onFailure: buffer + status: + enabled: true + enabled_set: false +eyex: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: X-ray eye motion + deviceClass: EpicsMotor + deviceConfig: + name: eyex + prefix: X12SA-ES2-ES01 + deviceTags: + - cSAXS + - xeye + onFailure: buffer + status: + enabled: true + enabled_set: false +eyey: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: X-ray eye motion + deviceClass: EpicsMotor + deviceConfig: + name: eyey + prefix: X12SA-ES2-ES02 + deviceTags: + - cSAXS + - xeye + onFailure: buffer + status: + enabled: true + enabled_set: false +fal0: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Some scaler... + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: fal0 + read_pv: X12SA-ES1-SCALER.S4 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +fal1: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Some scaler... + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: fal1 + read_pv: X12SA-ES1-SCALER.S5 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +fal2: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Some scaler... + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: fal2 + read_pv: X12SA-ES1-SCALER.S6 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +fi1try: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch filter 1 movement + deviceClass: EpicsMotor + deviceConfig: + name: fi1try + prefix: X12SA-OP-FI1:TRY1 + deviceTags: + - cSAXS + - filter + onFailure: buffer + status: + enabled: true + enabled_set: false +fi2try: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch filter 2 movement + deviceClass: EpicsMotor + deviceConfig: + name: fi2try + prefix: X12SA-OP-FI2:TRY1 + deviceTags: + - cSAXS + - filter + onFailure: buffer + status: + enabled: true + enabled_set: false +fi3try: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch filter 3 movement + deviceClass: EpicsMotor + deviceConfig: + name: fi3try + prefix: X12SA-OP-FI3:TRY1 + deviceTags: + - cSAXS + - filter + onFailure: buffer + status: + enabled: true + enabled_set: false +ftp: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Flight tube pressure + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: ftp + read_pv: X12SA-ES1-FT1MT1:PRESSURE + deviceTags: + - cSAXS + - flight tube + onFailure: buffer + status: + enabled: true + enabled_set: false +fttrx1: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Dunno these motors??? + deviceClass: EpicsMotor + deviceConfig: + name: fttrx1 + prefix: X12SA-ES1-FTS1:TRX1 + deviceTags: + - cSAXS + - flight tube + onFailure: buffer + status: + enabled: true + enabled_set: false +fttrx2: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Dunno these motors??? + deviceClass: EpicsMotor + deviceConfig: + name: fttrx2 + prefix: X12SA-ES1-FTS2:TRX1 + deviceTags: + - cSAXS + - flight tube + onFailure: buffer + status: + enabled: true + enabled_set: false +fttry1: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Dunno these motors??? + deviceClass: EpicsMotor + deviceConfig: + name: fttry1 + prefix: X12SA-ES1-FTS1:TRY1 + deviceTags: + - cSAXS + - flight tube + onFailure: buffer + status: + enabled: true + enabled_set: false +fttry2: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Dunno these motors??? + deviceClass: EpicsMotor + deviceConfig: + name: fttry2 + prefix: X12SA-ES1-FTS2:TRY1 + deviceTags: + - cSAXS + - flight tube + onFailure: buffer + status: + enabled: true + enabled_set: false +fttrz: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Dunno these motors??? + deviceClass: EpicsMotor + deviceConfig: + name: fttrz + prefix: X12SA-ES1-FTS1:TRZ1 + deviceTags: + - cSAXS + - flight tube + onFailure: buffer + status: + enabled: true + enabled_set: false +idgap: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Undulator gap size [mm] + deviceClass: InsertionDevice + deviceConfig: + name: idgap + prefix: X12SA-ID + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +led: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Some scaler... + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: led + read_pv: X12SA-ES1-SCALER.S4 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +mibd1: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Mirror bender 1 + deviceClass: EpicsMotor + deviceConfig: + name: mibd1 + prefix: X12SA-OP-MI:TRZ1 + deviceTags: + - cSAXS + - mirror + onFailure: buffer + status: + enabled: true + enabled_set: false +mibd2: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Mirror bender 2 + deviceClass: EpicsMotor + deviceConfig: + name: mibd2 + prefix: X12SA-OP-MI:TRZ2 + deviceTags: + - cSAXS + - mirror + onFailure: buffer + status: + enabled: true + enabled_set: false +micfoc: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Microscope focusing motor + deviceClass: EpicsMotor + deviceConfig: + name: micfoc + prefix: X12SA-ES2-ES03 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +mitrx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Mirror horizontal movement + deviceClass: EpicsMotor + deviceConfig: + name: mitrx + prefix: X12SA-OP-MI:TRX1 + deviceTags: + - cSAXS + - mirror + onFailure: buffer + status: + enabled: true + enabled_set: false +mitry1: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Mirror vertical movement 1 + deviceClass: EpicsMotor + deviceConfig: + name: mitry1 + prefix: X12SA-OP-MI:TRY1 + deviceTags: + - cSAXS + - mirror + onFailure: buffer + status: + enabled: true + enabled_set: false +mitry2: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Mirror vertical movement 2 + deviceClass: EpicsMotor + deviceConfig: + name: mitry2 + prefix: X12SA-OP-MI:TRY2 + deviceTags: + - cSAXS + - mirror + onFailure: buffer + status: + enabled: true + enabled_set: false +mitry3: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Mirror vertical movement 3 + deviceClass: EpicsMotor + deviceConfig: + name: mitry3 + prefix: X12SA-OP-MI:TRY3 + deviceTags: + - cSAXS + - mirror + onFailure: buffer + status: + enabled: true + enabled_set: false +mobd: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator bender virtual motor + deviceClass: PmMonoBender + deviceConfig: + name: mobd + prefix: "X12SA-OP-MO:" + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +mobdai: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator bender inner motor + deviceClass: EpicsMotor + deviceConfig: + name: mobdai + prefix: X12SA-OP-MO:TRYA + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +mobdbo: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator bender outer motor + deviceClass: EpicsMotor + deviceConfig: + name: mobdbo + prefix: X12SA-OP-MO:TRYB + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +mobdco: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator bender outer motor + deviceClass: EpicsMotor + deviceConfig: + name: mobdco + prefix: X12SA-OP-MO:TRYC + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +mobddi: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator bender inner motor + deviceClass: EpicsMotor + deviceConfig: + name: mobddi + prefix: X12SA-OP-MO:TRYD + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +mokev: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Monochromator energy in keV + deviceClass: EnergyKev + deviceConfig: + auto_monitor: true + name: mokev + read_pv: X12SA-OP-MO:ROX2 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +mopush1: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 1 angle + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: mopush1 + read_pv: X12SA-OP-MO:ROX1 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +mopush2: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 2 angle + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: mopush2 + read_pv: X12SA-OP-MO:ROX2 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +moroll1: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 1 roll + deviceClass: EpicsMotor + deviceConfig: + name: moroll1 + prefix: X12SA-OP-MO:ROZ1 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +moroll2: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 2 roll movement + deviceClass: EpicsMotor + deviceConfig: + name: moroll2 + prefix: X12SA-OP-MO:ROZ2 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +moth1: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Monochromator Theta 1 + deviceClass: MonoTheta1 + deviceConfig: + auto_monitor: true + name: moth1 + read_pv: X12SA-OP-MO:ROX1 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +moth1e: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 1 theta encoder + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: moth1e + read_pv: X12SA-OP-MO:ECX1 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +moth2: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Monochromator Theta 2 + deviceClass: MonoTheta2 + deviceConfig: + auto_monitor: true + name: moth2 + read_pv: X12SA-OP-MO:ROX2 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +moth2e: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 2 theta encoder + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: moth2e + read_pv: X12SA-OP-MO:ECX2 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +motrx2: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 2 horizontal movement + deviceClass: EpicsMotor + deviceConfig: + name: motrx2 + prefix: X12SA-OP-MO:TRX2 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +motry: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch optical table vertical movement + deviceClass: EpicsMotor + deviceConfig: + name: motry + prefix: X12SA-OP-OT:TRY + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +motry2: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 2 vertical movement + deviceClass: EpicsMotor + deviceConfig: + name: motry2 + prefix: X12SA-OP-MO:TRY2 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +motrz1: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 1 axial movement + deviceClass: EpicsMotor + deviceConfig: + name: motrz1 + prefix: X12SA-OP-MO:TRZ1 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +motrz1e: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 1 axial movement encoder + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: motrz1e + read_pv: X12SA-OP-MO:ECZ1 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +moyaw2: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Monochromator crystal 2 yaw movement + deviceClass: EpicsMotor + deviceConfig: + name: moyaw2 + prefix: X12SA-OP-MO:ROY2 + deviceTags: + - cSAXS + - mono + onFailure: buffer + status: + enabled: true + enabled_set: false +samx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Sample motion + deviceClass: EpicsMotor + deviceConfig: + name: samx + prefix: X12SA-ES2-ES04 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +samy: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Sample motion + deviceClass: EpicsMotor + deviceConfig: + name: samy + prefix: X12SA-ES2-ES05 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sec: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Some scaler... + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: sec + read_pv: X12SA-ES1-SCALER.S1 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl0h: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: FrontEnd slit virtual movement + deviceClass: SlitH + deviceConfig: + name: sl0h + prefix: "X12SA-FE-SH1:" + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl0trxi: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: FrontEnd slit inner blade movement + deviceClass: EpicsMotor + deviceConfig: + name: sl0trxi + prefix: X12SA-FE-SH1:TRX1 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl0trxo: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: FrontEnd slit outer blade movement + deviceClass: EpicsMotor + deviceConfig: + name: sl0trxo + prefix: X12SA-FE-SH1:TRX2 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl1h: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit virtual movement + deviceClass: SlitH + deviceConfig: + name: sl1h + prefix: "X12SA-OP-SH1:" + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl1trxi: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit inner blade movement + deviceClass: EpicsMotor + deviceConfig: + name: sl1trxi + prefix: X12SA-OP-SH1:TRX2 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl1trxo: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit outer blade movement + deviceClass: EpicsMotor + deviceConfig: + name: sl1trxo + prefix: X12SA-OP-SH1:TRX1 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl1tryb: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit bottom blade movement + deviceClass: EpicsMotor + deviceConfig: + name: sl1tryb + prefix: X12SA-OP-SV1:TRY2 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl1tryt: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit top blade movement + deviceClass: EpicsMotor + deviceConfig: + name: sl1tryt + prefix: X12SA-OP-SV1:TRY1 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl1v: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit virtual movement + deviceClass: SlitV + deviceConfig: + name: sl1v + prefix: "X12SA-OP-SV1:" + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl2h: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit 2 virtual movement + deviceClass: SlitH + deviceConfig: + name: sl2h + prefix: "X12SA-OP-SH2:" + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl2trxi: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit 2 inner blade movement + deviceClass: EpicsMotor + deviceConfig: + name: sl2trxi + prefix: X12SA-OP-SH2:TRX2 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl2trxo: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit 2 outer blade movement + deviceClass: EpicsMotor + deviceConfig: + name: sl2trxo + prefix: X12SA-OP-SH2:TRX1 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl2tryb: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit 2 bottom blade movement + deviceClass: EpicsMotor + deviceConfig: + name: sl2tryb + prefix: X12SA-OP-SV2:TRY2 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl2tryt: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit 2 top blade movement + deviceClass: EpicsMotor + deviceConfig: + name: sl2tryt + prefix: X12SA-OP-SV2:TRY1 + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sl2v: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: OpticsHutch slit 2 virtual movement + deviceClass: SlitV + deviceConfig: + name: sl2v + prefix: "X12SA-OP-SV2:" + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +strox: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Girder virtual pitch + deviceClass: GirderMotorPITCH + deviceConfig: + name: strox + prefix: X12SA-HG + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +stroy: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Girder virtual yaw + deviceClass: GirderMotorYAW + deviceConfig: + name: stroy + prefix: X12SA-HG + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +stroz: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Girder virtual roll + deviceClass: GirderMotorROLL + deviceConfig: + name: stroz + prefix: X12SA-HG + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sttrx: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Girder X translation + deviceClass: GirderMotorX1 + deviceConfig: + name: sttrx + prefix: X12SA-HG + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +sttry: + acquisitionConfig: + acquisitionGroup: motor + readoutPriority: baseline + schedule: sync + description: Girder Y translation + deviceClass: GirderMotorY1 + deviceConfig: + name: sttry + prefix: X12SA-HG + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false +transd: + acquisitionConfig: + acquisitionGroup: monitor + readoutPriority: baseline + schedule: sync + description: Transmission diode + deviceClass: EpicsSignalRO + deviceConfig: + auto_monitor: true + name: transd + read_pv: X12SA-OP-BPM1:Current1:MeanValue_RBV + deviceTags: + - cSAXS + onFailure: buffer + status: + enabled: true + enabled_set: false