Merge branch 'feature-first-epics-devices' into 'master'
First EPICS devices from controls repo See merge request bec/ophyd_devices!1
This commit is contained in:
commit
e119f122de
4
.gitignore
vendored
4
.gitignore
vendored
@ -5,4 +5,6 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
**/out
|
**/out
|
||||||
**/.vscode
|
**/.vscode
|
||||||
**/.pytest_cache
|
**/.pytest_cache
|
||||||
|
*.egg-info
|
||||||
|
|
||||||
|
66
ophyd_devices/epics/DeviceFactory.py
Normal file
66
ophyd_devices/epics/DeviceFactory.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Wed Oct 13 17:06:51 2021
|
||||||
|
|
||||||
|
@author: mohacsi_i
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
from ophyd.ophydobj import OphydObject
|
||||||
|
|
||||||
|
from devices import *
|
||||||
|
|
||||||
|
# ####################################################
|
||||||
|
# Test connection to beamline devices
|
||||||
|
# ####################################################
|
||||||
|
# Current file path
|
||||||
|
path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
# Load simulated device database
|
||||||
|
fp = open(f"{path}/db/test_database.yml", "r")
|
||||||
|
lut_db = yaml.load(fp, Loader=yaml.Loader)
|
||||||
|
|
||||||
|
# Load beamline specific database
|
||||||
|
bl = os.getenv("BEAMLINE_XNAME", "TESTENV")
|
||||||
|
if bl != "TESTENV":
|
||||||
|
fp = open(f"{path}/db/{bl.lower()}_database.yml", "r")
|
||||||
|
lut_db.update(yaml.load(fp, Loader=yaml.Loader))
|
||||||
|
|
||||||
|
|
||||||
|
def createProxy(name: str, connect=True) -> OphydObject:
|
||||||
|
"""Create an ophyd device from its alias
|
||||||
|
|
||||||
|
Factory routine that uses the beamline database to instantiate
|
||||||
|
ophyd devices from their alias and pre-defined configuration.
|
||||||
|
Does nothing if the device is already an OphydObject!
|
||||||
|
"""
|
||||||
|
if issubclass(type(name), OphydObject):
|
||||||
|
return name
|
||||||
|
|
||||||
|
entry = lut_db[name]
|
||||||
|
# Yeah, using global namespace
|
||||||
|
cls_candidate = globals()[entry["type"]]
|
||||||
|
# print(f"Class candidate: {cls_candidate}")
|
||||||
|
|
||||||
|
if issubclass(cls_candidate, OphydObject):
|
||||||
|
ret = cls_candidate(**entry["config"])
|
||||||
|
if connect:
|
||||||
|
ret.wait_for_connection(timeout=5)
|
||||||
|
return ret
|
||||||
|
else:
|
||||||
|
raise RuntimeError(f"Unsupported return class: {entry['type']}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
num_errors = 0
|
||||||
|
for key in lut_db:
|
||||||
|
try:
|
||||||
|
dut = createProxy(str(key))
|
||||||
|
print(f"{key}\t: {type(dut)}\t{dut.read()}")
|
||||||
|
# print(f"{key}\t: {type(dut)}")
|
||||||
|
except Exception as ex:
|
||||||
|
num_errors += 1
|
||||||
|
print(key)
|
||||||
|
print(ex)
|
||||||
|
print(f"\nTotal number of errors: {num_errors}")
|
10
ophyd_devices/epics/__init__.py
Normal file
10
ophyd_devices/epics/__init__.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
from .devices.DelayGeneratorDG645 import DelayGeneratorDG645
|
||||||
|
from .devices.slits import SlitH, SlitV
|
||||||
|
from .devices.XbpmBase import XbpmBase, XbpmCsaxsOp
|
||||||
|
from .devices.SpmBase import SpmBase
|
||||||
|
from .devices.InsertionDevice import InsertionDevice
|
||||||
|
|
||||||
|
# Standard ophyd classes
|
||||||
|
from ophyd import EpicsSignal, EpicsSignalRO, EpicsMotor
|
||||||
|
from ophyd.sim import SynAxis, SynSignal, SynPeriodicSignal
|
||||||
|
from ophyd.quadem import QuadEM
|
21
ophyd_devices/epics/db/machine_database.yml
Normal file
21
ophyd_devices/epics/db/machine_database.yml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
ring:
|
||||||
|
desc: 'SLS storage ring status'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: ring, prefix: ''}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SlsStatus
|
||||||
|
frontendstatus:
|
||||||
|
desc: 'Minimal beamline frontend status'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: frontendstatus, prefix: X06DA}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: FrontEndStatus
|
||||||
|
opticshutchstatus:
|
||||||
|
desc: 'Minimal beamline optics hutch status'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: opticshutchstatus, prefix: X06DA}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: OpticsHutchStatus
|
35
ophyd_devices/epics/db/test_database.yml
Normal file
35
ophyd_devices/epics/db/test_database.yml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
motor1:
|
||||||
|
desc: 'Simulated axis 1'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: motor1}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SynAxis
|
||||||
|
motor2:
|
||||||
|
desc: 'Simulated axis 2'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: motor2}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SynAxis
|
||||||
|
det1:
|
||||||
|
desc: 'Simulated signal 1'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: det1}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SynSignal
|
||||||
|
det2:
|
||||||
|
desc: 'Simulated signal 2'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: det2}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SynSignal
|
||||||
|
per1:
|
||||||
|
desc: 'Simulated periodic signal 1'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: per1}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SynPeriodicSignal
|
944
ophyd_devices/epics/db/x12sa_database.yml
Normal file
944
ophyd_devices/epics/db/x12sa_database.yml
Normal file
@ -0,0 +1,944 @@
|
|||||||
|
idgap:
|
||||||
|
desc: 'Undulator gap size [mm]'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: idgap, prefix: 'X12SA-ID'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: InsertionDevice
|
||||||
|
bm1trx:
|
||||||
|
desc: 'FrontEnd XBPM 1 horizontal movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bm1trx, prefix: 'X12SA-FE-BM1:TRH'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bm1try:
|
||||||
|
desc: 'FrontEnd XBPM 1 vertical movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bm1try, prefix: 'X12SA-FE-BM1:TRV'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bm2trx:
|
||||||
|
desc: 'FrontEnd XBPM 2 horizontal movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bm2trx, prefix: 'X12SA-FE-BM2:TRH'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bm2try:
|
||||||
|
desc: 'FrontEnd XBPM 2 vertical movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bm2try, prefix: 'X12SA-FE-BM2:TRV'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
di2trx:
|
||||||
|
desc: 'FrontEnd diaphragm 2 horizontal movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: di2trx, prefix: 'X12SA-FE-DI2:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
di2try:
|
||||||
|
desc: 'FrontEnd diaphragm 2 vertical movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: di2try, prefix: 'X12SA-FE-DI2:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl0trxo:
|
||||||
|
desc: 'FrontEnd slit outer blade movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl0trxo, prefix: 'X12SA-FE-SH1:TRX2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl0trxi:
|
||||||
|
desc: 'FrontEnd slit inner blade movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl0trxi, prefix: 'X12SA-FE-SH1:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl0h:
|
||||||
|
desc: 'FrontEnd slit virtual movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl0h, prefix: 'X12SA-FE-SH1:'}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SlitH
|
||||||
|
bm3trx:
|
||||||
|
desc: 'OpticsHutch XBPM 1 horizontal movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bm3trx, prefix: 'X12SA-OP-BM1:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bm3try:
|
||||||
|
desc: 'OpticsHutch XBPM 1 vertical movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bm3try, prefix: 'X12SA-OP-BM1:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl1trxo:
|
||||||
|
desc: 'OpticsHutch slit outer blade movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl1trxo, prefix: 'X12SA-OP-SH1:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl1trxi:
|
||||||
|
desc: 'OpticsHutch slit inner blade movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl1trxi, prefix: 'X12SA-OP-SH1:TRX2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl1tryt:
|
||||||
|
desc: 'OpticsHutch slit top blade movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl1tryt, prefix: 'X12SA-OP-SV1:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl1tryb:
|
||||||
|
desc: 'OpticsHutch slit bottom blade movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl1tryb, prefix: 'X12SA-OP-SV1:TRY2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl1h:
|
||||||
|
desc: 'OpticsHutch slit virtual movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl1h, prefix: 'X12SA-OP-SH1:'}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SlitH
|
||||||
|
sl1v:
|
||||||
|
desc: 'OpticsHutch slit virtual movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl1v, prefix: 'X12SA-OP-SV1:'}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SlitV
|
||||||
|
fi1try:
|
||||||
|
desc: 'OpticsHutch filter 1 movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fi1try, prefix: 'X12SA-OP-FI1:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
fi2try:
|
||||||
|
desc: 'OpticsHutch filter 2 movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fi2try, prefix: 'X12SA-OP-FI2:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
fi3try:
|
||||||
|
desc: 'OpticsHutch filter 3 movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fi3try, prefix: 'X12SA-OP-FI3:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
|
||||||
|
mobdai:
|
||||||
|
desc: 'Monochromator bender inner motor'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mobdai, prefix: 'X12SA-OP-MO:TRYA'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
mobdbo:
|
||||||
|
desc: 'Monochromator bender outer motor'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mobdbo, prefix: 'X12SA-OP-MO:TRYB'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
mobdco:
|
||||||
|
desc: 'Monochromator bender outer motor'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mobdco, prefix: 'X12SA-OP-MO:TRYC'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
mobddi:
|
||||||
|
desc: 'Monochromator bender inner motor'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mobddi, prefix: 'X12SA-OP-MO:TRYD'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
mobd:
|
||||||
|
desc: 'Monochromator bender virtual motor'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mobd, prefix: 'X12SA-OP-MO:'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: PmMonoBender
|
||||||
|
|
||||||
|
bm4trx:
|
||||||
|
desc: 'OpticsHutch XBPM 2 horizontal movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bm4trx, prefix: 'X12SA-OP-BM2:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bm4try:
|
||||||
|
desc: 'OpticsHutch XBPM 2 vertical movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bm4try, prefix: 'X12SA-OP-BM2:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
mitrx:
|
||||||
|
desc: 'Mirror horizontal movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mitrx, prefix: 'X12SA-OP-MI:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
mitry1:
|
||||||
|
desc: 'Mirror vertical movement 1'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mitry1, prefix: 'X12SA-OP-MI:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
mitry2:
|
||||||
|
desc: 'Mirror vertical movement 2'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mitry2, prefix: 'X12SA-OP-MI:TRY2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
mitry3:
|
||||||
|
desc: 'Mirror vertical movement 3'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mitry3, prefix: 'X12SA-OP-MI:TRY3'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
mibd1:
|
||||||
|
desc: 'Mirror bender 1'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mibd1, prefix: 'X12SA-OP-MI:TRZ1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
mibd2:
|
||||||
|
desc: 'Mirror bender 2'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mibd2, prefix: 'X12SA-OP-MI:TRZ2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bm5trx:
|
||||||
|
desc: 'OpticsHutch XBPM 3 horizontal movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bm5trx, prefix: 'X12SA-OP-BM3:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bm5try:
|
||||||
|
desc: 'OpticsHutch XBPM 3 vertical movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bm5try, prefix: 'X12SA-OP-BM3:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl2trxo:
|
||||||
|
desc: 'OpticsHutch slit 2 outer blade movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl2trxo, prefix: 'X12SA-OP-SH2:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl2trxi:
|
||||||
|
desc: 'OpticsHutch slit 2 inner blade movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl2trxi, prefix: 'X12SA-OP-SH2:TRX2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl2tryt:
|
||||||
|
desc: 'OpticsHutch slit 2 top blade movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl2tryt, prefix: 'X12SA-OP-SV2:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl2tryb:
|
||||||
|
desc: 'OpticsHutch slit 2 bottom blade movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl2tryb, prefix: 'X12SA-OP-SV2:TRY2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
sl2h:
|
||||||
|
desc: 'OpticsHutch slit 2 virtual movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl2h, prefix: 'X12SA-OP-SH2:'}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SlitH
|
||||||
|
sl2v:
|
||||||
|
desc: 'OpticsHutch slit 2 virtual movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sl2v, prefix: 'X12SA-OP-SV2:'}
|
||||||
|
deviceGroup: epicsDevice
|
||||||
|
status: {enabled: true}
|
||||||
|
type: SlitV
|
||||||
|
aptrx:
|
||||||
|
desc: 'ES aperture horizontal movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: aptrx, prefix: 'X12SA-ES1-PIN1:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
aptry:
|
||||||
|
desc: 'ES aperture vertical movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: aptry, prefix: 'X12SA-ES1-PIN1:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
ebtrx:
|
||||||
|
desc: 'Exposure box 2 horizontal movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: ebtrx, prefix: 'X12SA-ES1-EB:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
ebtry:
|
||||||
|
desc: 'Exposure box 2 vertical movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: ebtry, prefix: 'X12SA-ES1-EB:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
ebtrz:
|
||||||
|
desc: 'Exposure box 2 axial movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: ebtrz, prefix: 'X12SA-ES1-EB:TRZ1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
fttrx1:
|
||||||
|
desc: 'Dunno these motors???'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fttrx1, prefix: 'X12SA-ES1-FTS1:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
fttry1:
|
||||||
|
desc: 'Dunno these motors???'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fttry1, prefix: 'X12SA-ES1-FTS1:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
fttrz:
|
||||||
|
desc: 'Dunno these motors???'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fttrz, prefix: 'X12SA-ES1-FTS1:TRZ1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
fttrx2:
|
||||||
|
desc: 'Dunno these motors???'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fttrx2, prefix: 'X12SA-ES1-FTS2:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
fttry2:
|
||||||
|
desc: 'Dunno these motors???'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fttry2, prefix: 'X12SA-ES1-FTS2:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bs1x:
|
||||||
|
desc: 'Dunno these motors???'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bs1x, prefix: 'X12SA-ES1-BS1:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bs1y:
|
||||||
|
desc: 'Dunno these motors???'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bs1y, prefix: 'X12SA-ES1-BS1:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bs2x:
|
||||||
|
desc: 'Dunno these motors???'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bs2x, prefix: 'X12SA-ES1-BS2:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
bs2y:
|
||||||
|
desc: 'Dunno these motors???'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bs2y, prefix: 'X12SA-ES1-BS2:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
dttrx:
|
||||||
|
desc: 'Detector tower motion'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: dttrx, prefix: 'X12SA-ES1-DETT:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
dttry:
|
||||||
|
desc: 'Detector tower motion, no encoder'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: dttry, prefix: 'X12SA-ES1-DETT:TRY1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
dttrz:
|
||||||
|
desc: 'Detector tower motion'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: dttrz, prefix: 'X12SA-ES1-DETT:TRZ1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
dtpush:
|
||||||
|
desc: 'Detector tower tilt pusher'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: dtpush, prefix: 'X12SA-ES1-DETT:ROX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
dtth:
|
||||||
|
desc: 'Detector tower tilt rotation'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: dtth, prefix: 'X12SA-ES1-DETT:ROX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: PmDetectorRotation
|
||||||
|
dettrx:
|
||||||
|
desc: 'Detector tower motion'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: dettrx, prefix: 'X12SA-ES1-DET1:TRX1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
eyex:
|
||||||
|
desc: 'X-ray eye motion'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: eyex, prefix: X12SA-ES2-ES01}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
eyey:
|
||||||
|
desc: 'X-ray eye motion'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: eyey, prefix: X12SA-ES2-ES02}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
eyefoc:
|
||||||
|
desc: 'X-ray eye focusing motor'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: eyefoc, prefix: X12SA-ES2-ES25}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
micfoc:
|
||||||
|
desc: 'Microscope focusing motor'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: micfoc, prefix: X12SA-ES2-ES03}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
samx:
|
||||||
|
desc: 'Sample motion'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: samx, prefix: X12SA-ES2-ES04}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
samy:
|
||||||
|
desc: 'Sample motion'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: samy, prefix: X12SA-ES2-ES05}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
curr:
|
||||||
|
desc: 'SLS ring current'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: curr, read_pv: 'ARIDI-PCT:CURRENT'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
sec:
|
||||||
|
desc: 'Some scaler...'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sec, read_pv: X12SA-ES1-SCALER.S1}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
cyb:
|
||||||
|
desc: 'Some scaler...'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: cyb, read_pv: X12SA-ES1-SCALER.S2}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
diode:
|
||||||
|
desc: 'Some scaler...'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: diode, read_pv: X12SA-ES1-SCALER.S3}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
led:
|
||||||
|
desc: 'Some scaler...'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: led, read_pv: X12SA-ES1-SCALER.S4}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
fal0:
|
||||||
|
desc: 'Some scaler...'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fal0, read_pv: X12SA-ES1-SCALER.S4}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
fal1:
|
||||||
|
desc: 'Some scaler...'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fal1, read_pv: X12SA-ES1-SCALER.S5}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
fal2:
|
||||||
|
desc: 'Some scaler...'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: fal2, read_pv: X12SA-ES1-SCALER.S6}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
|
||||||
|
bpm1i:
|
||||||
|
desc: 'Some VME XBPM...'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm1i, read_pv: X12SA-OP-BPM1:SUM}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm1:
|
||||||
|
desc: 'XBPM 1: Somewhere around mono (VME)'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm1, prefix: 'X12SA-OP-BPM2:'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: XbpmCsaxsOp
|
||||||
|
bpm2i:
|
||||||
|
desc: 'Some VME XBPM...'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm2i, read_pv: X12SA-OP-BPM2:SUM}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm2:
|
||||||
|
desc: 'XBPM 2: Somewhere around mono (VME)'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm2, prefix: 'X12SA-OP-BPM2:'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: XbpmCsaxsOp
|
||||||
|
|
||||||
|
bpm3:
|
||||||
|
desc: 'XBPM 3: White beam AH501 before mono'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm3, prefix: 'X12SA-OP-BPM3:'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: QuadEM
|
||||||
|
bpm3a:
|
||||||
|
desc: 'XBPM 3: White beam AH501 before mono'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm3a, read_pv: 'X12SA-OP-BPM3:Current1:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm3b:
|
||||||
|
desc: 'XBPM 3: White beam AH501 before mono'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm3b, read_pv: 'X12SA-OP-BPM3:Current2:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm3c:
|
||||||
|
desc: 'XBPM 3: White beam AH501 before mono'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm3c, read_pv: 'X12SA-OP-BPM3:Current3:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm3d:
|
||||||
|
desc: 'XBPM 3: White beam AH501 before mono'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm3d, read_pv: 'X12SA-OP-BPM3:Current4:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
|
||||||
|
bpm4a:
|
||||||
|
desc: 'XBPM 4: VME between mono and mirror'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm4a, read_pv: 'X12SA-OP1-SCALER.S2'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm4b:
|
||||||
|
desc: 'XBPM 4: VME between mono and mirror'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm4b, read_pv: 'X12SA-OP1-SCALER.S3'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm4c:
|
||||||
|
desc: 'XBPM 4: VME between mono and mirror'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm4c, read_pv: 'X12SA-OP1-SCALER.S4'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm4d:
|
||||||
|
desc: 'XBPM 4: VME between mono and mirror'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm4d, read_pv: 'X12SA-OP1-SCALER.S5'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm5:
|
||||||
|
desc: 'XBPM 5: AH501 past the mirror'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm5, prefix: 'X12SA-OP-BPM5:'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: QuadEM
|
||||||
|
bpm5a:
|
||||||
|
desc: 'XBPM 5: AH501 past the mirror'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm5a, read_pv: 'X12SA-OP-BPM5:Current1:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm5b:
|
||||||
|
desc: 'XBPM 5: AH501 past the mirror'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm5b, read_pv: 'X12SA-OP-BPM5:Current2:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm5c:
|
||||||
|
desc: 'XBPM 5: AH501 past the mirror'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm5c, read_pv: 'X12SA-OP-BPM5:Current3:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm5d:
|
||||||
|
desc: 'XBPM 5: AH501 past the mirror'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm5d, read_pv: 'X12SA-OP-BPM5:Current4:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm6:
|
||||||
|
desc: 'XBPM 6: Xbox, not commissioned'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm6, prefix: 'X12SA-OP-BPM6:'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: QuadEM
|
||||||
|
bpm6a:
|
||||||
|
desc: 'XBPM 6: Xbox, not commissioned'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm6a, read_pv: 'X12SA-OP-BPM6:Current1:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm6b:
|
||||||
|
desc: 'XBPM 6: Xbox, not commissioned'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm6b, read_pv: 'X12SA-OP-BPM6:Current2:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm6c:
|
||||||
|
desc: 'XBPM 6: Xbox, not commissioned'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm6c, read_pv: 'X12SA-OP-BPM6:Current3:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
bpm6d:
|
||||||
|
desc: 'XBPM 6: Xbox, not commissioned'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: bpm6d, read_pv: 'X12SA-OP-BPM6:Current4:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
ftp:
|
||||||
|
desc: 'Flight tube pressure'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: ftp, read_pv: 'X12SA-ES1-FT1MT1:PRESSURE'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
eyecenx:
|
||||||
|
desc: 'X-ray eye intensit math'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: eyecenx, read_pv: 'XOMNYI-XEYE-XCEN:0'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
eyeceny:
|
||||||
|
desc: 'X-ray eye intensit math'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: eyeceny, read_pv: 'XOMNYI-XEYE-YCEN:0'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
eyeint:
|
||||||
|
desc: 'X-ray eye intensit math'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: eyeint, read_pv: 'XOMNYI-XEYE-INT_MEAN:0'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
transd:
|
||||||
|
desc: 'Transmission diode'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: transd, read_pv: 'X12SA-OP-BPM1:Current1:MeanValue_RBV'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
YASYM:
|
||||||
|
desc: 'FOFB reference'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: YASYM, read_pv: 'X12SA-LBB:Y-ASYM'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
YSYM:
|
||||||
|
desc: 'FOFB reference'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: YSYM, read_pv: 'X12SA-LBB:Y-SYM'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
XASYM:
|
||||||
|
desc: 'FOFB reference'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: XASYM, read_pv: 'X12SA-LBB:X-ASYM'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
XSYM:
|
||||||
|
desc: 'FOFB reference'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: XSYM, read_pv: 'X12SA-LBB:X-SYM'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
FBPMDX:
|
||||||
|
desc: 'FOFB reference'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: FBPMDX, read_pv: 'X12SA-ID-FBPMD:X'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
FBPMDY:
|
||||||
|
desc: 'FOFB reference'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: FBPMDY, read_pv: 'X12SA-ID-FBPMD:Y'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
FBPMUX:
|
||||||
|
desc: 'FOFB reference'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: FBPMUX, read_pv: 'X12SA-ID-FBPMU:X'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
FBPMUY:
|
||||||
|
desc: 'FOFB reference'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: FBPMUY, read_pv: 'X12SA-ID-FBPMU:Y'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
|
||||||
|
sttrx:
|
||||||
|
desc: 'Girder X translation'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sttrx, prefix: 'X12SA-HG'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: GirderMotorX1
|
||||||
|
sttry:
|
||||||
|
desc: 'Girder Y translation'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: sttry, prefix: 'X12SA-HG'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: GirderMotorY1
|
||||||
|
strox:
|
||||||
|
desc: 'Girder virtual pitch'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: strox, prefix: 'X12SA-HG'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: GirderMotorPITCH
|
||||||
|
stroy:
|
||||||
|
desc: 'Girder virtual yaw'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: stroy, prefix: 'X12SA-HG'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: GirderMotorYAW
|
||||||
|
stroz:
|
||||||
|
desc: 'Girder virtual roll'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: stroz, prefix: 'X12SA-HG'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: GirderMotorROLL
|
||||||
|
motry:
|
||||||
|
desc: 'OpticsHutch optical table vertical movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: motry, prefix: 'X12SA-OP-OT:TRY'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
motrz1:
|
||||||
|
desc: 'Monochromator crystal 1 axial movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: motrz1, prefix: 'X12SA-OP-MO:TRZ1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
motrz1e:
|
||||||
|
desc: 'Monochromator crystal 1 axial movement encoder'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: motrz1e, read_pv: 'X12SA-OP-MO:ECZ1'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
|
||||||
|
|
||||||
|
moroll1:
|
||||||
|
desc: 'Monochromator crystal 1 roll'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: moroll1, prefix: 'X12SA-OP-MO:ROZ1'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
motrx2:
|
||||||
|
desc: 'Monochromator crystal 2 horizontal movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: motrx2, prefix: 'X12SA-OP-MO:TRX2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
motry2:
|
||||||
|
desc: 'Monochromator crystal 2 vertical movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: motry2, prefix: 'X12SA-OP-MO:TRY2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
|
||||||
|
|
||||||
|
#monot:
|
||||||
|
# desc: 'Monochromator temperature'
|
||||||
|
# acquisition: {schedule: sync}
|
||||||
|
# config: {name: monot, read_pv: 'X12SA-OP-MO:TC3'}
|
||||||
|
# deviceGroup: monitor
|
||||||
|
# status: {enabled: true}
|
||||||
|
# type: EpicsSignalRO
|
||||||
|
moyaw2:
|
||||||
|
desc: 'Monochromator crystal 2 yaw movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: moyaw2, prefix: 'X12SA-OP-MO:ROY2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
moroll2:
|
||||||
|
desc: 'Monochromator crystal 2 roll movement'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: moroll2, prefix: 'X12SA-OP-MO:ROZ2'}
|
||||||
|
deviceGroup: beamlineMotor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsMotor
|
||||||
|
|
||||||
|
mopush1:
|
||||||
|
desc: 'Monochromator crystal 1 angle'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mopush1, read_pv: 'X12SA-OP-MO:ROX1'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
moth1:
|
||||||
|
desc: 'Monochromator Theta 1'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: moth1, read_pv: 'X12SA-OP-MO:ROX1'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: MonoTheta1
|
||||||
|
moth1e:
|
||||||
|
desc: 'Monochromator crystal 1 theta encoder'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: moth1e, read_pv: 'X12SA-OP-MO:ECX1'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
mopush2:
|
||||||
|
desc: 'Monochromator crystal 2 angle'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mopush2, read_pv: 'X12SA-OP-MO:ROX2'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
moth2:
|
||||||
|
desc: 'Monochromator Theta 2'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: moth1, read_pv: 'X12SA-OP-MO:ROX2'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: MonoTheta2
|
||||||
|
moth2e:
|
||||||
|
desc: 'Monochromator crystal 2 theta encoder'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: moth2e, read_pv: 'X12SA-OP-MO:ECX2'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EpicsSignalRO
|
||||||
|
mokev:
|
||||||
|
desc: 'Monochromator energy in keV'
|
||||||
|
acquisition: {schedule: sync}
|
||||||
|
config: {name: mokev, read_pv: 'X12SA-OP-MO:ROX2'}
|
||||||
|
deviceGroup: monitor
|
||||||
|
status: {enabled: true}
|
||||||
|
type: EnergyKev
|
||||||
|
|
||||||
|
|
208
ophyd_devices/epics/devices/DelayGeneratorDG645.py
Normal file
208
ophyd_devices/epics/devices/DelayGeneratorDG645.py
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Tue Nov 9 16:12:47 2021
|
||||||
|
|
||||||
|
@author: mohacsi_i
|
||||||
|
"""
|
||||||
|
|
||||||
|
from ophyd import Device, Component, EpicsSignal, EpicsSignalRO, Kind
|
||||||
|
from ophyd import PositionerBase
|
||||||
|
from ophyd.pseudopos import (
|
||||||
|
pseudo_position_argument,
|
||||||
|
real_position_argument,
|
||||||
|
PseudoSingle,
|
||||||
|
PseudoPositioner,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DelayStatic(Device):
|
||||||
|
"""Static axis for the T0 output channel
|
||||||
|
It allows setting the logic levels, but the timing is fixed.
|
||||||
|
The signal is high after receiving the trigger until the end
|
||||||
|
of the holdoff period.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Other channel stuff
|
||||||
|
ttl_mode = Component(EpicsSignal, "OutputModeTtlSS.PROC", kind=Kind.config)
|
||||||
|
nim_mode = Component(EpicsSignal, "OutputModeNimSS.PROC", kind=Kind.config)
|
||||||
|
polarity = Component(
|
||||||
|
EpicsSignal,
|
||||||
|
"OutputPolarityBI",
|
||||||
|
write_pv="OutputPolarityBO",
|
||||||
|
name="polarity",
|
||||||
|
kind=Kind.config,
|
||||||
|
)
|
||||||
|
amplitude = Component(
|
||||||
|
EpicsSignal, "OutputAmpAI", write_pv="OutputAmpAO", name="amplitude", kind=Kind.config
|
||||||
|
)
|
||||||
|
polarity = Component(
|
||||||
|
EpicsSignal, "OutputOffsetAI", write_pv="OutputOffsetAO", name="offset", kind=Kind.config
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DummyPositioner(Device, PositionerBase):
|
||||||
|
setpoint = Component(EpicsSignal, "DelayAO", kind=Kind.config)
|
||||||
|
readback = Component(EpicsSignalRO, "DelayAI", kind=Kind.config)
|
||||||
|
|
||||||
|
|
||||||
|
class DelayPair(PseudoPositioner):
|
||||||
|
"""Delay pair interface for DG645
|
||||||
|
|
||||||
|
Virtual motor interface to a pair of signals (on the frontpanel).
|
||||||
|
It offers a simple delay and pulse width interface for scanning.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# The pseudo positioner axes
|
||||||
|
delay = Component(PseudoSingle, limits=(0, 2000.0), name="delay")
|
||||||
|
width = Component(PseudoSingle, limits=(0, 2000.0), name="pulsewidth")
|
||||||
|
# The real delay axes
|
||||||
|
ch1 = Component(DummyPositioner, name="ch1")
|
||||||
|
ch2 = Component(DummyPositioner, name="ch2")
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
# Change suffix names before connecting (a bit of dynamic connections)
|
||||||
|
self.__class__.__dict__["ch1"].suffix = kwargs["channel"][0]
|
||||||
|
self.__class__.__dict__["ch2"].suffix = kwargs["channel"][1]
|
||||||
|
del kwargs["channel"]
|
||||||
|
# Call parent to start the connections
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@pseudo_position_argument
|
||||||
|
def forward(self, pseudo_pos):
|
||||||
|
"""Run a forward (pseudo -> real) calculation"""
|
||||||
|
return self.RealPosition(ch1=pseudo_pos.delay, ch2=pseudo_pos.delay + pseudo_pos.width)
|
||||||
|
|
||||||
|
@real_position_argument
|
||||||
|
def inverse(self, real_pos):
|
||||||
|
"""Run an inverse (real -> pseudo) calculation"""
|
||||||
|
return self.PseudoPosition(delay=real_pos.ch1, width=real_pos.ch2 - real_pos.ch1)
|
||||||
|
|
||||||
|
|
||||||
|
class DelayGeneratorDG645(Device):
|
||||||
|
"""DG645 delay generator
|
||||||
|
|
||||||
|
This class implements a thin Ophyd wrapper around the Stanford Research DG645
|
||||||
|
digital delay generator.
|
||||||
|
|
||||||
|
Internally, the DG645 generates 8+1 signals: A, B, C, D, E, F, G, H and T0
|
||||||
|
Front panel outputs T0, AB, CD, EF and GH are a combination of these signals.
|
||||||
|
Back panel outputs are directly routed signals. So signals are NOT INDEPENDENT.
|
||||||
|
|
||||||
|
Front panel signals:
|
||||||
|
All signals go high after their defined delays and go low after the trigger
|
||||||
|
holdoff period, i.e. this is the trigger window. Front panel outputs provide
|
||||||
|
a combination of these events.
|
||||||
|
Option 1 back panel 5V signals:
|
||||||
|
All signals go high after their defined delays and go low after the trigger
|
||||||
|
holdoff period, i.e. this is the trigger window. The signals will stay high
|
||||||
|
until the end of the window.
|
||||||
|
Option 2 back panel 30V signals:
|
||||||
|
All signals go high after their defined delays for ~100ns. This is fixed by
|
||||||
|
electronics (30V needs quite some power). This is not implemented in the
|
||||||
|
current device
|
||||||
|
"""
|
||||||
|
|
||||||
|
state = Component(EpicsSignalRO, "EventStatusLI", name="status_register")
|
||||||
|
status = Component(EpicsSignalRO, "StatusSI", name="status")
|
||||||
|
|
||||||
|
# Front Panel
|
||||||
|
channelT0 = Component(DelayStatic, "T0", name="T0")
|
||||||
|
channelAB = Component(DelayPair, "", name="AB", channel="AB")
|
||||||
|
channelCD = Component(DelayPair, "", name="CD", channel="CD")
|
||||||
|
channelEF = Component(DelayPair, "", name="EF", channel="EF")
|
||||||
|
channelGH = Component(DelayPair, "", name="GH", channel="GH")
|
||||||
|
|
||||||
|
# Minimum time between triggers
|
||||||
|
holdoff = Component(
|
||||||
|
EpicsSignal,
|
||||||
|
"TriggerHoldoffAI",
|
||||||
|
write_pv="TriggerHoldoffAO",
|
||||||
|
name="trigger_holdoff",
|
||||||
|
kind=Kind.config,
|
||||||
|
)
|
||||||
|
inhibit = Component(
|
||||||
|
EpicsSignal,
|
||||||
|
"TriggerInhibitMI",
|
||||||
|
write_pv="TriggerInhibitMO",
|
||||||
|
name="trigger_inhibit",
|
||||||
|
kind=Kind.config,
|
||||||
|
)
|
||||||
|
source = Component(
|
||||||
|
EpicsSignal,
|
||||||
|
"TriggerSourceMI",
|
||||||
|
write_pv="TriggerSourceMO",
|
||||||
|
name="trigger_source",
|
||||||
|
kind=Kind.config,
|
||||||
|
)
|
||||||
|
level = Component(
|
||||||
|
EpicsSignal,
|
||||||
|
"TriggerLevelAI",
|
||||||
|
write_pv="TriggerLevelAO",
|
||||||
|
name="trigger_level",
|
||||||
|
kind=Kind.config,
|
||||||
|
)
|
||||||
|
rate = Component(
|
||||||
|
EpicsSignal,
|
||||||
|
"TriggerRateAI",
|
||||||
|
write_pv="TriggerRateAO",
|
||||||
|
name="trigger_rate",
|
||||||
|
kind=Kind.config,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Command PVs
|
||||||
|
arm = Component(
|
||||||
|
EpicsSignal, "TriggerDelayBI", write_pv="TriggerDelayBO", name="arm", kind=Kind.omitted
|
||||||
|
)
|
||||||
|
|
||||||
|
# Burst mode
|
||||||
|
burstMode = Component(
|
||||||
|
EpicsSignal, "BurstModeBI", write_pv="BurstModeBO", name="burstmode", kind=Kind.config
|
||||||
|
)
|
||||||
|
burstConfig = Component(
|
||||||
|
EpicsSignal, "BurstConfigBI", write_pv="BurstConfigBO", name="burstconfig", kind=Kind.config
|
||||||
|
)
|
||||||
|
burstCount = Component(
|
||||||
|
EpicsSignal, "BurstCountLI", write_pv="BurstCountLO", name="burstcount", kind=Kind.config
|
||||||
|
)
|
||||||
|
burstDelay = Component(
|
||||||
|
EpicsSignal, "BurstDelayAI", write_pv="BurstDelayAO", name="burstdelay", kind=Kind.config
|
||||||
|
)
|
||||||
|
burstPeriod = Component(
|
||||||
|
EpicsSignal, "BurstPeriodAI", write_pv="BurstPeriodAO", name="burstperiod", kind=Kind.config
|
||||||
|
)
|
||||||
|
|
||||||
|
def stage(self):
|
||||||
|
"""Trigger the generator by arming to accept triggers"""
|
||||||
|
self.arm.write(1).wait()
|
||||||
|
|
||||||
|
def unstage(self):
|
||||||
|
"""Stop the trigger generator from accepting triggers"""
|
||||||
|
self.arm.write(0).wait()
|
||||||
|
|
||||||
|
def burstEnable(self, count, delay, period, config="all"):
|
||||||
|
"""Enable the burst mode"""
|
||||||
|
# Validate inputs
|
||||||
|
count = int(count)
|
||||||
|
assert count > 0, "Number of bursts must be positive"
|
||||||
|
assert delay > 0, "Burst delay must be positive"
|
||||||
|
assert period > 0, "Burst period must be positive"
|
||||||
|
assert config in ["all", "first"], "Supported bust configs are 'all' and 'first'"
|
||||||
|
|
||||||
|
self.burstMode.set(1).wait()
|
||||||
|
self.burstCount.set(count).wait()
|
||||||
|
self.burstDelay.set(delay).wait()
|
||||||
|
self.burstPeriod.set(period).wait()
|
||||||
|
|
||||||
|
if config == "all":
|
||||||
|
self.burstConfig.set(0).wait()
|
||||||
|
elif config == "first":
|
||||||
|
self.burstConfig.set(1).wait()
|
||||||
|
|
||||||
|
def busrtDisable(self):
|
||||||
|
"""Disable the burst mode"""
|
||||||
|
self.burstMode.set(0).wait()
|
||||||
|
|
||||||
|
|
||||||
|
# Automatically connect to test environmenr if directly invoked
|
||||||
|
if __name__ == "__main__":
|
||||||
|
dgen = DelayGeneratorDG645("X01DA-PC-DGEN:", name="delayer")
|
28
ophyd_devices/epics/devices/InsertionDevice.py
Normal file
28
ophyd_devices/epics/devices/InsertionDevice.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
from ophyd import PVPositioner, Component, EpicsSignal, EpicsSignalRO, Kind
|
||||||
|
|
||||||
|
|
||||||
|
class InsertionDevice(PVPositioner):
|
||||||
|
"""Python wrapper for the CSAXS insertion device control
|
||||||
|
|
||||||
|
This wrapper provides a positioner interface for the ID control.
|
||||||
|
is completely custom XBPM with templates directly in the
|
||||||
|
VME repo. Thus it needs a custom ophyd template as well...
|
||||||
|
|
||||||
|
WARN: The x and y are not updated by the IOC
|
||||||
|
"""
|
||||||
|
|
||||||
|
status = Component(EpicsSignalRO, "-USER:STATUS", auto_monitor=True)
|
||||||
|
errorSource = Component(EpicsSignalRO, "-USER:ERROR-SOURCE", auto_monitor=True)
|
||||||
|
isOpen = Component(EpicsSignalRO, "-GAP:ISOPEN", auto_monitor=True)
|
||||||
|
|
||||||
|
# PVPositioner interface
|
||||||
|
setpoint = Component(EpicsSignal, "-GAP:SET", auto_monitor=True)
|
||||||
|
readback = Component(EpicsSignalRO, "-GAP:READ", auto_monitor=True, kind=Kind.hinted)
|
||||||
|
done = Component(EpicsSignalRO, ":DONE", auto_monitor=True)
|
||||||
|
stop_signal = Component(EpicsSignal, "-GAP:STOP", kind=Kind.omitted)
|
||||||
|
|
||||||
|
|
||||||
|
# Automatically start simulation if directly invoked
|
||||||
|
# (NA for important devices)
|
||||||
|
if __name__ == "__main__":
|
||||||
|
pass
|
108
ophyd_devices/epics/devices/SpmBase.py
Normal file
108
ophyd_devices/epics/devices/SpmBase.py
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
import numpy as np
|
||||||
|
from ophyd import Device, Component, EpicsSignal, EpicsSignalRO
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
|
class SpmBase(Device):
|
||||||
|
"""Python wrapper for the Staggered Blade Pair Monitors
|
||||||
|
|
||||||
|
SPM's consist of a set of four horizontal tungsten blades and are
|
||||||
|
used to monitor the beam height (only Y) for the bending magnet
|
||||||
|
beamlines of SLS.
|
||||||
|
|
||||||
|
Note: EPICS provided signals are read only, but the users can
|
||||||
|
change the beam position offset.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Motor interface
|
||||||
|
s1 = Component(EpicsSignalRO, "Current1", auto_monitor=True)
|
||||||
|
s2 = Component(EpicsSignalRO, "Current2", auto_monitor=True)
|
||||||
|
s3 = Component(EpicsSignalRO, "Current3", auto_monitor=True)
|
||||||
|
s4 = Component(EpicsSignalRO, "Current4", auto_monitor=True)
|
||||||
|
sum = Component(EpicsSignalRO, "SumAll", auto_monitor=True)
|
||||||
|
y = Component(EpicsSignalRO, "Y", auto_monitor=True)
|
||||||
|
scale = Component(EpicsSignal, "PositionScaleY", auto_monitor=True)
|
||||||
|
offset = Component(EpicsSignal, "PositionOffsetY", auto_monitor=True)
|
||||||
|
|
||||||
|
|
||||||
|
class SpmSim(SpmBase):
|
||||||
|
"""Python wrapper for simulated Staggered Blade Pair Monitors
|
||||||
|
|
||||||
|
SPM's consist of a set of four horizontal tungsten blades and are
|
||||||
|
used to monitor the beam height (only Y) for the bending magnet
|
||||||
|
beamlines of SLS.
|
||||||
|
|
||||||
|
This simulation device extends the basic proxy with a script that
|
||||||
|
fills signals with quasi-randomized values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Motor interface
|
||||||
|
s1w = Component(EpicsSignal, "Current1:RAW.VAL", auto_monitor=False)
|
||||||
|
s2w = Component(EpicsSignal, "Current2:RAW.VAL", auto_monitor=False)
|
||||||
|
s3w = Component(EpicsSignal, "Current3:RAW.VAL", auto_monitor=False)
|
||||||
|
s4w = Component(EpicsSignal, "Current4:RAW.VAL", auto_monitor=False)
|
||||||
|
rangew = Component(EpicsSignal, "RANGEraw", auto_monitor=False)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self._MX = 0
|
||||||
|
self._MY = 0
|
||||||
|
self._I0 = 255.0
|
||||||
|
self._x = np.linspace(-5, 5, 64)
|
||||||
|
self._y = np.linspace(-5, 5, 64)
|
||||||
|
self._x, self._y = np.meshgrid(self._x, self._y)
|
||||||
|
|
||||||
|
def _simFrame(self):
|
||||||
|
"""Generator to simulate a jumping gaussian"""
|
||||||
|
# Define normalized 2D gaussian
|
||||||
|
def gaus2d(x=0, y=0, mx=0, my=0, sx=1, sy=1):
|
||||||
|
return np.exp(
|
||||||
|
-((x - mx) ** 2.0 / (2.0 * sx**2.0) + (y - my) ** 2.0 / (2.0 * sy**2.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generator for dynamic values
|
||||||
|
self._MX = 0.75 * self._MX + 0.25 * (10.0 * np.random.random() - 5.0)
|
||||||
|
self._MY = 0.75 * self._MY + 0.25 * (10.0 * np.random.random() - 5.0)
|
||||||
|
self._I0 = 0.75 * self._I0 + 0.25 * (255.0 * np.random.random())
|
||||||
|
|
||||||
|
arr = self._I0 * gaus2d(self._x, self._y, self._MX, self._MY)
|
||||||
|
return arr
|
||||||
|
|
||||||
|
def sim(self):
|
||||||
|
# Get next frame
|
||||||
|
beam = self._simFrame()
|
||||||
|
total = np.sum(beam) - np.sum(beam[24:48, :])
|
||||||
|
rnge = np.floor(np.log10(total) - 0.0)
|
||||||
|
s1 = np.sum(beam[0:16, :]) / 10**rnge
|
||||||
|
s2 = np.sum(beam[16:24, :]) / 10**rnge
|
||||||
|
s3 = np.sum(beam[40:48, :]) / 10**rnge
|
||||||
|
s4 = np.sum(beam[48:64, :]) / 10**rnge
|
||||||
|
|
||||||
|
self.s1w.set(s1).wait()
|
||||||
|
self.s2w.set(s2).wait()
|
||||||
|
self.s3w.set(s3).wait()
|
||||||
|
self.s4w.set(s4).wait()
|
||||||
|
self.rangew.set(rnge).wait()
|
||||||
|
# Print debug info
|
||||||
|
print(f"Raw signals: R={rnge}\t{s1}\t{s2}\t{s3}\t{s4}")
|
||||||
|
# plt.imshow(beam)
|
||||||
|
# plt.show(block=False)
|
||||||
|
plt.pause(0.5)
|
||||||
|
|
||||||
|
|
||||||
|
# Automatically start simulation if directly invoked
|
||||||
|
if __name__ == "__main__":
|
||||||
|
spm1 = SpmSim("X06D-FE-BM1:", name="spm1")
|
||||||
|
spm2 = SpmSim("X06D-FE-BM2:", name="spm2")
|
||||||
|
|
||||||
|
spm1.wait_for_connection(timeout=5)
|
||||||
|
spm2.wait_for_connection(timeout=5)
|
||||||
|
|
||||||
|
spm1.rangew.set(1).wait()
|
||||||
|
spm2.rangew.set(1).wait()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
print("---")
|
||||||
|
spm1.sim()
|
||||||
|
spm2.sim()
|
138
ophyd_devices/epics/devices/XbpmBase.py
Normal file
138
ophyd_devices/epics/devices/XbpmBase.py
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import numpy as np
|
||||||
|
from ophyd import Device, Component, EpicsSignal, EpicsSignalRO
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
|
class XbpmCsaxsOp(Device):
|
||||||
|
"""Python wrapper for custom XBPMs in the cSAXS optics hutch
|
||||||
|
|
||||||
|
This is completely custom XBPM with templates directly in the
|
||||||
|
VME repo. Thus it needs a custom ophyd template as well...
|
||||||
|
|
||||||
|
WARN: The x and y are not updated by the IOC
|
||||||
|
"""
|
||||||
|
|
||||||
|
sum = Component(EpicsSignalRO, "SUM", auto_monitor=True)
|
||||||
|
x = Component(EpicsSignalRO, "POSH", auto_monitor=True)
|
||||||
|
y = Component(EpicsSignalRO, "POSV", auto_monitor=True)
|
||||||
|
s1 = Component(EpicsSignalRO, "CHAN1", auto_monitor=True)
|
||||||
|
s2 = Component(EpicsSignalRO, "CHAN2", auto_monitor=True)
|
||||||
|
s3 = Component(EpicsSignalRO, "CHAN3", auto_monitor=True)
|
||||||
|
s4 = Component(EpicsSignalRO, "CHAN4", auto_monitor=True)
|
||||||
|
|
||||||
|
|
||||||
|
class XbpmBase(Device):
|
||||||
|
"""Python wrapper for X-ray Beam Position Monitors
|
||||||
|
|
||||||
|
XBPM's consist of a metal-coated diamond window that ejects
|
||||||
|
photoelectrons from the incoming X-ray beam. These electons
|
||||||
|
are collected and their current is measured. Effectively
|
||||||
|
they act as four quadrant photodiodes and are used as BPMs
|
||||||
|
at the undulator beamlines of SLS.
|
||||||
|
|
||||||
|
Note: EPICS provided signals are read only, but the user can
|
||||||
|
change the beam position offset.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Motor interface
|
||||||
|
s1 = Component(EpicsSignalRO, "Current1", auto_monitor=True)
|
||||||
|
s2 = Component(EpicsSignalRO, "Current2", auto_monitor=True)
|
||||||
|
s3 = Component(EpicsSignalRO, "Current3", auto_monitor=True)
|
||||||
|
s4 = Component(EpicsSignalRO, "Current4", auto_monitor=True)
|
||||||
|
sum = Component(EpicsSignalRO, "SumAll", auto_monitor=True)
|
||||||
|
asymH = Component(EpicsSignalRO, "asymH", auto_monitor=True)
|
||||||
|
asymV = Component(EpicsSignalRO, "asymV", auto_monitor=True)
|
||||||
|
x = Component(EpicsSignalRO, "X", auto_monitor=True)
|
||||||
|
y = Component(EpicsSignalRO, "Y", auto_monitor=True)
|
||||||
|
scaleH = Component(EpicsSignal, "PositionScaleX", auto_monitor=False)
|
||||||
|
offsetH = Component(EpicsSignal, "PositionOffsetX", auto_monitor=False)
|
||||||
|
scaleV = Component(EpicsSignal, "PositionScaleY", auto_monitor=False)
|
||||||
|
offsetV = Component(EpicsSignal, "PositionOffsetY", auto_monitor=False)
|
||||||
|
|
||||||
|
|
||||||
|
class XbpmSim(XbpmBase):
|
||||||
|
"""Python wrapper for simulated X-ray Beam Position Monitors
|
||||||
|
|
||||||
|
XBPM's consist of a metal-coated diamond window that ejects
|
||||||
|
photoelectrons from the incoming X-ray beam. These electons
|
||||||
|
are collected and their current is measured. Effectively
|
||||||
|
they act as four quadrant photodiodes and are used as BPMs
|
||||||
|
at the undulator beamlines of SLS.
|
||||||
|
|
||||||
|
Note: EPICS provided signals are read only, but the user can
|
||||||
|
change the beam position offset.
|
||||||
|
|
||||||
|
This simulation device extends the basic proxy with a script that
|
||||||
|
fills signals with quasi-randomized values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Motor interface
|
||||||
|
s1w = Component(EpicsSignal, "Current1:RAW.VAL", auto_monitor=False)
|
||||||
|
s2w = Component(EpicsSignal, "Current2:RAW.VAL", auto_monitor=False)
|
||||||
|
s3w = Component(EpicsSignal, "Current3:RAW.VAL", auto_monitor=False)
|
||||||
|
s4w = Component(EpicsSignal, "Current4:RAW.VAL", auto_monitor=False)
|
||||||
|
rangew = Component(EpicsSignal, "RANGEraw", auto_monitor=False)
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self._MX = 0
|
||||||
|
self._MY = 0
|
||||||
|
self._I0 = 255.0
|
||||||
|
self._x = np.linspace(-5, 5, 64)
|
||||||
|
self._y = np.linspace(-5, 5, 64)
|
||||||
|
self._x, self._y = np.meshgrid(self._x, self._y)
|
||||||
|
|
||||||
|
def _simFrame(self):
|
||||||
|
"""Generator to simulate a jumping gaussian"""
|
||||||
|
# define normalized 2D gaussian
|
||||||
|
def gaus2d(x=0, y=0, mx=0, my=0, sx=1, sy=1):
|
||||||
|
return np.exp(
|
||||||
|
-((x - mx) ** 2.0 / (2.0 * sx**2.0) + (y - my) ** 2.0 / (2.0 * sy**2.0))
|
||||||
|
)
|
||||||
|
|
||||||
|
# Generator for dynamic values
|
||||||
|
self._MX = 0.75 * self._MX + 0.25 * (10.0 * np.random.random() - 5.0)
|
||||||
|
self._MY = 0.75 * self._MY + 0.25 * (10.0 * np.random.random() - 5.0)
|
||||||
|
self._I0 = 0.75 * self._I0 + 0.25 * (255.0 * np.random.random())
|
||||||
|
|
||||||
|
arr = self._I0 * gaus2d(self._x, self._y, self._MX, self._MY)
|
||||||
|
return arr
|
||||||
|
|
||||||
|
def sim(self):
|
||||||
|
# Get next frame
|
||||||
|
beam = self._simFrame()
|
||||||
|
total = np.sum(beam)
|
||||||
|
rnge = np.floor(np.log10(total) - 0.0)
|
||||||
|
s1 = np.sum(beam[32:64, 32:64]) / 10**rnge
|
||||||
|
s2 = np.sum(beam[0:32, 32:64]) / 10**rnge
|
||||||
|
s3 = np.sum(beam[32:64, 0:32]) / 10**rnge
|
||||||
|
s4 = np.sum(beam[0:32, 0:32]) / 10**rnge
|
||||||
|
|
||||||
|
self.s1w.set(s1).wait()
|
||||||
|
self.s2w.set(s2).wait()
|
||||||
|
self.s3w.set(s3).wait()
|
||||||
|
self.s4w.set(s4).wait()
|
||||||
|
self.rangew.set(rnge).wait()
|
||||||
|
# Print debug info
|
||||||
|
print(f"Raw signals: R={rnge}\t{s1}\t{s2}\t{s3}\t{s4}")
|
||||||
|
# plt.imshow(beam)
|
||||||
|
# plt.show(block=False)
|
||||||
|
plt.pause(0.5)
|
||||||
|
|
||||||
|
|
||||||
|
# Automatically start simulation if directly invoked
|
||||||
|
if __name__ == "__main__":
|
||||||
|
xbpm1 = XbpmSim("X01DA-FE-XBPM1:", name="xbpm1")
|
||||||
|
xbpm2 = XbpmSim("X01DA-FE-XBPM2:", name="xbpm2")
|
||||||
|
|
||||||
|
xbpm1.wait_for_connection(timeout=5)
|
||||||
|
xbpm2.wait_for_connection(timeout=5)
|
||||||
|
|
||||||
|
xbpm1.rangew.set(1).wait()
|
||||||
|
xbpm2.rangew.set(1).wait()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
print("---")
|
||||||
|
xbpm1.sim()
|
||||||
|
xbpm2.sim()
|
23
ophyd_devices/epics/devices/__init__.py
Normal file
23
ophyd_devices/epics/devices/__init__.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
from .DelayGeneratorDG645 import DelayGeneratorDG645
|
||||||
|
from .slits import SlitH, SlitV
|
||||||
|
from .XbpmBase import XbpmBase, XbpmCsaxsOp
|
||||||
|
from .SpmBase import SpmBase
|
||||||
|
from .InsertionDevice import InsertionDevice
|
||||||
|
from .specMotors import (
|
||||||
|
PmMonoBender,
|
||||||
|
PmDetectorRotation,
|
||||||
|
GirderMotorX1,
|
||||||
|
GirderMotorY1,
|
||||||
|
GirderMotorROLL,
|
||||||
|
GirderMotorYAW,
|
||||||
|
GirderMotorPITCH,
|
||||||
|
MonoTheta1,
|
||||||
|
MonoTheta2,
|
||||||
|
EnergyKev,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Standard ophyd classes
|
||||||
|
from ophyd import EpicsSignal, EpicsSignalRO, EpicsMotor
|
||||||
|
from ophyd.sim import SynAxis, SynSignal, SynPeriodicSignal
|
||||||
|
from ophyd.quadem import QuadEM
|
30
ophyd_devices/epics/devices/cSaxsVirtualMotors.py
Normal file
30
ophyd_devices/epics/devices/cSaxsVirtualMotors.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
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
|
||||||
|
)
|
143
ophyd_devices/epics/devices/mono_dccm.py
Normal file
143
ophyd_devices/epics/devices/mono_dccm.py
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Wed Oct 13 18:06:15 2021
|
||||||
|
|
||||||
|
@author: mohacsi_i
|
||||||
|
|
||||||
|
IMPORTANT: Virtual monochromator axes should be implemented already in EPICS!!!
|
||||||
|
"""
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from math import isclose
|
||||||
|
from ophyd import (
|
||||||
|
EpicsSignal,
|
||||||
|
EpicsSignalRO,
|
||||||
|
EpicsMotor,
|
||||||
|
PseudoPositioner,
|
||||||
|
PseudoSingle,
|
||||||
|
Device,
|
||||||
|
Component,
|
||||||
|
Kind,
|
||||||
|
)
|
||||||
|
from ophyd.pseudopos import pseudo_position_argument, real_position_argument
|
||||||
|
from ophyd.sim import SynAxis, Syn2DGauss
|
||||||
|
|
||||||
|
LN_CORR = 2e-4
|
||||||
|
|
||||||
|
|
||||||
|
def a2e(angle, hkl=[1, 1, 1], lnc=False, bent=False, deg=False):
|
||||||
|
"""Convert between angle and energy for Si monchromators
|
||||||
|
ATTENTION: 'angle' must be in radians, not degrees!
|
||||||
|
"""
|
||||||
|
lncorr = LN_CORR if lnc else 0.0
|
||||||
|
angle = angle * np.pi / 180 if deg else angle
|
||||||
|
|
||||||
|
# Lattice constant along direction
|
||||||
|
d0 = 5.43102 * (1.0 - lncorr) / np.linalg.norm(hkl)
|
||||||
|
energy = 12.39842 / (2.0 * d0 * np.sin(angle))
|
||||||
|
return energy
|
||||||
|
|
||||||
|
|
||||||
|
def e2w(energy):
|
||||||
|
"""Convert between energy and wavelength"""
|
||||||
|
return 0.1 * 12398.42 / energy
|
||||||
|
|
||||||
|
|
||||||
|
def w2e(wwl):
|
||||||
|
"""Convert between wavelength and energy"""
|
||||||
|
return 12398.42 * 0.1 / wwl
|
||||||
|
|
||||||
|
|
||||||
|
def e2a(energy, hkl=[1, 1, 1], lnc=False, bent=False):
|
||||||
|
"""Convert between energy and angle for Si monchromators
|
||||||
|
ATTENTION: 'angle' must be in radians, not degrees!
|
||||||
|
"""
|
||||||
|
lncorr = LN_CORR if lnc else 0.0
|
||||||
|
|
||||||
|
# Lattice constant along direction
|
||||||
|
d0 = 2 * 5.43102 * (1.0 - lncorr) / np.linalg.norm(hkl)
|
||||||
|
angle = np.arcsin(12.39842 / d0 / energy)
|
||||||
|
|
||||||
|
# Rfine for bent mirror
|
||||||
|
if bent:
|
||||||
|
rho = 2 * 19.65 * 8.35 / 28 * np.sin(angle)
|
||||||
|
dt = 0.2e-3 / rho * 0.279
|
||||||
|
d0 = 2 * 5.43102 * (1.0 + dt) / np.linalg.norm(hkl)
|
||||||
|
angle = np.arcsin(12.39842 / d0 / energy)
|
||||||
|
|
||||||
|
return angle
|
||||||
|
|
||||||
|
|
||||||
|
class MonoMotor(PseudoPositioner):
|
||||||
|
"""Monochromator axis
|
||||||
|
|
||||||
|
Small wrapper to combine a real angular axis with the corresponding energy.
|
||||||
|
ATTENTION: 'angle' is in degrees, at least for PXIII
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Real axis (in degrees)
|
||||||
|
angle = Component(EpicsMotor, "", name="angle")
|
||||||
|
# Virtual axis
|
||||||
|
energy = Component(PseudoSingle, name="energy")
|
||||||
|
|
||||||
|
_real = ["angle"]
|
||||||
|
|
||||||
|
@pseudo_position_argument
|
||||||
|
def forward(self, pseudo_pos):
|
||||||
|
return self.RealPosition(angle=180.0 * e2a(pseudo_pos.energy) / 3.141592)
|
||||||
|
|
||||||
|
@real_position_argument
|
||||||
|
def inverse(self, real_pos):
|
||||||
|
return self.PseudoPosition(energy=a2e(3.141592 * real_pos.angle / 180.0))
|
||||||
|
|
||||||
|
|
||||||
|
class MonoDccm(PseudoPositioner):
|
||||||
|
"""Combined DCCM monochromator
|
||||||
|
|
||||||
|
The first crystal selects the energy, the second one is only following.
|
||||||
|
DCCMs are quite simple in terms that they can't crash and we don't
|
||||||
|
have a beam offset.
|
||||||
|
ATTENTION: 'angle' is in degrees, at least for PXIII
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Real axis (in degrees)
|
||||||
|
th1 = Component(EpicsMotor, "ROX1", name="theta1")
|
||||||
|
th2 = Component(EpicsMotor, "ROX2", name="theta2")
|
||||||
|
|
||||||
|
# Virtual axes
|
||||||
|
en1 = Component(PseudoSingle, name="en1")
|
||||||
|
en2 = Component(PseudoSingle, name="en2")
|
||||||
|
energy = Component(PseudoSingle, name="energy", kind=Kind.hinted)
|
||||||
|
|
||||||
|
# Other parameters
|
||||||
|
# feedback = Component(EpicsSignal, "MONOBEAM", name="feedback")
|
||||||
|
# enc1 = Component(EpicsSignalRO, "1:EXC1", name="enc1")
|
||||||
|
# enc2 = Component(EpicsSignalRO, "1:EXC2", name="enc2")
|
||||||
|
|
||||||
|
@pseudo_position_argument
|
||||||
|
def forward(self, pseudo_pos):
|
||||||
|
"""WARNING: We have an overdefined system! Not sure if common crystal movement is reliable without retuning"""
|
||||||
|
if (
|
||||||
|
abs(pseudo_pos.energy - self.energy.position) > 0.0001
|
||||||
|
and abs(pseudo_pos.en1 - self.en1.position) < 0.0001
|
||||||
|
and abs(pseudo_pos.en2 - self.en2.position) < 0.0001
|
||||||
|
):
|
||||||
|
# Probably the common energy was changed
|
||||||
|
return self.RealPosition(
|
||||||
|
th1=-180.0 * e2a(pseudo_pos.energy) / 3.141592,
|
||||||
|
th2=180.0 * e2a(pseudo_pos.energy) / 3.141592,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Probably the individual axes was changes
|
||||||
|
return self.RealPosition(
|
||||||
|
th1=-180.0 * e2a(pseudo_pos.en1) / 3.141592,
|
||||||
|
th2=180.0 * e2a(pseudo_pos.en2) / 3.141592,
|
||||||
|
)
|
||||||
|
|
||||||
|
@real_position_argument
|
||||||
|
def inverse(self, real_pos):
|
||||||
|
return self.PseudoPosition(
|
||||||
|
en1=-a2e(3.141592 * real_pos.th1 / 180.0),
|
||||||
|
en2=a2e(3.141592 * real_pos.th2 / 180.0),
|
||||||
|
energy=-a2e(3.141592 * real_pos.th1 / 180.0),
|
||||||
|
)
|
66
ophyd_devices/epics/devices/slits.py
Normal file
66
ophyd_devices/epics/devices/slits.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
from ophyd import Device, Component, EpicsMotor, PseudoPositioner, PseudoSingle
|
||||||
|
from ophyd.pseudopos import pseudo_position_argument, real_position_argument
|
||||||
|
|
||||||
|
|
||||||
|
class SlitH(PseudoPositioner):
|
||||||
|
"""Python wrapper for virtual slits
|
||||||
|
|
||||||
|
These devices should be implemented as an EPICS SoftMotor IOC,
|
||||||
|
but thats not the case for all slits. So here is a pure ophyd
|
||||||
|
implementation. Uses standard naming convention!
|
||||||
|
|
||||||
|
NOTE: The real and virtual axes are wrapped together.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Motor interface
|
||||||
|
x1 = Component(EpicsMotor, "TRX1")
|
||||||
|
x2 = Component(EpicsMotor, "TRX2")
|
||||||
|
|
||||||
|
cenx = Component(PseudoSingle)
|
||||||
|
gapx = Component(PseudoSingle)
|
||||||
|
|
||||||
|
@pseudo_position_argument
|
||||||
|
def forward(self, pseudo_pos):
|
||||||
|
"""Run a forward (pseudo -> real) calculation"""
|
||||||
|
return self.RealPosition(
|
||||||
|
x1=pseudo_pos.cenx - pseudo_pos.gapx / 2, x2=pseudo_pos.cenx + pseudo_pos.gapx / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
@real_position_argument
|
||||||
|
def inverse(self, real_pos):
|
||||||
|
"""Run an inverse (real -> pseudo) calculation"""
|
||||||
|
return self.PseudoPosition(
|
||||||
|
cenx=(real_pos.x1 + real_pos.x2) / 2, gapx=real_pos.x2 - real_pos.x1
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SlitV(PseudoPositioner):
|
||||||
|
"""Python wrapper for virtual slits
|
||||||
|
|
||||||
|
These devices should be implemented as an EPICS SoftMotor IOC,
|
||||||
|
but thats not the case for all slits. So here is a pure ophyd
|
||||||
|
implementation. Uses standard naming convention!
|
||||||
|
|
||||||
|
NOTE: The real and virtual axes are wrapped together.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Motor interface
|
||||||
|
y1 = Component(EpicsMotor, "TRY1")
|
||||||
|
y2 = Component(EpicsMotor, "TRY2")
|
||||||
|
|
||||||
|
ceny = Component(PseudoSingle)
|
||||||
|
gapy = Component(PseudoSingle)
|
||||||
|
|
||||||
|
@pseudo_position_argument
|
||||||
|
def forward(self, pseudo_pos):
|
||||||
|
"""Run a forward (pseudo -> real) calculation"""
|
||||||
|
return self.RealPosition(
|
||||||
|
y1=pseudo_pos.ceny - pseudo_pos.gapy / 2, y2=pseudo_pos.ceny + pseudo_pos.gapy / 2
|
||||||
|
)
|
||||||
|
|
||||||
|
@real_position_argument
|
||||||
|
def inverse(self, real_pos):
|
||||||
|
"""Run an inverse (real -> pseudo) calculation"""
|
||||||
|
return self.PseudoPosition(
|
||||||
|
ceny=(real_pos.y1 + real_pos.y2) / 2, gapy=real_pos.y2 - real_pos.y1
|
||||||
|
)
|
204
ophyd_devices/epics/devices/specMotors.py
Normal file
204
ophyd_devices/epics/devices/specMotors.py
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Created on Wed Oct 13 18:06:15 2021
|
||||||
|
|
||||||
|
@author: mohacsi_i
|
||||||
|
|
||||||
|
IMPORTANT: Virtual monochromator axes should be implemented already in EPICS!!!
|
||||||
|
"""
|
||||||
|
|
||||||
|
import numpy as np
|
||||||
|
from math import isclose, tan, atan, sqrt, sin, asin
|
||||||
|
from ophyd import (
|
||||||
|
EpicsSignal,
|
||||||
|
EpicsSignalRO,
|
||||||
|
EpicsMotor,
|
||||||
|
PseudoPositioner,
|
||||||
|
PseudoSingle,
|
||||||
|
PVPositioner,
|
||||||
|
Device,
|
||||||
|
Component,
|
||||||
|
Kind,
|
||||||
|
)
|
||||||
|
from ophyd.pseudopos import pseudo_position_argument, real_position_argument
|
||||||
|
from ophyd.utils.epics_pvs import data_type
|
||||||
|
|
||||||
|
|
||||||
|
class PmMonoBender(PseudoPositioner):
|
||||||
|
"""Monochromator bender
|
||||||
|
|
||||||
|
Small wrapper to combine the four monochromator bender motors.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Real axes
|
||||||
|
ai = Component(EpicsMotor, "TRYA", name="ai")
|
||||||
|
bo = Component(EpicsMotor, "TRYB", name="bo")
|
||||||
|
co = Component(EpicsMotor, "TRYC", name="co")
|
||||||
|
di = Component(EpicsMotor, "TRYD", name="di")
|
||||||
|
|
||||||
|
# Virtual axis
|
||||||
|
bend = Component(PseudoSingle, name="bend")
|
||||||
|
|
||||||
|
_real = ["ai", "bo", "co", "di"]
|
||||||
|
|
||||||
|
@pseudo_position_argument
|
||||||
|
def forward(self, pseudo_pos):
|
||||||
|
delta = pseudo_pos.bend - 0.25 * (
|
||||||
|
self.ai.position + self.bo.position + self.co.position + self.di.position
|
||||||
|
)
|
||||||
|
return self.RealPosition(
|
||||||
|
ai=self.ai.position + delta,
|
||||||
|
bo=self.bo.position + delta,
|
||||||
|
co=self.co.position + delta,
|
||||||
|
di=self.di.position + delta,
|
||||||
|
)
|
||||||
|
|
||||||
|
@real_position_argument
|
||||||
|
def inverse(self, real_pos):
|
||||||
|
return self.PseudoPosition(
|
||||||
|
bend=0.25 * (real_pos.ai + real_pos.bo + real_pos.co + real_pos.di)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def r2d(radians):
|
||||||
|
return radians * 180 / 3.141592
|
||||||
|
|
||||||
|
|
||||||
|
def d2r(degrees):
|
||||||
|
return degrees * 3.141592 / 180.0
|
||||||
|
|
||||||
|
|
||||||
|
class PmDetectorRotation(PseudoPositioner):
|
||||||
|
"""Detector rotation pseudo motor
|
||||||
|
|
||||||
|
Small wrapper to convert detector pusher position to rotation angle.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_tables_dt_push_dist_mm = 890
|
||||||
|
# Real axes
|
||||||
|
dtpush = Component(EpicsMotor, "", name="dtpush")
|
||||||
|
|
||||||
|
# Virtual axis
|
||||||
|
dtth = Component(PseudoSingle, name="dtth")
|
||||||
|
|
||||||
|
_real = ["dtpush"]
|
||||||
|
|
||||||
|
@pseudo_position_argument
|
||||||
|
def forward(self, pseudo_pos):
|
||||||
|
return self.RealPosition(
|
||||||
|
dtpush=d2r(tan(-3.14 / 180 * pseudo_pos.dtth)) * self._tables_dt_push_dist_mm
|
||||||
|
)
|
||||||
|
|
||||||
|
@real_position_argument
|
||||||
|
def inverse(self, real_pos):
|
||||||
|
return self.PseudoPosition(dtth=r2d(-atan(real_pos.dtpush / self._tables_dt_push_dist_mm)))
|
||||||
|
|
||||||
|
|
||||||
|
class GirderMotorX1(PVPositioner):
|
||||||
|
"""Girder X translation pseudo motor"""
|
||||||
|
|
||||||
|
setpoint = Component(EpicsSignal, ":X_SET", name="sp")
|
||||||
|
readback = Component(EpicsSignalRO, ":X1", name="rbv")
|
||||||
|
done = Component(EpicsSignal, ":M-DMOV", name="dmov")
|
||||||
|
|
||||||
|
|
||||||
|
class GirderMotorY1(PVPositioner):
|
||||||
|
"""Girder Y translation pseudo motor"""
|
||||||
|
|
||||||
|
setpoint = Component(EpicsSignal, ":Y_SET", name="sp")
|
||||||
|
readback = Component(EpicsSignalRO, ":Y1", name="rbv")
|
||||||
|
done = Component(EpicsSignal, ":M-DMOV", name="dmov")
|
||||||
|
|
||||||
|
|
||||||
|
class GirderMotorYAW(PVPositioner):
|
||||||
|
"""Girder YAW pseudo motor"""
|
||||||
|
|
||||||
|
setpoint = Component(EpicsSignal, ":YAW_SET", name="sp")
|
||||||
|
readback = Component(EpicsSignalRO, ":YAW1", name="rbv")
|
||||||
|
done = Component(EpicsSignal, ":M-DMOV", name="dmov")
|
||||||
|
|
||||||
|
|
||||||
|
class GirderMotorROLL(PVPositioner):
|
||||||
|
"""Girder ROLL pseudo motor"""
|
||||||
|
|
||||||
|
setpoint = Component(EpicsSignal, ":ROLL_SET", name="sp")
|
||||||
|
readback = Component(EpicsSignalRO, ":ROLL1", name="rbv")
|
||||||
|
done = Component(EpicsSignal, ":M-DMOV", name="dmov")
|
||||||
|
|
||||||
|
|
||||||
|
class GirderMotorPITCH(PVPositioner):
|
||||||
|
"""Girder YAW pseudo motor"""
|
||||||
|
|
||||||
|
setpoint = Component(EpicsSignal, ":PITCH_SET", name="sp")
|
||||||
|
readback = Component(EpicsSignalRO, ":PITCH1", name="rbv")
|
||||||
|
done = Component(EpicsSignal, ":M-DMOV", name="dmov")
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualEpicsSignalRO(EpicsSignalRO):
|
||||||
|
"""This is a test class to create derives signals from one or
|
||||||
|
multiple original signals...
|
||||||
|
"""
|
||||||
|
|
||||||
|
def calc(self, val):
|
||||||
|
return val
|
||||||
|
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
raw = super().get(*args, **kwargs)
|
||||||
|
return self.calc(raw)
|
||||||
|
|
||||||
|
# def describe(self):
|
||||||
|
# val = self.get()
|
||||||
|
# d = super().describe()
|
||||||
|
# d[self.name]["dtype"] = data_type(val)
|
||||||
|
# return d
|
||||||
|
|
||||||
|
|
||||||
|
class MonoTheta1(VirtualEpicsSignalRO):
|
||||||
|
"""Converts the pusher motor position to theta angle"""
|
||||||
|
|
||||||
|
_mono_a0_enc_scale1 = -1.0
|
||||||
|
_mono_a1_lever_length1 = 206.706
|
||||||
|
_mono_a2_pusher_offs1 = 6.85858
|
||||||
|
_mono_a3_enc_offs1 = -16.9731
|
||||||
|
|
||||||
|
def calc(self, val):
|
||||||
|
asin_arg = (val - self._mono_a2_pusher_offs1) / self._mono_a1_lever_length1
|
||||||
|
theta1 = (
|
||||||
|
self._mono_a0_enc_scale1 * asin(asin_arg) / 3.141592 * 180.0 + self._mono_a3_enc_offs1
|
||||||
|
)
|
||||||
|
return theta1
|
||||||
|
|
||||||
|
|
||||||
|
class MonoTheta2(VirtualEpicsSignalRO):
|
||||||
|
"""Converts the pusher motor position to theta angle"""
|
||||||
|
|
||||||
|
_mono_a3_enc_offs2 = -19.7072
|
||||||
|
_mono_a2_pusher_offs2 = 5.93905
|
||||||
|
_mono_a1_lever_length2 = 206.572
|
||||||
|
_mono_a0_enc_scale2 = -1.0
|
||||||
|
|
||||||
|
def calc(self, val):
|
||||||
|
asin_arg = (val - self._mono_a2_pusher_offs2) / self._mono_a1_lever_length2
|
||||||
|
theta2 = (
|
||||||
|
self._mono_a0_enc_scale2 * asin(asin_arg) / 3.141592 * 180.0 + self._mono_a3_enc_offs2
|
||||||
|
)
|
||||||
|
return theta2
|
||||||
|
|
||||||
|
|
||||||
|
class EnergyKev(VirtualEpicsSignalRO):
|
||||||
|
"""Converts the pusher motor position to energy in keV"""
|
||||||
|
|
||||||
|
_mono_a3_enc_offs2 = -19.7072
|
||||||
|
_mono_a2_pusher_offs2 = 5.93905
|
||||||
|
_mono_a1_lever_length2 = 206.572
|
||||||
|
_mono_a0_enc_scale2 = -1.0
|
||||||
|
_mono_hce = 12.39852066
|
||||||
|
_mono_2d2 = 2 * 5.43102 / sqrt(3)
|
||||||
|
|
||||||
|
def calc(self, val):
|
||||||
|
asin_arg = (val - self._mono_a2_pusher_offs2) / self._mono_a1_lever_length2
|
||||||
|
theta2_deg = (
|
||||||
|
self._mono_a0_enc_scale2 * asin(asin_arg) / 3.141592 * 180.0 + self._mono_a3_enc_offs2
|
||||||
|
)
|
||||||
|
E_keV = -self._mono_hce / self._mono_2d2 / sin(theta2_deg / 180.0 * 3.14152)
|
||||||
|
return E_keV
|
Loading…
x
Reference in New Issue
Block a user