Compare commits

..

4 Commits

Author SHA1 Message Date
x10sa 72262436b3 Automatic backup triggered by new deployment
CI for pxii_bec / test (push) Successful in 27s
2026-03-19 11:01:58 +01:00
perl_d 64aecf5639 wip: working but status future errors 2026-03-05 13:14:34 +01:00
x10sa 5bd9c9ad6d Beamline staff changes Thu Mar 5 01:01:48 PM CET 2026 2026-03-05 13:01:49 +01:00
x10sa 0467e857df updates to PV names and collision protection
CI for pxii_bec / test (push) Successful in 30s
2026-02-19 12:04:50 +01:00
29 changed files with 2290 additions and 2182 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
# It is needed to track the repo template version, and editing may break things.
# This file will be overwritten by copier on template updates.
_commit: v1.2.8
_commit: v1.2.2
_src_path: https://github.com/bec-project/plugin_copier_template.git
make_commit: false
project_name: pxii_bec
+9 -14
View File
@@ -28,7 +28,7 @@ on:
description: "Python version to use"
required: false
type: string
default: "3.12"
default: "3.11"
permissions:
pull-requests: write
@@ -44,19 +44,7 @@ jobs:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "${{ inputs.PYTHON_VERSION || '3.12' }}"
- name: Checkout BEC Plugin Repository
uses: actions/checkout@v4
with:
repository: bec/pxii_bec
ref: "${{ inputs.BEC_PLUGIN_REPO_BRANCH || github.head_ref || github.sha }}"
path: ./pxii_bec
- name: Lint for merge conflicts from template updates
shell: bash
# Find all Copier conflicts except this line
run: '! grep -r "<<<<<<< before updating" | grep -v "grep -r \"<<<<<<< before updating"'
python-version: "${{ inputs.PYTHON_VERSION || '3.11' }}"
- name: Checkout BEC Core
uses: actions/checkout@v4
@@ -79,6 +67,13 @@ jobs:
ref: "${{ inputs.BEC_WIDGETS_BRANCH || 'main' }}"
path: ./bec_widgets
- name: Checkout BEC Plugin Repository
uses: actions/checkout@v4
with:
repository: bec/pxii_bec
ref: "${{ inputs.BEC_PLUGIN_REPO_BRANCH || github.head_ref || github.sha }}"
path: ./pxii_bec
- name: Install dependencies
shell: bash
run: |
-62
View File
@@ -1,62 +0,0 @@
name: Create template upgrade PR for pxii_bec
on:
workflow_dispatch:
permissions:
pull-requests: write
jobs:
create_update_branch_and_pr:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install tools
run: |
pip install copier PySide6
- name: Checkout
uses: actions/checkout@v4
- name: Perform update
run: |
git config --global user.email "bec_ci_staging@psi.ch"
git config --global user.name "BEC automated CI"
branch="chore/update-template-$(python -m uuid)"
echo "switching to branch $branch"
git checkout -b $branch
echo "Running copier update..."
output="$(copier update --trust --defaults --conflict inline 2>&1)"
echo "$output"
msg="$(printf '%s\n' "$output" | head -n 1)"
if ! grep -q "make_commit: true" .copier-answers.yml ; then
echo "Autocommit not made, committing..."
git add -A
git commit -a -m "$msg"
fi
if diff-index --quiet HEAD ; then
echo "No changes detected"
exit 0
fi
git push -u origin $branch
curl -X POST "https://gitea.psi.ch/api/v1/repos/${{ gitea.repository }}/pulls" \
-H "Authorization: token ${{ secrets.CI_REPO_WRITE }}" \
-H "Content-Type: application/json" \
-d "{
\"title\": \"Template: $(echo $msg)\",
\"body\": \"This PR was created by Gitea Actions\",
\"head\": \"$(echo $branch)\",
\"base\": \"main\"
}"
+7
View File
@@ -0,0 +1,7 @@
include:
- file: /templates/plugin-repo-template.yml
inputs:
name: pxii_bec
target: pxii_bec
branch: $CHILD_PIPELINE_BRANCH
project: bec/awi_utils
+1 -3
View File
@@ -5,7 +5,7 @@ from __future__ import annotations
from bec_lib.logger import bec_logger
from bec_widgets.cli.rpc.rpc_base import RPCBase, rpc_call, rpc_timeout
from bec_widgets.cli.rpc.rpc_base import RPCBase, rpc_call
logger = bec_logger.logger
@@ -18,8 +18,6 @@ _Widgets = {
class ScanHistory(RPCBase):
_IMPORT_MODULE = "pxii_bec.bec_widgets.widgets.scan_history.scan_history"
@rpc_call
def select_scan_from_history(self, value: "int") -> "None":
"""
@@ -1,13 +0,0 @@
# This file was automatically generated by generate_cli.py
# type: ignore
from __future__ import annotations
# pylint: skip-file
designer_plugins = {
"ScanHistory": ("pxii_bec.bec_widgets.widgets.scan_history.scan_history", "ScanHistory"),
}
widget_icons = {
"ScanHistory": "widgets",
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,169 @@
name,description,deviceClass,PV,readoutPriority,tag,readOnly,include,userParameter,
sls_current,SLS current,SignalRO,ARS07-DPCT-0100:CURR,monitored,SLS,yes,yes,,
fe_bpm1,FE XBPM Signal 1,SignalRO,X10SA-FE-XBPM1:Current1:MeanValue_RBV,monitored,bpm,yes,yes,,
fe_bpm2,FE XBPM Signal 2,SignalRO,X10SA-FE-XBPM1:Current2:MeanValue_RBV,monitored,bpm,yes,yes,,
fe_bpm3,FE XBPM Signal 3,SignalRO,X10SA-FE-XBPM1:Current3:MeanValue_RBV,monitored,bpm,yes,yes,,
fe_bpm4,FE XBPM Signal 4,SignalRO,X10SA-FE-XBPM1:Current4:MeanValue_RBV,monitored,bpm,yes,yes,,
fe_bpmsum,FE XBPM Summed,SignalRO,X10SA-FE-XBPM1:SumAll:MeanValue_RBV,monitored,bpm,yes,yes,,
fe_bpm_x,FE BPM X,Motor,X10SA-FE-XBPM1:TRX,baseline,fe,no,yes,,
fe_bpm_y,FE BPM Y,Motor,X10SA-FE-XBPM1:TRY,baseline,fe,no,yes,,
fe_sl_xr,FE Slit X Ring,Motor,X10SA-FE-SL1:TRXR,baseline,fe,no,yes,,
fe_sl_yt,FE Slit Y top,Motor,X10SA-FE-SL1:TRYT,baseline,fe,no,yes,,
fe_sl_xw,FE Slit X Wall,Motor,X10SA-FE-SL1:TRXW,baseline,fe,no,yes,,
fe_sl_yb,FE SlitY Bottom,Motor,X10SA-FE-SL1:TRYB,baseline,fe,no,yes,,
fe_sl_xcen,FE Slit X Centre,Motor,X10SA-FE-SL1:CENTERX,baseline,fe,no,yes,,
fe_sl_xsize,FE Slit X Size,Motor,X10SA-FE-SL1:SIZEX,baseline,fe,no,yes,,
fe_sl_ycen,FE Slit Y Centre,Motor,X10SA-FE-SL1:CENTERY,baseline,fe,no,yes,,
fe_sl_ysize,FE Slit Y Size,Motor,X10SA-FE-SL1:SIZEY,baseline,fe,no,yes,,
bsf_bpm1,BSF BPM Signal 1,SignalRO,X10SA-OP-BSFBPM:SIGNAL1,monitored,bpm,yes,no,,
bsf_bpm2,BSF BPM Signal 2,SignalRO,X10SA-OP-BSFBPM:SIGNAL2,monitored,bpm,yes,no,,
bsf_bpm3,BSF BPM Signal 3,SignalRO,X10SA-OP-BSFBPM:SIGNAL3,monitored,bpm,yes,no,,
bsf_bpm4,BSF BPM Signal 4,SignalRO,X10SA-OP-BSFBPM:SIGNAL4,monitored,bpm,yes,no,,
bsf_bpmsum,BSF BPM Summed,SignalRO,X10SA-OP-BSFBPM:SUM,monitored,bpm,yes,no,,
bsf_sl_xw,BSF slit outboard,Motor,X10SA-OP-BSFSLH:TRXW,baseline,bsf,no,yes,,
bsf_sl_xr,BSF slit inboard,Motor,X10SA-OP-BSFSLH:TRXR,baseline,bsf,no,yes,,
bsf_sl_yt,BSF slit top,Motor,X10SA-OP-BSFSLV:TRYT,baseline,bsf,no,yes,,
bsf_sl_yb,BSF slit bottom,Motor,X10SA-OP-BSFSLV:TRYB,baseline,bsf,no,yes,,
bsf_sl_xcen,BSF X centre,Motor,X10SA-OP-BSFSLH:CENTER,baseline,bsf,no,yes,,
bsf_sl_xsize,BSF X size,Motor,X10SA-OP-BSFSLH:SIZE,baseline,bsf,no,yes,,
bsf_sl_ycen,BSF Y centre,Motor,X10SA-OP-BSFSLV:CENTER,baseline,bsf,no,yes,,
bsf_sl_ysize,BSF Y size,Motor,X10SA-OP-BSFSLV:SIZE,baseline,bsf,no,yes,,
bsf_f1_y,BSF Filter 1 Y,Motor,X10SA-OP-BSFFI1:TRY,baseline,bsf,no,yes,,
bsf_f2_y,BSF Filter 2 Y,Motor,X10SA-OP-BSFFI2:TRY,baseline,bsf,no,yes,,
dcm_bragg,DCM Bragg angle,Motor,X10SA-OP-DCM:ROTY,baseline,dcm,no,yes,,
dcm_x,DCM lateral,Motor,X10SA-OP-DCM:TRX,baseline,dcm,no,yes,,
dcm_perp,DCM Perp,Motor,X10SA-OP-DCM:TRX-CR2,baseline,dcm,no,yes,,
dcm_pitch,DCM 2nd crystal pitch,Motor,X10SA-OP-DCM:ROTY-CR2-PITCH,baseline,dcm,no,yes,,
dcm_fpitch,DCM 2nd crystal fine pitch,Motor,X10SA-OP-DCM:ROTY-CR2-FINEPITCH,baseline,dcm,no,yes,,
dcm_froll,DCM 2nd crystal fine roll,Motor,X10SA-OP-DCM:ROTZ-CR2-FINEROLL,baseline,dcm,no,yes,,
lu_bpm1,LU BPM Signal 1,SignalRO,X10SA-OP-LUBPM:Current1:MeanValue_RBV,monitored,bpm,yes,yes,,
lu_bpm2,LU BPM Signal 2,SignalRO,X10SA-OP-LUBPM:Current2:MeanValue_RBV,monitored,bpm,yes,yes,,
lu_bpm3,LU BPM Signal 3,SignalRO,X10SA-OP-LUBPM:Current3:MeanValue_RBV,monitored,bpm,yes,yes,,
lu_bpm4,LU BPM Signal 4,SignalRO,X10SA-OP-LUBPM:Current4:MeanValue_RBV,monitored,bpm,yes,yes,,
lu_bpmsum,LU BPM Summed,SignalRO,X10SA-OP-LUBPM:SumAll:MeanValue_RBV,monitored,bpm,yes,yes,,
lu_bpm_x,BPM2 X translation,Motor,X10SA-OP-LUBPM:TRX,baseline,lu,no,yes,,
lu_bpm_y,BPM2 Y translation,Motor,X10SA-OP-LUBPM:TRY,baseline,lu,no,yes,,
lu_z1,Lens Z1 Motion,Motor,X10SA-OP-LUTRZ1:TRZ,baseline,lu,no,yes,,
lu_z2,Lens Z2 Motion,Motor,X10SA-OP-LUTRZ2:TRZ,baseline,lu,no,yes,,
lu_pod1_x,SmarPod1 X,Motor,X10SA-OP-LUPOD1:TRX1,baseline,lu,no,no,,
lu_lens1_x2,Lenses1 X,Motor,X10SA-OP-LUPOD1:TRX2,baseline,lu,no,no,,
lu_pod1_y,SmarPod1 Y,Motor,X10SA-OP-LUPOD1:TRY,baseline,lu,no,yes,,
lu_pod1_z,SmarPod1 Z,Motor,X10SA-OP-LUPOD1:TRZ,baseline,lu,no,yes,,
lu_pod1_rotx,SmarPod1 RX,Motor,X10SA-OP-LUPOD1:ROTX,baseline,lu,no,yes,,
lu_pod1_roty,SmarPod1 RY,Motor,X10SA-OP-LUPOD1:ROTY,baseline,lu,no,yes,,
lu_pod1_rotz,SmarPod1 RZ,Motor,X10SA-OP-LUPOD1:ROTZ,baseline,lu,no,yes,,
lu_pod2_x,SmarPod2 X,Motor,X10SA-OP-LUPOD2:TRX1,baseline,lu,no,no,,
lu_lens2_x2,Lenses2 X,Motor,X10SA-OP-LUPOD2:TRX2,baseline,lu,no,no,,
lu_pod2_y,SmarPod2 Y,Motor,X10SA-OP-LUPOD2:TRY,baseline,lu,no,yes,,
lu_pod2_z,SmarPod2 Z,Motor,X10SA-OP-LUPOD2:TRZ,baseline,lu,no,yes,,
lu_pod2_rotx,SmarPod2 RX,Motor,X10SA-OP-LUPOD2:ROTX,baseline,lu,no,yes,,
lu_pod2_roty,SmarPod2 RY,Motor,X10SA-OP-LUPOD2:ROTY,baseline,lu,no,yes,,
lu_pod2_rotz,SmarPod2 RZ,Motor,X10SA-OP-LUPOD2:ROTZ,baseline,lu,no,yes,,
ss_bpm1,SS BPM Signal 1,SignalRO,X10SA-ES-SSBPM:Current1:MeanValue_RBV,monitored,bpm,yes,yes,,
ss_bpm2,SS BPM Signal 2,SignalRO,X10SA-ES-SSBPM:Current2:MeanValue_RBV,monitored,bpm,yes,yes,,
ss_bpm3,SS BPM Signal 3,SignalRO,X10SA-ES-SSBPM:Current3:MeanValue_RBV,monitored,bpm,yes,yes,,
ss_bpm4,SS BPM Signal 4,SignalRO,X10SA-ES-SSBPM:Current4:MeanValue_RBV,monitored,bpm,yes,yes,,
ss_bpmsum,SS BPM Summed,SignalRO,X10SA-ES-SSBPM:SumAll:MeanValue_RBV,monitored,bpm,yes,yes,,
ss_bpm_x,SS BPM X,Motor,X10SA-ES-SSBPM:TRX,baseline,ss,no,yes,,
ss_bpm_y,SS BPM Y,Motor,X10SA-ES-SSBPM:TRY,baseline,ss,no,yes,,
ss_f1_x,SS Filter 1 X,Motor,X10SA-ES-SSFI1:TRX,baseline,ss,no,yes,,
ss_f2_x,SS Filter 2 X,Motor,X10SA-ES-SSFI2:TRX,baseline,ss,no,yes,,
ss_f3_x,SS Filter 2 X,Motor,X10SA-ES-SSFI3:TRX,baseline,ss,no,yes,,
ss_f4_x,SS Filter 4 X,Motor,X10SA-ES-SSFI4:TRX,baseline,ss,no,yes,,
ss_sl_xw,SS slit wall,Motor,X10SA-ES-SSSLH:TRXW,baseline,ss,no,yes,,
ss_sl_xr,SS slit ring,Motor,X10SA-ES-SSSLH:TRXR,baseline,ss,no,yes,,
ss_sl_xcen,SS slit X centre,Motor,X10SA-ES-SSSLH:CENTER,baseline,ss,no,yes,,
ss_sl_xsize,SS slit X size,Motor,X10SA-ES-SSSLH:SIZE,baseline,ss,no,yes,,
ss_sl_yt,SS slit top,Motor,X10SA-ES-SSSLV:TRYT,baseline,ss,no,yes,,
ss_sl_yb,SS slit bottom,Motor,X10SA-ES-SSSLV:TRYB,baseline,ss,no,yes,,
ss_sl_ycen,SS slit Y centre,Motor,X10SA-ES-SSSLV:CENTER,baseline,ss,no,yes,,
ss_sl_ysize,SS slit Y size,Motor,X10SA-ES-SSSLV:SIZE,baseline,ss,no,yes,,
ss_xi_x,SS X-ray eye X,Motor,X10SA-ES-SSXI:TRX,baseline,ss,no,yes,"{""type"": multi-position,""in"": 7.5, ""out"": -2.1}",
ss_xi_y,SS X-ray eye Y,Motor,X10SA-ES-SSXI:TRY,baseline,ss,no,yes,,
ss_xicam_x,ss cam X,SignalRO,X10SA-ES-SSXI:cam1:Stats5:CentroidX_RBV,baseline,ss,yes,yes,,
ss_xicam_y,ss cam Y,SignalRO,X10SA-ES-SSXI:cam1:Stats5:CentroidY_RBV,baseline,ss,yes,yes,,
ss_xicam_max,ss cam max value,SignalRO,X10SA-ES-SSXI:cam1:Stats5:MaxValue_RBV,monitored,ss,yes,yes,,
ss_xicam_exp,ss camera exposure,Signal,X10SA-ES-SSXI:cam1:AcquireTime,baseline,ss,no,yes,,
ss_xicam_gain,ss camera gain,Signal,X10SA-ES-SSXI:cam1:cam1:Gain,baseline,ss,no,yes,,
ss_xicam_xsig,ss camera x sigma,Signal,X10SA-ES-SSXI:cam1:Stats5:SigmaX_RBV,baseline,ss,yes,yes,,
ss_xicam_ysig,ss camera y sigma,Signal,X10SA-ES-SSXI:cam1:Stats5:SigmaY_RBV,baseline,ss,yes,yes,,
vfm_xu,VFM Upstream X,Motor,X10SA-ES-KBV:TRXU,baseline,vfm,no,no,,
vfm_xd,VFM Downstream X,Motor,X10SA-ES-KBV:TRXD,baseline,vfm,no,no,,
vfm_yur,VFM Upstream Ring Y,Motor,X10SA-ES-KBV:TRYUR,baseline,vfm,no,no,,
vfm_yw,VFM Wall Y,Motor,X10SA-ES-KBV:TRYW,baseline,vfm,no,no,,
vfm_ydr,VFM Downstream Ring Y,Motor,X10SA-ES-KBV:TRYDR,baseline,vfm,no,no,,
vfm_bu,VFM Upstream Bender,Motor,X10SA-ES-KBV:BNDU,baseline,vfm,no,no,,
vfm_bd,VFM Downstream Bender,Motor,X10SA-ES-KBV:BNDD,baseline,vfm,no,no,,
vfm_yaw,VFM Virtual Yaw,Motor,X10SA-ES-KBV:YAW,baseline,vfm,no,no,,
vfm_roll,VFM Virtual Roll,Motor,X10SA-ES-KBV:ROLL,baseline,vfm,no,no,,
vfm_pitch,VFM Virtual Pitch,Motor,X10SA-ES-KBV:PITCH,baseline,vfm,no,no,,
vfm_x,VFM Virtual X,Motor,X10SA-ES-KBV:TRX,baseline,vfm,no,no,,
vfm_y,VFM Virtual Y ,Motor,X10SA-ES-KBV:TRY,baseline,vfm,no,no,,
hfm_xu,HFM Upstream X,Motor,X10SA-ES-KBH:TRXU,baseline,hfm,no,no,,
hfm_xd,HFM Downstream X,Motor,X10SA-ES-KBH:TRXD,baseline,hfm,no,no,,
hfm_yuw,HFM Upstream Wall Y,Motor,X10SA-ES-KBH:TRYUW,baseline,hfm,no,no,,
hfm_yr,HFM Ring Y,Motor,X10SA-ES-KBH:TRYR,baseline,hfm,no,no,,
hfm_ydw,HFM Downstream Wall Y,Motor,X10SA-ES-KBH:TRYDW,baseline,hfm,no,no,,
hfm_bu,HFM Upstream Bender,Motor,X10SA-ES-KBH:BNDU,baseline,hfm,no,no,,
hfm_bd,HFM Downstream Bender,Motor,X10SA-ES-KBH:BNDD,baseline,hfm,no,no,,
hfm_yaw,HFM Virtual Yaw,Motor,X10SA-ES-KBH:YAW,baseline,hfm,no,no,,
hfm_roll,HFM Virtual Roll,Motor,X10SA-ES-KBH:ROLL,baseline,hfm,no,no,,
hfm_pitch,HFM Virtual Pitch,Motor,X10SA-ES-KBH:PITCH,baseline,hfm,no,no,,
hfm_x,HFM Virtual X,Motor,X10SA-ES-KBH:TRX,baseline,hfm,no,no,,
hfm_y,HFM Virtual Y ,Motor,X10SA-ES-KBH:TRY,baseline,hfm,no,no,,
bcu_bpm1,BCU BPM Signal 1 ,SignalRO,X10SA-ES-BCBPM:Current1:MeanValue_RBV,monitored,bpm,yes,yes,,
bcu_bpm2,BCU BPM Signal 2,SignalRO,X10SA-ES-BCBPM:Current2:MeanValue_RBV,monitored,bpm,yes,yes,,
bcu_bpm3,BCU BPM Signal 3,SignalRO,X10SA-ES-BCBPM:Current3:MeanValue_RBV,monitored,bpm,yes,yes,,
bcu_bpm4,BCU BPM Signal 4,SignalRO,X10SA-ES-BCBPM:Current4:MeanValue_RBV,monitored,bpm,yes,yes,,
bcu_bpmsum,BCU BPM Summed,SignalRO,X10SA-ES-BCBPM:SumAll:MeanValue_RBV,monitored,bpm,yes,yes,,
bcu_bpm_x,BCU BPM X,Motor,X10SA-ES-BCBPM:TRX,baseline,bcu,no,yes,,
bcu_bpm_y,BCU BPM Y ,Motor,X10SA-ES-BCBPM:TRY,baseline,bcu,no,yes,,
bcu_sl_xw,BCU slit wall,Motor,X10SA-ES-BCSLH:TRXW,baseline,bcu,no,no,,
bcu_sl_xr,BCU slit ring,Motor,X10SA-ES-BCSLH:TRXR,baseline,bcu,no,no,,
bcu_sl_xcen,BCU slit X centre,Motor,X10SA-ES-BCSLH:CENTER,baseline,bcu,no,no,,
bcu_sl_xsize,BCU slit X size,Motor,X10SA-ES-BCSLH:SIZEX,baseline,bcu,no,no,,
bcu_sl_yt,BCU slit top,Motor,X10SA-ES-BCSLV:TRYT,baseline,bcu,no,no,,
bcu_sl_yb,BCU slit bottom,Motor,X10SA-ES-BCSLV:TRYB,baseline,bcu,no,no,,
bcu_sl_ycen,BCU slit Y centre,Motor,X10SA-ES-BCSLV:CENTER,baseline,bcu,no,no,,
bcu_sl_ysize,BCU slit Y size,Motor,X10SA-ES-BCSLV:SIZE,baseline,bcu,no,no,,
xrf_pos,XRF det in/out,Signal,X10SA-ES-XRF:POS-SET,baseline,se,no,yes,"{""type"":positioner}",
samcam_x,sample cam X ,SignalRO,X10SA-ES-MS:Stats5:CentroidX_RBV,baseline,scam,yes,yes,,
samcam_xsig,sample cam X sigma,SignalRO,X10SA-ES-MS:Stats5:SigmaX_RBV,monitored,scam,yes,yes,,
samcam_y,sample cam Y ,SignalRO,X10SA-ES-MS:Stats5:CentroidY_RBV,baseline,scam,yes,yes,,
samcam_ysig,sample cam Y sigma,SignalRO,X10SA-ES-MS:Stats5:SigmaY_RBV,monitored,scam,yes,yes,,
samcam_max,sample cam max value,SignalRO,X10SA-ES-MS:Stats5:MaxValue_RBV,monitored,scam,yes,yes,,
samcam_exp,sample cam exp time,Signal,X10SA-ES-MS:cam1:AcquireTime,baseline,scam,no,yes,,
samcam_gain,sample cam gain,Signal,X10SA-ES-MS:cam1:Gain,baseline,scam,no,yes,,
scam_zoom,Sample cam zoom,Motor,X10SA-ES-MS:ZOOM,baseline,scam,no,yes,,
fl_bright,Frontlight brightness,Signal,X10SA-ES-FL:SET,baseline,se,no,yes,,
coll_x,Collimator X,Motor,X10SA-ES-COL:TRX,baseline,se,no,yes,,
coll_y,Collimator Y,Motor,X10SA-ES-COL:TRY,baseline,se,no,yes,"{""type"": multi-position, ""in"": 41.5, ""out"": 20.0, ""park"": 0,""tol"":0.05}",
diag_y,Scintillator/diode Y,Motor,X10SA-ES-SCL:TRY,baseline,se,no,yes,"{""type"": multi-position, ""scint"": 38.62, ""i1"": 44.0, ""out"": 20.0,""park"": 0,""tol"":0.3}",
diag_z,Scintillator/diode Z,Motor,X10SA-ES-SCL:TRZ,baseline,se,no,yes,,
i1,i1 diode reading,SignalRO,X10SA-ES-SCLDI:READOUT,monitored,bpm,yes,yes,,
bl_pos,Backlight positioner,Signal,X10SA-ES-BL:POS-SET,baseline,se,no,yes,"{""type"":positioner}",
bl_bright,Backlight brightness,Signal,X10SA-ES-BL:SET,baseline,se,no,yes,,
bs_x,Beamstop X,Motor,X10SA-ES-BS:TRX,baseline,se,no,yes,,
bs_y,Beamstop Y,Motor,X10SA-ES-BS:TRY,baseline,se,no,yes,,
bs_z,Beamstop Z,Motor,X10SA-ES-BS:TRZ,baseline,se,no,yes,"{""type"": guarded, ""min"": 13, ""samp"": 15, ""work_min"": 20, ""safe"": 41, ""max_blin"": 42, ""max_blout"": 70}",
bs_pos,Beamstop positioner,Signal,X10SA-ES-BS:POS-SET,baseline,se,no,yes,"{""type"":positioner}",
gon_x,Goniometer X,Motor,X10SA-ES-DF1:TRX1,baseline,det,no,yes,"{""type"": guarded, ""in"": 18.0, ""out"": -10.0, ""safe"": -100,""tol"":0.5}",
gon_y,Goniometer Y,Motor,X10SA-ES-DF1:TRY1,baseline,det,no,yes,,
gon_z,Goniometer X,Motor,X10SA-ES-DF1:TRZ1,baseline,det,no,yes,,
omega,Omega,Motor,X10SA-ES-DF1:ROTU,baseline,det,no,yes,,
cryo_pos,Cryo positioner,Signal,X10SA-ES-CS:POS-SET,baseline,se,no,yes,"{""type"":positioner}",
cryo_x,Cryojet X ,Motor,X10SA-ES-CS:TRX,baseline,se,no,yes,,
det_xi_focus,X-ray eye 2 Focus,Motor,X10SA-ES-XEYE:FOCUS,baseline,det,no,yes,,
det_xi_zoom,X-ray eye 2 Zoom,Motor,X10SA-ES-XEYE:ZOOM,baseline,det,no,yes,,
det_xi_x,X-ray eye X,Motor,X10SA-ES-XEYE:TRX,baseline,det,no,yes,,
i2,i2,SignalRO,X10SA-ES-XEYEDI:READOUT,monitored,bpm,yes,yes,,
det_xicam_x,sample cam X ,SignalRO,X10SA-ES-XEYE:cam1:Stats5:CentroidX_RBV,baseline,scam,yes,no,,
det_xicam_xsig,sample cam X sigma,SignalRO,X10SA-ES-XEYE:cam1:Stats5:SigmaX_RBV,monitored,scam,yes,no,,
det_xicam_y,sample cam Y ,SignalRO,X10SA-ES-XEYE:cam1:Stats5:CentroidY_RBV,baseline,scam,yes,no,,
det_xicam_ysig,sample cam Y sigma,SignalRO,X10SA-ES-XEYE:cam1:Stats5:SigmaY_RBV,monitored,scam,yes,no,,
det_xicam_max,sample cam max value,SignalRO,X10SA-ES-XEYE:cam1:Stats5:MaxValue_RBV,monitored,scam,yes,no,,
det_xicam_exp,sample cam exp time,Signal,X10SA-ES-XEYE:cam1:cam1:AcquireTime,baseline,scam,no,no,,
det_xicam_gain,sample cam gain,Signal,X10SA-ES-XEYE:cam1:cam1:Gain,baseline,scam,no,no,,
det_cov,Detector cover,Signal,X10SA-ES-DETCOV:SET,baseline,det,no,yes,"{""type"":positioner}",
det_y,Detector Y,Motor,X10SA-ES-DET:TRY,baseline,det,no,yes,,
det_z,Detector Z,Motor,X10SA-ES-DET:TRZ,baseline,det,no,yes,,
1 name description deviceClass PV readoutPriority tag readOnly include userParameter
2 sls_current SLS current SignalRO ARS07-DPCT-0100:CURR monitored SLS yes yes
3 fe_bpm1 FE XBPM Signal 1 SignalRO X10SA-FE-XBPM1:Current1:MeanValue_RBV monitored bpm yes yes
4 fe_bpm2 FE XBPM Signal 2 SignalRO X10SA-FE-XBPM1:Current2:MeanValue_RBV monitored bpm yes yes
5 fe_bpm3 FE XBPM Signal 3 SignalRO X10SA-FE-XBPM1:Current3:MeanValue_RBV monitored bpm yes yes
6 fe_bpm4 FE XBPM Signal 4 SignalRO X10SA-FE-XBPM1:Current4:MeanValue_RBV monitored bpm yes yes
7 fe_bpmsum FE XBPM Summed SignalRO X10SA-FE-XBPM1:SumAll:MeanValue_RBV monitored bpm yes yes
8 fe_bpm_x FE BPM X Motor X10SA-FE-XBPM1:TRX baseline fe no yes
9 fe_bpm_y FE BPM Y Motor X10SA-FE-XBPM1:TRY baseline fe no yes
10 fe_sl_xr FE Slit X Ring Motor X10SA-FE-SL1:TRXR baseline fe no yes
11 fe_sl_yt FE Slit Y top Motor X10SA-FE-SL1:TRYT baseline fe no yes
12 fe_sl_xw FE Slit X Wall Motor X10SA-FE-SL1:TRXW baseline fe no yes
13 fe_sl_yb FE SlitY Bottom Motor X10SA-FE-SL1:TRYB baseline fe no yes
14 fe_sl_xcen FE Slit X Centre Motor X10SA-FE-SL1:CENTERX baseline fe no yes
15 fe_sl_xsize FE Slit X Size Motor X10SA-FE-SL1:SIZEX baseline fe no yes
16 fe_sl_ycen FE Slit Y Centre Motor X10SA-FE-SL1:CENTERY baseline fe no yes
17 fe_sl_ysize FE Slit Y Size Motor X10SA-FE-SL1:SIZEY baseline fe no yes
18 bsf_bpm1 BSF BPM Signal 1 SignalRO X10SA-OP-BSFBPM:SIGNAL1 monitored bpm yes no
19 bsf_bpm2 BSF BPM Signal 2 SignalRO X10SA-OP-BSFBPM:SIGNAL2 monitored bpm yes no
20 bsf_bpm3 BSF BPM Signal 3 SignalRO X10SA-OP-BSFBPM:SIGNAL3 monitored bpm yes no
21 bsf_bpm4 BSF BPM Signal 4 SignalRO X10SA-OP-BSFBPM:SIGNAL4 monitored bpm yes no
22 bsf_bpmsum BSF BPM Summed SignalRO X10SA-OP-BSFBPM:SUM monitored bpm yes no
23 bsf_sl_xw BSF slit outboard Motor X10SA-OP-BSFSLH:TRXW baseline bsf no yes
24 bsf_sl_xr BSF slit inboard Motor X10SA-OP-BSFSLH:TRXR baseline bsf no yes
25 bsf_sl_yt BSF slit top Motor X10SA-OP-BSFSLV:TRYT baseline bsf no yes
26 bsf_sl_yb BSF slit bottom Motor X10SA-OP-BSFSLV:TRYB baseline bsf no yes
27 bsf_sl_xcen BSF X centre Motor X10SA-OP-BSFSLH:CENTER baseline bsf no yes
28 bsf_sl_xsize BSF X size Motor X10SA-OP-BSFSLH:SIZE baseline bsf no yes
29 bsf_sl_ycen BSF Y centre Motor X10SA-OP-BSFSLV:CENTER baseline bsf no yes
30 bsf_sl_ysize BSF Y size Motor X10SA-OP-BSFSLV:SIZE baseline bsf no yes
31 bsf_f1_y BSF Filter 1 Y Motor X10SA-OP-BSFFI1:TRY baseline bsf no yes
32 bsf_f2_y BSF Filter 2 Y Motor X10SA-OP-BSFFI2:TRY baseline bsf no yes
33 dcm_bragg DCM Bragg angle Motor X10SA-OP-DCM:ROTY baseline dcm no yes
34 dcm_x DCM lateral Motor X10SA-OP-DCM:TRX baseline dcm no yes
35 dcm_perp DCM Perp Motor X10SA-OP-DCM:TRX-CR2 baseline dcm no yes
36 dcm_pitch DCM 2nd crystal pitch Motor X10SA-OP-DCM:ROTY-CR2-PITCH baseline dcm no yes
37 dcm_fpitch DCM 2nd crystal fine pitch Motor X10SA-OP-DCM:ROTY-CR2-FINEPITCH baseline dcm no yes
38 dcm_froll DCM 2nd crystal fine roll Motor X10SA-OP-DCM:ROTZ-CR2-FINEROLL baseline dcm no yes
39 lu_bpm1 LU BPM Signal 1 SignalRO X10SA-OP-LUBPM:Current1:MeanValue_RBV monitored bpm yes yes
40 lu_bpm2 LU BPM Signal 2 SignalRO X10SA-OP-LUBPM:Current2:MeanValue_RBV monitored bpm yes yes
41 lu_bpm3 LU BPM Signal 3 SignalRO X10SA-OP-LUBPM:Current3:MeanValue_RBV monitored bpm yes yes
42 lu_bpm4 LU BPM Signal 4 SignalRO X10SA-OP-LUBPM:Current4:MeanValue_RBV monitored bpm yes yes
43 lu_bpmsum LU BPM Summed SignalRO X10SA-OP-LUBPM:SumAll:MeanValue_RBV monitored bpm yes yes
44 lu_bpm_x BPM2 X translation Motor X10SA-OP-LUBPM:TRX baseline lu no yes
45 lu_bpm_y BPM2 Y translation Motor X10SA-OP-LUBPM:TRY baseline lu no yes
46 lu_z1 Lens Z1 Motion Motor X10SA-OP-LUTRZ1:TRZ baseline lu no yes
47 lu_z2 Lens Z2 Motion Motor X10SA-OP-LUTRZ2:TRZ baseline lu no yes
48 lu_pod1_x SmarPod1 X Motor X10SA-OP-LUPOD1:TRX1 baseline lu no no
49 lu_lens1_x2 Lenses1 X Motor X10SA-OP-LUPOD1:TRX2 baseline lu no no
50 lu_pod1_y SmarPod1 Y Motor X10SA-OP-LUPOD1:TRY baseline lu no yes
51 lu_pod1_z SmarPod1 Z Motor X10SA-OP-LUPOD1:TRZ baseline lu no yes
52 lu_pod1_rotx SmarPod1 RX Motor X10SA-OP-LUPOD1:ROTX baseline lu no yes
53 lu_pod1_roty SmarPod1 RY Motor X10SA-OP-LUPOD1:ROTY baseline lu no yes
54 lu_pod1_rotz SmarPod1 RZ Motor X10SA-OP-LUPOD1:ROTZ baseline lu no yes
55 lu_pod2_x SmarPod2 X Motor X10SA-OP-LUPOD2:TRX1 baseline lu no no
56 lu_lens2_x2 Lenses2 X Motor X10SA-OP-LUPOD2:TRX2 baseline lu no no
57 lu_pod2_y SmarPod2 Y Motor X10SA-OP-LUPOD2:TRY baseline lu no yes
58 lu_pod2_z SmarPod2 Z Motor X10SA-OP-LUPOD2:TRZ baseline lu no yes
59 lu_pod2_rotx SmarPod2 RX Motor X10SA-OP-LUPOD2:ROTX baseline lu no yes
60 lu_pod2_roty SmarPod2 RY Motor X10SA-OP-LUPOD2:ROTY baseline lu no yes
61 lu_pod2_rotz SmarPod2 RZ Motor X10SA-OP-LUPOD2:ROTZ baseline lu no yes
62 ss_bpm1 SS BPM Signal 1 SignalRO X10SA-ES-SSBPM:Current1:MeanValue_RBV monitored bpm yes yes
63 ss_bpm2 SS BPM Signal 2 SignalRO X10SA-ES-SSBPM:Current2:MeanValue_RBV monitored bpm yes yes
64 ss_bpm3 SS BPM Signal 3 SignalRO X10SA-ES-SSBPM:Current3:MeanValue_RBV monitored bpm yes yes
65 ss_bpm4 SS BPM Signal 4 SignalRO X10SA-ES-SSBPM:Current4:MeanValue_RBV monitored bpm yes yes
66 ss_bpmsum SS BPM Summed SignalRO X10SA-ES-SSBPM:SumAll:MeanValue_RBV monitored bpm yes yes
67 ss_bpm_x SS BPM X Motor X10SA-ES-SSBPM:TRX baseline ss no yes
68 ss_bpm_y SS BPM Y Motor X10SA-ES-SSBPM:TRY baseline ss no yes
69 ss_f1_x SS Filter 1 X Motor X10SA-ES-SSFI1:TRX baseline ss no yes
70 ss_f2_x SS Filter 2 X Motor X10SA-ES-SSFI2:TRX baseline ss no yes
71 ss_f3_x SS Filter 2 X Motor X10SA-ES-SSFI3:TRX baseline ss no yes
72 ss_f4_x SS Filter 4 X Motor X10SA-ES-SSFI4:TRX baseline ss no yes
73 ss_sl_xw SS slit wall Motor X10SA-ES-SSSLH:TRXW baseline ss no yes
74 ss_sl_xr SS slit ring Motor X10SA-ES-SSSLH:TRXR baseline ss no yes
75 ss_sl_xcen SS slit X centre Motor X10SA-ES-SSSLH:CENTER baseline ss no yes
76 ss_sl_xsize SS slit X size Motor X10SA-ES-SSSLH:SIZE baseline ss no yes
77 ss_sl_yt SS slit top Motor X10SA-ES-SSSLV:TRYT baseline ss no yes
78 ss_sl_yb SS slit bottom Motor X10SA-ES-SSSLV:TRYB baseline ss no yes
79 ss_sl_ycen SS slit Y centre Motor X10SA-ES-SSSLV:CENTER baseline ss no yes
80 ss_sl_ysize SS slit Y size Motor X10SA-ES-SSSLV:SIZE baseline ss no yes
81 ss_xi_x SS X-ray eye X Motor X10SA-ES-SSXI:TRX baseline ss no yes {"type": multi-position,"in": 7.5, "out": -2.1}
82 ss_xi_y SS X-ray eye Y Motor X10SA-ES-SSXI:TRY baseline ss no yes
83 ss_xicam_x ss cam X SignalRO X10SA-ES-SSXI:cam1:Stats5:CentroidX_RBV baseline ss yes yes
84 ss_xicam_y ss cam Y SignalRO X10SA-ES-SSXI:cam1:Stats5:CentroidY_RBV baseline ss yes yes
85 ss_xicam_max ss cam max value SignalRO X10SA-ES-SSXI:cam1:Stats5:MaxValue_RBV monitored ss yes yes
86 ss_xicam_exp ss camera exposure Signal X10SA-ES-SSXI:cam1:AcquireTime baseline ss no yes
87 ss_xicam_gain ss camera gain Signal X10SA-ES-SSXI:cam1:cam1:Gain baseline ss no yes
88 ss_xicam_xsig ss camera x sigma Signal X10SA-ES-SSXI:cam1:Stats5:SigmaX_RBV baseline ss yes yes
89 ss_xicam_ysig ss camera y sigma Signal X10SA-ES-SSXI:cam1:Stats5:SigmaY_RBV baseline ss yes yes
90 vfm_xu VFM Upstream X Motor X10SA-ES-KBV:TRXU baseline vfm no no
91 vfm_xd VFM Downstream X Motor X10SA-ES-KBV:TRXD baseline vfm no no
92 vfm_yur VFM Upstream Ring Y Motor X10SA-ES-KBV:TRYUR baseline vfm no no
93 vfm_yw VFM Wall Y Motor X10SA-ES-KBV:TRYW baseline vfm no no
94 vfm_ydr VFM Downstream Ring Y Motor X10SA-ES-KBV:TRYDR baseline vfm no no
95 vfm_bu VFM Upstream Bender Motor X10SA-ES-KBV:BNDU baseline vfm no no
96 vfm_bd VFM Downstream Bender Motor X10SA-ES-KBV:BNDD baseline vfm no no
97 vfm_yaw VFM Virtual Yaw Motor X10SA-ES-KBV:YAW baseline vfm no no
98 vfm_roll VFM Virtual Roll Motor X10SA-ES-KBV:ROLL baseline vfm no no
99 vfm_pitch VFM Virtual Pitch Motor X10SA-ES-KBV:PITCH baseline vfm no no
100 vfm_x VFM Virtual X Motor X10SA-ES-KBV:TRX baseline vfm no no
101 vfm_y VFM Virtual Y Motor X10SA-ES-KBV:TRY baseline vfm no no
102 hfm_xu HFM Upstream X Motor X10SA-ES-KBH:TRXU baseline hfm no no
103 hfm_xd HFM Downstream X Motor X10SA-ES-KBH:TRXD baseline hfm no no
104 hfm_yuw HFM Upstream Wall Y Motor X10SA-ES-KBH:TRYUW baseline hfm no no
105 hfm_yr HFM Ring Y Motor X10SA-ES-KBH:TRYR baseline hfm no no
106 hfm_ydw HFM Downstream Wall Y Motor X10SA-ES-KBH:TRYDW baseline hfm no no
107 hfm_bu HFM Upstream Bender Motor X10SA-ES-KBH:BNDU baseline hfm no no
108 hfm_bd HFM Downstream Bender Motor X10SA-ES-KBH:BNDD baseline hfm no no
109 hfm_yaw HFM Virtual Yaw Motor X10SA-ES-KBH:YAW baseline hfm no no
110 hfm_roll HFM Virtual Roll Motor X10SA-ES-KBH:ROLL baseline hfm no no
111 hfm_pitch HFM Virtual Pitch Motor X10SA-ES-KBH:PITCH baseline hfm no no
112 hfm_x HFM Virtual X Motor X10SA-ES-KBH:TRX baseline hfm no no
113 hfm_y HFM Virtual Y Motor X10SA-ES-KBH:TRY baseline hfm no no
114 bcu_bpm1 BCU BPM Signal 1 SignalRO X10SA-ES-BCBPM:Current1:MeanValue_RBV monitored bpm yes yes
115 bcu_bpm2 BCU BPM Signal 2 SignalRO X10SA-ES-BCBPM:Current2:MeanValue_RBV monitored bpm yes yes
116 bcu_bpm3 BCU BPM Signal 3 SignalRO X10SA-ES-BCBPM:Current3:MeanValue_RBV monitored bpm yes yes
117 bcu_bpm4 BCU BPM Signal 4 SignalRO X10SA-ES-BCBPM:Current4:MeanValue_RBV monitored bpm yes yes
118 bcu_bpmsum BCU BPM Summed SignalRO X10SA-ES-BCBPM:SumAll:MeanValue_RBV monitored bpm yes yes
119 bcu_bpm_x BCU BPM X Motor X10SA-ES-BCBPM:TRX baseline bcu no yes
120 bcu_bpm_y BCU BPM Y Motor X10SA-ES-BCBPM:TRY baseline bcu no yes
121 bcu_sl_xw BCU slit wall Motor X10SA-ES-BCSLH:TRXW baseline bcu no no
122 bcu_sl_xr BCU slit ring Motor X10SA-ES-BCSLH:TRXR baseline bcu no no
123 bcu_sl_xcen BCU slit X centre Motor X10SA-ES-BCSLH:CENTER baseline bcu no no
124 bcu_sl_xsize BCU slit X size Motor X10SA-ES-BCSLH:SIZEX baseline bcu no no
125 bcu_sl_yt BCU slit top Motor X10SA-ES-BCSLV:TRYT baseline bcu no no
126 bcu_sl_yb BCU slit bottom Motor X10SA-ES-BCSLV:TRYB baseline bcu no no
127 bcu_sl_ycen BCU slit Y centre Motor X10SA-ES-BCSLV:CENTER baseline bcu no no
128 bcu_sl_ysize BCU slit Y size Motor X10SA-ES-BCSLV:SIZE baseline bcu no no
129 xrf_pos XRF det in/out Signal X10SA-ES-XRF:POS-SET baseline se no yes {"type":positioner}
130 samcam_x sample cam X SignalRO X10SA-ES-MS:Stats5:CentroidX_RBV baseline scam yes yes
131 samcam_xsig sample cam X sigma SignalRO X10SA-ES-MS:Stats5:SigmaX_RBV monitored scam yes yes
132 samcam_y sample cam Y SignalRO X10SA-ES-MS:Stats5:CentroidY_RBV baseline scam yes yes
133 samcam_ysig sample cam Y sigma SignalRO X10SA-ES-MS:Stats5:SigmaY_RBV monitored scam yes yes
134 samcam_max sample cam max value SignalRO X10SA-ES-MS:Stats5:MaxValue_RBV monitored scam yes yes
135 samcam_exp sample cam exp time Signal X10SA-ES-MS:cam1:AcquireTime baseline scam no yes
136 samcam_gain sample cam gain Signal X10SA-ES-MS:cam1:Gain baseline scam no yes
137 scam_zoom Sample cam zoom Motor X10SA-ES-MS:ZOOM baseline scam no yes
138 fl_bright Frontlight brightness Signal X10SA-ES-FL:SET baseline se no yes
139 coll_x Collimator X Motor X10SA-ES-COL:TRX baseline se no yes
140 coll_y Collimator Y Motor X10SA-ES-COL:TRY baseline se no yes {"type": multi-position, "in": 41.5, "out": 20.0, "park": 0,"tol":0.05}
141 diag_y Scintillator/diode Y Motor X10SA-ES-SCL:TRY baseline se no yes {"type": multi-position, "scint": 38.62, "i1": 44.0, "out": 20.0,"park": 0,"tol":0.3}
142 diag_z Scintillator/diode Z Motor X10SA-ES-SCL:TRZ baseline se no yes
143 i1 i1 diode reading SignalRO X10SA-ES-SCLDI:READOUT monitored bpm yes yes
144 bl_pos Backlight positioner Signal X10SA-ES-BL:POS-SET baseline se no yes {"type":positioner}
145 bl_bright Backlight brightness Signal X10SA-ES-BL:SET baseline se no yes
146 bs_x Beamstop X Motor X10SA-ES-BS:TRX baseline se no yes
147 bs_y Beamstop Y Motor X10SA-ES-BS:TRY baseline se no yes
148 bs_z Beamstop Z Motor X10SA-ES-BS:TRZ baseline se no yes {"type": guarded, "min": 13, "samp": 15, "work_min": 20, "safe": 41, "max_blin": 42, "max_blout": 70}
149 bs_pos Beamstop positioner Signal X10SA-ES-BS:POS-SET baseline se no yes {"type":positioner}
150 gon_x Goniometer X Motor X10SA-ES-DF1:TRX1 baseline det no yes {"type": guarded, "in": 18.0, "out": -10.0, "safe": -100,"tol":0.5}
151 gon_y Goniometer Y Motor X10SA-ES-DF1:TRY1 baseline det no yes
152 gon_z Goniometer X Motor X10SA-ES-DF1:TRZ1 baseline det no yes
153 omega Omega Motor X10SA-ES-DF1:ROTU baseline det no yes
154 cryo_pos Cryo positioner Signal X10SA-ES-CS:POS-SET baseline se no yes {"type":positioner}
155 cryo_x Cryojet X Motor X10SA-ES-CS:TRX baseline se no yes
156 det_xi_focus X-ray eye 2 Focus Motor X10SA-ES-XEYE:FOCUS baseline det no yes
157 det_xi_zoom X-ray eye 2 Zoom Motor X10SA-ES-XEYE:ZOOM baseline det no yes
158 det_xi_x X-ray eye X Motor X10SA-ES-XEYE:TRX baseline det no yes
159 i2 i2 SignalRO X10SA-ES-XEYEDI:READOUT monitored bpm yes yes
160 det_xicam_x sample cam X SignalRO X10SA-ES-XEYE:cam1:Stats5:CentroidX_RBV baseline scam yes no
161 det_xicam_xsig sample cam X sigma SignalRO X10SA-ES-XEYE:cam1:Stats5:SigmaX_RBV monitored scam yes no
162 det_xicam_y sample cam Y SignalRO X10SA-ES-XEYE:cam1:Stats5:CentroidY_RBV baseline scam yes no
163 det_xicam_ysig sample cam Y sigma SignalRO X10SA-ES-XEYE:cam1:Stats5:SigmaY_RBV monitored scam yes no
164 det_xicam_max sample cam max value SignalRO X10SA-ES-XEYE:cam1:Stats5:MaxValue_RBV monitored scam yes no
165 det_xicam_exp sample cam exp time Signal X10SA-ES-XEYE:cam1:cam1:AcquireTime baseline scam no no
166 det_xicam_gain sample cam gain Signal X10SA-ES-XEYE:cam1:cam1:Gain baseline scam no no
167 det_cov Detector cover Signal X10SA-ES-DETCOV:SET baseline det no yes {"type":positioner}
168 det_y Detector Y Motor X10SA-ES-DET:TRY baseline det no yes
169 det_z Detector Z Motor X10SA-ES-DET:TRZ baseline det no yes
@@ -0,0 +1,12 @@
smargon:
description: REST-based device which connects to Smargopolo
deviceClass: pxii_bec.devices.smargopolo_smargon.Smargon
deviceConfig: {prefix: 'http://localhost:8000'}
onFailure: buffer
enabled: True
readoutPriority: baseline
deviceTags:
- smargon
- motors
readOnly: false
softwareTrigger: false
@@ -10,39 +10,6 @@ id_gap:
enabled: true
readOnly: false
softwareTrigger: false
coll_x:
description: Collimator X
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-ES-COL:TRX'}
onFailure: buffer
enabled: True
readoutPriority: baseline
deviceTags:
- se
readOnly: false
softwareTrigger: false
dcm_fpitch:
description: DCM 2nd crystal fine pitch
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-DCM:PITCH-C2'}
onFailure: buffer
enabled: True
readoutPriority: baseline
deviceTags:
- dcm
readOnly: false
softwareTrigger: false
dcm_froll:
description: DCM 2nd crystal fine roll
deviceClass: ophyd.EpicsMotor
deviceConfig: {prefix: 'X10SA-OP-DCM:ROLL-C2'}
onFailure: buffer
enabled: True
readoutPriority: baseline
deviceTags:
- dcm
readOnly: false
softwareTrigger: false
smargon:
description: REST-based device which connects to Smargopolo
@@ -55,17 +22,4 @@ smargon:
- smargon
- motors
readOnly: false
softwareTrigger: false
aerotech:
description: REST-based device which connects to AareScan
deviceClass: pxii_bec.devices.aerotech
deviceConfig: { prefix: "http://mx-x10sa-queue-01:5234/" }
onFailure: buffer
enabled: True
readoutPriority: baseline
deviceTags:
- aerotech
- motors
readOnly: false
softwareTrigger: false
softwareTrigger: false
-43
View File
@@ -1,43 +0,0 @@
from ophyd import Component as Cpt
from .http import TIMESTAMP_ID, HttpDeviceController, HttpDeviceSignal, HttpOphydDevice
class AerotechController(HttpDeviceController):
_readback_endpoint = "status"
_target_endpoint = "position"
def __init__(self, *, prefix, **kwargs):
self._readbacks: dict[str, dict[str, float | bool]] = {}
super().__init__(prefix=prefix, **kwargs)
def put(self, axis: str, val: float):
self._rest_post(body={axis: val})
def get_readback(self, axis_id: str) -> tuple[float, float] | None:
with self._readback_lock:
if axis_id not in self._readbacks or TIMESTAMP_ID not in self._readbacks:
return None
return self._readbacks.get(axis_id)["pos"], self._readbacks.get(TIMESTAMP_ID) # type: ignore
class Aerotech(HttpOphydDevice):
controller_class = AerotechController
x = Cpt(HttpDeviceSignal, axis_identifier="x", tolerance=0.01)
y = Cpt(HttpDeviceSignal, axis_identifier="y", tolerance=0.01)
z = Cpt(HttpDeviceSignal, axis_identifier="z", tolerance=0.01)
u = Cpt(HttpDeviceSignal, axis_identifier="u", tolerance=0.01)
vel_u_deg_s = Cpt(HttpDeviceSignal, axis_identifier="vel_u_deg_s", tolerance=0.01)
def _test():
a = Aerotech(name="aerotech", prefix="http://mx-x10sa-queue-01:5234")
a.wait_for_connection()
return a
if __name__ == "__main__":
aerotech = _test()
print(aerotech.read())
aerotech.stop()
-178
View File
@@ -1,178 +0,0 @@
import time
from abc import ABC, abstractmethod
from threading import Event, RLock, Thread
from typing import Any
from ophyd import OphydObject
from ophyd_devices import PSIDeviceBase
from ophyd_devices.utils.socket import SocketSignal
from requests import Response, Session
TIMESTAMP_ID = "__timestamp"
_POLL_INTERVAL_SLOW = 0.1
class HttpRestError(Exception):
"""Error for rest calls from a HttpRestSignal."""
def __init__(self, resp: Response, *args: object, value: Any | None = None) -> None:
method, url = resp.request.method, resp.request.url
data = f"{str(value)} to " if value is not None else ""
super().__init__(
f"Could not {method} {data}{url}. Code: {resp.status_code}. Reason: {resp.reason}.",
*args,
)
class HttpDeviceController(OphydObject, ABC):
"""Controller to consolidate polling loops and other REST calls for devices which communicate
with HTTP REST interfaces"""
_readback_endpoint: str
_target_endpoint: str
def __init__(self, *, prefix, **kwargs):
self._readbacks: dict
self._session = Session()
self._prefix = prefix
self._targets = {}
self._signal_registry: set[str] = set()
self._readback_poll_interval: float = _POLL_INTERVAL_SLOW
super().__init__(**kwargs)
self._setup_readback()
def _setup_readback(self):
self._stop_monitor_readback_event = Event()
self._readback_lock = RLock()
self._monitor_readback_thread = Thread(
target=self._monitor,
args=[
self._readback_endpoint,
self._stop_monitor_readback_event,
self._readback_lock,
self._readbacks,
],
)
def manual_update(self):
self._update_reading(self._readback_endpoint, self._readback_lock, self._readbacks)
def _update_reading(self, endpoint: str, lock: RLock, buffer: dict):
data = self._rest_get(endpoint)
timestamp = time.monotonic()
with lock:
buffer.update(data)
buffer["__timestamp"] = timestamp
def _monitor(self, endpoint: str, event: Event, lock: RLock, buffer: dict):
while not event.is_set():
self._update_reading(endpoint, lock, buffer)
time.sleep(self._readback_poll_interval)
def _clean_monitor(self):
if self._monitor_readback_thread.is_alive():
self._stop_monitor_readback_event.set()
self._monitor_readback_thread.join(timeout=2)
if self._monitor_readback_thread.is_alive():
raise RuntimeError("Failed to clean up Aerotech monitor thread.")
def register(self, axis_id: str):
self._signal_registry.add(axis_id)
def _rest_get(self, endpoint):
resp = self._session.get(self._prefix + endpoint)
if not resp.ok:
raise HttpRestError(resp)
return resp.json()
def _rest_put(self, params: dict | None = None, body: dict | None = None):
resp = self._session.put(self._prefix + self._target_endpoint, params=params, json=body)
if not resp.ok:
raise HttpRestError(resp, value=params)
def _rest_post(self, params: dict | None = None, body: dict | None = None):
resp = self._session.post(self._prefix + self._target_endpoint, params=params, json=body)
if not resp.ok:
raise HttpRestError(resp, value=params)
def start_monitor(self):
"""Start or restart the automonitor thread."""
self._clean_monitor()
self._setup_readback()
self._monitor_readback_thread.start()
def monitor_stopped(self):
return not self._monitor_readback_thread.is_alive()
def put(self, axis: str, val: float):
self._rest_put({axis: val})
@abstractmethod
def get_readback(self, axis_id: str) -> tuple[float, float] | None:
"""Return a tuple (reading, timestamp) if the axis_id exists"""
def stop(self):
# There doesn't appear to be a stop endpoint on the server
# Best effort: set the target to the current position
pass
# TODO: self._rest_put(self._readbacks)
class HttpDeviceSignal(SocketSignal):
"""Ophyd signal which gets and puts to a REST API rather than EPICS PVs, mediated through the Aerotech
Controller"""
def __init__(self, *args, axis_identifier: str, **kwargs):
super().__init__(*args, **kwargs)
controller: HttpDeviceController | None = getattr(self.root, "controller", None)
if controller is None:
raise TypeError("HttpDeviceSignal must be used in a device with a HttpDeviceController")
self._controller = controller
self._axis_id = axis_identifier
self._controller.register(self._axis_id)
def _socket_get(self): # type: ignore
self._readback, self.metadata["timestamp"] = self._controller.get_readback(
self._axis_id
) or (0.0, 0.0)
return self._readback
def _socket_set(self, val: float):
self._controller.put(self._axis_id, val)
def get(self, **kwargs):
if self._controller.monitor_stopped():
self._controller.start_monitor()
return super().get(**kwargs)
class HttpOphydDevice(PSIDeviceBase):
controller_class: type[HttpDeviceController]
def __init__(
self,
*,
name: str,
prefix: str = "",
scan_info=None,
device_manager=None,
**kwargs,
):
self.controller = self.controller_class(prefix=prefix)
super().__init__(
name=name,
prefix=prefix,
scan_info=scan_info,
device_manager=device_manager,
**kwargs,
)
def wait_for_connection(self, **kwargs): # type: ignore
self.controller.start_monitor()
self.controller.manual_update()
return super().wait_for_connection(**kwargs)
def stop(self, *, success: bool = False) -> None:
self.controller.stop()
return super().stop(success=success)
+133 -17
View File
@@ -1,21 +1,122 @@
from ophyd import Component as Cpt
from ophyd_devices import PSIDeviceBase
import time
from threading import Event, RLock, Thread
from typing import Any
from .http import HttpDeviceController, HttpDeviceSignal, HttpOphydDevice
from ophyd import Component as Cpt
from ophyd import OphydObject
from ophyd_devices import PSIDeviceBase
from ophyd_devices.utils.socket import SocketSignal
from requests import Response, get, put
_TIMESTAMP_ID = "__timestamp"
_POLL_INTERVAL_SLOW = 0.1
class SmargonController(HttpDeviceController):
class HttpRestError(Exception):
"""Error for rest calls from a HttpRestSignal."""
def __init__(self, resp: Response, *args: object, value: Any | None = None) -> None:
method, url = resp.request.method, resp.request.url
data = f"{str(value)} to " if value is not None else ""
super().__init__(
f"Could not {method} {data}{url}. Code: {resp.status_code}. Reason: {resp.reason}.",
*args,
)
class SmargonSignal(SocketSignal):
"""Ophyd signal which gets and puts to a REST API rather than EPICS PVs, mediated through the SmargonController"""
def __init__(self, *args, axis_identifier: str, **kwargs):
super().__init__(*args, **kwargs)
controller: SmargonController | None = getattr(self.root, "controller", None)
if controller is None:
raise TypeError("SmargonSignal must be used in a device with a SmargonController")
self._controller = controller
self._axis_id = axis_identifier
self._controller.register(self._axis_id)
def _socket_get(self): # type: ignore
self._readback, self.metadata["timestamp"] = self._controller.get_readback(
self._axis_id
) or (0.0, 0.0)
return self._readback
def _socket_set(self, val: float):
self._controller.put(self._axis_id, val)
def get(self):
if self._controller.monitor_stopped():
self._controller.start_monitor()
return super().get()
class SmargonController(OphydObject):
"""Controller to consolidate polling loops and other REST calls for the smargon"""
_readback_endpoint = "/readbackSCS"
_target_endpoint = "/targetSCS"
def __init__(self, *, prefix, **kwargs):
self._prefix = prefix
self._readback_endpoint = "/readbackSCS"
self._target_endpoint = "/targetSCS"
self._targets = {}
self._signal_registry: set[str] = set()
self._readback_poll_interval: float = _POLL_INTERVAL_SLOW
super().__init__(**kwargs)
self._setup_readback()
def _setup_readback(self):
self._readbacks: dict[str, float] = {}
super().__init__(prefix=prefix, **kwargs)
self._stop_monitor_readback_event = Event()
self._readback_lock = RLock()
self._monitor_readback_thread = Thread(
target=self._monitor,
args=[
self._readback_endpoint,
self._stop_monitor_readback_event,
self._readback_lock,
self._readbacks,
],
)
def _monitor(self, endpoint: str, event: Event, lock: RLock, buffer: dict):
while not event.is_set():
data = self._rest_get(endpoint)
timestamp = time.monotonic()
with lock:
buffer.update(data)
buffer["__timestamp"] = timestamp
time.sleep(self._readback_poll_interval)
def _clean_monitor(self):
if self._monitor_readback_thread.is_alive():
self._stop_monitor_readback_event.set()
self._monitor_readback_thread.join(timeout=2)
if self._monitor_readback_thread.is_alive():
raise RuntimeError("Failed to clean up Smargon monitor thread.")
def register(self, axis_id: str):
self._signal_registry.add(axis_id)
def _rest_get(self, endpoint):
resp = get(self._prefix + endpoint)
if not resp.ok:
raise HttpRestError(resp)
return resp.json()
def _rest_put(self, val: dict[str, float]):
resp = put(self._prefix + self._target_endpoint, params=val)
if not resp.ok:
raise HttpRestError(resp, value=val)
def start_monitor(self):
"""Start or restart the automonitor thread."""
self._clean_monitor()
self._setup_readback()
self._monitor_readback_thread.start()
def monitor_stopped(self):
return not self._monitor_readback_thread.is_alive()
def get_readback(self, axis_id: str) -> tuple[float, float] | None:
with self._readback_lock:
@@ -24,19 +125,34 @@ class SmargonController(HttpDeviceController):
return self._readbacks.get(axis_id), self._readbacks.get(_TIMESTAMP_ID) # type: ignore
def put(self, axis: str, val: float):
self._rest_put(params={axis: val})
self._rest_put({axis: val})
def stop(self):
# There doesn't appear to be a stop endpoint on the server
# Best effort: set the target to the current position
self._rest_put(params=self._readbacks)
self._rest_put(self._readbacks)
class Smargon(HttpOphydDevice):
controller_class = SmargonController
class Smargon(PSIDeviceBase):
x = Cpt(HttpDeviceSignal, axis_identifier="SHX", tolerance=0.01)
y = Cpt(HttpDeviceSignal, axis_identifier="SHY", tolerance=0.01)
z = Cpt(HttpDeviceSignal, axis_identifier="SHZ", tolerance=0.01)
phi = Cpt(HttpDeviceSignal, axis_identifier="PHI", tolerance=0.01)
chi = Cpt(HttpDeviceSignal, axis_identifier="CHI", tolerance=0.01)
x = Cpt(SmargonSignal, axis_identifier="SHX", tolerance=0.01)
y = Cpt(SmargonSignal, axis_identifier="SHY", tolerance=0.01)
z = Cpt(SmargonSignal, axis_identifier="SHZ", tolerance=0.01)
phi = Cpt(SmargonSignal, axis_identifier="PHI", tolerance=0.01)
chi = Cpt(SmargonSignal, axis_identifier="CHI", tolerance=0.01)
def __init__(
self, *, name: str, prefix: str = "", scan_info=None, device_manager=None, **kwargs
):
self.controller = SmargonController(prefix=prefix)
super().__init__(
name=name, prefix=prefix, scan_info=scan_info, device_manager=device_manager, **kwargs
)
def wait_for_connection(self, **kwargs): # type: ignore
self.controller.start_monitor()
return super().wait_for_connection(**kwargs)
def stop(self, *, success: bool = False) -> None:
self.controller.stop()
return super().stop(success=success)
View File
+120 -139
View File
@@ -45,12 +45,7 @@ def a2e(a, *hkl):
if "deg" in hkl:
ideg = 1
d0 = (
2
* 5.43102
* (1 - 2.4e-4 * iln)
/ np.sqrt(h[0] ** 2.0 + h[1] ** 2.0 + h[2] ** 2.0)
)
d0 = 2 * 5.43102 * (1 - 2.4e-4 * iln) / np.sqrt(h[0] ** 2.0 + h[1] ** 2.0 + h[2] ** 2.0)
if ideg or (a > 1):
a = math.radians(a) # *math.pi/180.
@@ -91,12 +86,7 @@ def angle(e, *hkl):
if "deg" in hkl:
ideg = 1
d0 = (
2
* 5.43102
* (1 - 2.4e-4 * iln)
/ np.sqrt(h[0] ** 2.0 + h[1] ** 2.0 + h[2] ** 2.0)
)
d0 = 2 * 5.43102 * (1 - 2.4e-4 * iln) / np.sqrt(h[0] ** 2.0 + h[1] ** 2.0 + h[2] ** 2.0)
a = math.asin(12.39842 / d0 / e)
if ideg:
@@ -149,7 +139,7 @@ def rock(**kwargs):
import matplotlib.pyplot as plt
dock_area = bec.gui.new()
wr = dock_area.new(bec.gui.available_widgets.Waveform)
wr = dock_area.new().new(bec.gui.available_widgets.Waveform)
# width of rocking curve of perfect xtal: at 20 keV: 14 urad == 0.0008 deg
@@ -206,7 +196,7 @@ def rock(**kwargs):
s = scans.line_scan(mot, -dx, dx, steps=50, exp_time=time, relative=True)
# md = scan.metadata["bec"]
wr.title = f"RockingScan at Energy of {e}"
wr.plot(device_x=mot.name, device_y=det.name) ##set names/axes first !
wr.plot(x_name=mot.name, y_name=det.name) ##set names/axes first !
wr.x_label = mot.name
wr.y_label = det.name
if ax == 0:
@@ -263,13 +253,7 @@ def justfit(data_x, data_y, model="gauss", ibg=0):
gfit, xmax = justfit(data_x, data_y, model = "lorentz", ibg =0) : Lorentzian, no BG
"""
from lmfit.models import (
LinearModel,
GaussianModel,
VoigtModel,
QuadraticModel,
LorentzianModel,
)
from lmfit.models import LinearModel, GaussianModel, VoigtModel, QuadraticModel, LorentzianModel
import matplotlib.pyplot as plt
peak = GaussianModel()
@@ -338,20 +322,12 @@ def fit_plothist(hindex: int, signal_name: str, model="gauss", ibg=0):
"""
from lmfit.models import (
LinearModel,
GaussianModel,
VoigtModel,
QuadraticModel,
LorentzianModel,
)
from lmfit.models import LinearModel, GaussianModel, VoigtModel, QuadraticModel, LorentzianModel
import matplotlib.pyplot as plt
h = bec.history[hindex]
md = h.metadata["bec"]
scanvar = list(md["args"].keys())[
0
] # string, returns the variable of the last performed scan
scanvar = list(md["args"].keys())[0] # string, returns the variable of the last performed scan
# data = h.devices[device_name][signal].read()["value"]
# data_x = h.devices.dcm_pitch.dcm_pitch.read()["value"]
# data_y = h.devices.lu_bpmsum.lu_bpmsum.read()["value"]
@@ -442,9 +418,9 @@ def fit_plot(data_x, data_y, model="gauss", ibg=1, fitrange=0, fitclick=0):
sigma = 1.0
gamma = 0.2 # blurring/widening of the sigma ; the larger, the more of a Lorentzian profile
print("maxy, indmax, xm = ", maxy, indmax, xm)
#p = model.make_params(
# p = model.make_params(
# amplitude=max(data_y), center=xm, slope=0, intercept=min(data_y)
#)
# )
p = model.make_params(amplitude=maxy, center=xm)
p["center"].set(min=min(data_x), max=max(data_x))
p["sigma"].set(min=0, max=(max(data_x) - min(data_x)) / 2.0)
@@ -541,7 +517,7 @@ def save_data(hindex: int, device_name: str, signal_name: str):
ans = "n"
ans = input("Store data in csv file? y/n ")
if ans == "y":
dirname = "/home/gac-x10sa/Data/"
dirname = "/sls/x10sa/config/commissioning/Data/"
# writing output to simple data file for later analysis:
combined = np.column_stack((data_x, data_y))
filename = dirname + "Scan" + str(hindex) + device_name + ".txt"
@@ -645,7 +621,7 @@ def save_plot_gaps(hindex: int, device_name: str, signal_name: str):
plt.show()
dirname = "/home/gac-x10sa/Data/"
dirname = "/sls/x10sa/config/commissioning/Data/"
# writing output to simple data file for later analysis:
combined = np.column_stack((en_vec, data_y))
filename = dirname + "EnScan" + str(hindex) + ".txt"
@@ -668,7 +644,8 @@ def getdiodepos(diode="i1"):
"""
diode_in = 1
dpos = 44 # mm
dpos = dev.diag_y.user_parameter["i1"] # 44 # mm
measdev = dev.scin_y
diodepos_rb = measdev.user_readback.get()
if abs(dpos - diodepos_rb) > 0.1:
@@ -692,7 +669,7 @@ def read_mon():
e = getenergy()
fesum = dev.fe_bpmsum.read()["fe_bpmsum"]["value"]
lusum = dev.lu_bpmsum.read()["lu_bpmsum"]["value"]
bscsum = dev.bsc_bpmsum.read()["bsc_bpmsum"]["value"]
sssum = dev.ss_bpmsum.read()["ss_bpmsum"]["value"]
# Mono
bragg = dev.dcm_bragg.read()["dcm_bragg"]["value"]
@@ -727,24 +704,24 @@ def read_mon():
fe_sy_size = dev.fe_sysize.read()["fe_sysize"]["value"]
## BSF slits centre and size
s1_xcen = dev.s1_xcen.read()["s1_xcen"]["value"]
s1_xsize = dev.s1_xsize.read()["s1_xsize"]["value"]
s1_ycen = dev.s1_ycen.read()["s1_ycen"]["value"]
s1_ysize = dev.s1_ysize.read()["s1_ysize"]["value"]
bsf_xcen = dev.bsf_sl_xcen.read()["bsf_sl_xcen"]["value"]
bsf_xsize = dev.bsf_sl_xsize.read()["bsf_sl_xsize"]["value"]
bsf_ycen = dev.bsf_sl_ycen.read()["bsf_sl_ycen"]["value"]
bsf_ysize = dev.bsf_sl_ysize.read()["bsf_sl_ysize"]["value"]
## BSC slits centre and size
s2_xcen = dev.s2_xcen.read()["s2_xcen"]["value"]
s2_xsize = dev.s2_xsize.read()["s2_xsize"]["value"]
s2_ycen = dev.s2_ycen.read()["s2_ycen"]["value"]
s2_ysize = dev.s2_ysize.read()["s2_ysize"]["value"]
## SS slits centre and size
ss_sl_xcen = dev.ss_sl_xcen.read()["ss_sl_xcen"]["value"]
ss_sl_xsize = dev.ss_sl_xsize.read()["ss_sl_xsize"]["value"]
ss_sl_ycen = dev.ss_sl_ycen.read()["ss_sl_ycen"]["value"]
ss_sl_ysize = dev.ss_sl_ysize.read()["ss_sl_ysize"]["value"]
## BCU slits centre and size
s3_xcen = dev.s3_xcen.read()["s3_xcen"]["value"]
s3_xsize = dev.s3_xsize.read()["s3_xsize"]["value"]
s3_ycen = dev.s3_ycen.read()["s3_ycen"]["value"]
s3_ysize = dev.s3_ysize.read()["s3_ysize"]["value"]
bcu_sl_xcen = dev.bcu_sl_xcen.read()["bcu_sl_xcen"]["value"]
bcu_sl_xsize = dev.bcu_sl_xsize.read()["bcu_sl_xsize"]["value"]
bcu_sl_ycen = dev.bcu_sl_ycen.read()["bcu_sl_ycen"]["value"]
bcu_sl_ysize = dev.bcu_sl_ysize.read()["bcu_sl_ysize"]["value"]
## move in screen in BSC chamber and get size and position
## move in screen in SS chamber and get size and position
## move out again
# umv(dev.samcam_xmot, 1)
@@ -767,12 +744,10 @@ def read_mon():
bcusum = dev.bcu_bpmsum.read()["bcu_bpmsum"]["value"]
i1signal = dev.i1.read()["i1"]["value"]
print("Energy = ", e, " keV")
print(f"fesum,lusum,bscsum,bcusum,i1signal = {fesum,lusum,bscsum,bcusum,i1signal}")
print(f"fesum,lusum,sssum,bcusum,i1signal = {fesum,lusum,sssum,bcusum,i1signal}")
print(
f"bragg, pitch, perp, fpitch, froll, gap = {bragg, pitch,perp, fpitch, froll, gap}"
)
# return e, fesum,lusum,bscsum,bcusum,i1signal
print(f"bragg, pitch, perp, fpitch, froll, gap = {bragg, pitch,perp, fpitch, froll, gap}")
# return e, fesum,lusum,sssum,bcusum,i1signal
print("KB VERT")
print(
f"vbu, vbd, vbpitch,vbyaw,vbroll,vblat,vbvert = {vbu, vbd, vbpitch,vbyaw,vbroll,vblat,vbvert}"
@@ -786,7 +761,7 @@ def read_mon():
## dump status in CSV
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
dirname = "/home/gac-x10sa/Data/"
dirname = "/sls/x10sa/config/commissioning/Data/"
filename = dirname + f"BLstatus_{timestamp}.txt"
with open(filename, "w") as f:
combined = np.column_stack((e, gap))
@@ -810,19 +785,19 @@ def read_mon():
np.savetxt(f, combined, delimiter=",", fmt="%5f")
f.write("BSF slits\n")
combined = np.column_stack((s1_xcen, s1_xsize, s1_ycen, s1_ysize))
combined = np.column_stack((bsf_sl_xcen, bsf_sl_xsize, bsf_sl_ycen, bsf_sl_ysize))
np.savetxt(f, combined, delimiter=",", fmt="%5f")
f.write("BSC slits\n")
combined = np.column_stack((s2_xcen, s2_xsize, s2_ycen, s2_ysize))
f.write("SS slits\n")
combined = np.column_stack((ss_sl_xcen, ss_sl_xsize, ss_sl_ycen, ss_sl_ysize))
np.savetxt(f, combined, delimiter=",", fmt="%5f")
f.write("BCU slits\n")
combined = np.column_stack((s3_xcen, s3_xsize, s3_ycen, s3_ysize))
combined = np.column_stack((bcu_sl_xcen, bcu_sl_xsize, bcu_sl_ycen, bcu_sl_ysize))
np.savetxt(f, combined, delimiter=",", fmt="%5f")
f.write("fesum,lusum,bscsum,bcusum,i1signal\n")
combined = np.column_stack((fesum, lusum, bscsum, bcusum, i1signal))
f.write("fesum,lusum,sssum,bcusum,i1signal\n")
combined = np.column_stack((fesum, lusum, sssum, bcusum, i1signal))
np.savetxt(f, combined, delimiter=",", fmt="%5f")
return
@@ -840,33 +815,23 @@ def longscan():
umv(dev.id_gap, 4.5)
time.sleep(0.2)
s = scans.line_scan(
dev.dcm_bragg, 406.5, 65.9, steps=1500, exp_time=0.05, relative=False
)
s = scans.line_scan(dev.dcm_bragg, 406.5, 65.9, steps=1500, exp_time=0.05, relative=False)
time.sleep(2)
umv(dev.id_gap, 5.0)
time.sleep(0.2)
s = scans.line_scan(
dev.dcm_bragg, 65.9, 406.5, steps=1500, exp_time=0.05, relative=False
)
s = scans.line_scan(dev.dcm_bragg, 65.9, 406.5, steps=1500, exp_time=0.05, relative=False)
time.sleep(2)
umv(dev.id_gap, 5.5)
time.sleep(0.2)
s = scans.line_scan(
dev.dcm_bragg, 406.5, 65.9, steps=1500, exp_time=0.05, relative=False
)
s = scans.line_scan(dev.dcm_bragg, 406.5, 65.9, steps=1500, exp_time=0.05, relative=False)
time.sleep(2)
umv(dev.id_gap, 6.0)
time.sleep(0.2)
s = scans.line_scan(
dev.dcm_bragg, 65.9, 406.5, steps=1500, exp_time=0.05, relative=False
)
s = scans.line_scan(dev.dcm_bragg, 65.9, 406.5, steps=1500, exp_time=0.05, relative=False)
time.sleep(2)
umv(dev.id_gap, 6.5)
time.sleep(0.2)
s = scans.line_scan(
dev.dcm_bragg, 406.5, 65.9, steps=1500, exp_time=0.05, relative=False
)
s = scans.line_scan(dev.dcm_bragg, 406.5, 65.9, steps=1500, exp_time=0.05, relative=False)
#####################
@@ -890,15 +855,15 @@ def colliscan(direction: str, range=0.3, nsteps=30, stime=0.5, centre=1):
import sys
dock_area = bec.gui.new()
wr = dock_area.new(bec.gui.available_widgets.Waveform)
wr = dock_area.new().new(bec.gui.available_widgets.Waveform)
# check if i1 DIODE is IN
# if not, aks to be moved
diodeinpos = 44 # mm
colli_up = 41 # mm
diodeinpos = dev.diag_y.user_parameter["i1"] # 44 # mm
colli_up = dev.coll_y.user_parameter["in"] # 41.5 # mm
measdev = dev.scin_y
measdev = dev.diag_y
diodepos_rb = measdev.user_readback.get()
if abs(diodeinpos - diodepos_rb) > 0.1:
print("Diode not in, please move")
@@ -999,32 +964,32 @@ def slitscan(device_location: str, direction: str, range: 1, nsteps=50, centre=0
default_h = 3.0 # 8125
default_v = 3.0 # ??? close more ???? # 8149.8
det = dev.lu_bpmsum
# det = dev.bsc_bpmsum or #det = dev.bcu_bpmsum would also work
# det = dev.ss_bpmsum or #det = dev.bcu_bpmsum would also work
if direction == "h":
mot = dev.s1_xcen
size = dev.s1_xsize
mot = dev.bsf_sl_xcen
size = dev.bsf_sl_xsize
s_closed = 0.1
s_open = default_h
else:
mot = dev.s1_ycen
size = dev.s1_ysize
mot = dev.bsf_sl_ycen
size = dev.bsf_sl_ysize
s_closed = 0.1
s_open = default_v
# BSC slits ================================
# SS slits ================================
if device_location in ["bsc", "s2", "ss"]:
if device_location in ["ss", "ss_sl", "ss"]:
default_h = 6.0 # ???
default_v = 5.0 # ??? close more ???? # 8149.8
det = dev.bcu_bpmsum
if direction == "h":
mot = dev.s2_xcen
size = dev.s2_xsize
mot = dev.ss_sl_xcen
size = dev.ss_sl_xsize
s_closed = 0.1
s_open = default_h
else:
mot = dev.s2_ycen
size = dev.s2_ysize
mot = dev.ss_sl_ycen
size = dev.ss_sl_ysize
s_closed = 0.1
s_open = default_v
@@ -1035,20 +1000,20 @@ def slitscan(device_location: str, direction: str, range: 1, nsteps=50, centre=0
default_v = 2.0 # ??? close more ???? # 8149.8
# change to i0 later ??
det = dev.i1
dposm = 43.8
dposm = dev.diag_y.user_parameter["i1"]
# dpos0 = dev.scin_y.user_readback.get()
# if abs(dposm - dpos0) > 1:
# print("moving diode i1 in")
# umv(dev.scin_y, dposm)
if direction == "h":
mot = dev.s3_xcen
size = dev.s3_xsize
mot = dev.bcu_sl_xcen
size = dev.bcu_sl_xsize
s_closed = 3.0 ## very large, else does not work !
s_open = default_h
else:
mot = dev.s3_ycen
size = dev.s3_ysize
mot = dev.bcu_sl_ycen
size = dev.bcu_sl_ysize
s_closed = 3.0 ## very large !
s_open = default_v
@@ -1064,14 +1029,14 @@ def slitscan(device_location: str, direction: str, range: 1, nsteps=50, centre=0
return
dock_area = bec.gui.new()
wr = dock_area.new(bec.gui.available_widgets.Waveform)
wr = dock_area.new().new(bec.gui.available_widgets.Waveform)
pos0 = mot.user_readback.get()
siz0 = size.user_readback.get()
umv(size, s_closed)
s = scans.line_scan(mot, -dx, dx, steps=nsteps, exp_time=time, relative=True)
wr.plot(device_x=mot.name, device_y=det.name)
wr.plot(x_name=mot.name, y_name=det.name)
wr.x_label = mot.name
wr.y_label = det.name
@@ -1088,25 +1053,25 @@ def slitscan(device_location: str, direction: str, range: 1, nsteps=50, centre=0
if mot.name == "fe_sycen":
data_x = s.scan.live_data.fe_sycen.fe_sycen.val
# BSF slits ================================
if mot.name == "s1_xcen":
data_x = s.scan.live_data.s1_xcen.s1_xcen.val
if mot.name == "s1_ycen":
data_x = s.scan.live_data.s1_ycen.s1_ycen.val
if mot.name == "bsf_sl_xcen":
data_x = s.scan.live_data.bsf_sl_xcen.bsf_sl_xcen.val
if mot.name == "bsf_ycen":
data_x = s.scan.live_data.bsf_sl_ycen.bsf_sl_ycen.val
# BSC slits ================================
if mot.name == "s2_xcen":
data_x = s.scan.live_data.s2_xcen.s2_xcen.val
# SS slits ================================
if mot.name == "ss_sl_xcen":
data_x = s.scan.live_data.ss_sl_xcen.ss_sl_xcen.val
data_y = s.scan.live_data.bcu_bpmsum.bcu_bpmsum.val
if mot.name == "s2_ycen":
data_x = s.scan.live_data.s2_ycen.s2_ycen.val
if mot.name == "ss_sl_ycen":
data_x = s.scan.live_data.ss_sl_ycen.ss_sl_ycen.val
data_y = s.scan.live_data.bcu_bpmsum.bcu_bpmsum.val
# BCU slits ================================
if mot.name == "s3_xcen":
data_x = s.scan.live_data.s3_xcen.s3_xcen.val
if mot.name == "bcu_sl_xcen":
data_x = s.scan.live_data.bcu_sl_xcen.bcu_sl_xcen.val
data_y = s.scan.live_data.i1.i1.val
if mot.name == "s3_ycen":
data_x = s.scan.live_data.s3_ycen.s3_ycen.val
if mot.name == "bcu_sl_ycen":
data_x = s.scan.live_data.bcu_sl_ycen.bcu_sl_ycen.val
data_y = s.scan.live_data.i1.i1.val
# change to i0 later ??
@@ -1161,9 +1126,7 @@ def kbfocus(sizex, sizey):
en = getenergy()
print(f"Energy is {en} keV")
print(
"Currently only 2 sizes supported, small approx.(2 x 2.7) and medium approx.(40 x 40)"
)
print("Currently only 2 sizes supported, small approx.(2 x 2.7) and medium approx.(40 x 40)")
## is the pitch ok ?
vpitch = 2.695
@@ -1261,11 +1224,9 @@ def bstatus():
#
dock_area = bec.gui.new()
dbrowser = dock_area.new(
bec.gui.available_widgets.DeviceBrowser, object_name="device_browser"
)
dbrowser = dock_area.new("device_browser").new(bec.gui.available_widgets.DeviceBrowser)
dock_area.new(bec.gui.available_widgets.BECQueue, object_name="queue")
dock_area.new("queue").new(bec.gui.available_widgets.BECQueue)
# queue = dock_area.queue.BECQueue # give it a name
# text_box = dock_area.new().new(widget=bec.gui.available_widgets.TextBox)
# text_box.set_plain_text("Hello, World!")
@@ -1307,15 +1268,15 @@ def detxeye_in():
print("moving X-ray eye below Eiger in")
xrpos = dev.xeye2_x.user_readback.get()
xrpos = dev.det_xi_x.user_readback.get()
if abs(setxrpos - xrpos) > 1:
umv(dev.xeye2_x, setxrpos)
zoompos = dev.xeye2_zoom.user_readback.get()
umv(dev.det_xi_x, setxrpos)
zoompos = dev.det_xi_zoom.user_readback.get()
if abs(setzoom - zoompos) > 1:
umv(dev.xeye2_zoom, setzoom)
focpos = dev.xeye2_focus.user_readback.get()
umv(dev.det_xi_zoom, setzoom)
focpos = dev.det_xi_focus.user_readback.get()
if abs(setfoc - focpos) > 1:
umv(dev.xeye2_focus, setfoc)
umv(dev.det_xi_focus, setfoc)
def detxeye_out():
@@ -1352,20 +1313,20 @@ def measure_samcam(zoom=1000):
return sx, sy
def measure_bsccam():
x_inpos = 7 # mm
def measure_sscam():
x_inpos = dev.ss_xi_x.user_parameter["in"] # 7 # mm
px2mum = 20
scpos_rb = dev.xeye_x.user_readback.get()
scpos_rb = dev.ss_xi_x.user_readback.get()
if abs(x_inpos - scpos_rb) > 0.3:
print("Scinti not in, please move")
sys.exit(0)
auto_exposure(cam="bsccam", target=200)
a = dev.bsccam_xsig.read()["bsccam_xsig"]["value"]
b = dev.bsccam_ysig.read()["bsccam_ysig"]["value"]
auto_exposure(cam="sscam", target=200)
a = dev.ss_xicam_xsig.read()["ss_xicam_xsig"]["value"]
b = dev.ss_xicam_ysig.read()["ss_xicam_ysig"]["value"]
sx = a * px2mum * 2.35
sy = b * px2mum * 2.35
print(f"FWHM at BSC cam in um : {sx, sy}")
print(f"FWHM at SS cam in um : {sx, sy}")
return sx, sy
@@ -1381,9 +1342,7 @@ def knife_edge(dir="hor", range=0.05, steps=100):
if dir == "vert":
mot = dev.gon_y
s = scans.line_scan(
dev.gon_x, -range, range, steps=steps, exp_time=1, relative=True
)
s = scans.line_scan(dev.gon_x, -range, range, steps=steps, exp_time=1, relative=True)
return
@@ -1472,9 +1431,7 @@ def scan_eg(erange, nsteps=50, fit=True):
print(f"Scanning Bragg from {a_start} to {a_end} mrad")
s = scans.line_scan(
mot_scan, a_start, a_end, steps=nsteps, exp_time=exptime, relative=False
)
s = scans.line_scan(mot_scan, a_start, a_end, steps=nsteps, exp_time=exptime, relative=False)
## plot and fit the scan
bragg_data = (
@@ -1520,3 +1477,27 @@ def scan_eg(erange, nsteps=50, fit=True):
### compute a signal
###########################
# see in config file
################################################
### open window/doch for long gap scan
################################################
#
def scan_window(wname="Scan", fit=True):
dock_area = bec.gui.new()
# Add a new dock with a Waveform to the BECDockArea
nam = "waveform_dock_"+wname
dock_area.new(name=nam, widget="Waveform")
# dock_area.panels
# dock_area.panel_list
plt1 = dock_area.panels[nam]
# Add signals to the WaveformWidget
plt1.plot(device_x='id_gap', device_y='bpm')
dock2 = dock_area.new(name="motor_dock", widget="MotorMap",relative_to=nam, position="right")
###do stuff
### if done, remove
dock2.remove()
+51 -56
View File
@@ -10,12 +10,9 @@ from scipy.optimize import curve_fit
from scipy.ndimage import gaussian_filter1d
def fit_harm(harm, n, order):
x = harm[0, :].astype(float)
y = harm[1, :].astype(
float
) ## else funny object that might contain funny strings ...
y = harm[1, :].astype(float) ## else funny object that might contain funny strings ...
coeff = np.polyfit(x, y, order) # 3 in general i.e., 4 params
polynomial = np.poly1d(coeff)
x_fit = np.linspace(min(x), max(x), 100)
@@ -23,30 +20,27 @@ def fit_harm(harm, n, order):
print("Polynomial coefficients of the harmonic: ", n, coeff)
plot(x_fit, y_fit, color="blue")
return (
x / n,
y,
) # get the normalized energy gap relation for fitting the Halbach coeff
return (x / n, y) # get the normalized energy gap relation for fitting the Halbach coeff
######### simple exp fit ############
def exponential_func0(x, a, b, c):
# fit 3 params a,b,c
return a * np.exp(b * x + c * x ** 2)
return a * np.exp(b * x + c * x**2)
######### inverse exp fit ############
def exponential_func1(x, a, b, c):
# fit 3 params a,b,c
return 1 / (1 + (a * np.exp(b * x + c * x ** 2)))
return 1 / (1 + (a * np.exp(b * x + c * x**2)))
######### inverse exp fit plus E_max ############
def exponential_func2(x, a, b, c, d):
# fit 4 params a,b,c, e.g., fit energy of storage ring as well
return d / (1 + (a * np.exp(b * x + c * x ** 2)))
return d / (1 + (a * np.exp(b * x + c * x**2)))
##################################
@@ -79,36 +73,38 @@ def return_harmon():
return h
def plot_harmon(e_start, e_end, h_no, pr_out = False):
enarr= np.arange(e_start, e_end+1, 0.5)
def plot_harmon(e_start, e_end, h_no, pr_out=False):
enarr = np.arange(e_start, e_end + 1, 0.5)
h_all = return_harmon()
h=h_all[h_no]
h = h_all[h_no]
polynomial = np.poly1d(h)
gaps = polynomial(enarr)
if (pr_out):
print("en =", enarr )
print("gaps =", gaps )
if pr_out:
print("en =", enarr)
print("gaps =", gaps)
plt.ion()
plt.figure()
plt.plot(enarr,gaps,'*')
plt.plot(enarr, gaps, "*")
plt.title(f"harmonic no {h_no}")
plt.xlabel("E / keV")
plt.ylabel("Gap / mm")
plt.show()
def setu19(en, *harm_no, detune=0):
"""
set the U19 to the gaps defined in Jul2025, or the "theoretical" ones for higher
harmonics
USAGE:
setu19(en, *harm_no, detune=0)
en in keV, possibly select a special harmonics, or detune [0/1] to a value
with a nicer beam shape but less flux
en in keV, possibly select a special harmonics, or detune [0/1] to a value
with a nicer beam shape but less flux
"""
g0 = dev.id_gap.readback.get()
@@ -168,30 +164,31 @@ def setu19(en, *harm_no, detune=0):
print("Moving Undulator gap to ", g, " mm")
else:
print("not a valid gap, do nothing")
if detune:
g =g *0.996
g = g * 0.996
print("moving to detuned gap value, slightly below max, about 0.15 % ")
#print("move disabled!!")
res = scans.umv(dev.id_gap, g, relative=False)
# print("move disabled!!")
res = scans.umv(dev.id_gap, g, relative=False)
return
##################################
def harmon_walk(estart=7.5, end_en=13):
import time
en = estart
ans ='y'
while en < end_en+0.5 and ans == 'y':
print(en)
ans = "y"
while en < end_en + 0.5 and ans == "y":
print(en)
setu19(en, 5)
time.sleep(2)
sete(en)
en = en+0.5
sete(en)
en = en + 0.5
ans = input("Next energy? y/n: ")
##################################
def gap_harm(e=12.4):
fitpar_u19 = np.array([2.17078531, 0.519452, -0.00720255])
@@ -217,7 +214,7 @@ def long_gscan(estart=7, end_en=20.5, g_low=4.5, g_high=9.0, nsteps=1500):
import time
import numpy as np
dirname = "/home/gac-x10sa/Data/"
dirname = "/sls/x10sa/config/commissioning/Data/"
print(
f"scanning the U19 gap from {estart} keV to {end_en} keV, for a gapsize from {g_low} to {g_high}"
@@ -225,29 +222,30 @@ def long_gscan(estart=7, end_en=20.5, g_low=4.5, g_high=9.0, nsteps=1500):
resol = (g_high - g_low) / nsteps
print(f"nsteps = {nsteps}; resolution is {resol} mm")
dock_area = bec.gui.new("LongGapScan")
wr = dock_area.new(bec.gui.available_widgets.Waveform)
wr = dock_area.new().new(bec.gui.available_widgets.Waveform)
mot = dev.id_gap
det = dev.lu_bpmsum
wr.plot(device_x=mot.name, device_y=det.name) ## names first !
wr.plot(x_name=mot.name, y_name=det.name) ## names first !
wr.x_label = mot.name
wr.y_label = det.name
g0 = dev.id_gap.readback.get()
### parameters
# g_low = 4.5 # 4.5
# g_high = 9.0 # 9.0
# nsteps = 1500 # res = 3 um
## now: probably do from 5 keV to ?? 30 keV ???
en = estart
while en < end_en:
sete(en)
time.sleep(1)
time.sleep(0.2)
rock()
print(f"setting energy to {en}")
time.sleep(2)
ds = scans.line_scan(
dev.id_gap, g_low, g_high, steps=nsteps, exp_time=0.8, relative=False
)
time.sleep(0.2)
ds = scans.line_scan(dev.id_gap, g_low, g_high, steps=nsteps, exp_time=0.1, relative=False)
gap_data = ds.scan.live_data.id_gap.id_gap.val
bpm_data = ds.scan.live_data.lu_bpmsum.lu_bpmsum.val
wr.plot(x=gap_data, y=bpm_data)
@@ -270,30 +268,28 @@ def gscan(centre=0, gomax=0, detune=0):
gscan(centre=1): go to centre of fit max
gscan(centre=1, gomax=1): go to max of intensity
gscan(centre=1,detune=1): position of slightly less flux with nicer beam shape
"""
"""
import time
dock_area = bec.gui.new()
wr = dock_area.new(bec.gui.available_widgets.Waveform)
wr = dock_area.new().new(bec.gui.available_widgets.Waveform)
mot = dev.id_gap
det = dev.lu_bpmsum
wr.plot(device_x=mot.name, device_y=det.name) ## names first !
wr.plot(x_name=mot.name, y_name=det.name) ## names first !
# wr.plot(x=mot.name,y=det.name) ### this comes later
wr.x_label = mot.name
wr.y_label = det.name
g0 = dev.id_gap.readback.get()
deltag = 0.05
ds = scans.line_scan(
dev.id_gap, -deltag, deltag, steps=30, exp_time=0.5, relative=True
)
ds = scans.line_scan(dev.id_gap, -deltag, deltag, steps=30, exp_time=0.5, relative=True)
gap_data = ds.scan.live_data.id_gap.id_gap.val
bpm_data = ds.scan.live_data.lu_bpmsum.lu_bpmsum.val
#maxy = max(bpm_data)
#indmax = np.argmax(bpm_data)
#gm = gap_data[indmax]
# maxy = max(bpm_data)
# indmax = np.argmax(bpm_data)
# gm = gap_data[indmax]
gcen, xm = fit_plot(gap_data, bpm_data, model="gauss")
@@ -304,16 +300,15 @@ def gscan(centre=0, gomax=0, detune=0):
print("gap off by ", g0 - gm, " mm")
if detune:
gm=gm*0.996
gm = gm * 0.996
print("moving to detuned gap value, slightly (0.15 %) below max")
if centre:
time.sleep(0.2)
if min(gap_data) <= gm <= max(gap_data):
if min(gap_data) <= gm <= max(gap_data):
scans.umv(dev.id_gap, gm, relative=False)
print("moving to ", gm, " mm")
else:
print("Fit too far off, try using option gomax=1")
return
return
+49
View File
@@ -0,0 +1,49 @@
#### find out about a certain class --
#### retrieve the struct of dictionaries
# if you know the attribute you are searching for:
def check_attr(obj, attr):
# att as string
attr = getattr(obj, attr)
if isinstance(attr, dict):
print("keys:", attr.keys())
print("values:", attr.values())
print("items:", attr.items())
# Automatically Detect All Dictionary Attributes:
def list_dict_attr_single(obj):
for attr_name, value in vars(obj).items():
if isinstance(value, dict):
print(f"\nDictionary attribute: {attr_name}")
print(" Keys:", list(value.keys()))
print(" Items:")
for key, val in value.items():
print(f" {key} -> {val}")
# Also Handle Nested Dictionaries:
def list_dict_attr(obj):
def print_dict(d, indent=0): # start with zero indentation
for key, value in d.items():
print(" " * indent + str(key) + ":", end=" ")
if isinstance(value, dict):
print()
print_dict(value, indent+1)
else:
print(value)
for attr_name, value in vars(obj).items():
if isinstance(value, dict):
print(f"\nDictionary attribute: {attr_name}")
print_dict(value)
+50 -22
View File
@@ -1,14 +1,23 @@
"""Get data from an h5 file or BEC history and perform fitting."""
import numpy as np
from lmfit.models import GaussianModel, LorentzianModel, VoigtModel, ConstantModel, LinearModel
from lmfit.models import (
GaussianModel,
LorentzianModel,
VoigtModel,
ConstantModel,
LinearModel,
)
from scipy.ndimage import gaussian_filter1d
import h5py
import matplotlib.pyplot as plt
def create_fit_parameters(
deriv: bool = False, model: str = "Voigt", baseline: str = "Linear", smoothing: None = None
deriv: bool = False,
model: str = "Voigt",
baseline: str = "Linear",
smoothing: None = None,
):
"""Store the fit parameters in a dictionary."""
# map input model to lmfit model name
@@ -45,7 +54,10 @@ def get_data_from_h5(signal_name: str = "lu_bpmsum"):
}
def get_data_from_history(history_index: int, signal_name: str = "lu_bpmsum"):
def get_data_from_history(
history_index: int,
signal_name: str = "lu_bpmsum",
):
"""Read data from the BEC history and return the X and Y data as arrays."""
scan = bec.history[history_index]
md = scan.metadata["bec"]
@@ -84,7 +96,10 @@ def process_data(data, fit_params):
else:
fitting_data = y_data
updated_data = {"y_to_fit": fitting_data, "signal_name": signal_name}
updated_data = {
"y_to_fit": fitting_data,
"signal_name": signal_name,
}
data.update(updated_data)
return data
@@ -109,15 +124,23 @@ def fit(data, fit_params):
params["base_slope"].set(value=0)
# Add peak-specific parameters
params.update(peak_model.guess(processed_data["y_to_fit"], x=processed_data["x_data"]))
params.update(
peak_model.guess(processed_data["y_to_fit"], x=processed_data["x_data"])
)
# Perform the fitting
lmfit_result = full_model.fit(processed_data["y_to_fit"], params, x=processed_data["x_data"])
lmfit_result = full_model.fit(
processed_data["y_to_fit"], params, x=processed_data["x_data"]
)
# Find the X that gives the max Y
max_index = np.argmax(processed_data["y_to_fit"])
x_max = processed_data["x_data"][max_index]
# Generate data for a smoothed fit curve
fit_xdata = np.linspace(np.min(data["x_data"]), np.max(data["x_data"]), 500)
fit_ydata = lmfit_result.eval(x=fit_xdata, params=lmfit_result.params)
# Collect results
return {
"model": fit_params["model"].__name__,
@@ -127,20 +150,15 @@ def fit(data, fit_params):
"chi_sq": lmfit_result.chisqr,
"lmfit_result": lmfit_result,
"x_max": x_max,
"fit_xdata": fit_xdata,
"fit_ydata": fit_ydata,
}
def plot_fitted_data(data, fit_result):
"""Plot the original data and the fitted model."""
plt.plot(data["x_data"], data["y_to_fit"], label="Data")
plt.plot(
data["x_data"],
fit_result["lmfit_result"].best_fit,
"-",
label=f"FWHM = {fit_result['fwhm']:.3f},"
f"Centre = {fit_result['centre']:.3f}, "
f"Height = {fit_result['height']:.3f}",
)
plt.plot(fit_result['fit_xdata'], fit_result['fit_ydata'], label="Fit")
plt.xlabel(data["motor_name"])
plt.ylabel(data["signal_name"])
plt.title(f"Scan {data['scan_number']}, fitted with {fit_result['model']}")
@@ -154,9 +172,9 @@ def select_bec_window(dock_area_name="Fitting"):
open_docks = bec.gui.windows
if open_docks.get(dock_area_name) is None:
dock_area = bec.gui.new(dock_area_name)
wf = dock_area.new(bec.gui.available_widgets.Waveform, object_name="Plot")
text_box = dock_area.new(
bec.gui.available_widgets.TextBox, object_name="Results", where="bottom"
wf = dock_area.new("Plot").new(bec.gui.available_widgets.Waveform)
text_box = dock_area.new("Results", position="bottom").new(
widget=bec.gui.available_widgets.TextBox
)
else:
wf = bec.gui.Fitting.Plot.Waveform
@@ -164,7 +182,11 @@ def select_bec_window(dock_area_name="Fitting"):
return wf, text_box
def plot_live_data_bec(motor_name, signal_name, window_name="Fitting"):
def plot_live_data_bec(
motor_name,
signal_name,
window_name="Fitting"
):
"""
Plotting live data for motor and signal using BEC.
@@ -186,10 +208,13 @@ def plot_live_data_bec(motor_name, signal_name, window_name="Fitting"):
wf.title = "Scan: Live scan"
wf.x_label = motor_name
wf.y_label = signal_name
wf.plot(device_x=motor_name, device_y=signal_name)
wf.plot(x_name=motor_name, y_name=signal_name)
def plot_fitted_data_bec(data, fit_result):
def plot_fitted_data_bec(
data,
fit_result,
):
"""
Plot fitted data and display fitting parameters in the specified window.
@@ -221,5 +246,8 @@ def plot_fitted_data_bec(data, fit_result):
wf.title = f"Scan: {data['scan_number']}"
wf.x_label = data["motor_name"]
wf.y_label = data["signal_name"]
wf.plot(x=data["x_data"], y=data["y_to_fit"], label="data")
wf.plot(x=data["x_data"], y=fit_result["lmfit_result"].best_fit, label="Fit to data")
wf.plot(x=data["x_data"], y=data["y_to_fit"], label="Data")
wf.plot(x=fit_result["fit_xdata"], y=fit_result["fit_ydata"], label="Fit")
wf.Fit.set(symbol_size = 0)
+19 -61
View File
@@ -74,7 +74,7 @@ def move_to_position(motor_device, motor_name: str, position: float, data: dict)
scans.umv(motor_device, motor_centre, relative=False)
msg = (
f"Position {position: .2f} is outside the scan range of "
f"{motor_min: .2f}to {motor_max: .2f}. "
f"{motor_min: .2f} to {motor_max: .2f}. "
f"Returning to centre of scan range {motor_centre: .3f}."
)
raise ValueError(msg)
@@ -249,79 +249,42 @@ def scan_bpm(bpmname):
# Open a dock area and set up the heatmaps
dock_area = bec.gui.new("XBPM_Scan")
wf5 = dock_area.new(bec.gui.available_widgets.Heatmap, object_name="Sum")
wf1 = dock_area.new(
bec.gui.available_widgets.Heatmap,
object_name="Ch1",
relative_to="Sum",
where="bottom",
wf5 = dock_area.new("Sum").new(bec.gui.available_widgets.Heatmap)
wf1 = dock_area.new("Ch1", relative_to="Sum", position="bottom").new(
bec.gui.available_widgets.Heatmap
)
wf3 = dock_area.new(
bec.gui.available_widgets.Heatmap,
object_name="Ch3",
relative_to="Ch1",
where="right",
wf3 = dock_area.new("Ch3", relative_to="Ch1", position="right").new(
bec.gui.available_widgets.Heatmap
)
wf4 = dock_area.new(
bec.gui.available_widgets.Heatmap,
object_name="Ch4",
relative_to="Ch3",
where="bottom",
wf4 = dock_area.new("Ch4", relative_to="Ch3", position="bottom").new(
bec.gui.available_widgets.Heatmap
)
wf2 = dock_area.new(
bec.gui.available_widgets.Heatmap,
object_name="Ch2",
relative_to="Ch1",
where="bottom",
wf2 = dock_area.new("Ch2", relative_to="Ch1", position="bottom").new(
bec.gui.available_widgets.Heatmap
)
wfscan = dock_area.new(bec.gui.available_widgets.ScanControl, object_name="ScanControl")
wfscan = dock_area.new("ScanControl").new(bec.gui.available_widgets.ScanControl)
cfg = getattr(BPMScans, bpmname)
wf1.x_label = cfg["x_name"]
wf1.y_label = cfg["y_name"]
wf1.plot(
device_x=cfg["x_name"],
device_y=cfg["y_name"],
device_z=cfg["z1_name"],
color_map="plasma",
)
wf1.plot(x_name=cfg["x_name"], y_name=cfg["y_name"], z_name=cfg["z1_name"], color_map="plasma")
wf2.x_label = cfg["x_name"]
wf2.y_label = cfg["y_name"]
wf2.plot(
device_x=cfg["x_name"],
device_y=cfg["y_name"],
device_z=cfg["z2_name"],
color_map="plasma",
)
wf2.plot(x_name=cfg["x_name"], y_name=cfg["y_name"], z_name=cfg["z2_name"], color_map="plasma")
wf3.x_label = cfg["x_name"]
wf3.y_label = cfg["y_name"]
wf3.plot(
device_x=cfg["x_name"],
device_y=cfg["y_name"],
device_z=cfg["z3_name"],
color_map="plasma",
)
wf3.plot(x_name=cfg["x_name"], y_name=cfg["y_name"], z_name=cfg["z3_name"], color_map="plasma")
wf4.x_label = cfg["x_name"]
wf4.y_label = cfg["y_name"]
wf4.plot(
device_x=cfg["x_name"],
device_y=cfg["y_name"],
device_z=cfg["z4_name"],
color_map="plasma",
)
wf4.plot(x_name=cfg["x_name"], y_name=cfg["y_name"], z_name=cfg["z4_name"], color_map="plasma")
wf5.x_label = cfg["x_name"]
wf5.y_label = cfg["y_name"]
wf5.plot(
device_x=cfg["x_name"],
device_y=cfg["y_name"],
device_z=cfg["z5_name"],
color_map="plasma",
)
wf5.plot(x_name=cfg["x_name"], y_name=cfg["y_name"], z_name=cfg["z5_name"], color_map="plasma")
# Run the scan
x_mot = cfg["x_device"]
y_mot = cfg["y_device"]
@@ -341,20 +304,15 @@ def optimise_kb(mirror):
# Open a dock area and set up the heatmaps
dock_area = bec.gui.new(mirror)
wf1 = dock_area.new(bec.gui.available_widgets.Heatmap, object_name="Heatmap")
wf1 = dock_area.new("Heatmap").new(bec.gui.available_widgets.Heatmap)
wfscan = dock_area.new(bec.gui.available_widgets.ScanControl, object_name="ScanControl")
wfscan = dock_area.new("ScanControl").new(bec.gui.available_widgets.ScanControl)
cfg = getattr(MirrorConfig, mirror)
wf1.x_label = cfg["bu_name"]
wf1.y_label = cfg["bd_name"]
wf1.plot(
device_x=cfg["bu_name"],
device_y=cfg["bd_name"],
device_z=cfg["z_name"],
color_map="plasma",
)
wf1.plot(x_name=cfg["bu_name"], y_name=cfg["bd_name"], z_name=cfg["z_name"], color_map="plasma")
# Run the scan
x_mot = cfg["x_device"]
+1
View File
@@ -81,6 +81,7 @@ def set_mirror_stripe(energy_ev):
def mono_pitch_scan(plot=True):
"""Scan the monochromator pitch and move to the peak."""
# Move to the calculated pitch value for the current energy
print("Starting Mono Pitch Scan.")
energy = get_current_energy()
pos = get_dcm_motors_positions(energy)
print(f"Setting DCM Pitch to default value of {pos['dcm_pitch']}")
+280
View File
@@ -0,0 +1,280 @@
"""Guards for preventing clashing devices in
the sample environment"""
# PD_guards2.py
from dataclasses import dataclass, field
from typing import Callable, List, Dict
# ----------------------------
# Exceptions
# ----------------------------
class GuardViolation(RuntimeError):
"""Raised when a guarded move is not allowed."""
# ----------------------------
# Guarded axis
# ----------------------------
class GuardedAxis:
""" Motor axis protected by guard policy """
def __init__(
self,
bec_name: str,
policy: Callable[[float], None],
config: Dict[str, float] = None
):
self.bec_name = bec_name
self.policy = policy
self.config = config or {}
self.mot = getattr(dev, self.bec_name)
@property
def actual(self) -> float:
"""Returns the current motor position"""
return self.mot.read()[self.bec_name]["value"]
def move(self, target: float):
"""Used to move a guarded axis to a target value"""
self.policy(target) # must raise if disallowed
scans.umv(self.mot, target, relative=False)
# ----------------------------
# Positioned device (IN / OUT)
# ----------------------------
@dataclass
class PositionedDevice:
"""Applies to devices that only have IN and OUT positions
Guards are defined by guard rules to ensure their safe operation"""
bec_name: str
inpos: float
outpos: float
tol: float = 0.01
guards: List[Callable[[], None]] = field(default_factory=list)
def __post_init__(self):
self.mot = getattr(dev, self.bec_name)
def _check_guards(self):
for g in self.guards:
g()
def mvin(self):
"""Move a positioned device to IN position"""
self._check_guards()
scans.umv(self.mot, self.inpos, relative=False)
def mvout(self):
"""Move a positioned device to OUT position"""
self._check_guards()
scans.umv(self.mot, self.outpos, relative=False)
def is_in(self):
"""Returns true if the device is IN"""
return abs(self.mot.read()[self.bec_name]["value"] - self.inpos) <= self.tol
def is_out(self):
"""Returns true if the device is OUT"""
return abs(self.mot.read()[self.bec_name]["value"] - self.outpos) <= self.tol
@dataclass
class MultiPositionDevice:
""" Devices that have multiple defined positions. Guards rules are defined to
ensure their safe operation"""
bec_name: str
positions: Dict[str, float] # {"out": 0.0, "scint": 10.0, "i1": 20.0}
tol: float = 0.01
guards: List[Callable[[], None]] = field(default_factory=list)
def __post_init__(self):
self.mot = getattr(dev, self.bec_name)
def _check_guards(self):
"""Check guard conditions"""
for g in self.guards:
g()
def move_to(self, state: str):
"""Move to one of the states defined in self.positions"""
if state not in self.positions:
raise ValueError(f"Unknown state '{state}'")
self._check_guards()
scans.umv(self.mot, self.positions[state], relative=False)
def is_at(self, state: str) -> bool:
"""Check if device is at a given state"""
if state not in self.positions:
raise ValueError(f"Unknown state '{state}'")
return abs(self.mot.read()[self.bec_name]["value"] - self.positions[state]) <= self.tol
@property
def actual(self) -> float:
"""Returns current motor position"""
return self.mot.read()[self.bec_name]["value"]
@property
def state(self) -> str:
"""Returns current state"""
for name, pos in self.positions.items():
if abs(self.mot.read()[self.bec_name]["value"] - pos) <= self.tol:
return name
return "unknown"
def is_clear(self):
"""Returns true if device is at OUT or below e.g. PARK"""
if "out" not in self.positions:
raise ValueError("MultiPosition device requires 'out' state")
return self.actual < (self.positions["out"] + self.tol)
# ----------------------------
# PD namespace (filled at runtime)
# ----------------------------
class PD:
"""Populated when the PD devices are initialised"""
pass
# ----------------------------
# Guard rules for BS_Z
# ----------------------------
# BS positioner must be in for BS_Z to move
def bs_z_requires_bs_pos_in():
"""Cannot move bs_z unless the BS positioner is in"""
if not PD.bs_pos.is_in():
raise GuardViolation("BS_Z cannot move unless beamstop positioner is IN")
def bs_z_range_check(target):
"""Checks that the target position is within limits"""
cfg = PD.bs_z.config
# Lower bound
if target < cfg["work_min"] and not is_sample_area_clear(beamstop=True):
raise GuardViolation(
f"Requested beamstop Z {target} is below working range minimum {cfg['work_min']}"
)
if target < cfg["min"]:
raise GuardViolation(
f"Requested beamstop Z {target} is below absolute minimum {cfg['min']}"
)
# Maximum position depend on backlight position
if PD.bl_pos.is_in():
if target > cfg["max_blin"]:
raise GuardViolation(
f"Requested beamstop Z value of {target} mm exceeds maximum allowed"
f"value of {cfg['max_blin']} while backlight is IN"
)
else:
if target > cfg["max_blout"]:
raise GuardViolation(
f"Requested beamstop Z value of {target} mm exceeds maximum allowed "
f"value of {cfg['max_blout']} mm"
)
def is_sample_area_clear(beamstop=True):
"""Check if the sample area is clear, raising GuardViolation if constraints are not met."""
if beamstop:
# Check collimator, and diagnostic device positions
if not PD.coll_y.is_clear():
raise GuardViolation("Sample area is not clear: Collimator is IN")
if not PD.diag_y.is_clear():
raise GuardViolation("Sample area is not clear: Diagnostic device is IN")
# Validate goniometer position
if not abs(PD.gon_x.actual - PD.gon_x.config["out"]) < PD.gon_x.config["tol"]:
raise GuardViolation("Sample area is not clear: Goniometer is IN")
else:
# Check that diagnostic (scintillator/i1) device is out
if not PD.diag_y.is_clear():
raise GuardViolation("Sample are is not clear: Diagnostic device is IN")
return True
def bs_z_policy(target):
"""Defines the policy for bs_z operation"""
# Beamstop z can only move when the positioner is in
bs_z_requires_bs_pos_in()
# Check the allowed range for bs_z
bs_z_range_check(target)
return True
def gon_x_policy(target):
"""Defines the policy for gon_x operation"""
is_sample_area_clear(beamstop=False)
bs_z_above_work_min()
return True
def bs_pos_requires_bs_z_safe():
"""bs_pos can only move when bs_z is at the safe position"""
safe = PD.bs_z.config["safe"]
actual = PD.bs_z.actual
tol = 0.1
if abs(actual - safe) > tol:
raise GuardViolation(f"Beamstop positioner can only move when BS_Z is at {safe} mm")
def bs_z_above_work_min():
"""work_min specifies the minimum bs_z value that is
outside of the sample area i.e. no clashes with diagnostic
device or collimator"""
work_min = PD.bs_z.config["work_min"]
if PD.bs_z.actual < work_min:
raise GuardViolation(f"BS_Z must be greater than {work_min} mm")
def bs_z_below_max_blin():
"""Maximum bs_z vale when the backlight is in"""
max_blin = PD.bs_z.config["max_blin"]
if PD.bs_z.actual > max_blin:
raise GuardViolation(f"BS_Z must be less than {max_blin} mm")
def gonio_is_out():
"""Maximum bs_z value when the backlight is out"""
if not abs(PD.gon_x.actual - PD.gon_x.config["out"]) < PD.gon_x.config["tol"]:
raise GuardViolation(f"Goniometer must be OUT ({PD.gon_x.config['out']} mm)")
def get_policy_for_axis(bec_name):
"""Specify the policy for guarded axis"""
policy_registry = {"bs_z": bs_z_policy, "gon_x": gon_x_policy}
return policy_registry.get(bec_name, lambda target: True)
def init_collision_guards():
"""Add the guard rules for positioned devices"""
PD.bs_pos.guards.append(bs_pos_requires_bs_z_safe)
PD.bl_pos.guards.append(bs_z_below_max_blin)
PD.coll_y.guards.append(bs_z_above_work_min)
PD.diag_y.guards.append(bs_z_above_work_min)
PD.diag_y.guards.append(gonio_is_out)
PD.diag_y.guards.append(bs_z_requires_bs_pos_in)
def init_positioned_devices():
"""Initialises the positioned devices"""
file = "/sls/x10sa/config/bec/production/pxii_bec/pxii_bec/device_configs/pxii-autogenerated.yaml"
build_pd(file)
init_collision_guards()
print(f"Defined positions for devices have been updated from {file}")
+10 -158
View File
@@ -1,10 +1,7 @@
"""File to store beamline parameters and defaults"""
from dataclasses import dataclass
from typing import Callable
import numpy as np
import yaml
@@ -16,7 +13,7 @@ class EnergyDefaults:
min_energy_ev = 4800
max_energy_ev = 30002
beam_offset = 6
signals = {"sig1": dev.lu_bpmsum, "sig2": dev.bsc_bpmsum, "sig3": dev.bcu_bpmsum}
signals = {"sig1": dev.lu_bpmsum, "sig2": dev.ss_bpmsum, "sig3": dev.bcu_bpmsum}
energy = dev.dcm_bragg
mono_pitch = dev.dcm_pitch
mono_perp = dev.dcm_perp
@@ -148,15 +145,15 @@ class BPMScans:
"y_device": dev.lu_bpm_y,
}
bsc = {
"x_name": dev.bsc_bpm_x.name,
"y_name": dev.bsc_bpm_y.name,
"z1_name": dev.bsc_bpm1.name,
"z2_name": dev.bsc_bpm2.name,
"z3_name": dev.bsc_bpm3.name,
"z4_name": dev.bsc_bpm4.name,
"z5_name": dev.bsc_bpmsum.name,
"x_device": dev.bsc_bpm_x,
"y_device": dev.bsc_bpm_y,
"x_name": dev.ss_bpm_x.name,
"y_name": dev.ss_bpm_y.name,
"z1_name": dev.ss_bpm1.name,
"z2_name": dev.ss_bpm2.name,
"z3_name": dev.ss_bpm3.name,
"z4_name": dev.ss_bpm4.name,
"z5_name": dev.ss_bpmsum.name,
"x_device": dev.ss_bpm_x,
"y_device": dev.ss_bpm_y,
}
bcu = {
"x_name": dev.bcu_bpm_x.name,
@@ -191,148 +188,3 @@ class MirrorConfig:
}
@dataclass
class PositionedDevice:
"""Class for devices with defined in and out positions"""
device_name: str
type: str
name: str
inpos: float
outpos: float
tol: float
mot: str
reader: Callable[[], float]
@property
def actual(self):
"""Returns current motor position"""
return self.reader()
def checkin(self):
"""Returns True if motor in in the 'in' position"""
return abs(self.actual - self.inpos) <= self.tol
def mvin(self):
"""Moves motor to the 'in' position"""
scans.umv(self.mot, self.inpos, relative=False)
def mvout(self):
"""Moves motor to the 'out' position"""
scans.umv(self.mot, self.outpos, relative=False)
def status(self):
""" Check if device is in or out or moving"""
positions = ("in", "out", "moving", "undefined")
target_in = self.inpos
target_out = self.outpos
actual = self.actual
delta_in = actual - target_in
delta_out = actual - target_out
# Check if motor is moving
if "Signal" in self.type:
moving = 0
elif "Motor" in self.type:
d = getattr(dev, self.device_name)
moving = d.motor_is_moving.get()
if moving:
pos = positions[2]
return {"position": pos.upper(),
"name": self.name,
"moving": moving}
if abs(delta_in) > self.tol and abs(delta_out) > self.tol:
pos = positions[3]
return {"position": pos.upper(),
"name": self.name,
"actual": actual,
"moving": moving}
elif abs(delta_in) <= self.tol:
target = self.inpos
pos = positions[0]
delta = delta_in
elif abs(delta_out) <= self.tol:
target = self.outpos
pos = positions[1]
delta = delta_out
return {
"name": self.name,
"position": pos.upper(),
"target": target,
"actual": actual,
"delta": delta,
"tol": self.tol,
"moving": moving,
}
def report(self):
""" Print status of motor """
s = self.status()
if s['position'] == "UNDEFINED":
return (f"{s['name']:15s}: "
f"{s['position']} "
f"position {s['actual']:.3f}")
elif s['position'] == "MOVING":
return (f"{s['name']:15s}: "
f"{s['position']} ")
else:
return (
f"{s['name']:15s}: "
f"[{s['position']}] "
f"actual = {s['actual']:.3f} "
f"target = {s['target']:.3f} "
f"delta = {s['delta']:.3f}"
)
@dataclass(frozen=True)
class PD:
"""Class for positioned device positions"""
def build_pd(yaml_file):
"""Takes the in and out values from the yaml file
and adds them to the PD class
"""
with open(yaml_file, encoding="utf-8") as f:
data = yaml.safe_load(f)
for device_name, cfg in data.items():
# Skip devices without userParameter
user = cfg.get("userParameter")
if not user:
continue
# Set tolerance
if "tol" not in user:
user["tol"] = 0.01
try:
dev_obj = getattr(dev, device_name)
except:
raise KeyError(f"Device {device_name} not found in device list")
desc = cfg.get("description")
type = cfg.get("deviceClass")
target = PositionedDevice(
device_name=device_name,
type = type,
name=desc,
inpos=user["in"],
outpos=user["out"],
tol=user["tol"],
mot=dev_obj,
reader=lambda d=dev_obj, n=device_name: d.read()[n]["value"],
)
setattr(PD, device_name, target)
def init_positioned_devices():
"""Initialises the positioned devices"""
file = (
"/sls/x10sa/config/bec/production/pxii_bec/pxii_bec/device_configs/pxii-autogenerated.yaml"
)
build_pd(file)
print("Defined positions for devices have been updated from pxii-autogenerated.yaml")
+75
View File
@@ -0,0 +1,75 @@
#!/usr/bin/env bash
#
# Script Name: set_kbox.sh
# Description: Sets a value on a given device, such as scinti, diode, colli
#
set -euo pipefail
#######################################
# Usage
#######################################
usage() {
echo "Usage: $(basename "$0") <device_name> <set_value>"
echo
echo "Example:"
echo " $(basename "$0") colli_in 41.5"
echo " $(basename "$0") colli_out 20."
echo " $(basename "$0") scinti_in 40."
echo " $(basename "$0") diode_in 44."
echo " $(basename "$0") diode_out 20. or"
echo " $(basename "$0") scinti_out 20."
exit 1
}
#######################################
# Validate Arguments
#######################################
if [[ $# -ne 2 ]]; then
usage
fi
DEVICE_NAME="$1"
SET_VALUE="$2"
if ! [[ "$SET_VALUE" =~ ^[0-9]+$ ]]; then
echo "Error: set_value must be numeric"
exit 1
fi
#######################################
# Main
#######################################
main() {
echo "Device: $DEVICE_NAME"
echo "Value : $SET_VALUE"
# --- Your logic here ---
# Example placeholder:
if [[ $DEVICE_NAME == "colli_in" ]]; then
echo "caput X10SA-ES-COL:POS-SET-SEQ.DO2 $SET_VALUE"
fi
if [[ $DEVICE_NAME == "colli_out" ]]; then
echo "caput X10SA-ES-COL:POS-SET-SEQ.DO1 $SET_VALUE"
fi
#
if [[ $DEVICE_NAME == "scinti_in" ]]; then
echo "caput X10SA-ES-SCL:POS-SET-SEQ.DO2 $SET_VALUE"
fi
if [[ $DEVICE_NAME == "diode_in" ]]; then
echo "caput X10SA-ES-SCL:POS-SET-SEQ.DO3 $SET_VALUE"
fi
if [[ $DEVICE_NAME == "scinti_out" || $DEVICE_NAME == "diode_out" ]]; then
echo "caput X10SA-ES-SCL:POS-SET-SEQ.DO1 $SET_VALUE"
fi
#
echo "Setting device '$DEVICE_NAME' to '$SET_VALUE'..."
# Simulate success
echo "Done."
}
main
+6
View File
@@ -0,0 +1,6 @@
print("Hello World")
try:
print(PD.coll_y.state)
print("success")
except Exception as e:
print(f"Error {e}")
+59
View File
@@ -0,0 +1,59 @@
"""
update_PD_from_yaml.py
Creates PositionedDevice, MultiPositionDevice and GuardedAxis
instances from YAML configuration.
"""
import yaml
def build_pd(yaml_file):
"""Takes the defined positions from the device yaml file
and adds them to the PD class
"""
pos_devs = []
mp_devs = []
ga_devs = []
with open(yaml_file, encoding="utf-8") as f:
data = yaml.safe_load(f)
for bec_name, cfg in data.items():
# Skip devices without userParameter
user = cfg.get("userParameter")
if not user:
continue
# ------------------------------------------------------------------
# Positioned device
# ------------------------------------------------------------------
if user["type"] == "positioner":
pos_devs.append(bec_name)
posdev = PositionedDevice(bec_name=bec_name, inpos=1.0, outpos=0.0)
setattr(PD, bec_name, posdev)
# ------------------------------------------------------------------
# Multi-position device
# ------------------------------------------------------------------
elif user["type"] == "multi-position":
mp_devs.append(bec_name)
positions = {k: v for k, v in user.items() if k != "type"}
mpdev = MultiPositionDevice(bec_name=bec_name, positions=positions)
setattr(PD, bec_name, mpdev)
# ------------------------------------------------------------------
# Guarded device
# ------------------------------------------------------------------
elif user["type"] == "guarded":
ga_devs.append(bec_name)
config = {k: v for k, v in user.items() if k != "type"}
gadev = GuardedAxis(
bec_name=bec_name, policy=get_policy_for_axis(bec_name), config=config
)
setattr(PD, bec_name, gadev)
print(f"Positioned devices: {pos_devs}")
print(f"Guarded axes: {ga_devs}")
print(f"Multi position devices: {mp_devs}")
+1 -5
View File
@@ -6,7 +6,7 @@ build-backend = "hatchling.build"
name = "pxii_bec"
version = "0.0.0"
description = "A plugin repository for BEC"
requires-python = ">=3.11"
requires-python = ">=3.10"
classifiers = [
"Development Status :: 3 - Alpha",
"Programming Language :: Python :: 3",
@@ -25,7 +25,6 @@ dev = [
"pytest-random-order",
"ophyd_devices",
"bec_server",
"requests-mock",
]
[project.entry-points."bec"]
@@ -77,6 +76,3 @@ good-names-rgxs = [
".*_2D.*",
".*_1D.*",
]
[tool.ruff]
line-length = 100
-2
View File
@@ -1,5 +1,3 @@
"""A mock smargopolo REST interface with mock motoers, for testing devices against"""
import asyncio
import random
import time
-58
View File
@@ -1,58 +0,0 @@
from copy import copy
from threading import RLock
from unittest.mock import ANY
import pytest
class MockServer:
def __init__(self) -> None:
self.lock = RLock()
self.mock_data = {
"x": {"pos": 1.0},
"y": {"pos": 1.0},
"z": {"pos": 1.0},
"u": {"pos": 1.0},
"vel_u_deg_s": {"pos": 1.0},
}
def get(self, endpoint):
with self.lock:
return copy(self.mock_data)
def put(self, params: dict | None = None, body: dict | None = None):
with self.lock:
assert body is not None
for k, v in body.items():
self.mock_data[k]["pos"] = v
@pytest.fixture
def aerotech():
mock_server = MockServer()
from pxii_bec.devices.aerotech import Aerotech
s = Aerotech(name="aerotech", prefix="http://test-aerotech.psi.ch")
s.controller._rest_get = mock_server.get
s.controller._rest_post = mock_server.put
yield s
s.controller._stop_monitor_readback_event.set()
class TestAerotech:
def test_aerotech_read(self, aerotech):
aerotech.wait_for_connection()
reading = aerotech.read()
assert dict(reading) == {
"aerotech_x": {"value": 1.0, "timestamp": ANY},
"aerotech_y": {"value": 1.0, "timestamp": ANY},
"aerotech_z": {"value": 1.0, "timestamp": ANY},
"aerotech_u": {"value": 1.0, "timestamp": ANY},
"aerotech_vel_u_deg_s": {"value": 1.0, "timestamp": ANY},
}
def test_aerotech_set_with_status(self, aerotech):
aerotech.wait_for_connection()
st = aerotech.x.set(5.0)
st.wait(timeout=1)
assert aerotech.x.get() == 5.0
-51
View File
@@ -1,51 +0,0 @@
from copy import copy
from threading import RLock
from unittest.mock import ANY
import pytest
class MockServer:
def __init__(self) -> None:
self.lock = RLock()
self.mock_data = {"SHX": 1.0, "SHY": 1.0, "SHZ": 1.0, "PHI": 1.0, "CHI": 1.0}
def get(self, endpoint):
with self.lock:
return copy(self.mock_data)
def put(self, params: dict | None = None, body: dict | None = None):
with self.lock:
assert params is not None
self.mock_data.update(params)
@pytest.fixture
def smargon():
mock_server = MockServer()
from pxii_bec.devices.smargopolo_smargon import Smargon
s = Smargon(name="smargon", prefix="http://test-smargopolo.psi.ch")
s.controller._rest_get = mock_server.get
s.controller._rest_put = mock_server.put
yield s
s.controller._stop_monitor_readback_event.set()
class TestSmargon:
def test_smargon_read(self, smargon):
smargon.wait_for_connection()
reading = smargon.read()
assert dict(reading) == {
"smargon_x": {"value": 1.0, "timestamp": ANY},
"smargon_y": {"value": 1.0, "timestamp": ANY},
"smargon_z": {"value": 1.0, "timestamp": ANY},
"smargon_phi": {"value": 1.0, "timestamp": ANY},
"smargon_chi": {"value": 1.0, "timestamp": ANY},
}
def test_smargon_set_with_status(self, smargon):
smargon.wait_for_connection()
st = smargon.x.set(5.0)
st.wait(timeout=1)
assert smargon.x.get() == 5.0