Compare commits

..

1 Commits

Author SHA1 Message Date
cc2e0ecaa3 refactor: converted to new plugin structure 2024-03-10 21:09:06 +01:00
241 changed files with 12728 additions and 39242 deletions

View File

@@ -1,9 +0,0 @@
# Do not edit this file!
# 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.2
_src_path: https://github.com/bec-project/plugin_copier_template.git
make_commit: false
project_name: csaxs_bec
widget_plugins_input: []

View File

@@ -1,3 +0,0 @@
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
semantic-release changelog -D version_variable=$SCRIPT_DIR/../../semantic_release/__init__.py:__version__
semantic-release version -D version_variable=$SCRIPT_DIR/../../semantic_release/__init__.py:__version__

View File

@@ -1,3 +1,2 @@
black --line-length=100 $(git diff --cached --name-only --diff-filter=ACM -- '*.py')
isort --line-length=100 --profile=black --multi-line=3 --trailing-comma $(git diff --cached --name-only --diff-filter=ACM -- '*.py')
git add $(git diff --cached --name-only --diff-filter=ACM -- '*.py')
black --line-length=100 $(git diff --cached --name-only --diff-filter=ACM)
git add $(git diff --cached --name-only --diff-filter=ACM)

View File

@@ -1,85 +0,0 @@
name: CI for csaxs_bec
on:
push:
pull_request:
workflow_dispatch:
inputs:
BEC_WIDGETS_BRANCH:
description: "Branch of BEC Widgets to install"
required: false
type: string
default: "main"
BEC_CORE_BRANCH:
description: "Branch of BEC Core to install"
required: false
type: string
default: "main"
OPHYD_DEVICES_BRANCH:
description: "Branch of Ophyd Devices to install"
required: false
type: string
default: "main"
BEC_PLUGIN_REPO_BRANCH:
description: "Branch of the BEC Plugin Repository to install"
required: false
type: string
default: "main"
PYTHON_VERSION:
description: "Python version to use"
required: false
type: string
default: "3.11"
permissions:
pull-requests: write
jobs:
test:
runs-on: ubuntu-latest
env:
QTWEBENGINE_DISABLE_SANDBOX: 1
QT_QPA_PLATFORM: "offscreen"
steps:
- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "${{ inputs.PYTHON_VERSION || '3.11' }}"
- name: Checkout BEC Core
run: git clone --depth 1 --branch "${{ inputs.BEC_CORE_BRANCH || 'main' }}" https://github.com/bec-project/bec.git ./bec
- name: Checkout Ophyd Devices
run: git clone --depth 1 --branch "${{ inputs.OPHYD_DEVICES_BRANCH || 'main' }}" https://github.com/bec-project/ophyd_devices.git ./ophyd_devices
- name: Checkout BEC Widgets
run: git clone --depth 1 --branch "${{ inputs.BEC_WIDGETS_BRANCH || 'main' }}" https://github.com/bec-project/bec_widgets.git ./bec_widgets
- name: Checkout BEC Plugin Repository
uses: actions/checkout@v4
with:
repository: bec/csaxs_bec
ref: "${{ inputs.BEC_PLUGIN_REPO_BRANCH || github.head_ref || github.sha }}"
path: ./csaxs_bec
- name: Install dependencies
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y libgl1 libegl1 x11-utils libxkbcommon-x11-0 libdbus-1-3 xvfb
sudo apt-get -y install libnss3 libxdamage1 libasound2t64 libatomic1 libxcursor1
- name: Install Python dependencies
shell: bash
run: |
pip install uv
uv pip install --system -e ./ophyd_devices
uv pip install --system -e ./bec/bec_lib[dev]
uv pip install --system -e ./bec/bec_ipython_client
uv pip install --system -e ./bec/bec_server[dev]
uv pip install --system -e ./bec_widgets[dev,pyside6]
uv pip install --system -e ./csaxs_bec
- name: Run Pytest with Coverage
id: coverage
run: pytest --random-order --cov=./csaxs_bec --cov-config=./csaxs_bec/pyproject.toml --cov-branch --cov-report=xml --no-cov-on-fail ./csaxs_bec/tests/ || test $? -eq 5

3
.gitignore vendored
View File

@@ -8,9 +8,6 @@
**/.pytest_cache
**/*.egg*
# recovery_config files
recovery_config_*
# file writer data
**.h5

View File

@@ -1,20 +0,0 @@
include:
- project: bec/awi_utils
file: /templates/plugin-repo-template.yml
inputs:
name: "csaxs"
target: "csaxs_bec"
branch: $CHILD_PIPELINE_BRANCH
pages:
stage: Deploy
needs: []
variables:
TARGET_BRANCH: $CI_COMMIT_REF_NAME
rules:
- if: "$CI_COMMIT_TAG != null"
variables:
TARGET_BRANCH: $CI_COMMIT_TAG
- if: '$CI_COMMIT_REF_NAME == "main" && $CI_PROJECT_PATH == "bec/csaxs_bec"'
script:
- curl -X POST -d "branches=$CI_COMMIT_REF_NAME" -d "token=$RTD_TOKEN" https://readthedocs.org/api/v2/webhook/sls-csaxs/270162/

View File

@@ -1,29 +0,0 @@
# .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-20.04
tools:
python: "3.10"
jobs:
pre_install:
- pip install .
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# If using Sphinx, optionally build your docs in additional formats such as PDF
# formats:
# - pdf
# Optionally declare the Python requirements required to build your docs
python:
install:
- requirements: docs/requirements.txt

29
LICENSE
View File

@@ -1,29 +0,0 @@
BSD 3-Clause License
Copyright (c) 2025, Paul Scherrer Institute
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -6,12 +6,15 @@ You might want to run cSAXS copy scripts before in case you want to have the for
## Overview
- Clone cSAXS BEC repository into e-account (e.g. into ~/Data10/software/.)
- Start Epics iocs
- Start BEC, BEC server and load/modify the device config with relevant hardware
- BEC commands
1. Clone cSAXS BEC repository into e-account (e.g. into ~/Data10/software/.)
2. Install BEC
3. Start Epics iocs
4. Start BEC, BEC server and load/modify the device config with relevant hardware
5. BEC commands
6. Start BEC widgets (GUI for motor control, eiger live plot)
7. Troubleshooting and common problems
## Clone cSAXS BEC repository
## 1. Clone cSAXS BEC repository
Clone the current cSAXS BEC repository from GIT into the new e-account.
Create directory
@@ -21,9 +24,19 @@ cd ~/Data10/software
```
Clone repository
```bash
git clone https://gitlab.psi.ch/bec/csaxs_bec.git
git clone https://gitlab.psi.ch/bec/csaxs-bec.git
```
## Start epics iocs
## 2. Install BEC
There is a bash sript in the followin directory.
Go to the directory and run the script on pc15543 logged in as the e-account (BEC server):
```bash
ssh pc15543
cd ~/Data10/software/csaxs-bec/bin/
./setup_bec.sh
```
## 3. Start epics iocs
You can start up the iocs while the *./setup_bec.sh* script is running. Be aware though that the scripts requires you to interact with it.
@@ -81,7 +94,7 @@ iocsh -7.0.6 startup.script
```
Be aware -7.0.6 is referring to the current epics version and might change in future (SLS 2.0)
## Start BEC, BEC server and load device config
## 4. Start BEC, BEC server and load device config
Step 1 needs to have finished for continuing with these steps.
What remains now is to start the bec server. Connect to pc15543 and open a new terminal to run:
@@ -114,7 +127,7 @@ bec.config.save_current_session('~/Data10/software/current_config.yaml')
```
The second command is helpful if you adjust limits of motors, which will then be stored in the config and loaded if a reload of the configuration is needed.
## BEC commands
## 5. BEC commands
A number of commands that are useful:
@@ -134,3 +147,41 @@ scans.line_scan(dev.samx, -1, 1, dev.samy, -1, 1, steps=20, exp_time=0.5, readou
scans.sgalil_grid(start_y = , end_y = , interval_y = , start_x=, end_x=, interval_x =, exp_time=0.5, readout_time=3e-3, relative=True)
```
## 6. Start BEC widgets (GUI for motor control, eiger live plot)
To start the BEC widgets, the first step is to make the bec_widgets_venv using the start startup script.
Follow the commands below:
``` bash
cd ~/Data10/software/csaxs-bec/bin
./setup_bec_widgets.sh
```
Afterwards, activate the environment on either cons-01 comp-1/2
``` bash
cd ~/Data10/software/
source activate bec_widgets_venv/bin/activate
```
Each Plot needs their own shell with activate environment
1. Eiger Plot
``` bash
cd ~/Data10/software/bec-widgets/bec_widgets/examples/eiger_plot
python eiger_plot.py
```
2. Motor Controller
``` bash
cd ~/Data10/software/bec-widgets/bec_widgets/examples/motor_movement
python motor_example.py --config csaxs_config.yaml
```
## 7. Troubleshooting and common problems
Sometimes the data backend for the Eiger gets stuck or misses frames, this will result in an error
``` python
raise EigerTimeoutError(
ophyd_devices.epics.devices.eiger9m_csaxs.EigerTimeoutError: Reached timeout with detector state 1, std_daq state FINISHED and received frames of 100 for the file writer)
```
This happens more likely after CTRL C of a scan. To recover from this more reliably, perform the an acquisition in burst mode with 100 frames, little exposure until no error message is raised after. This can be up to 3 times from former experience.
``` bash
scans.acquire(exp_time=0.02, frames_per_trigger=100, readout_time= 3e-3)
```
Afterwards, you should be good to continue with 2D gridscans.

1
bec_plugins/__init__.py Normal file
View File

@@ -0,0 +1 @@
from .bec_client import *

View File

@@ -0,0 +1 @@
from .plugins import *

View File

@@ -0,0 +1,245 @@
from bec_lib.devicemanager import Device
from bec_lib.scan_report import ScanReport
# pylint:disable=undefined-variable
# pylint: disable=too-many-arguments
def dscan(
motor1: Device, m1_from: float, m1_to: float, steps: int, exp_time: float, **kwargs
) -> ScanReport:
"""Relative line scan with one device.
Args:
motor1 (Device): Device that should be scanned.
m1_from (float): Start position relative to the current position.
m1_to (float): End position relative to the current position.
steps (int): Number of steps.
exp_time (float): Exposure time.
Returns:
ScanReport: Status object.
Examples:
>>> dscan(dev.motor1, -5, 5, 10, 0.1)
"""
return scans.line_scan(
motor1, m1_from, m1_to, steps=steps, exp_time=exp_time, relative=True, **kwargs
)
def d2scan(
motor1: Device,
m1_from: float,
m1_to: float,
motor2: Device,
m2_from: float,
m2_to: float,
steps: int,
exp_time: float,
**kwargs
) -> ScanReport:
"""Relative line scan with two devices.
Args:
motor1 (Device): First device that should be scanned.
m1_from (float): Start position of the first device relative to its current position.
m1_to (float): End position of the first device relative to its current position.
motor2 (Device): Second device that should be scanned.
m2_from (float): Start position of the second device relative to its current position.
m2_to (float): End position of the second device relative to its current position.
steps (int): Number of steps.
exp_time (float): Exposure time
Returns:
ScanReport: Status object.
Examples:
>>> d2scan(dev.motor1, -5, 5, dev.motor2, -8, 8, 10, 0.1)
"""
return scans.line_scan(
motor1,
m1_from,
m1_to,
motor2,
m2_from,
m2_to,
steps=steps,
exp_time=exp_time,
relative=True,
**kwargs
)
def ascan(motor1, m1_from, m1_to, steps, exp_time, **kwargs):
"""Absolute line scan with one device.
Args:
motor1 (Device): Device that should be scanned.
m1_from (float): Start position.
m1_to (float): End position.
steps (int): Number of steps.
exp_time (float): Exposure time.
Returns:
ScanReport: Status object.
Examples:
>>> ascan(dev.motor1, -5, 5, 10, 0.1)
"""
return scans.line_scan(
motor1, m1_from, m1_to, steps=steps, exp_time=exp_time, relative=False, **kwargs
)
def a2scan(motor1, m1_from, m1_to, motor2, m2_from, m2_to, steps, exp_time, **kwargs):
"""Absolute line scan with two devices.
Args:
motor1 (Device): First device that should be scanned.
m1_from (float): Start position of the first device.
m1_to (float): End position of the first device.
motor2 (Device): Second device that should be scanned.
m2_from (float): Start position of the second device.
m2_to (float): End position of the second device.
steps (int): Number of steps.
exp_time (float): Exposure time
Returns:
ScanReport: Status object.
Examples:
>>> a2scan(dev.motor1, -5, 5, dev.motor2, -8, 8, 10, 0.1)
"""
return scans.line_scan(
motor1,
m1_from,
m1_to,
motor2,
m2_from,
m2_to,
steps=steps,
exp_time=exp_time,
relative=False,
**kwargs
)
def dmesh(motor1, m1_from, m1_to, m1_steps, motor2, m2_from, m2_to, m2_steps, exp_time, **kwargs):
"""Relative mesh scan (grid scan) with two devices.
Args:
motor1 (Device): First device that should be scanned.
m1_from (float): Start position of the first device relative to its current position.
m1_to (float): End position of the first device relative to its current position.
m1_steps (int): Number of steps for motor1.
motor2 (Device): Second device that should be scanned.
m2_from (float): Start position of the second device relative to its current position.
m2_to (float): End position of the second device relative to its current position.
m2_steps (int): Number of steps for motor2.
exp_time (float): Exposure time
Returns:
ScanReport: Status object.
Examples:
>>> dmesh(dev.motor1, -5, 5, 10, dev.motor2, -8, 8, 10, 0.1)
"""
return scans.grid_scan(
motor1,
m1_from,
m1_to,
m1_steps,
motor2,
m2_from,
m2_to,
m2_steps,
exp_time=exp_time,
relative=True,
)
def amesh(motor1, m1_from, m1_to, m1_steps, motor2, m2_from, m2_to, m2_steps, exp_time, **kwargs):
"""Absolute mesh scan (grid scan) with two devices.
Args:
motor1 (Device): First device that should be scanned.
m1_from (float): Start position of the first device.
m1_to (float): End position of the first device.
m1_steps (int): Number of steps for motor1.
motor2 (Device): Second device that should be scanned.
m2_from (float): Start position of the second device.
m2_to (float): End position of the second device.
m2_steps (int): Number of steps for motor2.
exp_time (float): Exposure time
Returns:
ScanReport: Status object.
Examples:
>>> amesh(dev.motor1, -5, 5, 10, dev.motor2, -8, 8, 10, 0.1)
"""
return scans.grid_scan(
motor1,
m1_from,
m1_to,
m1_steps,
motor2,
m2_from,
m2_to,
m2_steps,
exp_time=exp_time,
relative=False,
)
def umv(*args) -> ScanReport:
"""Updated absolute move (i.e. blocking) for one or more devices.
Returns:
ScanReport: Status object.
Examples:
>>> umv(dev.samx, 1)
>>> umv(dev.samx, 1, dev.samy, 2)
"""
return scans.umv(*args, relative=False)
def umvr(*args) -> ScanReport:
"""Updated relative move (i.e. blocking) for one or more devices.
Returns:
ScanReport: Status object.
Examples:
>>> umvr(dev.samx, 1)
>>> umvr(dev.samx, 1, dev.samy, 2)
"""
return scans.umv(*args, relative=True)
def mv(*args) -> ScanReport:
"""Absolute move for one or more devices.
Returns:
ScanReport: Status object.
Examples:
>>> mv(dev.samx, 1)
>>> mv(dev.samx, 1, dev.samy, 2)
"""
return scans.mv(*args, relative=False)
def mvr(*args) -> ScanReport:
"""Relative move for one or more devices.
Returns:
ScanReport: Status object.
Examples:
>>> mvr(dev.samx, 1)
>>> mvr(dev.samx, 1, dev.samy, 2)
"""
return scans.mv(*args, relative=True)

View File

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@@ -0,0 +1,2 @@
from .load_additional_correction import lamni_read_additional_correction
from .x_ray_eye_align import LamNI, XrayEyeAlign, MagLamNI, DataDrivenLamNI

View File

@@ -0,0 +1,269 @@
def bl_show_all '{
local gap
printf("beamline status at %s:\n",date())
if (!_bl_hall_temperature_ok()) {
bl_hall_temperature
printf("\n")
}
if (_bl_sls_status_unusual()) {
bl_sls_status
} else {
bl_ring_current
}
bl_chk_beam _show
printf("\n")
printf("U19 ID gap : ",gap)
gap = _id_get_gap_mm()
if (gap >= 8) {
text_bf
}
printf("%.3f mm\n",gap)
text_non_bf
if (!_id_loss_rate_ok()) {
id_loss_rate
}
bl_shutter_status
if (_bl_cvd_filter_open()) {
text_bf
printf("CVD diamond filter : open / out\n")
text_non_bf
}
if (!_bl_xbox_valve_es1_open()) {
bl_xbox_valve_es1 _show
}
if (_bl_ln2_non_standard()) {
text_bf
printf("\nNon standard liquid nitrogen cooling-warning parameters occur. Please report this to your local contact.\n")
text_non_bf
printf("The macro bl_ln2_warn can be used to control this e-mail warning feature.\n")
bl_ln2_warn "show"
printf("\n")
}
printf("\n")
bl_flight_tube_pressure
printf("\n")
bl_attended _show
_bl_check_alarm_records(1,1)
printf("\n")
bl_op_msg
}'
def _bl_hall_temperature_ok() '{
local temp_ok
local stat
temp_ok = 1
# EH T02 average temperature
stat = epics_get("ILUUL-02AV:TEMP")
if ((stat < 23.0) || (stat > 26.0)) {
temp_ok = 0
}
# EH T02 temperature at T0204 axis 16
stat = epics_get("ILUUL-0200-EB104:TEMP")
if ((stat < 23.0) || (stat > 26.0)) {
temp_ok = 0
}
# EH T02 temperature at T0205 axis 18
stat = epics_get("ILUUL-0200-EB105:TEMP")
if ((stat < 23.0) || (stat > 26.0)) {
temp_ok = 0
}
return (temp_ok)
}'
# ----------------------------------------------------------------------
def bl_hall_temperature '{
local stat
stat = epics_get("ILUUL-02AV:TEMP")
printf("hall T02 average temperature : ")
if ((stat < 23.0) || (stat > 25.0)) {
text_bf
}
printf("%.2f deg.C\n",stat)
text_non_bf
stat = epics_get("ILUUL-0200-EB104:TEMP")
printf("hall temperature at T0204 axis 16 : ")
if ((stat < 23) || (stat > 25)) {
text_bf
}
printf("%.2f deg.C\n",stat)
text_non_bf
stat = epics_get("ILUUL-0200-EB105:TEMP")
printf("hall temperature at T0205 axis 18 : ")
if ((stat < 23) || (stat > 25)) {
text_bf
}
printf("%.2f deg.C\n",stat)
text_non_bf
# stat = epics_get("ILUUL-0300-EB102:TEMP")
# printf("EH T03 temperature at T0302 axis 21: ")
# if ((stat < 23) || (stat > 25)) {
# text_bf
# }
# printf("%.2f deg.C\n",stat)
# text_non_bf
}'
def _bl_sls_status_unusual() '{
local unusual
local stat
unusual = 0
stat = epics_get("X12SA-SR-VAC:SETPOINT")
if (stat != "OK") {
unusual = 1
}
stat = epics_get("ACOAU-ACCU:OP-MODE.VAL")
if ((stat != "Light Available") && (stat != "Light-Available")) {
unusual = 1
}
stat = epics_get("ALIRF-GUN:INJ-MODE")
if (stat != "TOP-UP") {
unusual = 1
}
# current threshold
stat = epics_get("ALIRF-GUN:CUR-LOWLIM")
if (stat < 350) {
unusual = 1
}
# current deadband
stat = epics_get("ALIRF-GUN:CUR-DBAND")
if (stat > 2) {
unusual = 1
}
# orbit feedback mode
stat = epics_get("ARIDI-BPM:OFB-MODE")
if (stat != "fast") {
unusual = 1
}
# fast orbit feedback
stat = epics_get("ARIDI-BPM:FOFBSTATUS-G")
if (stat != "running") {
unusual = 1
}
return(unusual)
}'
def bl_sls_status '{
local stat
stat = epics_get("ACOAU-ACCU:OP-MODE.VAL")
printf("SLS status : ")
if ((stat != "Light Available") && (stat != "Light-Available")) {
text_bf
}
printf("%s\n",stat)
text_non_bf
stat = epics_get("ALIRF-GUN:INJ-MODE")
printf("SLS injection mode : ")
if (stat != "TOP-UP") {
text_bf
}
printf("%s\n",stat)
text_non_bf
stat = epics_get("ALIRF-GUN:CUR-LOWLIM")
printf("SLS current threshold : ")
if (stat < 350) {
text_bf
}
printf("%7.3f\n",stat)
text_non_bf
stat = epics_get("ALIRF-GUN:CUR-DBAND")
printf("SLS current deadband : ")
if (stat > 2) {
text_bf
}
printf("%7.3f\n",stat)
text_non_bf
stat = epics_get("ACORF-FILL:PAT-SELECT")
printf("SLS filling pattern : ")
printf("%s\n",stat)
bl_ring_current
stat = epics_get("ARIDI-PCT:TAU-HOUR")
printf("SLS filling life time : ")
printf("%.2f h\n",stat)
stat = epics_get("ARIDI-BPM:OFB-MODE")
printf("orbit feedback mode : ")
if (stat != "fast") {
text_bf
}
printf("%s\n",stat)
text_non_bf
stat = epics_get("ARIDI-BPM:FOFBSTATUS-G")
printf("fast orbit feedback : ")
if (stat != "running") {
text_bf
}
printf("%s\n",stat)
text_non_bf
}'
def _bl_get_ring_current() '{
return epics_get("ARIDI-PCT:CURRENT")
}'
# ----------------------------------------------------------------------
def _bl_no_ring_current() '{
# set an arbitrary current limit of 100mA as no-beam limit
if (_bl_get_ring_current() < 100) {
return 1
} else {
return 0
}
}'
# ----------------------------------------------------------------------
def bl_ring_current '{
local curr
curr = _bl_get_ring_current()
if (curr < 300) {
text_bf
}
printf("SLS ring current : %.3f mA\n",curr)
text_non_bf
}'

View File

@@ -0,0 +1,161 @@
import builtins
import time
from rich import box
from rich.console import Console
from rich.table import Table
from bec_client.plugins.cSAXS import epics_get, epics_put, fshclose
# import builtins to avoid linter errors
dev = builtins.__dict__.get("dev")
umv = builtins.__dict__.get("umv")
bec = builtins.__dict__.get("bec")
class LamNIOpticsMixin:
@staticmethod
def _get_user_param_safe(device, var):
param = dev[device].user_parameter
if not param or param.get(var) is None:
raise ValueError(f"Device {device} has no user parameter definition for {var}.")
return param.get(var)
def leye_out(self):
self.loptics_in()
fshclose()
leyey_out = self._get_user_param_safe("leyey", "out")
umv(dev.leyey, leyey_out)
epics_put("XOMNYI-XEYE-ACQ:0", 2)
# move rotation stage to zero to avoid problems with wires
umv(dev.lsamrot, 0)
umv(dev.dttrz, 5854, dev.fttrz, 2395)
def leye_in(self):
bec.queue.next_dataset_number += 1
# move rotation stage to zero to avoid problems with wires
umv(dev.lsamrot, 0)
umv(dev.dttrz, 6419.677, dev.fttrz, 2959.979)
while True:
moved_out = (input("Did the flight tube move out? (Y/n)") or "y").lower()
if moved_out == "y":
break
if moved_out == "n":
return
leyex_in = self._get_user_param_safe("leyex", "in")
leyey_in = self._get_user_param_safe("leyey", "in")
umv(dev.leyex, leyex_in, dev.leyey, leyey_in)
self.align.update_frame()
def _lfzp_in(self):
loptx_in = self._get_user_param_safe("loptx", "in")
lopty_in = self._get_user_param_safe("lopty", "in")
umv(
dev.loptx, loptx_in, dev.lopty, lopty_in
) # for 7.2567 keV and 150 mu, 60 nm fzp, loptz 83.6000 for propagation 1.4 mm
def lfzp_in(self):
"""
move in the lamni zone plate.
This will disable rt feedback, move the FZP and re-enabled the feedback.
"""
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_disable()
self._lfzp_in()
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_enable_with_reset()
def loptics_in(self):
"""
Move in the lamni optics, including the FZP and the OSA.
"""
self.lfzp_in()
self.losa_in()
def loptics_out(self):
"""Move out the lamni optics"""
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_disable()
# self.lcs_out()
self.losa_out()
loptx_out = self._get_user_param_safe("loptx", "out")
lopty_out = self._get_user_param_safe("lopty", "out")
umv(dev.loptx, loptx_out, dev.lopty, lopty_out)
if "rtx" in dev and dev.rtx.enabled:
time.sleep(1)
dev.rtx.controller.feedback_enable_with_reset()
def lcs_in(self):
# umv lcsx -1.852 lcsy -0.095
pass
def lcs_out(self):
umv(dev.lcsy, 3)
def losa_in(self):
# 6.2 keV, 170 um FZP
# umv(dev.losax, -1.4450000, dev.losay, -0.1800)
# umv(dev.losaz, -1)
# 6.7, 170
# umv(dev.losax, -1.4850, dev.losay, -0.1930)
# umv(dev.losaz, 1.0000)
# 7.2, 150
losax_in = self._get_user_param_safe("losax", "in")
losay_in = self._get_user_param_safe("losay", "in")
losaz_in = self._get_user_param_safe("losaz", "in")
umv(dev.losax, losax_in, dev.losay, losay_in)
umv(dev.losaz, losaz_in)
# 11 kev
# umv(dev.losax, -1.161000, dev.losay, -0.196)
# umv(dev.losaz, 1.0000)
def losa_out(self):
losay_out = self._get_user_param_safe("losay", "out")
losaz_out = self._get_user_param_safe("losaz", "out")
umv(dev.losaz, losaz_out)
umv(dev.losay, losay_out)
def lfzp_info(self):
loptz_val = dev.loptz.read()["loptz"]["value"]
distance = -loptz_val + 85.6 + 52
print(f"The sample is in a distance of {distance:.1f} mm from the FZP.")
diameters = [80e-6, 100e-6, 120e-6, 150e-6, 170e-6, 200e-6, 220e-6, 250e-6]
mokev_val = dev.mokev.read()["mokev"]["value"]
console = Console()
table = Table(
title=f"At the current energy of {mokev_val:.4f} keV we have following options:",
box=box.SQUARE,
)
table.add_column("Diameter", justify="center")
table.add_column("Focal distance", justify="center")
table.add_column("Current beam size", justify="center")
wavelength = 1.2398e-9 / mokev_val
for diameter in diameters:
outermost_zonewidth = 60e-9
focal_distance = diameter * outermost_zonewidth / wavelength
beam_size = (
-diameter / (focal_distance * 1000) * (focal_distance * 1000 - distance) * 1e6
)
table.add_row(
f"{diameter*1e6:.2f} microns",
f"{focal_distance:.2f} mm",
f"{beam_size:.2f} microns",
)
console.print(table)
print("OSA Information:")
# print(f"Current losaz %.1f\n", A[losaz])
# print("The OSA will collide with the sample plane at %.1f\n\n", 89.3-A[loptz])
print(
"The numbers presented here are for a sample in the plane of the lamni sample holder.\n"
)

View File

@@ -1,6 +1,7 @@
def lamni_read_additional_correction():
# "additional_correction_shift"
# [0][] x , [1][] y, [2][] angle, [3][0] number of elements
import numpy as np
with open("correction_lamni_um_S01405_.txt", "r") as f:
num_elements = f.readline()

View File

@@ -9,14 +9,13 @@ from pathlib import Path
import h5py
import numpy as np
from bec_client.plugins.cSAXS import epics_get, epics_put, fshclose, fshopen
from bec_lib import bec_logger
from bec_lib.alarm_handler import AlarmBase
from bec_lib.pdf_writer import PDFWriter
from typeguard import typechecked
from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen
from .lamni_optics_mixin import LaMNIInitStagesMixin, LamNIOpticsMixin
from .lamni_optics_mixin import LamNIOpticsMixin
logger = bec_logger.logger
bec = builtins.__dict__.get("bec")
@@ -32,7 +31,7 @@ class XrayEyeAlign:
self.lamni = lamni
self.device_manager = client.device_manager
self.scans = client.scans
# self.xeye = self.device_manager.devices.xeye
self.xeye = self.device_manager.devices.xeye
self.alignment_values = defaultdict(list)
self._reset_init_values()
self.corr_pos_x = []
@@ -189,8 +188,7 @@ class XrayEyeAlign:
val_y = epics_get(f"XOMNYI-XEYE-YVAL_Y:{k}") * self.PIXEL_CALIBRATION # in mm
self.alignment_values[k] = [val_x, val_y]
print(
f"Clicked position {k}: x {self.alignment_values[k][0]}, y"
f" {self.alignment_values[k][1]}"
f"Clicked position {k}: x {self.alignment_values[k][0]}, y {self.alignment_values[k][1]}"
)
if k == 0: # received center value of FZP
@@ -215,10 +213,7 @@ class XrayEyeAlign:
elif (
k == 1
): # received sample center value at samroy 0 ie the final base shift values
msg = (
f"Base shift values from movement are x {self.shift_xy[0]}, y"
f" {self.shift_xy[1]}"
)
msg = f"Base shift values from movement are x {self.shift_xy[0]}, y {self.shift_xy[1]}"
print(msg)
logger.info(msg)
self.shift_xy[0] += (
@@ -228,12 +223,13 @@ class XrayEyeAlign:
self.alignment_values[1][1] - self.alignment_values[0][1]
) * 1000
print(
"Base shift values from movement and clicked position are x"
f" {self.shift_xy[0]}, y {self.shift_xy[1]}"
f"Base shift values from movement and clicked position are x {self.shift_xy[0]}, y {self.shift_xy[1]}"
)
self.scans.lamni_move_to_scan_center(
self.shift_xy[0] / 1000, self.shift_xy[1] / 1000, self.get_tomo_angle()
self.shift_xy[0] / 1000,
self.shift_xy[1] / 1000,
self.get_tomo_angle(),
).wait()
self.send_message("please wait ...")
@@ -242,7 +238,9 @@ class XrayEyeAlign:
time.sleep(1)
self.scans.lamni_move_to_scan_center(
self.shift_xy[0] / 1000, self.shift_xy[1] / 1000, self.get_tomo_angle()
self.shift_xy[0] / 1000,
self.shift_xy[1] / 1000,
self.get_tomo_angle(),
).wait()
epics_put("XOMNYI-XEYE-ANGLE:0", self.get_tomo_angle())
@@ -259,12 +257,16 @@ class XrayEyeAlign:
self._disable_rt_feedback()
self.tomo_rotate((k - 1) * 45 - 45 / 2)
self.scans.lamni_move_to_scan_center(
self.shift_xy[0] / 1000, self.shift_xy[1] / 1000, self.get_tomo_angle()
self.shift_xy[0] / 1000,
self.shift_xy[1] / 1000,
self.get_tomo_angle(),
).wait()
self._disable_rt_feedback()
self.tomo_rotate((k - 1) * 45)
self.scans.lamni_move_to_scan_center(
self.shift_xy[0] / 1000, self.shift_xy[1] / 1000, self.get_tomo_angle()
self.shift_xy[0] / 1000,
self.shift_xy[1] / 1000,
self.get_tomo_angle(),
).wait()
epics_put("XOMNYI-XEYE-ANGLE:0", self.get_tomo_angle())
@@ -291,7 +293,9 @@ class XrayEyeAlign:
self.shift_xy[0] = self.shift_xy[0] + _xrayeyalignmvx
self.shift_xy[1] = self.shift_xy[1] + _xrayeyalignmvy
self.scans.lamni_move_to_scan_center(
self.shift_xy[0] / 1000, self.shift_xy[1] / 1000, self.get_tomo_angle()
self.shift_xy[0] / 1000,
self.shift_xy[1] / 1000,
self.get_tomo_angle(),
).wait()
print(
f"Current center horizontal {self.shift_xy[0]} vertical {self.shift_xy[1]}"
@@ -306,14 +310,12 @@ class XrayEyeAlign:
fovx = self._xray_fov_xy[0] * self.PIXEL_CALIBRATION * 1000 / 2
fovy = self._xray_fov_xy[1] * self.PIXEL_CALIBRATION * 1000 / 2
print(
f"The largest field of view from the xrayeyealign was \nfovx = {fovx:.0f} microns, fovy"
f" = {fovy:.0f} microns"
f"The largest field of view from the xrayeyealign was \nfovx = {fovx:.0f} microns, fovy = {fovy:.0f} microns"
)
print("Use matlab routine to fit the current alignment...")
print(
"This additional shift is applied to the base shift values\n which are x"
f" {self.shift_xy[0]}, y {self.shift_xy[1]}"
f"This additional shift is applied to the base shift values\n which are x {self.shift_xy[0]}, y {self.shift_xy[1]}"
)
self._disable_rt_feedback()
@@ -321,8 +323,7 @@ class XrayEyeAlign:
self.tomo_rotate(0)
print(
"\n\nNEXT LOAD ALIGNMENT PARAMETERS\nby running"
" lamni.align.read_xray_eye_correction()\n"
"\n\nNEXT LOAD ALIGNMENT PARAMETERS\nby running lamni.align.read_xray_eye_correction()\n"
)
self.client.set_global_var("tomo_fov_offset", self.shift_xy)
@@ -331,13 +332,12 @@ class XrayEyeAlign:
with open(
os.path.expanduser("~/Data10/specES1/internal/xrayeye_alignmentvalues"), "w"
) as alignment_values_file:
alignment_values_file.write("angle\thorizontal\tvertical\n")
alignment_values_file.write(f"angle\thorizontal\tvertical\n")
for k in range(2, 11):
fovx_offset = (self.alignment_values[0][0] - self.alignment_values[k][0]) * 1000
fovy_offset = (self.alignment_values[k][1] - self.alignment_values[0][1]) * 1000
print(
f"Writing to file new alignment: number {k}, value x {fovx_offset}, y"
f" {fovy_offset}"
f"Writing to file new alignment: number {k}, value x {fovx_offset}, y {fovy_offset}"
)
alignment_values_file.write(f"{(k-2)*45}\t{fovx_offset}\t{fovy_offset}\n")
@@ -422,8 +422,7 @@ class XrayEyeAlign:
additional_correction_shift_x = self.corr_pos_x[-1]
additional_correction_shift_y = self.corr_pos_y[-1]
print(
"Additional correction shifts:"
f" {additional_correction_shift_x} {additional_correction_shift_y}"
f"Additional correction shifts: {additional_correction_shift_x} {additional_correction_shift_y}"
)
return (additional_correction_shift_x, additional_correction_shift_y)
@@ -477,8 +476,7 @@ class XrayEyeAlign:
additional_correction_shift_x = self.corr_pos_x_2[-1]
additional_correction_shift_y = self.corr_pos_y_2[-1]
print(
"Additional correction shifts 2:"
f" {additional_correction_shift_x} {additional_correction_shift_y}"
f"Additional correction shifts 2: {additional_correction_shift_x} {additional_correction_shift_y}"
)
return (additional_correction_shift_x, additional_correction_shift_y)
@@ -511,7 +509,7 @@ class LamNI(LamNIOpticsMixin):
super().__init__()
self.client = client
self.align = XrayEyeAlign(client, self)
self.init = LaMNIInitStagesMixin()
self.check_shutter = True
self.check_light_available = True
self.check_fofb = True
@@ -527,8 +525,7 @@ class LamNI(LamNIOpticsMixin):
def get_beamline_checks_enabled(self):
print(
f"Shutter: {self.check_shutter}\nFOFB: {self.check_fofb}\nLight available:"
f" {self.check_light_available}"
f"Shutter: {self.check_shutter}\nFOFB: {self.check_fofb}\nLight available: {self.check_light_available}"
)
@property
@@ -586,27 +583,6 @@ class LamNI(LamNIOpticsMixin):
def tomo_circfov(self, val: float):
self.client.set_global_var("tomo_circfov", val)
@property
def tomo_type(self):
val = self.client.get_global_var("tomo_type")
if val is None:
return 1
return val
@tomo_type.setter
def tomo_type(self, val: float):
if val == 1:
# equally spaced tomography with 8 sub tomograms
self.client.set_global_var("tomo_type", val)
# elif val == 2:
# # golden ratio tomography (sorted bunches)
# self.client.set_global_var("tomo_type", val)
# elif val == 3:
# # equally spaced tomography with starting angles shifted by golden ratio
# self.client.set_global_var("tomo_type", val)
else:
raise ValueError("Unknown tomo_type.")
@property
def tomo_countingtime(self):
val = self.client.get_global_var("tomo_countingtime")
@@ -790,18 +766,13 @@ class LamNI(LamNIOpticsMixin):
# pylint: disable=undefined-variable
self._current_scan_list.append(bec.queue.next_scan_number)
logger.info(
f"scans.lamni_fermat_scan(fov_size=[{self.lamni_piezo_range_x},{self.lamni_piezo_range_y}],"
f" step={self.tomo_shellstep}, stitch_x={0}, stitch_y={0},"
f" stitch_overlap={1},center_x={self.align.tomo_fovx_offset},"
f" center_y={self.align.tomo_fovy_offset},"
f" shift_x={self.manual_shift_x+correction_xeye_mu[0]-additional_correction[0]-additional_correction_2[0]},"
f" shift_y={self.manual_shift_y+correction_xeye_mu[1]-additional_correction[1]-additional_correction_2[1]},"
f" fov_circular={self.tomo_circfov}, angle={angle}, scan_type='fly')"
)
log_message = (
f"{str(datetime.datetime.now())}: LamNI scan projection at angle {angle}, scan"
f" number {bec.queue.next_scan_number}.\n"
f"scans.lamni_fermat_scan(fov_size=[{self.lamni_piezo_range_x},{self.lamni_piezo_range_y}], step={self.tomo_shellstep}, stitch_x={0}, stitch_y={0}, stitch_overlap={1},"
f"center_x={self.align.tomo_fovx_offset}, center_y={self.align.tomo_fovy_offset}, "
f"shift_x={self.manual_shift_x+correction_xeye_mu[0]-additional_correction[0]-additional_correction_2[0]}, "
f"shift_y={self.manual_shift_y+correction_xeye_mu[1]-additional_correction[1]-additional_correction_2[1]}, "
f"fov_circular={self.tomo_circfov}, angle={angle}, scan_type='fly')"
)
log_message = f"{str(datetime.datetime.now())}: LamNI scan projection at angle {angle}, scan number {bec.queue.next_scan_number}.\n"
self.write_to_spec_log(log_message)
# self.write_to_scilog(log_message, ["BEC_scans", self.sample_name])
corridor_size = self.corridor_size if self.corridor_size > 0 else None
@@ -843,7 +814,10 @@ class LamNI(LamNIOpticsMixin):
msgs.append("Check beam failed: Shutter is closed.")
if self.check_light_available:
machine_status = dev.sls_machine_status.read(cached=True)
if machine_status["value"] not in ["Light Available", "Light-Available"]:
if machine_status["value"] not in [
"Light Available",
"Light-Available",
]:
self._beam_is_okay = False
msgs.append("Check beam failed: Light not available.")
if self.check_fofb:
@@ -884,8 +858,7 @@ class LamNI(LamNIOpticsMixin):
try:
msg = bec.logbook.LogbookMessage()
msg.add_text(
"<p><mark class='pen-red'><strong>Beamline checks failed at"
f" {str(datetime.datetime.now())}: {''.join(self._check_msgs)}</strong></mark></p>"
f"<p><mark class='pen-red'><strong>Beamline checks failed at {str(datetime.datetime.now())}: {''.join(self._check_msgs)}</strong></mark></p>"
).add_tag(["BEC", "beam_check"])
self.client.logbook.send_logbook_message(msg)
except Exception:
@@ -902,20 +875,25 @@ class LamNI(LamNIOpticsMixin):
try:
msg = bec.logbook.LogbookMessage()
msg.add_text(
"<p><mark class='pen-red'><strong>Operation resumed at"
f" {str(datetime.datetime.now())}.</strong></mark></p>"
f"<p><mark class='pen-red'><strong>Operation resumed at {str(datetime.datetime.now())}.</strong></mark></p>"
).add_tag(["BEC", "beam_check"])
self.client.logbook.send_logbook_message(msg)
except Exception:
logger.warning("Failed to send update to SciLog.")
def add_sample_database(
self, samplename, date, eaccount, scan_number, setup, sample_additional_info, user
self,
samplename,
date,
eaccount,
scan_number,
setup,
sample_additional_info,
user,
):
"""Add a sample to the omny sample database. This also retrieves the tomo id."""
subprocess.run(
"wget --user=omny --password=samples -q -O /tmp/currsamplesnr.txt"
f" 'https://omny.web.psi.ch/samples/newmeasurement.php?sample={samplename}&date={date}&eaccount={eaccount}&scannr={scan_number}&setup={setup}&additional={sample_additional_info}&user={user}'",
f"wget --user=omny --password=samples -q -O /tmp/currsamplesnr.txt 'https://omny.web.psi.ch/samples/newmeasurement.php?sample={samplename}&date={date}&eaccount={eaccount}&scannr={scan_number}&setup={setup}&additional={sample_additional_info}&user={user}'",
shell=True,
)
with open("/tmp/currsamplesnr.txt") as tomo_number_file:
@@ -969,63 +947,25 @@ class LamNI(LamNIOpticsMixin):
# _tomo_shift_angles (potential global variable)
_tomo_shift_angles = 0
angle_end = start_angle + 360
angles = np.linspace(
for angle in np.linspace(
start_angle + _tomo_shift_angles,
angle_end,
num=int(360 / self.tomo_angle_stepsize) + 1,
endpoint=True,
)
# reverse even sub-tomograms
if not (subtomo_number % 2):
angles = np.flip(angles)
for angle in angles:
self.progress["subtomo"] = subtomo_number
self.progress["subtomo_projection"] = angles.index(angle)
self.progress["subtomo_total_projections"] = 180 / self.tomo_angle_stepsize
self.progress["projection"] = (subtomo_number - 1) * self.progress[
"subtomo_total_projections"
] + self.progress["subtomo_projection"]
self.progress["total_projections"] = 180 / self.tomo_angle_stepsize * 8
self.progress["angle"] = angle
self._tomo_scan_at_angle(angle, subtomo_number)
def _print_progress(self):
print("\x1b[95mProgress report:")
print(f"Tomo type: ....................... {self.progress['tomo_type']}")
print(f"Projection: ...................... {self.progress['projection']}")
print(f"Total projections expected ....... {self.progress['total_projections']}")
print(f"Angle: ........................... {self.progress['angle']}")
print(f"Current subtomo: ................. {self.progress['subtomo']}")
print(f"Current projection within subtomo: {self.progress['subtomo_projection']}\x1b[0m")
def _tomo_scan_at_angle(self, angle, subtomo_number):
successful = False
error_caught = False
if 0 <= angle < 360.05:
print(f"Starting LamNI scan for angle {angle} in subtomo {subtomo_number}")
self._print_progress()
while not successful:
self._start_beam_check()
if not self.special_angles:
self._current_special_angles = []
if self._current_special_angles:
next_special_angle = self._current_special_angles[0]
if np.isclose(angle, next_special_angle, atol=0.5):
self._current_special_angles.pop(0)
num_repeats = self.special_angle_repeats
else:
num_repeats = 1
try:
start_scan_number = bec.queue.next_scan_number
for i in range(num_repeats):
self._at_each_angle(angle)
error_caught = False
except AlarmBase as exc:
if exc.alarm_type == "TimeoutError":
bec.queue.request_queue_reset()
time.sleep(2)
error_caught = True
):
successful = False
error_caught = False
if 0 <= angle < 360.05:
print(f"Starting LamNI scan for angle {angle}")
while not successful:
self._start_beam_check()
if not self.special_angles:
self._current_special_angles = []
if self._current_special_angles:
next_special_angle = self._current_special_angles[0]
if np.isclose(angle, next_special_angle, atol=0.5):
self._current_special_angles.pop(0)
num_repeats = self.special_angle_repeats
else:
num_repeats = 1
try:
@@ -1065,7 +1005,7 @@ class LamNI(LamNIOpticsMixin):
scans = builtins.__dict__.get("scans")
self._current_special_angles = self.special_angles.copy()
if self.tomo_type == 1 and subtomo_start == 1 and start_angle is None:
if subtomo_start == 1 and start_angle is None:
# pylint: disable=undefined-variable
self.tomo_id = self.add_sample_database(
self.sample_name,
@@ -1088,16 +1028,14 @@ class LamNI(LamNIOpticsMixin):
print(f"Counting time <ctime> = {self.tomo_countingtime} s")
print(f"Stepsize microns <step> = {self.tomo_shellstep}")
print(
f"Piezo range (max 80) <microns> = {self.lamni_piezo_range_x},"
f" {self.lamni_piezo_range_y}"
f"Piezo range (max 80) <microns> = {self.lamni_piezo_range_x}, {self.lamni_piezo_range_y}"
)
print(f"Stitching number x,y = {self.lamni_stitch_x}, {self.lamni_stitch_y}")
print(f"Stitching overlap = {self.tomo_stitch_overlap}")
print(f"Circuilar FOV diam <microns> = {self.tomo_circfov}")
print(f"Reconstruction queue name = {self.ptycho_reconstruct_foldername}")
print(
"For information, fov offset is rotating and finding the ROI, manual shift moves"
" rotation center"
"For information, fov offset is rotating and finding the ROI, manual shift moves rotation center"
)
print(f" _tomo_fovx_offset <mm> = {self.align.tomo_fovx_offset}")
print(f" _tomo_fovy_offset <mm> = {self.align.tomo_fovy_offset}")
@@ -1153,7 +1091,7 @@ class LamNI(LamNIOpticsMixin):
with open(ptycho_queue_file, "w") as queue_file:
scans = " ".join([str(scan) for scan in self._current_scan_list])
queue_file.write(f"p.scan_number {scans}\n")
queue_file.write("p.check_nextscan_started 1\n")
queue_file.write(f"p.check_nextscan_started 1\n")
def write_pdf_report(self):
"""create and write the pdf report with the current LamNI settings"""
@@ -1178,25 +1116,17 @@ class LamNI(LamNIOpticsMixin):
f"{'Dataset ID:':<{padding}}{dataset_id:>{padding}}\n",
f"{'Sample Info:':<{padding}}{'Sample Info':>{padding}}\n",
f"{'e-account:':<{padding}}{str(self.client.username):>{padding}}\n",
(
f"{'Number of projections:':<{padding}}{int(360 / self.tomo_angle_stepsize * 8):>{padding}}\n"
),
f"{'Number of projections:':<{padding}}{int(360 / self.tomo_angle_stepsize * 8):>{padding}}\n",
f"{'First scan number:':<{padding}}{self.client.queue.next_scan_number:>{padding}}\n",
(
f"{'Last scan number approx.:':<{padding}}{self.client.queue.next_scan_number + int(360 / self.tomo_angle_stepsize * 8) + 10:>{padding}}\n"
),
(
f"{'Current photon energy:':<{padding}}{dev.mokev.read(cached=True)['value']:>{padding}.4f}\n"
),
f"{'Last scan number approx.:':<{padding}}{self.client.queue.next_scan_number + int(360 / self.tomo_angle_stepsize * 8) + 10:>{padding}}\n",
f"{'Current photon energy:':<{padding}}{dev.mokev.read(cached=True)['value']:>{padding}.4f}\n",
f"{'Exposure time:':<{padding}}{self.tomo_countingtime:>{padding}.2f}\n",
f"{'Fermat spiral step size:':<{padding}}{self.tomo_shellstep:>{padding}.2f}\n",
f"{'Piezo range (FOV sample plane):':<{padding}}{piezo_range:>{padding}}\n",
f"{'Restriction to circular FOV:':<{padding}}{self.tomo_circfov:>{padding}.2f}\n",
f"{'Stitching:':<{padding}}{stitching:>{padding}}\n",
f"{'Number of individual sub-tomograms:':<{padding}}{8:>{padding}}\n",
(
f"{'Angular step within sub-tomogram:':<{padding}}{self.tomo_angle_stepsize:>{padding}.2f}\n"
),
f"{'Angular step within sub-tomogram:':<{padding}}{self.tomo_angle_stepsize:>{padding}.2f}\n",
]
content = "".join(content)
user_target = os.path.expanduser(f"~/Data10/documentation/tomo_scan_ID_{self.tomo_id}.pdf")
@@ -1204,13 +1134,20 @@ class LamNI(LamNIOpticsMixin):
file.write(header)
file.write(content)
subprocess.run(
"xterm /work/sls/spec/local/XOMNY/bin/upload/upload_last_pon.sh &", shell=True
"xterm /work/sls/spec/local/XOMNY/bin/upload/upload_last_pon.sh &",
shell=True,
)
# status = subprocess.run(f"cp /tmp/spec-e20131-specES1.pdf {user_target}", shell=True)
msg = bec.logbook.LogbookMessage()
logo_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "LamNI_logo.png")
msg.add_file(logo_path).add_text("".join(content).replace("\n", "</p><p>")).add_tag(
["BEC", "tomo_parameters", f"dataset_id_{dataset_id}", "LamNI", self.sample_name]
[
"BEC",
"tomo_parameters",
f"dataset_id_{dataset_id}",
"LamNI",
self.sample_name,
]
)
self.client.logbook.send_logbook_message(msg)
@@ -1295,12 +1232,16 @@ class DataDrivenLamNI(LamNI):
def sub_tomo_scan(self):
raise NotImplementedError(
"Cannot run sub_tomo_scan with data-driven LamNI. Please use"
" lamni.tomo_scan(subtomo_start=<START_NUM>) instead."
"Cannot run sub_tomo_scan with data-driven LamNI. Please use lamni.tomo_scan(subtomo_start=<START_NUM>) instead."
)
def _at_each_angle(
self, angle=None, stepsize=None, loptz_pos=None, manual_shift_x=0, manual_shift_y=0
self,
angle=None,
stepsize=None,
loptz_pos=None,
manual_shift_x=0,
manual_shift_y=0,
):
# Do something...
# self.tomo_parameters

View File

@@ -0,0 +1,3 @@
from .cSAXS import *
# from .LamNI import *

View File

@@ -0,0 +1 @@
from .cSAXS_beamline import fshopen, fshclose, fshstatus, epics_get, epics_put

View File

@@ -1,9 +1,10 @@
import builtins
from bec_ipython_client.beamline_mixin import BeamlineShowInfo
from rich import box
from rich.table import Table
from bec_client.beamline_mixin import BeamlineShowInfo
class BeamlineInfo(BeamlineShowInfo):
def show(self):

View File

@@ -0,0 +1,76 @@
"""
Post startup script for the BEC client. This script is executed after the
IPython shell is started. It is used to load the beamline specific
information and to setup the prompts.
The script is executed in the global namespace of the IPython shell. This
means that all variables defined here are available in the shell.
If needed, bec command-line arguments can be parsed here. For example, to
parse the --session argument, add the following lines to the script:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--session", help="Session name", type=str, default="my_default_session")
args = parser.parse_args()
if args.session == "my_session":
print("Loading my_session session")
from bec_plugins.bec_client.plugins.my_session import *
else:
print("Loading default session")
from bec_plugins.bec_client.plugins.default_session import *
"""
# pylint: disable=invalid-name, unused-import, import-error, undefined-variable, unused-variable, unused-argument, no-name-in-module
import argparse
from bec_lib import bec_logger
logger = bec_logger.logger
logger.info("Using the cSAXS startup script.")
parser = argparse.ArgumentParser()
parser.add_argument("--session", help="Session name", type=str, default="cSAXS")
args = parser.parse_args()
if args.session == "LamNI":
print("Loading LamNI session")
from bec_plugins.bec_client.plugins.cSAXS import *
from bec_plugins.bec_client.plugins.LamNI import *
lamni = LamNI(bec)
elif args.session == "cSAXS":
print("Loading cSAXS session")
from bec_plugins.bec_client.plugins.cSAXS import *
# SETUP BEAMLINE INFO
from bec_client.plugins.SLS.sls_info import OperatorInfo, SLSInfo
from bec_plugins.bec_client.plugins.cSAXS.beamline_info import BeamlineInfo
bec._beamline_mixin._bl_info_register(BeamlineInfo)
bec._beamline_mixin._bl_info_register(SLSInfo)
bec._beamline_mixin._bl_info_register(OperatorInfo)
# SETUP PROMPTS
bec._ip.prompts.username = args.session
bec._ip.prompts.status = 1
# REGISTER BEAMLINE CHECKS
from bec_lib.bl_conditions import (
FastOrbitFeedbackCondition,
LightAvailableCondition,
ShutterCondition,
)
# _fast_orbit_feedback_condition = FastOrbitFeedbackCondition(dev.sls_fast_orbit_feedback)
_light_available_condition = LightAvailableCondition(dev.sls_machine_status)
_shutter_condition = ShutterCondition(dev.x12sa_es1_shutter_status)
# bec.bl_checks.register(_fast_orbit_feedback_condition)
bec.bl_checks.register(_light_available_condition)
bec.bl_checks.register(_shutter_condition)

View File

@@ -0,0 +1,25 @@
"""
Pre-startup script for BEC client. This script is executed before the BEC client
is started. It can be used to set up the BEC client configuration. The script is
executed in the global namespace of the BEC client. This means that all
variables defined here are available in the BEC client.
To set up the BEC client configuration, use the ServiceConfig class. For example,
to set the configuration file path, add the following lines to the script:
import pathlib
from bec_lib import ServiceConfig
current_path = pathlib.Path(__file__).parent.resolve()
CONFIG_PATH = f"{current_path}/<path_to_my_config_file.yaml>"
config = ServiceConfig(CONFIG_PATH)
If this startup script defined a ServiceConfig object, the BEC client will use
it to configure itself. Otherwise, the BEC client will use the default config.
"""
# example:
# current_path = pathlib.Path(__file__).parent.resolve()
# CONFIG_PATH = f"{current_path}/../../../bec_config.yaml"
# config = ServiceConfig(CONFIG_PATH)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,152 @@
import yaml
# TODO: fix imports, those classes are located in .../bec/scibec/init_scibec/configs/config.py
# (see also lamni_config.py in bec repository)
from .config import DemoConfig, X12SAConfig
class LamNIConfig(DemoConfig, X12SAConfig):
def run(self):
# self.write_galil_motors()
# self.write_rt_motors()
# self.write_smaract_motors()
# self.write_eiger1p5m()
self.write_x12sa_status()
self.write_sls_status()
self.load_csaxs_config()
# self.write_sim_user_motors()
# self.write_sim_beamline_motors()
# self.write_sim_beamline_monitors()
def write_galil_motors(self):
lamni_galil_motors = [
("lsamx", "A", -1, 0.5, {"center": 8.768000}),
("lsamy", "B", 1, 0.5, {"center": 10.041000}),
("lsamrot", "C", 1, 0.5, {}),
("loptz", "D", -1, 0.5, {}),
("loptx", "E", 1, 0.5, {"in": -0.8380, "out": -0.699}),
("lopty", "F", 1, 0.5, {"in": 3.3540, "out": 3.53}),
("leyex", "G", -1, 0.001, {"in": 14.117000}),
("leyey", "H", -1, 0.001, {"in": 48.069000, "out": 0.5}),
]
out = {}
for m in lamni_galil_motors:
out[m[0]] = dict(
{
"status": {"enabled": True, "enabled_set": True},
"deviceClass": "GalilMotor",
"deviceConfig": {
"axis_Id": m[1],
"name": m[0],
"labels": m[0],
"host": "mpc2680.psi.ch",
"port": 8081,
"sign": m[2],
"limits": [0, 0],
"tolerance": m[3],
"device_access": True,
"device_mapping": {"rt": "rtx"},
},
"acquisitionConfig": {
"schedule": "sync",
"acquisitionGroup": "motor",
"readoutPriority": "baseline",
},
"deviceTags": ["lamni"],
}
)
if m[4]:
out[m[0]]["userParameter"] = m[4]
self.write_section(out, "LamNI Galil motors")
def write_rt_motors(self):
lamni_rt_motors = [
("rtx", "A", 1),
("rty", "B", 1),
]
out = dict()
for m in lamni_rt_motors:
out[m[0]] = dict(
{
"status": {"enabled": True, "enabled_set": True},
"deviceClass": "RtLamniMotor",
"deviceConfig": {
"axis_Id": m[1],
"name": m[0],
"labels": m[0],
"host": "mpc2680.psi.ch",
"port": 3333,
"limits": [0, 0],
"sign": m[2],
"device_access": True,
},
"acquisitionConfig": {
"schedule": "sync",
"acquisitionGroup": "motor",
"readoutPriority": "baseline",
},
"deviceTags": ["lamni"],
}
)
self.write_section(out, "LamNI RT")
def write_smaract_motors(self):
lamni_smaract_motors = [
("losax", "A", -1, {"in": -0.848000}),
("losay", "B", -1, {"in": 0.135000, "out": 3.8}),
("losaz", "C", 1, {"in": -1, "out": -3}),
("lcsx", "D", -1, {}),
("lcsy", "E", -1, {}),
]
out = dict()
for m in lamni_smaract_motors:
out[m[0]] = dict(
{
"status": {"enabled": True, "enabled_set": True},
"deviceClass": "SmaractMotor",
"deviceConfig": {
"axis_Id": m[1],
"name": m[0],
"labels": m[0],
"host": "mpc2680.psi.ch",
"port": 8085,
"limits": [0, 0],
"sign": m[2],
"tolerance": 0.05,
},
"acquisitionConfig": {
"schedule": "sync",
"acquisitionGroup": "motor",
"readoutPriority": "baseline",
},
"deviceTags": ["lamni"],
}
)
if m[3]:
out[m[0]]["userParameter"] = m[3]
self.write_section(out, "LamNI SmarAct motors")
def write_eiger1p5m(self):
out = {
"eiger1p5m": {
"description": "Eiger 1.5M in vacuum detector, in-house developed, PSI",
"status": {"enabled": True, "enabled_set": True},
"deviceClass": "Eiger1p5MDetector",
"deviceConfig": {"device_access": True, "name": "eiger1p5m"},
"acquisitionConfig": {
"schedule": "sync",
"acquisitionGroup": "detector",
"readoutPriority": "monitored",
},
"deviceTags": ["detector"],
}
}
self.write_section(out, "LamNI Eiger 1.5M in vacuum")
def load_csaxs_config(self):
CONFIG_PATH = "./init_scibec/configs/test_config_cSAXS.yaml"
content = {}
with open(CONFIG_PATH, "r") as csaxs_config_file:
content = yaml.safe_load(csaxs_config_file.read())
self.write_section(content, "Default cSAXS config")

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
import os
def setup_epics_ca():
os.environ["EPICS_CA_AUTO_ADDR_LIST"] = "NO"
os.environ["EPICS_CA_ADDR_LIST"] = "129.129.122.255 sls-x12sa-cagw.psi.ch:5836"
os.environ["PYTHONIOENCODING"] = "latin1"
def run():
setup_epics_ca()

View File

@@ -23,10 +23,9 @@ but they are executed in a specific order:
import time
import numpy as np
from bec_lib import bec_logger
from bec_lib.endpoints import MessageEndpoints
from bec_server.scan_server.errors import ScanAbortion
from bec_server.scan_server.scans import RequestBase, ScanArgType, ScanBase
from bec_lib import MessageEndpoints, bec_logger, messages
from scan_server.errors import ScanAbortion
from scan_server.scans import RequestBase, ScanArgType, ScanBase
MOVEMENT_SCALE_X = np.sin(np.radians(15)) * np.cos(np.radians(30))
MOVEMENT_SCALE_Y = np.cos(np.radians(15))
@@ -97,8 +96,8 @@ class LamNIMixin:
coarse_move_req_x = np.abs(lsamx_current - move_x)
coarse_move_req_y = np.abs(lsamy_current - move_y)
self.device_manager.devices.lsamx.read_only = False
self.device_manager.devices.lsamy.read_only = False
self.device_manager.devices.lsamx.enabled_set = True
self.device_manager.devices.lsamy.enabled_set = True
if (
np.abs(y_drift) > 150
@@ -110,8 +109,9 @@ class LamNIMixin:
logger.info(
f"Compensating {[val/1000 for val in lamni_to_stage_coordinates(x_drift,y_drift)]}"
)
yield from self.stubs.set(device="lsamx", value=move_x)
yield from self.stubs.set(device="lsamy", value=move_y)
yield from self.stubs.set_and_wait(
device=["lsamx", "lsamy"], positions=[move_x, move_y]
)
time.sleep(0.01)
rtx_current = yield from self.stubs.send_rpc_and_wait("rtx", "readback.get")
@@ -126,14 +126,12 @@ class LamNIMixin:
x_drift2 = x_center_expect * 1000 - rtx_current
y_drift2 = y_center_expect * 1000 - rty_current
logger.info(
f"Uncompensated drift of setup after first iteration is x={x_drift2:.3f},"
f" y={y_drift2:.3f}"
f"Uncompensated drift of setup after first iteration is x={x_drift2:.3f}, y={y_drift2:.3f}"
)
if np.abs(x_drift2) > 5 or np.abs(y_drift2) > 5:
logger.info(
"Compensating second iteration"
f" {[val/1000 for val in lamni_to_stage_coordinates(x_drift2,y_drift2)]}"
f"Compensating second iteration {[val/1000 for val in lamni_to_stage_coordinates(x_drift2,y_drift2)]}"
)
move_x = (
x_stage
@@ -147,28 +145,26 @@ class LamNIMixin:
+ lamni_to_stage_coordinates(x_drift, y_drift)[1] / 1000
+ lamni_to_stage_coordinates(x_drift2, y_drift2)[1] / 1000
)
yield from self.stubs.set(device="lsamx", value=move_x)
yield from self.stubs.set(device="lsamy", value=move_y)
yield from self.stubs.set_and_wait(
device=["lsamx", "lsamy"], positions=[move_x, move_y]
)
time.sleep(0.01)
rtx_current = yield from self.stubs.send_rpc_and_wait("rtx", "readback.get")
rty_current = yield from self.stubs.send_rpc_and_wait("rty", "readback.get")
logger.info(
f"New scan center interferometer after second iteration {rtx_current:.3f},"
f" {rty_current:.3f} microns"
f"New scan center interferometer after second iteration {rtx_current:.3f}, {rty_current:.3f} microns"
)
x_drift2 = x_center_expect * 1000 - rtx_current
y_drift2 = y_center_expect * 1000 - rty_current
logger.info(
f"Uncompensated drift of setup after second iteration is x={x_drift2:.3f},"
f" y={y_drift2:.3f}"
f"Uncompensated drift of setup after second iteration is x={x_drift2:.3f}, y={y_drift2:.3f}"
)
else:
logger.info("No second iteration required")
self.device_manager.devices.lsamx.read_only = True
self.device_manager.devices.lsamy.read_only = True
self.device_manager.devices.lsamx.enabled_set = False
self.device_manager.devices.lsamy.enabled_set = False
yield from self.stubs.send_rpc_and_wait("rtx", "controller.feedback_enable_without_reset")
@@ -178,12 +174,8 @@ class LamNIMoveToScanCenter(RequestBase, LamNIMixin):
scan_report_hint = None
scan_type = "step"
required_kwargs = []
arg_input = {
"shift_x": ScanArgType.FLOAT,
"shift_y": ScanArgType.FLOAT,
"angle": ScanArgType.FLOAT,
}
arg_bundle_size = {"bundle": len(arg_input), "min": 1, "max": 1}
arg_input = [ScanArgType.FLOAT, ScanArgType.FLOAT, ScanArgType.FLOAT]
arg_bundle_size = None
def __init__(self, *args, parameter=None, **kwargs):
"""
@@ -207,10 +199,10 @@ class LamNIFermatScan(ScanBase, LamNIMixin):
scan_report_hint = "table"
scan_type = "step"
required_kwargs = ["fov_size", "exp_time", "step", "angle"]
arg_input = {}
arg_bundle_size = {"bundle": len(arg_input), "min": None, "max": None}
arg_input = []
arg_bundle_size = None
def __init__(self, *args, parameter: dict = None, **kwargs):
def __init__(self, *args, parameter=None, **kwargs):
"""
A LamNI scan following Fermat's spiral.
@@ -365,8 +357,7 @@ class LamNIFermatScan(ScanBase, LamNIMixin):
self.stitch_x, self.stitch_y, self.angle
)
logger.info(
f"Total shift [mm] {_shfitx+x_stitch_shift/1000+self.shift_x},"
f" {_shfity+y_stitch_shift/1000+self.shift_y}"
f"Total shift [mm] {_shfitx+x_stitch_shift/1000+self.shift_x}, {_shfity+y_stitch_shift/1000+self.shift_y}"
)
return (
_shfitx + x_stitch_shift / 1000 + self.shift_x,
@@ -443,7 +434,7 @@ class LamNIFermatScan(ScanBase, LamNIMixin):
}
}
)
yield from self.stubs.set(device="lsamrot", value=angle)
yield from self.stubs.set_and_wait(device=["lsamrot"], positions=[angle])
def scan_core(self):
if self.scan_type == "step":
@@ -456,18 +447,17 @@ class LamNIFermatScan(ScanBase, LamNIMixin):
# scan ID before sending the message to the device server
yield from self.stubs.kickoff(device="rtx")
while True:
yield from self.stubs.read(group="monitored")
msg = self.device_manager.connector.get(MessageEndpoints.device_status("rt_scan"))
yield from self.stubs.read_and_wait(group="primary", wait_group="readout_primary")
msg = self.device_manager.producer.get(MessageEndpoints.device_status("rt_scan"))
if msg:
status = msg
status = messages.DeviceStatusMessage.loads(msg)
status_id = status.content.get("status", 1)
request_id = status.metadata.get("RID")
if status_id == 0 and self.metadata.get("RID") == request_id:
break
if status_id == 2 and self.metadata.get("RID") == request_id:
raise ScanAbortion(
"An error occured during the LamNI readout:"
f" {status.metadata.get('error')}"
f"An error occured during the LamNI readout: {status.metadata.get('error')}"
)
time.sleep(1)

View File

@@ -25,9 +25,8 @@ but they are executed in a specific order:
# import numpy as np
# from bec_lib import bec_logger, messages
# from bec_lib.endpoints import MessageEndpoints
# from bec_server.scan_server.errors import ScanAbortion
# from bec_server.scan_server.scans import FlyScanBase, RequestBase, ScanArgType, ScanBase
# from bec_lib import MessageEndpoints, bec_logger, messages
# from scan_server.errors import ScanAbortion
# from scan_server.scans import FlyScanBase, RequestBase, ScanArgType, ScanBase
# logger = bec_logger.logger

View File

@@ -0,0 +1,182 @@
import os
import json
import subprocess
import requests
from bec_lib.file_utils import FileWriterMixin
#from bec_lib.bec_service import SERVICE_CONFIG
class csaxs_archiver:
"""Class to archive data from a beamtime.
To run this script from a shell, go to discovery.psi.ch and copy your token.
Complement the information in user_input below in the if __name__ == __main__ part of the script.
Afterwards, get a Keberos token (kinit) for yourself in the shell.
Activate the bec_venv by doing "source bec_venv/bin/activate" and then run this code via python $filename.
As a last step, adjust the dictionary below in if __name__ == '__main__' with your token as well as information about the experiment
"""
def __init__(
self,
start_scan: int,
stop_scan: int,
base_path: str,
log_path: str,
eacc: str,
pi: str,
pi_email: str,
token: str,
type: str = "raw",
overwrite: bool = False,
online: bool = True,
):
self.start_scan = start_scan
self.stop_scan = stop_scan
self.log_path = os.path.expanduser(log_path)
self.eacc = eacc
self.pi = pi
self.pi_email = pi_email
self.token = token
self.type = type
self.overwrite = overwrite
self.online = online
#from bec_lib.bec_service import SERVICE_CONFIG
#SERVICE_CONFIG.config["service_config"]["file_writer"]
self._load_datacatalogue_module()
self._create_directory(base_path)
self._disable_mail_confirmation()
self.service_cfg = {'base_path' : f'{self.base_path}'}
self.file_writer = FileWriterMixin(self.service_cfg)
def _disable_mail_confirmation(self):
# Define the URL and payload
url = "https://dacat.psi.ch/api/v3/Policies/updatewhere"
payload = {
"ownerGroupList": f'p{self.eacc[1:]}',
"data": '{"archiveEmailNotification": false, "accessGroups": ["slscsaxs"]}'
}
# Define headers
headers = {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
}
# Add the access_token to the URL
url += "?access_token=" + self.token
# Make a POST request
print(url, payload, headers)
response = requests.post(url, data=payload, headers=headers)
# Check the response
if response.status_code == 200:
print("Request was successful.")
print(response.json())
else:
print("Request failed with status code:", response.status_code)
print(response.text)
def _create_directory(self, base_path: str) -> None:
if self.online:
self.base_path = os.path.expanduser("~/Data10")
else:
self.base_path = base_path
if not os.path.exists(self.log_path):
os.makedirs(self.log_path)
def _load_datacatalogue_module(self):
command = 'module add datacatalog/1.1.9'
os.popen(command)
# result = subprocess.run(
# command,
# shell=False,
# stdout=subprocess.PIPE,
# stderr=subprocess.PIPE,
# universal_newlines=True,
# )
# if result.returncode == 0:
# print(f"Command {command} was succesful")
# else:
# print(f"Failed to run command {command} with return message {result.returncode}")
def prep_metadata(self, scannr: int) -> dict:
user_metadata = {}
user_metadata.update(
{
"principalInvestigator": self.pi_email,
"owner": self.pi,
"ownerEmail": self.pi_email,
"sourceFolder": self.base_path,
"creationLocation": "/PSI/SLS/CSAXS",
"type": "raw",
"ownerGroup": f"p{self.eacc.strip('e')}",
"datasetName": f"S{scannr:05d}",
}
)
return user_metadata
def _write_ingestion_log(self, scannr: int) -> None:
...
def run_for_all_scans(self):
for scan in range(self.start_scan, self.stop_scan + 1):
print(f"Start ingestion for scan {scan}")
fname = os.path.join(os.path.expanduser(self.log_path), f"ingestion_log_S{scan:05d}")
self.datafile_name = f"{fname}.txt"
if os.path.isfile(self.datafile_name) and not self.overwrite == True:
print(
f"Skipping scan {scan}, already ingested due to logs, moving on to next scan {scan+1}"
)
continue
user_metadata = self.prep_metadata(scan)
# Write metadata file in json file
self.metadata_file = f"{fname}.json"
with open(self.metadata_file, "w") as file:
json.dump(user_metadata, file)
# Compile datapath based on structure a cSAXS
datadir_path = os.path.join('data', self.file_writer.get_scan_directory(scan, 1000, 5))
print(f"Archiving directory {datadir_path}")
if not os.path.isdir(os.path.join(self.base_path, datadir_path)):
print(f"Did not find directory {datadir_path}, skipping scan {scan}")
continue
# Create datafile path for archiving
with open(self.datafile_name, "w") as file:
file.write(datadir_path)
print(f"Starting ingestion for S#{scan}")
command = f'datasetIngestor -allowexistingsource -ingest -autoarchive -noninteractive -token {self.token} {self.metadata_file} {self.datafile_name}'
rtr = os.popen(command)
#with open(os.path.join(fname,'_log.txt'), "w") as file:
# print(f'Writing reponse to file')
# file.write(rtr.read())
# result = subprocess.run(command, shell=False, stdout = subprocess.PIPE, stderr = subprocess.PIPE, universal_newlines=True)
# if result.returncode == 0:
# print(f"Command {command} was succesful")
# else:
# print(f"Failed to run command {command} with return message {result.returncode}")
if __name__ == "__main__":
# Generate dictionary with user input.
user_input = {
"base_path": "~/Data10",
"eacc": "e20638",
"pi": "Emma Sparr",
"pi_email": "emma.sparr@fkem1.lu.se",
'log_path' : '~/Data10/documentation/ingestion_logs/',
'token' : 'YK8gkmQmEVxVxjiA57D6tVmpBVs7T235nWEuBT0behN9BPM2BdWARWPPgEsQVrPe',
'start_scan' : 1,
'stop_scan' : 450,
}
archiver = csaxs_archiver(**user_input)
archiver.run_for_all_scans()

View File

@@ -0,0 +1,88 @@
import csv
import os
from collections import defaultdict
from collections.abc import Callable
import numpy as np
class ScanItem:
def __init__(self, offset_xy: Callable) -> None:
self.start_entry = None
self.end_entry = None
self.offset_xy = offset_xy
def to_scan_params(self) -> dict:
scan_params = {
"start_x": float(self.start_entry["X"]) + self.offset_xy()[0],
"start_y": float(self.start_entry["Y"]) + self.offset_xy()[1],
"end_x": float(self.end_entry["X"]) + self.offset_xy()[0],
"end_y": float(self.end_entry["Y"]) + self.offset_xy()[1],
"interval_x": int(
np.round(
np.abs(float(self.start_entry["X"]) - float(self.end_entry["X"]))
/ (float(self.start_entry["step_x [mu]"]) * 1e-3)
)
),
"interval_y": int(
np.round(
np.abs(float(self.start_entry["Y"]) - float(self.end_entry["Y"]))
/ (float(self.start_entry["step_y [mu]"]) * 1e-3)
)
),
"exp_time": float(self.start_entry["exp_time [s]"]),
"readout_time": 3e-3,
"md": {"sample_name": self.start_entry["sample name"]},
}
if scan_params["interval_x"] < 1 or scan_params["interval_x"] < 1:
raise ValueError("Bugger off...")
return scan_params
class SAXSParams:
def __init__(self, offset: Callable):
self.offset_xy = offset
self.data = defaultdict(lambda: ScanItem(offset))
def load_from_csv(self, file_path: str) -> None:
"""
Load the acquisition parameter from a csv file.
"""
if not os.path.exists(file_path):
raise FileNotFoundError(
f"The specified CSV file could not be found. Please check that the given path is correct: {file_path}."
)
data_transposed = defaultdict(lambda: [])
with open(os.path.expanduser(file_path), "r") as file:
csv_reader = csv.DictReader(file)
for row in csv_reader:
for key, val in row.items():
data_transposed[key].append(val)
if int(row["start"]):
self.data[row["sample name"]].start_entry = row
else:
self.data[row["sample name"]].end_entry = row
self._check_params(dict(data_transposed))
def _check_params(self, data_transposed: dict) -> dict:
sample_names = set(data_transposed["sample name"])
if len(data_transposed["start"]) != len(sample_names) * 2:
raise ValueError(
f"The given params file does not provide N*2 start/stop positions. Found {len(sample_names)} samples and {len(data_transposed['start'])} start/stop positions."
)
if __name__ == "__main__":
from pprint import pprint
INPUT_FILE = "/sls/X12SA/data/e21206/Data10/software/test_script.csv"
def my_offset():
return [0, 0]
params = SAXSParams(my_offset)
params.load_from_csv(INPUT_FILE)
for key in params.data:
pprint(params.data[key].to_scan_params())

View File

@@ -1,7 +1,3 @@
""" Script used for parsing scan parameters from a CSV file as created by the motormap GUI.
This needs to be reviewed and tested during the deployment phase."""
# pylint: skip-file
import csv
import os
from collections import defaultdict

View File

@@ -0,0 +1,13 @@
import yaml
CONFIG_PATH = "/sls/X12SA/data/gac-x12saop/bec/config/bec_service_config.yaml"
def load_service_config() -> dict:
"""Load the service configuration from the YAML file."""
with open(CONFIG_PATH, "r", encoding="utf-8") as stream:
try:
config = yaml.safe_load(stream)
except yaml.YAMLError as exc:
print(exc)
return config

1
bin/.gitignore vendored
View File

@@ -1 +0,0 @@
# Add anything you don't want to check in to git, e.g. very large files

View File

@@ -3,9 +3,8 @@ from __future__ import annotations
import os
import subprocess
from bec_lib import bec_logger, messages
from bec_lib.endpoints import MessageEndpoints
from bec_lib.redis_connector import MessageObject, RedisConnector
from bec_lib import MessageEndpoints, RedisConnector, bec_logger, messages
from bec_lib.redis_connector import MessageObject
logger = bec_logger.logger
@@ -13,6 +12,7 @@ logger = bec_logger.logger
class PilatusConverter:
def __init__(self, host: str, port: int) -> None:
self._connector = RedisConnector(f"{host}:{port}")
self._producer = self._connector.producer()
def start(self) -> None:
"""start the consumer"""
@@ -27,7 +27,7 @@ class PilatusConverter:
message (MessageObject): Message object
parent (PilatusConverter): Parent object
"""
msg = message.value
msg = messages.MessageReader.loads(message.value)
print(msg)
if not msg:
return
@@ -56,9 +56,10 @@ class PilatusConverter:
"""
Start the consumer.
"""
self._connector.register(
file_consumer = self._connector.consumer(
MessageEndpoints.file_event("pilatus_2"), cb=self.on_new_message, parent=self
)
file_consumer.start()
if __name__ == "__main__":

5
bin/open_tunnel.sh Executable file
View File

@@ -0,0 +1,5 @@
for i in `seq 1 8`
do
ssh -N -R 6379:localhost:6379 x12sa-cn-$i &
done

48
bin/setup_bec.sh Executable file
View File

@@ -0,0 +1,48 @@
if [ "pc15543.psi.ch" != "$(hostname)" ]; then
echo "Please run this script on pc15543"
exit 1
fi
module use unstable
module add psi-python311/2024.02
echo module add tmux/3.2 >> ~/.bashrc
echo module add redis/7.0.12 >> ~/.bashrc
source ~/.bashrc
cd ~/Data10
mkdir -p software/
mkdir -p ~/bec/scripts
cd software
git clone https://gitlab.psi.ch/bec/bec.git
git clone https://gitlab.psi.ch/bec/ophyd_devices.git
git clone https://gitlab.psi.ch/bec/bec-widgets.git
python -m venv ./bec_venv
source ./bec_venv/bin/activate
cd bec
git checkout sastt-online-changes
pip install -e ./bec_server[dev]
cd ../csaxs-bec
pip install -e .[dev]
#redis-server --protected-mode no &
read -p "Do you want to set the current BEC account to $(whoami)? (yes/no) " yn
val=$(whoami)
case $yn in
yes ) echo ok, setting account to $val;
redis-cli SET internal/account:val $val;;
no ) echo ;;
* ) echo invalid response;
exit 1;;
esac
$(pwd)/open_tunnel.sh

17
bin/setup_bec_widgets.sh Executable file
View File

@@ -0,0 +1,17 @@
module use unstable
module add psi-python311/2024.02
cd ~/Data10/software
python -m venv ./bec_widgets_venv
source ./bec_widgets_venv/bin/activate
pip install --upgrade pip
cd ~/Data10/software/bec/bec_lib
pip install -e .
cd ~/Data10/software/csaxs-bec
pip install -e .
cd ~/Data10/software/bec-widgets
pip install -e .
echo "For the moment widgets only run on beamline consoles comp1/2 and cons1"

View File

@@ -1,2 +0,0 @@
from .load_additional_correction import lamni_read_additional_correction
from .x_ray_eye_align import DataDrivenLamNI, LamNI, MagLamNI, XrayEyeAlign

View File

@@ -1,329 +0,0 @@
import builtins
import time
from rich import box
from rich.console import Console
from rich.table import Table
from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_put, fshclose
# import builtins to avoid linter errors
dev = builtins.__dict__.get("dev")
umv = builtins.__dict__.get("umv")
bec = builtins.__dict__.get("bec")
class LamNIInitError(Exception):
pass
class LaMNIInitStagesMixin:
def lamni_init_stages(self):
user_input = input("Starting initialization of LamNI stages. OK? [y/n]")
if user_input == "y":
print("staring...")
dev.lsamrot.enabled = True
else:
return
if self.check_all_axes_of_lamni_referenced():
user_input = input("Continue anyways? [y/n]")
if user_input == "y":
print("ok then...")
else:
return
axis_id_lsamrot = dev.lsamrot._config["deviceConfig"].get("axis_Id")
if dev.lsamrot.controller.get_motor_limit_switch(axis_id_lsamrot)[1] == False:
user_input = input("The rotation stage will be moved to one limit [y/n]")
if user_input == "y":
print("starting...")
else:
return
self.drive_axis_to_limit(dev.lsamrot, "forward")
dev.lsamrot.enabled = False
print("Now hard reboot the controller and run the initialization routine again.")
print("The controller will be disabled in bec. To enable dev.lsamrot.enabled=True")
return
user_input = input(
"Init of loptz. Can the stage move to the upstream limit without collision?? [y/n]"
)
if user_input == "y":
print("ok then...")
else:
return
print("Referencing loptz")
self.drive_axis_to_limit(dev.loptz, "forward")
self.find_reference_mark(dev.loptz)
print("Referencing loptx")
self.drive_axis_to_limit(dev.loptx, "reverse")
self.find_reference_mark(dev.loptx)
print("Referencing lopty")
self.drive_axis_to_limit(dev.lopty, "forward")
self.find_reference_mark(dev.lopty)
print("Referencing lsamx")
self.drive_axis_to_limit(dev.lsamx, "forward")
self.find_reference_mark(dev.lsamx)
print("Referencing lsamy")
self.drive_axis_to_limit(dev.lsamy, "reverse")
self.find_reference_mark(dev.lsamy)
# the dual encoder requires the reference mark to pass on both encoders
print("Referencing lsamrot")
self.drive_axis_to_limit(dev.lsamrot, "reverse")
time.sleep(0.1)
self.find_reference_mark(dev.lsamrot)
user_input = input("Init of leye. Can the stage move to -x limit without collision? [y/n]")
if user_input == "y":
print("starting...")
else:
return
print("Referencing leyex")
self.drive_axis_to_limit(dev.leyex, "forward")
print("Referencing leyey")
self.drive_axis_to_limit(dev.leyey, "forward")
# set_lm lsamx 6 14
# set_lm lsamy 6 14
# set_lm lsamrot -3 362
# set_lm loptx -1 -0.2
# set_lm lopty 3.0 3.6
# set_lm loptz 82 87
# set_lm leyex 0 25
# set_lm leyey 0.5 50
print("Init of Smaract stages")
dev.losax.controller.find_reference_mark(2, 0, 1000, 1)
time.sleep(1)
dev.losax.controller.find_reference_mark(0, 0, 1000, 1)
time.sleep(1)
dev.losax.controller.find_reference_mark(1, 0, 1000, 1)
time.sleep(1)
# dev.losax.controller.find_reference_mark(3, 1, 1000, 1)
# time.sleep(1)
# dev.losax.controller.find_reference_mark(4, 1, 1000, 1)
# time.sleep(1)
# set_lm losax -1.5 0.25
# set_lm losay -2.5 4.1
# set_lm losaz -4.1 -0.5
# set_lm lcsy -1.5 5
self._align_setup()
def find_reference_mark(self, device):
axis_id = device._config["deviceConfig"].get("axis_Id")
axis_id_numeric = self.axis_id_to_numeric(axis_id)
device.controller.find_reference(axis_id_numeric)
def drive_axis_to_limit(self, device, direction):
axis_id = device._config["deviceConfig"].get("axis_Id")
axis_id_numeric = self.axis_id_to_numeric(axis_id)
device.controller.drive_axis_to_limit(axis_id_numeric, direction)
def axis_id_to_numeric(self, axis_id) -> int:
return ord(axis_id.lower()) - 97
def _align_setup(self):
user_input = input("Start moving stages to default initial positions? [y/n]")
if user_input == "y":
print("Start moving stages...")
else:
print("Stopping.")
return
lsamx_center = dev.lsamx.user_parameter.get("center")
if lsamx_center is None:
raise LamNIInitError(
"Could not find a lsamx center position. Please check your device config."
)
lsamy_center = dev.lsamy.user_parameter.get("center")
if lsamy_center is None:
raise LamNIInitError(
"Could not find a lsamy center position. Please check your device config."
)
umv(dev.lsamx, lsamx_center, dev.lsamy, lsamy_center, dev.loptx, -0.3, dev.lopty, 0)
umv(dev.losax, -1)
umv(dev.loptz, 82.25)
umv(dev.lsamrot, -1)
umv(dev.lsamrot, 0)
time.sleep(2)
dev.rtx.controller.feedback_disable_and_even_reset_lamni_angle_interferometer()
def check_all_axes_of_lamni_referenced(self):
if (
dev.losax.controller.axis_is_referenced(0)
& dev.losax.controller.axis_is_referenced(1)
& dev.losax.controller.axis_is_referenced(2)
& dev.lsamx.controller.all_axes_referenced()
):
print("All axes of LamNI are referenced.")
return True
else:
return False
class LamNIOpticsMixin:
@staticmethod
def _get_user_param_safe(device, var):
param = dev[device].user_parameter
if not param or param.get(var) is None:
raise ValueError(f"Device {device} has no user parameter definition for {var}.")
return param.get(var)
def leye_out(self):
self.loptics_in()
fshclose()
leyey_out = self._get_user_param_safe("leyey", "out")
umv(dev.leyey, leyey_out)
epics_put("XOMNYI-XEYE-ACQ:0", 2)
# move rotation stage to zero to avoid problems with wires
umv(dev.lsamrot, 0)
umv(dev.dttrz, 5854, dev.fttrz, 2395)
def leye_in(self):
bec.queue.next_dataset_number += 1
# move rotation stage to zero to avoid problems with wires
umv(dev.lsamrot, 0)
umv(dev.dttrz, 6419.677, dev.fttrz, 2959.979)
while True:
moved_out = (input("Did the flight tube move out? (Y/n)") or "y").lower()
if moved_out == "y":
break
if moved_out == "n":
return
leyex_in = self._get_user_param_safe("leyex", "in")
leyey_in = self._get_user_param_safe("leyey", "in")
umv(dev.leyex, leyex_in, dev.leyey, leyey_in)
self.align.update_frame()
def _lfzp_in(self):
loptx_in = self._get_user_param_safe("loptx", "in")
lopty_in = self._get_user_param_safe("lopty", "in")
umv(
dev.loptx, loptx_in, dev.lopty, lopty_in
) # for 7.2567 keV and 150 mu, 60 nm fzp, loptz 83.6000 for propagation 1.4 mm
def lfzp_in(self):
"""
move in the lamni zone plate.
This will disable rt feedback, move the FZP and re-enabled the feedback.
"""
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_disable()
self._lfzp_in()
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_enable_with_reset()
def loptics_in(self):
"""
Move in the lamni optics, including the FZP and the OSA.
"""
self.lfzp_in()
self.losa_in()
def loptics_out(self):
"""Move out the lamni optics"""
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_disable()
# self.lcs_out()
self.losa_out()
loptx_out = self._get_user_param_safe("loptx", "out")
lopty_out = self._get_user_param_safe("lopty", "out")
umv(dev.loptx, loptx_out, dev.lopty, lopty_out)
if "rtx" in dev and dev.rtx.enabled:
time.sleep(1)
dev.rtx.controller.feedback_enable_with_reset()
def lcs_in(self):
# umv lcsx -1.852 lcsy -0.095
pass
def lcs_out(self):
umv(dev.lcsy, 3)
def losa_in(self):
# 6.2 keV, 170 um FZP
# umv(dev.losax, -1.4450000, dev.losay, -0.1800)
# umv(dev.losaz, -1)
# 6.7, 170
# umv(dev.losax, -1.4850, dev.losay, -0.1930)
# umv(dev.losaz, 1.0000)
# 7.2, 150
losax_in = self._get_user_param_safe("losax", "in")
losay_in = self._get_user_param_safe("losay", "in")
losaz_in = self._get_user_param_safe("losaz", "in")
umv(dev.losax, losax_in, dev.losay, losay_in)
umv(dev.losaz, losaz_in)
# 11 kev
# umv(dev.losax, -1.161000, dev.losay, -0.196)
# umv(dev.losaz, 1.0000)
def losa_out(self):
losay_out = self._get_user_param_safe("losay", "out")
losaz_out = self._get_user_param_safe("losaz", "out")
umv(dev.losaz, losaz_out)
umv(dev.losay, losay_out)
def lfzp_info(self, mokev_val=-1):
if mokev_val == -1:
try:
mokev_val = dev.mokev.readback.get()
except:
print(
"Device mokev does not exist. You can specify the energy in keV as an argument instead."
)
return
loptz_val = dev.loptz.read()["loptz"]["value"]
distance = -loptz_val + 85.6 + 52
print(f"The sample is in a distance of {distance:.1f} mm from the FZP.")
diameters = [80e-6, 100e-6, 120e-6, 150e-6, 170e-6, 200e-6, 220e-6, 250e-6]
console = Console()
table = Table(
title=f"At the current energy of {mokev_val:.4f} keV we have following options:",
box=box.SQUARE,
)
table.add_column("Diameter", justify="center")
table.add_column("Focal distance", justify="center")
table.add_column("Current beam size", justify="center")
wavelength = 1.2398e-9 / mokev_val
for diameter in diameters:
outermost_zonewidth = 60e-9
focal_distance = diameter * outermost_zonewidth / wavelength
beam_size = (
-diameter / (focal_distance * 1000) * (focal_distance * 1000 - distance) * 1e6
)
table.add_row(
f"{diameter*1e6:.2f} microns",
f"{focal_distance:.2f} mm",
f"{beam_size:.2f} microns",
)
console.print(table)
print("OSA Information:")
# print(f"Current losaz %.1f\n", A[losaz])
# print("The OSA will collide with the sample plane at %.1f\n\n", 89.3-A[loptz])
print(
"The numbers presented here are for a sample in the plane of the lamni sample holder.\n"
)

View File

@@ -1,2 +0,0 @@
from .cSAXS_beamline import epics_get, epics_put, fshclose, fshopen, fshstatus
from .csaxs_bl_checks import cSAXSBeamlineChecks

View File

@@ -1,122 +0,0 @@
import builtins
import datetime
import threading
import time
from bec_lib import bec_logger
logger = bec_logger.logger
if builtins.__dict__.get("bec"):
bec = builtins.__dict__.get("bec")
class cSAXSBeamlineChecks:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.check_shutter = True
self.check_light_available = True
self.check_fofb = True
self._check_msgs = []
self._beam_is_okay = True
self._stop_beam_check_event = None
self.beam_check_thread = None
def get_beamline_checks_enabled(self):
print(
f"Shutter: {self.check_shutter}\nFOFB: {self.check_fofb}\nLight available:"
f" {self.check_light_available}"
)
@property
def beamline_checks_enabled(self):
return {
"shutter": self.check_shutter,
"fofb": self.check_fofb,
"light available": self.check_light_available,
}
@beamline_checks_enabled.setter
def beamline_checks_enabled(self, val: bool):
self.check_shutter = val
self.check_light_available = val
self.check_fofb = val
self.get_beamline_checks_enabled()
def _run_beamline_checks(self):
msgs = []
dev = builtins.__dict__.get("dev")
try:
if self.check_shutter:
shutter_val = dev.x12sa_es1_shutter_status.read(cached=True)
if shutter_val["value"].lower() != "open":
self._beam_is_okay = False
msgs.append("Check beam failed: Shutter is closed.")
if self.check_light_available:
machine_status = dev.sls_machine_status.read(cached=True)
if machine_status["value"] not in ["Light Available", "Light-Available"]:
self._beam_is_okay = False
msgs.append("Check beam failed: Light not available.")
if self.check_fofb:
fast_orbit_feedback = dev.sls_fast_orbit_feedback.read(cached=True)
if fast_orbit_feedback["value"] != "running":
self._beam_is_okay = False
msgs.append("Check beam failed: Fast orbit feedback is not running.")
except Exception:
logger.warning("Failed to check beam.")
return msgs
def _check_beam(self):
while not self._stop_beam_check_event.is_set():
self._check_msgs = self._run_beamline_checks()
if not self._beam_is_okay:
self._stop_beam_check_event.set()
time.sleep(1)
def _start_beam_check(self):
self._beam_is_okay = True
self._stop_beam_check_event = threading.Event()
self.beam_check_thread = threading.Thread(target=self._check_beam, daemon=True)
self.beam_check_thread.start()
def _was_beam_okay(self):
self._stop_beam_check_event.set()
self.beam_check_thread.join()
return self._beam_is_okay
def _print_beamline_checks(self):
for msg in self._check_msgs:
logger.warning(msg)
def _wait_for_beamline_checks(self):
self._print_beamline_checks()
try:
msg = bec.logbook.LogbookMessage()
msg.add_text(
"<p><mark class='pen-red'><strong>Beamline checks failed at"
f" {str(datetime.datetime.now())}: {''.join(self._check_msgs)}</strong></mark></p>"
).add_tag(["BEC", "beam_check"])
bec.logbook.send_logbook_message(msg)
except Exception:
logger.warning("Failed to send update to SciLog.")
while True:
self._beam_is_okay = True
self._check_msgs = self._run_beamline_checks()
if self._beam_is_okay:
break
self._print_beamline_checks()
time.sleep(1)
try:
msg = bec.logbook.LogbookMessage()
msg.add_text(
"<p><mark class='pen-red'><strong>Operation resumed at"
f" {str(datetime.datetime.now())}.</strong></mark></p>"
).add_tag(["BEC", "beam_check"])
bec.logbook.send_logbook_message(msg)
except Exception:
logger.warning("Failed to send update to SciLog.")

View File

@@ -1 +0,0 @@
from .flomni import Flomni

View File

@@ -1,351 +0,0 @@
corr_elements = 175
corr_angle[0] = 0.100000
corr_angle[1] = 1.001000
corr_angle[2] = 1.902000
corr_angle[3] = 2.798000
corr_angle[4] = 7.305000
corr_angle[5] = 8.204000
corr_angle[6] = 9.104000
corr_angle[7] = 10.005000
corr_angle[8] = 14.504000
corr_angle[9] = 15.404000
corr_angle[10] = 16.304000
corr_angle[11] = 17.204000
corr_angle[12] = 21.704000
corr_angle[13] = 22.604000
corr_angle[14] = 23.504000
corr_angle[15] = 24.404000
corr_angle[16] = 28.904000
corr_angle[17] = 29.804000
corr_angle[18] = 30.704000
corr_angle[19] = 31.604000
corr_angle[20] = 36.104000
corr_angle[21] = 37.004000
corr_angle[22] = 37.904000
corr_angle[23] = 38.804000
corr_angle[24] = 43.305000
corr_angle[25] = 44.205000
corr_angle[26] = 45.104000
corr_angle[27] = 46.005000
corr_angle[28] = 50.504000
corr_angle[29] = 51.404000
corr_angle[30] = 52.305000
corr_angle[31] = 53.205000
corr_angle[32] = 57.705000
corr_angle[33] = 58.605000
corr_angle[34] = 59.505000
corr_angle[35] = 60.405000
corr_angle[36] = 64.904000
corr_angle[37] = 65.804000
corr_angle[38] = 66.704000
corr_angle[39] = 67.604000
corr_angle[40] = 72.104000
corr_angle[41] = 73.004000
corr_angle[42] = 73.904000
corr_angle[43] = 74.804000
corr_angle[44] = 79.304000
corr_angle[45] = 80.204000
corr_angle[46] = 81.104000
corr_angle[47] = 82.005000
corr_angle[48] = 86.505000
corr_angle[49] = 87.404000
corr_angle[50] = 88.304000
corr_angle[51] = 89.205000
corr_angle[52] = 93.704000
corr_angle[53] = 94.604000
corr_angle[54] = 95.505000
corr_angle[55] = 96.404000
corr_angle[56] = 100.904000
corr_angle[57] = 101.804000
corr_angle[58] = 102.704000
corr_angle[59] = 103.604000
corr_angle[60] = 108.104000
corr_angle[61] = 109.004000
corr_angle[62] = 109.904000
corr_angle[63] = 110.804000
corr_angle[64] = 115.304000
corr_angle[65] = 116.204000
corr_angle[66] = 117.104000
corr_angle[67] = 118.004000
corr_angle[68] = 122.504000
corr_angle[69] = 123.404000
corr_angle[70] = 124.304000
corr_angle[71] = 125.204000
corr_angle[72] = 129.704000
corr_angle[73] = 130.604000
corr_angle[74] = 131.504000
corr_angle[75] = 132.404000
corr_angle[76] = 136.904000
corr_angle[77] = 137.804000
corr_angle[78] = 138.701000
corr_angle[79] = 139.604000
corr_angle[80] = 144.104000
corr_angle[81] = 145.004000
corr_angle[82] = 145.904000
corr_angle[83] = 146.804000
corr_angle[84] = 151.304000
corr_angle[85] = 152.204000
corr_angle[86] = 153.104000
corr_angle[87] = 154.004000
corr_angle[88] = 158.504000
corr_angle[89] = 159.404000
corr_angle[90] = 160.304000
corr_angle[91] = 161.204000
corr_angle[92] = 165.704000
corr_angle[93] = 166.604000
corr_angle[94] = 167.504000
corr_angle[95] = 168.404000
corr_angle[96] = 172.904000
corr_angle[97] = 173.805000
corr_angle[98] = 174.704000
corr_angle[99] = 180.104000
corr_angle[100] = 183.704000
corr_angle[101] = 184.603000
corr_angle[102] = 185.504000
corr_angle[103] = 190.904000
corr_angle[104] = 191.805000
corr_angle[105] = 192.704000
corr_angle[106] = 198.104000
corr_angle[107] = 199.004000
corr_angle[108] = 199.904000
corr_angle[109] = 205.304000
corr_angle[110] = 206.204000
corr_angle[111] = 207.104000
corr_angle[112] = 212.504000
corr_angle[113] = 213.404000
corr_angle[114] = 214.304000
corr_angle[115] = 219.704000
corr_angle[116] = 220.604000
corr_angle[117] = 221.504000
corr_angle[118] = 226.904000
corr_angle[119] = 227.804000
corr_angle[120] = 228.704000
corr_angle[121] = 234.104000
corr_angle[122] = 235.004000
corr_angle[123] = 235.904000
corr_angle[124] = 241.304000
corr_angle[125] = 242.204000
corr_angle[126] = 243.104000
corr_angle[127] = 248.504000
corr_angle[128] = 249.404000
corr_angle[129] = 250.304000
corr_angle[130] = 255.704000
corr_angle[131] = 256.604000
corr_angle[132] = 257.504000
corr_angle[133] = 262.904000
corr_angle[134] = 263.804000
corr_angle[135] = 264.704000
corr_angle[136] = 270.104000
corr_angle[137] = 271.004000
corr_angle[138] = 271.904000
corr_angle[139] = 277.304000
corr_angle[140] = 278.205000
corr_angle[141] = 279.104000
corr_angle[142] = 284.504000
corr_angle[143] = 285.405000
corr_angle[144] = 286.304000
corr_angle[145] = 291.703000
corr_angle[146] = 292.604000
corr_angle[147] = 293.504000
corr_angle[148] = 298.904000
corr_angle[149] = 299.804000
corr_angle[150] = 300.704000
corr_angle[151] = 306.104000
corr_angle[152] = 307.004000
corr_angle[153] = 307.904000
corr_angle[154] = 313.304000
corr_angle[155] = 314.204000
corr_angle[156] = 315.104000
corr_angle[157] = 320.504000
corr_angle[158] = 321.404000
corr_angle[159] = 322.304000
corr_angle[160] = 327.704000
corr_angle[161] = 328.605000
corr_angle[162] = 329.504000
corr_angle[163] = 334.904000
corr_angle[164] = 335.804000
corr_angle[165] = 336.705000
corr_angle[166] = 342.104000
corr_angle[167] = 343.004000
corr_angle[168] = 343.904000
corr_angle[169] = 349.304000
corr_angle[170] = 350.204000
corr_angle[171] = 351.104000
corr_angle[172] = 356.504000
corr_angle[173] = 357.404000
corr_angle[174] = 358.304000
corr_pos[0] = 0.012330
corr_pos[1] = 0.024870
corr_pos[2] = 0.037262
corr_pos[3] = 0.049438
corr_pos[4] = 0.108462
corr_pos[5] = 0.119791
corr_pos[6] = 0.130986
corr_pos[7] = 0.142044
corr_pos[8] = 0.195045
corr_pos[9] = 0.205834
corr_pos[10] = 0.216589
corr_pos[11] = 0.226402
corr_pos[12] = 0.271931
corr_pos[13] = 0.281684
corr_pos[14] = 0.290769
corr_pos[15] = 0.299392
corr_pos[16] = 0.337986
corr_pos[17] = 0.345178
corr_pos[18] = 0.352253
corr_pos[19] = 0.358528
corr_pos[20] = 0.379520
corr_pos[21] = 0.382947
corr_pos[22] = 0.386110
corr_pos[23] = 0.388887
corr_pos[24] = 0.395645
corr_pos[25] = 0.396154
corr_pos[26] = 0.396679
corr_pos[27] = 0.396367
corr_pos[28] = 0.392400
corr_pos[29] = 0.392530
corr_pos[30] = 0.391826
corr_pos[31] = 0.391325
corr_pos[32] = 0.379188
corr_pos[33] = 0.376449
corr_pos[34] = 0.373195
corr_pos[35] = 0.369588
corr_pos[36] = 0.343021
corr_pos[37] = 0.336754
corr_pos[38] = 0.330310
corr_pos[39] = 0.323447
corr_pos[40] = 0.281171
corr_pos[41] = 0.272735
corr_pos[42] = 0.263888
corr_pos[43] = 0.254469
corr_pos[44] = 0.203024
corr_pos[45] = 0.192980
corr_pos[46] = 0.182359
corr_pos[47] = 0.170780
corr_pos[48] = 0.111580
corr_pos[49] = 0.101222
corr_pos[50] = 0.090051
corr_pos[51] = 0.077918
corr_pos[52] = 0.015225
corr_pos[53] = 0.003876
corr_pos[54] = -0.007756
corr_pos[55] = -0.020383
corr_pos[56] = -0.084910
corr_pos[57] = -0.096848
corr_pos[58] = -0.108732
corr_pos[59] = -0.121432
corr_pos[60] = -0.184349
corr_pos[61] = -0.195904
corr_pos[62] = -0.207113
corr_pos[63] = -0.219319
corr_pos[64] = -0.274799
corr_pos[65] = -0.284942
corr_pos[66] = -0.294809
corr_pos[67] = -0.305437
corr_pos[68] = -0.352114
corr_pos[69] = -0.359689
corr_pos[70] = -0.367451
corr_pos[71] = -0.375629
corr_pos[72] = -0.404347
corr_pos[73] = -0.407683
corr_pos[74] = -0.410857
corr_pos[75] = -0.414904
corr_pos[76] = -0.424625
corr_pos[77] = -0.424357
corr_pos[78] = -0.424411
corr_pos[79] = -0.424615
corr_pos[80] = -0.421392
corr_pos[81] = -0.419977
corr_pos[82] = -0.417860
corr_pos[83] = -0.416400
corr_pos[84] = -0.398108
corr_pos[85] = -0.392613
corr_pos[86] = -0.386455
corr_pos[87] = -0.380338
corr_pos[88] = -0.341288
corr_pos[89] = -0.332298
corr_pos[90] = -0.322767
corr_pos[91] = -0.311630
corr_pos[92] = -0.256798
corr_pos[93] = -0.244808
corr_pos[94] = -0.232932
corr_pos[95] = -0.219785
corr_pos[96] = -0.158685
corr_pos[97] = -0.146198
corr_pos[98] = -0.130743
corr_pos[99] = -0.054066
corr_pos[100] = -0.001498
corr_pos[101] = 0.012010
corr_pos[102] = 0.025195
corr_pos[103] = 0.094982
corr_pos[104] = 0.109235
corr_pos[105] = 0.120813
corr_pos[106] = 0.179893
corr_pos[107] = 0.192147
corr_pos[108] = 0.201902
corr_pos[109] = 0.245092
corr_pos[110] = 0.250501
corr_pos[111] = 0.255536
corr_pos[112] = 0.280598
corr_pos[113] = 0.280673
corr_pos[114] = 0.282529
corr_pos[115] = 0.293286
corr_pos[116] = 0.292713
corr_pos[117] = 0.291123
corr_pos[118] = 0.288721
corr_pos[119] = 0.288260
corr_pos[120] = 0.286480
corr_pos[121] = 0.271630
corr_pos[122] = 0.268004
corr_pos[123] = 0.265418
corr_pos[124] = 0.239187
corr_pos[125] = 0.233224
corr_pos[126] = 0.226652
corr_pos[127] = 0.189034
corr_pos[128] = 0.180302
corr_pos[129] = 0.170931
corr_pos[130] = 0.125760
corr_pos[131] = 0.116433
corr_pos[132] = 0.106845
corr_pos[133] = 0.057551
corr_pos[134] = 0.048531
corr_pos[135] = 0.038276
corr_pos[136] = -0.012292
corr_pos[137] = -0.021223
corr_pos[138] = -0.030046
corr_pos[139] = -0.077647
corr_pos[140] = -0.085863
corr_pos[141] = -0.093816
corr_pos[142] = -0.138532
corr_pos[143] = -0.145584
corr_pos[144] = -0.152699
corr_pos[145] = -0.192594
corr_pos[146] = -0.200980
corr_pos[147] = -0.208816
corr_pos[148] = -0.241983
corr_pos[149] = -0.248319
corr_pos[150] = -0.253789
corr_pos[151] = -0.276869
corr_pos[152] = -0.279240
corr_pos[153] = -0.281538
corr_pos[154] = -0.294317
corr_pos[155] = -0.295586
corr_pos[156] = -0.296029
corr_pos[157] = -0.297443
corr_pos[158] = -0.297185
corr_pos[159] = -0.295946
corr_pos[160] = -0.284463
corr_pos[161] = -0.282598
corr_pos[162] = -0.281260
corr_pos[163] = -0.256509
corr_pos[164] = -0.250326
corr_pos[165] = -0.244372
corr_pos[166] = -0.204043
corr_pos[167] = -0.196147
corr_pos[168] = -0.187928
corr_pos[169] = -0.131831
corr_pos[170] = -0.121351
corr_pos[171] = -0.110548
corr_pos[172] = -0.038951
corr_pos[173] = -0.025887
corr_pos[174] = -0.012501

File diff suppressed because it is too large Load Diff

View File

@@ -1,177 +0,0 @@
import time
from rich import box
from rich.console import Console
from rich.table import Table
from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_put, fshclose
class FlomniOpticsMixin:
@staticmethod
def _get_user_param_safe(device, var):
param = dev[device].user_parameter
if not param or param.get(var) is None:
raise ValueError(f"Device {device} has no user parameter definition for {var}.")
return param.get(var)
def feye_out(self):
fshclose()
self.foptics_in()
feyex_out = self._get_user_param_safe("feyex", "out")
umv(dev.feyex, feyex_out)
epics_put("XOMNYI-XEYE-ACQ:0", 2)
# move rotation stage to zero to avoid problems with wires
umv(dev.fsamroy, 0)
# umv(dev.fttrx1, 9.2)
def feye_in(self):
bec.queue.next_dataset_number += 1
# umv(dev.fttrx1, -17)
feyex_in = self._get_user_param_safe("feyex", "in")
feyey_in = self._get_user_param_safe("feyey", "in")
umv(dev.feyex, feyex_in, dev.feyey, feyey_in)
#self.align.update_frame()
def _ffzp_in(self):
foptx_in = self._get_user_param_safe("foptx", "in")
fopty_in = self._get_user_param_safe("fopty", "in")
umv(dev.foptx, foptx_in)
umv(
dev.fopty, fopty_in
) # for 7.2567 keV and 150 mu, 60 nm fzp, loptz 83.6000 for propagation 1.4 mm
def ffzp_in(self):
"""
move in the flomni zone plate.
This will disable rt feedback, move the FZP and re-enabled the feedback.
"""
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_disable()
self._ffzp_in()
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_enable_with_reset()
def foptics_in(self):
"""
Move in the flomni optics, including the FZP and the OSA.
"""
self.ffzp_in()
self.fosa_in()
def foptics_out(self):
"""Move out the flomni optics"""
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_disable()
self.fosa_out()
fopty_out = self._get_user_param_safe("fopty", "out")
umv(dev.fopty, fopty_out)
if "rtx" in dev and dev.rtx.enabled:
time.sleep(1)
dev.rtx.controller.feedback_enable_with_reset()
def fosa_in(self):
# 6.2 keV, 170 um FZP
# umv(dev.losax, -1.4450000, dev.losay, -0.1800)
# umv(dev.losaz, -1)
# 6.7, 170
# umv(dev.losax, -1.4850, dev.losay, -0.1930)
# umv(dev.losaz, 1.0000)
# 7.2, 150
fosax_in = self._get_user_param_safe("fosax", "in")
fosay_in = self._get_user_param_safe("fosay", "in")
fosaz_in = self._get_user_param_safe("fosaz", "in")
dev.fosax.limits = [fosax_in - 0.1, fosax_in + 0.1]
dev.fosay.limits = [fosay_in - 0.1, fosay_in + 0.1]
dev.fosaz.limits = [fosaz_in - 0.1, fosaz_in + 0.1]
umv(dev.fosax, fosax_in, dev.fosay, fosay_in)
umv(dev.fosaz, fosaz_in)
# 11 kev
# umv(dev.losax, -1.161000, dev.losay, -0.196)
# umv(dev.losaz, 1.0000)
def fosa_out(self):
self.ensure_fheater_up()
curtain_is_triggered = dev.foptz.controller.fosaz_light_curtain_is_triggered()
if not curtain_is_triggered:
fosaz_out = self._get_user_param_safe("fosaz", "out")
dev.fosaz.limits = [fosaz_out - 0.1, fosaz_out + 0.1]
umv(dev.fosaz, fosaz_out)
fosax_out = self._get_user_param_safe("fosax", "out")
dev.fosax.limits = [fosax_out - 0.1, fosax_out + 0.1]
umv(dev.fosax, fosax_out)
def ffzp_info(self, mokev_val=-1):
if mokev_val == -1:
try:
mokev_val = dev.mokev.readback.get()
except:
print(
"Device mokev does not exist. You can specify the energy in keV as an argument instead."
)
return
foptz_val = dev.foptz.readback.get()
distance = -foptz_val + 43.15 + 36.7
print(f"\nThe sample is in a distance of \033[1m{distance:.1f} mm\033[0m from the FZP.\n")
print(f"At the current energy of {mokev_val:.4f} keV we have following options:\n")
diameters = [80e-6, 100e-6, 120e-6, 150e-6, 170e-6, 200e-6, 220e-6, 250e-6]
console = Console()
table = Table(title="Outermost zone width \033[1m60 nm\033[0m", box=box.SQUARE)
table.add_column("Diameter", justify="center")
table.add_column("Focal distance", justify="center")
table.add_column("Current beam size", justify="center")
wavelength = 1.2398e-9 / mokev_val
for diameter in diameters:
outermost_zonewidth = 60e-9
focal_distance = diameter * outermost_zonewidth / wavelength * 1000
beam_size = -diameter / (focal_distance * 1000) * (focal_distance - distance) * 1e9
table.add_row(
f"{diameter*1e6:.2f} microns",
f"{focal_distance:.2f} mm",
f"{beam_size:.2f} microns",
)
console.print(table)
diameters = [150e-6, 250e-6]
console = Console()
table = Table(title="Outermost zone width \033[1m30 nm\033[0m", box=box.SQUARE)
table.add_column("Diameter", justify="center")
table.add_column("Focal distance", justify="center")
table.add_column("Current beam size", justify="center")
wavelength = 1.2398e-9 / mokev_val
for diameter in diameters:
outermost_zonewidth = 30e-9
focal_distance = diameter * outermost_zonewidth / wavelength * 1000
beam_size = -diameter / (focal_distance * 1000) * (focal_distance - distance) * 1e9
table.add_row(
f"{diameter*1e6:.2f} microns",
f"{focal_distance:.2f} mm",
f"{beam_size:.2f} microns",
)
console.print(table)
fosaz_val = dev.fosaz.readback.get()
print("\nOSA Information:")
print(f" Current fosaz {fosaz_val:.1f}")
print(
f" The OSA will collide with a normal OMNY pin at fosaz \033[1m{(33-fosaz_val):.1f}\033[0m"
)
print(f" Remaining space: \033[1m{-fosaz_val+(33-foptz_val):.1f}\033[0m")

View File

@@ -1,173 +0,0 @@
import builtins
from bec_widgets.cli.client import BECDockArea
# from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose
if builtins.__dict__.get("bec") is not None:
bec = builtins.__dict__.get("bec")
dev = builtins.__dict__.get("dev")
umv = builtins.__dict__.get("umv")
umvr = builtins.__dict__.get("umvr")
class flomniGuiToolsError(Exception):
pass
class flomniGuiTools:
def __init__(self):
self.text_box = None
self.progressbar = None
def set_client(self, client):
self.client = client
self.gui = self.client.gui
def flomnigui_show_gui(self):
if "flomni" in self.gui.windows:
self.gui.flomni.show()
else:
self.gui.new("flomni")
def flomnigui_stop_gui(self):
self.gui.flomni.hide()
def flomnigui_raise(self):
self.gui.flomni.raise_window()
# def flomnigui_show_xeyealign(self):
# self.flomnigui_show_gui()
# if self.xeyegui is None:
# self.flomnigui_remove_all_docks()
# self.xeyegui = self.gui.flomni.new("xeyegui").new("XRayEye")
# # start live
# if not dev.cam_xeye.live_mode:
# dev.cam_xeye.live_mode = True
def flomnigui_show_xeyealign(self):
self.flomnigui_show_gui()
if self._flomnigui_check_attribute_not_exists("xeyegui"):
self.flomnigui_remove_all_docks()
self.xeyegui = self.gui.flomni.new("xeyegui").new("XRayEye")
# start live
if not dev.cam_xeye.live_mode:
dev.cam_xeye.live_mode = True
def _flomnigui_check_attribute_not_exists(self, attribute_name):
if hasattr(self.gui,"flomni"):
if hasattr(self.gui.flomni,attribute_name):
return False
return True
def flomnigui_show_cameras(self):
self.flomnigui_show_gui()
if self._flomnigui_check_attribute_not_exists("camera_gripper") or self._flomnigui_check_attribute_not_exists("camera_overview"):
self.flomnigui_remove_all_docks()
camera_gripper_image = self.gui.flomni.new("camera_gripper").new("Image")
if self._flomnicam_check_device_exists(dev.cam_flomni_gripper):
camera_gripper_image.image(("cam_flomni_gripper", "preview"))
camera_gripper_image.lock_aspect_ratio = True
camera_gripper_image.enable_fps_monitor = True
camera_gripper_image.enable_toolbar = False
camera_gripper_image.outer_axes = False
camera_gripper_image.inner_axes = False
dev.cam_flomni_gripper.start_live_mode()
else:
print("Cannot open camera_gripper. Device does not exist.")
camera_overview_image = self.gui.flomni.new("camera_overview").new("Image")
if self._flomnicam_check_device_exists(dev.cam_flomni_overview):
camera_overview_image.image(("cam_flomni_overview", "preview"))
camera_overview_image.lock_aspect_ratio = True
camera_overview_image.enable_fps_monitor = True
camera_overview_image.enable_toolbar = False
camera_overview_image.outer_axes = False
camera_overview_image.inner_axes = False
dev.cam_flomni_overview.start_live_mode()
else:
print("Cannot open camera_overview. Device does not exist.")
def flomnigui_remove_all_docks(self):
dev.cam_flomni_overview.stop_live_mode()
dev.cam_flomni_gripper.stop_live_mode()
dev.cam_xeye.live_mode = False
self.gui.flomni.delete_all()
self.progressbar = None
self.text_box = None
def flomnigui_idle(self):
self.flomnigui_show_gui()
if self._flomnigui_check_attribute_not_exists("idle_text_box"):
self.flomnigui_remove_all_docks()
idle_text_box = self.gui.flomni.new("idle_textbox").new("TextBox")
text = (
"<pre>"
+ " ,---.,--. ,-----. ,--. ,--.,--. ,--.,--. \n"
+ "/ .-'| |' .-. '| `.' || ,'.| || | \n"
+ "| `-,| || | | || |'.'| || |' ' || | \n"
+ "| .-'| |' '-' '| | | || | ` || | \n"
+ "`--' `--' `-----' `--' `--'`--' `--'`--' \n"
+ "</pre>"
)
idle_text_box.set_html_text(text)
def _flomnicam_check_device_exists(self, device):
try:
device
except:
return False
else:
return True
def flomnigui_show_progress(self):
self.flomnigui_show_gui()
if self._flomnigui_check_attribute_not_exists("progressbar"):
self.flomnigui_remove_all_docks()
# Add a new dock with a RingProgressBar widget
self.progressbar = self.gui.flomni.new("progressbar").new("RingProgressBar")
# Customize the size of the progress ring
self.progressbar.set_line_widths(20)
# Disable automatic updates and manually set the self.progressbar value
self.progressbar.enable_auto_updates(False)
# Set precision for the self.progressbar display
self.progressbar.set_precision(1) # Display self.progressbar with one decimal places
# Setting multiple rigns with different values
self.progressbar.set_number_of_bars(3)
self.progressbar.rings[0].set_update("manual")
self.progressbar.rings[1].set_update("manual")
self.progressbar.rings[2].set_update("scan")
# Set the values of the rings to 50, 75, and 25 from outer to inner ring
# self.progressbar.set_value([50, 75])
# Add a new dock with a TextBox widget
self.text_box = self.gui.flomni.new(name="progress_text").new("TextBox")
self._flomnigui_update_progress()
def _flomnigui_update_progress(self):
if self.progressbar is not None:
progress = self.progress["projection"] / self.progress["total_projections"] * 100
subtomo_progress = (
self.progress["subtomo_projection"]
/ self.progress["subtomo_total_projections"]
* 100
)
self.progressbar.set_value([progress, subtomo_progress, 0])
if self.text_box is not None:
text = f"Progress report:\n Tomo type: ....................... {self.progress['tomo_type']}\n Projection: ...................... {self.progress['projection']:.0f}\n Total projections expected ....... {self.progress['total_projections']}\n Angle: ........................... {self.progress['angle']}\n Current subtomo: ................. {self.progress['subtomo']}\n Current projection within subtomo: {self.progress['subtomo_projection']}\n Total projections per subtomo: ... {self.progress['subtomo_total_projections']}"
self.text_box.set_plain_text(text)
if __name__ == "__main__":
from bec_lib.client import BECClient
from bec_widgets.cli.client_utils import BECGuiClient
client = BECClient()
client.start()
client.gui = BECGuiClient()
flomni_gui = flomniGuiTools(client)
flomni_gui.flomnigui_show_gui()
flomni_gui.flomnigui_show_progress()

View File

@@ -1,277 +0,0 @@
from __future__ import annotations
import builtins
import os
import time
from typing import TYPE_CHECKING
from bec_lib import bec_logger
from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose
logger = bec_logger.logger
# import builtins to avoid linter errors
bec = builtins.__dict__.get("bec")
dev = builtins.__dict__.get("dev")
umv = builtins.__dict__.get("umv")
umvr = builtins.__dict__.get("umvr")
if TYPE_CHECKING:
from bec_ipython_client.plugins.flomni import Flomni
class XrayEyeAlign:
# pixel calibration, multiply to get mm
labview=False
PIXEL_CALIBRATION = 0.1 / 113 # .2 with binning
def __init__(self, client, flomni: Flomni) -> None:
self.client = client
self.flomni = flomni
self.device_manager = client.device_manager
self.scans = client.scans
self.alignment_values = {}
self.flomni.reset_correction()
self.flomni.reset_tomo_alignment_fit()
def _reset_init_values(self):
self.shift_xy = [0, 0]
self._xray_fov_xy = [0, 0]
def save_frame(self):
epics_put("XOMNYI-XEYE-SAVFRAME:0", 1)
def update_frame(self,keep_shutter_open=False):
if self.labview:
epics_put("XOMNYI-XEYE-ACQDONE:0", 0)
if not self.labview:
self.flomni.flomnigui_show_xeyealign()
if not dev.cam_xeye.live_mode:
dev.cam_xeye.live_mode = True
epics_put("XOMNYI-XEYE-ACQ:0", 1)
if self.labview:
# wait for start live
while epics_get("XOMNYI-XEYE-ACQDONE:0") == 0:
time.sleep(0.5)
print("waiting for live view to start...")
fshopen()
if self.labview:
epics_put("XOMNYI-XEYE-ACQDONE:0", 0)
while epics_get("XOMNYI-XEYE-ACQDONE:0") == 0:
print("waiting for new frame...")
time.sleep(0.5)
time.sleep(0.5)
# stop live view
if not keep_shutter_open:
epics_put("XOMNYI-XEYE-ACQ:0", 0)
time.sleep(0.1)
fshclose()
print("got new frame")
else:
print("Staying in live view, shutter is and remains open!")
def tomo_rotate(self, val: float):
# pylint: disable=undefined-variable
umv(self.device_manager.devices.fsamroy, val)
def get_tomo_angle(self):
return self.device_manager.devices.fsamroy.readback.get()
def update_fov(self, k: int):
self._xray_fov_xy[0] = max(epics_get(f"XOMNYI-XEYE-XWIDTH_X:{k}"), self._xray_fov_xy[0])
self._xray_fov_xy[1] = max(0, self._xray_fov_xy[0])
@property
def movement_buttons_enabled(self):
return [epics_get("XOMNYI-XEYE-ENAMVX:0"), epics_get("XOMNYI-XEYE-ENAMVY:0")]
@movement_buttons_enabled.setter
def movement_buttons_enabled(self, enabled: bool):
enabled = int(enabled)
epics_put("XOMNYI-XEYE-ENAMVX:0", enabled)
epics_put("XOMNYI-XEYE-ENAMVY:0", enabled)
def send_message(self, msg: str):
epics_put("XOMNYI-XEYE-MESSAGE:0.DESC", msg)
def align(self,keep_shutter_open=False):
if not keep_shutter_open:
print("This routine can be called with paramter keep_shutter_open=True to keep the shutter always open")
self.send_message("Getting things ready. Please wait...")
#potential unresolved movement requests to zero
epics_put("XOMNYI-XEYE-MVX:0", 0)
epics_put("XOMNYI-XEYE-MVY:0", 0)
# reset shift xy and fov params
self._reset_init_values()
self.flomni.lights_off()
self.flomni.flomnigui_show_xeyealign()
self.flomni.flomnigui_raise()
self.tomo_rotate(0)
epics_put("XOMNYI-XEYE-ANGLE:0", 0)
self.flomni.feye_in()
self.flomni.laser_tracker_on()
self.flomni.feedback_enable_with_reset()
# disable movement buttons
self.movement_buttons_enabled = False
sample_name = self.flomni.sample_get_name(0)
epics_put("XOMNYI-XEYE-SAMPLENAME:0.DESC", sample_name)
# this makes sure we are in a defined state
self.flomni.feedback_disable()
epics_put("XOMNYI-XEYE-PIXELSIZE:0", self.PIXEL_CALIBRATION)
self.flomni.fosa_out()
fsamx_in = self.flomni._get_user_param_safe("fsamx", "in")
umv(dev.fsamx, fsamx_in - 0.25)
self.flomni.ffzp_in()
self.update_frame(keep_shutter_open)
# enable submit buttons
self.movement_buttons_enabled = False
epics_put("XOMNYI-XEYE-SUBMIT:0", 0)
epics_put("XOMNYI-XEYE-STEP:0", 0)
self.send_message("Submit center value of FZP.")
k = 0
while True:
if epics_get("XOMNYI-XEYE-SUBMIT:0") == 1:
val_x = epics_get(f"XOMNYI-XEYE-XVAL_X:{k}") / 2 * self.PIXEL_CALIBRATION # in mm
self.alignment_values[k] = val_x
print(f"Clicked position {k}: x {self.alignment_values[k]}")
rtx_position = dev.rtx.readback.get() / 1000
print(f"Current rtx position {rtx_position}")
self.alignment_values[k] -= rtx_position
print(f"Corrected position {k}: x {self.alignment_values[k]}")
if k == 0: # received center value of FZP
self.send_message("please wait ...")
self.movement_buttons_enabled = False
epics_put("XOMNYI-XEYE-SUBMIT:0", -1) # disable submit button
self.flomni.feedback_disable()
fsamx_in = self.flomni._get_user_param_safe("fsamx", "in")
umv(dev.fsamx, fsamx_in)
self.flomni.foptics_out()
self.flomni.feedback_disable()
umv(dev.fsamx, fsamx_in - 0.25)
if self.labview:
self.update_frame(keep_shutter_open)
epics_put("XOMNYI-XEYE-RECBG:0", 1)
while epics_get("XOMNYI-XEYE-RECBG:0") == 1:
time.sleep(0.5)
print("waiting for background frame...")
umv(dev.fsamx, fsamx_in)
time.sleep(0.5)
self.flomni.feedback_enable_with_reset()
self.update_frame(keep_shutter_open)
self.send_message("Adjust sample height and submit center")
epics_put("XOMNYI-XEYE-SUBMIT:0", 0)
self.movement_buttons_enabled = True
elif 1 <= k < 5: # received sample center value at samroy 0 ... 315
self.send_message("please wait ...")
epics_put("XOMNYI-XEYE-SUBMIT:0", -1)
self.movement_buttons_enabled = False
umv(dev.rtx, 0)
self.tomo_rotate(k * 45)
epics_put("XOMNYI-XEYE-ANGLE:0", self.get_tomo_angle())
self.update_frame(keep_shutter_open)
self.send_message("Submit sample center")
epics_put("XOMNYI-XEYE-SUBMIT:0", 0)
epics_put("XOMNYI-XEYE-ENAMVX:0", 1)
self.update_fov(k)
elif k == 5: # received sample center value at samroy 270 and done
self.send_message("done...")
epics_put("XOMNYI-XEYE-SUBMIT:0", -1) # disable submit button
self.movement_buttons_enabled = False
self.update_fov(k)
break
k += 1
epics_put("XOMNYI-XEYE-STEP:0", k)
_xrayeyalignmvx = epics_get("XOMNYI-XEYE-MVX:0")
if _xrayeyalignmvx != 0:
umvr(dev.rtx, _xrayeyalignmvx)
print(f"Current rtx position {dev.rtx.readback.get() / 1000}")
epics_put("XOMNYI-XEYE-MVX:0", 0)
if k > 0:
epics_put(f"XOMNYI-XEYE-STAGEPOSX:{k}", dev.rtx.readback.get() / 1000)
time.sleep(3)
self.update_frame(keep_shutter_open)
if k < 2:
# allow movements, store movements to calculate center
_xrayeyalignmvy = epics_get("XOMNYI-XEYE-MVY:0")
if _xrayeyalignmvy != 0:
self.flomni.feedback_disable()
umvr(dev.fsamy, _xrayeyalignmvy / 1000)
time.sleep(2)
epics_put("XOMNYI-XEYE-MVY:0", 0)
self.flomni.feedback_enable_with_reset()
self.update_frame(keep_shutter_open)
time.sleep(0.2)
self.write_output()
fovx = self._xray_fov_xy[0] * self.PIXEL_CALIBRATION * 1000 / 2
fovy = self._xray_fov_xy[1] * self.PIXEL_CALIBRATION * 1000 / 2
self.tomo_rotate(0)
umv(dev.rtx, 0)
# free camera
if self.labview:
epics_put("XOMNYI-XEYE-ACQ:0", 2)
if keep_shutter_open and not self.labview:
if self.flomni.OMNYTools.yesno("Close the shutter now?","y"):
fshclose()
epics_put("XOMNYI-XEYE-ACQ:0", 0)
if not self.labview:
self.flomni.flomnigui_idle()
print(
f"The largest field of view from the xrayeyealign was \nfovx = {fovx:.0f} microns, fovy"
f" = {fovy:.0f} microns"
)
print("Use the matlab routine to FIT the current alignment...")
print("Then LOAD ALIGNMENT PARAMETERS by running flomni.read_alignment_offset()\n")
def write_output(self):
file = os.path.expanduser("~/Data10/specES1/internal/xrayeye_alignmentvalues")
if not os.path.exists(file):
os.makedirs(os.path.dirname(file), exist_ok=True)
with open(file, "w") as alignment_values_file:
alignment_values_file.write("angle\thorizontal\n")
for k in range(1, 6):
fovx_offset = self.alignment_values[0] - self.alignment_values[k]
print(f"Writing to file new alignment: number {k}, value x {fovx_offset}")
alignment_values_file.write(f"{(k-1)*45}\t{fovx_offset*1000}\n")

View File

@@ -1 +0,0 @@
from .omny import OMNY

View File

@@ -1,715 +0,0 @@
corr_elements = 357
corr_angle[0] = 0.097400
corr_angle[1] = 0.603500
corr_angle[2] = 1.134200
corr_angle[3] = 1.625000
corr_angle[4] = 2.162200
corr_angle[5] = 2.700100
corr_angle[6] = 3.191600
corr_angle[7] = 3.714300
corr_angle[8] = 4.223200
corr_angle[9] = 4.730900
corr_angle[10] = 5.253300
corr_angle[11] = 5.743300
corr_angle[12] = 6.279200
corr_angle[13] = 6.782900
corr_angle[14] = 7.301200
corr_angle[15] = 7.808100
corr_angle[16] = 8.325300
corr_angle[17] = 8.859400
corr_angle[18] = 9.359400
corr_angle[19] = 9.887900
corr_angle[20] = 10.395400
corr_angle[21] = 10.930700
corr_angle[22] = 11.415400
corr_angle[23] = 11.928900
corr_angle[24] = 12.456900
corr_angle[25] = 12.955900
corr_angle[26] = 13.479000
corr_angle[27] = 13.982700
corr_angle[28] = 14.500900
corr_angle[29] = 15.016200
corr_angle[30] = 15.528000
corr_angle[31] = 16.053800
corr_angle[32] = 16.562800
corr_angle[33] = 17.076600
corr_angle[34] = 17.592400
corr_angle[35] = 18.094100
corr_angle[36] = 18.623800
corr_angle[37] = 19.118400
corr_angle[38] = 19.655000
corr_angle[39] = 20.143900
corr_angle[40] = 20.672200
corr_angle[41] = 21.171500
corr_angle[42] = 21.696300
corr_angle[43] = 22.215900
corr_angle[44] = 22.728900
corr_angle[45] = 23.233100
corr_angle[46] = 23.760000
corr_angle[47] = 24.267700
corr_angle[48] = 24.792100
corr_angle[49] = 25.287900
corr_angle[50] = 25.824600
corr_angle[51] = 26.321900
corr_angle[52] = 26.842600
corr_angle[53] = 27.337200
corr_angle[54] = 27.873500
corr_angle[55] = 28.373900
corr_angle[56] = 28.895900
corr_angle[57] = 29.404900
corr_angle[58] = 29.926700
corr_angle[59] = 30.439300
corr_angle[60] = 30.963700
corr_angle[61] = 31.460000
corr_angle[62] = 31.989500
corr_angle[63] = 32.489700
corr_angle[64] = 33.026400
corr_angle[65] = 33.516500
corr_angle[66] = 34.044000
corr_angle[67] = 34.547700
corr_angle[68] = 35.070800
corr_angle[69] = 35.578400
corr_angle[70] = 36.103200
corr_angle[71] = 36.603200
corr_angle[72] = 37.128200
corr_angle[73] = 37.645200
corr_angle[74] = 38.164300
corr_angle[75] = 38.662100
corr_angle[76] = 39.191000
corr_angle[77] = 39.693400
corr_angle[78] = 40.222500
corr_angle[79] = 40.719500
corr_angle[80] = 41.247000
corr_angle[81] = 41.755700
corr_angle[82] = 42.271300
corr_angle[83] = 42.767600
corr_angle[84] = 43.302200
corr_angle[85] = 43.818700
corr_angle[86] = 44.330000
corr_angle[87] = 44.835300
corr_angle[88] = 45.359800
corr_angle[89] = 45.867200
corr_angle[90] = 46.396200
corr_angle[91] = 46.895000
corr_angle[92] = 47.411500
corr_angle[93] = 47.915200
corr_angle[94] = 48.436600
corr_angle[95] = 48.946100
corr_angle[96] = 49.472300
corr_angle[97] = 49.979800
corr_angle[98] = 50.503300
corr_angle[99] = 51.008700
corr_angle[100] = 51.535000
corr_angle[101] = 52.036700
corr_angle[102] = 52.563700
corr_angle[103] = 53.063600
corr_angle[104] = 53.591700
corr_angle[105] = 54.091200
corr_angle[106] = 54.618900
corr_angle[107] = 55.116900
corr_angle[108] = 55.636100
corr_angle[109] = 56.143100
corr_angle[110] = 56.672200
corr_angle[111] = 57.172900
corr_angle[112] = 57.704600
corr_angle[113] = 58.204800
corr_angle[114] = 58.728600
corr_angle[115] = 59.239500
corr_angle[116] = 59.768400
corr_angle[117] = 60.268300
corr_angle[118] = 60.788800
corr_angle[119] = 61.289300
corr_angle[120] = 61.813300
corr_angle[121] = 62.310800
corr_angle[122] = 62.836700
corr_angle[123] = 63.353700
corr_angle[124] = 63.866600
corr_angle[125] = 64.377200
corr_angle[126] = 64.906600
corr_angle[127] = 65.414000
corr_angle[128] = 65.937100
corr_angle[129] = 66.429400
corr_angle[130] = 66.970000
corr_angle[131] = 67.459200
corr_angle[132] = 67.996400
corr_angle[133] = 68.499800
corr_angle[134] = 69.014500
corr_angle[135] = 69.509500
corr_angle[136] = 70.044000
corr_angle[137] = 70.543200
corr_angle[138] = 71.079400
corr_angle[139] = 71.579300
corr_angle[140] = 72.103500
corr_angle[141] = 72.607000
corr_angle[142] = 73.137100
corr_angle[143] = 73.633000
corr_angle[144] = 74.164500
corr_angle[145] = 74.660200
corr_angle[146] = 75.180600
corr_angle[147] = 75.674200
corr_angle[148] = 76.215400
corr_angle[149] = 76.718900
corr_angle[150] = 77.242300
corr_angle[151] = 77.752000
corr_angle[152] = 78.279300
corr_angle[153] = 78.780500
corr_angle[154] = 79.314900
corr_angle[155] = 79.424500
corr_angle[156] = 79.807000
corr_angle[157] = 80.336500
corr_angle[158] = 80.338700
corr_angle[159] = 80.835300
corr_angle[160] = 81.367100
corr_angle[161] = 81.376100
corr_angle[162] = 81.859000
corr_angle[163] = 82.382300
corr_angle[164] = 82.384000
corr_angle[165] = 82.881100
corr_angle[166] = 83.415000
corr_angle[167] = 83.421600
corr_angle[168] = 83.917100
corr_angle[169] = 84.439200
corr_angle[170] = 84.439300
corr_angle[171] = 84.947400
corr_angle[172] = 85.472700
corr_angle[173] = 85.987700
corr_angle[174] = 86.512400
corr_angle[175] = 87.009500
corr_angle[176] = 87.536500
corr_angle[177] = 88.035600
corr_angle[178] = 88.560100
corr_angle[179] = 89.057600
corr_angle[180] = 89.583200
corr_angle[181] = 90.090500
corr_angle[182] = 90.614600
corr_angle[183] = 91.119900
corr_angle[184] = 91.638300
corr_angle[185] = 92.153300
corr_angle[186] = 92.681600
corr_angle[187] = 93.181800
corr_angle[188] = 93.710900
corr_angle[189] = 94.206100
corr_angle[190] = 94.732700
corr_angle[191] = 95.226600
corr_angle[192] = 95.766400
corr_angle[193] = 96.259000
corr_angle[194] = 96.783400
corr_angle[195] = 97.283100
corr_angle[196] = 97.811500
corr_angle[197] = 98.323600
corr_angle[198] = 98.839400
corr_angle[199] = 99.350700
corr_angle[200] = 99.880200
corr_angle[201] = 100.378100
corr_angle[202] = 100.913300
corr_angle[203] = 101.405000
corr_angle[204] = 101.935700
corr_angle[205] = 102.426300
corr_angle[206] = 102.957300
corr_angle[207] = 103.456500
corr_angle[208] = 103.985100
corr_angle[209] = 104.490300
corr_angle[210] = 105.015100
corr_angle[211] = 105.518400
corr_angle[212] = 106.047400
corr_angle[213] = 106.551100
corr_angle[214] = 107.077100
corr_angle[215] = 107.575800
corr_angle[216] = 108.115200
corr_angle[217] = 108.598800
corr_angle[218] = 109.129100
corr_angle[219] = 109.626200
corr_angle[220] = 110.158400
corr_angle[221] = 110.661700
corr_angle[222] = 111.188600
corr_angle[223] = 111.695000
corr_angle[224] = 112.216400
corr_angle[225] = 112.720000
corr_angle[226] = 113.246200
corr_angle[227] = 113.757100
corr_angle[228] = 114.281900
corr_angle[229] = 114.780000
corr_angle[230] = 115.306800
corr_angle[231] = 115.799800
corr_angle[232] = 116.328400
corr_angle[233] = 116.829900
corr_angle[234] = 117.355400
corr_angle[235] = 117.858200
corr_angle[236] = 118.380600
corr_angle[237] = 118.893200
corr_angle[238] = 119.418200
corr_angle[239] = 119.921000
corr_angle[240] = 120.446100
corr_angle[241] = 120.953800
corr_angle[242] = 121.480300
corr_angle[243] = 121.977600
corr_angle[244] = 122.507100
corr_angle[245] = 122.996200
corr_angle[246] = 123.529600
corr_angle[247] = 124.032200
corr_angle[248] = 124.554800
corr_angle[249] = 125.057800
corr_angle[250] = 125.580800
corr_angle[251] = 126.090000
corr_angle[252] = 126.611600
corr_angle[253] = 127.127000
corr_angle[254] = 127.650900
corr_angle[255] = 128.153700
corr_angle[256] = 128.677200
corr_angle[257] = 129.170800
corr_angle[258] = 129.703000
corr_angle[259] = 130.194900
corr_angle[260] = 130.733600
corr_angle[261] = 131.228300
corr_angle[262] = 131.756900
corr_angle[263] = 132.263200
corr_angle[264] = 132.784700
corr_angle[265] = 133.296300
corr_angle[266] = 133.818800
corr_angle[267] = 134.318900
corr_angle[268] = 134.846800
corr_angle[269] = 135.344800
corr_angle[270] = 135.876000
corr_angle[271] = 136.372600
corr_angle[272] = 136.902700
corr_angle[273] = 137.397800
corr_angle[274] = 137.926800
corr_angle[275] = 138.428000
corr_angle[276] = 138.953700
corr_angle[277] = 139.461300
corr_angle[278] = 139.987800
corr_angle[279] = 140.492600
corr_angle[280] = 141.017900
corr_angle[281] = 141.524000
corr_angle[282] = 142.061200
corr_angle[283] = 142.545400
corr_angle[284] = 143.069900
corr_angle[285] = 143.568700
corr_angle[286] = 144.102200
corr_angle[287] = 144.596200
corr_angle[288] = 145.126400
corr_angle[289] = 145.626900
corr_angle[290] = 146.158700
corr_angle[291] = 146.662200
corr_angle[292] = 147.184700
corr_angle[293] = 147.692300
corr_angle[294] = 148.225900
corr_angle[295] = 148.716000
corr_angle[296] = 149.243400
corr_angle[297] = 149.739400
corr_angle[298] = 150.272400
corr_angle[299] = 150.762100
corr_angle[300] = 151.301800
corr_angle[301] = 151.796300
corr_angle[302] = 152.333500
corr_angle[303] = 152.833100
corr_angle[304] = 153.363600
corr_angle[305] = 153.862900
corr_angle[306] = 154.396100
corr_angle[307] = 154.895400
corr_angle[308] = 155.417700
corr_angle[309] = 155.916900
corr_angle[310] = 156.451600
corr_angle[311] = 156.942700
corr_angle[312] = 157.467600
corr_angle[313] = 157.970900
corr_angle[314] = 158.501100
corr_angle[315] = 158.999600
corr_angle[316] = 159.526100
corr_angle[317] = 160.028100
corr_angle[318] = 160.555800
corr_angle[319] = 161.067700
corr_angle[320] = 161.597300
corr_angle[321] = 162.091300
corr_angle[322] = 162.616600
corr_angle[323] = 163.107400
corr_angle[324] = 163.639100
corr_angle[325] = 164.139700
corr_angle[326] = 164.672500
corr_angle[327] = 165.176800
corr_angle[328] = 165.698600
corr_angle[329] = 166.205400
corr_angle[330] = 166.727300
corr_angle[331] = 167.238600
corr_angle[332] = 167.771500
corr_angle[333] = 168.265000
corr_angle[334] = 168.794800
corr_angle[335] = 169.293800
corr_angle[336] = 169.813300
corr_angle[337] = 170.320200
corr_angle[338] = 170.845100
corr_angle[339] = 171.344000
corr_angle[340] = 171.866200
corr_angle[341] = 172.375300
corr_angle[342] = 172.899400
corr_angle[343] = 173.404400
corr_angle[344] = 173.928500
corr_angle[345] = 174.430800
corr_angle[346] = 174.827800
corr_angle[347] = 175.460400
corr_angle[348] = 175.993100
corr_angle[349] = 176.492100
corr_angle[350] = 177.014200
corr_angle[351] = 177.512100
corr_angle[352] = 178.044200
corr_angle[353] = 178.643800
corr_angle[354] = 179.067200
corr_angle[355] = 179.571600
corr_angle[356] = 180.093700
corr_pos[0] = -0.814519
corr_pos[1] = -0.810675
corr_pos[2] = -0.806338
corr_pos[3] = -0.802047
corr_pos[4] = -0.797044
corr_pos[5] = -0.791712
corr_pos[6] = -0.786558
corr_pos[7] = -0.780781
corr_pos[8] = -0.774864
corr_pos[9] = -0.768674
corr_pos[10] = -0.762005
corr_pos[11] = -0.755474
corr_pos[12] = -0.748024
corr_pos[13] = -0.740730
corr_pos[14] = -0.732929
corr_pos[15] = -0.725011
corr_pos[16] = -0.716637
corr_pos[17] = -0.707676
corr_pos[18] = -0.698861
corr_pos[19] = -0.686245
corr_pos[20] = -0.676596
corr_pos[21] = -0.663643
corr_pos[22] = -0.655664
corr_pos[23] = -0.642249
corr_pos[24] = -0.633516
corr_pos[25] = -0.621402
corr_pos[26] = -0.612846
corr_pos[27] = -0.599978
corr_pos[28] = -0.592135
corr_pos[29] = -0.577949
corr_pos[30] = -0.570695
corr_pos[31] = -0.555560
corr_pos[32] = -0.549015
corr_pos[33] = -0.532162
corr_pos[34] = -0.524479
corr_pos[35] = -0.507848
corr_pos[36] = -0.500301
corr_pos[37] = -0.484361
corr_pos[38] = -0.477365
corr_pos[39] = -0.461751
corr_pos[40] = -0.455963
corr_pos[41] = -0.439438
corr_pos[42] = -0.436103
corr_pos[43] = -0.420587
corr_pos[44] = -0.417361
corr_pos[45] = -0.402270
corr_pos[46] = -0.400459
corr_pos[47] = -0.384745
corr_pos[48] = -0.385280
corr_pos[49] = -0.371217
corr_pos[50] = -0.373612
corr_pos[51] = -0.361838
corr_pos[52] = -0.365771
corr_pos[53] = -0.354641
corr_pos[54] = -0.360647
corr_pos[55] = -0.351446
corr_pos[56] = -0.359147
corr_pos[57] = -0.348696
corr_pos[58] = -0.359348
corr_pos[59] = -0.349962
corr_pos[60] = -0.360782
corr_pos[61] = -0.355040
corr_pos[62] = -0.367441
corr_pos[63] = -0.360511
corr_pos[64] = -0.372617
corr_pos[65] = -0.366120
corr_pos[66] = -0.377568
corr_pos[67] = -0.370362
corr_pos[68] = -0.380918
corr_pos[69] = -0.372553
corr_pos[70] = -0.381460
corr_pos[71] = -0.371828
corr_pos[72] = -0.379428
corr_pos[73] = -0.368465
corr_pos[74] = -0.374297
corr_pos[75] = -0.360340
corr_pos[76] = -0.365429
corr_pos[77] = -0.349168
corr_pos[78] = -0.351544
corr_pos[79] = -0.332514
corr_pos[80] = -0.333505
corr_pos[81] = -0.313316
corr_pos[82] = -0.313014
corr_pos[83] = -0.291107
corr_pos[84] = -0.287807
corr_pos[85] = -0.264483
corr_pos[86] = -0.260891
corr_pos[87] = -0.236646
corr_pos[88] = -0.231362
corr_pos[89] = -0.205833
corr_pos[90] = -0.200663
corr_pos[91] = -0.174834
corr_pos[92] = -0.171662
corr_pos[93] = -0.146200
corr_pos[94] = -0.142890
corr_pos[95] = -0.118389
corr_pos[96] = -0.112895
corr_pos[97] = -0.088879
corr_pos[98] = -0.084146
corr_pos[99] = -0.061625
corr_pos[100] = -0.054742
corr_pos[101] = -0.032741
corr_pos[102] = -0.026725
corr_pos[103] = -0.004827
corr_pos[104] = 0.002678
corr_pos[105] = 0.024418
corr_pos[106] = 0.031105
corr_pos[107] = 0.052334
corr_pos[108] = 0.060072
corr_pos[109] = 0.081470
corr_pos[110] = 0.088550
corr_pos[111] = 0.109685
corr_pos[112] = 0.116675
corr_pos[113] = 0.136298
corr_pos[114] = 0.142152
corr_pos[115] = 0.161986
corr_pos[116] = 0.166996
corr_pos[117] = 0.186466
corr_pos[118] = 0.190874
corr_pos[119] = 0.208729
corr_pos[120] = 0.212714
corr_pos[121] = 0.229992
corr_pos[122] = 0.232685
corr_pos[123] = 0.248748
corr_pos[124] = 0.249688
corr_pos[125] = 0.264768
corr_pos[126] = 0.264432
corr_pos[127] = 0.278801
corr_pos[128] = 0.278424
corr_pos[129] = 0.289656
corr_pos[130] = 0.289612
corr_pos[131] = 0.299899
corr_pos[132] = 0.299782
corr_pos[133] = 0.307473
corr_pos[134] = 0.309489
corr_pos[135] = 0.315999
corr_pos[136] = 0.317413
corr_pos[137] = 0.325060
corr_pos[138] = 0.325931
corr_pos[139] = 0.332544
corr_pos[140] = 0.332095
corr_pos[141] = 0.338738
corr_pos[142] = 0.337864
corr_pos[143] = 0.343909
corr_pos[144] = 0.342973
corr_pos[145] = 0.348449
corr_pos[146] = 0.346060
corr_pos[147] = 0.351949
corr_pos[148] = 0.349873
corr_pos[149] = 0.354969
corr_pos[150] = 0.352375
corr_pos[151] = 0.358388
corr_pos[152] = 0.354736
corr_pos[153] = 0.359539
corr_pos[154] = 0.354369
corr_pos[155] = 0.358549
corr_pos[156] = 0.353217
corr_pos[157] = 0.355795
corr_pos[158] = 0.351299
corr_pos[159] = 0.352922
corr_pos[160] = 0.346933
corr_pos[161] = 0.350201
corr_pos[162] = 0.343937
corr_pos[163] = 0.349304
corr_pos[164] = 0.344857
corr_pos[165] = 0.347471
corr_pos[166] = 0.335793
corr_pos[167] = 0.341109
corr_pos[168] = 0.330453
corr_pos[169] = 0.326576
corr_pos[170] = 0.320299
corr_pos[171] = 0.315348
corr_pos[172] = 0.300744
corr_pos[173] = 0.294366
corr_pos[174] = 0.280438
corr_pos[175] = 0.273618
corr_pos[176] = 0.259335
corr_pos[177] = 0.251464
corr_pos[178] = 0.237247
corr_pos[179] = 0.229092
corr_pos[180] = 0.214882
corr_pos[181] = 0.207363
corr_pos[182] = 0.193246
corr_pos[183] = 0.185868
corr_pos[184] = 0.173256
corr_pos[185] = 0.165378
corr_pos[186] = 0.153853
corr_pos[187] = 0.147031
corr_pos[188] = 0.135535
corr_pos[189] = 0.129435
corr_pos[190] = 0.119843
corr_pos[191] = 0.115062
corr_pos[192] = 0.107115
corr_pos[193] = 0.105182
corr_pos[194] = 0.099647
corr_pos[195] = 0.098162
corr_pos[196] = 0.094628
corr_pos[197] = 0.095257
corr_pos[198] = 0.084490
corr_pos[199] = 0.085389
corr_pos[200] = 0.074302
corr_pos[201] = 0.075265
corr_pos[202] = 0.063623
corr_pos[203] = 0.064487
corr_pos[204] = 0.052841
corr_pos[205] = 0.053559
corr_pos[206] = 0.042022
corr_pos[207] = 0.043321
corr_pos[208] = 0.032826
corr_pos[209] = 0.032277
corr_pos[210] = 0.022169
corr_pos[211] = 0.020740
corr_pos[212] = 0.010378
corr_pos[213] = 0.008668
corr_pos[214] = -0.001930
corr_pos[215] = -0.003882
corr_pos[216] = -0.014694
corr_pos[217] = -0.016629
corr_pos[218] = -0.026380
corr_pos[219] = -0.028069
corr_pos[220] = -0.037125
corr_pos[221] = -0.039429
corr_pos[222] = -0.047773
corr_pos[223] = -0.049261
corr_pos[224] = -0.056872
corr_pos[225] = -0.058756
corr_pos[226] = -0.065279
corr_pos[227] = -0.066859
corr_pos[228] = -0.073027
corr_pos[229] = -0.074253
corr_pos[230] = -0.079062
corr_pos[231] = -0.079800
corr_pos[232] = -0.084350
corr_pos[233] = -0.084805
corr_pos[234] = -0.088621
corr_pos[235] = -0.088437
corr_pos[236] = -0.092166
corr_pos[237] = -0.090769
corr_pos[238] = -0.093820
corr_pos[239] = -0.091724
corr_pos[240] = -0.094152
corr_pos[241] = -0.090866
corr_pos[242] = -0.092575
corr_pos[243] = -0.089239
corr_pos[244] = -0.089666
corr_pos[245] = -0.086864
corr_pos[246] = -0.087183
corr_pos[247] = -0.083974
corr_pos[248] = -0.083709
corr_pos[249] = -0.079848
corr_pos[250] = -0.078494
corr_pos[251] = -0.072782
corr_pos[252] = -0.069638
corr_pos[253] = -0.063004
corr_pos[254] = -0.058599
corr_pos[255] = -0.051737
corr_pos[256] = -0.046451
corr_pos[257] = -0.040238
corr_pos[258] = -0.034071
corr_pos[259] = -0.028578
corr_pos[260] = -0.021481
corr_pos[261] = -0.016346
corr_pos[262] = -0.009475
corr_pos[263] = -0.004141
corr_pos[264] = 0.002780
corr_pos[265] = 0.008455
corr_pos[266] = 0.015507
corr_pos[267] = 0.021672
corr_pos[268] = 0.029704
corr_pos[269] = 0.036662
corr_pos[270] = 0.044794
corr_pos[271] = 0.052031
corr_pos[272] = 0.061478
corr_pos[273] = 0.069150
corr_pos[274] = 0.078715
corr_pos[275] = 0.087785
corr_pos[276] = 0.098593
corr_pos[277] = 0.107863
corr_pos[278] = 0.118256
corr_pos[279] = 0.127631
corr_pos[280] = 0.139011
corr_pos[281] = 0.150077
corr_pos[282] = 0.162154
corr_pos[283] = 0.173758
corr_pos[284] = 0.186998
corr_pos[285] = 0.200111
corr_pos[286] = 0.214116
corr_pos[287] = 0.227291
corr_pos[288] = 0.240662
corr_pos[289] = 0.252955
corr_pos[290] = 0.265359
corr_pos[291] = 0.275995
corr_pos[292] = 0.287613
corr_pos[293] = 0.295789
corr_pos[294] = 0.306424
corr_pos[295] = 0.313027
corr_pos[296] = 0.322181
corr_pos[297] = 0.329313
corr_pos[298] = 0.338191
corr_pos[299] = 0.345243
corr_pos[300] = 0.353316
corr_pos[301] = 0.360491
corr_pos[302] = 0.367891
corr_pos[303] = 0.375027
corr_pos[304] = 0.380865
corr_pos[305] = 0.388354
corr_pos[306] = 0.395257
corr_pos[307] = 0.401478
corr_pos[308] = 0.408087
corr_pos[309] = 0.414083
corr_pos[310] = 0.420361
corr_pos[311] = 0.424284
corr_pos[312] = 0.429614
corr_pos[313] = 0.433888
corr_pos[314] = 0.438529
corr_pos[315] = 0.442302
corr_pos[316] = 0.445899
corr_pos[317] = 0.449014
corr_pos[318] = 0.451693
corr_pos[319] = 0.453795
corr_pos[320] = 0.455132
corr_pos[321] = 0.455438
corr_pos[322] = 0.455334
corr_pos[323] = 0.454055
corr_pos[324] = 0.451146
corr_pos[325] = 0.447259
corr_pos[326] = 0.442478
corr_pos[327] = 0.437520
corr_pos[328] = 0.432297
corr_pos[329] = 0.426442
corr_pos[330] = 0.418918
corr_pos[331] = 0.411040
corr_pos[332] = 0.402610
corr_pos[333] = 0.394491
corr_pos[334] = 0.383925
corr_pos[335] = 0.374590
corr_pos[336] = 0.363650
corr_pos[337] = 0.353143
corr_pos[338] = 0.339756
corr_pos[339] = 0.328074
corr_pos[340] = 0.315463
corr_pos[341] = 0.302641
corr_pos[342] = 0.288898
corr_pos[343] = 0.275134
corr_pos[344] = 0.260308
corr_pos[345] = 0.245582
corr_pos[346] = 0.233584
corr_pos[347] = 0.213812
corr_pos[348] = 0.196540
corr_pos[349] = 0.179844
corr_pos[350] = 0.161839
corr_pos[351] = 0.144160
corr_pos[352] = 0.124715
corr_pos[353] = 0.102123
corr_pos[354] = 0.085736
corr_pos[355] = 0.065743
corr_pos[356] = 0.044511

View File

@@ -1,171 +0,0 @@
import builtins
from bec_widgets.cli.client import BECDockArea
# from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose
if builtins.__dict__.get("bec") is not None:
bec = builtins.__dict__.get("bec")
dev = builtins.__dict__.get("dev")
umv = builtins.__dict__.get("umv")
umvr = builtins.__dict__.get("umvr")
class OMNYGuiToolsError(Exception):
pass
class OMNYGuiTools:
def __init__(self, client):
self.gui = getattr(client, "gui", None)
self.gui_window = self.gui.windows['main'].widget
self.fig200 = None
self.fig201 = None
self.fig202 = None
self.fig203 = None
self.progressbar = None
self.text_box = None
self.idle_text_box = None
def omnygui_show_gui(self):
self.gui_window.show()
def omnygui_stop_gui(self):
self.gui_window.hide()
def _omnycam_parking(self):
self.omnygui_show_omnycam_parking()
def omnygui_show_omnycam_parking(self):
self.omnygui_show_gui()
if self.fig200 is None:
self._omnycam_clear()
self.fig200 = self.gui_window.add_dock(name="omnycam200").add_widget("BECImageWidget")
if self._omnycam_check_device_exists(dev.cam200):
fig = self.fig200.image("cam200")
fig.set_rotation(deg_90=3)
self.fig200.lock_aspect_ratio(True)
else:
print("Cannot open cam200. Device does not exist.")
self.fig203 = self.gui_window.add_dock(name="omnycam203").add_widget("BECImageWidget")
if self._omnycam_check_device_exists(dev.cam203):
fig = self.fig203.image("cam203")
fig.set_rotation(deg_90=3)
self.fig203.lock_aspect_ratio(True)
else:
print("Cannot open cam203. Device does not exist.")
try:
self.gui_window.remove_dock(name="default_figure")
except:
pass
def omnygui_remove_all_docks(self):
self.gui_window.clear_all()
self.fig200 = None
self.fig201 = None
self.fig202 = None
self.fig203 = None
self.progressbar = None
self.text_box = None
self.idle_text_box = None
def omnygui_idle(self):
self.omnygui_show_gui()
if self.idle_text_box is None:
self.omnygui_remove_all_docks()
self.idle_text_box = self.gui_window.add_dock(name="idle_text").add_widget("TextBox")
try:
self.gui_window.remove_dock(name="default_figure")
except:
pass
text = (
"<pre>"
+ " ,o888888o. ,8. ,8. b. 8 `8.`8888. ,8' \n"
+ " . 8888 `88. ,888. ,888. 888o. 8 `8.`8888. ,8' \n"
+ ",8 8888 `8b .`8888. .`8888. Y88888o. 8 `8.`8888. ,8' \n"
+ "88 8888 `8b ,8.`8888. ,8.`8888. .`Y888888o. 8 `8.`8888.,8' \n"
+ "88 8888 88 ,8'8.`8888,8^8.`8888. 8o. `Y888888o. 8 `8.`88888' \n"
+ "88 8888 88 ,8' `8.`8888' `8.`8888. 8`Y8o. `Y88888o8 `8. 8888 \n"
+ "88 8888 ,8P ,8' `8.`88' `8.`8888. 8 `Y8o. `Y8888 `8 8888 \n"
+ "`8 8888 ,8P ,8' `8.`' `8.`8888. 8 `Y8o. `Y8 8 8888 \n"
+ " ` 8888 ,88' ,8' `8 `8.`8888. 8 `Y8o.` 8 8888 \n"
+ " `8888888P' ,8' ` `8.`8888. 8 `Yo 8 8888 \n"
+ "</pre>"
)
self.idle_text_box.set_html_text(text)
def _omnycam_clear(self):
self.omnygui_remove_all_docks()
def _omnycam_check_device_exists(self, device):
try:
device
except:
return False
else:
return True
def _omnycam_samplestage(self):
self.omnygui_show_omnycam_samplestage()
def omnygui_show_omnycam_samplestage(self):
self.omnygui_show_gui()
if self.fig201 is None:
self.omnygui_remove_all_docks()
self.fig201 = self.gui_window.add_dock(name="omnycam201").add_widget("BECImageWidget")
if self._omnycam_check_device_exists(dev.cam201):
fig = self.fig201.image("cam201")
fig.set_rotation(deg_90=3)
self.fig201.lock_aspect_ratio(True)
else:
print("Cannot open cam201. Device does not exist.")
self.fig202 = self.gui_window.add_dock(name="omnycam202").add_widget("BECImageWidget")
if self._omnycam_check_device_exists(dev.cam202):
fig = self.fig202.image("cam202")
fig.set_rotation(deg_90=3)
self.fig202.lock_aspect_ratio(True)
else:
print("Cannot open cam202. Device does not exist.")
try:
self.gui_window.remove_dock(name="default_figure")
except:
pass
def omnygui_show_progress(self):
self.omnygui_show_gui()
if self.progressbar is None:
self.omnygui_remove_all_docks()
# Add a new dock with a RingProgressBar widget
self.progressbar = self.gui_window.add_dock(name="progress").add_widget("RingProgressBar")
# Customize the size of the progress ring
self.progressbar.set_line_widths(20)
# Disable automatic updates and manually set the self.progressbar value
self.progressbar.enable_auto_updates(False)
# Set precision for the self.progressbar display
self.progressbar.set_precision(1) # Display self.progressbar with one decimal places
# Setting multiple rigns with different values
self.progressbar.set_number_of_bars(3)
self.progressbar.rings[2].set_update("scan")
# Set the values of the rings to 50, 75, and 25 from outer to inner ring
# self.progressbar.set_value([50, 75])
# Add a new dock with a TextBox widget
self.text_box = self.gui_window.add_dock(name="progress_text").add_widget("TextBox")
try:
self.gui_window.remove_dock(name="default_figure")
except:
pass
self._omnygui_update_progress()
def _omnygui_update_progress(self):
if self.progressbar is not None:
progress = self.progress["projection"] / self.progress["total_projections"] * 100
subotmo_progress = (
self.progress["subtomo_projection"]
/ self.progress["subtomo_total_projections"]
* 100
)
self.progressbar.set_value([progress, subotmo_progress])
text = f"Progress report:\n Tomo type: ....................... {self.progress['tomo_type']}\n Projection: ...................... {self.progress['projection']:.0f}\n Total projections expected ....... {self.progress['total_projections']}\n Angle: ........................... {self.progress['angle']}\n Current subtomo: ................. {self.progress['subtomo']}\n Current projection within subtomo: {self.progress['subtomo_projection']}\n Total projections per subtomo: ... {self.progress['subtomo_total_projections']}"
self.text_box.set_plain_text(text)

File diff suppressed because it is too large Load Diff

View File

@@ -1,246 +0,0 @@
import time
import numpy as np
import os
from rich import box
from rich.console import Console
from rich.table import Table
from typeguard import typechecked
from bec_lib import bec_logger
logger = bec_logger.logger
class OMNYAlignmentError(Exception):
pass
class OMNYAlignmentMixin:
default_correction_file = "correction_omny_202204.txt"
default_correction_file_x = "correction_omny_202204_x.txt"
def reset_correction(self, use_default_correction=True):
"""
Reset the correction to the default values.
If use_default_correction is False, the correction will be set to empty values.
Otherwise the default values will be loaded.
Args:
use_default_correction (bool, optional): If set to true, a call reset the correction to the default values. Defaults to True.
"""
self.corr_pos_x = []
self.corr_angle_x = []
self.corr_pos_y = []
self.corr_angle_y = []
self.corr_pos_y_2 = []
self.corr_angle_y_2 = []
if use_default_correction:
try:
self.read_additional_correction_x(self.default_correction_file_x)
logger.info(f"Applying default x correction from {self.default_correction_file_x}")
except FileNotFoundError:
logger.warning(
f"Could not find default correction file {self.default_correction_file_x}."
)
logger.warning("Not applying any correction.")
try:
self.read_additional_correction_y(self.default_correction_file)
logger.info(f"Applying default y correction from {self.default_correction_file}")
except FileNotFoundError:
logger.warning(
f"Could not find default correction file {self.default_correction_file}."
)
logger.warning("Not applying any correction.")
def reset_tomo_alignment_fit(self):
self.client.delete_global_var("tomo_alignment_fit")
def read_alignment_offset(
self,
dir_path=os.path.expanduser("~/Data10/specES1/internal/"),
setup="omny",
use_vertical_default_values=True,
):
"""
Read the alignment offset from the given directory and set the global parameter
tomo_alignment_fit.
Args:
dir_path (str, optional): The directory to read the alignment offset from. Defaults to os.path.expanduser("~/Data10/specES1/internal/").
"""
tomo_alignment_fit = np.zeros((2, 5))
with open(os.path.join(dir_path, "ptychotomoalign_Ax.txt"), "r") as file:
tomo_alignment_fit[0][0] = file.readline()
with open(os.path.join(dir_path, "ptychotomoalign_Bx.txt"), "r") as file:
tomo_alignment_fit[0][1] = file.readline()
with open(os.path.join(dir_path, "ptychotomoalign_Cx.txt"), "r") as file:
tomo_alignment_fit[0][2] = file.readline()
with open(os.path.join(dir_path, "ptychotomoalign_Ay.txt"), "r") as file:
tomo_alignment_fit[1][0] = file.readline()
with open(os.path.join(dir_path, "ptychotomoalign_By.txt"), "r") as file:
tomo_alignment_fit[1][1] = file.readline()
with open(os.path.join(dir_path, "ptychotomoalign_Cy.txt"), "r") as file:
tomo_alignment_fit[1][2] = file.readline()
with open(os.path.join(dir_path, "ptychotomoalign_Ay3.txt"), "r") as file:
tomo_alignment_fit[1][3] = file.readline()
with open(os.path.join(dir_path, "ptychotomoalign_Cy3.txt"), "r") as file:
tomo_alignment_fit[1][4] = file.readline()
print("New alignment parameters loaded:")
print(
f"X Amplitude {tomo_alignment_fit[0][0]}, "
f"X Phase {tomo_alignment_fit[0][1]}, "
f"X Offset {tomo_alignment_fit[0][2]}, "
f"Y Amplitude {tomo_alignment_fit[1][0]}, "
f"Y Phase {tomo_alignment_fit[1][1]}, "
f"Y Offset {tomo_alignment_fit[1][2]}, "
f"Y 3rd Order Amplitude {tomo_alignment_fit[1][3]}, "
f"Y 3rd Order Phase {tomo_alignment_fit[1][4]} ."
)
if use_vertical_default_values:
print(
f"Using default values for vertical alignment for setup {setup}. Optional: use_vertical_default_values=False"
)
if setup == "flomni":
tomo_alignment_fit[1][0] = 0
tomo_alignment_fit[1][1] = 0
tomo_alignment_fit[1][2] = 0
tomo_alignment_fit[1][3] = 0
tomo_alignment_fit[1][4] = 0
elif setup == "omny":
tomo_alignment_fit[1][0] = 2.588628
tomo_alignment_fit[1][1] = -2.385422
tomo_alignment_fit[1][2] = 0
tomo_alignment_fit[1][3] = 1.010583
tomo_alignment_fit[1][4] = -1.359157
print("Follwing parameters will be used:")
print(
f"X Amplitude {tomo_alignment_fit[0][0]}, "
f"X Phase {tomo_alignment_fit[0][1]}, "
f"X Offset {tomo_alignment_fit[0][2]}, "
f"Y Amplitude {tomo_alignment_fit[1][0]}, "
f"Y Phase {tomo_alignment_fit[1][1]}, "
f"Y Offset {tomo_alignment_fit[1][2]}, "
f"Y 3rd Order Amplitude {tomo_alignment_fit[1][3]}, "
f"Y 3rd Order Phase {tomo_alignment_fit[1][4]} ."
)
self.client.set_global_var("tomo_alignment_fit", tomo_alignment_fit.tolist())
# x amp, phase, offset, y amp, phase, offset, 3rd order amp, 3rd order phase
# 0 0 0 1 0 2 1 0 1 1 1 2 1 3 1 4
def get_alignment_offset(self, angle: float):
"""
Compute the alignment offset for the given angle.
Args:
angle (float): The angle to compute the alignment offset for.
Returns:
tuple: The alignment offset in x, y and z direction.
"""
tomo_alignment_fit = self.client.get_global_var("tomo_alignment_fit")
if tomo_alignment_fit is None:
print("Not applying any alignment offsets. No tomo alignment fit data available.\n")
return (0, 0, 0)
# x amp, phase, offset, y amp, phase, offset
# 0 0 0 1 0 2 1 0 1 1 1 2
correction_x = (
tomo_alignment_fit[0][0] * np.sin(np.radians(angle) + tomo_alignment_fit[0][1])
+ tomo_alignment_fit[0][2]
)
correction_y = (
tomo_alignment_fit[1][0] * np.sin(np.radians(angle) + tomo_alignment_fit[1][1])
+ tomo_alignment_fit[1][2]
+ tomo_alignment_fit[1][3] * np.sin(3 * np.radians(angle) + tomo_alignment_fit[1][4])
)
correction_z = tomo_alignment_fit[0][0] * np.sin(
np.radians(angle + 90) + tomo_alignment_fit[0][1]
)
print(
f"Alignment offset x {correction_x}, y {correction_y}, z {correction_z} for angle"
f" {angle}\n"
)
return (correction_x, correction_y, correction_z)
def _read_correction_file(self, correction_file: str):
with open(correction_file, "r") as f:
num_elements = f.readline()
int_num_elements = int(num_elements.split(" ")[2])
corr_pos = []
corr_angle = []
for j in range(int_num_elements * 2):
line = f.readline()
value = line.split(" ")[2]
name = line.split(" ")[0].split("[")[0]
if name == "corr_pos":
corr_pos.append(float(value))
elif name == "corr_angle":
corr_angle.append(float(value))
print(
f"Loading default mirror correction from file {correction_file} containing {int_num_elements} elements."
)
# print(corr_pos)
return corr_pos, corr_angle
def read_additional_correction_x(self, correction_file: str):
self.corr_pos_x, self.corr_angle_x = self._read_correction_file(correction_file)
def read_additional_correction_y(self, correction_file: str):
self.corr_pos_y, self.corr_angle_y = self._read_correction_file(correction_file)
def read_additional_correction_y_2(self, correction_file: str):
self.corr_pos_y_2, self.corr_angle_y_2 = self._read_correction_file(correction_file)
def compute_additional_correction_x(self, angle):
return self._compute_additional_correction(angle, iteration="x1")
def compute_additional_correction_y(self, angle):
return self._compute_additional_correction(angle, iteration="y1")
def compute_additional_correction_y_2(self, angle):
return self._compute_additional_correction(angle, iteration="y2")
def _compute_additional_correction(self, angle, iteration="y1"):
if iteration == "x1":
corr_pos = self.corr_pos_x
corr_angle = self.corr_angle_x
elif iteration == "y1":
corr_pos = self.corr_pos_y
corr_angle = self.corr_angle_y
elif iteration == "y2":
corr_pos = self.corr_pos_y_2
corr_angle = self.corr_angle_y_2
if not corr_pos:
print(f"Not applying any additional correction {iteration}. No data available.\n")
return 0
# find index of closest angle
for j, _ in enumerate(corr_pos):
newangledelta = np.fabs(corr_angle[j] - angle)
if j == 0:
angledelta = newangledelta
additional_correction_shift = corr_pos[j]
continue
if newangledelta < angledelta:
additional_correction_shift = corr_pos[j]
angledelta = newangledelta
if additional_correction_shift == 0 and angle > corr_angle[-1]:
additional_correction_shift = corr_pos[-1]
print(f"Additional correction shift {iteration}: {additional_correction_shift}")
return additional_correction_shift

View File

@@ -1,153 +0,0 @@
import time
import numpy as np
import sys
import termios
import tty
import fcntl
import os
import builtins
from rich import box
from rich.console import Console
from rich.table import Table
# from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose
if builtins.__dict__.get("bec") is not None:
bec = builtins.__dict__.get("bec")
dev = builtins.__dict__.get("dev")
umv = builtins.__dict__.get("umv")
umvr = builtins.__dict__.get("umvr")
class OMNYToolsError(Exception):
pass
class OMNYTools:
HEADER = "\033[95m"
OKBLUE = "\033[94m"
OKCYAN = "\033[96m"
OKGREEN = "\033[92m"
WARNING = "\033[93m"
FAIL = "\033[91m"
ENDC = "\033[0m"
BOLD = "\033[1m"
UNDERLINE = "\033[4m"
def __init__(self, client) -> None:
self.client = client
@staticmethod
def _get_user_param_safe(device, var):
param = dev[device].user_parameter
if not param or param.get(var) is None:
raise ValueError(f"Device {device} has no user parameter definition for {var}.")
return param.get(var)
def printgreen(self, string: str):
print(self.OKGREEN + string + self.ENDC)
def printgreenbold(self, string: str):
print(self.BOLD + self.OKGREEN + string + self.ENDC)
def yesno(self, message: str, default="none", autoconfirm=0) -> bool:
if autoconfirm and default == "y":
self.printgreen(message + " Automatically confirming default: yes")
return True
elif autoconfirm and default == "n":
self.printgreen(message + " Automatically confirming default: no")
return False
if default == "y":
message_ending = " [Y]/n? "
elif default == "n":
message_ending = " y/[N]? "
else:
message_ending = " y/n? "
while True:
user_input = input(self.OKBLUE + message + message_ending + self.ENDC)
if (
user_input == "Y" or user_input == "y" or user_input == "yes" or user_input == "Yes"
) or (default == "y" and user_input == ""):
return True
if (
user_input == "N" or user_input == "n" or user_input == "no" or user_input == "No"
) or (default == "n" and user_input == ""):
return False
else:
print("Please expicitely confirm y or n.")
def tweak_cursor(
self, dev1, step1: float, dev2="none", step2: float = "0", special_command="none"
):
if dev1 not in dev.enabled_devices:
print(f"Device 1 {dev} is not in enabled devices.")
return
if dev2 not in dev.enabled_devices and dev2 != "none":
print(f"Device 2 {dev} is not in enabled devices.")
return
# Save the current terminal settings
fd = sys.stdin.fileno()
old_term = termios.tcgetattr(fd)
try:
# Set the terminal to raw mode to capture single key presses
tty.setraw(fd)
# Set stdin to non-blocking mode
old_flags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, old_flags | os.O_NONBLOCK)
print("Tweak Cursor." + self.BOLD + self.OKBLUE + "Press (q) to quit!\r" + self.ENDC)
while True:
try:
# Read single character input
key = sys.stdin.read(1)
if key == "q":
print("\n\rExiting tweak mode\r")
break
elif key == "\x1b": # Escape sequences for arrow keys
next1, next2 = sys.stdin.read(2)
if next1 == "[":
if next2 == "A":
# print("up")
if dev2 != "none":
umvr(dev2, step2)
if special_command != "none":
special_command()
elif next2 == "B":
# print(" down")
if dev2 != "none":
umvr(dev2, -step2)
if special_command != "none":
special_command()
elif next2 == "C":
# print("right")
umvr(dev1, step1)
if special_command != "none":
special_command()
elif next2 == "D":
# print("left")
umvr(dev1, -step1)
if special_command != "none":
special_command()
elif key == "+":
step1 = step1 * 2
if dev2 != "none":
step2 = step2 * 2
print(f"\rDouble step size. New step size: {step1}, {step2}\r")
elif key == "-":
step1 = step1 / 2
if dev2 != "none":
step2 = step2 / 2
print(f"\rHalf step size. New step size: {step1}, {step2}\r")
except IOError:
# No input available, keep looping
pass
# Sleep for a short period to avoid high CPU usage
time.sleep(0.02)
finally:
# Restore the terminal to its original state
termios.tcsetattr(fd, termios.TCSADRAIN, old_term)
fcntl.fcntl(fd, fcntl.F_SETFL, old_flags)

View File

@@ -1,384 +0,0 @@
import time
import numpy as np
from rich import box
from rich.console import Console
from rich.table import Table
from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_put, fshclose
class OMNYError(Exception):
pass
class OMNYOpticsMixin:
@staticmethod
def _get_user_param_safe(device, var):
param = dev[device].user_parameter
if not param or param.get(var) is None:
raise ValueError(f"Device {device} has no user parameter definition for {var}.")
return param.get(var)
def ooptics_in(self):
self.ofzp_in()
# ocs_in
self.oosa_in()
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_enable()
self.align.update_frame()
user_input = input(
"Is the direct beam gone on the xray eye? Do you see the cone of the FZP?"
)
if user_input == "y":
printf("Next oeye_out...\n")
else:
raise OMNYError("Failed to properly move in the Xray optics")
def _oeyey_mv(self, position):
# direction dependent speeds
if dev.oeyez.get().readback < position:
dev.oeyez.controller.socket_put_confirmed("axspeed[7]=15000")
else:
dev.oeyez.controller.socket_put_confirmed("axspeed[7]=10000")
umv(dev.oeyey, position)
dev.oeyez.controller.socket_put_confirmed("axspeed[7]=10000")
def oeye_out(self):
fshclose()
if self.OMNYTools.yesno("Did you move in the optics?"):
umv(dev.oeyez, -2)
self._oeyey_mv(-60.3)
# free camera
epics_put("XOMNYI-XEYE-ACQ:0", 2)
else:
raise OMNYError("The optics were not moved in. Please do so prior to eyey_out")
self.OMNYTools.printgreen("Oeye is out.")
def oeye_cam_in(self):
if dev.oeyez.get().readback < -80:
umv(dev.oeyez, -50)
if np.fabs(dev.oeyey.get().readback + 4.8) > 0.1:
self._oeyey_mv(-4.8)
if np.fabs(dev.oeyez.get().readback + 2) > 0.1 or np.fabs(dev.oeyex.get().readback) > 0.1:
umv(dev.oeyez, -2, dev.oeyex, 0)
# if still too close in z -- safety check
if np.fabs(dev.oeyez.get().readback + 2) > 0.1:
raise OMNYError("The oeye is too close in z for transfer. ERROR! Aborting.")
self.OMNYTools.printgreen("Oeye is at cam position.")
def _oeye_xray_is_in(self) -> bool:
omny_oeye_xray_inx = self._get_user_param_safe("oeyex", "xray_in")
omny_oeye_xray_iny = self._get_user_param_safe("oeyey", "xray_in")
omny_oeye_currentx = dev.oeyex.get().readback
omny_oeye_currenty = dev.oeyey.get().readback
if (
np.fabs(omny_oeye_currentx - omny_oeye_xray_inx) < 0.1
and np.fabs(omny_oeye_currenty - omny_oeye_xray_iny) < 0.1
):
return True
else:
return False
def oeye_xray_in(self):
if self._oeye_xray_is_in():
pass
else:
# todo
# self._otransfer_gripper_safe_xray_in_operation()
# if(!_oshield_is_ST_closed())
# {
# printf("The shield of the sample stage is not closed. Aborting.\n")
# exit
# }
omny_oeye_xray_inx = self._get_user_param_safe("oeyex", "xray_in")
omny_oeye_xray_iny = self._get_user_param_safe("oeyey", "xray_in")
omny_oeye_xray_inz = self._get_user_param_safe("oeyez", "xray_in")
self._oeyey_mv(omny_oeye_xray_iny)
omny_oeye_currenty = dev.oeyey.get().readback
if np.fabs(omny_oeye_currenty - omny_oeye_xray_iny) > 0.1:
raise OMNYError("The oeye did not move up.\n")
umv(dev.oeyex, omny_oeye_xray_inx, dev.oeyez, omny_oeye_xray_inz)
self.OMNYTools.printgreen("Oeye is at X-ray position.")
# some notes for the vis microscope:
# initial position for the vis light microscope
# do not open the shield when the microscope is at the vis mic position
# found eoeyx -45.13, z -84.9, y 0.64
# for a samy position of 2.8 with delta off
# the osa position should be in z around 7.4. in x it seems better
# around -0.6, where potentially xrays dont pass anymore
#
def _oosa_check_y(self):
omny_oosa_currenty = dev.oosay.get().readback
if np.fabs(omny_oosa_currenty - 0.9) > 0.05:
umv(dev.oosay, 0.9)
omny_oosa_currenty = dev.oosay.get().readback
if np.fabs(omny_oosa_currenty - 0.9) > 0.05:
raise OMNYError("oosay is not around 0.9. Aborting.")
def _oosa_to_move_corridor(self):
self._oosa_check_y()
dev.oosax.limits = [-3, 3.7] # risk collision with shield
umv(dev.oosax, -2)
dev.oosax.read(cached=False)
omny_oosa_currentx = dev.oosax.get().readback
if np.fabs(omny_oosa_currentx + 2) > 0.1:
raise OMNYError("oosax did not reach target position. Not moving in z.\n")
def oosa_in(self):
self._oosa_check_y()
dev.oshield.read(cached=False)
omny_oshield_current = dev.oshield.get().readback
if omny_oshield_current < 15:
self._oshield_ST_close()
if self.near_field == False:
x_in_pos = self._get_user_param_safe("oosax", "far_field_in")
y_in_pos = self._get_user_param_safe("oosay", "far_field_in")
z_in_pos = self._get_user_param_safe("oosaz", "far_field_in")
print("OSA movement in far-field mode.")
dev.oosaz.read(cached=False)
omny_oosa_currentz = dev.oosaz.get().readback
if omny_oosa_currentz < 6.4:
self._oosa_to_move_corridor()
dev.oosaz.limits = [6.4, 6.6]
umv(dev.oosaz, z_in_pos)
umv(dev.oosax, x_in_pos)
umv(dev.oosay, y_in_pos)
#### For the 30 nm FZP 220 um we use this part
# umv oosaz 6.5
# umv oosax 3.2453
# umv oosay 0.386015
if self.near_field == True:
x_in_pos = self._get_user_param_safe("oosax", "near_field_in")
y_in_pos = self._get_user_param_safe("oosay", "near_field_in")
z_in_pos = self._get_user_param_safe("oosaz", "near_field_in")
print("OSA movement in near-field mode.")
dev.oosaz.read(cached=False)
omny_oosa_currentz = dev.oosaz.get().readback
if omny_oosa_currentz > 0:
self._oosa_to_move_corridor()
dev.oosaz.limits = [-0.4, -0.6]
umv(dev.oosaz, z_in_pos)
umv(dev.oosax, x_in_pos)
omny_osamy_current = dev.osamy.get().readback
if omny_osamy_current < 3.25:
umv(dev.oosay, y_in_pos)
else:
raise OMNYError("Failed to move oosa in. osamy position is too large.")
self.OMNYTools.printgreen("OSA is in.")
# todo
# _omny_interferometer_align_tracking
# rt_feedback_enable
def oosa_out(self):
self._oosa_check_y()
dev.oshield.read(cached=False)
omny_oshield_current = dev.oshield.get().readback
if omny_oshield_current < 15:
self._oshield_ST_close()
omny_oosaz_current = dev.oosaz.get().readback
if self.near_field == False:
print("OSA movement in far-field mode.")
if omny_oosaz_current < 6.4:
self._oosa_to_move_corridor()
dev.oosaz.limits = [6.4, 6.6]
umv(dev.oosaz, 6.5)
umv(dev.oosax, -2)
if self.near_field == True:
print("OSA movement in near-field mode.")
if omny_oosaz_current > 0:
self._oosa_to_move_corridor()
dev.oosaz.limits = [-0.4, -0.6]
umv(dev.oosaz, -0.45)
umv(dev.oosax, -2)
# todo _omny_interferometer_align_tracking
self.OMNYTools.printgreen("OSA is out.")
def oosa_move_out_of_shield(self):
# todo: _omnycam_samplestage
self._oosa_check_y()
self._oosa_to_move_corridor()
omny_osamx_current = dev.osamx.get().readback
if np.fabs(omny_osamx_current) > 0.2:
umv(dev.osamx, 0)
omny_oosaz_current = dev.oosaz.get().readback
if omny_oosaz_current > 0.1:
dev.oosaz.limits = [-0.1, 0.1]
umv(dev.oosaz, 0)
self.OMNYTools.printgreen("OSA is out of shield.")
def ofzp_out(self):
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_disable()
y_out_pos = self._get_user_param_safe("ofzpy", "out")
if np.fabs(dev.ofzpy.get().readback - y_out_pos) > 0.02:
umv(dev.ofzpy, y_out_pos)
self.OMNYTools.printgreen("FZP at out position")
def ofzp_in(self):
if "rtx" in dev and dev.rtx.enabled:
dev.rtx.controller.feedback_disable()
x_in_pos = self._get_user_param_safe("ofzpx", "in")
y_in_pos = self._get_user_param_safe("ofzpy", "in")
if np.fabs(dev.ofzpy.get().readback - y_in_pos) > 0.02:
umv(dev.ofzpy, y_in_pos)
if np.fabs(dev.ofzpx.get().readback - x_in_pos) > 0.02:
umv(dev.ofzpx, x_in_pos)
self.OMNYTools.printgreen("FZP at in position")
# 220 mu FZP at ofzpz 31.8025 for eiger probe (about 2.4 mm propagation after focus)
# umv(dev.ofzpy, 0.7944)
# if np.fabs(dev.ofzpx.get().readback+0.4317)>0.05:
# umv(dev.ofzpx, -0.4317)
# note the 220 fzp also works for near field 6.2 kev by just moving back osa and fzp
# ofzpz 24.8 leads to a 9.5 mm propagation distance.
# With the 220 mu FZP this gives 100 nm pixel recons
# for the oosa macro set near_field=1
# 170 mu FZP at 6.2 kev for large beam at ofzpz 31.8025 of about 58 mu diameter
# 120 mu FZP at ofzpz 28.1991
# 250 mu FZP 60 nm at 5.65 keV
# ofzpz 29.7 for propagation distance 2.2
# umv ofzpx -0.4457
# umv ofzpy 0.193630
# 150 um fzp, 60 nm, ofzpz 33.8 at 8.9 kev for propagation of 1.7 mm after focus
# umv ofzpx -0.756678
# umv ofzpy 0.193515
# 250 um 30 nm FZP upper right
# small abberrations, seems to give good results in weak objects
# ofzpx -0.609240
# umv ofzpy 0.118265
# 250 um 30 nm FZP lower right very aberated
# ofzpx -0.881935
# umv ofzpy 0.537050
# ofzpz 28.4027
# 5.30 mm prop at 8.9 keV, 45 nm pixel in near field
# ofzpz 33.103
# 0.6 mm prop at 8.9 kev far field 7 m flight tube at foptz
# ofzpz 49.4 is reachable just without interferometer swap
# which at 6.2 keV and 250 um diam, 30 nm should gives a propagation of 0.8 after focus
# and a beam size of 6 microns diamter
###coordinates 30 nm FZP for comparing them
# not sure if that is really correct
# FZP 1 - FZP 2
# FZP 5
# FZP 4 - FZP 1
# FZP
##upper right
# umv ofzpx -0.6154 ofzpy 0.1183
# umv ocsx -0.6070 ocsy 0.0540
# lower right
# umv ofzpx -0.8341 ofzpy 0.5683
# umv ocsx -0.3880 ocsy -0.3960
# lower left
# umv ofzpx -0.3876 ofzpy 0.7902
# umv ocsx -0.8380 ocsy -0.6180
# upper left
# umv ofzpx -0.1678 ofzpy 0.3403
# umv ocsx -1.0550 ocsy -0.1680
def ofzp_info(self, mokev_val=-1, ofzpz_val=-1):
print(f"{ofzpz_val}")
if mokev_val == -1:
try:
mokev_val = dev.mokev.readback.get()
except:
print(
"Device mokev does not exist. You can specify the energy in keV as an argument instead."
)
return
if ofzpz_val == -1:
ofzpz_val = dev.ofzpz.readback.get()
distance = 66 + 2.4 + 31.8025 - ofzpz_val
print(
f"\nThe sample is in a distance of \033[1m{distance:.1f} mm\033[0m from the 60 nm FZP.\n"
)
print(f"At the current energy of {mokev_val:.4f} keV we have following options:\n")
diameters = [80e-6, 100e-6, 120e-6, 150e-6, 170e-6, 200e-6, 220e-6, 250e-6]
console = Console()
table = Table(title="Outermost zone width \033[1m60 nm\033[0m", box=box.SQUARE)
table.add_column("Diameter", justify="center")
table.add_column("Focal distance", justify="center")
table.add_column("Current beam size", justify="center")
wavelength = 1.2398e-9 / mokev_val
for diameter in diameters:
outermost_zonewidth = 60e-9
focal_distance = diameter * outermost_zonewidth / wavelength * 1000
beam_size = -diameter / (focal_distance * 1000) * (focal_distance - distance) * 1e9
table.add_row(
f"{diameter*1e6:.2f} microns",
f"{focal_distance:.2f} mm",
f"{beam_size:.2f} microns",
)
console.print(table)
# 30 nm with additional spacer
distance = 53.84 + 0.6 + 33.1 - ofzpz_val
print(
f"\nThe sample is in a distance of \033[1m{distance:.1f} mm\033[0m from the 30 nm FZP.\n"
)
diameters = [150e-6, 250e-6]
console = Console()
table = Table(title="Outermost zone width \033[1m30 nm\033[0m", box=box.SQUARE)
table.add_column("Diameter", justify="center")
table.add_column("Focal distance", justify="center")
table.add_column("Current beam size", justify="center")
wavelength = 1.2398e-9 / mokev_val
for diameter in diameters:
outermost_zonewidth = 30e-9
focal_distance = diameter * outermost_zonewidth / wavelength * 1000
beam_size = -diameter / (focal_distance * 1000) * (focal_distance - distance) * 1e9
table.add_row(
f"{diameter*1e6:.2f} microns",
f"{focal_distance:.2f} mm",
f"{beam_size:.2f} microns",
)
console.print(table)
print(
"This function can be called with explicit energy and ofzpz position.\n Example: omny.ffzp_info(mokev_val=6.2, ofzpz_val=33.2)"
)
# from flomni
# oosaz_val = dev.oosaz.readback.get()
# print("\nOSA Information:")
# print(f" Current fosaz {fosaz_val:.1f}")
# print(
# f" The OSA will collide with a normal OMNY pin at fosaz \033[1m{(33-fosaz_val):.1f}\033[0m"
# )
# print(f" Remaining space: \033[1m{-fosaz_val+(33-foptz_val):.1f}\033[0m")

View File

@@ -1,241 +0,0 @@
import time
import numpy as np
import sys
import termios
import tty
import fcntl
import os
from rich import box
from rich.console import Console
from rich.table import Table
from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen, fshclose
class OMNY_rt_clientError(Exception):
pass
class OMNY_rt_client:
def __init__(self):
self.mirror_channel = -1
self.mirror_amplitutde_increase = 0
self.mirror_parameters = {}
for j in range(1, 9):
self.mirror_parameters[j] = dev.rtx.controller.get_mirror_parameters(j)
@staticmethod
def _get_user_param_safe(device, var):
param = dev[device].user_parameter
if not param or param.get(var) is None:
raise OMNY_rt_clientError(
f"Device {device} has no user parameter definition for {var}."
)
return param.get(var)
def _omny_interferometer_openloop_steps(self, channel, steps, amplitude):
dev.rtx.controller._omny_interferometer_openloop_steps(channel, steps, amplitude)
def interferometer_tweaking(self):
self._tweak_interferometer()
def _tweak_interferometer(self):
self.mirror_channel = -1
# Save the current terminal settings
fd = sys.stdin.fileno()
old_term = termios.tcgetattr(fd)
print("Ready to tweak the interferometer. Press q to quit.")
print("The arrows adjust directions.")
print("Numbers select the mirror aligner.")
try:
# Set the terminal to raw mode to capture single key presses
tty.setraw(fd)
# Set stdin to non-blocking mode
old_flags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, old_flags | os.O_NONBLOCK)
opt_mirrorname = "none"
max = 0
while True:
try:
# Read single character input
key = sys.stdin.read(1)
if key == "q":
self.mirror_amplitutde_increase = 0
self.mirror_channel = -1
print("\n\rExiting tweak mode\r")
break
elif key == "\x1b": # Escape sequences for arrow keys
next1, next2 = sys.stdin.read(2)
if next1 == "[":
printit = True
if next2 == "A":
# print("up")
if self.mirror_channel != -1:
self._omny_interferometer_openloop_steps(
4,
-self.mirror_parameters[self.mirror_channel][
"opt_steps2_neg"
],
self.mirror_parameters[self.mirror_channel][
"opt_amplitude2_neg"
]
+ self.mirror_amplitutde_increase,
)
elif next2 == "B":
# print(" down")
if self.mirror_channel != -1:
self._omny_interferometer_openloop_steps(
4,
self.mirror_parameters[self.mirror_channel][
"opt_steps2_pos"
],
self.mirror_parameters[self.mirror_channel][
"opt_amplitude2_pos"
]
+ self.mirror_amplitutde_increase,
)
elif next2 == "C":
# print("right")
if self.mirror_channel != -1:
self._omny_interferometer_openloop_steps(
3,
-self.mirror_parameters[self.mirror_channel][
"opt_steps1_neg"
],
self.mirror_parameters[self.mirror_channel][
"opt_amplitude1_neg"
]
+ self.mirror_amplitutde_increase,
)
elif next2 == "D":
# print("left")
if self.mirror_channel != -1:
self._omny_interferometer_openloop_steps(
3,
self.mirror_parameters[self.mirror_channel][
"opt_steps1_pos"
],
self.mirror_parameters[self.mirror_channel][
"opt_amplitude1_pos"
]
+ self.mirror_amplitutde_increase,
)
elif key.isdigit() and 1 <= int(key) <= 8:
self.mirror_channel = int(key)
opt_mirrorname = self.mirror_parameters[self.mirror_channel][
"opt_mirrorname"
]
autostop = self.mirror_parameters[self.mirror_channel]["opt_signal_stop"]
averaging_time = self.mirror_parameters[self.mirror_channel][
"opt_averaging_time"
]
print(
f"\nSelected mirror channel {self.mirror_channel}: {opt_mirrorname}. Autostop {autostop}. Signal averaging time: {averaging_time}\r"
)
if int(key) == 6:
dev.rtx.controller.laser_tracker_on()
dev.rtx.controller._omny_interferometer_switch_channel(self.mirror_channel)
max = 0
printit = True
elif key == "+":
print("\nIncreasing voltage amplitudes by 100.\r")
self.mirror_amplitutde_increase += 100
elif key == "-":
print("\nDecreasing voltage amplitudes by 100.\r")
self.mirror_amplitutde_increase -= 100
elif key == "a":
if self.mirror_channel != -1:
dev.rtx.controller._omny_interferometer_optimize(
mirror_channel=self.mirror_channel, channel=3
)
dev.rtx.controller._omny_interferometer_optimize(
mirror_channel=self.mirror_channel, channel=4
)
dev.rtx.controller._omny_interferometer_optimize(
mirror_channel=self.mirror_channel, channel=3
)
dev.rtx.controller._omny_interferometer_optimize(
mirror_channel=self.mirror_channel, channel=4
)
if self.mirror_channel != -1 and printit:
printit = False
signal = dev.rtx.controller._omny_interferometer_get_signalsample(
self.mirror_parameters[self.mirror_channel]["opt_signalchannel"],
self.mirror_parameters[self.mirror_channel]["opt_averaging_time"],
)
if signal > max:
max = signal
info_str = f"Channel {self.mirror_channel}, {opt_mirrorname}, Current signal: {signal:.0f}"
filling = " " * (50 - len(info_str))
# Calculate the number of filled and unfilled segments
length = 30
percentage = signal / max
filled_length = int(length * percentage)
unfilled_length = length - filled_length
bar = "#" * filled_length + "-" * unfilled_length
print(info_str + filling + "0 " + bar + f" {max:.0f} (q)uit\r", end="")
except IOError:
# No input available, keep looping
pass
# Sleep for a short period to avoid high CPU usage
time.sleep(0.02)
finally:
# Restore the terminal to its original state
termios.tcsetattr(fd, termios.TCSADRAIN, old_term)
fcntl.fcntl(fd, fcntl.F_SETFL, old_flags)
dev.rtx.controller._omny_interferometer_switch_alloff()
self.mirror_channel = -1
self.mirror_amplitutde_increase = 0
dev.rtx.controller.show_signal_strength_interferometer()
def show_signal_strength_interferometer(self):
dev.rtx.controller.show_signal_strength_interferometer()
def omny_interferometer_align_incoupling_angle(self):
dev.rtx.controller.omny_interferometer_align_incoupling_angle()
def interferometer_tweak_otrack(self):
self.OMNYTools.tweak_cursor(
dev.otrackz,
0.1,
dev.otracky,
0.1,
special_command=dev.rtx.controller.laser_tracker_print_intensity_for_otrack_tweaking,
)
def feedback_enable_with_reset(self):
dev.rtx.controller.feedback_enable_with_reset()
def feedback_disable(self):
dev.rtx.controller.feedback_disable()
def feedback_status(self):
if dev.rtx.controller.feedback_is_running():
print("Feedback is running.")
else:
print("Feedback is NOT running.")
def laser_tracker_on(self):
dev.rtx.controller.laser_tracker_on()
def laser_tracker_off(self):
dev.rtx.controller.laser_tracker_off()
def laser_tracker_show_all(self):
dev.rtx.controller.laser_tracker_show_all()
def omny_interferometer_align_tracking(self):
dev.rtx.controller.omny_interferometer_align_tracking()
def laser_tracker_check_and_wait_for_signalstrength(self):
dev.rtx.controller.laser_tracker_check_and_wait_for_signalstrength()

View File

@@ -1,235 +0,0 @@
from __future__ import annotations
import builtins
import os
import time
from typing import TYPE_CHECKING
from bec_lib import bec_logger
from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen
logger = bec_logger.logger
# import builtins to avoid linter errors
bec = builtins.__dict__.get("bec")
dev = builtins.__dict__.get("dev")
umv = builtins.__dict__.get("umv")
umvr = builtins.__dict__.get("umvr")
if TYPE_CHECKING:
from bec_ipython_client.plugins.omny import OMNY
class XrayEyeAlign:
# pixel calibration, multiply to get mm
PIXEL_CALIBRATION = 0.2 / 218 # .2 with binning
def __init__(self, client, omny: OMNY) -> None:
self.client = client
self.omny = omny
self.device_manager = client.device_manager
self.scans = client.scans
self.alignment_values = {}
self.omny.reset_correction()
self.omny.reset_tomo_alignment_fit()
def _reset_init_values(self):
self.shift_xy = [0, 0]
self._xray_fov_xy = [0, 0]
def save_frame(self):
epics_put("XOMNYI-XEYE-SAVFRAME:0", 1)
def update_frame(self):
epics_put("XOMNYI-XEYE-ACQDONE:0", 0)
# start live
epics_put("XOMNYI-XEYE-ACQ:0", 1)
# wait for start live
while epics_get("XOMNYI-XEYE-ACQDONE:0") == 0:
time.sleep(0.5)
print("waiting for live view to start...")
fshopen()
epics_put("XOMNYI-XEYE-ACQDONE:0", 0)
while epics_get("XOMNYI-XEYE-ACQDONE:0") == 0:
print("waiting for new frame...")
time.sleep(0.5)
time.sleep(0.5)
# stop live view
epics_put("XOMNYI-XEYE-ACQ:0", 0)
time.sleep(1)
# fshclose
print("got new frame")
def tomo_rotate(self, val: float):
# pylint: disable=undefined-variable
umv(self.device_manager.devices.osamroy, val)
def get_tomo_angle(self):
return self.device_manager.devices.osamroy.readback.get()
def update_fov(self, k: int):
self._xray_fov_xy[0] = max(epics_get(f"XOMNYI-XEYE-XWIDTH_X:{k}"), self._xray_fov_xy[0])
self._xray_fov_xy[1] = max(0, self._xray_fov_xy[0])
@property
def movement_buttons_enabled(self):
return [epics_get("XOMNYI-XEYE-ENAMVX:0"), epics_get("XOMNYI-XEYE-ENAMVY:0")]
@movement_buttons_enabled.setter
def movement_buttons_enabled(self, enabled: bool):
enabled = int(enabled)
epics_put("XOMNYI-XEYE-ENAMVX:0", enabled)
epics_put("XOMNYI-XEYE-ENAMVY:0", enabled)
def send_message(self, msg: str):
epics_put("XOMNYI-XEYE-MESSAGE:0.DESC", msg)
def align(self):
# reset shift xy and fov params
self._reset_init_values()
self.tomo_rotate(0)
epics_put("XOMNYI-XEYE-ANGLE:0", 0)
self.omny.oeye_xray_in()
self.omny.feedback_enable_with_reset()
# disable movement buttons
self.movement_buttons_enabled = False
sample_name = dev.omny_samples.get_sample_name_in_samplestage()
epics_put("XOMNYI-XEYE-SAMPLENAME:0.DESC", sample_name)
# this makes sure we are in a defined state
self.omny.feedback_disable()
epics_put("XOMNYI-XEYE-PIXELSIZE:0", self.PIXEL_CALIBRATION)
osamx_in = self.omny.OMNYTools._get_user_param_safe("osamx", "in")
umv(dev.osamx, osamx_in - 0.35)
self.omny.ofzp_in()
self.update_frame()
# enable submit buttons
self.movement_buttons_enabled = False
epics_put("XOMNYI-XEYE-SUBMIT:0", 0)
epics_put("XOMNYI-XEYE-STEP:0", 0)
self.send_message("Submit center value of FZP.")
k = 0
while True:
if epics_get("XOMNYI-XEYE-SUBMIT:0") == 1:
val_x = epics_get(f"XOMNYI-XEYE-XVAL_X:{k}") / 2 * self.PIXEL_CALIBRATION # in mm
self.alignment_values[k] = val_x
print(f"Clicked position {k}: x {self.alignment_values[k]}")
rtx_position = dev.rtx.readback.get() / 1000
print(f"Current rtx position {rtx_position}")
self.alignment_values[k] -= rtx_position
print(f"Corrected position {k}: x {self.alignment_values[k]}")
if k == 0: # received center value of FZP
self.send_message("please wait ...")
self.movement_buttons_enabled = False
epics_put("XOMNYI-XEYE-SUBMIT:0", -1) # disable submit button
self.omny.feedback_disable()
osamx_in = self.omny.OMNYTools._get_user_param_safe("osamx", "in")
umv(dev.osamx, osamx_in)
self.omny.ofzp_out()
self.update_frame()
epics_put("XOMNYI-XEYE-RECBG:0", 1)
while epics_get("XOMNYI-XEYE-RECBG:0") == 1:
time.sleep(0.5)
print("waiting for background frame...")
umv(dev.osamx, osamx_in)
time.sleep(0.5)
self.omny.feedback_enable_with_reset()
self.update_frame()
self.send_message("Adjust sample height and submit center")
epics_put("XOMNYI-XEYE-SUBMIT:0", 0)
self.movement_buttons_enabled = True
elif 1 <= k < 5: # received sample center value at samroy 0 ... 315
self.send_message("please wait ...")
epics_put("XOMNYI-XEYE-SUBMIT:0", -1)
self.movement_buttons_enabled = False
umv(dev.rtx, 0)
self.tomo_rotate(k * 45)
epics_put("XOMNYI-XEYE-ANGLE:0", self.get_tomo_angle())
self.update_frame()
self.send_message("Submit sample center")
epics_put("XOMNYI-XEYE-SUBMIT:0", 0)
epics_put("XOMNYI-XEYE-ENAMVX:0", 1)
self.update_fov(k)
elif k == 5: # received sample center value at samroy 270 and done
self.send_message("done...")
epics_put("XOMNYI-XEYE-SUBMIT:0", -1) # disable submit button
self.movement_buttons_enabled = False
self.update_fov(k)
break
k += 1
epics_put("XOMNYI-XEYE-STEP:0", k)
_xrayeyalignmvx = epics_get("XOMNYI-XEYE-MVX:0")
if _xrayeyalignmvx != 0:
umvr(dev.rtx, _xrayeyalignmvx)
print(f"Current rtx position {dev.rtx.readback.get() / 1000}")
epics_put("XOMNYI-XEYE-MVX:0", 0)
if k > 0:
epics_put(f"XOMNYI-XEYE-STAGEPOSX:{k}", dev.rtx.readback.get() / 1000)
time.sleep(3)
self.update_frame()
if k < 2:
# allow movements, store movements to calculate center
_xrayeyalignmvy = epics_get("XOMNYI-XEYE-MVY:0")
if _xrayeyalignmvy != 0:
self.omny.feedback_disable()
umvr(dev.osamy, _xrayeyalignmvy / 1000)
time.sleep(2)
epics_put("XOMNYI-XEYE-MVY:0", 0)
self.omny.feedback_enable_with_reset()
self.update_frame()
time.sleep(0.2)
self.write_output()
fovx = self._xray_fov_xy[0] * self.PIXEL_CALIBRATION * 1000 / 2
fovy = self._xray_fov_xy[1] * self.PIXEL_CALIBRATION * 1000 / 2
self.tomo_rotate(0)
umv(dev.rtx, 0)
# free camera
epics_put("XOMNYI-XEYE-ACQ:0", 2)
print(
f"The largest field of view from the xrayeyealign was \nfovx = {fovx:.0f} microns, fovy"
f" = {fovy:.0f} microns"
)
print("Use the matlab routine to FIT the current alignment...")
print("Then LOAD ALIGNMENT PARAMETERS by running omny.read_alignment_offset()\n")
def write_output(self):
file = os.path.expanduser("~/Data10/specES1/internal/xrayeye_alignmentvalues")
if not os.path.exists(file):
os.makedirs(os.path.dirname(file), exist_ok=True)
with open(file, "w") as alignment_values_file:
alignment_values_file.write("angle\thorizontal\n")
for k in range(1, 6):
fovx_offset = self.alignment_values[0] - self.alignment_values[k]
print(f"Writing to file new alignment: number {k}, value x {fovx_offset}")
alignment_values_file.write(f"{(k-1)*45}\t{fovx_offset*1000}\n")

View File

@@ -1,85 +0,0 @@
"""
Post startup script for the BEC client. This script is executed after the
IPython shell is started. It is used to load the beamline specific
information and to setup the prompts.
The script is executed in the global namespace of the IPython shell. This
means that all variables defined here are available in the shell.
If needed, bec command-line arguments can be parsed here. For example, to
parse the --session argument, add the following lines to the script:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--session", help="Session name", type=str, default="my_default_session")
args = parser.parse_args()
if args.session == "my_session":
print("Loading my_session session")
from bec_plugins.bec_ipython_client.plugins.my_session import *
else:
print("Loading default session")
from bec_plugins.bec_ipython_client.plugins.default_session import *
"""
# pylint: disable=invalid-name, unused-import, import-error, undefined-variable, unused-variable, unused-argument, no-name-in-module
from bec_lib import bec_logger
logger = bec_logger.logger
logger.info("Using the cSAXS startup script.")
# pylint: disable=import-error
_args = _main_dict["args"]
_session_name = "cSAXS"
if _args.session.lower() == "lamni":
from csaxs_bec.bec_ipython_client.plugins.cSAXS import *
from csaxs_bec.bec_ipython_client.plugins.LamNI import *
_session_name = "LamNI"
lamni = LamNI(bec)
logger.success("LamNI session loaded.")
elif _args.session.lower() == "csaxs":
print("Loading cSAXS session")
from csaxs_bec.bec_ipython_client.plugins.cSAXS import *
logger.success("cSAXS session loaded.")
# SETUP BEAMLINE INFO
from bec_ipython_client.plugins.SLS.sls_info import OperatorInfo, SLSInfo
from csaxs_bec.bec_ipython_client.plugins.cSAXS.beamline_info import BeamlineInfo
bec._beamline_mixin._bl_info_register(BeamlineInfo)
bec._beamline_mixin._bl_info_register(SLSInfo)
bec._beamline_mixin._bl_info_register(OperatorInfo)
# SETUP PROMPTS
bec._ip.prompts.session_name = _session_name
bec._ip.prompts.status = 1
# REGISTER BEAMLINE CHECKS
from bec_lib.bl_conditions import (
FastOrbitFeedbackCondition,
LightAvailableCondition,
ShutterCondition,
)
if "sls_machine_status" in dev:
print("Registering light available condition for SLS machine status")
_light_available_condition = LightAvailableCondition(dev.sls_machine_status)
bec.bl_checks.register(_light_available_condition)
if "x12sa_es1_shutter_status" in dev:
print("Registering shutter condition for X12SA ES1 shutter status")
_shutter_condition = ShutterCondition(dev.x12sa_es1_shutter_status)
bec.bl_checks.register(_shutter_condition)
# if hasattr(dev, "sls_fast_orbit_feedback"):
# print("Registering fast orbit feedback condition for SLS fast orbit feedback")
# _fast_orbit_feedback_condition = FastOrbitFeedbackCondition(dev.sls_fast_orbit_feedback)
# bec.bl_checks.register(_fast_orbit_feedback_condition)

View File

@@ -1,32 +0,0 @@
"""
Pre-startup script for BEC client. This script is executed before the BEC client
is started. It can be used to add additional command line arguments.
"""
import os
from bec_lib.service_config import ServiceConfig
import csaxs_bec
def extend_command_line_args(parser):
"""
Extend the command line arguments of the BEC client.
"""
parser.add_argument("--session", help="Session name", type=str, default="cSAXS")
return parser
# def get_config() -> ServiceConfig:
# """
# Create and return the ServiceConfig for the plugin repository
# """
# deployment_path = os.path.dirname(os.path.dirname(os.path.dirname(csaxs_bec.__file__)))
# files = os.listdir(deployment_path)
# if "bec_config.yaml" in files:
# return ServiceConfig(config_path=os.path.join(deployment_path, "bec_config.yaml"))
# else:
# return ServiceConfig(redis={"host": "localhost", "port": 6379})

View File

@@ -1,63 +0,0 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from bec_widgets.widgets.containers.auto_update.auto_updates import AutoUpdates
if TYPE_CHECKING: # pragma: no cover
from bec_lib.messages import ScanStatusMessage
class cSAXSUpdate(AutoUpdates):
#######################################################################
################# GUI Callbacks #######################################
#######################################################################
def on_start(self) -> None:
"""
Procedure to run when the auto updates are enabled.
"""
self.start_default_dock()
def on_stop(self) -> None:
"""
Procedure to run when the auto updates are disabled.
"""
def on_scan_open(self, msg: ScanStatusMessage) -> None:
"""
Procedure to run when a scan starts.
Args:
msg (ScanStatusMessage): The scan status message.
"""
if msg.scan_name == "line_scan" and msg.scan_report_devices:
return self.simple_line_scan(msg)
if msg.scan_name == "grid_scan" and msg.scan_report_devices:
return self.simple_grid_scan(msg)
if msg.scan_report_devices:
return self.best_effort(msg)
return None
def on_scan_closed(self, msg: ScanStatusMessage) -> None:
"""
Procedure to run when a scan ends.
Args:
msg (ScanStatusMessage): The scan status message.
"""
def on_scan_abort(self, msg: ScanStatusMessage) -> None:
"""
Procedure to run when a scan is aborted.
Args:
msg (ScanStatusMessage): The scan status message.
"""
class cSAXSUpdateAlignment(AutoUpdates): ...
class cSAXSUpdateScan(AutoUpdates): ...

View File

@@ -1,148 +0,0 @@
# This file was automatically generated by generate_cli.py
# type: ignore
from __future__ import annotations
from bec_lib.logger import bec_logger
from bec_widgets.cli.rpc.rpc_base import RPCBase, rpc_call, rpc_timeout
logger = bec_logger.logger
# pylint: skip-file
_Widgets = {
"OmnyAlignment": "OmnyAlignment",
"XRayEye": "XRayEye",
}
class OmnyAlignment(RPCBase):
@property
@rpc_call
def enable_live_view(self):
"""
None
"""
@enable_live_view.setter
@rpc_call
def enable_live_view(self):
"""
None
"""
@property
@rpc_call
def user_message(self):
"""
None
"""
@user_message.setter
@rpc_call
def user_message(self):
"""
None
"""
@property
@rpc_call
def sample_name(self):
"""
None
"""
@sample_name.setter
@rpc_call
def sample_name(self):
"""
None
"""
@property
@rpc_call
def enable_move_buttons(self):
"""
None
"""
@enable_move_buttons.setter
@rpc_call
def enable_move_buttons(self):
"""
None
"""
class XRayEye(RPCBase):
@rpc_call
def active_roi(self) -> "BaseROI | None":
"""
Return the currently active ROI, or None if no ROI is active.
"""
@property
@rpc_call
def enable_live_view(self):
"""
Get or set the live view enabled state.
"""
@enable_live_view.setter
@rpc_call
def enable_live_view(self):
"""
Get or set the live view enabled state.
"""
@property
@rpc_call
def user_message(self):
"""
None
"""
@user_message.setter
@rpc_call
def user_message(self):
"""
None
"""
@property
@rpc_call
def sample_name(self):
"""
None
"""
@sample_name.setter
@rpc_call
def sample_name(self):
"""
None
"""
@property
@rpc_call
def enable_move_buttons(self):
"""
None
"""
@enable_move_buttons.setter
@rpc_call
def enable_move_buttons(self):
"""
None
"""
class XRayEye2DControl(RPCBase):
@rpc_call
def remove(self):
"""
Cleanup the BECConnector
"""

View File

@@ -1,140 +0,0 @@
from typing import TypedDict
from bec_widgets.utils.error_popups import SafeSlot
import os
from bec_widgets.utils.bec_widget import BECWidget
from bec_widgets.utils.ui_loader import UILoader
from qtpy.QtWidgets import QWidget, QPushButton, QLineEdit, QLabel, QVBoxLayout
from bec_qthemes import material_icon
from bec_lib.logger import bec_logger
logger = bec_logger.logger
# class OmnyAlignmentUIComponents(TypedDict):
# moveRightButton: QPushButton
# moveLeftButton: QPushButton
# moveUpButton: QPushButton
# moveDownButton: QPushButton
# image: Image
class OmnyAlignment(BECWidget, QWidget):
USER_ACCESS = ["enable_live_view", "enable_live_view.setter", "user_message", "user_message.setter","sample_name", "sample_name.setter", "enable_move_buttons", "enable_move_buttons.setter"]
PLUGIN = True
ui_file = "./omny_alignment.ui"
def __init__(self, parent=None, **kwargs):
super().__init__(parent=parent, **kwargs)
self._load_ui()
def _load_ui(self):
current_path = os.path.dirname(__file__)
self.ui = UILoader(self).loader(os.path.join(current_path, self.ui_file))
layout = QVBoxLayout()
layout.addWidget(self.ui)
self.setLayout(layout)
icon_options = {"size": (16, 16), "convert_to_pixmap": False}
self.ui.moveRightButton.setText("")
self.ui.moveRightButton.setIcon(
material_icon(icon_name="keyboard_arrow_right", **icon_options)
)
self.ui.moveLeftButton.setText("")
self.ui.moveLeftButton.setIcon(
material_icon(icon_name="keyboard_arrow_left", **icon_options)
)
self.ui.moveUpButton.setText("")
self.ui.moveUpButton.setIcon(
material_icon(icon_name="keyboard_arrow_up", **icon_options)
)
self.ui.moveDownButton.setText("")
self.ui.moveDownButton.setIcon(
material_icon(icon_name="keyboard_arrow_down", **icon_options)
)
self.ui.confirmButton.setText("OK")
self.ui.liveViewSwitch.enabled.connect(self.on_live_view_enabled)
# self.ui.moveUpButton.clicked.connect(self.on_move_up)
@property
def enable_live_view(self):
return self.ui.liveViewSwitch.checked
@enable_live_view.setter
def enable_live_view(self, enable:bool):
self.ui.liveViewSwitch.checked = enable
@property
def user_message(self):
return self.ui.messageLineEdit.text()
@user_message.setter
def user_message(self, message:str):
self.ui.messageLineEdit.setText(message)
@property
def sample_name(self):
return self.ui.sampleLineEdit.text()
@sample_name.setter
def sample_name(self, message:str):
self.ui.sampleLineEdit.setText(message)
@SafeSlot(bool)
def on_live_view_enabled(self, enabled:bool):
from bec_widgets.widgets.plots.image.image import Image
logger.info(f"Live view is enabled: {enabled}")
image: Image = self.ui.image
if enabled:
image.image("cam_xeye")
return
image.disconnect_monitor("cam_xeye")
@property
def enable_move_buttons(self):
move_up:QPushButton = self.ui.moveUpButton
move_down:QPushButton = self.ui.moveDownButton
move_left:QPushButton = self.ui.moveLeftButton
move_right:QPushButton = self.ui.moveRightButton
return move_up.isEnabled() and move_down.isEnabled() and move_left.isEnabled() and move_right.isEnabled()
@enable_move_buttons.setter
def enable_move_buttons(self, enabled:bool):
move_up:QPushButton = self.ui.moveUpButton
move_down:QPushButton = self.ui.moveDownButton
move_left:QPushButton = self.ui.moveLeftButton
move_right:QPushButton = self.ui.moveRightButton
move_up.setEnabled(enabled)
move_down.setEnabled(enabled)
move_left.setEnabled(enabled)
move_right.setEnabled(enabled)
if __name__ == "__main__":
from qtpy.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)
widget = OmnyAlignment()
widget.show()
sys.exit(app.exec_())

View File

@@ -1 +0,0 @@
{'files': ['omny_alignment.py']}

View File

@@ -1,125 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>988</width>
<height>821</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="2" column="2">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="2">
<widget class="QPushButton" name="moveRightButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="moveLeftButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="moveUpButton">
<property name="text">
<string>Up</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="moveDownButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="confirmButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="1">
<widget class="QLineEdit" name="sampleLineEdit"/>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="messageLineEdit"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Sample</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Message</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" colspan="3">
<widget class="Image" name="image">
<property name="enable_toolbar" stdset="0">
<bool>false</bool>
</property>
<property name="inner_axes" stdset="0">
<bool>false</bool>
</property>
<property name="monitor" stdset="0">
<string>cam_xeye</string>
</property>
<property name="rotation" stdset="0">
<number>3</number>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="ToggleSwitch" name="liveViewSwitch"/>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Live View</string>
</property>
<property name="alignment">
<set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Image</class>
<extends>QWidget</extends>
<header>image</header>
</customwidget>
<customwidget>
<class>ToggleSwitch</class>
<extends>QWidget</extends>
<header>toggle_switch</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -1,54 +0,0 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
from bec_widgets.utils.bec_designer import designer_material_icon
from csaxs_bec.bec_widgets.widgets.omny_alignment.omny_alignment import OmnyAlignment
DOM_XML = """
<ui language='c++'>
<widget class='OmnyAlignment' name='omny_alignment'>
</widget>
</ui>
"""
class OmnyAlignmentPlugin(QDesignerCustomWidgetInterface): # pragma: no cover
def __init__(self):
super().__init__()
self._form_editor = None
def createWidget(self, parent):
t = OmnyAlignment(parent)
return t
def domXml(self):
return DOM_XML
def group(self):
return ""
def icon(self):
return designer_material_icon(OmnyAlignment.ICON_NAME)
def includeFile(self):
return "omny_alignment"
def initialize(self, form_editor):
self._form_editor = form_editor
def isContainer(self):
return False
def isInitialized(self):
return self._form_editor is not None
def name(self):
return "OmnyAlignment"
def toolTip(self):
return "OmnyAlignment"
def whatsThis(self):
return self.toolTip()

View File

@@ -1,15 +0,0 @@
def main(): # pragma: no cover
from qtpy import PYSIDE6
if not PYSIDE6:
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
return
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
from csaxs_bec.bec_widgets.widgets.omny_alignment.omny_alignment_plugin import OmnyAlignmentPlugin
QPyDesignerCustomWidgetCollection.addCustomWidget(OmnyAlignmentPlugin())
if __name__ == "__main__": # pragma: no cover
main()

View File

@@ -1,15 +0,0 @@
def main(): # pragma: no cover
from qtpy import PYSIDE6
if not PYSIDE6:
print("PYSIDE6 is not available in the environment. Cannot patch designer.")
return
from PySide6.QtDesigner import QPyDesignerCustomWidgetCollection
from csaxs_bec.bec_widgets.widgets.xray_eye.x_ray_eye_plugin import XRayEyePlugin
QPyDesignerCustomWidgetCollection.addCustomWidget(XRayEyePlugin())
if __name__ == "__main__": # pragma: no cover
main()

View File

@@ -1,426 +0,0 @@
from __future__ import annotations
from bec_lib import bec_logger
from bec_lib.endpoints import MessageEndpoints
from bec_qthemes import material_icon
from bec_widgets import BECWidget, SafeProperty, SafeSlot
from bec_widgets.widgets.plots.image.image import Image
from bec_widgets.widgets.plots.image.setting_widgets.image_roi_tree import ROIPropertyTree
from bec_widgets.widgets.plots.roi.image_roi import BaseROI, CircularROI, RectangularROI
from bec_widgets.widgets.utility.toggle.toggle import ToggleSwitch
from qtpy.QtCore import Qt, QTimer
from qtpy.QtWidgets import (
QFrame,
QGridLayout,
QHBoxLayout,
QLabel,
QLineEdit,
QPushButton,
QSizePolicy,
QSpinBox,
QToolButton,
QVBoxLayout,
QWidget,
)
logger = bec_logger.logger
CAMERA = ("cam_xeye", "image")
class XRayEye2DControl(BECWidget, QWidget):
def __init__(self, parent=None, step_size: int = 100, *arg, **kwargs):
super().__init__(parent=parent, *arg, **kwargs)
self.get_bec_shortcuts()
self._step_size = step_size
self.root_layout = QGridLayout(self)
self.setStyleSheet("""
QToolButton {
border: 1px solid;
border-radius: 4px;
}
""")
# Up
self.move_up_button = QToolButton(parent=self)
self.move_up_button.setIcon(material_icon('keyboard_double_arrow_up'))
self.root_layout.addWidget(self.move_up_button, 0, 2)
# Up tweak button
self.move_up_tweak_button = QToolButton(parent=self)
self.move_up_tweak_button.setIcon(material_icon('keyboard_arrow_up'))
self.root_layout.addWidget(self.move_up_tweak_button, 1, 2)
# Left
self.move_left_button = QToolButton(parent=self)
self.move_left_button.setIcon(material_icon('keyboard_double_arrow_left'))
self.root_layout.addWidget(self.move_left_button, 2, 0)
# Left tweak button
self.move_left_tweak_button = QToolButton(parent=self)
self.move_left_tweak_button.setIcon(material_icon('keyboard_arrow_left'))
self.root_layout.addWidget(self.move_left_tweak_button, 2, 1)
# Right
self.move_right_button = QToolButton(parent=self)
self.move_right_button.setIcon(material_icon('keyboard_double_arrow_right'))
self.root_layout.addWidget(self.move_right_button, 2, 4)
# Right tweak button
self.move_right_tweak_button = QToolButton(parent=self)
self.move_right_tweak_button.setIcon(material_icon('keyboard_arrow_right'))
self.root_layout.addWidget(self.move_right_tweak_button, 2, 3)
# Down
self.move_down_button = QToolButton(parent=self)
self.move_down_button.setIcon(material_icon('keyboard_double_arrow_down'))
self.root_layout.addWidget(self.move_down_button, 4, 2)
# Down tweak button
self.move_down_tweak_button = QToolButton(parent=self)
self.move_down_tweak_button.setIcon(material_icon('keyboard_arrow_down'))
self.root_layout.addWidget(self.move_down_tweak_button, 3, 2)
# Connections
self.move_up_button.clicked.connect(lambda: self.move("up", tweak=False))
self.move_up_tweak_button.clicked.connect(lambda: self.move("up", tweak=True))
self.move_down_button.clicked.connect(lambda: self.move("down", tweak=False))
self.move_down_tweak_button.clicked.connect(lambda: self.move("down", tweak=True))
self.move_left_button.clicked.connect(lambda: self.move("left", tweak=False))
self.move_left_tweak_button.clicked.connect(lambda: self.move("left", tweak=True))
self.move_right_button.clicked.connect(lambda: self.move("right", tweak=False))
self.move_right_tweak_button.clicked.connect(lambda: self.move("right", tweak=True))
@SafeProperty(int)
def step_size(self) -> int:
return self._step_size
@step_size.setter
def step_size(self, step_size: int):
self._step_size = step_size
@SafeSlot(bool)
def enable_controls_hor(self, enable: bool):
self.move_left_button.setEnabled(enable)
self.move_left_tweak_button.setEnabled(enable)
self.move_right_button.setEnabled(enable)
self.move_right_tweak_button.setEnabled(enable)
@SafeSlot(bool)
def enable_controls_ver(self, enable: bool):
self.move_up_button.setEnabled(enable)
self.move_up_tweak_button.setEnabled(enable)
self.move_down_button.setEnabled(enable)
self.move_down_tweak_button.setEnabled(enable)
def move(self, direction: str, tweak: bool = False):
step = self._step_size
if tweak:
step = int(self._step_size / 5)
if direction == "up":
self.dev.omny_xray_gui.mvy.set(step)
elif direction == "down":
self.dev.omny_xray_gui.mvy.set(-step)
elif direction == "left":
self.dev.omny_xray_gui.mvx.set(-step)
elif direction == "right":
self.dev.omny_xray_gui.mvx.set(step)
else:
logger.warning(f"Unknown direction {direction} for move command.")
class XRayEye(BECWidget, QWidget):
USER_ACCESS = ["active_roi", "enable_live_view", "enable_live_view.setter", "user_message", "user_message.setter",
"sample_name", "sample_name.setter", "enable_move_buttons", "enable_move_buttons.setter"]
PLUGIN = True
def __init__(self, parent=None, **kwargs):
super().__init__(parent=parent, **kwargs)
self.get_bec_shortcuts()
self._init_ui()
self._make_connections()
# Connection to redis endpoints
self.bec_dispatcher.connect_slot(self.device_updates, MessageEndpoints.device_readback("omny_xray_gui"))
self.connect_motors()
self.resize(800, 600)
QTimer.singleShot(0, self._init_gui_trigger)
def _init_ui(self):
self.core_layout = QHBoxLayout(self)
self.image = Image(parent=self)
self.image.enable_toolbar = False # Disable default toolbar to not allow to user set anything
self.image.inner_axes = False # Disable inner axes to maximize image area
self.image.plot_item.vb.invertY(True) # #TODO Invert y axis to match logic of LabView GUI
# Control panel on the right: vertical layout inside a fixed-width widget
self.control_panel = QWidget(parent=self)
self.control_panel_layout = QVBoxLayout(self.control_panel)
self.control_panel_layout.setContentsMargins(0, 0, 0, 0)
self.control_panel_layout.setSpacing(10)
# ROI toolbar + Live toggle (header row)
self.roi_manager = ROIPropertyTree(parent=self, image_widget=self.image, compact=True,
compact_orientation="horizontal")
header_row = QHBoxLayout()
header_row.setContentsMargins(0, 0, 0, 0)
header_row.setSpacing(8)
header_row.addWidget(self.roi_manager, 0)
header_row.addStretch()
self.live_preview_label = QLabel("Live Preview", parent=self)
self.live_preview_toggle = ToggleSwitch(parent=self)
self.live_preview_toggle.checked = False
header_row.addWidget(self.live_preview_label, 0, Qt.AlignVCenter)
header_row.addWidget(self.live_preview_toggle, 0, Qt.AlignVCenter)
self.control_panel_layout.addLayout(header_row)
# separator
self.control_panel_layout.addWidget(self._create_separator())
# 2D Positioner (fixed size)
self.motor_control_2d = XRayEye2DControl(parent=self)
self.control_panel_layout.addWidget(self.motor_control_2d, 0, Qt.AlignTop | Qt.AlignCenter)
# separator
self.control_panel_layout.addWidget(self._create_separator())
# Step size label
step_size_form = QGridLayout()
# General Step size
self.step_size = QSpinBox(parent=self)
self.step_size.setRange(10, 100)
self.step_size.setSingleStep(10)
self.step_size.setValue(100)
# Submit button
self.submit_button = QPushButton("Submit", parent=self)
# Add to layout form
step_size_form.addWidget(QLabel("Horizontal", parent=self), 0, 0)
step_size_form.addWidget(self.step_size, 0, 1)
step_size_form.addWidget(QLabel("Vertical", parent=self), 1, 0)
step_size_form.addWidget(self.submit_button, 2, 0, 1, 2)
# Add form to control panel
self.control_panel_layout.addLayout(step_size_form)
# Push form to bottom
self.control_panel_layout.addStretch()
# Sample/Message form (bottom)
form = QGridLayout()
self.sample_name_line_edit = QLineEdit(parent=self)
self.sample_name_line_edit.setReadOnly(True)
form.addWidget(QLabel("Sample", parent=self), 0, 0)
form.addWidget(self.sample_name_line_edit, 0, 1)
self.message_line_edit = QLineEdit(parent=self)
self.message_line_edit.setReadOnly(True)
form.addWidget(QLabel("Message", parent=self), 1, 0)
form.addWidget(self.message_line_edit, 1, 1)
self.control_panel_layout.addLayout(form)
# Fix panel width and allow vertical expansion
self.control_panel.adjustSize()
p_hint = self.control_panel.sizeHint()
self.control_panel.setFixedWidth(p_hint.width())
self.control_panel.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Expanding)
# Core Layout: image (expanding) | control panel (fixed)
self.core_layout.addWidget(self.image)
self.core_layout.addWidget(self.control_panel)
def _make_connections(self):
# Fetch initial state
self.on_live_view_enabled(True)
self.step_size.setValue(self.motor_control_2d.step_size)
# Make connections
self.live_preview_toggle.enabled.connect(self.on_live_view_enabled)
self.step_size.valueChanged.connect(lambda x: self.motor_control_2d.setProperty("step_size", x))
self.submit_button.clicked.connect(self.submit)
def _create_separator(self):
sep = QFrame(parent=self)
sep.setFrameShape(QFrame.HLine)
sep.setFrameShadow(QFrame.Sunken)
sep.setLineWidth(1)
return sep
def _init_gui_trigger(self):
self.dev.omny_xray_gui.read()
################################################################################
# Device Connection logic
################################################################################
def connect_motors(self):
""" Checks one of the possible motors for flomni, omny and lamni setup."""
possible_motors = ['osamroy', 'lsamrot', 'fsamroy']
for motor in possible_motors:
if motor in self.dev:
self.bec_dispatcher.connect_slot(self.on_tomo_angle_readback, MessageEndpoints.device_readback(motor))
logger.info(f"Succesfully connected to {motor}")
################################################################################
# Properties ported from the original OmnyAlignment, can be adjusted as needed
################################################################################
@SafeProperty(str)
def user_message(self):
return self.message_line_edit.text()
@user_message.setter
def user_message(self, message: str):
self.message_line_edit.setText(message)
@SafeProperty(str)
def sample_name(self):
return self.sample_name_line_edit.text()
@sample_name.setter
def sample_name(self, message: str):
self.sample_name_line_edit.setText(message)
@SafeProperty(bool)
def enable_move_buttons(self):
return self.motor_control_2d.isEnabled()
@enable_move_buttons.setter
def enable_move_buttons(self, enabled: bool):
self.motor_control_2d.setEnabled(enabled)
def active_roi(self) -> BaseROI | None:
"""Return the currently active ROI, or None if no ROI is active."""
return self.roi_manager.single_active_roi
################################################################################
# Slots ported from the original OmnyAlignment, can be adjusted as needed
################################################################################
@SafeSlot()
def get_roi_coordinates(self) -> dict | None:
"""Get the coordinates of the currently active ROI."""
roi = self.roi_manager.single_active_roi
if roi is None:
logger.warning("No active ROI")
return None
logger.info(f"Active ROI coordinates: {roi.get_coordinates()}")
return roi.get_coordinates()
@SafeSlot(bool)
def on_live_view_enabled(self, enabled: bool):
logger.info(f"Live view is enabled: {enabled}")
self.live_preview_toggle.blockSignals(True)
if enabled:
self.live_preview_toggle.checked = enabled
self.image.image(CAMERA)
self.live_preview_toggle.blockSignals(False)
return
self.image.disconnect_monitor(CAMERA)
self.live_preview_toggle.checked = enabled
self.live_preview_toggle.blockSignals(False)
@SafeSlot(bool, bool)
def on_motors_enable(self, x_enable: bool, y_enable: bool):
"""
Enable/Disable motor controls
Args:
x_enable(bool): enable x motor controls
y_enable(bool): enable y motor controls
"""
self.motor_control_2d.enable_controls_hor(x_enable)
self.motor_control_2d.enable_controls_ver(y_enable)
@SafeSlot(int)
def enable_submit_button(self, enable: int):
"""
Enable/disable submit button.
Args:
enable(int): -1 disable else enable
"""
if enable == -1:
self.submit_button.setEnabled(False)
else:
self.submit_button.setEnabled(True)
@SafeSlot(bool, bool)
def on_tomo_angle_readback(self, data: dict, meta: dict):
#TODO implement if needed
print(f"data: {data}")
print(f"meta: {meta}")
@SafeSlot(dict, dict)
def device_updates(self, data: dict, meta: dict):
"""
Slot to handle device updates from omny_xray_gui device.
Args:
data(dict): data from device
meta(dict): metadata from device
"""
signals = data.get('signals')
enable_live_preview = signals.get("omny_xray_gui_update_frame_acq").get('value')
enable_x_motor = signals.get("omny_xray_gui_enable_mv_x").get('value')
enable_y_motor = signals.get("omny_xray_gui_enable_mv_y").get('value')
self.on_live_view_enabled(bool(enable_live_preview))
self.on_motors_enable(bool(enable_x_motor), bool(enable_y_motor))
# Signals from epics gui device
# send message
user_message = signals.get("omny_xray_gui_send_message").get('value')
self.user_message = user_message
# sample name
sample_message = signals.get("omny_xray_gui_sample_name").get('value')
self.sample_name = sample_message
# enable frame acquisition
update_frame_acq = signals.get("omny_xray_gui_update_frame_acq").get('value')
self.on_live_view_enabled(bool(update_frame_acq))
# enable submit button
enable_submit_button = signals.get("omny_xray_gui_submit").get('value')
self.enable_submit_button(enable_submit_button)
@SafeSlot()
def submit(self):
"""Execute submit action by submit button."""
if self.roi_manager.single_active_roi is None:
logger.warning("No active ROI")
return
roi_coordinates = self.roi_manager.single_active_roi.get_coordinates()
roi_center_x = roi_coordinates['center_x']
roi_center_y = roi_coordinates['center_y']
# Case of rectangular ROI
if isinstance(self.roi_manager.single_active_roi, RectangularROI):
roi_width = roi_coordinates['width']
roi_height = roi_coordinates['height']
elif isinstance(self.roi_manager.single_active_roi, CircularROI):
roi_width = roi_coordinates['diameter']
roi_height = roi_coordinates['radius']
else:
logger.warning("Unsupported ROI type for submit action.")
return
print(f"current roi: x:{roi_center_x}, y:{roi_center_y}, w:{roi_width},h:{roi_height}") #TODO remove when will be not needed for debugging
# submit roi coordinates
step = int(self.dev.omny_xray_gui.step.read().get("omny_xray_gui_step").get('value'))
xval_x = getattr(self.dev.omny_xray_gui.xval_x, f"xval_x_{step}").set(roi_center_x)
xval_y = getattr(self.dev.omny_xray_gui.yval_y, f"yval_y_{step}").set(roi_center_y)
width_x = getattr(self.dev.omny_xray_gui.width_x, f"width_x_{step}").set(roi_width)
width_y = getattr(self.dev.omny_xray_gui.width_y, f"width_y_{step}").set(roi_height)
self.dev.omny_xray_gui.submit.set(1)
def cleanup(self):
"""Cleanup connections on widget close -> disconnect slots and stop live mode of camera."""
self.bec_dispatcher.disconnect_slot(self.device_updates, MessageEndpoints.device_readback("omny_xray_gui"))
getattr(self.dev,CAMERA[0]).live_mode = False
super().cleanup()
if __name__ == "__main__":
import sys
from qtpy.QtWidgets import QApplication
app = QApplication(sys.argv)
win = XRayEye()
win.resize(1000, 800)
win.show()
sys.exit(app.exec_())

View File

@@ -1 +0,0 @@
{'files': ['x_ray_eye.py']}

View File

@@ -1,57 +0,0 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
from bec_widgets.utils.bec_designer import designer_material_icon
from qtpy.QtDesigner import QDesignerCustomWidgetInterface
from qtpy.QtWidgets import QWidget
from csaxs_bec.bec_widgets.widgets.xray_eye.x_ray_eye import XRayEye
DOM_XML = """
<ui language='c++'>
<widget class='XRayEye' name='x_ray_eye'>
</widget>
</ui>
"""
class XRayEyePlugin(QDesignerCustomWidgetInterface): # pragma: no cover
def __init__(self):
super().__init__()
self._form_editor = None
def createWidget(self, parent):
if parent is None:
return QWidget()
t = XRayEye(parent)
return t
def domXml(self):
return DOM_XML
def group(self):
return ""
def icon(self):
return designer_material_icon(XRayEye.ICON_NAME)
def includeFile(self):
return "x_ray_eye"
def initialize(self, form_editor):
self._form_editor = form_editor
def isContainer(self):
return False
def isInitialized(self):
return self._form_editor is not None
def name(self):
return "XRayEye"
def toolTip(self):
return "XRayEye"
def whatsThis(self):
return self.toolTip()

View File

@@ -1,11 +0,0 @@
import os
def setup_epics_ca():
# os.environ["EPICS_CA_AUTO_ADDR_LIST"] = "NO"
# os.environ["EPICS_CA_ADDR_LIST"] = "129.129.122.255 sls-x12sa-cagw.psi.ch:5836"
os.environ["PYTHONIOENCODING"] = "latin1"
def run():
setup_epics_ca()

View File

@@ -1,212 +0,0 @@
# This configuration file was used for the cSAXS beamtimes in September 2023
##################################################
#############Config for cSAXS SAXS imaging########
##################################################
bpm4i:
description: 'XBPM 4: integrated counts'
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: X12SA-OP1-SCALER.
deviceTags:
- monitor
enabled: true
readOnly: false
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
mokev:
description: Monochromator energy in keV
deviceClass: csaxs_bec.devices.epics.specMotors.EnergyKev
deviceConfig:
read_pv: X12SA-OP-MO:ROX2
deviceTags:
- monitor
enabled: true
readOnly: false
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
# mcs:
# description: Mcs scalar card for transmission readout
# deviceClass: csaxs_bec.devices.epics.mcs_csaxs.MCScSAXS
# deviceConfig:
# prefix: 'X12SA-MCS:'
# mcs_config:
# num_lines: 1
# deviceTags:
# - cSAXS
# - mcs
# onFailure: buffer
# enabled: true
# readoutPriority: monitored
# softwareTrigger: false
eiger9m:
description: Eiger9m HPC area detector 9M
deviceClass: csaxs_bec.devices.epics.eiger9m_csaxs.Eiger9McSAXS
deviceConfig:
prefix: 'X12SA-ES-EIGER9M:'
deviceTags:
- cSAXS
- eiger9m
onFailure: buffer
enabled: true
readoutPriority: async
softwareTrigger: false
falcon:
description: Falcon detector x-ray fluoresence
deviceClass: csaxs_bec.devices.epics.falcon_csaxs.FalconcSAXS
deviceConfig:
prefix: 'X12SA-SITORO:'
deviceTags:
- cSAXS
- falcon
onFailure: buffer
enabled: true
readoutPriority: async
softwareTrigger: false
pilatus_2:
description: Pilatus2 HPC area detector 300k
deviceClass: csaxs_bec.devices.epics.pilatus_csaxs.PilatuscSAXS
deviceConfig:
prefix: 'X12SA-ES-PILATUS300K:'
deviceTags:
- cSAXS
- pilatus_2
onFailure: buffer
enabled: true
readoutPriority: async
softwareTrigger: false
samx:
description: SGalil motor stage
deviceClass: csaxs_bec.devices.omny.galil.SGalilMotor
deviceConfig:
axis_Id: "E"
host: '129.129.122.26'
port: 23
sign: -1
limits:
- -181
- -0.1
deviceTags:
- cSAXS
- sgalil
onFailure: buffer
enabled: true
readoutPriority: baseline
softwareTrigger: false
samy:
description: SGalil motor stage
deviceClass: csaxs_bec.devices.omny.galil.SGalilMotor
deviceConfig:
axis_Id: "C"
host: '129.129.122.26'
port: 23
sign: -1
limits:
- -66
- -12
deviceTags:
- cSAXS
- sgalil
onFailure: buffer
enabled: true
readoutPriority: baseline
softwareTrigger: false
micfoc:
description: Focusing motor of Microscope stage
deviceClass: ophyd_devices.devices.EpicsMotorEx
deviceConfig:
prefix: X12SA-ES2-ES06
motor_resolution: 0.00125
base_velocity: 0.25
velocity: 2.5
backlash_distance: 0.125
acceleration: 0.4
user_offset_dir: 0
deviceTags:
- cSAXS
- micfoc
onFailure: buffer
enabled: true
readoutPriority: baseline
softwareTrigger: false
owis_samx:
description: Owis motor stage samx
deviceClass: ophyd_devices.devices.EpicsMotorEx
deviceConfig:
prefix: X12SA-ES2-ES01
motor_resolution: 0.00125
base_velocity: 0.0625
velocity: 10
backlash_distance: 0.125
acceleration: 0.2
user_offset_dir: 0
deviceTags:
- cSAXS
- owis_samx
onFailure: buffer
enabled: true
readoutPriority: baseline
softwareTrigger: false
owis_samy:
description: Owis motor stage samx
deviceClass: ophyd_devices.devices.EpicsMotorEx
deviceConfig:
prefix: X12SA-ES2-ES02
motor_resolution: 0.00125
base_velocity: 0.0625
velocity: 10
backlash_distance: 0.125
acceleration: 0.2
user_offset_dir: 0
deviceTags:
- cSAXS
- owis_samx
onFailure: buffer
enabled: true
readoutPriority: baseline
softwareTrigger: false
rotx:
description: Rotation stage rotx
deviceClass: ophyd_devices.devices.EpicsMotorEx
deviceConfig:
prefix: X12SA-ES2-ES05
motor_resolution: 0.0025
base_velocity: 0.5
velocity: 7.5
backlash_distance: 0.25
acceleration: 0.2
user_offset_dir: 1
limits:
- -0.1
- 0.1
deviceTags:
- cSAXS
- rotx
onFailure: buffer
enabled: true
readoutPriority: baseline
softwareTrigger: false
roty:
description: Rotation stage rotx
deviceClass: ophyd_devices.devices.EpicsMotorEx
deviceConfig:
prefix: X12SA-ES2-ES04
motor_resolution: 0.0025
base_velocity: 0.5
velocity: 7.5
backlash_distance: 0.25
acceleration: 0.2
user_offset_dir: 1
limits:
- -0.1
- 0.1
deviceTags:
- cSAXS
- rotx
onFailure: buffer
enabled: true
readoutPriority: baseline
softwareTrigger: false

File diff suppressed because it is too large Load Diff

View File

@@ -1,55 +0,0 @@
ddg1:
description: Main delay Generator for triggering
deviceClass: csaxs_bec.devices.epics.delay_generator_csaxs.DDG1
enabled: true
deviceConfig:
prefix: 'X12SA-CPCL-DDG1:'
onFailure: raise
readOnly: false
readoutPriority: baseline
softwareTrigger: true
ddg2:
description: Detector delay Generator for trigger burst
deviceClass: csaxs_bec.devices.epics.delay_generator_csaxs.DDG2
enabled: true
deviceConfig:
prefix: 'X12SA-CPCL-DDG2:'
onFailure: raise
readOnly: false
readoutPriority: baseline
softwareTrigger: false
mcs:
description: Mcs scalar card for transmission readout
deviceClass: csaxs_bec.devices.epics.mcs_card.mcs_card_csaxs.MCSCardCSAXS
deviceConfig:
prefix: 'X12SA-MCS:'
onFailure: raise
enabled: true
readoutPriority: monitored
softwareTrigger: false
ids_cam:
description: IDS camera for live image acquisition
deviceClass: csaxs_bec.devices.ids_cameras.IDSCamera
deviceConfig:
camera_id: 201
bits_per_pixel: 24
m_n_colormode: 1
live_mode: True
onFailure: raise
enabled: true
readoutPriority: async
softwareTrigger: True
eiger_1_5:
description: Eiger 1.5M in-vacuum detector
deviceClass: csaxs_bec.devices.jungfraujoch.eiger_1_5m.Eiger1_5M
deviceConfig:
detector_distance: 100
beam_center: [0, 0]
onFailure: raise
enabled: true
readoutPriority: async
softwareTrigger: False

View File

@@ -1,8 +0,0 @@
optics:
- !include ./optics_hutch.yaml
frontend:
- !include ./frontend.yaml
endstation:
- !include ./endstation.yaml

View File

@@ -1,468 +0,0 @@
############################################################
#################### flOMNI Galil motors ###################
############################################################
feyex:
description: Xray eye X
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: D
host: mpc2844.psi.ch
limits:
- -30
- -1
port: 8082
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: -16.267
out: -1
feyey:
description: Xray eye Y
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: E
host: mpc2844.psi.ch
limits:
- -1
- -10
port: 8082
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: -10.467
fheater:
description: Heater Y
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: C
host: mpc2844.psi.ch
limits:
- -15
- 0
port: 8082
sign: -1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
foptx:
description: Optics X
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: B
host: mpc2844.psi.ch
limits:
- -17
- -12
port: 8082
sign: 1
enabled: true
onFailure: buffer
readOnly: true
readoutPriority: baseline
userParameter:
in: -13.761
fopty:
description: Optics Y
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: F
host: mpc2844.psi.ch
limits:
- 0
- 4
port: 8082
sign: 1
enabled: true
onFailure: buffer
readOnly: true
readoutPriority: baseline
userParameter:
in: 0.552
out: 0.752
foptz:
description: Optics Z
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
limits:
- 0
- 27
port: 8082
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 23
fsamroy:
description: Sample rotation
deviceClass: csaxs_bec.devices.omny.galil.fupr_ophyd.FuprGalilMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
limits:
- -5
- 365
port: 8084
sign: -1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
fsamx:
description: Sample coarse X
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: E
host: mpc2844.psi.ch
limits:
- -162
- 0
port: 8081
sign: 1
enabled: true
onFailure: buffer
readOnly: true
readoutPriority: baseline
userParameter:
in: -1.1
fsamy:
description: Sample coarse Y
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: F
host: mpc2844.psi.ch
limits:
- 2
- 3.1
port: 8081
sign: 1
enabled: true
onFailure: buffer
readOnly: true
readoutPriority: baseline
userParameter:
in: 2.75
ftracky:
description: Laser Tracker coarse Y
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: H
host: mpc2844.psi.ch
limits:
- 2.2
- 2.8
port: 8082
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
ftrackz:
description: Laser Tracker coarse Z
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: G
host: mpc2844.psi.ch
limits:
- 4.5
- 5.5
port: 8082
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
ftransx:
description: Sample transer X
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: C
host: mpc2844.psi.ch
limits:
- 0
- 50
port: 8081
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
ftransy:
description: Sample transer Y
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
limits:
- -100
- 0
port: 8081
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
sensor_voltage: -2.4
ftransz:
description: Sample transer Z
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: B
host: mpc2844.psi.ch
limits:
- 0
- 145
port: 8081
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
ftray:
description: Sample transfer tray
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: D
host: mpc2844.psi.ch
limits:
- -200
- 0
port: 8081
sign: -1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
############################################################
#################### flOMNI Sample Names ###################
############################################################
flomni_samples:
description: Sample names and storage
deviceClass: csaxs_bec.devices.omny.flomni_sample_storage.FlomniSampleStorage
deviceConfig: {}
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
############################################################
#################### flOMNI Smaract motors #################
############################################################
fosax:
description: OSA X
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
limits:
- 10.2
- 10.6
port: 3334
sign: -1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 9.124
out: 5.3
fosay:
description: OSA Y
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: B
host: mpc2844.psi.ch
limits:
- -3.1
- -2.9
port: 3334
sign: -1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 0.367
fosaz:
description: OSA Z
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: C
host: mpc2844.psi.ch
limits:
- -6
- -4
port: 3334
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 8.5
out: 6
############################################################
#################### flOMNI RT motors ######################
############################################################
rtx:
description: flomni rt
deviceClass: csaxs_bec.devices.omny.rt.rt_flomni_ophyd.RtFlomniMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
port: 2222
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: on_request
userParameter:
low_signal: 10000
min_signal: 9000
rt_pid_voltage: -0.06219
rty:
description: flomni rt
deviceClass: csaxs_bec.devices.omny.rt.rt_flomni_ophyd.RtFlomniMotor
deviceConfig:
axis_Id: B
host: mpc2844.psi.ch
port: 2222
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: on_request
userParameter:
tomo_additional_offsety: 0
rtz:
description: flomni rt
deviceClass: csaxs_bec.devices.omny.rt.rt_flomni_ophyd.RtFlomniMotor
deviceConfig:
axis_Id: C
host: mpc2844.psi.ch
port: 2222
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: on_request
############################################################
####################### Cameras ############################
############################################################
cam_flomni_gripper:
description: Camera sample changer
deviceClass: csaxs_bec.devices.omny.webcam_viewer.WebcamViewer
deviceConfig:
url: http://flomnicamserver:5000/video_high
num_rotation_90: 3
transpose: false
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: on_request
cam_flomni_overview:
description: Camera flomni overview
deviceClass: csaxs_bec.devices.omny.webcam_viewer.WebcamViewer
deviceConfig:
url: http://flomnicamserver:5001/video_high
num_rotation_90: 3
transpose: false
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: on_request
cam_xeye:
description: Camera flOMNI Xray eye ID1
deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
deviceConfig:
camera_id: 1
bits_per_pixel: 24
num_rotation_90: 3
transpose: false
force_monochrome: true
m_n_colormode: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: async
cam_ids_rgb:
description: Camera flOMNI Xray eye ID203
deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
deviceConfig:
camera_id: 203
bits_per_pixel: 24
num_rotation_90: 3
transpose: false
force_monochrome: true
m_n_colormode: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: async
# ############################################################
# ################### flOMNI temperatures ####################
# ############################################################
flomni_temphum:
description: flOMNI Temperatures and humidity
deviceClass: csaxs_bec.devices.omny.flomni_temp_and_humidity.FlomniTempHum
deviceConfig: {}
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
# ############################################################
# ########## OMNY / flOMNI / LamNI fast shutter ##############
# ############################################################
omnyfsh:
description: omnyfsh connects to read fast shutter at X12 if in that network
deviceClass: csaxs_bec.devices.omny.shutter.OMNYFastEpicsShutter
deviceConfig: {}
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
############################################################
#################### GUI Signals ###########################
############################################################
omny_xray_gui:
description: Gui Epics signals
deviceClass: csaxs_bec.devices.omny.xray_epics_gui.OMNYXRayEpicsGUI
deviceConfig: {}
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: on_request
calculated_signal:
description: Calculated signal from alignment for fit
deviceClass: ophyd_devices.ComputedSignal
deviceConfig:
compute_method: "def just_rand():\n return 42"
enabled: true
readOnly: false
readoutPriority: baseline

View File

@@ -1,215 +0,0 @@
idgap:
description: 'Motor to control the IDGap of X12SA'
deviceClass: ophyd_devices.devices.undulator.UndulatorGap
deviceConfig:
prefix: 'X12SA-UIND:'
onFailure: raise # Consider changing to buffer
enabled: true
readoutPriority: baseline
readOnly: false # put to false if you like to move it
softwareTrigger: false
xbpm1x:
description: 'X-ray BPM1 in frontend translation x'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X12SA-FE-XBPM1:TRX'
onFailure: raise
enabled: true
readoutPriority: baseline
readOnly: false
softwareTrigger: false
deviceTags:
- cSAXS
- frontend
xbpm1y:
description: 'X-ray BPM1 in frontend translation y'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X12SA-FE-XBPM1:TRY'
onFailure: raise
enabled: true
readoutPriority: baseline
readOnly: false
softwareTrigger: false
deviceTags:
- cSAXS
- frontend
sl1xr:
description: 'slit 1 (frontend) x ring'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X12SA-FE-SL1:TRXR'
onFailure: raise
enabled: true
readoutPriority: baseline
readOnly: false
softwareTrigger: false
deviceTags:
- cSAXS
- frontend
sl1xw:
description: 'slit 1 (frontend) x wall'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X12SA-FE-SL1:TRXW'
onFailure: raise
enabled: true
readoutPriority: baseline
readOnly: false
softwareTrigger: false
deviceTags:
- cSAXS
- frontend
sl1yb:
description: 'slit 1 (frontend) y bottom'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X12SA-FE-SL1:TRYB'
onFailure: raise
enabled: true
readoutPriority: baseline
readOnly: false
softwareTrigger: false
deviceTags:
- cSAXS
- frontend
sl1yt:
description: 'slit 1 (frontend) y top'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X12SA-FE-SL1:TRYT'
onFailure: raise
enabled: true
readoutPriority: baseline
readOnly: false
softwareTrigger: false
deviceTags:
- cSAXS
- frontend
sl1xc:
description: 'slit 1 (frontend) x center'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X12SA-FE-SL1:CENTERX'
onFailure: raise
enabled: true
readoutPriority: baseline
readOnly: false
softwareTrigger: false
deviceTags:
- cSAXS
- frontend
sl1xs:
description: 'slit 1 (frontend) x size'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X12SA-FE-SL1:SIZEX'
onFailure: raise
enabled: true
readoutPriority: baseline
readOnly: false
softwareTrigger: false
deviceTags:
- cSAXS
- frontend
sl1yc:
description: 'slit 1 (frontend) y center'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X12SA-FE-SL1:CENTERY'
onFailure: raise
enabled: true
readoutPriority: baseline
readOnly: false
softwareTrigger: false
deviceTags:
- cSAXS
- frontend
sl1ys:
description: 'slit 1 (frontend) y size'
deviceClass: ophyd.EpicsMotor
deviceConfig:
prefix: 'X12SA-FE-SL1:SIZEY'
onFailure: raise
enabled: true
readoutPriority: baseline
readOnly: false
softwareTrigger: false
deviceTags:
- cSAXS
- frontend
#####################################
#### XBPM ###########################
#####################################
# Note: The following device may not be relevant anymore
# and can be fully replaced by the combined device "xbpm1", see below
xbpm1c1:
description: 'XBPM1 (frontend) current 1'
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: 'X12SA-FE-XBPM1:Current1:MeanValue_RBV'
onFailure: raise
enabled: true
readoutPriority: monitored
readOnly: true
softwareTrigger: false
xbpm1c2:
description: 'XBPM1 (frontend) current 2'
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: 'X12SA-FE-XBPM1:Current2:MeanValue_RBV'
onFailure: raise
enabled: true
readoutPriority: monitored
readOnly: true
softwareTrigger: false
xbpm1c3:
description: 'XBPM1 (frontend) current 3'
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: 'X12SA-FE-XBPM1:Current3:MeanValue_RBV'
onFailure: raise
enabled: true
readoutPriority: monitored
readOnly: true
softwareTrigger: false
xbpm1c4:
description: 'XBPM1 (frontend) current 4'
deviceClass: ophyd.EpicsSignalRO
deviceConfig:
read_pv: 'X12SA-FE-XBPM1:Current4:MeanValue_RBV'
onFailure: raise
enabled: true
readoutPriority: monitored
readOnly: true
softwareTrigger: false
############################################
######### End of xbpm sub devices ##########
############################################
xbpm1:
description: 'XBPM1 (frontend)'
deviceClass: csaxs_bec.devices.epics.xbpms.BPMDevice
deviceConfig:
prefix: 'X12SA-FE-XBPM1'
onFailure: raise
enabled: true
readoutPriority: monitored
readOnly: true
softwareTrigger: false

View File

@@ -1,261 +0,0 @@
############################################################
#################### LamNI Galil motors ####################
############################################################
leyex:
description: Xray eye X
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: G
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8081
sign: -1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 14.117
leyey:
description: Xray eye Y
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: H
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8081
sign: -1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 48.069
out: 0.5
loptx:
description: Optics X
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: E
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8081
sign: 1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: -0.244
out: -0.699
lopty:
description: Optics Y
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: F
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8081
sign: 1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 3.724
out: 3.53
loptz:
description: Optics Z
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: D
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8081
sign: -1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
lsamrot:
description: Sample rotation
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: C
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8081
sign: 1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
lsamx:
description: Sample coarse X
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: A
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8081
sign: -1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
center: 8.768
lsamy:
description: Sample coarse Y
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: B
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8081
sign: 1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
center: 10.041
############################################################
################ LamNI Smaract motors ######################
############################################################
losax:
description: OSA X
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: A
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8085
sign: -1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: -1.442
losay:
description: OSA Y
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: B
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8085
sign: -1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: -0.171
out: 3.8
losaz:
description: OSA Z
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: C
host: mpc2680.psi.ch
limits:
- 0
- 0
port: 8085
sign: 1
deviceTags:
- lamni
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: -1
out: -3
############################################################
#################### flOMNI RT motors ######################
############################################################
rtx:
deviceClass: csaxs_bec.devices.omny.rt.rt_lamni_ophyd.RtLamniMotor
deviceConfig:
axis_Id: A
device_access: true
host: mpc2680.psi.ch
labels: rtx
limits:
- 0
- 0
port: 3333
sign: 1
deviceTags:
- lamni
readoutPriority: baseline
enabled: true
readOnly: False
rty:
deviceClass: csaxs_bec.devices.omny.rt.rt_lamni_ophyd.RtLamniMotor
deviceConfig:
axis_Id: B
device_access: true
host: mpc2680.psi.ch
labels: rty
limits:
- 0
- 0
port: 3333
sign: 1
deviceTags:
- lamni
readoutPriority: baseline
enabled: true
readOnly: False

View File

@@ -1,38 +0,0 @@
############################################################
#################### npoint motors #########################
############################################################
npx:
description: nPoint x axis on the big npoint controller
deviceClass: csaxs_bec.devices.npoint.npoint.NPointAxis
deviceConfig:
axis_Id: A
host: "nPoint000003.psi.ch"
limits:
- -50
- 50
port: 23
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
deviceTags:
- npoint
npy:
description: nPoint y axis on the big npoint controller
deviceClass: csaxs_bec.devices.npoint.npoint.NPointAxis
deviceConfig:
axis_Id: B
host: "nPoint000003.psi.ch"
limits:
- -50
- 50
port: 23
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
deviceTags:
- npoint

View File

@@ -1,610 +0,0 @@
# ############################################################
# #################### IDS Camera ######################
# ############################################################
cam200:
description: Camera200
deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
deviceConfig:
camera_ID: 200
bits_per_pixel: 24
channels: 3
m_n_colormode: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: async
cam201:
description: Camera201
deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
deviceConfig:
camera_ID: 201
bits_per_pixel: 24
channels: 3
m_n_colormode: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: async
cam202:
description: Camera202
deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
deviceConfig:
camera_ID: 202
bits_per_pixel: 24
channels: 3
m_n_colormode: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: async
cam203:
description: Camera203
deviceClass: csaxs_bec.devices.ids_cameras.ids_camera.IDSCamera
deviceConfig:
camera_ID: 203
bits_per_pixel: 24
channels: 3
m_n_colormode: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: async
############################################################
#################### OMNY RT motors ########################
############################################################
rtx:
description: OMNY rt
deviceClass: csaxs_bec.devices.omny.rt.rt_omny_ophyd.RtOMNYMotor
deviceConfig:
axis_Id: A
host: mpc3217.psi.ch
port: 3333
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: on_request
userParameter:
low_signal: 8500
min_signal: 8000
rty:
description: OMNY rt
deviceClass: csaxs_bec.devices.omny.rt.rt_omny_ophyd.RtOMNYMotor
deviceConfig:
axis_Id: B
host: mpc3217.psi.ch
port: 3333
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: on_request
userParameter:
tomo_additional_offsety: 0
rtz:
description: OMNY rt
deviceClass: csaxs_bec.devices.omny.rt.rt_omny_ophyd.RtOMNYMotor
deviceConfig:
axis_Id: C
host: mpc3217.psi.ch
port: 3333
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: on_request
# ############################################################
# ##################### OMNY samples #########################
# ############################################################
omny_samples:
description: OMNYSampleStorage
deviceClass: csaxs_bec.devices.omny.omny_sample_storage.OMNYSampleStorage
deviceConfig: {}
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
# ############################################################
# ##################### OMNY vacuum ##########################
# ############################################################
# omny_vcs:
# description: OMNYVCS
# deviceClass: csaxs_bec.devices.omny.omny_vcs.OMNYVCS
# deviceConfig: {}
# enabled: true
# onFailure: buffer
# readOnly: false
# readoutPriority: baseline
# ############################################################
# ##################### OMNY dewar ###########################
# ############################################################
omny_dewar:
description: OMNY Dewar Information
deviceClass: csaxs_bec.devices.omny.omny_dewar.OMNYDewar
deviceConfig: {}
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
# ############################################################
# ##################### OMNY temperatures ####################
# ############################################################
omny_temperatures:
description: OMNY Temperatures and pressures
deviceClass: csaxs_bec.devices.omny.omny_temperatures.OMNYTemperatures
deviceConfig: {}
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
############################################################
##################### OMNY Galil motors ####################
############################################################
ofzpx:
description: FZP X
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: A
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8081
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: -0.4317
ofzpy:
description: FZP Y
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: B
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8081
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 0.7944
out: 0.6377
ofzpz:
description: FZP Z
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: C
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8081
sign: -1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 0
otransx:
description: Transfer X
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: D
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8081
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 0
otransy:
description: Transfer Y
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: E
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8081
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
up_position: -1.2
gripper_sensorvoltagetarget: -2.30
otransz:
description: Transfer Z
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: F
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8081
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 0
osamx:
description: Sample X
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: A
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8082
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: -0.1
osamz:
description: Sample Z
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: B
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8082
sign: 1
deviceTags:
- omny
enabled: false
onFailure: buffer
readOnly: true
readoutPriority: baseline
userParameter:
in: 0
oosay:
description: OSA Y
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: C
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8082
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
near_field_in: 0.531
far_field_in: 0.4122
oosax:
description: OSA X
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: D
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8082
sign: -1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
near_field_in: 3.2044
far_field_in: 3.022
oosaz:
description: OSA Z
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: E
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8082
sign: -1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
near_field_in: -0.4452
far_field_in: 6.5
oparkz:
description: OSA Y
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: F
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8082
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 0
oshuttleopen:
description: Shuttle opener
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: G
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8082
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: true
readoutPriority: baseline
userParameter:
in: 0
oshuttlealign:
description: Shuttle aligner
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: H
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8082
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: true
readoutPriority: baseline
userParameter:
in: 0
osamy:
description: Sample Y
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: A
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8083
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 0
otracky:
description: Laser Tracker Y
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: B
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8083
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
start_pos: -4.3431
osamroy:
description: Sample rotation
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: C
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8083
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
in: 0
otrackz:
description: Laser Tracker Z
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: E
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8083
sign: -1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
start_pos: -0.6948
oeyex:
description: Xray eye X
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: F
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8083
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
xray_in: -45.7394
oeyez:
description: Xray eye Z
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: G
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8083
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
xray_in: -2
oeyey:
description: Xray eye Y
deviceClass: csaxs_bec.devices.omny.galil.ogalil_ophyd.OMNYGalilMotor
deviceConfig:
axis_Id: H
host: mpc3217.psi.ch
limits:
- 0
- 0
port: 8083
sign: 1
deviceTags:
- omny
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
xray_in: 0.0229
############################################################
#################### flOMNI Smaract motors #################
############################################################
ocsx:
description: Central Stop X
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: B
host: mpc3217.psi.ch
limits:
- -2
- 2
port: 3334
sign: -1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
nothing: 0
ocsy:
description: Central Stop Y
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: A
host: mpc3217.psi.ch
limits:
- -2
- 2
port: 3334
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
nothing: 0
oshield:
description: Thermal Shield Sample Stage
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: C
host: mpc3217.psi.ch
limits:
- -14.5
- 15.8
port: 3334
sign: 1
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
userParameter:
nothing: 0

Some files were not shown because too many files have changed in this diff Show More