Compare commits

...

44 Commits

Author SHA1 Message Date
ca40a8eb3d fix: added missing init file to omny subdir 2024-05-23 16:13:11 +02:00
f476b210f1 docs: added csaxs docs templates 2024-05-23 16:03:11 +02:00
ci_update_bot
fad8b516a6 docs: Update device list 2024-05-23 12:28:05 +00:00
a9ff092a66 fix: commented outdated epics devices 2024-05-23 14:26:05 +02:00
37d80e9a7b fix: added backward compatible fix 2024-05-22 11:52:32 +02:00
ed335dc308 fix: renamed move_and_wait 2024-05-22 11:46:02 +02:00
78d2dd2436 fix: fixed scan_progress after renaming in bec 2024-05-21 12:51:05 +02:00
ci_update_bot
bf2e6baba5 docs: Update device list 2024-05-17 15:45:17 +00:00
fcf9ee4545 refactor: restructured device module 2024-05-17 17:40:26 +02:00
ci_update_bot
53a200928f docs: Update device list 2024-05-16 17:57:58 +00:00
ci_update_bot
1572dd4484 docs: Update device list 2024-05-16 17:55:57 +00:00
ci_update_bot
f72be7ab70 docs: Update device list 2024-05-16 17:53:55 +00:00
ci_update_bot
8aee3e3c0d docs: Update device list 2024-05-16 17:51:51 +00:00
ci_update_bot
6a517d3ac5 docs: Update device list 2024-05-16 17:49:48 +00:00
ci_update_bot
b6b2850853 docs: Update device list 2024-05-16 17:47:44 +00:00
ci_update_bot
5841a56255 docs: Update device list 2024-05-16 17:45:47 +00:00
ci_update_bot
333b4f1dd2 docs: Update device list 2024-05-16 17:43:45 +00:00
ci_update_bot
97e09cf622 docs: Update device list 2024-05-16 17:41:44 +00:00
ci_update_bot
174c0689bc docs: Update device list 2024-05-16 15:39:19 +00:00
ci_update_bot
a3899bf7a5 docs: Update device list 2024-05-16 15:35:03 +00:00
ci_update_bot
d4c12a3c9c docs: Update device list 2024-05-16 15:33:00 +00:00
ci_update_bot
8f0ed4e250 docs: Update device list 2024-05-16 15:29:28 +00:00
ci_update_bot
0359f1f431 docs: Update device list 2024-05-16 15:27:23 +00:00
ci_update_bot
b45070332d docs: Update device list 2024-05-16 15:25:21 +00:00
ci_update_bot
35e4ea0916 docs: Update device list 2024-05-16 15:23:14 +00:00
ae86fbd329 fix: fixed lgalil init 2024-05-16 16:15:07 +02:00
a195be64e7 test: support for repeated runs 2024-05-16 15:38:41 +02:00
e980bf2ee6 fix: fixed import 2024-05-16 15:07:53 +02:00
ce876f58d6 fix: fixed config after refactoring 2024-05-16 15:07:53 +02:00
Holler Mirko
b0a1c32b47 conig_files 2024-05-16 15:07:53 +02:00
11ed6e112f test: ensure to reset the controller instances 2024-05-16 15:07:53 +02:00
8a24692e82 fix: fixed import after refactoring 2024-05-16 15:07:53 +02:00
1d266c5da9 refactor: added galil base class and moved lamni-specific galil components to subclass 2024-05-16 15:07:53 +02:00
7d93154761 refactor: renamed rt_lamni module to rt 2024-05-16 15:07:53 +02:00
c85c6ec5ab refactor: cleanup for rt 2024-05-16 15:07:53 +02:00
Holler Mirko
d567e49b53 fixes at beamline 2024-05-16 15:07:53 +02:00
73ce61dd96 refactor: merged controller methods 2024-05-16 15:07:53 +02:00
8410b3ceec fix: disabled detector_table_theta class 2024-05-16 13:19:37 +02:00
477567e61a fix: fixed bec_lib imports 2024-05-15 18:41:17 +02:00
7da1bddef8 fix: apply black and isort 2024-05-15 14:47:48 +02:00
5007b182ca fix: change imports according to latest bec_lib 2024-05-15 14:47:48 +02:00
063308042d ci: added child pipeline branch 2024-05-13 16:10:21 +02:00
0180e4a3f9 fix: update to new ophyd_devices structure 2024-05-08 17:37:56 +02:00
fc37892ea1 fix: fixed import for new ophyd_devices repo structure 2024-05-08 16:59:04 +02:00
72 changed files with 1881 additions and 2270 deletions

View File

@@ -4,4 +4,5 @@ include:
inputs:
name: "csaxs"
target: "csaxs_bec"
branch: $CHILD_PIPELINE_BRANCH

View File

@@ -3,8 +3,9 @@ from __future__ import annotations
import os
import subprocess
from bec_lib import MessageEndpoints, RedisConnector, bec_logger, messages
from bec_lib.redis_connector import MessageObject
from bec_lib import bec_logger, messages
from bec_lib.endpoints import MessageEndpoints
from bec_lib.redis_connector import MessageObject, RedisConnector
logger = bec_logger.logger
@@ -27,7 +28,7 @@ class PilatusConverter:
message (MessageObject): Message object
parent (PilatusConverter): Parent object
"""
msg = messages.MessageReader.loads(message.value)
msg = message.value
print(msg)
if not msg:
return

View File

@@ -22,7 +22,7 @@ class LaMNIInitStagesMixin:
user_input = input("Starting initialization of LamNI stages. OK? [y/n]")
if user_input == "y":
print("staring...")
dev.lsamrot.enabled=True
dev.lsamrot.enabled = True
else:
return
@@ -42,7 +42,7 @@ class LaMNIInitStagesMixin:
return
self.drive_axis_to_limit(dev.lsamrot, "forward")
dev.lsamrot.enabled=False
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
@@ -108,10 +108,10 @@ class LaMNIInitStagesMixin:
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)
# 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

View File

@@ -16,8 +16,7 @@ from typeguard import typechecked
from csaxs_bec.bec_ipython_client.plugins.cSAXS import epics_get, epics_put, fshopen
from .lamni_optics_mixin import LamNIOpticsMixin
from .lamni_optics_mixin import LaMNIInitStagesMixin
from .lamni_optics_mixin import LaMNIInitStagesMixin, LamNIOpticsMixin
logger = bec_logger.logger
bec = builtins.__dict__.get("bec")
@@ -587,6 +586,27 @@ 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")
@@ -949,25 +969,63 @@ class LamNI(LamNIOpticsMixin):
# _tomo_shift_angles (potential global variable)
_tomo_shift_angles = 0
angle_end = start_angle + 360
for angle in np.linspace(
angles = np.linspace(
start_angle + _tomo_shift_angles,
angle_end,
num=int(360 / self.tomo_angle_stepsize) + 1,
endpoint=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
)
# 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
else:
num_repeats = 1
try:
@@ -1007,7 +1065,7 @@ class LamNI(LamNIOpticsMixin):
scans = builtins.__dict__.get("scans")
self._current_special_angles = self.special_angles.copy()
if subtomo_start == 1 and start_angle is None:
if self.tomo_type == 1 and subtomo_start == 1 and start_angle is None:
# pylint: disable=undefined-variable
self.tomo_id = self.add_sample_database(
self.sample_name,

View File

@@ -309,6 +309,7 @@ class FlomniInitStagesMixin:
else:
print("Stopping.")
return
dev.rtx.controller.feedback_disable()
# positions for optics out and 50 mm distance to sample
umv(dev.ftrackz, 4.73, dev.ftracky, 2.5170, dev.foptx, -14.3, dev.fopty, 3.87)

View File

@@ -17,7 +17,7 @@ bpm4i:
softwareTrigger: false
mokev:
description: Monochromator energy in keV
deviceClass: csaxs_bec.devices.epics.devices.specMotors.EnergyKev
deviceClass: csaxs_bec.devices.epics.specMotors.EnergyKev
deviceConfig:
read_pv: X12SA-OP-MO:ROX2
deviceTags:
@@ -29,7 +29,7 @@ mokev:
softwareTrigger: false
mcs:
description: Mcs scalar card for transmission readout
deviceClass: csaxs_bec.devices.epics.devices.MCScSAXS
deviceClass: csaxs_bec.devices.epics.mcs_csaxs.MCScSAXS
deviceConfig:
prefix: 'X12SA-MCS:'
mcs_config:
@@ -43,7 +43,7 @@ mcs:
softwareTrigger: false
eiger9m:
description: Eiger9m HPC area detector 9M
deviceClass: csaxs_bec.devices.epics.devices.Eiger9McSAXS
deviceClass: csaxs_bec.devices.epics.eiger9m_csaxs.Eiger9McSAXS
deviceConfig:
prefix: 'X12SA-ES-EIGER9M:'
deviceTags:
@@ -55,7 +55,7 @@ eiger9m:
softwareTrigger: false
ddg_detectors:
description: DelayGenerator for detector triggering
deviceClass: csaxs_bec.devices.epics.devices.DelayGeneratorcSAXS
deviceClass: csaxs_bec.devices.epics.delay_generator_csaxs.DelayGeneratorcSAXS
deviceConfig:
prefix: 'delaygen:DG1:'
ddg_config:
@@ -82,7 +82,7 @@ ddg_detectors:
softwareTrigger: false
ddg_mcs:
description: DelayGenerator for mcs triggering
deviceClass: csaxs_bec.devices.epics.devices.DelayGeneratorcSAXS
deviceClass: csaxs_bec.devices.epics.delay_generator_csaxs.DelayGeneratorcSAXS
deviceConfig:
prefix: 'delaygen:DG2:'
ddg_config:
@@ -111,7 +111,7 @@ ddg_mcs:
softwareTrigger: false
ddg_fsh:
description: DelayGenerator for fast shutter control
deviceClass: csaxs_bec.devices.epics.devices.DelayGeneratorcSAXS
deviceClass: csaxs_bec.devices.epics.delay_generator_csaxs.DelayGeneratorcSAXS
deviceConfig:
prefix: 'delaygen:DG3:'
ddg_config:
@@ -138,7 +138,7 @@ ddg_fsh:
softwareTrigger: false
falcon:
description: Falcon detector x-ray fluoresence
deviceClass: csaxs_bec.devices.epics.devices.FalconcSAXS
deviceClass: csaxs_bec.devices.epics.falcon_csaxs.FalconcSAXS
deviceConfig:
prefix: 'X12SA-SITORO:'
deviceTags:
@@ -150,7 +150,7 @@ falcon:
softwareTrigger: false
pilatus_2:
description: Pilatus2 HPC area detector 300k
deviceClass: csaxs_bec.devices.epics.devices.PilatuscSAXS
deviceClass: csaxs_bec.devices.epics.pilatus_csaxs.PilatuscSAXS
deviceConfig:
prefix: 'X12SA-ES-PILATUS300K:'
deviceTags:
@@ -162,7 +162,7 @@ pilatus_2:
softwareTrigger: false
samx:
description: SGalil motor stage
deviceClass: csaxs_bec.devices.galil.SGalilMotor
deviceClass: csaxs_bec.devices.omny.galil.SGalilMotor
deviceConfig:
axis_Id: "E"
host: '129.129.122.26'
@@ -180,7 +180,7 @@ samx:
softwareTrigger: false
samy:
description: SGalil motor stage
deviceClass: csaxs_bec.devices.galil.SGalilMotor
deviceClass: csaxs_bec.devices.omny.galil.SGalilMotor
deviceConfig:
axis_Id: "C"
host: '129.129.122.26'
@@ -198,7 +198,7 @@ samy:
softwareTrigger: false
micfoc:
description: Focusing motor of Microscope stage
deviceClass: ophyd_devices.epics.devices.EpicsMotorEx
deviceClass: ophyd_devices.devices.EpicsMotorEx
deviceConfig:
prefix: X12SA-ES2-ES06
motor_resolution: 0.00125
@@ -216,7 +216,7 @@ micfoc:
softwareTrigger: false
owis_samx:
description: Owis motor stage samx
deviceClass: ophyd_devices.epics.devices.EpicsMotorEx
deviceClass: ophyd_devices.devices.EpicsMotorEx
deviceConfig:
prefix: X12SA-ES2-ES01
motor_resolution: 0.00125
@@ -234,7 +234,7 @@ owis_samx:
softwareTrigger: false
owis_samy:
description: Owis motor stage samx
deviceClass: ophyd_devices.epics.devices.EpicsMotorEx
deviceClass: ophyd_devices.devices.EpicsMotorEx
deviceConfig:
prefix: X12SA-ES2-ES02
motor_resolution: 0.00125
@@ -252,7 +252,7 @@ owis_samy:
softwareTrigger: false
rotx:
description: Rotation stage rotx
deviceClass: ophyd_devices.epics.devices.EpicsMotorEx
deviceClass: ophyd_devices.devices.EpicsMotorEx
deviceConfig:
prefix: X12SA-ES2-ES05
motor_resolution: 0.0025
@@ -273,7 +273,7 @@ rotx:
softwareTrigger: false
roty:
description: Rotation stage rotx
deviceClass: ophyd_devices.epics.devices.EpicsMotorEx
deviceClass: ophyd_devices.devices.EpicsMotorEx
deviceConfig:
prefix: X12SA-ES2-ES04
motor_resolution: 0.0025

View File

@@ -3,7 +3,7 @@
############################################################
leyex:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.galil_ophyd.GalilMotor
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: G
host: mpc2680.psi.ch
@@ -22,7 +22,7 @@ leyex:
in: 14.117
leyey:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.galil_ophyd.GalilMotor
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: H
host: mpc2680.psi.ch
@@ -42,7 +42,7 @@ leyey:
out: 0.5
loptx:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.galil_ophyd.GalilMotor
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: E
host: mpc2680.psi.ch
@@ -62,7 +62,7 @@ loptx:
out: -0.699
lopty:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.galil_ophyd.GalilMotor
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: F
host: mpc2680.psi.ch
@@ -82,7 +82,7 @@ lopty:
out: 3.53
loptz:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.galil_ophyd.GalilMotor
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: D
host: mpc2680.psi.ch
@@ -99,7 +99,7 @@ loptz:
readoutPriority: baseline
lsamrot:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.galil_ophyd.GalilMotor
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: C
host: mpc2680.psi.ch
@@ -116,7 +116,7 @@ lsamrot:
readoutPriority: baseline
lsamx:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.galil_ophyd.GalilMotor
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: A
host: mpc2680.psi.ch
@@ -135,7 +135,7 @@ lsamx:
center: 8.768
lsamy:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.galil_ophyd.GalilMotor
deviceClass: csaxs_bec.devices.omny.galil.lgalil_ophyd.LamniGalilMotor
deviceConfig:
axis_Id: B
host: mpc2680.psi.ch
@@ -162,7 +162,7 @@ lsamy:
############################################################
rtx:
deviceClass: csaxs_bec.devices.rt_lamni.rt_lamni_ophyd.RtLamniMotor
deviceClass: csaxs_bec.devices.omny.rt.rt_lamni_ophyd.RtLamniMotor
deviceConfig:
axis_Id: A
device_access: true
@@ -179,7 +179,7 @@ rtx:
enabled: true
readOnly: False
rty:
deviceClass: csaxs_bec.devices.rt_lamni.rt_lamni_ophyd.RtLamniMotor
deviceClass: csaxs_bec.devices.omny.rt.rt_lamni_ophyd.RtLamniMotor
deviceConfig:
axis_Id: B
device_access: true
@@ -293,7 +293,7 @@ losaz:
eiger1p5m:
description: Eiger 1.5M in vacuum detector, in-house developed, PSI
deviceClass: csaxs_bec.devices.eiger1p5m_csaxs.eiger1p5m.Eiger1p5MDetector
deviceClass: csaxs_bec.devices.omny.eiger1p5m.Eiger1p5MDetector
deviceConfig:
device_access: true
deviceTags:
@@ -504,7 +504,7 @@ sls_filling_pattern:
readOnly: True
sls_info:
readoutPriority: on_request
deviceClass: ophyd_devices.sls_devices.sls_devices.SLSInfo
deviceClass: ophyd_devices.devices.sls_devices.SLSInfo
deviceConfig:
deviceTags:
- SLS status
@@ -537,7 +537,7 @@ sls_machine_status:
readOnly: True
sls_operator:
readoutPriority: on_request
deviceClass: ophyd_devices.sls_devices.sls_devices.SLSOperatorMessages
deviceClass: ophyd_devices.devices.sls_devices.SLSOperatorMessages
deviceConfig:
auto_monitor: true
read_pv: ARIDI-BPM:OFB-MODE
@@ -802,7 +802,7 @@ bm5try:
softwareTrigger: false
bpm1:
description: 'XBPM 1: Somewhere around mono (VME)'
deviceClass: csaxs_bec.devices.epics.devices.XbpmBase.XbpmCsaxsOp
deviceClass: csaxs_bec.devices.epics.XbpmBase.XbpmCsaxsOp
deviceConfig:
prefix: 'X12SA-OP-BPM2:'
deviceTags:
@@ -824,7 +824,7 @@ bpm1i:
softwareTrigger: false
bpm2:
description: 'XBPM 2: Somewhere around mono (VME)'
deviceClass: csaxs_bec.devices.epics.devices.XbpmBase.XbpmCsaxsOp
deviceClass: csaxs_bec.devices.epics.XbpmBase.XbpmCsaxsOp
deviceConfig:
prefix: 'X12SA-OP-BPM2:'
deviceTags:
@@ -1187,7 +1187,7 @@ dtpush:
softwareTrigger: false
dtth:
description: Detector tower tilt rotation
deviceClass: csaxs_bec.devices.epics.devices.specMotors.PmDetectorRotation
deviceClass: csaxs_bec.devices.epics.specMotors.PmDetectorRotation
deviceConfig:
prefix: X12SA-ES1-DETT:ROX1
deviceTags:
@@ -1462,7 +1462,7 @@ fttrz:
softwareTrigger: false
idgap:
description: Undulator gap size [mm]
deviceClass: csaxs_bec.devices.epics.devices.InsertionDevice
deviceClass: csaxs_bec.devices.epics.InsertionDevice.InsertionDevice
deviceConfig:
prefix: X12SA-ID
deviceTags:
@@ -1561,7 +1561,7 @@ mitry3:
softwareTrigger: false
mobd:
description: Monochromator bender virtual motor
deviceClass: csaxs_bec.devices.epics.devices.specMotors.PmMonoBender
deviceClass: csaxs_bec.devices.epics.specMotors.PmMonoBender
deviceConfig:
prefix: 'X12SA-OP-MO:'
deviceTags:
@@ -1616,7 +1616,7 @@ mobddi:
softwareTrigger: false
mokev:
description: Monochromator energy in keV
deviceClass: csaxs_bec.devices.epics.devices.specMotors.EnergyKev
deviceClass: csaxs_bec.devices.epics.specMotors.EnergyKev
deviceConfig:
read_pv: X12SA-OP-MO:ROX2
deviceTags:
@@ -1671,7 +1671,7 @@ moroll2:
softwareTrigger: false
moth1:
description: Monochromator Theta 1
deviceClass: csaxs_bec.devices.epics.devices.specMotors.MonoTheta1
deviceClass: csaxs_bec.devices.epics.specMotors.MonoTheta1
deviceConfig:
read_pv: X12SA-OP-MO:ROX1
deviceTags:
@@ -1693,7 +1693,7 @@ moth1e:
softwareTrigger: false
moth2:
description: Monochromator Theta 2
deviceClass: csaxs_bec.devices.epics.devices.specMotors.MonoTheta2
deviceClass: csaxs_bec.devices.epics.specMotors.MonoTheta2
deviceConfig:
read_pv: X12SA-OP-MO:ROX2
deviceTags:
@@ -1812,17 +1812,17 @@ sec:
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sl0h:
description: FrontEnd slit virtual movement
deviceClass: ophyd_devices.epics.devices.SlitH
deviceConfig:
prefix: 'X12SA-FE-SH1:'
deviceTags:
- epicsDevice
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
# sl0h:
# description: FrontEnd slit virtual movement
# deviceClass: ophyd_devices.devices.SlitH
# deviceConfig:
# prefix: 'X12SA-FE-SH1:'
# deviceTags:
# - epicsDevice
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
sl0trxi:
description: FrontEnd slit inner blade movement
deviceClass: ophyd.EpicsMotor
@@ -1845,17 +1845,17 @@ sl0trxo:
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sl1h:
description: OpticsHutch slit virtual movement
deviceClass: ophyd_devices.epics.devices.SlitH
deviceConfig:
prefix: 'X12SA-OP-SH1:'
deviceTags:
- epicsDevice
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
# sl1h:
# description: OpticsHutch slit virtual movement
# deviceClass: ophyd_devices.devices.SlitH
# deviceConfig:
# prefix: 'X12SA-OP-SH1:'
# deviceTags:
# - epicsDevice
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
sl1trxi:
description: OpticsHutch slit inner blade movement
deviceClass: ophyd.EpicsMotor
@@ -1900,28 +1900,28 @@ sl1tryt:
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sl1v:
description: OpticsHutch slit virtual movement
deviceClass: ophyd_devices.epics.devices.SlitV
deviceConfig:
prefix: 'X12SA-OP-SV1:'
deviceTags:
- epicsDevice
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sl2h:
description: OpticsHutch slit 2 virtual movement
deviceClass: ophyd_devices.epics.devices.SlitH
deviceConfig:
prefix: 'X12SA-OP-SH2:'
deviceTags:
- epicsDevice
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
# sl1v:
# description: OpticsHutch slit virtual movement
# deviceClass: ophyd_devices.devices.SlitV
# deviceConfig:
# prefix: 'X12SA-OP-SV1:'
# deviceTags:
# - epicsDevice
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# sl2h:
# description: OpticsHutch slit 2 virtual movement
# deviceClass: ophyd_devices.devices.SlitH
# deviceConfig:
# prefix: 'X12SA-OP-SH2:'
# deviceTags:
# - epicsDevice
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
sl2trxi:
description: OpticsHutch slit 2 inner blade movement
deviceClass: ophyd.EpicsMotor
@@ -1966,72 +1966,72 @@ sl2tryt:
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sl2v:
description: OpticsHutch slit 2 virtual movement
deviceClass: ophyd_devices.epics.devices.SlitV
deviceConfig:
prefix: 'X12SA-OP-SV2:'
deviceTags:
- epicsDevice
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
strox:
description: Girder virtual pitch
deviceClass: ophyd_devices.epics.devices.GirderMotorPITCH
deviceConfig:
prefix: X12SA-HG
deviceTags:
- beamlineMotor
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
stroy:
description: Girder virtual yaw
deviceClass: ophyd_devices.epics.devices.GirderMotorYAW
deviceConfig:
prefix: X12SA-HG
deviceTags:
- beamlineMotor
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
stroz:
description: Girder virtual roll
deviceClass: ophyd_devices.epics.devices.GirderMotorROLL
deviceConfig:
prefix: X12SA-HG
deviceTags:
- beamlineMotor
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sttrx:
description: Girder X translation
deviceClass: ophyd_devices.epics.devices.GirderMotorX1
deviceConfig:
prefix: X12SA-HG
deviceTags:
- beamlineMotor
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sttry:
description: Girder Y translation
deviceClass: ophyd_devices.epics.devices.GirderMotorY1
deviceConfig:
prefix: X12SA-HG
deviceTags:
- beamlineMotor
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
# sl2v:
# description: OpticsHutch slit 2 virtual movement
# deviceClass: ophyd_devices.devices.SlitV
# deviceConfig:
# prefix: 'X12SA-OP-SV2:'
# deviceTags:
# - epicsDevice
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# strox:
# description: Girder virtual pitch
# deviceClass: ophyd_devices.devices.GirderMotorPITCH
# deviceConfig:
# prefix: X12SA-HG
# deviceTags:
# - beamlineMotor
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# stroy:
# description: Girder virtual yaw
# deviceClass: ophyd_devices.devices.GirderMotorYAW
# deviceConfig:
# prefix: X12SA-HG
# deviceTags:
# - beamlineMotor
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# stroz:
# description: Girder virtual roll
# deviceClass: ophyd_devices.devices.GirderMotorROLL
# deviceConfig:
# prefix: X12SA-HG
# deviceTags:
# - beamlineMotor
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# sttrx:
# description: Girder X translation
# deviceClass: ophyd_devices.devices.GirderMotorX1
# deviceConfig:
# prefix: X12SA-HG
# deviceTags:
# - beamlineMotor
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# sttry:
# description: Girder Y translation
# deviceClass: ophyd_devices.devices.GirderMotorY1
# deviceConfig:
# prefix: X12SA-HG
# deviceTags:
# - beamlineMotor
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
transd:
description: Transmission diode
deviceClass: ophyd.EpicsSignalRO

View File

@@ -1,6 +1,10 @@
############################################################
#################### flOMNI Galil motors ###################
############################################################
feyex:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
description: Xray eye X
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: D
host: mpc2844.psi.ch
@@ -17,8 +21,8 @@ feyex:
in: -16.267
out: -1
feyey:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
description: Xray eye Y
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: E
host: mpc2844.psi.ch
@@ -34,8 +38,8 @@ feyey:
userParameter:
in: -10.467
fheater:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
description: Heater Y
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: C
host: mpc2844.psi.ch
@@ -48,17 +52,9 @@ fheater:
onFailure: buffer
readOnly: false
readoutPriority: baseline
flomni_samples:
description: phase plate angle
deviceClass: csaxs_bec.devices.epics.devices.flomni_sample_storage.FlomniSampleStorage
deviceConfig: {}
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline
foptx:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
description: Optics X
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: B
host: mpc2844.psi.ch
@@ -74,8 +70,8 @@ foptx:
userParameter:
in: -13.761
fopty:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
description: Optics Y
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: F
host: mpc2844.psi.ch
@@ -92,8 +88,8 @@ fopty:
in: 0.552
out: 0.752
foptz:
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
description: Optics Z
deviceClass: csaxs_bec.devices.omny.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
@@ -108,8 +104,166 @@ foptz:
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
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: phase plate angle
description: OSA X
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: A
@@ -127,7 +281,7 @@ fosax:
in: 9.124
out: 5.3
fosay:
description: phase plate angle
description: OSA Y
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: B
@@ -144,7 +298,7 @@ fosay:
userParameter:
in: 0.367
fosaz:
description: phase plate angle
description: OSA Z
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: C
@@ -161,148 +315,14 @@ fosaz:
userParameter:
in: 8.5
out: 6
fsamroy:
description: phase plate angle
deviceClass: csaxs_bec.devices.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: phase plate angle
deviceClass: csaxs_bec.devices.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: phase plate angle
deviceClass: csaxs_bec.devices.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: phase plate angle
deviceClass: csaxs_bec.devices.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: phase plate angle
deviceClass: csaxs_bec.devices.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: phase plate angle
deviceClass: csaxs_bec.devices.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: phase plate angle
deviceClass: csaxs_bec.devices.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
ftransz:
description: phase plate angle
deviceClass: csaxs_bec.devices.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: phase plate angle
deviceClass: csaxs_bec.devices.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 RT motors ######################
############################################################
rtx:
description: flomni rt
deviceClass: csaxs_bec.devices.rt_lamni.rt_flomni_ophyd.RtFlomniMotor
deviceClass: csaxs_bec.devices.omny.rt.rt_flomni_ophyd.RtFlomniMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
@@ -318,7 +338,7 @@ rtx:
rt_pid_voltage: -0.06219
rty:
description: flomni rt
deviceClass: csaxs_bec.devices.rt_lamni.rt_flomni_ophyd.RtFlomniMotor
deviceClass: csaxs_bec.devices.omny.rt.rt_flomni_ophyd.RtFlomniMotor
deviceConfig:
axis_Id: B
host: mpc2844.psi.ch
@@ -332,7 +352,7 @@ rty:
tomo_additional_offsety: 0
rtz:
description: flomni rt
deviceClass: csaxs_bec.devices.rt_lamni.rt_flomni_ophyd.RtFlomniMotor
deviceClass: csaxs_bec.devices.omny.rt.rt_flomni_ophyd.RtFlomniMotor
deviceConfig:
axis_Id: C
host: mpc2844.psi.ch

View File

@@ -1,340 +0,0 @@
fheater:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: C
host: mpc2844.psi.ch
limits:
- -15
- 0
port: 8082
sign: -1
onFailure: buffer
enabled: true
readOnly: false
feyex:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: D
host: mpc2844.psi.ch
limits:
- -30
- -1
port: 8082
sign: 1
onFailure: buffer
userParameter:
in: -16.267
out: -1
enabled: true
readOnly: false
feyey:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: E
host: mpc2844.psi.ch
limits:
- -1
- -10
port: 8082
sign: 1
onFailure: buffer
userParameter:
in: -10.467
enabled: true
readOnly: false
foptx:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: B
host: mpc2844.psi.ch
limits:
- -17
- -12
port: 8082
sign: 1
onFailure: buffer
userParameter:
in: -13.761
enabled: true
readOnly: false
fopty:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: F
host: mpc2844.psi.ch
limits:
- 0
- 4
port: 8082
sign: 1
onFailure: buffer
userParameter:
in: 0.552
out: 0.752
enabled: true
readOnly: false
foptz:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
limits:
- 0
- 27
port: 8082
sign: 1
onFailure: buffer
userParameter:
in: 23
enabled: true
readOnly: false
fosax:
readoutPriority: baseline
description: phase plate angle
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
onFailure: buffer
userParameter:
in: 9.124
out: 5.3
enabled: true
readOnly: false
fosay:
readoutPriority: baseline
description: phase plate angle
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
onFailure: buffer
userParameter:
in: 0.367
enabled: true
readOnly: false
fosaz:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.smaract.smaract_ophyd.SmaractMotor
deviceConfig:
axis_Id: C
host: mpc2844.psi.ch
limits:
- -6
- -4
port: 3334
sign: 1
onFailure: buffer
userParameter:
in: 8.5
out: 6
enabled: true
readOnly: false
fsamroy:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fupr_ophyd.FuprGalilMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
limits:
- -5
- 365
port: 8084
sign: -1
onFailure: buffer
enabled: true
readOnly: false
fsamx:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: E
host: mpc2844.psi.ch
limits:
- -162
- 0
port: 8081
sign: 1
onFailure: buffer
enabled: true
readOnly: false
userParameter:
in: -1.1
fsamy:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: F
host: mpc2844.psi.ch
limits:
- 2
- 3.1
port: 8081
sign: 1
onFailure: buffer
enabled: true
readOnly: false
userParameter:
in: 2.75
ftracky:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: H
host: mpc2844.psi.ch
limits:
- 2.2
- 2.8
port: 8082
sign: 1
onFailure: buffer
enabled: true
readOnly: false
ftrackz:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: G
host: mpc2844.psi.ch
limits:
- 4.5
- 5.5
port: 8082
sign: 1
onFailure: buffer
enabled: true
readOnly: false
ftransx:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: C
host: mpc2844.psi.ch
limits:
- 0
- 50
port: 8081
sign: 1
onFailure: buffer
enabled: true
readOnly: false
ftransy:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
limits:
- -100
- 0
port: 8081
sign: 1
onFailure: buffer
enabled: true
readOnly: false
ftransz:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: B
host: mpc2844.psi.ch
limits:
- 0
- 145
port: 8081
sign: 1
onFailure: buffer
enabled: true
readOnly: false
ftray:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.galil.fgalil_ophyd.FlomniGalilMotor
deviceConfig:
axis_Id: D
host: mpc2844.psi.ch
limits:
- -200
- 0
port: 8081
sign: -1
onFailure: buffer
enabled: true
readOnly: false
flomni_samples:
readoutPriority: baseline
description: phase plate angle
deviceClass: csaxs_bec.devices.epics.devices.FlomniSampleStorage
deviceConfig:
onFailure: buffer
enabled: true
readOnly: false
rtx:
readoutPriority: on_request
description: flomni rt
deviceClass: csaxs_bec.devices.rt_lamni.rt_flomni_ophyd.RtFlomniMotor
deviceConfig:
axis_Id: A
host: mpc2844.psi.ch
port: 2222
sign: 1
onFailure: buffer
enabled: true
readOnly: false
rty:
readoutPriority: on_request
description: flomni rt
deviceClass: csaxs_bec.devices.rt_lamni.rt_flomni_ophyd.RtFlomniMotor
deviceConfig:
axis_Id: B
host: mpc2844.psi.ch
port: 2222
sign: 1
onFailure: buffer
enabled: true
readOnly: false
rtz:
readoutPriority: on_request
description: flomni rt
deviceClass: csaxs_bec.devices.rt_lamni.rt_flomni_ophyd.RtFlomniMotor
deviceConfig:
axis_Id: C
host: mpc2844.psi.ch
port: 2222
sign: 1
onFailure: buffer
enabled: true
readOnly: false

View File

@@ -0,0 +1,261 @@
############################################################
#################### 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

@@ -0,0 +1,8 @@
omny_samples:
description: OMNYSampleStorage
deviceClass: csaxs_bec.devices.omny.omny_sample_storage.OMNYSampleStorage
deviceConfig: {}
enabled: true
onFailure: buffer
readOnly: false
readoutPriority: baseline

View File

@@ -220,7 +220,7 @@ bm5try:
softwareTrigger: false
bpm1:
description: 'XBPM 1: Somewhere around mono (VME)'
deviceClass: csaxs_bec.devices.epics.devices.XbpmBase.XbpmCsaxsOp
deviceClass: csaxs_bec.devices.epics.XbpmBase.XbpmCsaxsOp
deviceConfig:
prefix: 'X12SA-OP-BPM2:'
deviceTags:
@@ -242,7 +242,7 @@ bpm1i:
softwareTrigger: false
bpm2:
description: 'XBPM 2: Somewhere around mono (VME)'
deviceClass: csaxs_bec.devices.epics.devices.XbpmBase.XbpmCsaxsOp
deviceClass: csaxs_bec.devices.epics.XbpmBase.XbpmCsaxsOp
deviceConfig:
prefix: 'X12SA-OP-BPM2:'
deviceTags:
@@ -605,7 +605,7 @@ dtpush:
softwareTrigger: false
dtth:
description: Detector tower tilt rotation
deviceClass: csaxs_bec.devices.epics.devices.specMotors.PmDetectorRotation
deviceClass: csaxs_bec.devices.epics.specMotors.PmDetectorRotation
deviceConfig:
prefix: X12SA-ES1-DETT:ROX1
deviceTags:
@@ -880,7 +880,7 @@ fttrz:
softwareTrigger: false
idgap:
description: Undulator gap size [mm]
deviceClass: csaxs_bec.devices.epics.devices.InsertionDevice.InsertionDevice
deviceClass: csaxs_bec.devices.epics.InsertionDevice.InsertionDevice
deviceConfig:
prefix: X12SA-ID
deviceTags:
@@ -979,7 +979,7 @@ mitry3:
softwareTrigger: false
mobd:
description: Monochromator bender virtual motor
deviceClass: csaxs_bec.devices.epics.devices.specMotors.PmMonoBender
deviceClass: csaxs_bec.devices.epics.specMotors.PmMonoBender
deviceConfig:
prefix: 'X12SA-OP-MO:'
deviceTags:
@@ -1034,7 +1034,7 @@ mobddi:
softwareTrigger: false
mokev:
description: Monochromator energy in keV
deviceClass: csaxs_bec.devices.epics.devices.specMotors.EnergyKev
deviceClass: csaxs_bec.devices.epics.specMotors.EnergyKev
deviceConfig:
read_pv: X12SA-OP-MO:ROX2
deviceTags:
@@ -1089,7 +1089,7 @@ moroll2:
softwareTrigger: false
moth1:
description: Monochromator Theta 1
deviceClass: csaxs_bec.devices.epics.devices.specMotors.MonoTheta1
deviceClass: csaxs_bec.devices.epics.specMotors.MonoTheta1
deviceConfig:
read_pv: X12SA-OP-MO:ROX1
deviceTags:
@@ -1111,7 +1111,7 @@ moth1e:
softwareTrigger: false
moth2:
description: Monochromator Theta 2
deviceClass: csaxs_bec.devices.epics.devices.specMotors.MonoTheta2
deviceClass: csaxs_bec.devices.epics.specMotors.MonoTheta2
deviceConfig:
read_pv: X12SA-OP-MO:ROX2
deviceTags:
@@ -1230,17 +1230,17 @@ sec:
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sl0h:
description: FrontEnd slit virtual movement
deviceClass: ophyd_devices.epics.devices.SlitH
deviceConfig:
prefix: 'X12SA-FE-SH1:'
deviceTags:
- epicsDevice
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
# sl0h:
# description: FrontEnd slit virtual movement
# deviceClass: ophyd_devices.devices.SlitH
# deviceConfig:
# prefix: 'X12SA-FE-SH1:'
# deviceTags:
# - epicsDevice
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
sl0trxi:
description: FrontEnd slit inner blade movement
deviceClass: ophyd.EpicsMotor
@@ -1263,17 +1263,17 @@ sl0trxo:
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sl1h:
description: OpticsHutch slit virtual movement
deviceClass: ophyd_devices.epics.devices.SlitH
deviceConfig:
prefix: 'X12SA-OP-SH1:'
deviceTags:
- epicsDevice
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
# sl1h:
# description: OpticsHutch slit virtual movement
# deviceClass: ophyd_devices.devices.SlitH
# deviceConfig:
# prefix: 'X12SA-OP-SH1:'
# deviceTags:
# - epicsDevice
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
sl1trxi:
description: OpticsHutch slit inner blade movement
deviceClass: ophyd.EpicsMotor
@@ -1318,28 +1318,28 @@ sl1tryt:
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sl1v:
description: OpticsHutch slit virtual movement
deviceClass: ophyd_devices.epics.devices.SlitV
deviceConfig:
prefix: 'X12SA-OP-SV1:'
deviceTags:
- epicsDevice
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sl2h:
description: OpticsHutch slit 2 virtual movement
deviceClass: ophyd_devices.epics.devices.SlitH
deviceConfig:
prefix: 'X12SA-OP-SH2:'
deviceTags:
- epicsDevice
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
# sl1v:
# description: OpticsHutch slit virtual movement
# deviceClass: ophyd_devices.devices.SlitV
# deviceConfig:
# prefix: 'X12SA-OP-SV1:'
# deviceTags:
# - epicsDevice
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# sl2h:
# description: OpticsHutch slit 2 virtual movement
# deviceClass: ophyd_devices.devices.SlitH
# deviceConfig:
# prefix: 'X12SA-OP-SH2:'
# deviceTags:
# - epicsDevice
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
sl2trxi:
description: OpticsHutch slit 2 inner blade movement
deviceClass: ophyd.EpicsMotor
@@ -1384,72 +1384,72 @@ sl2tryt:
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sl2v:
description: OpticsHutch slit 2 virtual movement
deviceClass: ophyd_devices.epics.devices.SlitV
deviceConfig:
prefix: 'X12SA-OP-SV2:'
deviceTags:
- epicsDevice
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
strox:
description: Girder virtual pitch
deviceClass: ophyd_devices.epics.devices.GirderMotorPITCH
deviceConfig:
prefix: X12SA-HG
deviceTags:
- beamlineMotor
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
stroy:
description: Girder virtual yaw
deviceClass: ophyd_devices.epics.devices.GirderMotorYAW
deviceConfig:
prefix: X12SA-HG
deviceTags:
- beamlineMotor
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
stroz:
description: Girder virtual roll
deviceClass: ophyd_devices.epics.devices.GirderMotorROLL
deviceConfig:
prefix: X12SA-HG
deviceTags:
- beamlineMotor
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sttrx:
description: Girder X translation
deviceClass: ophyd_devices.epics.devices.GirderMotorX1
deviceConfig:
prefix: X12SA-HG
deviceTags:
- beamlineMotor
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
sttry:
description: Girder Y translation
deviceClass: ophyd_devices.epics.devices.GirderMotorY1
deviceConfig:
prefix: X12SA-HG
deviceTags:
- beamlineMotor
enabled: true
onFailure: buffer
readoutPriority: baseline
softwareTrigger: false
# sl2v:
# description: OpticsHutch slit 2 virtual movement
# deviceClass: ophyd_devices.devices.SlitV
# deviceConfig:
# prefix: 'X12SA-OP-SV2:'
# deviceTags:
# - epicsDevice
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# strox:
# description: Girder virtual pitch
# deviceClass: ophyd_devices.devices.GirderMotorPITCH
# deviceConfig:
# prefix: X12SA-HG
# deviceTags:
# - beamlineMotor
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# stroy:
# description: Girder virtual yaw
# deviceClass: ophyd_devices.devices.GirderMotorYAW
# deviceConfig:
# prefix: X12SA-HG
# deviceTags:
# - beamlineMotor
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# stroz:
# description: Girder virtual roll
# deviceClass: ophyd_devices.devices.GirderMotorROLL
# deviceConfig:
# prefix: X12SA-HG
# deviceTags:
# - beamlineMotor
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# sttrx:
# description: Girder X translation
# deviceClass: ophyd_devices.devices.GirderMotorX1
# deviceConfig:
# prefix: X12SA-HG
# deviceTags:
# - beamlineMotor
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
# sttry:
# description: Girder Y translation
# deviceClass: ophyd_devices.devices.GirderMotorY1
# deviceConfig:
# prefix: X12SA-HG
# deviceTags:
# - beamlineMotor
# enabled: true
# onFailure: buffer
# readoutPriority: baseline
# softwareTrigger: false
transd:
description: Transmission diode
deviceClass: ophyd.EpicsSignalRO

View File

@@ -0,0 +1,37 @@
// This file was autogenerated. Do not edit it manually.
## Device List
### csaxs_bec
| Device | Documentation | Module |
| :----- | :------------- | :------ |
| InsertionDevice | Python wrapper for the CSAXS insertion device control<br><br> This wrapper provides a positioner interface for the ID control.<br> is completely custom XBPM with templates directly in the<br> VME repo. Thus it needs a custom ophyd template as well...<br><br> WARN: The x and y are not updated by the IOC<br> | [csaxs_bec.devices.epics.InsertionDevice](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/InsertionDevice.py) |
| XbpmBase | Python wrapper for X-ray Beam Position Monitors<br><br> XBPM's consist of a metal-coated diamond window that ejects<br> photoelectrons from the incoming X-ray beam. These electons<br> are collected and their current is measured. Effectively<br> they act as four quadrant photodiodes and are used as BPMs<br> at the undulator beamlines of SLS.<br><br> Note: EPICS provided signals are read only, but the user can<br> change the beam position offset.<br> | [csaxs_bec.devices.epics.XbpmBase](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/XbpmBase.py) |
| XbpmCsaxsOp | Python wrapper for custom XBPMs in the cSAXS optics hutch<br><br> This is completely custom XBPM with templates directly in the<br> VME repo. Thus it needs a custom ophyd template as well...<br><br> WARN: The x and y are not updated by the IOC<br> | [csaxs_bec.devices.epics.XbpmBase](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/XbpmBase.py) |
| XbpmSim | Python wrapper for simulated X-ray Beam Position Monitors<br><br> XBPM's consist of a metal-coated diamond window that ejects<br> photoelectrons from the incoming X-ray beam. These electons<br> are collected and their current is measured. Effectively<br> they act as four quadrant photodiodes and are used as BPMs<br> at the undulator beamlines of SLS.<br><br> Note: EPICS provided signals are read only, but the user can<br> change the beam position offset.<br><br> This simulation device extends the basic proxy with a script that<br> fills signals with quasi-randomized values.<br> | [csaxs_bec.devices.epics.XbpmBase](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/XbpmBase.py) |
| DelayGeneratorcSAXS | <br> DG645 delay generator at cSAXS (multiple can be in use depending on the setup)<br><br> Default values for setting up DDG.<br> Note: checks of set calues are not (only partially) included, check manual for details on possible settings.<br> https://www.thinksrs.com/downloads/pdfs/manuals/DG645m.pdf<br><br> - delay_burst : (float >=0) Delay between trigger and first pulse in burst mode<br> - delta_width : (float >= 0) Add width to fast shutter signal to make sure its open during acquisition<br> - additional_triggers : (int) add additional triggers to burst mode (mcs card needs +1 triggers per line)<br> - polarity : (list of 0/1) polarity for different channels<br> - amplitude : (float) amplitude voltage of TTLs<br> - offset : (float) offset for ampltitude<br> - thres_trig_level : (float) threshold of trigger amplitude<br><br> Custom signals for logic in different DDGs during scans (for custom_prepare.prepare_ddg):<br><br> - set_high_on_exposure : (bool): if True, then TTL signal should go high during the full acquisition time of a scan.<br> # TODO trigger_width and fixed_ttl could be combined into single list.<br> - fixed_ttl_width : (list of either 1 or 0), one for each channel.<br> - trigger_width : (float) if fixed_ttl_width is True, then the width of the TTL pulse is set to this value.<br> - set_trigger_source : (TriggerSource) specifies the default trigger source for the DDG.<br> - premove_trigger : (bool) if True, then a trigger should be executed before the scan starts (to be implemented in on_pre_scan).<br> - set_high_on_stage : (bool) if True, then TTL signal should go high already on stage.<br> | [csaxs_bec.devices.epics.delay_generator_csaxs](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/delay_generator_csaxs.py) |
| Eiger9McSAXS | <br> Eiger9M detector for CSAXS<br><br> Parent class: PSIDetectorBase<br><br> class attributes:<br> custom_prepare_cls (FalconSetup) : Custom detector setup class for cSAXS,<br> inherits from CustomDetectorMixin<br> PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector<br> Various EpicsPVs for controlling the detector<br> | [csaxs_bec.devices.epics.eiger9m_csaxs](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/eiger9m_csaxs.py) |
| SLSDetectorCam | SLS Detector Camera - Pilatus<br><br> Base class to map EPICS PVs to ophyd signals.<br> | [csaxs_bec.devices.epics.pilatus_csaxs](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/pilatus_csaxs.py) |
| EpicsDXPFalcon | <br> DXP parameters for Falcon detector<br><br> Base class to map EPICS PVs from DXP parameters to ophyd signals.<br> | [csaxs_bec.devices.epics.falcon_csaxs](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/falcon_csaxs.py) |
| FalconHDF5Plugins | <br> HDF5 parameters for Falcon detector<br><br> Base class to map EPICS PVs from HDF5 Plugin to ophyd signals.<br> | [csaxs_bec.devices.epics.falcon_csaxs](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/falcon_csaxs.py) |
| FalconcSAXS | <br> Falcon Sitoro detector for CSAXS<br><br> Parent class: PSIDetectorBase<br><br> class attributes:<br> custom_prepare_cls (FalconSetup) : Custom detector setup class for cSAXS,<br> inherits from CustomDetectorMixin<br> PSIDetectorBase.set_min_readout (float) : Minimum readout time for the detector<br> dxp (EpicsDXPFalcon) : DXP parameters for Falcon detector<br> mca (EpicsMCARecord) : MCA parameters for Falcon detector<br> hdf5 (FalconHDF5Plugins) : HDF5 parameters for Falcon detector<br> MIN_READOUT (float) : Minimum readout time for the detector<br> | [csaxs_bec.devices.epics.falcon_csaxs](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/falcon_csaxs.py) |
| MCScSAXS | MCS card for cSAXS for implementation at cSAXS beamline | [csaxs_bec.devices.epics.mcs_csaxs](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/mcs_csaxs.py) |
| SIS38XX | SIS38XX card for access to EPICs PVs at cSAXS beamline | [csaxs_bec.devices.epics.mcs_csaxs](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/mcs_csaxs.py) |
| PilatuscSAXS | Pilatus_2 300k detector for CSAXS<br><br> Parent class: PSIDetectorBase<br><br> class attributes:<br> custom_prepare_cls (Eiger9MSetup) : Custom detector setup class for cSAXS,<br> inherits from CustomDetectorMixin<br> cam (SLSDetectorCam) : Detector camera<br> MIN_READOUT (float) : Minimum readout time for the detector<br><br> | [csaxs_bec.devices.epics.pilatus_csaxs](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/pilatus_csaxs.py) |
| Bpm4i | | [csaxs_bec.devices.epics.specMotors](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/specMotors.py) |
| GirderMotorPITCH | Girder YAW pseudo motor | [csaxs_bec.devices.epics.specMotors](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/specMotors.py) |
| GirderMotorROLL | Girder ROLL pseudo motor | [csaxs_bec.devices.epics.specMotors](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/specMotors.py) |
| GirderMotorX1 | Girder X translation pseudo motor | [csaxs_bec.devices.epics.specMotors](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/specMotors.py) |
| GirderMotorY1 | Girder Y translation pseudo motor | [csaxs_bec.devices.epics.specMotors](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/specMotors.py) |
| GirderMotorYAW | Girder YAW pseudo motor | [csaxs_bec.devices.epics.specMotors](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/specMotors.py) |
| PmDetectorRotation | Detector rotation pseudo motor<br><br> Small wrapper to convert detector pusher position to rotation angle.<br> | [csaxs_bec.devices.epics.specMotors](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/specMotors.py) |
| PmMonoBender | Monochromator bender<br><br> Small wrapper to combine the four monochromator bender motors.<br> | [csaxs_bec.devices.epics.specMotors](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/epics/specMotors.py) |
| Xeye | | [csaxs_bec.devices.sls_devices.cSAXS.xeye](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/sls_devices/cSAXS/xeye.py) |
| SmaractMotor | | [csaxs_bec.devices.smaract.smaract_ophyd](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/smaract/smaract_ophyd.py) |
| Eiger1p5MDetector | | [csaxs_bec.devices.omny.eiger1p5m](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/omny/eiger1p5m.py) |
| FlomniSampleStorage | | [csaxs_bec.devices.omny.flomni_sample_storage](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/omny/flomni_sample_storage.py) |
| OMNYSampleStorage | | [csaxs_bec.devices.omny.omny_sample_storage](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/omny/omny_sample_storage.py) |
| FlomniGalilMotor | | [csaxs_bec.devices.omny.galil.fgalil_ophyd](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/omny/galil/fgalil_ophyd.py) |
| FuprGalilMotor | | [csaxs_bec.devices.omny.galil.fupr_ophyd](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/omny/galil/fupr_ophyd.py) |
| LamniGalilMotor | | [csaxs_bec.devices.omny.galil.lgalil_ophyd](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/omny/galil/lgalil_ophyd.py) |
| SGalilMotor | "SGalil Motors at cSAXS have a<br> DC motor (y axis - vertical) - implemented as C<br> and a step motor (x-axis horizontal) - implemented as E<br> that require different communication for control<br> | [csaxs_bec.devices.omny.galil.sgalil_ophyd](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/omny/galil/sgalil_ophyd.py) |
| RtFlomniMotor | | [csaxs_bec.devices.omny.rt.rt_flomni_ophyd](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/omny/rt/rt_flomni_ophyd.py) |
| RtLamniMotor | | [csaxs_bec.devices.omny.rt.rt_lamni_ophyd](https://gitlab.psi.ch/bec/csaxs_bec/-/blob/main/csaxs_bec/devices/omny/rt/rt_lamni_ophyd.py) |

View File

@@ -1,8 +1 @@
# Standard ophyd classes
from ophyd import EpicsMotor, EpicsSignal, EpicsSignalRO
from ophyd.quadem import QuadEM
from ophyd.sim import SynAxis, SynPeriodicSignal, SynSignal
from .devices.delay_generator_csaxs import DelayGeneratorcSAXS
from .devices.flomni_sample_storage import FlomniSampleStorage
from .devices.InsertionDevice import InsertionDevice

View File

@@ -0,0 +1,32 @@
""" TODO This class seems to be missing various imports and appears to have not been tested in motion yet."""
# TABLES_DT_PUSH_DIST_MM = 890
# class DetectorTableTheta(PseudoPositioner):
# """Detector table tilt motor
# Small wrapper to adjust the detector table tilt as angle.
# The table is pushed from one side by a single vertical motor.
# Note: Rarely used!
# """
# # Real axis (in degrees)
# pusher = Component(EpicsMotor, "", name="pusher")
# # Virtual axis
# theta = Component(PseudoSingle, name="theta")
# _real = ["pusher"]
# @pseudo_position_argument
# def forward(self, pseudo_pos):
# return self.RealPosition(
# pusher=tan(pseudo_pos.theta * 3.141592 / 180.0) * TABLES_DT_PUSH_DIST_MM
# )
# @real_position_argument
# def inverse(self, real_pos):
# return self.PseudoPosition(
# theta=-180 * atan(real_pos.pusher / TABLES_DT_PUSH_DIST_MM) / 3.141592
# )

View File

@@ -1,6 +1,6 @@
from bec_lib import bec_logger
from ophyd import Component
from ophyd_devices.epics.devices.psi_delay_generator_base import (
from ophyd_devices.interfaces.base_classes.psi_delay_generator_base import (
DDGCustomMixin,
PSIDelayGeneratorBase,
TriggerSource,

View File

@@ -1,14 +0,0 @@
# Standard ophyd classes
from ophyd import EpicsMotor, EpicsSignal, EpicsSignalRO
from ophyd.quadem import QuadEM
from ophyd.sim import SynAxis, SynPeriodicSignal, SynSignal
from .delay_generator_csaxs import DelayGeneratorcSAXS
from .eiger9m_csaxs import Eiger9McSAXS
# cSAXS
from .falcon_csaxs import FalconcSAXS
from .flomni_sample_storage import FlomniSampleStorage
from .InsertionDevice import InsertionDevice
from .mcs_csaxs import MCScSAXS
from .pilatus_csaxs import PilatuscSAXS

View File

@@ -1,32 +0,0 @@
""" TODO This class seems to be missing various imports and appears to have not been tested in motion yet."""
TABLES_DT_PUSH_DIST_MM = 890
class DetectorTableTheta(PseudoPositioner):
"""Detector table tilt motor
Small wrapper to adjust the detector table tilt as angle.
The table is pushed from one side by a single vertical motor.
Note: Rarely used!
"""
# Real axis (in degrees)
pusher = Component(EpicsMotor, "", name="pusher")
# Virtual axis
theta = Component(PseudoSingle, name="theta")
_real = ["pusher"]
@pseudo_position_argument
def forward(self, pseudo_pos):
return self.RealPosition(
pusher=tan(pseudo_pos.theta * 3.141592 / 180.0) * TABLES_DT_PUSH_DIST_MM
)
@real_position_argument
def inverse(self, real_pos):
return self.PseudoPosition(
theta=-180 * atan(real_pos.pusher / TABLES_DT_PUSH_DIST_MM) / 3.141592
)

View File

@@ -5,12 +5,15 @@ import time
from typing import Any
import numpy as np
from bec_lib import messages, threadlocked
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from bec_lib.logger import bec_logger
from ophyd import ADComponent as ADCpt
from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
from ophyd_devices.epics.devices.psi_detector_base import CustomDetectorMixin, PSIDetectorBase
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
CustomDetectorMixin,
PSIDetectorBase,
)
from std_daq_client import StdDaqClient
logger = bec_logger.logger
@@ -305,33 +308,35 @@ class Eiger9MSetup(CustomDetectorMixin):
)
pipe.execute()
@threadlocked
def finished(self):
"""Check if acquisition is finished."""
signal_conditions = [
(
lambda: self.parent.cam.acquire.read()[self.parent.cam.acquire.name]["value"],
DetectorState.IDLE,
),
(lambda: self.std_client.get_status()["acquisition"]["state"], "FINISHED"),
(
lambda: self.std_client.get_status()["acquisition"]["stats"]["n_write_completed"],
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger),
),
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=self.parent.timeout,
check_stopped=True,
all_signals=True,
):
raise EigerTimeoutError(
f"Reached timeout with detector state {signal_conditions[0][0]}, std_daq state"
f" {signal_conditions[1][0]} and received frames of {signal_conditions[2][0]} for"
" the file writer"
)
self.stop_detector()
self.stop_detector_backend()
with self._lock:
signal_conditions = [
(
lambda: self.parent.cam.acquire.read()[self.parent.cam.acquire.name]["value"],
DetectorState.IDLE,
),
(lambda: self.std_client.get_status()["acquisition"]["state"], "FINISHED"),
(
lambda: self.std_client.get_status()["acquisition"]["stats"][
"n_write_completed"
],
int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger),
),
]
if not self.wait_for_signals(
signal_conditions=signal_conditions,
timeout=self.parent.timeout,
check_stopped=True,
all_signals=True,
):
raise EigerTimeoutError(
f"Reached timeout with detector state {signal_conditions[0][0]}, std_daq state"
f" {signal_conditions[1][0]} and received frames of {signal_conditions[2][0]} for"
" the file writer"
)
self.stop_detector()
self.stop_detector_backend()
class SLSDetectorCam(Device):

View File

@@ -7,7 +7,10 @@ from bec_lib.logger import bec_logger
from ophyd import Component as Cpt
from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV
from ophyd.mca import EpicsMCARecord
from ophyd_devices.epics.devices.psi_detector_base import CustomDetectorMixin, PSIDetectorBase
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
CustomDetectorMixin,
PSIDetectorBase,
)
logger = bec_logger.logger

View File

@@ -3,10 +3,14 @@ import threading
from collections import defaultdict
import numpy as np
from bec_lib import MessageEndpoints, bec_logger, messages, threadlocked
from bec_lib import bec_logger, messages
from bec_lib.endpoints import MessageEndpoints
from ophyd import Component as Cpt
from ophyd import Device, EpicsSignal, EpicsSignalRO
from ophyd_devices.epics.devices.psi_detector_base import CustomDetectorMixin, PSIDetectorBase
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
CustomDetectorMixin,
PSIDetectorBase,
)
from ophyd_devices.utils import bec_utils
logger = bec_logger.logger
@@ -109,17 +113,17 @@ class MCSSetup(CustomDetectorMixin):
done=bool(max_value == value), # == self.counter),
)
@threadlocked
def _on_mca_data(self, *args, obj=None, value=None, **kwargs) -> None:
"""Callback function for scan progress"""
if not isinstance(value, (list, np.ndarray)):
return
self.mca_data[obj.attr_name] = value
if len(self.mca_names) != len(self.mca_data):
return
self.acquisition_done = True
self._send_data_to_bec()
self.mca_data = defaultdict(lambda: [])
with self._lock:
if not isinstance(value, (list, np.ndarray)):
return
self.mca_data[obj.attr_name] = value
if len(self.mca_names) != len(self.mca_data):
return
self.acquisition_done = True
self._send_data_to_bec()
self.mca_data = defaultdict(lambda: [])
def _send_data_to_bec(self) -> None:
"""Sends bundled data to BEC"""

View File

@@ -5,10 +5,14 @@ import time
import numpy as np
import requests
from bec_lib import MessageEndpoints, bec_logger, messages
from bec_lib import bec_logger, messages
from bec_lib.endpoints import MessageEndpoints
from ophyd import ADComponent as ADCpt
from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV, Staged
from ophyd_devices.epics.devices.psi_detector_base import CustomDetectorMixin, PSIDetectorBase
from ophyd_devices.interfaces.base_classes.psi_detector_base import (
CustomDetectorMixin,
PSIDetectorBase,
)
logger = bec_logger.logger

View File

@@ -1,9 +1,10 @@
import os
import time
from bec_lib import MessageEndpoints, bec_logger, messages
from bec_lib import bec_logger, messages
from bec_lib.endpoints import MessageEndpoints
from ophyd import Component as Cpt
from ophyd import Device, DeviceStatus, EpicsSignal, EpicsSignalRO, Signal
from ophyd import Device, EpicsSignal, EpicsSignalRO, Signal
logger = bec_logger.logger

View File

@@ -1,4 +1,4 @@
from .fgalil_ophyd import FlomniGalilController, FlomniGalilMotor
from .fupr_ophyd import FuprGalilController, FuprGalilMotor
from .galil_ophyd import GalilController, GalilMotor
from .lgalil_ophyd import LamniGalilController, LamniGalilMotor
from .sgalil_ophyd import SGalilMotor

View File

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 157 KiB

View File

@@ -10,7 +10,7 @@ from ophyd.utils import LimitError
from ophyd_devices.utils.controller import threadlocked
from ophyd_devices.utils.socket import SocketIO, raise_if_disconnected
from csaxs_bec.devices.galil.galil_ophyd import (
from csaxs_bec.devices.omny.galil.galil_ophyd import (
BECConfigError,
GalilAxesReferenced,
GalilController,

View File

@@ -1,4 +1,3 @@
import functools
import threading
import time
@@ -7,15 +6,13 @@ from bec_lib import bec_logger
from ophyd import Component as Cpt
from ophyd import Device, PositionerBase, Signal
from ophyd.status import wait as status_wait
from ophyd.utils import LimitError, ReadOnlyError
from ophyd_devices.utils.controller import Controller, threadlocked
from ophyd_devices.utils.socket import SocketIO, SocketSignal, raise_if_disconnected
from prettytable import PrettyTable
from ophyd.utils import LimitError
from ophyd_devices.utils.controller import threadlocked
from ophyd_devices.utils.socket import SocketIO, raise_if_disconnected
from csaxs_bec.devices.galil.galil_ophyd import (
from csaxs_bec.devices.omny.galil.galil_ophyd import (
BECConfigError,
GalilAxesReferenced,
GalilCommunicationError,
GalilController,
GalilError,
GalilMotorIsMoving,

View File

@@ -1,15 +1,14 @@
"""
This module contains the base class for Galil controllers as well as the signals used for Galil devices.
"""
import functools
import threading
import time
import numpy as np
from bec_lib import bec_logger
from ophyd import Component as Cpt
from ophyd import Device, PositionerBase, Signal
from ophyd.status import wait as status_wait
from ophyd.utils import LimitError, ReadOnlyError
from ophyd.utils import ReadOnlyError
from ophyd_devices.utils.controller import Controller, threadlocked
from ophyd_devices.utils.socket import SocketIO, SocketSignal, raise_if_disconnected
from ophyd_devices.utils.socket import SocketSignal
from prettytable import PrettyTable
logger = bec_logger.logger
@@ -42,6 +41,10 @@ def retry_once(fcn):
class GalilController(Controller):
"""
Base class for Galil controllers. This class provides the basic functionality for Galil controllers and should be subclassed for specific devices.
"""
_axes_per_controller = 8
USER_ACCESS = [
"describe",
@@ -49,7 +52,6 @@ class GalilController(Controller):
"galil_show_all",
"socket_put_and_receive",
"socket_put_confirmed",
"lgalil_is_air_off_and_orchestra_enabled",
"drive_axis_to_limit",
"find_reference",
"get_motor_limit_switch",
@@ -61,18 +63,6 @@ class GalilController(Controller):
def socket_put(self, val: str) -> None:
self.sock.put(f"{val}\r".encode())
@threadlocked
def socket_get(self) -> str:
return self.sock.receive().decode()
@retry_once
@threadlocked
def socket_put_and_receive(self, val: str, remove_trailing_chars=True) -> str:
self.socket_put(val)
if remove_trailing_chars:
return self._remove_trailing_characters(self.sock.receive().decode())
return self.socket_get()
@retry_once
def socket_put_confirmed(self, val: str) -> None:
"""Send message to controller and ensure that it is received by checking that the socket receives a colon.
@@ -94,7 +84,9 @@ class GalilController(Controller):
if axis_Id is None and axis_Id_numeric is not None:
axis_Id = self.axis_Id_numeric_to_alpha(axis_Id_numeric)
is_moving = bool(float(self.socket_put_and_receive(f"MG_BG{axis_Id}")) != 0)
backlash_is_active = bool(float(self.socket_put_and_receive(f"MGbcklact[{axis_Id_numeric}]")) != 0)
backlash_is_active = bool(
float(self.socket_put_and_receive(f"MGbcklact[{axis_Id_numeric}]")) != 0
)
return bool(
is_moving or backlash_is_active or self.is_thread_active(0) or self.is_thread_active(2)
)
@@ -105,21 +97,10 @@ class GalilController(Controller):
return False
return True
def _remove_trailing_characters(self, var) -> str:
if len(var) > 1:
return var.split("\r\n")[0]
return var
def stop_all_axes(self) -> str:
return self.socket_put_and_receive(f"XQ#STOP,1")
return self.socket_put_and_receive("XQ#STOP,1")
def lgalil_is_air_off_and_orchestra_enabled(self) -> bool:
# TODO: move this to the LamNI-specific controller
rt_not_blocked_by_galil = bool(self.socket_put_and_receive(f"MG@OUT[9]"))
air_off = bool(self.socket_put_and_receive(f"MG@OUT[13]"))
return rt_not_blocked_by_galil and air_off
def get_digital_input(self,channel):
def get_digital_input(self, channel):
return bool(float(self.socket_put_and_receive(f"MG @IN[{channel}]").strip()))
def axis_is_referenced(self, axis_Id_numeric) -> bool:
@@ -248,40 +229,17 @@ class GalilController(Controller):
self.show_running_threads()
self.show_status_other()
def show_status_other(self):
#Todo: move to lgalil specific section
if(self.get_digital_input(5)):
print("Emergency stop is not pushed.")
else:
print("Emergency stop is pushed.")
if(self.get_digital_input(6)):
print("Driver axis 2 error.")
if(self.get_digital_input(13)):
print("No air pressure at inner rotation.")
else:
print("There is air pressure at the inner rotation.")
if(self.get_digital_input(14)):
print("No air pressure at outer rotation axial.")
else:
print("There is air pressure at the outer rotation axial.")
if(self.get_digital_input(15)):
print("No air pressure at outer rotation radial.")
else:
print("There is air pressure at the outer rotation radial.")
swver = float(self.socket_put_and_receive("MGswver"))
print(f"Lgalil LAMNI firmware version {swver:2.0f}.")
def show_status_other(self) -> None:
"""
Show additional device-specific status information.
Override in subclasses.
"""
def galil_show_all(self) -> None:
for controller in self._controller_instances.values():
if isinstance(controller, GalilController):
controller.describe()
def lamni_lights_off(self):
self.socket_put_confirmed("SB1")
def lamni_lights_on(self):
self.socket_put_confirmed("CB1")
@staticmethod
def axis_Id_to_numeric(axis_Id: str) -> int:
return ord(axis_Id.lower()) - 97
@@ -378,7 +336,9 @@ class GalilSetpointSignal(GalilSignalBase):
if angle_status:
self.controller.socket_put_confirmed("angintf=1")
except KeyError:
logger.warning("RT is disabled. Failed to update RT angle interferometer status to galil.")
logger.warning(
"RT is disabled. Failed to update RT angle interferometer status to galil."
)
self.controller.socket_put_confirmed(f"naxis={self.parent.axis_Id_numeric}")
self.controller.socket_put_confirmed(f"ntarget={target_val:.3f}")
@@ -415,229 +375,3 @@ class GalilAxesReferenced(GalilSignalRO):
@threadlocked
def _socket_get(self):
return self.controller.all_axes_referenced()
class GalilMotor(Device, PositionerBase):
USER_ACCESS = ["controller"]
readback = Cpt(GalilReadbackSignal, signal_name="readback", kind="hinted")
user_setpoint = Cpt(GalilSetpointSignal, signal_name="setpoint")
motor_resolution = Cpt(GalilMotorResolution, signal_name="resolution", kind="config")
motor_is_moving = Cpt(GalilMotorIsMoving, signal_name="motor_is_moving", kind="normal")
all_axes_referenced = Cpt(GalilAxesReferenced, signal_name="all_axes_referenced", kind="config")
high_limit_travel = Cpt(Signal, value=0, kind="omitted")
low_limit_travel = Cpt(Signal, value=0, kind="omitted")
SUB_READBACK = "readback"
SUB_CONNECTION_CHANGE = "connection_change"
_default_sub = SUB_READBACK
def __init__(
self,
axis_Id,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
host="mpc2680.psi.ch",
port=8081,
limits=None,
sign=1,
socket_cls=SocketIO,
device_manager=None,
**kwargs,
):
self.controller = GalilController(socket_cls=socket_cls, socket_host=host, socket_port=port)
self.axis_Id = axis_Id
self.controller.set_axis(axis=self, axis_nr=self.axis_Id_numeric)
self.sign = sign
self.tolerance = kwargs.pop("tolerance", 0.5)
self.device_mapping = kwargs.pop("device_mapping", {})
self.device_manager = device_manager
if len(self.device_mapping) > 0 and self.device_manager is None:
raise BECConfigError(
"device_mapping has been specified but the device_manager cannot be accessed."
)
self.rt = self.device_mapping.get("rt")
super().__init__(
prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
**kwargs,
)
self.readback.name = self.name
self.controller.subscribe(
self._update_connection_state, event_type=self.SUB_CONNECTION_CHANGE
)
self._update_connection_state()
# self.readback.subscribe(self._forward_readback, event_type=self.readback.SUB_VALUE)
if limits is not None:
assert len(limits) == 2
self.low_limit_travel.put(limits[0])
self.high_limit_travel.put(limits[1])
@property
def limits(self):
return (self.low_limit_travel.get(), self.high_limit_travel.get())
@property
def low_limit(self):
return self.limits[0]
@property
def high_limit(self):
return self.limits[1]
def check_value(self, pos):
"""Check that the position is within the soft limits"""
low_limit, high_limit = self.limits
if low_limit < high_limit and not (low_limit <= pos <= high_limit):
raise LimitError(f"position={pos} not within limits {self.limits}")
def _update_connection_state(self, **kwargs):
for walk in self.walk_signals():
walk.item._metadata["connected"] = self.controller.connected
def _forward_readback(self, **kwargs):
kwargs.pop("sub_type")
self._run_subs(sub_type="readback", **kwargs)
@raise_if_disconnected
def move(self, position, wait=True, **kwargs):
"""Move to a specified position, optionally waiting for motion to
complete.
Parameters
----------
position
Position to move to
moved_cb : callable
Call this callback when movement has finished. This callback must
accept one keyword argument: 'obj' which will be set to this
positioner instance.
timeout : float, optional
Maximum time to wait for the motion. If None, the default timeout
for this positioner is used.
Returns
-------
status : MoveStatus
Raises
------
TimeoutError
When motion takes longer than `timeout`
ValueError
On invalid positions
RuntimeError
If motion fails other than timing out
"""
self._started_moving = False
timeout = kwargs.pop("timeout", 100)
status = super().move(position, timeout=timeout, **kwargs)
self.user_setpoint.put(position, wait=False)
def move_and_finish():
while self.motor_is_moving.get():
logger.info("motor is moving")
val = self.readback.read()
self._run_subs(sub_type=self.SUB_READBACK, value=val, timestamp=time.time())
time.sleep(0.1)
val = self.readback.read()
success = np.isclose(val[self.name]["value"], position, atol=self.tolerance)
if not success:
print(" stop")
self._done_moving(success=success)
logger.info("Move finished")
threading.Thread(target=move_and_finish, daemon=True).start()
try:
if wait:
status_wait(status)
except KeyboardInterrupt:
self.stop()
raise
return status
@property
def axis_Id(self):
return self._axis_Id_alpha
@axis_Id.setter
def axis_Id(self, val):
if isinstance(val, str):
if len(val) != 1:
raise ValueError(f"Only single-character axis_Ids are supported.")
self._axis_Id_alpha = val
self._axis_Id_numeric = self.controller.axis_Id_to_numeric(val)
else:
raise TypeError(f"Expected value of type str but received {type(val)}")
@property
def axis_Id_numeric(self):
return self._axis_Id_numeric
@axis_Id_numeric.setter
def axis_Id_numeric(self, val):
if isinstance(val, int):
if val > 26:
raise ValueError(f"Numeric value exceeds supported range.")
self._axis_Id_alpha = self.controller.axis_Id_numeric_to_alpha(val)
self._axis_Id_numeric = val
else:
raise TypeError(f"Expected value of type int but received {type(val)}")
@property
def egu(self):
"""The engineering units (EGU) for positions"""
return "mm"
def stage(self) -> list[object]:
return super().stage()
def unstage(self) -> list[object]:
return super().unstage()
def stop(self, *, success=False):
self.controller.stop_all_axes()
return super().stop(success=success)
if __name__ == "__main__":
# pytest: skip-file
mock = False
if not mock:
leyey = GalilMotor("H", name="leyey", host="mpc2680.psi.ch", port=8081, sign=-1)
leyey.stage()
status = leyey.move(0, wait=True)
status = leyey.move(10, wait=True)
leyey.read()
leyey.get()
leyey.describe()
leyey.unstage()
else:
from ophyd_devices.utils.socket import SocketMock
leyex = GalilMotor(
"G", name="leyex", host="mpc2680.psi.ch", port=8081, socket_cls=SocketMock
)
leyey = GalilMotor(
"H", name="leyey", host="mpc2680.psi.ch", port=8081, socket_cls=SocketMock
)
leyex.stage()
# leyey.stage()
leyex.controller.galil_show_all()

View File

@@ -0,0 +1,265 @@
import threading
import time
import numpy as np
from bec_lib.logger import bec_logger
from ophyd import Component as Cpt
from ophyd import Device, PositionerBase, Signal
from ophyd.status import wait as status_wait
from ophyd.utils import LimitError
from ophyd_devices.utils.socket import SocketIO, raise_if_disconnected
from csaxs_bec.devices.omny.galil.galil_ophyd import (
BECConfigError,
GalilAxesReferenced,
GalilController,
GalilMotorIsMoving,
GalilMotorResolution,
GalilReadbackSignal,
GalilSetpointSignal,
)
logger = bec_logger.logger
class LamniGalilController(GalilController):
USER_ACCESS = [
"describe",
"show_running_threads",
"galil_show_all",
"socket_put_and_receive",
"socket_put_confirmed",
"lgalil_is_air_off_and_orchestra_enabled",
"drive_axis_to_limit",
"find_reference",
"get_motor_limit_switch",
"is_motor_on",
"all_axes_referenced",
]
def show_status_other(self):
if self.get_digital_input(5):
print("Emergency stop is not pushed.")
else:
print("Emergency stop is pushed.")
if self.get_digital_input(6):
print("Driver axis 2 error.")
if self.get_digital_input(13):
print("No air pressure at inner rotation.")
else:
print("There is air pressure at the inner rotation.")
if self.get_digital_input(14):
print("No air pressure at outer rotation axial.")
else:
print("There is air pressure at the outer rotation axial.")
if self.get_digital_input(15):
print("No air pressure at outer rotation radial.")
else:
print("There is air pressure at the outer rotation radial.")
swver = float(self.socket_put_and_receive("MGswver"))
print(f"Lgalil LAMNI firmware version {swver:2.0f}.")
def lamni_lights_off(self):
self.socket_put_confirmed("SB1")
def lamni_lights_on(self):
self.socket_put_confirmed("CB1")
def lgalil_is_air_off_and_orchestra_enabled(self) -> bool:
# TODO: move this to the LamNI-specific controller
rt_not_blocked_by_galil = bool(self.socket_put_and_receive("MG@OUT[9]"))
air_off = bool(self.socket_put_and_receive("MG@OUT[13]"))
return rt_not_blocked_by_galil and air_off
class LamniGalilMotor(Device, PositionerBase):
USER_ACCESS = ["controller"]
readback = Cpt(GalilReadbackSignal, signal_name="readback", kind="hinted")
user_setpoint = Cpt(GalilSetpointSignal, signal_name="setpoint")
motor_resolution = Cpt(GalilMotorResolution, signal_name="resolution", kind="config")
motor_is_moving = Cpt(GalilMotorIsMoving, signal_name="motor_is_moving", kind="normal")
all_axes_referenced = Cpt(GalilAxesReferenced, signal_name="all_axes_referenced", kind="config")
high_limit_travel = Cpt(Signal, value=0, kind="omitted")
low_limit_travel = Cpt(Signal, value=0, kind="omitted")
SUB_READBACK = "readback"
SUB_CONNECTION_CHANGE = "connection_change"
_default_sub = SUB_READBACK
def __init__(
self,
axis_Id,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
host="mpc2680.psi.ch",
port=8081,
limits=None,
sign=1,
socket_cls=SocketIO,
device_manager=None,
**kwargs,
):
self.controller = LamniGalilController(
socket_cls=socket_cls, socket_host=host, socket_port=port
)
self.axis_Id = axis_Id
self.controller.set_axis(axis=self, axis_nr=self.axis_Id_numeric)
self.sign = sign
self.tolerance = kwargs.pop("tolerance", 0.5)
self.device_mapping = kwargs.pop("device_mapping", {})
self.device_manager = device_manager
if len(self.device_mapping) > 0 and self.device_manager is None:
raise BECConfigError(
"device_mapping has been specified but the device_manager cannot be accessed."
)
self.rt = self.device_mapping.get("rt")
super().__init__(
prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
**kwargs,
)
self.readback.name = self.name
self.controller.subscribe(
self._update_connection_state, event_type=self.SUB_CONNECTION_CHANGE
)
self._update_connection_state()
# self.readback.subscribe(self._forward_readback, event_type=self.readback.SUB_VALUE)
if limits is not None:
assert len(limits) == 2
self.low_limit_travel.put(limits[0])
self.high_limit_travel.put(limits[1])
@property
def limits(self):
return (self.low_limit_travel.get(), self.high_limit_travel.get())
@property
def low_limit(self):
return self.limits[0]
@property
def high_limit(self):
return self.limits[1]
def check_value(self, pos):
"""Check that the position is within the soft limits"""
low_limit, high_limit = self.limits
if low_limit < high_limit and not (low_limit <= pos <= high_limit):
raise LimitError(f"position={pos} not within limits {self.limits}")
def _update_connection_state(self, **kwargs):
for walk in self.walk_signals():
walk.item._metadata["connected"] = self.controller.connected
def _forward_readback(self, **kwargs):
kwargs.pop("sub_type")
self._run_subs(sub_type="readback", **kwargs)
@raise_if_disconnected
def move(self, position, wait=True, **kwargs):
"""Move to a specified position, optionally waiting for motion to
complete.
Parameters
----------
position
Position to move to
moved_cb : callable
Call this callback when movement has finished. This callback must
accept one keyword argument: 'obj' which will be set to this
positioner instance.
timeout : float, optional
Maximum time to wait for the motion. If None, the default timeout
for this positioner is used.
Returns
-------
status : MoveStatus
Raises
------
TimeoutError
When motion takes longer than `timeout`
ValueError
On invalid positions
RuntimeError
If motion fails other than timing out
"""
self._started_moving = False
timeout = kwargs.pop("timeout", 100)
status = super().move(position, timeout=timeout, **kwargs)
self.user_setpoint.put(position, wait=False)
def move_and_finish():
while self.motor_is_moving.get():
logger.info("motor is moving")
val = self.readback.read()
self._run_subs(sub_type=self.SUB_READBACK, value=val, timestamp=time.time())
time.sleep(0.1)
val = self.readback.read()
success = np.isclose(val[self.name]["value"], position, atol=self.tolerance)
if not success:
print(" stop")
self._done_moving(success=success)
logger.info("Move finished")
threading.Thread(target=move_and_finish, daemon=True).start()
try:
if wait:
status_wait(status)
except KeyboardInterrupt:
self.stop()
raise
return status
@property
def axis_Id(self):
return self._axis_Id_alpha
@axis_Id.setter
def axis_Id(self, val):
if isinstance(val, str):
if len(val) != 1:
raise ValueError("Only single-character axis_Ids are supported.")
self._axis_Id_alpha = val
self._axis_Id_numeric = self.controller.axis_Id_to_numeric(val)
else:
raise TypeError(f"Expected value of type str but received {type(val)}")
@property
def axis_Id_numeric(self):
return self._axis_Id_numeric
@axis_Id_numeric.setter
def axis_Id_numeric(self, val):
if isinstance(val, int):
if val > 26:
raise ValueError("Numeric value exceeds supported range.")
self._axis_Id_alpha = self.controller.axis_Id_numeric_to_alpha(val)
self._axis_Id_numeric = val
else:
raise TypeError(f"Expected value of type int but received {type(val)}")
@property
def egu(self):
"""The engineering units (EGU) for positions"""
return "mm"
def stop(self, *, success=False):
self.controller.stop_all_axes()
return super().stop(success=success)

View File

@@ -4,7 +4,7 @@ Ophyd wrapper for the SGalil controller and stages.
## Integration of the device in IPython kernel
BEC needs to be able to reach the host TCP to initiate a connection to the device.
```Python
from csaxs_bec.devices.galil.sgalil_ophyd import SGalilMotor
from csaxs_bec.devices.omny.galil.sgalil_ophyd import SGalilMotor
samx = SGalilMotor("E", name="samx", host="129.129.122.26", port=23, sign=-1)
samy = SGalilMotor("C", name="samy", host="129.129.122.26", port=23, sign=-1)
# connect to the controller

View File

@@ -4,7 +4,7 @@ from ophyd import Component as Cpt
from ophyd import Device
from ophyd import DynamicDeviceComponent as Dcpt
from ophyd import EpicsSignal
from prettytable import PrettyTable, FRAME
from prettytable import FRAME, PrettyTable
class OMNYSampleStorageError(Exception):

View File

@@ -3,29 +3,31 @@ import time
from typing import List
import numpy as np
from bec_lib import MessageEndpoints, bec_logger, messages
from bec_lib import bec_logger, messages
from bec_lib.endpoints import MessageEndpoints
from ophyd import Component as Cpt
from ophyd import Device, PositionerBase, Signal
from ophyd.status import wait as status_wait
from ophyd.utils import LimitError
from csaxs_bec.devices.rt_lamni.rt_ophyd import (
from ophyd_devices.utils.controller import Controller, threadlocked
from ophyd_devices.utils.socket import SocketIO, raise_if_disconnected
from prettytable import PrettyTable
from csaxs_bec.devices.omny.rt.rt_ophyd import (
BECConfigError,
RtCommunicationError,
RtController,
RtError,
RtReadbackSignal,
RtSetpointSignal,
RtSignalRO,
retry_once,
)
from ophyd_devices.utils.controller import threadlocked
from ophyd_devices.utils.socket import SocketIO, raise_if_disconnected
from prettytable import PrettyTable
logger = bec_logger.logger
class RtFlomniController(RtController):
class RtFlomniController(Controller):
_axes_per_controller = 3
USER_ACCESS = [
"socket_put_and_receive",
"set_rotation_angle",
@@ -50,7 +52,7 @@ class RtFlomniController(RtController):
def __init__(
self,
*,
name=None,
name="RtFlomniController",
socket_cls=None,
socket_host=None,
socket_port=None,
@@ -73,6 +75,15 @@ class RtFlomniController(RtController):
self._min_scan_buffer_reached = False
self.rt_pid_voltage = None
def is_axis_moving(self, axis_Id) -> bool:
# this checks that axis is on target
axis_is_on_target = bool(float(self.socket_put_and_receive("o")))
return not axis_is_on_target
@threadlocked
def stop_all_axes(self):
self.socket_put("sc")
def add_pos_to_scan(self, positions) -> None:
def send_positions(parent, positions):
parent._min_scan_buffer_reached = False
@@ -487,7 +498,7 @@ class RtFlomniController(RtController):
MessageEndpoints.device_status("rt_scan"),
messages.DeviceStatusMessage(
device="rt_scan", status=1, metadata=self.readout_metadata
).dumps(),
),
)
# while scan is running
while mode > 0:
@@ -522,7 +533,7 @@ class RtFlomniController(RtController):
MessageEndpoints.device_status("rt_scan"),
messages.DeviceStatusMessage(
device="rt_scan", status=0, metadata=self.readout_metadata
).dumps(),
),
)
logger.info(
@@ -536,7 +547,7 @@ class RtFlomniController(RtController):
MessageEndpoints.device_read("rt_flomni"),
messages.DeviceMessage(
signals=signals, metadata={"point_id": point_id, **self.readout_metadata}
).dumps(),
),
)
def start_readout(self):
@@ -683,15 +694,16 @@ class RtFlomniMotor(Device, PositionerBase):
def high_limit(self):
return self.limits[1]
def check_value(self, pos):
def check_value(self, value, **kwargs):
"""Check that the position is within the soft limits"""
low_limit, high_limit = self.limits
if low_limit < high_limit and not (low_limit <= pos <= high_limit):
raise LimitError(f"position={pos} not within limits {self.limits}")
if low_limit < high_limit and not (low_limit <= value <= high_limit):
raise LimitError(f"position={value} not within limits {self.limits}")
def _update_connection_state(self, **kwargs):
for walk in self.walk_signals():
# pylint: disable=protected-access
walk.item._metadata["connected"] = self.controller.connected
def _forward_readback(self, **kwargs):
@@ -761,7 +773,7 @@ class RtFlomniMotor(Device, PositionerBase):
def axis_Id(self, val):
if isinstance(val, str):
if len(val) != 1:
raise ValueError(f"Only single-character axis_Ids are supported.")
raise ValueError("Only single-character axis_Ids are supported.")
self._axis_Id_alpha = val
self._axis_Id_numeric = ord(val.lower()) - 97
else:
@@ -775,7 +787,7 @@ class RtFlomniMotor(Device, PositionerBase):
def axis_Id_numeric(self, val):
if isinstance(val, int):
if val > 26:
raise ValueError(f"Numeric value exceeds supported range.")
raise ValueError("Numeric value exceeds supported range.")
self._axis_Id_alpha = val
self._axis_Id_numeric = (chr(val + 97)).capitalize()
else:
@@ -789,14 +801,6 @@ class RtFlomniMotor(Device, PositionerBase):
"""The engineering units (EGU) for positions"""
return "um"
# how is this used later?
def stage(self) -> List[object]:
return super().stage()
def unstage(self) -> List[object]:
return super().unstage()
def stop(self, *, success=False):
self.controller.stop_all_axes()
self._stopped = True

View File

@@ -3,7 +3,8 @@ import threading
import time
import numpy as np
from bec_lib import MessageEndpoints, bec_logger, messages
from bec_lib import bec_logger, messages
from bec_lib.endpoints import MessageEndpoints
from ophyd import Component as Cpt
from ophyd import Device, PositionerBase, Signal
from ophyd.status import wait as status_wait
@@ -11,6 +12,8 @@ from ophyd.utils import LimitError, ReadOnlyError
from ophyd_devices.utils.controller import Controller, threadlocked
from ophyd_devices.utils.socket import SocketIO, SocketSignal, raise_if_disconnected
from csaxs_bec.devices.omny.rt.rt_ophyd import RtCommunicationError, RtError
logger = bec_logger.logger
@@ -41,6 +44,11 @@ def retry_once(fcn):
class RtLamniController(Controller):
"""
RT-Lamni controller class for all rt devices.
"""
_axes_per_controller = 3
USER_ACCESS = [
"socket_put_and_receive",
"set_rotation_angle",
@@ -60,110 +68,25 @@ class RtLamniController(Controller):
self,
*,
name="RtLamniController",
kind=None,
parent=None,
socket=None,
socket_cls=None,
socket_host=None,
socket_port=None,
attr_name="",
parent=None,
labels=None,
kind=None,
):
if not hasattr(self, "_initialized") or not self._initialized:
self._rtlamni_axis_per_controller = 3
self._axis = [None for axis_num in range(self._rtlamni_axis_per_controller)]
self._min_scan_buffer_reached = False
super().__init__(
name=name,
socket=socket,
attr_name=attr_name,
parent=parent,
labels=labels,
kind=kind,
)
self.readout_metadata = {}
def on(self, controller_num=0) -> None:
"""Open a new socket connection to the controller"""
if not self.connected:
try:
self.sock.open()
# discuss - after disconnect takes a while for the server to be ready again
max_retries = 10
tries = 0
while not self.connected:
try:
welcome_message = self.sock.receive()
self.connected = True
except ConnectionResetError as conn_reset:
if tries > max_retries:
raise conn_reset
tries += 1
time.sleep(2)
except ConnectionRefusedError as conn_error:
logger.error("Failed to open a connection to RTLamNI.")
raise RtLamniCommunicationError from conn_error
else:
logger.info("The connection has already been established.")
# warnings.warn(f"The connection has already been established.", stacklevel=2)
self._update_flyer_device_info()
def off(self) -> None:
"""Close the socket connection to the controller"""
if self.connected:
self.sock.close()
self.connected = False
else:
logger.info("The connection is already closed.")
def set_axis(self, axis: Device, axis_nr: int) -> None:
"""Assign an axis to a device instance.
Args:
axis (Device): Device instance (e.g. GalilMotor)
axis_nr (int): Controller axis number
"""
self._axis[axis_nr] = axis
@threadlocked
def socket_put(self, val: str) -> None:
self.sock.put(f"{val}\n".encode())
@threadlocked
def socket_get(self) -> str:
return self.sock.receive().decode()
@retry_once
@threadlocked
def socket_put_and_receive(self, val: str, remove_trailing_chars=True) -> str:
self.socket_put(val)
if remove_trailing_chars:
return self._remove_trailing_characters(self.sock.receive().decode())
return self.socket_get()
def is_axis_moving(self, axis_Id) -> bool:
# this checks that axis is on target
axis_is_on_target = bool(float(self.socket_put_and_receive(f"o")))
return not axis_is_on_target
# def is_thread_active(self, thread_id: int) -> bool:
# val = float(self.socket_put_and_receive(f"MG_XQ{thread_id}"))
# if val == -1:
# return False
# return True
def _remove_trailing_characters(self, var) -> str:
if len(var) > 1:
return var.split("\r\n")[0]
return var
@threadlocked
def set_rotation_angle(self, val: float):
self.socket_put(f"a{(val-300+30.538)/180*np.pi}")
@threadlocked
def stop_all_axes(self):
self.socket_put("sc")
super().__init__(
name=name,
socket_cls=socket_cls,
socket_host=socket_host,
socket_port=socket_port,
attr_name=attr_name,
parent=parent,
labels=labels,
kind=kind,
)
self._min_scan_buffer_reached = False
@threadlocked
def feedback_disable(self):
@@ -175,6 +98,19 @@ class RtLamniController(Controller):
self.set_device_enabled("lopty", True)
self.set_device_enabled("loptz", True)
def is_axis_moving(self, axis_Id) -> bool:
# this checks that axis is on target
axis_is_on_target = bool(float(self.socket_put_and_receive("o")))
return not axis_is_on_target
@threadlocked
def stop_all_axes(self):
self.socket_put("sc")
@threadlocked
def set_rotation_angle(self, val: float):
self.socket_put(f"a{(val-300+30.538)/180*np.pi}")
@threadlocked
def _set_axis_velocity(self, um_per_s):
self.socket_put(f"V{um_per_s}")
@@ -234,19 +170,6 @@ class RtLamniController(Controller):
self.set_device_enabled("lopty", True)
self.set_device_enabled("loptz", True)
def get_device_manager(self):
for axis in self._axis:
if hasattr(axis, "device_manager") and axis.device_manager:
return axis.device_manager
raise BECConfigError("Could not access the device_manager")
def get_axis_by_name(self, name):
for axis in self._axis:
if axis:
if axis.name == name:
return axis
raise RuntimeError(f"Could not find an axis with name {name}")
@threadlocked
def clear_trajectory_generator(self):
self.socket_put("sc")
@@ -256,7 +179,7 @@ class RtLamniController(Controller):
def send_positions(parent, positions):
parent._min_scan_buffer_reached = False
for pos_index, pos in enumerate(positions):
parent.socket_put_and_receive(f"s{pos[0]:.05f},{pos[1]:05f},0")
parent.socket_put_and_receive(f"s{pos[0]:.05f},{pos[1]:.05f},0")
if pos_index > 100:
parent._min_scan_buffer_reached = True
parent._min_scan_buffer_reached = True
@@ -268,7 +191,7 @@ class RtLamniController(Controller):
def get_scan_status(self):
return_table = (self.socket_put_and_receive(f"sr")).split(",")
if len(return_table) != 3:
raise RtLamniCommunicationError(
raise RtCommunicationError(
f"Expected to receive 3 return values. Instead received {return_table}"
)
mode = int(return_table[0])
@@ -288,7 +211,7 @@ class RtLamniController(Controller):
logger.error(
"Cannot start scan because feedback loop is not running or there is an interferometer error."
)
raise RtLamniError(
raise RtError(
"Cannot start scan because feedback loop is not running or there is an interferometer error."
)
# here exception
@@ -296,7 +219,7 @@ class RtLamniController(Controller):
if number_of_positions_planned == 0:
logger.error("Cannot start scan because no target positions are planned.")
raise RtLamniError("Cannot start scan because no target positions are planned.")
raise RtError("Cannot start scan because no target positions are planned.")
# hier exception
# start a point-by-point scan (for cont scan in flomni it would be "sa")
self.socket_put_and_receive("sd")
@@ -305,29 +228,6 @@ class RtLamniController(Controller):
readout = threading.Thread(target=self.read_positions_from_sampler)
readout.start()
def _update_flyer_device_info(self):
flyer_info = self._get_flyer_device_info()
self.get_device_manager().connector.set(
MessageEndpoints.device_info("rt_scan"),
messages.DeviceInfoMessage(device="rt_scan", info=flyer_info),
)
def _get_flyer_device_info(self) -> dict:
return {
"device_name": self.name,
"device_attr_name": getattr(self, "attr_name", ""),
"device_dotted_name": getattr(self, "dotted_name", ""),
"device_info": {
"device_base_class": "ophydobject",
"signals": [],
"hints": {"fields": ["average_x_st_fzp", "average_y_st_fzp"]},
"describe": {},
"describe_configuration": {},
"sub_devices": [],
"custom_user_access": [],
},
}
def kickoff(self, metadata):
self.readout_metadata = metadata
while not self._min_scan_buffer_reached:
@@ -439,7 +339,7 @@ class RtLamniController(Controller):
)
def feedback_status_angle_lamni(self) -> bool:
return_table = (self.socket_put_and_receive(f"J7")).split(",")
return_table = (self.socket_put_and_receive("J7")).split(",")
logger.debug(
f"LamNI angle interferomter status {bool(return_table[0])}, position {float(return_table[1])}, signal {float(return_table[2])}"
)
@@ -448,21 +348,21 @@ class RtLamniController(Controller):
def feedback_enable_with_reset(self):
if not self.feedback_status_angle_lamni():
self.feedback_disable_and_even_reset_lamni_angle_interferometer()
logger.info(f"LamNI resetting interferometer inclusive angular interferomter.")
logger.info("LamNI resetting interferometer inclusive angular interferomter.")
else:
self.feedback_disable()
logger.info(
f"LamNI resetting interferomter except angular interferometer which is already running."
"LamNI resetting interferomter except angular interferometer which is already running."
)
# set these as closed loop target position
self.socket_put(f"pa0,0")
self.socket_put("pa0,0")
self.get_axis_by_name("rtx").user_setpoint.setpoint = 0
self.socket_put(f"pa1,0")
self.socket_put("pa1,0")
self.get_axis_by_name("rty").user_setpoint.setpoint = 0
self.socket_put(
f"pa2,0"
"pa2,0"
) # we set all three outputs of the traj. gen. although in LamNI case only 0,1 are used
self.clear_trajectory_generator()
@@ -476,7 +376,7 @@ class RtLamniController(Controller):
logger.error(
"Cannot enable feedback. The small rotation air is on and/or orchestra disabled by the motor controller."
)
raise RtLamniError(
raise RtError(
"Cannot enable feedback. The small rotation air is on and/or orchestra disabled by the motor controller."
)
@@ -515,7 +415,7 @@ class RtLamniController(Controller):
logger.error(
"Cannot start scan because feedback loop is not running or there is an interferometer error."
)
raise RtLamniError(
raise RtError(
"Cannot start scan because feedback loop is not running or there is an interferometer error."
)
@@ -523,15 +423,6 @@ class RtLamniController(Controller):
# ptychography_alignment_done = 0
def set_device_enabled(self, device_name: str, enabled: bool) -> None:
"""enable / disable a device"""
if device_name not in self.get_device_manager().devices:
logger.warning(
f"Device {device_name} is not configured and cannot be enabled/disabled."
)
return
self.get_device_manager().devices[device_name].read_only = not enabled
class RtLamniSignalBase(SocketSignal):
def __init__(self, signal_name, **kwargs):
@@ -567,7 +458,7 @@ class RtLamniReadbackSignal(RtLamniSignalRO):
readback_index = 1
else:
raise RtLamniError("Currently, only two axes are supported.")
print(return_table)
current_pos = float(return_table[readback_index])
current_pos *= self.parent.sign
@@ -667,7 +558,9 @@ class RtLamniMotor(Device, PositionerBase):
):
self.axis_Id = axis_Id
self.sign = sign
self.controller = RtLamniController(socket=socket_cls(host=host, port=port))
self.controller = RtLamniController(
socket_cls=socket_cls, socket_host=host, socket_port=port
)
self.controller.set_axis(axis=self, axis_nr=self.axis_Id_numeric)
self.device_manager = device_manager
self.tolerance = kwargs.pop("tolerance", 0.5)
@@ -705,15 +598,16 @@ class RtLamniMotor(Device, PositionerBase):
def high_limit(self):
return self.limits[1]
def check_value(self, pos):
def check_value(self, value, **kwargs):
"""Check that the position is within the soft limits"""
low_limit, high_limit = self.limits
if low_limit < high_limit and not (low_limit <= pos <= high_limit):
raise LimitError(f"position={pos} not within limits {self.limits}")
if low_limit < high_limit and not (low_limit <= value <= high_limit):
raise LimitError(f"position={value} not within limits {self.limits}")
def _update_connection_state(self, **kwargs):
for walk in self.walk_signals():
# pylint: disable=protected-access
walk.item._metadata["connected"] = self.controller.connected
def _forward_readback(self, **kwargs):
@@ -782,7 +676,7 @@ class RtLamniMotor(Device, PositionerBase):
def axis_Id(self, val):
if isinstance(val, str):
if len(val) != 1:
raise ValueError(f"Only single-character axis_Ids are supported.")
raise ValueError("Only single-character axis_Ids are supported.")
self._axis_Id_alpha = val
self._axis_Id_numeric = ord(val.lower()) - 97
else:
@@ -796,7 +690,7 @@ class RtLamniMotor(Device, PositionerBase):
def axis_Id_numeric(self, val):
if isinstance(val, int):
if val > 26:
raise ValueError(f"Numeric value exceeds supported range.")
raise ValueError("Numeric value exceeds supported range.")
self._axis_Id_alpha = val
self._axis_Id_numeric = (chr(val + 97)).capitalize()
else:
@@ -823,9 +717,7 @@ class RtLamniMotor(Device, PositionerBase):
return super().stop(success=success)
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
if __name__ == "__main__": # pragma: no cover
mock = False
if not mock:
rty = RtLamniMotor("B", name="rty", host="mpc2680.psi.ch", port=3333, sign=1)

View File

@@ -0,0 +1,140 @@
"""
This module contains base signals for RT devices. Controller and motors are implemented in the
bespoke modules such as `rt_flomni_ophyd.py` or `rt_lamni_ophyd.py`.
"""
import functools
import time
from bec_lib import bec_logger
from ophyd.utils import ReadOnlyError
from ophyd_devices.utils.controller import ControllerCommunicationError, threadlocked
from ophyd_devices.utils.socket import SocketSignal
logger = bec_logger.logger
class RtCommunicationError(ControllerCommunicationError):
pass
class RtError(ControllerCommunicationError):
pass
class BECConfigError(Exception):
pass
def retry_once(fcn):
"""Decorator to rerun a function in case a CommunicationError was raised. This may happen if the buffer was not empty."""
@functools.wraps(fcn)
def wrapper(self, *args, **kwargs):
try:
val = fcn(self, *args, **kwargs)
except (RtCommunicationError, RtError):
val = fcn(self, *args, **kwargs)
return val
return wrapper
class RtSignalBase(SocketSignal):
def __init__(self, signal_name, **kwargs):
self.signal_name = signal_name
super().__init__(**kwargs)
self.controller = self.parent.controller
self.sock = self.parent.controller.sock
class RtSignalRO(RtSignalBase):
def __init__(self, signal_name, **kwargs):
super().__init__(signal_name, **kwargs)
self._metadata["write_access"] = False
def _socket_set(self, val):
raise ReadOnlyError("Read-only signals cannot be set")
class RtReadbackSignal(RtSignalRO):
@retry_once
@threadlocked
def _socket_get(self) -> float:
"""Get command for the readback signal
Returns:
float: Readback value after adjusting for sign and motor resolution.
"""
return_table = (self.controller.socket_put_and_receive("J4")).split(",")
if self.parent.axis_Id_numeric == 0:
readback_index = 2
elif self.parent.axis_Id_numeric == 1:
readback_index = 1
else:
raise RtError("Currently, only two axes are supported.")
current_pos = float(return_table[readback_index])
current_pos *= self.parent.sign
return current_pos
class RtSetpointSignal(RtSignalBase):
setpoint = 0
def _socket_get(self) -> float:
"""Get command for receiving the setpoint / target value.
The value is not pulled from the controller but instead just the last setpoint used.
Returns:
float: setpoint / target value
"""
return self.setpoint
@retry_once
@threadlocked
def _socket_set(self, val: float) -> None:
"""Set a new target value / setpoint value. Before submission, the target value is adjusted for the axis' sign.
Furthermore, it is ensured that all axes are referenced before a new setpoint is submitted.
Args:
val (float): Target value / setpoint value
Raises:
RtError: Raised if interferometer feedback is disabled.
"""
interferometer_feedback_not_running = int(
(self.controller.socket_put_and_receive("J2")).split(",")[0]
)
if interferometer_feedback_not_running != 0:
raise RtError(
"The interferometer feedback is not running. Either it is turned off or and interferometer error occured."
)
self.set_with_feedback_disabled(val)
def set_with_feedback_disabled(self, val):
target_val = val * self.parent.sign
self.setpoint = target_val
self.controller.socket_put(f"pa{self.parent.axis_Id_numeric},{target_val:.4f}")
class RtMotorIsMoving(RtSignalRO):
def _socket_get(self):
return self.controller.is_axis_moving(self.parent.axis_Id_numeric)
def get(self):
val = super().get()
if val is not None:
self._run_subs(sub_type=self.SUB_VALUE, value=val, timestamp=time.time())
return val
class RtFeedbackRunning(RtSignalRO):
@threadlocked
def _socket_get(self):
if int((self.controller.socket_put_and_receive("J2")).split(",")[0]) == 0:
return 1
else:
return 0

View File

@@ -1,817 +0,0 @@
import functools
import threading
import time
from typing import List
import numpy as np
from bec_lib import MessageEndpoints, bec_logger, messages
from ophyd import Component as Cpt
from ophyd import Device, PositionerBase, Signal
from ophyd.status import wait as status_wait
from ophyd.utils import LimitError, ReadOnlyError
from ophyd_devices.utils.controller import Controller, threadlocked
from ophyd_devices.utils.socket import SocketIO, SocketSignal, raise_if_disconnected
logger = bec_logger.logger
class RtCommunicationError(Exception):
pass
class RtError(Exception):
pass
class BECConfigError(Exception):
pass
def retry_once(fcn):
"""Decorator to rerun a function in case a CommunicationError was raised. This may happen if the buffer was not empty."""
@functools.wraps(fcn)
def wrapper(self, *args, **kwargs):
try:
val = fcn(self, *args, **kwargs)
except (RtCommunicationError, RtError):
val = fcn(self, *args, **kwargs)
return val
return wrapper
class RtController(Controller):
_axes_per_controller = 3
USER_ACCESS = [
"socket_put_and_receive",
"set_rotation_angle",
"feedback_disable",
"feedback_enable_without_reset",
"feedback_disable_and_even_reset_lamni_angle_interferometer",
"feedback_enable_with_reset",
"add_pos_to_scan",
"clear_trajectory_generator",
"_set_axis_velocity",
"_set_axis_velocity_maximum_speed",
"_position_sampling_single_read",
"_position_sampling_single_reset_and_start_sampling",
]
def on(self, controller_num=0) -> None:
"""Open a new socket connection to the controller"""
# if not self.connected:
# try:
# self.sock.open()
# # discuss - after disconnect takes a while for the server to be ready again
# max_retries = 10
# tries = 0
# while not self.connected:
# try:
# welcome_message = self.sock.receive()
# self.connected = True
# except ConnectionResetError as conn_reset:
# if tries > max_retries:
# raise conn_reset
# tries += 1
# time.sleep(2)
# except ConnectionRefusedError as conn_error:
# logger.error("Failed to open a connection to RTLamNI.")
# raise RtCommunicationError from conn_error
# else:
# logger.info("The connection has already been established.")
# # warnings.warn(f"The connection has already been established.", stacklevel=2)
super().on()
# self._update_flyer_device_info()
def set_axis(self, axis: Device, axis_nr: int) -> None:
"""Assign an axis to a device instance.
Args:
axis (Device): Device instance (e.g. GalilMotor)
axis_nr (int): Controller axis number
"""
self._axis[axis_nr] = axis
@threadlocked
def socket_put(self, val: str) -> None:
self.sock.put(f"{val}\n".encode())
@threadlocked
def socket_get(self) -> str:
return self.sock.receive().decode()
@retry_once
@threadlocked
def socket_put_and_receive(self, val: str, remove_trailing_chars=True) -> str:
self.socket_put(val)
if remove_trailing_chars:
return self._remove_trailing_characters(self.sock.receive().decode())
return self.socket_get()
def is_axis_moving(self, axis_Id) -> bool:
# this checks that axis is on target
axis_is_on_target = bool(float(self.socket_put_and_receive(f"o")))
return not axis_is_on_target
# def is_thread_active(self, thread_id: int) -> bool:
# val = float(self.socket_put_and_receive(f"MG_XQ{thread_id}"))
# if val == -1:
# return False
# return True
def _remove_trailing_characters(self, var) -> str:
if len(var) > 1:
return var.split("\r\n")[0]
return var
@threadlocked
def set_rotation_angle(self, val: float):
self.socket_put(f"a{(val-300+30.538)/180*np.pi}")
@threadlocked
def stop_all_axes(self):
self.socket_put("sc")
@threadlocked
def feedback_disable(self):
self.socket_put("J0")
logger.info("LamNI Feedback disabled.")
self.set_device_enabled("lsamx", True)
self.set_device_enabled("lsamy", True)
self.set_device_enabled("loptx", True)
self.set_device_enabled("lopty", True)
self.set_device_enabled("loptz", True)
@threadlocked
def _set_axis_velocity(self, um_per_s):
self.socket_put(f"V{um_per_s}")
@threadlocked
def _set_axis_velocity_maximum_speed(self):
self.socket_put(f"V0")
# for developement of soft continuous scanning
@threadlocked
def _position_sampling_single_reset_and_start_sampling(self):
self.socket_put(f"Ss")
@threadlocked
def _position_sampling_single_read(self):
(number_of_samples, sum0, sum0_2, sum1, sum1_2, sum2, sum2_2) = self.socket_put_and_receive(
f"Sr"
).split(",")
avg_x = float(sum1) / int(number_of_samples)
avg_y = float(sum0) / int(number_of_samples)
stdev_x = np.sqrt(
float(sum1_2) / int(number_of_samples)
- np.power(float(sum1) / int(number_of_samples), 2)
)
stdev_y = np.sqrt(
float(sum0_2) / int(number_of_samples)
- np.power(float(sum0) / int(number_of_samples), 2)
)
return (avg_x, avg_y, stdev_x, stdev_y)
@threadlocked
def feedback_enable_without_reset(self):
# read current interferometer position
return_table = (self.socket_put_and_receive(f"J4")).split(",")
x_curr = float(return_table[2])
y_curr = float(return_table[1])
# set these as closed loop target position
self.socket_put(f"pa0,{x_curr:.4f}")
self.socket_put(f"pa1,{y_curr:.4f}")
self.get_device_manager().devices.rtx.obj.user_setpoint.set_with_feedback_disabled(x_curr)
self.get_device_manager().devices.rty.obj.user_setpoint.set_with_feedback_disabled(y_curr)
self.socket_put("J5")
logger.info("LamNI Feedback enabled (without reset).")
self.set_device_enabled("lsamx", False)
self.set_device_enabled("lsamy", False)
self.set_device_enabled("loptx", False)
self.set_device_enabled("lopty", False)
self.set_device_enabled("loptz", False)
@threadlocked
def feedback_disable_and_even_reset_lamni_angle_interferometer(self):
self.socket_put("J6")
logger.info("LamNI Feedback disabled including the angular interferometer.")
self.set_device_enabled("lsamx", True)
self.set_device_enabled("lsamy", True)
self.set_device_enabled("loptx", True)
self.set_device_enabled("lopty", True)
self.set_device_enabled("loptz", True)
def get_device_manager(self):
for axis in self._axis:
if hasattr(axis, "device_manager") and axis.device_manager:
return axis.device_manager
raise BECConfigError("Could not access the device_manager")
def get_axis_by_name(self, name):
for axis in self._axis:
if axis:
if axis.name == name:
return axis
raise RuntimeError(f"Could not find an axis with name {name}")
@threadlocked
def clear_trajectory_generator(self):
self.socket_put("sc")
logger.info("LamNI scan stopped and deleted, moving to start position")
def add_pos_to_scan(self, positions) -> None:
def send_positions(parent, positions):
parent._min_scan_buffer_reached = False
for pos_index, pos in enumerate(positions):
parent.socket_put_and_receive(f"s{pos[0]},{pos[1]},0")
if pos_index > 100:
parent._min_scan_buffer_reached = True
parent._min_scan_buffer_reached = True
threading.Thread(target=send_positions, args=(self, positions), daemon=True).start()
@retry_once
@threadlocked
def get_scan_status(self):
return_table = (self.socket_put_and_receive(f"sr")).split(",")
if len(return_table) != 3:
raise RtCommunicationError(
f"Expected to receive 3 return values. Instead received {return_table}"
)
mode = int(return_table[0])
# mode 0: direct positioning
# mode 1: running internal timer (not tested/used anymore)
# mode 2: rt point scan running
# mode 3: rt point scan starting
# mode 5/6: rt continuous scanning (not available in LamNI)
number_of_positions_planned = int(return_table[1])
current_position_in_scan = int(return_table[2])
return (mode, number_of_positions_planned, current_position_in_scan)
@threadlocked
def start_scan(self):
interferometer_feedback_not_running = int((self.socket_put_and_receive("J2")).split(",")[0])
if interferometer_feedback_not_running == 1:
logger.error(
"Cannot start scan because feedback loop is not running or there is an interferometer error."
)
raise RtError(
"Cannot start scan because feedback loop is not running or there is an interferometer error."
)
# here exception
(mode, number_of_positions_planned, current_position_in_scan) = self.get_scan_status()
if number_of_positions_planned == 0:
logger.error("Cannot start scan because no target positions are planned.")
raise RtError("Cannot start scan because no target positions are planned.")
# hier exception
# start a point-by-point scan (for cont scan in flomni it would be "sa")
self.socket_put_and_receive("sd")
def start_readout(self):
readout = threading.Thread(target=self.read_positions_from_sampler)
readout.start()
def _update_flyer_device_info(self):
flyer_info = self._get_flyer_device_info()
self.get_device_manager().connector.set(
MessageEndpoints.device_info("rt_scan"),
messages.DeviceInfoMessage(device="rt_scan", info=flyer_info).dumps(),
)
def _get_flyer_device_info(self) -> dict:
return {
"device_name": self.name,
"device_attr_name": getattr(self, "attr_name", ""),
"device_dotted_name": getattr(self, "dotted_name", ""),
"device_info": {
"device_base_class": "ophydobject",
"signals": [],
"hints": {"fields": ["average_x_st_fzp", "average_y_st_fzp"]},
"describe": {},
"describe_configuration": {},
"sub_devices": [],
"custom_user_access": [],
},
}
def kickoff(self, metadata):
self.readout_metadata = metadata
while not self._min_scan_buffer_reached:
time.sleep(0.001)
self.start_scan()
time.sleep(0.1)
self.start_readout()
def _get_signals_from_table(self, return_table) -> dict:
self.average_stdeviations_x_st_fzp += float(return_table[5])
self.average_stdeviations_y_st_fzp += float(return_table[8])
self.average_lamni_angle += float(return_table[19])
signals = {
"target_x": {"value": float(return_table[3])},
"average_x_st_fzp": {"value": float(return_table[4])},
"stdev_x_st_fzp": {"value": float(return_table[5])},
"target_y": {"value": float(return_table[6])},
"average_y_st_fzp": {"value": float(return_table[7])},
"stdev_y_st_fzp": {"value": float(return_table[8])},
"average_cap1": {"value": float(return_table[9])},
"stdev_cap1": {"value": float(return_table[10])},
"average_cap2": {"value": float(return_table[11])},
"stdev_cap2": {"value": float(return_table[12])},
"average_cap3": {"value": float(return_table[13])},
"stdev_cap3": {"value": float(return_table[14])},
"average_cap4": {"value": float(return_table[15])},
"stdev_cap4": {"value": float(return_table[16])},
"average_cap5": {"value": float(return_table[17])},
"stdev_cap5": {"value": float(return_table[18])},
"average_angle_interf_ST": {"value": float(return_table[19])},
"stdev_angle_interf_ST": {"value": float(return_table[20])},
"average_stdeviations_x_st_fzp": {
"value": self.average_stdeviations_x_st_fzp / (int(return_table[0]) + 1)
},
"average_stdeviations_y_st_fzp": {
"value": self.average_stdeviations_y_st_fzp / (int(return_table[0]) + 1)
},
"average_lamni_angle": {"value": self.average_lamni_angle / (int(return_table[0]) + 1)},
}
return signals
def read_positions_from_sampler(self):
# this was for reading after the scan completed
number_of_samples_to_read = 1 # self.get_scan_status()[1] #number of valid samples, will be updated upon first data read
read_counter = 0
previous_point_in_scan = 0
self.average_stdeviations_x_st_fzp = 0
self.average_stdeviations_y_st_fzp = 0
self.average_lamni_angle = 0
mode, number_of_positions_planned, current_position_in_scan = self.get_scan_status()
# if not (mode==2 or mode==3):
# error
self.get_device_manager().connector.set(
MessageEndpoints.device_status("rt_scan"),
messages.DeviceStatusMessage(
device="rt_scan", status=1, metadata=self.readout_metadata
).dumps(),
)
# while scan is running
while mode > 0:
# logger.info(f"Current scan position {current_position_in_scan} out of {number_of_positions_planned}")
mode, number_of_positions_planned, current_position_in_scan = self.get_scan_status()
time.sleep(0.01)
if current_position_in_scan > 5:
while current_position_in_scan > read_counter + 1:
return_table = (self.socket_put_and_receive(f"r{read_counter}")).split(",")
# logger.info(f"{return_table}")
logger.info(f"Read {read_counter} out of {number_of_positions_planned}")
read_counter = read_counter + 1
signals = self._get_signals_from_table(return_table)
self.publish_device_data(signals=signals, point_id=int(return_table[0]))
time.sleep(0.05)
# read the last samples even though scan is finished already
while number_of_positions_planned > read_counter:
return_table = (self.socket_put_and_receive(f"r{read_counter}")).split(",")
logger.info(f"Read {read_counter} out of {number_of_positions_planned}")
# logger.info(f"{return_table}")
read_counter = read_counter + 1
signals = self._get_signals_from_table(return_table)
self.publish_device_data(signals=signals, point_id=int(return_table[0]))
self.get_device_manager().connector.set(
MessageEndpoints.device_status("rt_scan"),
messages.DeviceStatusMessage(
device="rt_scan", status=0, metadata=self.readout_metadata
).dumps(),
)
logger.info(
f"LamNI statistics: Average of all standard deviations: x {self.average_stdeviations_x_st_fzp/number_of_samples_to_read}, y {self.average_stdeviations_y_st_fzp/number_of_samples_to_read}, angle {self.average_lamni_angle/number_of_samples_to_read}."
)
def publish_device_data(self, signals, point_id):
self.get_device_manager().connector.set_and_publish(
MessageEndpoints.device_read("rt_lamni"),
messages.DeviceMessage(
signals=signals, metadata={"point_id": point_id, **self.readout_metadata}
).dumps(),
)
def feedback_status_angle_lamni(self) -> bool:
return_table = (self.socket_put_and_receive(f"J7")).split(",")
logger.debug(
f"LamNI angle interferomter status {bool(return_table[0])}, position {float(return_table[1])}, signal {float(return_table[2])}"
)
return bool(return_table[0])
def feedback_enable_with_reset(self):
if not self.feedback_status_angle_lamni():
self.feedback_disable_and_even_reset_lamni_angle_interferometer()
logger.info(f"LamNI resetting interferometer inclusive angular interferomter.")
else:
self.feedback_disable()
logger.info(
f"LamNI resetting interferomter except angular interferometer which is already running."
)
# set these as closed loop target position
self.socket_put(f"pa0,0")
self.get_axis_by_name("rtx").user_setpoint.setpoint = 0
self.socket_put(f"pa1,0")
self.get_axis_by_name("rty").user_setpoint.setpoint = 0
self.socket_put(
f"pa2,0"
) # we set all three outputs of the traj. gen. although in LamNI case only 0,1 are used
self.clear_trajectory_generator()
self.get_device_manager().devices.lsamrot.obj.move(0, wait=True)
galil_controller_rt_status = (
self.get_device_manager().devices.lsamx.obj.controller.lgalil_is_air_off_and_orchestra_enabled()
)
if galil_controller_rt_status == 0:
logger.error(
"Cannot enable feedback. The small rotation air is on and/or orchestra disabled by the motor controller."
)
raise RtError(
"Cannot enable feedback. The small rotation air is on and/or orchestra disabled by the motor controller."
)
time.sleep(0.03)
lsamx_user_params = self.get_device_manager().devices.lsamx.user_parameter
if lsamx_user_params is None or lsamx_user_params.get("center") is None:
raise RuntimeError("lsamx center is not defined")
lsamy_user_params = self.get_device_manager().devices.lsamy.user_parameter
if lsamy_user_params is None or lsamy_user_params.get("center") is None:
raise RuntimeError("lsamy center is not defined")
lsamx_center = lsamx_user_params.get("center")
lsamy_center = lsamy_user_params.get("center")
self.get_device_manager().devices.lsamx.obj.move(lsamx_center, wait=True)
self.get_device_manager().devices.lsamy.obj.move(lsamy_center, wait=True)
self.socket_put("J1")
_waitforfeedbackctr = 0
interferometer_feedback_not_running = int((self.socket_put_and_receive("J2")).split(",")[0])
while interferometer_feedback_not_running == 1 and _waitforfeedbackctr < 100:
time.sleep(0.01)
_waitforfeedbackctr = _waitforfeedbackctr + 1
interferometer_feedback_not_running = int(
(self.socket_put_and_receive("J2")).split(",")[0]
)
self.set_device_enabled("lsamx", False)
self.set_device_enabled("lsamy", False)
self.set_device_enabled("loptx", False)
self.set_device_enabled("lopty", False)
self.set_device_enabled("loptz", False)
if interferometer_feedback_not_running == 1:
logger.error(
"Cannot start scan because feedback loop is not running or there is an interferometer error."
)
raise RtError(
"Cannot start scan because feedback loop is not running or there is an interferometer error."
)
time.sleep(0.01)
# ptychography_alignment_done = 0
def set_device_enabled(self, device_name: str, enabled: bool) -> None:
"""enable / disable a device"""
if device_name not in self.get_device_manager().devices:
logger.warning(
f"Device {device_name} is not configured and cannot be enabled/disabled."
)
return
self.get_device_manager().devices[device_name].read_only = not enabled
class RtSignalBase(SocketSignal):
def __init__(self, signal_name, **kwargs):
self.signal_name = signal_name
super().__init__(**kwargs)
self.controller = self.parent.controller
self.sock = self.parent.controller.sock
class RtSignalRO(RtSignalBase):
def __init__(self, signal_name, **kwargs):
super().__init__(signal_name, **kwargs)
self._metadata["write_access"] = False
def _socket_set(self, val):
raise ReadOnlyError("Read-only signals cannot be set")
class RtReadbackSignal(RtSignalRO):
@retry_once
@threadlocked
def _socket_get(self) -> float:
"""Get command for the readback signal
Returns:
float: Readback value after adjusting for sign and motor resolution.
"""
return_table = (self.controller.socket_put_and_receive(f"J4")).split(",")
print(return_table)
if self.parent.axis_Id_numeric == 0:
readback_index = 2
elif self.parent.axis_Id_numeric == 1:
readback_index = 1
else:
raise RtError("Currently, only two axes are supported.")
current_pos = float(return_table[readback_index])
current_pos *= self.parent.sign
return current_pos
class RtSetpointSignal(RtSignalBase):
setpoint = 0
def _socket_get(self) -> float:
"""Get command for receiving the setpoint / target value.
The value is not pulled from the controller but instead just the last setpoint used.
Returns:
float: setpoint / target value
"""
return self.setpoint
@retry_once
@threadlocked
def _socket_set(self, val: float) -> None:
"""Set a new target value / setpoint value. Before submission, the target value is adjusted for the axis' sign.
Furthermore, it is ensured that all axes are referenced before a new setpoint is submitted.
Args:
val (float): Target value / setpoint value
Raises:
RtError: Raised if interferometer feedback is disabled.
"""
interferometer_feedback_not_running = int(
(self.controller.socket_put_and_receive("J2")).split(",")[0]
)
if interferometer_feedback_not_running != 0:
raise RtError(
"The interferometer feedback is not running. Either it is turned off or and interferometer error occured."
)
self.set_with_feedback_disabled(val)
def set_with_feedback_disabled(self, val):
target_val = val * self.parent.sign
self.setpoint = target_val
self.controller.socket_put(f"pa{self.parent.axis_Id_numeric},{target_val:.4f}")
class RtMotorIsMoving(RtSignalRO):
def _socket_get(self):
return self.controller.is_axis_moving(self.parent.axis_Id_numeric)
def get(self):
val = super().get()
if val is not None:
self._run_subs(sub_type=self.SUB_VALUE, value=val, timestamp=time.time())
return val
class RtFeedbackRunning(RtSignalRO):
@threadlocked
def _socket_get(self):
if int((self.controller.socket_put_and_receive("J2")).split(",")[0]) == 0:
return 1
else:
return 0
class RtMotor(Device, PositionerBase):
USER_ACCESS = ["controller"]
readback = Cpt(RtReadbackSignal, signal_name="readback", kind="hinted")
user_setpoint = Cpt(RtSetpointSignal, signal_name="setpoint")
motor_is_moving = Cpt(RtMotorIsMoving, signal_name="motor_is_moving", kind="normal")
high_limit_travel = Cpt(Signal, value=0, kind="omitted")
low_limit_travel = Cpt(Signal, value=0, kind="omitted")
SUB_READBACK = "readback"
SUB_CONNECTION_CHANGE = "connection_change"
_default_sub = SUB_READBACK
def __init__(
self,
axis_Id,
prefix="",
*,
name,
kind=None,
read_attrs=None,
configuration_attrs=None,
parent=None,
host="mpc2680.psi.ch",
port=3333,
sign=1,
socket_cls=SocketIO,
device_manager=None,
limits=None,
**kwargs,
):
self.axis_Id = axis_Id
self.sign = sign
self.controller = RtController(socket=socket_cls(host=host, port=port))
self.controller.set_axis(axis=self, axis_nr=self.axis_Id_numeric)
self.device_manager = device_manager
self.tolerance = kwargs.pop("tolerance", 0.5)
super().__init__(
prefix,
name=name,
kind=kind,
read_attrs=read_attrs,
configuration_attrs=configuration_attrs,
parent=parent,
**kwargs,
)
self.readback.name = self.name
self.controller.subscribe(
self._update_connection_state, event_type=self.SUB_CONNECTION_CHANGE
)
self._update_connection_state()
# self.readback.subscribe(self._forward_readback, event_type=self.readback.SUB_VALUE)
if limits is not None:
assert len(limits) == 2
self.low_limit_travel.put(limits[0])
self.high_limit_travel.put(limits[1])
@property
def limits(self):
return (self.low_limit_travel.get(), self.high_limit_travel.get())
@property
def low_limit(self):
return self.limits[0]
@property
def high_limit(self):
return self.limits[1]
def check_value(self, pos):
"""Check that the position is within the soft limits"""
low_limit, high_limit = self.limits
if low_limit < high_limit and not (low_limit <= pos <= high_limit):
raise LimitError(f"position={pos} not within limits {self.limits}")
def _update_connection_state(self, **kwargs):
for walk in self.walk_signals():
walk.item._metadata["connected"] = self.controller.connected
def _forward_readback(self, **kwargs):
kwargs.pop("sub_type")
self._run_subs(sub_type="readback", **kwargs)
@raise_if_disconnected
def move(self, position, wait=True, **kwargs):
"""Move to a specified position, optionally waiting for motion to
complete.
Parameters
----------
position
Position to move to
moved_cb : callable
Call this callback when movement has finished. This callback must
accept one keyword argument: 'obj' which will be set to this
positioner instance.
timeout : float, optional
Maximum time to wait for the motion. If None, the default timeout
for this positioner is used.
Returns
-------
status : MoveStatus
Raises
------
TimeoutError
When motion takes longer than `timeout`
ValueError
On invalid positions
RuntimeError
If motion fails other than timing out
"""
self._started_moving = False
timeout = kwargs.pop("timeout", 100)
status = super().move(position, timeout=timeout, **kwargs)
self.user_setpoint.put(position, wait=False)
def move_and_finish():
while self.motor_is_moving.get():
print("motor is moving")
val = self.readback.read()
self._run_subs(sub_type=self.SUB_READBACK, value=val, timestamp=time.time())
time.sleep(0.01)
print("Move finished")
self._done_moving()
threading.Thread(target=move_and_finish, daemon=True).start()
try:
if wait:
status_wait(status)
except KeyboardInterrupt:
self.stop()
raise
return status
@property
def axis_Id(self):
return self._axis_Id_alpha
@axis_Id.setter
def axis_Id(self, val):
if isinstance(val, str):
if len(val) != 1:
raise ValueError(f"Only single-character axis_Ids are supported.")
self._axis_Id_alpha = val
self._axis_Id_numeric = ord(val.lower()) - 97
else:
raise TypeError(f"Expected value of type str but received {type(val)}")
@property
def axis_Id_numeric(self):
return self._axis_Id_numeric
@axis_Id_numeric.setter
def axis_Id_numeric(self, val):
if isinstance(val, int):
if val > 26:
raise ValueError(f"Numeric value exceeds supported range.")
self._axis_Id_alpha = val
self._axis_Id_numeric = (chr(val + 97)).capitalize()
else:
raise TypeError(f"Expected value of type int but received {type(val)}")
def kickoff(self, metadata, **kwargs) -> None:
self.controller.kickoff(metadata)
@property
def egu(self):
"""The engineering units (EGU) for positions"""
return "um"
# how is this used later?
def stage(self) -> List[object]:
return super().stage()
def unstage(self) -> List[object]:
return super().unstage()
def stop(self, *, success=False):
self.controller.stop_all_axes()
return super().stop(success=success)
if __name__ == "__main__":
logging.basicConfig(level=logging.DEBUG)
mock = False
if not mock:
rty = RtLamniMotor("B", name="rty", host="mpc2680.psi.ch", port=3333, sign=1)
rty.stage()
status = rty.move(0, wait=True)
status = rty.move(10, wait=True)
rty.read()
rty.get()
rty.describe()
rty.unstage()
else:
from ophyd_devices.utils.socket import SocketMock
rtx = RtLamniMotor("A", name="rtx", host="mpc2680.psi.ch", port=3333, socket_cls=SocketMock)
rty = RtLamniMotor("B", name="rty", host="mpc2680.psi.ch", port=3333, socket_cls=SocketMock)
rtx.stage()
# rty.stage()

View File

@@ -111,10 +111,6 @@ class SmaractController(Controller):
def socket_put(self, val: str):
self.sock.put(f":{val}\n".encode())
@threadlocked
def socket_get(self):
return self.sock.receive().decode()
@threadlocked
def socket_put_and_receive(
self, val: str, remove_trailing_chars=True, check_for_errors=True, raise_if_not_status=False

View File

@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any
import numpy as np
if TYPE_CHECKING:
from bec_lib import DeviceManagerBase
from bec_lib.devicemanager import DeviceManagerBase
from bec_server.file_writer.file_writer import HDF5Storage

View File

@@ -23,7 +23,8 @@ but they are executed in a specific order:
import time
import numpy as np
from bec_lib import MessageEndpoints, bec_logger
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

View File

@@ -23,7 +23,8 @@ but they are executed in a specific order:
import time
import numpy as np
from bec_lib import MessageEndpoints, bec_logger, messages
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 SyncFlyScanBase

View File

@@ -22,7 +22,8 @@ but they are executed in a specific order:
import time
from bec_lib import MessageEndpoints, bec_logger
from bec_lib import bec_logger
from bec_lib.endpoints import MessageEndpoints
from bec_server.scan_server.scans import AsyncFlyScanBase, ScanAbortion
logger = bec_logger.logger
@@ -32,7 +33,7 @@ class OwisGrid(AsyncFlyScanBase):
"""Owis-based grid scan."""
scan_name = "owis_grid"
scan_report_hint = "scan_progress"
scan_report_hint = "device_progress"
required_kwargs = []
arg_input = {}
arg_bundle_size = {"bundle": len(arg_input), "min": None, "max": None}
@@ -153,11 +154,11 @@ class OwisGrid(AsyncFlyScanBase):
if not self.scan_report_hint:
yield None
return
yield from self.stubs.scan_report_instruction({"scan_progress": ["mcs"]})
yield from self.stubs.scan_report_instruction({"device_progress": ["mcs"]})
def pre_scan(self):
"""Pre scan instructions, move to start position"""
yield from self._move_and_wait([self.start_x, self.start_y])
yield from self._move_scan_motors_and_wait([self.start_x, self.start_y])
yield from self.stubs.pre_scan()
def scan_progress(self) -> int:
@@ -307,3 +308,11 @@ class OwisGrid(AsyncFlyScanBase):
yield from self.stubs.send_rpc_and_wait("samy", "velocity.put", self.high_velocity)
yield from self.stubs.send_rpc_and_wait("samy", "acceleration.put", self.high_acc_time)
super().finalize()
def _move_scan_motors_and_wait(self, pos):
# TODO: remove this method once BEC MR 637 is merged
# pylint: disable=no-member
if hasattr(super(), "_move_scan_motors_and_wait"):
yield from super()._move_scan_motors_and_wait(pos)
else:
yield from self._move_and_wait(pos)

View File

@@ -25,7 +25,8 @@ but they are executed in a specific order:
# import numpy as np
# from bec_lib import MessageEndpoints, bec_logger, messages
# 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

View File

@@ -22,7 +22,8 @@ but they are executed in a specific order:
import time
from bec_lib import MessageEndpoints, bec_logger
from bec_lib import bec_logger
from bec_lib.endpoints import MessageEndpoints
from bec_server.scan_server.scans import AsyncFlyScanBase
logger = bec_logger.logger
@@ -30,7 +31,7 @@ logger = bec_logger.logger
class SgalilGrid(AsyncFlyScanBase):
scan_name = "sgalil_grid"
scan_report_hint = "scan_progress"
scan_report_hint = "device_progress"
required_kwargs = []
arg_input = {}
arg_bundle_size = {"bundle": len(arg_input), "min": None, "max": None}
@@ -96,10 +97,10 @@ class SgalilGrid(AsyncFlyScanBase):
if not self.scan_report_hint:
yield None
return
yield from self.stubs.scan_report_instruction({"scan_progress": ["mcs"]})
yield from self.stubs.scan_report_instruction({"device_progress": ["mcs"]})
def pre_scan(self):
yield from self._move_and_wait([self.start_x, self.start_y])
yield from self._move_scan_motors_and_wait([self.start_x, self.start_y])
yield from self.stubs.pre_scan()
# TODO move to start position
@@ -214,3 +215,11 @@ class SgalilGrid(AsyncFlyScanBase):
# logger.info('Testing Scan abortion, would have raised here!')
# except Exception as exc:
# logger.info(f'{exc}')
def _move_scan_motors_and_wait(self, pos):
# TODO: remove this method once BEC MR 637 is merged
# pylint: disable=no-member
if hasattr(super(), "_move_scan_motors_and_wait"):
yield from super()._move_scan_motors_and_wait(pos)
else:
yield from self._move_and_wait(pos)

20
docs/Makefile Normal file
View File

@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@@ -0,0 +1,34 @@
{{ fullname | escape | underline}}
.. currentmodule:: {{ module }}
.. autoclass:: {{ objname }}
:members:
:show-inheritance:
:inherited-members:
:special-members: __call__, __add__, __mul__
{% block methods %}
{% if methods %}
.. rubric:: {{ _('Methods') }}
.. autosummary::
:nosignatures:
{% for item in methods %}
{%- if not item.startswith('_') %}
~{{ name }}.{{ item }}
{%- endif -%}
{%- endfor %}
{% endif %}
{% endblock %}
{% block attributes %}
{% if attributes %}
.. rubric:: {{ _('Attributes') }}
.. autosummary::
{% for item in attributes %}
~{{ name }}.{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,66 @@
{{ fullname | escape | underline}}
.. automodule:: {{ fullname }}
{% block attributes %}
{% if attributes %}
.. rubric:: Module attributes
.. autosummary::
:toctree:
{% for item in attributes %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block functions %}
{% if functions %}
.. rubric:: {{ _('Functions') }}
.. autosummary::
:toctree:
:nosignatures:
{% for item in functions %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block classes %}
{% if classes %}
.. rubric:: {{ _('Classes') }}
.. autosummary::
:toctree:
:template: custom-class-template.rst
:nosignatures:
{% for item in classes %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block exceptions %}
{% if exceptions %}
.. rubric:: {{ _('Exceptions') }}
.. autosummary::
:toctree:
{% for item in exceptions %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}
{% block modules %}
{% if modules %}
.. autosummary::
:toctree:
:template: custom-module-template.rst
:recursive:
{% for item in modules %}
{{ item }}
{%- endfor %}
{% endif %}
{% endblock %}

81
docs/conf.py Normal file
View File

@@ -0,0 +1,81 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
import os
import pathlib
import tomli
project = "cSAXS"
copyright = "2023, Paul Scherrer Institute"
author = "Paul Scherrer Institute"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
current_path = pathlib.Path(__file__).parent.parent.resolve()
version_path = f"{current_path}/pyproject.toml"
def get_version():
"""load the version from the version file"""
with open(version_path, "r", encoding="utf-8") as file:
res = tomli.loads(file.read())
return res["project"]["version"]
release = get_version()
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
# "sphinx.ext.coverage",
"sphinx.ext.viewcode",
"sphinx.ext.napoleon",
"sphinx_toolbox.collapse",
"sphinx_copybutton",
"myst_parser",
"sphinx_design",
]
myst_enable_extensions = [
"amsmath",
"attrs_inline",
"colon_fence",
"deflist",
"dollarmath",
"fieldlist",
"html_admonition",
"html_image",
"replacements",
"smartquotes",
"strikethrough",
"substitution",
"tasklist",
]
autosummary_generate = True # Turn on sphinx.ext.autosummary
add_module_names = False # Remove namespaces from class/method signatures
autodoc_inherit_docstrings = True # If no docstring, inherit from base class
set_type_checking_flag = True # Enable 'expensive' imports for sphinx_autodoc_typehints
autoclass_content = "both" # Include both class docstring and __init__
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
language = "Python"
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = "pydata_sphinx_theme"
html_static_path = ["_static"]
html_css_files = ["css/custom.css"]
html_logo = "_static/bec.png"
html_theme_options = {"show_nav_level": 1, "navbar_align": "content"}

View File

@@ -0,0 +1,11 @@
(developer)=
# Development
```{toctree}
---
maxdepth: 1
hidden: true
---
reference/
```

View File

@@ -0,0 +1,10 @@
## API Reference
```{eval-rst}
.. autosummary::
:toctree: _autosummary
:template: custom-module-template.rst
:recursive:
csaxs_bec
```

39
docs/index.md Normal file
View File

@@ -0,0 +1,39 @@
# cSAXS documentation
<br><br>
````{grid} 3
:gutter: 5
```{grid-item-card} Introduction
:link: introduction
:link-type: ref
General information.
```
```{grid-item-card} User
:link: user
:link-type: ref
Information for users.
```
```{grid-item-card} Developer
:link: developer
:link-type: ref
Information for developers.
```
````
```{toctree}
---
numbered: true
maxdepth: 1
---
introduction/introduction
user/user
developer/developer

View File

@@ -0,0 +1,2 @@
(introduction)=
# Introduction

35
docs/make.bat Normal file
View File

@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd

9
docs/requirements.txt Normal file
View File

@@ -0,0 +1,9 @@
sphinx
sphinx_copybutton
recommonmark
sphinx-toolbox
pydata-sphinx-theme
sphinx-copybutton
myst-parser
sphinx-design
tomli

2
docs/user/user.md Normal file
View File

@@ -0,0 +1,2 @@
(user)=
# User

View File

@@ -2,9 +2,9 @@
from unittest import mock
import pytest
from ophyd_devices.epics.devices.psi_delay_generator_base import TriggerSource
from ophyd_devices.interfaces.base_classes.psi_delay_generator_base import TriggerSource
from csaxs_bec.devices.epics.devices.delay_generator_csaxs import DDGSetup
from csaxs_bec.devices.epics.delay_generator_csaxs import DDGSetup
def patch_dual_pvs(device):

View File

@@ -4,11 +4,12 @@ from unittest import mock
import ophyd
import pytest
from bec_lib import MessageEndpoints, messages
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from bec_server.device_server.tests.utils import DMMock
from ophyd_devices.tests.utils import MockPV
from csaxs_bec.devices.epics.devices.eiger9m_csaxs import Eiger9McSAXS
from csaxs_bec.devices.epics.eiger9m_csaxs import Eiger9McSAXS
def patch_dual_pvs(device):
@@ -29,9 +30,9 @@ def mock_det():
dm = DMMock()
with mock.patch.object(dm, "connector"):
with (
mock.patch("ophyd_devices.epics.devices.psi_detector_base.FileWriter"),
mock.patch("ophyd_devices.interfaces.base_classes.psi_detector_base.FileWriter"),
mock.patch(
"ophyd_devices.epics.devices.psi_detector_base.PSIDetectorBase._update_service_config"
"ophyd_devices.interfaces.base_classes.psi_detector_base.PSIDetectorBase._update_service_config"
),
):
with mock.patch.object(ophyd, "cl") as mock_cl:
@@ -53,22 +54,22 @@ def test_init():
dm = DMMock()
with mock.patch.object(dm, "connector"):
with (
mock.patch("ophyd_devices.epics.devices.psi_detector_base.FileWriter"),
mock.patch("ophyd_devices.interfaces.base_classes.psi_detector_base.FileWriter"),
mock.patch(
"ophyd_devices.epics.devices.psi_detector_base.PSIDetectorBase._update_service_config"
"ophyd_devices.interfaces.base_classes.psi_detector_base.PSIDetectorBase._update_service_config"
),
):
with mock.patch.object(ophyd, "cl") as mock_cl:
mock_cl.get_pv = MockPV
with (
mock.patch(
"csaxs_bec.devices.epics.devices.eiger9m_csaxs.Eiger9MSetup.initialize_default_parameter"
"csaxs_bec.devices.epics.eiger9m_csaxs.Eiger9MSetup.initialize_default_parameter"
) as mock_default,
mock.patch(
"csaxs_bec.devices.epics.devices.eiger9m_csaxs.Eiger9MSetup.initialize_detector"
"csaxs_bec.devices.epics.eiger9m_csaxs.Eiger9MSetup.initialize_detector"
) as mock_init_det,
mock.patch(
"csaxs_bec.devices.epics.devices.eiger9m_csaxs.Eiger9MSetup.initialize_detector_backend"
"csaxs_bec.devices.epics.eiger9m_csaxs.Eiger9MSetup.initialize_detector_backend"
) as mock_init_backend,
):
Eiger9McSAXS(name=name, prefix=prefix, device_manager=dm, sim_mode=sim_mode)
@@ -146,7 +147,7 @@ def test_initialize_detector_backend(
Validation upon checking set values in mocked std_daq instance
"""
with mock.patch("csaxs_bec.devices.epics.devices.eiger9m_csaxs.StdDaqClient") as mock_std_daq:
with mock.patch("csaxs_bec.devices.epics.eiger9m_csaxs.StdDaqClient") as mock_std_daq:
instance = mock_std_daq.return_value
instance.stop_writer.return_value = None
instance.get_status.return_value = daq_status

View File

@@ -5,11 +5,12 @@ from unittest import mock
import ophyd
import pytest
from bec_lib import MessageEndpoints, messages
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from bec_server.device_server.tests.utils import DMMock
from ophyd_devices.tests.utils import MockPV
from csaxs_bec.devices.epics.devices.falcon_csaxs import FalconcSAXS, FalconTimeoutError
from csaxs_bec.devices.epics.falcon_csaxs import FalconcSAXS, FalconTimeoutError
def patch_dual_pvs(device):
@@ -30,9 +31,11 @@ def mock_det():
dm = DMMock()
with mock.patch.object(dm, "connector"):
with (
mock.patch("ophyd_devices.epics.devices.psi_detector_base.FileWriter") as filemixin,
mock.patch(
"ophyd_devices.epics.devices.psi_detector_base.PSIDetectorBase._update_service_config"
"ophyd_devices.interfaces.base_classes.psi_detector_base.FileWriter"
) as filemixin,
mock.patch(
"ophyd_devices.interfaces.base_classes.psi_detector_base.PSIDetectorBase._update_service_config"
) as mock_service_config,
):
with mock.patch.object(ophyd, "cl") as mock_cl:

View File

@@ -3,7 +3,7 @@ from unittest import mock
import pytest
from ophyd_devices.tests.utils import SocketMock
from csaxs_bec.devices.galil.fupr_ophyd import FuprGalilController, FuprGalilMotor
from csaxs_bec.devices.omny.galil.fupr_ophyd import FuprGalilController, FuprGalilMotor
@pytest.fixture

View File

@@ -1,15 +1,16 @@
import copy
from unittest import mock
import pytest
from ophyd_devices.tests.utils import SocketMock
from csaxs_bec.devices.galil.galil_ophyd import GalilController, GalilMotor
from csaxs_bec.devices.omny.galil.lgalil_ophyd import LamniGalilController, LamniGalilMotor
@pytest.fixture(scope="function")
def leyey():
GalilController._reset_controller()
leyey_motor = GalilMotor(
LamniGalilController._reset_controller()
leyey_motor = LamniGalilMotor(
"H", name="leyey", host="mpc2680.psi.ch", port=8081, socket_cls=SocketMock
)
leyey_motor.controller.on()
@@ -20,8 +21,8 @@ def leyey():
@pytest.fixture(scope="function")
def leyex():
GalilController._reset_controller()
leyex_motor = GalilMotor(
LamniGalilController._reset_controller()
leyex_motor = LamniGalilMotor(
"A", name="leyey", host="mpc2680.psi.ch", port=8081, socket_cls=SocketMock
)
leyex_motor.controller.on()
@@ -60,7 +61,7 @@ def test_axis_get(leyey, pos, msg, sign):
)
def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
leyey.controller.sock.flush_buffer()
leyey.controller.sock.buffer_recv = socket_get_messages
leyey.controller.sock.buffer_recv = copy.deepcopy(socket_get_messages)
leyey.user_setpoint.put(target_pos)
assert leyey.controller.sock.buffer_put == socket_put_messages
@@ -104,7 +105,7 @@ def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
)
def test_drive_axis_to_limit(leyex, axis_nr, direction, socket_put_messages, socket_get_messages):
leyex.controller.sock.flush_buffer()
leyex.controller.sock.buffer_recv = socket_get_messages
leyex.controller.sock.buffer_recv = copy.deepcopy(socket_get_messages)
leyex.controller.drive_axis_to_limit(axis_nr, direction)
assert leyex.controller.sock.buffer_put == socket_put_messages
@@ -144,6 +145,9 @@ def test_drive_axis_to_limit(leyex, axis_nr, direction, socket_put_messages, soc
)
def test_find_reference(leyex, axis_nr, socket_put_messages, socket_get_messages):
leyex.controller.sock.flush_buffer()
leyex.controller.sock.buffer_recv = socket_get_messages
leyex.controller.find_reference(axis_nr)
leyex.controller.sock.buffer_recv = copy.deepcopy(socket_get_messages)
try:
leyex.controller.find_reference(axis_nr)
except Exception as e:
print(e)
assert leyex.controller.sock.buffer_put == socket_put_messages

View File

@@ -1,9 +1,9 @@
from unittest import mock
import copy
import pytest
from ophyd_devices.tests.utils import SocketMock
from csaxs_bec.devices.galil.fgalil_ophyd import FlomniGalilController, FlomniGalilMotor
from csaxs_bec.devices.omny.galil.fgalil_ophyd import FlomniGalilController, FlomniGalilMotor
@pytest.fixture(scope="function")
@@ -60,7 +60,7 @@ def test_axis_get(leyey, pos, msg, sign):
)
def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
leyey.controller.sock.flush_buffer()
leyey.controller.sock.buffer_recv = socket_get_messages
leyey.controller.sock.buffer_recv = copy.deepcopy(socket_get_messages)
leyey.user_setpoint.put(target_pos)
assert leyey.controller.sock.buffer_put == socket_put_messages
@@ -104,7 +104,7 @@ def test_axis_put(leyey, target_pos, socket_put_messages, socket_get_messages):
)
def test_drive_axis_to_limit(leyex, axis_nr, direction, socket_put_messages, socket_get_messages):
leyex.controller.sock.flush_buffer()
leyex.controller.sock.buffer_recv = socket_get_messages
leyex.controller.sock.buffer_recv = copy.deepcopy(socket_get_messages)
leyex.controller.drive_axis_to_limit(axis_nr, direction)
assert leyex.controller.sock.buffer_put == socket_put_messages
@@ -144,7 +144,7 @@ def test_drive_axis_to_limit(leyex, axis_nr, direction, socket_put_messages, soc
)
def test_find_reference(leyex, axis_nr, socket_put_messages, socket_get_messages):
leyex.controller.sock.flush_buffer()
leyex.controller.sock.buffer_recv = socket_get_messages
leyex.controller.sock.buffer_recv = copy.deepcopy(socket_get_messages)
leyex.controller.find_reference(axis_nr)
assert leyex.controller.sock.buffer_put == socket_put_messages
@@ -165,7 +165,7 @@ def test_fosaz_light_curtain_is_triggered(
)
fosaz.controller.on()
fosaz.controller.sock.flush_buffer()
fosaz.controller.sock.buffer_recv = socket_get_messages
fosaz.controller.sock.buffer_recv = copy.deepcopy(socket_get_messages)
assert fosaz.controller.fosaz_light_curtain_is_triggered() == triggered
assert fosaz.controller.sock.buffer_put == socket_put_messages
fosaz.controller.off()

View File

@@ -4,11 +4,12 @@ from unittest import mock
import ophyd
import pytest
from bec_lib import MessageEndpoints, messages
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from bec_server.device_server.tests.utils import DMMock
from ophyd_devices.tests.utils import MockPV
from csaxs_bec.devices.epics.devices.mcs_csaxs import (
from csaxs_bec.devices.epics.mcs_csaxs import (
MCScSAXS,
MCSError,
MCSTimeoutError,
@@ -35,9 +36,11 @@ def mock_det():
dm = DMMock()
with mock.patch.object(dm, "connector"):
with (
mock.patch("ophyd_devices.epics.devices.psi_detector_base.FileWriter") as filemixin,
mock.patch(
"ophyd_devices.epics.devices.psi_detector_base.PSIDetectorBase._update_service_config"
"ophyd_devices.interfaces.base_classes.psi_detector_base.FileWriter"
) as filemixin,
mock.patch(
"ophyd_devices.interfaces.base_classes.psi_detector_base.PSIDetectorBase._update_service_config"
) as mock_service_config,
):
with mock.patch.object(ophyd, "cl") as mock_cl:
@@ -57,22 +60,22 @@ def test_init():
dm = DMMock()
with mock.patch.object(dm, "connector"):
with (
mock.patch("ophyd_devices.epics.devices.psi_detector_base.FileWriter"),
mock.patch("ophyd_devices.interfaces.base_classes.psi_detector_base.FileWriter"),
mock.patch(
"ophyd_devices.epics.devices.psi_detector_base.PSIDetectorBase._update_service_config"
"ophyd_devices.interfaces.base_classes.psi_detector_base.PSIDetectorBase._update_service_config"
),
):
with mock.patch.object(ophyd, "cl") as mock_cl:
mock_cl.get_pv = MockPV
with (
mock.patch(
"csaxs_bec.devices.epics.devices.mcs_csaxs.MCSSetup.initialize_default_parameter"
"csaxs_bec.devices.epics.mcs_csaxs.MCSSetup.initialize_default_parameter"
) as mock_default,
mock.patch(
"csaxs_bec.devices.epics.devices.mcs_csaxs.MCSSetup.initialize_detector"
"csaxs_bec.devices.epics.mcs_csaxs.MCSSetup.initialize_detector"
) as mock_init_det,
mock.patch(
"csaxs_bec.devices.epics.devices.mcs_csaxs.MCSSetup.initialize_detector_backend"
"csaxs_bec.devices.epics.mcs_csaxs.MCSSetup.initialize_detector_backend"
) as mock_init_backend,
):
MCScSAXS(name=name, prefix=prefix, device_manager=dm, sim_mode=sim_mode)

View File

@@ -1,3 +1,5 @@
import copy
import pytest
from csaxs_bec.devices.npoint import NPointAxis, NPointController
@@ -121,7 +123,12 @@ def test_set_axis_out_of_range():
],
)
def test_hex_list_to_int(in_buffer, byteorder, signed, val):
assert NPointController._hex_list_to_int(in_buffer, byteorder=byteorder, signed=signed) == val
assert (
NPointController._hex_list_to_int(
copy.deepcopy(in_buffer), byteorder=byteorder, signed=signed
)
== val
)
@pytest.mark.parametrize(

View File

@@ -5,11 +5,12 @@ from unittest import mock
import ophyd
import pytest
from bec_lib import MessageEndpoints, messages
from bec_lib import messages
from bec_lib.endpoints import MessageEndpoints
from bec_server.device_server.tests.utils import DMMock
from ophyd_devices.tests.utils import MockPV
from csaxs_bec.devices.epics.devices.pilatus_csaxs import PilatuscSAXS
from csaxs_bec.devices.epics.pilatus_csaxs import PilatuscSAXS
def patch_dual_pvs(device):
@@ -30,9 +31,9 @@ def mock_det():
dm = DMMock()
with mock.patch.object(dm, "connector"):
with (
mock.patch("ophyd_devices.epics.devices.psi_detector_base.FileWriter"),
mock.patch("ophyd_devices.interfaces.base_classes.psi_detector_base.FileWriter"),
mock.patch(
"ophyd_devices.epics.devices.psi_detector_base.PSIDetectorBase._update_service_config"
"ophyd_devices.interfaces.base_classes.psi_detector_base.PSIDetectorBase._update_service_config"
),
):
with mock.patch.object(ophyd, "cl") as mock_cl:

View File

@@ -3,12 +3,13 @@ from unittest import mock
import pytest
from ophyd_devices.tests.utils import SocketMock
from csaxs_bec.devices.rt_lamni import RtFlomniController, RtFlomniMotor
from csaxs_bec.devices.rt_lamni.rt_ophyd import RtError
from csaxs_bec.devices.omny.rt import RtFlomniController, RtFlomniMotor
from csaxs_bec.devices.omny.rt.rt_ophyd import RtError
@pytest.fixture()
def rt_flomni():
RtFlomniController._reset_controller()
rt_flomni = RtFlomniController(
name="rt_flomni", socket_cls=SocketMock, socket_host="localhost", socket_port=8081
)
@@ -16,14 +17,21 @@ def rt_flomni():
with mock.patch.object(rt_flomni, "sock"):
rtx = mock.MagicMock(spec=RtFlomniMotor)
rtx.name = "rtx"
rtx.axis_Id = "A"
rtx.axis_Id_numeric = 0
rty = mock.MagicMock(spec=RtFlomniMotor)
rty.name = "rty"
rty.axis_Id = "B"
rty.axis_Id_numeric = 1
rtz = mock.MagicMock(spec=RtFlomniMotor)
rtz.name = "rtz"
rt_flomni.set_axis(rtx, 0)
rt_flomni.set_axis(rty, 1)
rt_flomni.set_axis(rtz, 2)
rtz.axis_Id = "C"
rtz.axis_Id_numeric = 2
rt_flomni.set_axis(axis=rtx, axis_nr=0)
rt_flomni.set_axis(axis=rty, axis_nr=1)
rt_flomni.set_axis(axis=rtz, axis_nr=2)
yield rt_flomni
RtFlomniController._reset_controller()
def test_rt_flomni_move_to_zero(rt_flomni):