diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 97c3def..f56988e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -138,7 +138,7 @@ pytest: config_test: stage: test script: - - ophyd_test --config ./ophyd_devices/epics/db/ --output ./config_tests + - ophyd_test --config ./ophyd_devices/configs/ --output ./config_tests artifacts: paths: - ./config_tests diff --git a/ophyd_devices/__init__.py b/ophyd_devices/__init__.py index 0afd3b9..4588441 100644 --- a/ophyd_devices/__init__.py +++ b/ophyd_devices/__init__.py @@ -2,7 +2,7 @@ from .ophyd_patch import monkey_patch_ophyd monkey_patch_ophyd() -from .epics import * +from .devices.sls_devices import SLSInfo, SLSOperatorMessages from .sim.sim import SimCamera from .sim.sim import SimFlyer from .sim.sim import SimFlyer as SynFlyer @@ -15,7 +15,6 @@ from .sim.sim import SimWaveform, SynDeviceOPAAS from .sim.sim_frameworks import DeviceProxy, H5ImageReplayProxy, SlitProxy from .sim.sim_signals import ReadOnlySignal from .sim.sim_signals import ReadOnlySignal as SynSignalRO -from .sls_devices.sls_devices import SLSInfo, SLSOperatorMessages from .utils.bec_device_base import BECDeviceBase from .utils.dynamic_pseudo import ComputedSignal from .utils.static_device_test import launch diff --git a/ophyd_devices/epics/devices/aerotech/__init__.py b/ophyd_devices/configs/__init__.py similarity index 100% rename from ophyd_devices/epics/devices/aerotech/__init__.py rename to ophyd_devices/configs/__init__.py diff --git a/ophyd_devices/configs/ophyd_devices_simulation.yaml b/ophyd_devices/configs/ophyd_devices_simulation.yaml new file mode 100644 index 0000000..90b10b8 --- /dev/null +++ b/ophyd_devices/configs/ophyd_devices_simulation.yaml @@ -0,0 +1,91 @@ +eiger: + readoutPriority: monitored + deviceClass: ophyd_devices.SimCamera + deviceConfig: + device_access: true + deviceTags: + - detector + enabled: true + readOnly: false + softwareTrigger: true + +dyn_signals: + readoutPriority: baseline + deviceClass: ophyd_devices.sim.sim.SynDynamicComponents + deviceConfig: + enabled: true + readOnly: false + +pseudo_signal1: + deviceClass: ophyd_devices.ComputedSignal + deviceConfig: + compute_method: "def compute_signals(signal1, signal2):\n return signal1.get()*signal2.get()\n" + input_signals: + - "bpm4i_readback" + - "bpm5i_readback" + enabled: true + readOnly: false + readoutPriority: baseline + +hexapod: + readoutPriority: baseline + deviceClass: ophyd_devices.SynDeviceOPAAS + deviceConfig: + deviceTags: + - user motors + enabled: true + readOnly: false + +eyefoc: + readoutPriority: baseline + deviceClass: ophyd_devices.SimPositioner + deviceConfig: + delay: 1 + limits: + - -50 + - 50 + tolerance: 0.01 + update_frequency: 400 + deviceTags: + - user motors + enabled: true + readOnly: false + +flyer_sim: + readoutPriority: on_request + deviceClass: ophyd_devices.SynFlyer + deviceConfig: + delay: 1 + device_access: true + update_frequency: 400 + deviceTags: + - flyer + enabled: true + readOnly: false + +bpm4i: + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false + +bpm5i: + readoutPriority: monitored + deviceClass: ophyd_devices.SimMonitor + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false + +ring_current_sim: + readoutPriority: monitored + deviceClass: ophyd_devices.ReadOnlySignal + deviceConfig: + deviceTags: + - beamline + enabled: true + readOnly: false diff --git a/ophyd_devices/epics/db/test_database.yml b/ophyd_devices/configs/ophyd_simulation.yaml similarity index 100% rename from ophyd_devices/epics/db/test_database.yml rename to ophyd_devices/configs/ophyd_simulation.yaml diff --git a/ophyd_devices/configs/sls_devices.yaml b/ophyd_devices/configs/sls_devices.yaml new file mode 100644 index 0000000..dc089d9 --- /dev/null +++ b/ophyd_devices/configs/sls_devices.yaml @@ -0,0 +1,19 @@ +sls_info: + readoutPriority: on_request + description: 'sls info' + deviceClass: ophyd_devices.SLSInfo + deviceConfig: {} + onFailure: buffer + enabled: true + readOnly: false + softwareTrigger: false + +sls_operator: + readoutPriority: on_request + description: 'sls operator messages' + deviceClass: ophyd_devices.SLSOperatorMessages + deviceConfig: {} + onFailure: buffer + enabled: true + readOnly: false + softwareTrigger: false \ No newline at end of file diff --git a/ophyd_devices/epics/devices/SpmBase.py b/ophyd_devices/devices/SpmBase.py similarity index 100% rename from ophyd_devices/epics/devices/SpmBase.py rename to ophyd_devices/devices/SpmBase.py diff --git a/ophyd_devices/epics/devices/XbpmBase.py b/ophyd_devices/devices/XbpmBase.py similarity index 100% rename from ophyd_devices/epics/devices/XbpmBase.py rename to ophyd_devices/devices/XbpmBase.py diff --git a/ophyd_devices/epics/__init__.py b/ophyd_devices/devices/__init__.py similarity index 58% rename from ophyd_devices/epics/__init__.py rename to ophyd_devices/devices/__init__.py index 39e8a80..ec060a7 100644 --- a/ophyd_devices/epics/__init__.py +++ b/ophyd_devices/devices/__init__.py @@ -1,10 +1,11 @@ -# Standard ophyd classes from ophyd import EpicsMotor, EpicsSignal, EpicsSignalRO from ophyd.quadem import QuadEM from ophyd.sim import SynAxis, SynPeriodicSignal, SynSignal -from .devices.slits import SlitH, SlitV -from .devices.specMotors import ( +from .epics_motor_ex import EpicsMotorEx +from .slits import SlitH, SlitV +from .sls_devices import SLSInfo, SLSOperatorMessages +from .specMotors import ( Bpm4i, EnergyKev, GirderMotorPITCH, @@ -17,8 +18,4 @@ from .devices.specMotors import ( PmDetectorRotation, PmMonoBender, ) -from .devices.SpmBase import SpmBase - -# X07MA specific devices -from .devices.X07MADevices import * -from .devices.XbpmBase import XbpmBase, XbpmCsaxsOp +from .SpmBase import SpmBase diff --git a/ophyd_devices/epics/devices/epics_motor_ex.py b/ophyd_devices/devices/epics_motor_ex.py similarity index 100% rename from ophyd_devices/epics/devices/epics_motor_ex.py rename to ophyd_devices/devices/epics_motor_ex.py diff --git a/ophyd_devices/epics/devices/mono_dccm.py b/ophyd_devices/devices/mono_dccm.py similarity index 100% rename from ophyd_devices/epics/devices/mono_dccm.py rename to ophyd_devices/devices/mono_dccm.py diff --git a/ophyd_devices/epics/devices/slits.py b/ophyd_devices/devices/slits.py similarity index 100% rename from ophyd_devices/epics/devices/slits.py rename to ophyd_devices/devices/slits.py diff --git a/ophyd_devices/epics/devices/slsDetector.py b/ophyd_devices/devices/slsDetector.py similarity index 100% rename from ophyd_devices/epics/devices/slsDetector.py rename to ophyd_devices/devices/slsDetector.py diff --git a/ophyd_devices/sls_devices/sls_devices.py b/ophyd_devices/devices/sls_devices.py similarity index 100% rename from ophyd_devices/sls_devices/sls_devices.py rename to ophyd_devices/devices/sls_devices.py diff --git a/ophyd_devices/epics/devices/specMotors.py b/ophyd_devices/devices/specMotors.py similarity index 100% rename from ophyd_devices/epics/devices/specMotors.py rename to ophyd_devices/devices/specMotors.py diff --git a/ophyd_devices/epics/DeviceFactory.py b/ophyd_devices/epics/DeviceFactory.py deleted file mode 100644 index 591e62c..0000000 --- a/ophyd_devices/epics/DeviceFactory.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -""" -Created on Wed Oct 13 17:06:51 2021 - -@author: mohacsi_i -""" - -import os - -import yaml -from devices import * -from ophyd.ophydobj import OphydObject - -# #################################################### -# 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}") diff --git a/ophyd_devices/epics/db/office_teststand_bec_config.yaml b/ophyd_devices/epics/db/office_teststand_bec_config.yaml deleted file mode 100644 index 1fc59b4..0000000 --- a/ophyd_devices/epics/db/office_teststand_bec_config.yaml +++ /dev/null @@ -1,56 +0,0 @@ -es1_roty: - description: 'Test rotation stage' -# deviceClass: EpicsMotor - deviceClass: ophyd_devices.epics.devices.EpicsMotorX - deviceConfig: {prefix: 'X02DA-ES1-SMP1:ROTY'} - onFailure: buffer - enabled: true - readoutPriority: monitored - -es1_aa1: - description: 'Rotation stage controller status' - deviceClass: ophyd_devices.epics.devices.aa1Controller - deviceConfig: {prefix: 'X02DA-ES1-SMP1:CTRL:'} - onFailure: buffer - enabled: true - readoutPriority: monitored - -es1_aa1Tasks: - description: 'AA1 task management interface' - deviceClass: ophyd_devices.epics.devices.aa1Tasks - deviceConfig: {prefix: 'X02DA-ES1-SMP1:TASK:'} - onFailure: buffer - enabled: true - readoutPriority: monitored - -es1_aa1GlobalVar: - description: 'AA1 global variables interface' - deviceClass: ophyd_devices.epics.devices.aa1GlobalVariables - deviceConfig: {prefix: 'X02DA-ES1-SMP1:VAR:'} - onFailure: buffer - enabled: true - readoutPriority: monitored - -es1_aa1GlobalMon: - description: 'AA1 polled global variable interface' - deviceClass: ophyd_devices.epics.devices.aa1GlobalVariableBindings - deviceConfig: {prefix: 'X02DA-ES1-SMP1:VAR:'} - onFailure: buffer - enabled: true - readoutPriority: monitored - -es1_psod: - description: 'AA1 PSO output interface' - deviceClass: ophyd_devices.epics.devices.aa1AxisPsoDistance - deviceConfig: {prefix: 'X02DA-ES1-SMP1:ROTY:PSO:'} - onFailure: buffer - enabled: true - readoutPriority: monitored - -es1_ddaq: - description: 'AA1 drive data collection interface' - deviceClass: ophyd_devices.epics.devices.aa1AxisDriveDataCollection - deviceConfig: {prefix: 'X02DA-ES1-SMP1:ROTY:DDC:'} - onFailure: buffer - enabled: true - readoutPriority: monitored diff --git a/ophyd_devices/epics/db/x07ma_database.yaml b/ophyd_devices/epics/db/x07ma_database.yaml deleted file mode 100644 index 169728d..0000000 --- a/ophyd_devices/epics/db/x07ma_database.yaml +++ /dev/null @@ -1,274 +0,0 @@ -slsinfo: - readoutPriority: baseline - description: 'SLS beam info' - deviceClass: ophyd_devices.SLSInfo - deviceConfig: {} - onFailure: buffer - enabled: true - readOnly: false - softwareTrigger: false - -undulator: - readoutPriority: baseline - description: 'Undulator' - deviceClass: ophyd_devices.X07MAUndulator - deviceConfig: - prefix: 'X07MA-ID:' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -aperture: - readoutPriority: baseline - description: 'Frontend aperture' - deviceClass: ophyd.EpicsSignal - deviceConfig: - read_pv: 'X07MA-FE-DSAPER' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -mono: - readoutPriority: baseline - description: 'PGM Monochromator' - deviceClass: ophyd_devices.PGMMonochromator - deviceConfig: - prefix: 'X07MA-' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -otf: - readoutPriority: baseline - description: 'PGM on-the-fly scan' - deviceClass: ophyd_devices.PGMOtFScan - deviceConfig: {} - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -valve: - readoutPriority: baseline - description: 'Endstation valve' - deviceClass: ophyd_devices.VacuumValve - deviceConfig: - prefix: 'X07MA-OP-VG13:' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -exit_slit: - readoutPriority: baseline - description: 'Exit slit' - deviceClass: ophyd_devices.X07MAExitSlit - deviceConfig: - prefix: 'X07MA-OP-SL1SV1:' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -goldmesh1: - readoutPriority: baseline - description: 'Gold mesh 1' - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: 'X07MA-OP-IO1:TR1' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -goldmesh2: - readoutPriority: baseline - description: 'Gold mesh 2' - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: 'X07MA-OP-IO2:TR1' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -field_x: - readoutPriority: baseline - description: 'Magnetic field x' - deviceClass: ophyd_devices.X07MAMagnetAxis - deviceConfig: - axis_id: X - ps_prefix: "X07MA-PC-PS2:" - prefix: 'X07MA-PC-MAG:' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -field_z: - readoutPriority: baseline - description: 'Magnetic field z' - deviceClass: ophyd_devices.X07MAMagnetAxis - deviceConfig: - axis_id: Z - ps_prefix: "X07MA-PC-PS1:" - prefix: 'X07MA-PC-MAG:' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -signals: - readoutPriority: baseline - description: 'ADC signals' - deviceClass: ophyd_devices.X07MAAnalogSignals - deviceConfig: - prefix: 'X07MA-ES1-AI:' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -sample_hor: - readoutPriority: baseline - description: 'Horizontal sample position' - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: 'X07MA-ES1-MAG:TRZS' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -sample_vert: - readoutPriority: baseline - description: 'Horizontal sample position' - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: 'X07MA-ES1-MAG:TRY1' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -sample_rot: - readoutPriority: baseline - description: 'Horizontal sample position' - deviceClass: ophyd.EpicsMotor - deviceConfig: - prefix: 'X07MA-ES1-MAG:ROY1' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -harmonic: - readoutPriority: baseline - description: 'ID harmonic' - deviceClass: ophyd.EpicsSignal - deviceConfig: - read_pv: 'X07MA-ID:HARMONIC' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -energy: - readoutPriority: baseline - description: 'Energy in eV' - deviceClass: ophyd.EpicsSignal - deviceConfig: - read_pv: 'X07MA-PHS-E:GO.A' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -energy_mode: - readoutPriority: baseline - description: 'Energy mode. Either PGM = 0 or PGM+ID = 1' - deviceClass: ophyd.EpicsSignal - deviceConfig: - read_pv: 'X07MA-PHS-E:OPT' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -keithley_1: - readoutPriority: baseline - description: 'Keithley 1 / 428 tey' - deviceClass: ophyd.EpicsSignal - deviceConfig: - read_pv: 'X07MA-PC-K428:1:setGain' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -keithley_2: - readoutPriority: baseline - description: 'Keithley 2 / 428 i0' - deviceClass: ophyd.EpicsSignal - deviceConfig: - read_pv: 'X07MA-PC-K428:2:setGain' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -keithley_3: - readoutPriority: baseline - description: 'Keithley 3 / 428 diode' - deviceClass: ophyd.EpicsSignal - deviceConfig: - read_pv: 'X07MA-PC-K428:3:setGain' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -temperature: - readoutPriority: baseline - description: 'Temperature controller' - deviceClass: ophyd_devices.X07MATemperatureController - deviceConfig: - prefix: 'X07MA-PC-TC:' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -tcontrol: - readoutPriority: baseline - description: 'Automatic temperature control' - deviceClass: ophyd_devices.X07MAAutoTemperatureControl - deviceConfig: - prefix: 'X07MA-ES1-TEMP:' - onFailure: retry - enabled: true - readOnly: false - softwareTrigger: false - -sls_info: - readoutPriority: on_request - description: 'sls info' - deviceClass: ophyd_devices.SLSInfo - deviceConfig: {} - onFailure: buffer - enabled: true - readOnly: false - softwareTrigger: false - -sls_operator: - readoutPriority: on_request - description: 'sls operator messages' - deviceClass: ophyd_devices.SLSOperatorMessages - deviceConfig: {} - onFailure: buffer - enabled: true - readOnly: false - softwareTrigger: false diff --git a/ophyd_devices/epics/devices/X07MADevices.py b/ophyd_devices/epics/devices/X07MADevices.py deleted file mode 100644 index efafac1..0000000 --- a/ophyd_devices/epics/devices/X07MADevices.py +++ /dev/null @@ -1,410 +0,0 @@ -""" -ophyd device classes for X07MA beamline -""" - -import time -import traceback -from collections import OrderedDict -from typing import Any - -from bec_lib import bec_logger -from ophyd import Component as Cpt -from ophyd import Device, EpicsMotor, EpicsSignal, EpicsSignalRO -from ophyd import FormattedComponent as FCpt -from ophyd import Kind, PVPositioner, Signal -from ophyd.flyers import FlyerInterface -from ophyd.pv_positioner import PVPositionerComparator -from ophyd.status import DeviceStatus, SubscriptionStatus - -logger = bec_logger.logger - - -__all__ = [ - "X07MAUndulator", - "PGMMonochromator", - "PGMOtFScan", - "VacuumValve", - "X07MAExitSlit", - "X07MAMagnetAxis", - "X07MAAnalogSignals", - "X07MASampleManipulator", - "X07MATemperatureController", - "X07MAAutoTemperatureControl", -] - - -class X07MAUndulator(PVPositioner): - """ - X07MA undulator - """ - - setpoint = Cpt(EpicsSignal, "ENERGY", auto_monitor=True) - readback = Cpt(EpicsSignalRO, "ENERGY-READ", kind=Kind.hinted, auto_monitor=True) - done = Cpt(EpicsSignalRO, "DONE", kind=Kind.omitted, auto_monitor=True) - stop_signal = Cpt(EpicsSignal, "STOP", kind=Kind.omitted) - - energy_offset = Cpt(EpicsSignal, "ENERGY-OFFS", kind=Kind.config) - pol_mode = Cpt(EpicsSignal, "MODE") - pol_angle = Cpt(EpicsSignal, "ALPHA") - harmonic = Cpt(EpicsSignal, "HARMONIC") - - def __init__( - self, - prefix="", - *, - limits=None, - name=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - egu="", - **kwargs, - ): - super().__init__( - prefix, - limits=limits, - name=name, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - egu=egu, - **kwargs, - ) - self.readback.name = self.name - - -class PGMMonochromator(PVPositioner): - """ - PGM monochromator - """ - - setpoint = Cpt(EpicsSignal, "PHS-E:GO.A", auto_monitor=True) - readback = Cpt(EpicsSignalRO, "PGM:CERBK", kind=Kind.hinted, auto_monitor=True) - done = Cpt(EpicsSignalRO, "PHS:alldone", kind=Kind.omitted, auto_monitor=True) - stop_signal = Cpt(EpicsSignal, "PGM:stop", kind=Kind.omitted) - - cff = Cpt(EpicsSignal, "PGM:rbkcff", write_pv="PGM:cff.A", kind=Kind.config) - with_undulator = Cpt(EpicsSignal, "PHS-E:OPT", kind=Kind.config) - - def __init__( - self, - prefix="", - *, - limits=None, - name=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - egu="", - **kwargs, - ): - super().__init__( - prefix, - limits=limits, - name=name, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - egu=egu, - **kwargs, - ) - self.readback.name = self.name - - -class PGMOtFScan(FlyerInterface, Device): - """ - PGM on-the-fly scan - """ - - SUB_VALUE = "value" - SUB_FLYER = "flyer" - _default_sub = SUB_VALUE - - e1 = Cpt(EpicsSignal, "E1", kind=Kind.config) - e2 = Cpt(EpicsSignal, "E2", kind=Kind.config) - time = Cpt(EpicsSignal, "TIME", kind=Kind.config) - folder = Cpt(EpicsSignal, "FOLDER", kind=Kind.config) - file = Cpt(EpicsSignal, "FILE", kind=Kind.config) - acquire = Cpt(EpicsSignal, "START", auto_monitor=True) - edata = Cpt(EpicsSignalRO, "EDATA", kind=Kind.hinted, auto_monitor=True) - data = Cpt(EpicsSignalRO, "DATA", kind=Kind.hinted, auto_monitor=True) - idata = Cpt(EpicsSignalRO, "IDATA", kind=Kind.hinted, auto_monitor=True) - fdata = Cpt(EpicsSignalRO, "FDATA", kind=Kind.hinted, auto_monitor=True) - count = Cpt(EpicsSignalRO, "COUNT", kind=Kind.omitted, auto_monitor=True) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._start_time = 0 - self.acquire.subscribe(self._update_status, run=False) - self.count.subscribe(self._update_data, run=False) - - def kickoff(self): - self._start_time = time.time() - self.acquire.put(1, use_complete=True) - status = DeviceStatus(self) - status.set_finished() - return status - - def complete(self): - def check_value(*, old_value, value, **kwargs): - return old_value == 1 and value == 0 - - status = SubscriptionStatus(self.acquire, check_value, event_type=self.acquire.SUB_VALUE) - return status - - def collect(self): - data = {"time": self._start_time, "data": {}, "timestamps": {}} - for attr in ("edata", "data", "idata", "fdata"): - obj = getattr(self, attr) - data["data"][obj.name] = obj.get() - data["timestamps"][obj.name] = obj.timestamp - - return data - - def describe_collect(self): - desc = OrderedDict() - for attr in ("edata", "data", "idata", "fdata"): - desc.update(getattr(self, attr).describe()) - return desc - - def _update_status(self, *, old_value, value, **kwargs): - if old_value == 1 and value == 0: - self._update_data(100) # make sure that the last entries are also emitted - self._done_acquiring() - - def _update_data(self, value, **kwargs): - try: - if value == 0: - return - data = self.collect() - - # FIXME: to avoid emitting outdated / stale data, wait until all signals report > 10 entries - if any(len(val) < 10 for val in data["data"].values()) or value < 10: - return - self._run_subs(sub_type=self.SUB_FLYER, value=data) - except Exception as exc: - content = traceback.format_exc() - logger.error(content) - - -class VacuumValve(PVPositionerComparator): - """ - EPS vacuum valve. - - The setpoint is of 2 choices - 0 - Close - 1 - Try open - - The readback is of 8 choices - 0 - TO CONNECT - 1 - MAN OPEN - 2 - CLOSED - 3 - ERROR - 4 - MOVING - 5 - OPEN - 6 - ERROR - 7 - ERROR - """ - - setpoint = Cpt(EpicsSignal, "WT_SET") - readback = Cpt(EpicsSignalRO, "POSITION") - - def __init__(self, prefix: str, *, name: str, **kwargs): - kwargs.update({"limits": (0, 1)}) - super().__init__(prefix, name=name, **kwargs) - self.readback.name = self.name - - def done_comparator(self, readback: Any, setpoint: Any) -> bool: - return readback != 4 - - -class X07MAExitSlit(PVPositioner): - """ - Exit slit - """ - - setpoint = Cpt(EpicsSignal, "TR_AP") - readback = Cpt(EpicsSignalRO, "TR_ISAP", kind=Kind.hinted, auto_monitor=True) - done = Cpt(EpicsSignalRO, "TR.DMOV", kind=Kind.omitted, auto_monitor=True) - - def __init__( - self, - prefix="", - *, - limits=None, - name=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - egu="", - **kwargs, - ): - super().__init__( - prefix, - limits=limits, - name=name, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - egu=egu, - **kwargs, - ) - self.readback.name = self.name - - -# class X07MAMagnet(Device): -# """ -# Magnet fields. -# """ - - -class X07MAMagnetAxis(PVPositioner): - """ - A single magnet field axis. - """ - - done_value = 1 - actuate_value = 1 - setpoint = FCpt(EpicsSignal, "{prefix}{_axis_id}:DMD") - readback = FCpt(EpicsSignalRO, "{prefix}{_axis_id}:RBV", kind=Kind.hinted, auto_monitor=True) - actuate = Cpt(EpicsSignal, "STARTRAMP.PROC", kind=Kind.omitted) - done = FCpt(EpicsSignalRO, "{prefix}{_axis_id}:RAMP:DONE", auto_monitor=True) - ramprate = FCpt( - EpicsSignal, "{_ps_prefix}STS:RAMPRATE:TPM", write_pv="{_ps_prefix}SET:DMD:RAMPRATE:TPM" - ) - - def __init__(self, prefix="", axis_id="", ps_prefix="", *, name=None, **kwargs): - self._axis_id = axis_id - self._ps_prefix = ps_prefix - super().__init__(prefix, name=name, **kwargs) - self.readback.name = self.name - - def _setup_move(self, position): - # If the setpoint is close to the current readback, the controller will not do anything, - # and the done value will not change. So we simply say is done. - if abs(position - self.readback.get()) < max(0.004, position * 0.0025): - self._done_moving(success=True, time=time.ctime(), value=position) - else: - super()._setup_move(position) - - # x = Cpt(MagnetAxis, "", axis_id="X", ps_prefix="X07MA-PC-PS2:", name="x") - # z = Cpt(MagnetAxis, "", axis_id="Z", ps_prefix="X07MA-PC-PS1:", name="z") - - -class NormSignal(Signal): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self._metadata.update(write_access=False) - - def wait_for_connection(self, timeout=0): - super().wait_for_connection(timeout) - self._metadata.update(connected=True) - - def get(self, **kwargs): - val1 = self.parent.signal1.get() - val2 = self.parent.signal2.get() - return val1 / val2 if val2 != 0 else 0 - - def describe(self): - desc = { - "shape": [], - "dtype": "number", - "source": "PV: {} / {}".format(self.parent.signal1.pvname, self.parent.signal2.pvname), - "units": "", - "precision": self.parent.signal1.precision, - } - return desc - - -class NormTEYSignals(Device): - signal1 = Cpt(EpicsSignalRO, name="signal1", read_pv="X07MA-ES1-AI:SIGNAL1", kind="omitted") - signal2 = Cpt(EpicsSignalRO, name="signal2", read_pv="X07MA-ES1-AI:SIGNAL2", kind="omitted") - norm = Cpt(NormSignal, name="norm", kind="hinted") - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.norm.name = self.name - - -class NormDIODESignals(Device): - signal1 = Cpt(EpicsSignalRO, name="signal1", read_pv="X07MA-ES1-AI:SIGNAL3", kind="omitted") - signal2 = Cpt(EpicsSignalRO, name="signal2", read_pv="X07MA-ES1-AI:SIGNAL2", kind="omitted") - norm = Cpt(NormSignal, name="norm", kind="hinted") - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.norm.name = self.name - - -class X07MAAnalogSignals(Device): - """ - ADC inputs - """ - - s1 = Cpt(EpicsSignalRO, "SIGNAL0", kind=Kind.hinted, auto_monitor=True) - s2 = Cpt(EpicsSignalRO, "SIGNAL1", kind=Kind.hinted, auto_monitor=True) - s3 = Cpt(EpicsSignalRO, "SIGNAL2", kind=Kind.hinted, auto_monitor=True) - s4 = Cpt(EpicsSignalRO, "SIGNAL3", kind=Kind.hinted, auto_monitor=True) - s5 = Cpt(EpicsSignalRO, "SIGNAL4", kind=Kind.hinted, auto_monitor=True) - s6 = Cpt(EpicsSignalRO, "SIGNAL5", kind=Kind.hinted, auto_monitor=True) - s7 = Cpt(EpicsSignalRO, "SIGNAL6", kind=Kind.hinted, auto_monitor=True) - norm_tey = Cpt(NormTEYSignals, name="norm_tey", kind=Kind.hinted) - norm_diode = Cpt(NormDIODESignals, name="norm_tey", kind=Kind.hinted) - - # Aliases - # tey = s1 - # i0 = s2 - # trans = s3 - # field_z = s4 - # field_x = s5 - - -class X07MASampleManipulator(Device): - """ - Sample manipulator - """ - - hor = Cpt(EpicsMotor, "TRZS") - vert = Cpt(EpicsMotor, "TRY1") - rot = Cpt(EpicsMotor, "ROY1") - - -class X07MATemperatureController(Device): - """ - Temperature controller - """ - - needle_valve = Cpt(EpicsSignal, "STS:LOOP2:MANUAL", write_pv="DMD:LOOP2:MANUAL") - setpoint = Cpt(EpicsSignal, "STS:LOOP1:SETPOINT", write_pv="DMD:LOOP1:SETPOINT") - readback = Cpt(EpicsSignalRO, "STS:T1", kind=Kind.hinted, auto_monitor=True) - - def __init__( - self, - prefix="", - *, - name, - kind=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - **kwargs, - ): - super().__init__( - prefix, - name=name, - kind=kind, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - **kwargs, - ) - self.readback.name = self.name - - -class X07MAAutoTemperatureControl(Device): - """ - Automatic temperature control. - """ - - control = Cpt(EpicsSignal, "CONTROL") - status = Cpt(EpicsSignalRO, "STATUS", string=True, auto_monitor=True) diff --git a/ophyd_devices/epics/devices/__init__.py b/ophyd_devices/epics/devices/__init__.py deleted file mode 100644 index db2470c..0000000 --- a/ophyd_devices/epics/devices/__init__.py +++ /dev/null @@ -1,34 +0,0 @@ -# Standard ophyd classes -from ophyd import EpicsMotor, EpicsSignal, EpicsSignalRO -from ophyd.quadem import QuadEM -from ophyd.sim import SynAxis, SynPeriodicSignal, SynSignal - -from .aerotech.AerotechAutomation1 import ( - EpicsMotorX, - aa1AxisDriveDataCollection, - aa1AxisPsoDistance, - aa1Controller, - aa1GlobalVariableBindings, - aa1GlobalVariables, - aa1Tasks, -) - -# cSAXS -from .epics_motor_ex import EpicsMotorEx -from .grashopper_tomcat import GrashopperTOMCAT -from .psi_detector_base import CustomDetectorMixin, PSIDetectorBase -from .slits import SlitH, SlitV -from .specMotors import ( - Bpm4i, - EnergyKev, - GirderMotorPITCH, - GirderMotorROLL, - GirderMotorX1, - GirderMotorY1, - GirderMotorYAW, - MonoTheta1, - MonoTheta2, - PmDetectorRotation, - PmMonoBender, -) -from .SpmBase import SpmBase diff --git a/ophyd_devices/epics/devices/aerotech/AerotechAutomation1.py b/ophyd_devices/epics/devices/aerotech/AerotechAutomation1.py deleted file mode 100644 index 0a1ef5e..0000000 --- a/ophyd_devices/epics/devices/aerotech/AerotechAutomation1.py +++ /dev/null @@ -1,1490 +0,0 @@ -import time -from collections import OrderedDict -from time import sleep - -import numpy as np -from ophyd import Component, Device, EpicsMotor, EpicsSignal, EpicsSignalRO, Kind -from ophyd.status import DeviceStatus, Status, StatusBase, SubscriptionStatus - -from ophyd_devices.epics.devices.aerotech.AerotechAutomation1Enums import ( - DataCollectionFrequency, - DataCollectionMode, - DriveDataCaptureInput, - DriveDataCaptureTrigger, -) - - -class EpicsMotorX(EpicsMotor): - """Special motor class that provides flyer interface and progress bar.""" - - SUB_PROGRESS = "progress" - - def __init__( - self, - prefix="", - *, - name, - kind=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - **kwargs, - ): - super().__init__( - prefix=prefix, - name=name, - kind=kind, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - **kwargs, - ) - self._startPosition = None - self._targetPosition = None - self.subscribe(self._progress_update, run=False) - - def configure(self, d: dict): - if "target" in d: - self._targetPosition = d["target"] - del d["target"] - if "position" in d: - self._targetPosition = d["position"] - del d["position"] - return super().configure(d) - - def kickoff(self): - self._startPosition = float(self.position) - return self.move(self._targetPosition, wait=False) - - def move(self, position, wait=True, **kwargs): - self._startPosition = float(self.position) - return super().move(position, wait, **kwargs) - - def _progress_update(self, value, **kwargs) -> None: - """Progress update on the scan""" - if (self._startPosition is None) or (self._targetPosition is None) or (not self.moving): - self._run_subs(sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1) - return - - progress = np.abs( - (value - self._startPosition) / (self._targetPosition - self._startPosition) - ) - max_value = 100 - self._run_subs( - sub_type=self.SUB_PROGRESS, - value=int(100 * progress), - max_value=max_value, - done=int(np.isclose(max_value, progress, 1e-3)), - ) - - -class EpicsPassiveRO(EpicsSignalRO): - """Small helper class to read PVs that need to be processed first.""" - - def __init__(self, read_pv, *, string=False, name=None, **kwargs): - super().__init__(read_pv, string=string, name=name, **kwargs) - self._proc = EpicsSignal(read_pv + ".PROC", kind=Kind.omitted, put_complete=True) - - def wait_for_connection(self, *args, **kwargs): - super().wait_for_connection(*args, **kwargs) - self._proc.wait_for_connection(*args, **kwargs) - - def get(self, *args, **kwargs): - self._proc.set(1).wait() - return super().get(*args, **kwargs) - - @property - def value(self): - return super().value - - -class aa1Controller(Device): - """Ophyd proxy class for the Aerotech Automation 1's core controller functionality""" - - controllername = Component(EpicsSignalRO, "NAME", kind=Kind.config) - serialnumber = Component(EpicsSignalRO, "SN", kind=Kind.config) - apiversion = Component(EpicsSignalRO, "API_VERSION", kind=Kind.config) - axiscount = Component(EpicsSignalRO, "AXISCOUNT", kind=Kind.config) - taskcount = Component(EpicsSignalRO, "TASKCOUNT", kind=Kind.config) - fastpoll = Component(EpicsSignalRO, "POLLTIME", auto_monitor=True, kind=Kind.hinted) - slowpoll = Component(EpicsSignalRO, "DRVPOLLTIME", auto_monitor=True, kind=Kind.hinted) - - def __init__( - self, - prefix="", - *, - name, - kind=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - **kwargs, - ): - super().__init__( - prefix=prefix, - name=name, - kind=kind, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - **kwargs, - ) - - -class aa1Tasks(Device): - """Task management API - - The place to manage tasks and AeroScript user files on the controller. - You can read/write/compile/execute AeroScript files and also retrieve - saved data files from the controller. It will also work around an ophyd - bug that swallows failures. - - Execution does not require to store the script in a file, it will compile - it and run it directly on a certain thread. But there's no way to - retrieve the source code. - - Write a text into a file on the aerotech controller and execute it with kickoff. - ''' - script="var $axis as axis = ROTY\\nMoveAbsolute($axis, 42, 90)" - tsk = aa1Tasks(AA1_IOC_NAME+":TASK:", name="tsk") - tsk.wait_for_connection() - tsk.configure({'text': script, 'filename': "foobar.ascript", 'taskIndex': 4}) - tsk.kickoff().wait() - ''' - - Just execute an ascript file already on the aerotech controller. - ''' - tsk = aa1Tasks(AA1_IOC_NAME+":TASK:", name="tsk") - tsk.wait_for_connection() - tsk.configure({'filename': "foobar.ascript", 'taskIndex': 4}) - tsk.kickoff().wait() - ''' - - """ - - SUB_PROGRESS = "progress" - _failure = Component(EpicsSignalRO, "FAILURE", auto_monitor=True, kind=Kind.hinted) - errStatus = Component(EpicsSignalRO, "ERRW", auto_monitor=True, kind=Kind.hinted) - warnStatus = Component(EpicsSignalRO, "WARNW", auto_monitor=True, kind=Kind.hinted) - taskStates = Component(EpicsSignalRO, "STATES-RBV", auto_monitor=True, kind=Kind.hinted) - taskIndex = Component(EpicsSignal, "TASKIDX", kind=Kind.config, put_complete=True) - switch = Component(EpicsSignal, "SWITCH", kind=Kind.config, put_complete=True) - _execute = Component(EpicsSignal, "EXECUTE", string=True, kind=Kind.config, put_complete=True) - _executeMode = Component(EpicsSignal, "EXECUTE-MODE", kind=Kind.config, put_complete=True) - _executeReply = Component(EpicsSignalRO, "EXECUTE_RBV", string=True, auto_monitor=True) - - fileName = Component(EpicsSignal, "FILENAME", string=True, kind=Kind.omitted, put_complete=True) - _fileRead = Component(EpicsPassiveRO, "FILEREAD", string=True, kind=Kind.omitted) - _fileWrite = Component( - EpicsSignal, "FILEWRITE", string=True, kind=Kind.omitted, put_complete=True - ) - - def __init__( - self, - prefix="", - *, - name, - kind=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - **kwargs, - ): - """__init__ MUST have a full argument list""" - super().__init__( - prefix=prefix, - name=name, - kind=kind, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - **kwargs, - ) - self._currentTask = None - self._textToExecute = None - self._isConfigured = False - self._isStepConfig = False - self.subscribe(self._progress_update, "progress", run=False) - - def _progress_update(self, value, **kwargs) -> None: - """Progress update on the scan""" - value = self.progress() - self._run_subs(sub_type=self.SUB_PROGRESS, value=value, max_value=1, done=1) - - def _progress(self) -> None: - """Progress update on the scan""" - if self._currentTaskMonitor is None: - return 1 - else: - if self._currentTaskMonitor.status.value in ["Running", 4]: - return 0 - else: - return 1 - - def readFile(self, filename: str) -> str: - """Read a file from the controller""" - # Have to use CHAR array due to EPICS LSI bug... - self.fileName.set(filename).wait() - filebytes = self._fileRead.get() - # C-strings terminate with trailing zero - if filebytes[-1] == 0: - filebytes = filebytes[:-1] - filetext = filebytes - return filetext - - def writeFile(self, filename: str, filetext: str) -> None: - """Write a file to the controller""" - self.fileName.set(filename).wait() - self._fileWrite.set(filetext).wait() - - def runScript(self, filename: str, taskIndex: int == 2, filetext=None, settle_time=0.5) -> None: - """Run a script file that either exists, or is newly created and compiled""" - - self.configure({"text": filetext, "filename": filename, "taskIndex": taskIndex}) - print("Runscript configured") - self.trigger().wait() - print("Runscript waited") - - def execute(self, text: str, taskIndex: int = 3, mode: str = 0, settle_time=0.5): - """Run a short text command on the Automation1 controller""" - - print(f"Executing program on task: {taskIndex}") - self.configure({"text": text, "taskIndex": taskIndex, "mode": mode}) - self.kickoff().wait() - - if mode in [0, "None", None]: - return None - else: - raw = self._executeReply.get() - return raw - - def configure(self, d: dict = {}) -> tuple: - """Configuration interface for flying""" - # Unrolling the configuration dict - text = str(d["text"]) if "text" in d else None - filename = str(d["filename"]) if "filename" in d else None - taskIndex = int(d["taskIndex"]) if "taskIndex" in d else 4 - settle_time = float(d["settle_time"]) if "settle_time" in d else None - mode = d["mode"] if "mode" in d else None - self._isStepConfig = d["stepper"] if "stepper" in d else False - - # Validation - if taskIndex < 1 or taskIndex > 31: - raise RuntimeError(f"Invalid task index: {taskIndex}") - if (text is None) and (filename is None): - raise RuntimeError("Task execution requires either AeroScript text or filename") - - # Common operations - old = self.read_configuration() - self.taskIndex.set(taskIndex).wait() - self._textToExecute = None - self._currentTask = taskIndex - - # Choose the right execution mode - if (filename is None) and (text not in [None, ""]): - # Direct command execution - print("Preparing for direct command execution") - if mode is not None: - self._executeMode.set(mode).wait() - self._textToExecute = text - elif (filename is not None) and (text in [None, ""]): - # Execute existing file - self.fileName.set(filename).wait() - self.switch.set("Load").wait() - elif (filename is not None) and (text not in [None, ""]): - print("Preparing to execute via intermediary file") - # Execute text via intermediate file - self.taskIndex.set(taskIndex).wait() - self.fileName.set(filename).wait() - self._fileWrite.set(text).wait() - self.switch.set("Load").wait() - self._textToExecute = None - else: - raise RuntimeError("Unsupported filename-text combo") - - if self._failure.value: - raise RuntimeError("Failed to launch task, please check the Aerotech IOC") - - self._isConfigured = True - new = self.read_configuration() - return (old, new) - - ########################################################################## - # Bluesky stepper interface - def stage(self) -> None: - """Default staging""" - super().stage() - - def unstage(self) -> None: - """Default unstaging""" - super().unstage() - - def trigger(self, settle_time=0.2) -> Status: - """Execute the script on the configured task""" - if self._isStepConfig: - return self.kickoff(settle_time) - else: - status = DeviceStatus(self, settle_time=settle_time) - status.set_finished() - if settle_time is not None: - sleep(settle_time) - return status - - def stop(self): - """Stop the currently selected task""" - self.switch.set("Stop").wait() - - ########################################################################## - # Flyer interface - def kickoff(self, settle_time=0.2) -> DeviceStatus: - """Execute the script on the configured task""" - if self._isConfigured: - if self._textToExecute is not None: - print(f"Kickoff directly executing string: {self._textToExecute}") - status = self._execute.set(self._textToExecute, settle_time=0.5) - else: - status = self.switch.set("Run", settle_time=0.1) - else: - status = DeviceStatus(self) - status.set_finished() - if settle_time is not None: - sleep(settle_time) - return status - - def complete(self) -> DeviceStatus: - """Execute the script on the configured task""" - print("Called aa1Task.complete()") - timestamp_ = 0 - taskIdx = int(self.taskIndex.get()) - - def notRunning2(*args, old_value, value, timestamp, **kwargs): - nonlocal timestamp_ - result = False if value[taskIdx] in ["Running", 4] else True - timestamp_ = timestamp - print(result) - return result - - # Subscribe and wait for update - status = SubscriptionStatus(self.taskStates, notRunning2, settle_time=0.5) - return status - - def describe_collect(self) -> OrderedDict: - dd = OrderedDict() - dd["success"] = { - "source": "internal", - "dtype": "integer", - "shape": [], - "units": "", - "lower_ctrl_limit": 0, - "upper_ctrl_limit": 0, - } - return {self.name: dd} - - def collect(self) -> OrderedDict: - ret = OrderedDict() - ret["timestamps"] = {"success": time.time()} - ret["data"] = {"success": 1} - yield ret - - -class aa1TaskState(Device): - """Task state monitoring API - - This is the task state monitoring interface for Automation1 tasks. It - does not launch execution, but can wait for the execution to complete. - """ - - index = Component(EpicsSignalRO, "INDEX", kind=Kind.config) - status = Component(EpicsSignalRO, "STATUS", auto_monitor=True, kind=Kind.hinted) - errorCode = Component(EpicsSignalRO, "ERROR", auto_monitor=True, kind=Kind.hinted) - warnCode = Component(EpicsSignalRO, "WARNING", auto_monitor=True, kind=Kind.hinted) - - def complete(self) -> StatusBase: - """Bluesky flyer interface""" - print("Called aa1TaskState.complete()") - # Define wait until the busy flag goes down (excluding initial update) - timestamp_ = 0 - - def notRunning(*args, old_value, value, timestamp, **kwargs): - nonlocal timestamp_ - result = False if (timestamp_ == 0) else (value not in ["Running", 4]) - timestamp_ = timestamp - print(result) - return result - - # Subscribe and wait for update - status = SubscriptionStatus(self.status, notRunning, settle_time=0.5) - return status - - def kickoff(self) -> DeviceStatus: - status = DeviceStatus(self) - status.set_finished() - return status - - def describe_collect(self) -> OrderedDict: - dd = OrderedDict() - dd["success"] = { - "source": "internal", - "dtype": "integer", - "shape": [], - "units": "", - "lower_ctrl_limit": 0, - "upper_ctrl_limit": 0, - } - return dd - - def collect(self) -> OrderedDict: - ret = OrderedDict() - ret["timestamps"] = {"success": time.time()} - ret["data"] = {"success": 1} - yield ret - - -class aa1DataAcquisition(Device): - """Controller Data Acquisition - DONT USE at Tomcat - - This class implements the controller data collection feature of the - Automation1 controller. This feature logs various inputs at a - **fixed frequency** from 1 kHz up to 200 kHz. - Usage: - 1. Start a new configuration with "startConfig" - 2. Add your signals with "addXxxSignal" - 3. Start your data collection - 4. Read back the recorded data with "readback" - """ - - # Status monitoring - status = Component(EpicsSignalRO, "RUNNING", auto_monitor=True, kind=Kind.hinted) - points_max = Component(EpicsSignal, "MAXPOINTS", kind=Kind.config, put_complete=True) - signal_num = Component(EpicsSignalRO, "NITEMS", kind=Kind.config) - - points_total = Component(EpicsSignalRO, "NTOTAL", auto_monitor=True, kind=Kind.hinted) - points_collected = Component(EpicsSignalRO, "NCOLLECTED", auto_monitor=True, kind=Kind.hinted) - points_retrieved = Component(EpicsSignalRO, "NRETRIEVED", auto_monitor=True, kind=Kind.hinted) - overflow = Component(EpicsSignalRO, "OVERFLOW", auto_monitor=True, kind=Kind.hinted) - runmode = Component(EpicsSignalRO, "MODE_RBV", auto_monitor=True, kind=Kind.hinted) - # DAQ setup - numpoints = Component(EpicsSignal, "NPOINTS", kind=Kind.config, put_complete=True) - frequency = Component(EpicsSignal, "FREQUENCY", kind=Kind.config, put_complete=True) - _configure = Component(EpicsSignal, "CONFIGURE", kind=Kind.omitted, put_complete=True) - - def startConfig(self, npoints: int, frequency: DataCollectionFrequency): - self.numpoints.set(npoints).wait() - self.frequency.set(frequency).wait() - self._configure.set("START").wait() - - def clearConfig(self): - self._configure.set("CLEAR").wait() - - srcTask = Component(EpicsSignal, "SRC_TASK", kind=Kind.config, put_complete=True) - srcAxis = Component(EpicsSignal, "SRC_AXIS", kind=Kind.config, put_complete=True) - srcCode = Component(EpicsSignal, "SRC_CODE", kind=Kind.config, put_complete=True) - _srcAdd = Component(EpicsSignal, "SRC_ADD", kind=Kind.omitted, put_complete=True) - - def addAxisSignal(self, axis: int, code: int) -> None: - """Add a new axis-specific data signal to the DAQ configuration. The - most common signals are PositionFeedback and PositionError. - """ - self.srcAxis.set(axis).wait() - self.srcCode.set(code).wait() - self._srcAdd.set("AXIS").wait() - - def addTaskSignal(self, task: int, code: int) -> None: - """Add a new task-specific data signal to the DAQ configuration""" - self.srcTask.set(task).wait() - self.srcCode.set(code).wait() - self._srcAdd.set("TASK").wait() - - def addSystemSignal(self, code: int) -> None: - """Add a new system data signal to the DAQ configuration. The most - common signal is SampleCollectionTime.""" - self.srcCode.set(code).wait() - self._srcAdd.set("SYSTEM").wait() - - # Starting / stopping the DAQ - _mode = Component(EpicsSignal, "MODE", kind=Kind.config, put_complete=True) - _switch = Component(EpicsSignal, "SET", kind=Kind.omitted, put_complete=True) - - def start(self, mode=DataCollectionMode.Snapshot) -> None: - """Start a new data collection""" - self._mode.set(mode).wait() - self._switch.set("START").wait() - - def stop(self) -> None: - """Stop a running data collection""" - self._switch.set("STOP").wait() - - def run(self, mode=DataCollectionMode.Snapshot) -> None: - """Start a new data collection""" - self._mode.set(mode).wait() - self._switch.set("START").wait() - # Wait for finishing acquisition - # Note: this is very bad blocking sleep - while self.status.value != 0: - sleep(0.1) - sleep(0.1) - - # Data readback - data = self.data_rb.get() - rows = self.data_rows.get() - cols = self.data_cols.get() - if len(data) == 0 or rows == 0 or cols == 0: - sleep(0.5) - data = self.data_rb.get() - rows = self.data_rows.get() - cols = self.data_cols.get() - print(f"Data shape: {rows} x {cols}") - data = data.reshape([int(rows), -1]) - return data - - # DAQ data readback - data_rb = Component(EpicsPassiveRO, "DATA", kind=Kind.hinted) - data_rows = Component(EpicsSignalRO, "DATA_ROWS", auto_monitor=True, kind=Kind.hinted) - data_cols = Component(EpicsSignalRO, "DATA_COLS", auto_monitor=True, kind=Kind.hinted) - data_stat = Component(EpicsSignalRO, "DATA_AVG", auto_monitor=True, kind=Kind.hinted) - - def dataReadBack(self) -> np.ndarray: - """Retrieves collected data from the controller""" - data = self.data_rb.get() - rows = self.data_rows.get() - cols = self.data_cols.get() - if len(data) == 0 or rows == 0 or cols == 0: - sleep(0.2) - data = self.data_rb.get() - rows = self.data_rows.get() - cols = self.data_cols.get() - print(f"Data shape: {rows} x {cols}") - data = data.reshape([int(rows), -1]) - return data - - -class aa1GlobalVariables(Device): - """Global variables - - This class provides an interface to directly read/write global variables - on the Automation1 controller. These variables are accesible from script - files and are thus a convenient way to interface with the outside word. - - Read operations take as input the memory address and the size - Write operations work with the memory address and value - - Usage: - var = aa1Tasks(AA1_IOC_NAME+":VAR:", name="var") - var.wait_for_connection() - ret = var.readInt(42) - var.writeFloat(1000, np.random.random(1024)) - ret_arr = var.readFloat(1000, 1024) - - """ - - # Status monitoring - num_real = Component(EpicsSignalRO, "NUM-REAL_RBV", kind=Kind.config) - num_int = Component(EpicsSignalRO, "NUM-INT_RBV", kind=Kind.config) - num_string = Component(EpicsSignalRO, "NUM-STRING_RBV", kind=Kind.config) - - integer_addr = Component(EpicsSignal, "INT-ADDR", kind=Kind.omitted, put_complete=True) - integer_size = Component(EpicsSignal, "INT-SIZE", kind=Kind.omitted, put_complete=True) - integer = Component(EpicsSignal, "INT", kind=Kind.omitted, put_complete=True) - integer_rb = Component(EpicsPassiveRO, "INT-RBV", kind=Kind.omitted) - integerarr = Component(EpicsSignal, "INTARR", kind=Kind.omitted, put_complete=True) - integerarr_rb = Component(EpicsPassiveRO, "INTARR-RBV", kind=Kind.omitted) - - real_addr = Component(EpicsSignal, "REAL-ADDR", kind=Kind.omitted, put_complete=True) - real_size = Component(EpicsSignal, "REAL-SIZE", kind=Kind.omitted, put_complete=True) - real = Component(EpicsSignal, "REAL", kind=Kind.omitted, put_complete=True) - real_rb = Component(EpicsPassiveRO, "REAL-RBV", kind=Kind.omitted) - realarr = Component(EpicsSignal, "REALARR", kind=Kind.omitted, put_complete=True) - realarr_rb = Component(EpicsPassiveRO, "REALARR-RBV", kind=Kind.omitted) - - string_addr = Component(EpicsSignal, "STRING-ADDR", kind=Kind.omitted, put_complete=True) - string = Component(EpicsSignal, "STRING", string=True, kind=Kind.omitted, put_complete=True) - string_rb = Component(EpicsPassiveRO, "STRING-RBV", string=True, kind=Kind.omitted) - - def readInt(self, address: int, size: int = None) -> int: - """Read a 64-bit integer global variable""" - if address > self.num_int.get(): - raise RuntimeError("Integer address {address} is out of range") - - if size is None: - self.integer_addr.set(address).wait() - return self.integer_rb.get() - else: - self.integer_addr.set(address).wait() - self.integer_size.set(size).wait() - return self.integerarr_rb.get() - - def writeInt(self, address: int, value) -> None: - """Write a 64-bit integer global variable""" - if address > self.num_int.get(): - raise RuntimeError("Integer address {address} is out of range") - - if isinstance(value, (int, float)): - self.integer_addr.set(address).wait() - self.integer.set(value).wait() - elif isinstance(value, np.ndarray): - self.integer_addr.set(address).wait() - self.integerarr.set(value).wait() - elif isinstance(value, (list, tuple)): - value = np.array(value, dtype=np.int32) - self.integer_addr.set(address).wait() - self.integerarr.set(value).wait() - else: - raise RuntimeError("Unsupported integer value type: {type(value)}") - - def readFloat(self, address: int, size: int = None) -> float: - """Read a 64-bit double global variable""" - if address > self.num_real.get(): - raise RuntimeError("Floating point address {address} is out of range") - - if size is None: - self.real_addr.set(address).wait() - return self.real_rb.get() - else: - self.real_addr.set(address).wait() - self.real_size.set(size).wait() - return self.realarr_rb.get() - - def writeFloat(self, address: int, value) -> None: - """Write a 64-bit float global variable""" - if address > self.num_real.get(): - raise RuntimeError("Float address {address} is out of range") - - if isinstance(value, (int, float)): - self.real_addr.set(address).wait() - self.real.set(value).wait() - elif isinstance(value, np.ndarray): - self.real_addr.set(address).wait() - self.realarr.set(value).wait() - elif isinstance(value, (list, tuple)): - value = np.array(value) - self.real_addr.set(address).wait() - self.realarr.set(value).wait() - else: - raise RuntimeError("Unsupported float value type: {type(value)}") - - def readString(self, address: int) -> str: - """Read a 40 letter string global variable - ToDo: Automation 1 strings are 256 bytes - """ - if address > self.num_string.get(): - raise RuntimeError("String address {address} is out of range") - - self.string_addr.set(address).wait() - return self.string_rb.get() - - def writeString(self, address: int, value) -> None: - """Write a 40 bytes string global variable""" - if address > self.num_string.get(): - raise RuntimeError("Integer address {address} is out of range") - - if isinstance(value, str): - self.string_addr.set(address).wait() - self.string.set(value).wait() - else: - raise RuntimeError("Unsupported string value type: {type(value)}") - - -class aa1GlobalVariableBindings(Device): - """Polled global variables - - This class provides an interface to read/write the first few global variables - on the Automation1 controller. These variables are continuously polled - and are thus a convenient way to interface scripts with the outside word. - """ - - int0 = Component(EpicsSignalRO, "INT0_RBV", auto_monitor=True, name="int0", kind=Kind.hinted) - int1 = Component(EpicsSignalRO, "INT1_RBV", auto_monitor=True, name="int1", kind=Kind.hinted) - int2 = Component(EpicsSignalRO, "INT2_RBV", auto_monitor=True, name="int2", kind=Kind.hinted) - int3 = Component(EpicsSignalRO, "INT3_RBV", auto_monitor=True, name="int3", kind=Kind.hinted) - int8 = Component( - EpicsSignal, - "INT8_RBV", - put_complete=True, - write_pv="INT8", - auto_monitor=True, - name="int8", - kind=Kind.hinted, - ) - int9 = Component( - EpicsSignal, - "INT9_RBV", - put_complete=True, - write_pv="INT9", - auto_monitor=True, - name="int9", - kind=Kind.hinted, - ) - int10 = Component( - EpicsSignal, - "INT10_RBV", - put_complete=True, - write_pv="INT10", - auto_monitor=True, - name="int10", - kind=Kind.hinted, - ) - int11 = Component( - EpicsSignal, - "INT11_RBV", - put_complete=True, - write_pv="INT11", - auto_monitor=True, - name="int11", - kind=Kind.hinted, - ) - - float0 = Component( - EpicsSignalRO, "REAL0_RBV", auto_monitor=True, name="float0", kind=Kind.hinted - ) - float1 = Component( - EpicsSignalRO, "REAL1_RBV", auto_monitor=True, name="float1", kind=Kind.hinted - ) - float2 = Component( - EpicsSignalRO, "REAL2_RBV", auto_monitor=True, name="float2", kind=Kind.hinted - ) - float3 = Component( - EpicsSignalRO, "REAL3_RBV", auto_monitor=True, name="float3", kind=Kind.hinted - ) - float16 = Component( - EpicsSignal, - "REAL16_RBV", - write_pv="REAL16", - put_complete=True, - auto_monitor=True, - name="float16", - kind=Kind.hinted, - ) - float17 = Component( - EpicsSignal, - "REAL17_RBV", - write_pv="REAL17", - put_complete=True, - auto_monitor=True, - name="float17", - kind=Kind.hinted, - ) - float18 = Component( - EpicsSignal, - "REAL18_RBV", - write_pv="REAL18", - put_complete=True, - auto_monitor=True, - name="float18", - kind=Kind.hinted, - ) - float19 = Component( - EpicsSignal, - "REAL19_RBV", - write_pv="REAL19", - put_complete=True, - auto_monitor=True, - name="float19", - kind=Kind.hinted, - ) - - # BEC LiveTable crashes on non-numeric values - str0 = Component( - EpicsSignalRO, "STR0_RBV", auto_monitor=True, string=True, name="str0", kind=Kind.config - ) - str1 = Component( - EpicsSignalRO, "STR1_RBV", auto_monitor=True, string=True, name="str1", kind=Kind.config - ) - str4 = Component( - EpicsSignal, - "STR4_RBV", - put_complete=True, - string=True, - auto_monitor=True, - write_pv="STR4", - name="str4", - kind=Kind.config, - ) - str5 = Component( - EpicsSignal, - "STR5_RBV", - put_complete=True, - string=True, - auto_monitor=True, - write_pv="STR5", - name="str5", - kind=Kind.config, - ) - - -class aa1AxisIo(Device): - """Analog / digital Input-Output - - This class provides convenience wrappers around the Aerotech API's axis - specific IO functionality. Note that this is a low-speed API, actual work - should be done in AeroScript. Only one pin can be writen directly but - several can be polled! - """ - - polllvl = Component(EpicsSignal, "POLLLVL", put_complete=True, kind=Kind.config) - ai0 = Component(EpicsSignalRO, "AI0-RBV", auto_monitor=True, kind=Kind.hinted) - ai1 = Component(EpicsSignalRO, "AI1-RBV", auto_monitor=True, kind=Kind.hinted) - ai2 = Component(EpicsSignalRO, "AI2-RBV", auto_monitor=True, kind=Kind.hinted) - ai3 = Component(EpicsSignalRO, "AI3-RBV", auto_monitor=True, kind=Kind.hinted) - ao0 = Component(EpicsSignalRO, "AO0-RBV", auto_monitor=True, kind=Kind.hinted) - ao1 = Component(EpicsSignalRO, "AO1-RBV", auto_monitor=True, kind=Kind.hinted) - ao2 = Component(EpicsSignalRO, "AO2-RBV", auto_monitor=True, kind=Kind.hinted) - ao3 = Component(EpicsSignalRO, "AO3-RBV", auto_monitor=True, kind=Kind.hinted) - di0 = Component(EpicsSignalRO, "DI0-RBV", auto_monitor=True, kind=Kind.hinted) - do0 = Component(EpicsSignalRO, "DO0-RBV", auto_monitor=True, kind=Kind.hinted) - - ai_addr = Component(EpicsSignal, "AI-ADDR", put_complete=True, kind=Kind.config) - ai = Component(EpicsSignalRO, "AI-RBV", auto_monitor=True, kind=Kind.hinted) - - ao_addr = Component(EpicsSignal, "AO-ADDR", put_complete=True, kind=Kind.config) - ao = Component(EpicsSignal, "AO-RBV", write_pv="AO", auto_monitor=True, kind=Kind.hinted) - - di_addr = Component(EpicsSignal, "DI-ADDR", put_complete=True, kind=Kind.config) - di = Component(EpicsSignalRO, "DI-RBV", auto_monitor=True, kind=Kind.hinted) - - do_addr = Component(EpicsSignal, "DO-ADDR", put_complete=True, kind=Kind.config) - do = Component(EpicsSignal, "DO-RBV", write_pv="DO", auto_monitor=True, kind=Kind.hinted) - - def setAnalog(self, pin: int, value: float, settle_time=0.05): - # Set the address - self.ao_addr.set(pin).wait() - # Set the voltage - self.ao.set(value, settle_time=settle_time).wait() - - def setDigital(self, pin: int, value: int, settle_time=0.05): - # Set the address - self.do_addr.set(pin).wait() - # Set the voltage - self.do.set(value, settle_time=settle_time).wait() - - -class aa1AxisPsoBase(Device): - """Position Sensitive Output - Base class - - This class provides convenience wrappers around the Aerotech IOC's PSO - functionality. As a base class, it's just a collection of PVs without - significant logic (that should be implemented in the child classes). - It uses event-waveform concept to produce signals on the configured - output pin: a specified position based event will trigger the generation - of a waveform on the oputput that can be either used as exposure enable, - as individual trigger or as a series of triggers per each event. - As a first approach, the module follows a simple pipeline structure: - Genrator --> Event --> Waveform --> Output - - Specific operation modes should be implemented in child classes. - """ - - # ######################################################################## - # General module status - status = Component(EpicsSignalRO, "STATUS", auto_monitor=True, kind=Kind.hinted) - output = Component(EpicsSignalRO, "OUTPUT-RBV", auto_monitor=True, kind=Kind.hinted) - _eventSingle = Component(EpicsSignal, "EVENT:SINGLE", put_complete=True, kind=Kind.omitted) - _reset = Component(EpicsSignal, "RESET", put_complete=True, kind=Kind.omitted) - posInput = Component(EpicsSignal, "DIST:INPUT", put_complete=True, kind=Kind.omitted) - - # ######################################################################## - # PSO Distance event module - dstEventsEna = Component(EpicsSignal, "DIST:EVENTS", put_complete=True, kind=Kind.omitted) - dstCounterEna = Component(EpicsSignal, "DIST:COUNTER", put_complete=True, kind=Kind.omitted) - dstCounterVal = Component(EpicsSignalRO, "DIST:CTR0_RBV", auto_monitor=True, kind=Kind.hinted) - dstArrayIdx = Component(EpicsSignalRO, "DIST:IDX_RBV", auto_monitor=True, kind=Kind.hinted) - dstArrayDepleted = Component( - EpicsSignalRO, "DIST:ARRAY-DEPLETED-RBV", auto_monitor=True, kind=Kind.hinted - ) - - dstDirection = Component(EpicsSignal, "DIST:EVENTDIR", put_complete=True, kind=Kind.omitted) - dstDistance = Component(EpicsSignal, "DIST:DISTANCE", put_complete=True, kind=Kind.hinted) - dstDistanceArr = Component(EpicsSignal, "DIST:DISTANCES", put_complete=True, kind=Kind.omitted) - dstArrayRearm = Component(EpicsSignal, "DIST:REARM-ARRAY", put_complete=True, kind=Kind.omitted) - - # ######################################################################## - # PSO Window event module - winEvents = Component(EpicsSignal, "WINDOW:EVENTS", put_complete=True, kind=Kind.omitted) - winOutput = Component(EpicsSignal, "WINDOW0:OUTPUT", put_complete=True, kind=Kind.omitted) - winInput = Component(EpicsSignal, "WINDOW0:INPUT", put_complete=True, kind=Kind.omitted) - winCounter = Component(EpicsSignal, "WINDOW0:COUNTER", put_complete=True, kind=Kind.omitted) - _winLower = Component(EpicsSignal, "WINDOW0:LOWER", put_complete=True, kind=Kind.omitted) - _winUpper = Component(EpicsSignal, "WINDOW0:UPPER", put_complete=True, kind=Kind.omitted) - - # ######################################################################## - # PSO waveform module - waveEnable = Component(EpicsSignal, "WAVE:ENABLE", put_complete=True, kind=Kind.omitted) - waveMode = Component(EpicsSignal, "WAVE:MODE", put_complete=True, kind=Kind.omitted) - # waveDelay = Component(EpicsSignal, "WAVE:DELAY", put_complete=True, kind=Kind.omitted) - - # PSO waveform pulse output - # pulseTrunc = Component(EpicsSignal, "WAVE:PULSE:TRUNC", put_complete=True, kind=Kind.omitted) - pulseOnTime = Component(EpicsSignal, "WAVE:PULSE:ONTIME", put_complete=True, kind=Kind.omitted) - pulseWindow = Component(EpicsSignal, "WAVE:PULSE:PERIOD", put_complete=True, kind=Kind.omitted) - pulseCount = Component(EpicsSignal, "WAVE:PULSE:COUNT", put_complete=True, kind=Kind.omitted) - pulseApply = Component(EpicsSignal, "WAVE:PULSE:APPLY", put_complete=True, kind=Kind.omitted) - - # ######################################################################## - # PSO output module - outPin = Component(EpicsSignal, "PIN", put_complete=True, kind=Kind.omitted) - outSource = Component(EpicsSignal, "SOURCE", put_complete=True, kind=Kind.omitted) - - def fire(self, settle_time=None): - """Fire a single PSO event (i.e. manual software trigger)""" - self._eventSingle.set(1, settle_time=settle_time).wait() - - def toggle(self): - orig_waveMode = self.waveMode.get() - self.waveMode.set("Toggle").wait() - self.fire(0.1) - self.waveMode.set(orig_waveMode).wait() - - -class aa1AxisPsoDistance(aa1AxisPsoBase): - """Position Sensitive Output - Distance mode - - This class provides convenience wrappers around the Aerotech API's PSO - functionality in distance mode. It uses event-waveform concept to produce - signals on the configured output pin: a specified position based event - will trigger the generation af a waveform on the oputput that can be either - used as exposure enable, as individual trigger or as a series of triggers - per each event. - As a first approach, the module follows a simple pipeline structure: - Genrator (distance) --> Event --> Waveform --> Output - - The module provides configuration interface to common functionality, such - as fixed distance or array based triggering and can serve as a base for - future advanced functionality. The relative distances ease the limitations - coming from 32 bit PSO positions. - For a more detailed description of additional signals and masking plase - refer to Automation1's online manual. - - Usage: - # Configure the PSO to raise an 'enable' signal for 180 degrees - pso = aa1AxisPsoDistance(AA1_IOC_NAME+":ROTY:PSO:", name="pso") - pso.wait_for_connection() - pso.configure(d={'distance': [180, 0.1], 'wmode': "toggle"}) - pso.kickoff().wait() - - # Configure the PSO to emmit 5 triggers every 1.8 degrees - pso = aa1AxisPsoDistance(AA1_IOC_NAME+":ROTY:PSO:", name="pso") - pso.wait_for_connection() - pso.configure(d={'distance': 1.8, 'wmode': "pulsed", 'n_pulse': 5}) - pso.kickoff().wait() - """ - - SUB_PROGRESS = "progress" - - def __init__( - self, - prefix="", - *, - name, - kind=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - **kwargs, - ): - """__init__ MUST have a full argument list""" - super().__init__( - prefix=prefix, - name=name, - kind=kind, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - **kwargs, - ) - self._Vdistance = 3.141592 - self.subscribe(self._progress_update, "progress", run=False) - - def _progress_update(self, value, **kwargs) -> None: - """Progress update on the scan""" - if self.dstArrayDepleted.value: - print("PSO array depleted") - self._run_subs(sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1) - return - - progress = 1 - max_value = 1 - print(f"PSO array proggress: {progress}") - self._run_subs( - sub_type=self.SUB_PROGRESS, - value=int(progress), - max_value=max_value, - done=int(np.isclose(max_value, progress, 1e-3)), - ) - - # ######################################################################## - # PSO high level interface - def configure(self, d: dict = {}) -> tuple: - """Simplified configuration interface to access the most common - functionality for distance mode PSO. - - :param distance: The trigger distance or the array of distances between subsequent points. - :param wmode: Waveform mode configuration, usually pulsed/toggled. - """ - distance = d["distance"] - wmode = str(d["wmode"]) - t_pulse = float(d["t_pulse"]) if "t_pulse" in d else 100 - w_pulse = float(d["w_pulse"]) if "w_pulse" in d else 200 - n_pulse = float(d["n_pulse"]) if "n_pulse" in d else 1 - - # Validate input parameters - if wmode not in ["pulse", "pulsed", "toggle", "toggled"]: - raise RuntimeError(f"Unsupported distace triggering mode: {wmode}") - - old = self.read_configuration() - # Configure distance generator (also resets counter to 0) - self._distanceValue = distance - if isinstance(distance, (float, int)): - self.dstDistance.set(distance).wait() - elif isinstance(distance, (np.ndarray, list, tuple)): - self.dstDistanceArr.set(distance).wait() - - self.winEvents.set("Off").wait() - self.dstCounterEna.set("Off").wait() - self.dstEventsEna.set("Off").wait() - - # Configure the pulsed/toggled waveform - if wmode in ["toggle", "toggled"]: - # Switching to simple toggle mode - self.waveEnable.set("On").wait() - self.waveMode.set("Toggle").wait() - - elif wmode in ["pulse", "pulsed"]: - # Switching to pulsed mode - self.waveEnable.set("On").wait() - self.waveMode.set("Pulse").wait() - # Setting pulse shape - if w_pulse is not None: - self.pulseWindow.set(w_pulse).wait() - if t_pulse is not None: - self.pulseOnTime.set(t_pulse).wait() - if n_pulse is not None: - self.pulseCount.set(n_pulse).wait() - # Commiting configuration - self.pulseApply.set(1).wait() - # Enabling PSO waveform outputs - self.waveEnable.set("On").wait() - else: - raise RuntimeError(f"Unsupported waveform mode: {wmode}") - - # Ensure output is set to low - if self.output.value: - self.toggle() - - # Set PSO output data source - self.outSource.set("Waveform").wait() - - new = self.read_configuration() - print("PSO configured") - return (old, new) - - # ######################################################################## - # Bluesky step scan interface - def complete(self, settle_time=0.1) -> DeviceStatus: - """DDC just reads back whatever is available in the buffers""" - sleep(settle_time) - status = DeviceStatus(self) - status.set_finished() - return status - - def trigger(self): - return super().trigger() - - def unstage(self): - # Turn off counter monitoring - self.dstEventsEna.set("Off").wait() - self.dstCounterEna.set("Off").wait() - return super().unstage() - - # ######################################################################## - # Bluesky flyer interface - def kickoff(self) -> DeviceStatus: - # Rearm the configured array - if hasattr(self, "_distanceValue") and isinstance( - self._distanceValue, (np.ndarray, list, tuple) - ): - self.dstArrayRearm.set(1).wait() - # Start monitoring the counters - self.dstEventsEna.set("On").wait() - self.dstCounterEna.set("On").wait() - status = DeviceStatus(self) - status.set_finished() - print("PSO kicked off") - return status - - # def complete(self) -> DeviceStatus: - # """Bluesky flyer interface""" - # # Array mode waits until the buffer is empty - # if hasattr(self, "_distanceValue") and isinstance( - # self._distanceValue, (np.ndarray, list, tuple) - # ): - # # Define wait until the busy flag goes down (excluding initial update) - # timestamp_ = 0 - - # def notRunning(*args, old_value, value, timestamp, **kwargs): - # nonlocal timestamp_ - # result = False if (timestamp_ == 0) else bool(int(value) & 0x1000) - # print(f"Old {old_value}\tNew: {value}\tResult: {result}") - # timestamp_ = timestamp - # return result - - # # Subscribe and wait for update - # # status = SubscriptionStatus(self.status, notRunning, settle_time=0.5) - # # Data capture can be stopped any time - # status = DeviceStatus(self) - # status.set_finished() - # else: - # # In distance trigger mode there's no specific goal - # status = DeviceStatus(self) - # status.set_finished() - # return status - - def describe_collect(self) -> OrderedDict: - ret = OrderedDict() - ret["index"] = { - "source": "internal", - "dtype": "integer", - "shape": [], - "units": "", - "lower_ctrl_limit": 0, - "upper_ctrl_limit": 0, - } - return {self.name: ret} - - def collect(self) -> OrderedDict: - ret = OrderedDict() - ret["timestamps"] = {"index": time.time()} - ret["data"] = {"index": self.dstCounterVal.value} - yield ret - - -class aa1AxisPsoWindow(aa1AxisPsoBase): - """Position Sensitive Output - Window mode - - This class provides convenience wrappers around the Aerotech API's PSO - functionality in window mode. It can either use the event-waveform concept - or provide a direct window output signal (in/out) to the output pin. The - latter is particularly well-suited for the generation of trigger enable - signals, while in event mode it allows the finetuning of trigger lenth. - As a first approach, the module follows a simple pipeline structure: - Genrator --> Event --> Waveform --> Output pin - Genrator --> Window output --> Output pin - - The module provides configuration interface to common functionality, such - as repeated trigger enable signal or fixed area scaning. Unfortunately the - entered positions are absolute, meaning this mode has an inherent limitation - with encoder counters being kept in 32 bit integers. - For a more detailed description of additional signals and masking plase - refer to Automation1's online manual. - """ - - def __init__( - self, - prefix="", - *, - name, - kind=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - **kwargs, - ): - """__init__ MUST have a full argument list""" - super().__init__( - prefix=prefix, - name=name, - kind=kind, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - **kwargs, - ) - self._mode = "output" - self._eventMode = "Enter" - - # ######################################################################## - # PSO high level interface - def configure(self, d: dict = {}) -> tuple: - """Simplified configuration interface to access the most common - functionality for distance mode PSO. - - :param distance: The trigger distance or the array of distances between subsequent points. - :param wmode: Waveform mode configuration, usually output/pulsed/toggled. - """ - bounds = d["distance"] - wmode = str(d["wmode"]) - emode = str(d["emode"]) - t_pulse = float(d["t_pulse"]) if "t_pulse" in d else None - w_pulse = float(d["w_pulse"]) if "w_pulse" in d else None - n_pulse = float(d["n_pulse"]) if "n_pulse" in d else None - - # Validate input parameters - if wmode not in ["pulse", "pulsed", "toggle", "toggled", "output", "flag"]: - raise RuntimeError(f"Unsupported window triggering mode: {wmode}") - self._mode = wmode - self._eventMode = emode - - old = self.read_configuration() - - # Configure the window module - # Set the window ranges (MUST be in start position) - if len(bounds) == 2: - self.winCounter.set(0).wait() - self._winLower.set(bounds[0]).wait() - self._winUpper.set(bounds[1]).wait() - - elif isinstance(bounds, np.ndarray): - # ToDo... - pass - - # Don't start triggering just yet - self.winOutput.set("Off").wait() - self.winEvents.set("Off").wait() - - # Configure the pulsed/toggled waveform - if wmode in ["toggle", "toggled"]: - # Switching to simple toggle mode - self.waveEnable.set("On").wait() - self.waveMode.set("Toggle").wait() - elif wmode in ["pulse", "pulsed"]: - # Switching to pulsed mode - self.waveEnable.set("On").wait() - self.waveMode.set("Pulse").wait() - # Setting pulse shape - self.pulseWindow.set(w_pulse).wait() - self.pulseOnTime.set(t_pulse).wait() - self.pulseCount.set(n_pulse).wait() - # Commiting configuration - self.pulseApply.set(1).wait() - # Enabling PSO waveform outputs - self.waveEnable.set("On").wait() - elif wmode in ["output", "flag"]: - self.waveEnable.set("Off").wait() - else: - raise RuntimeError(f"Unsupported window mode: {wmode}") - - # Set PSO output data source - if wmode in ["toggle", "toggled", "pulse", "pulsed"]: - self.outSource.set("Waveform").wait() - elif wmode in ["output", "flag"]: - self.outSource.set("Window").wait() - - new = self.read_configuration() - return (old, new) - - def stage(self, settle_time=None): - if self.outSource.get() in ["Window", 2]: - self.winOutput.set("On").wait() - else: - self.winEvents.set(self._eventMode).wait() - if settle_time is not None: - sleep(settle_time) - return super().stage() - - def kickoff(self, settle_time=None): - if self.outSource.get() in ["Window", 2]: - self.winOutput.set("On").wait() - else: - self.winEvents.set(self._eventMode).wait() - if settle_time is not None: - sleep(settle_time) - - def unstage(self, settle_time=None): - self.winOutput.set("Off").wait() - self.winEvents.set("Off").wait() - if settle_time is not None: - sleep(settle_time) - return super().unstage() - - -class aa1AxisDriveDataCollection(Device): - """Axis data collection - - This class provides convenience wrappers around the Aerotech API's axis - specific data collection functionality. This module allows to record - hardware synchronized signals with up to 200 kHz. - - The default configuration is using a fixed memory mapping allowing up to - 1 million recorded data points on an XC4e (this depends on controller). - - Usage: - # Configure the DDC with default internal triggers - ddc = aa1AxisPsoDistance(AA1_IOC_NAME+":ROTY:DDC:", name="ddc") - ddc.wait_for_connection() - ddc.configure(d={'npoints': 5000}) - ddc.kickoff().wait() - ... - ret = yield from ddc.collect() - """ - - # ######################################################################## - # General module status - state = Component(EpicsSignalRO, "STATE", auto_monitor=True, kind=Kind.hinted) - nsamples_rbv = Component(EpicsSignalRO, "SAMPLES_RBV", auto_monitor=True, kind=Kind.hinted) - _switch = Component(EpicsSignal, "ACQUIRE", put_complete=True, kind=Kind.omitted) - _input0 = Component(EpicsSignal, "INPUT0", put_complete=True, kind=Kind.config) - _input1 = Component(EpicsSignal, "INPUT1", put_complete=True, kind=Kind.config) - _trigger = Component(EpicsSignal, "TRIGGER", put_complete=True, kind=Kind.config) - - npoints = Component(EpicsSignal, "NPOINTS", put_complete=True, kind=Kind.config) - _readback0 = Component(EpicsSignal, "AREAD0", kind=Kind.omitted) - _readstatus0 = Component(EpicsSignalRO, "AREAD0_RBV", auto_monitor=True, kind=Kind.omitted) - _readback1 = Component(EpicsSignal, "AREAD1", kind=Kind.omitted) - _readstatus1 = Component(EpicsSignalRO, "AREAD1_RBV", auto_monitor=True, kind=Kind.omitted) - - _buffer0 = Component(EpicsSignalRO, "BUFFER0", auto_monitor=True, kind=Kind.hinted) - _buffer1 = Component(EpicsSignalRO, "BUFFER1", auto_monitor=True, kind=Kind.hinted) - - SUB_PROGRESS = "progress" - - def __init__( - self, - prefix="", - *, - name, - kind=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - **kwargs, - ): - super().__init__( - prefix=prefix, - name=name, - kind=kind, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - **kwargs, - ) - self.subscribe(self._progress_update, "progress", run=False) - - def _progress_update(self, value, **kwargs) -> None: - """Progress update on the scan""" - if self.state.value not in (2, "Acquiring"): - self._run_subs(sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1) - return - - progress = 1 - max_value = 1 - self._run_subs( - sub_type=self.SUB_PROGRESS, - value=int(progress), - max_value=max_value, - done=int(np.isclose(max_value, progress, 1e-3)), - ) - - def configure(self, d: dict = {}) -> tuple: - npoints = int(d["npoints"]) - trigger = int(d["trigger"]) if "trigger" in d else DriveDataCaptureTrigger.PsoOutput - source0 = int(d["source0"]) if "source0" in d else DriveDataCaptureInput.PrimaryFeedback - source1 = int(d["source1"]) if "source1" in d else DriveDataCaptureInput.PositionCommand - - old = self.read_configuration() - - self._input0.set(source0).wait() - self._input1.set(source1).wait() - self._trigger.set(trigger).wait() - # This allocates the memory... - self.npoints.set(npoints).wait() - - new = self.read_configuration() - return (old, new) - - # Bluesky step scanning interface - def stage(self, settle_time=0.1): - self._switch.set("Start", settle_time=0.5).wait() - return super().stage() - - def unstage(self, settle_time=0.1): - self._switch.set("Stop", settle_time=settle_time).wait() - super().unstage() - print(f"Recorded samples: {self.nsamples_rbv.value}") - - # Bluesky flyer interface - def kickoff(self, settle_time=0.1) -> Status: - status = self._switch.set("Start", settle_time=settle_time) - return status - - def complete(self, settle_time=0.1) -> DeviceStatus: - """DDC just reads back whatever is available in the buffers""" - sleep(settle_time) - status = DeviceStatus(self) - status.set_finished() - return status - - def _collect(self, index=0): - """Force a readback of the data buffer - - Note that there's a weird behaviour in ophyd that it issues an - initial update event with the initial value but 0 timestamp. Theese - old_values are invalid and must be filtered out. - """ - - # Define wait until the busy flag goes down (excluding initial update) - timestamp_ = 0 - - def negEdge(*args, old_value, value, timestamp, **kwargs): - nonlocal timestamp_ - result = False if (timestamp_ == 0) else (old_value == 1 and value == 0) - # print(f"\nBuffer1 status:\t{old_value} ({timestamp_}) to {value} ({timestamp}) Result: {result}") - timestamp_ = timestamp - return result - - if index == 0: - status = SubscriptionStatus(self._readstatus0, negEdge, settle_time=0.5) - self._readback0.set(1).wait() - elif index == 1: - status = SubscriptionStatus(self._readstatus1, negEdge, settle_time=0.5) - self._readback1.set(1).wait() - - # Start asynchronous readback - status.wait() - return status - - def describe_collect(self) -> OrderedDict: - ret = OrderedDict() - ret["buffer0"] = { - "source": "internal", - "dtype": "array", - "shape": [], - "units": "", - "lower_ctrl_limit": 0, - "upper_ctrl_limit": 0, - } - ret["buffer1"] = { - "source": "internal", - "dtype": "array", - "shape": [], - "units": "", - "lower_ctrl_limit": 0, - "upper_ctrl_limit": 0, - } - return {self.name: ret} - - def collect(self) -> OrderedDict: - - self._collect(0).wait() - self._collect(1).wait() - - b0 = self._buffer0.value - b1 = self._buffer1.value - ret = OrderedDict() - ret["timestamps"] = {"buffer0": time.time(), "buffer1": time.time()} - ret["data"] = {"buffer0": b0, "buffer1": b1} - yield ret - - -# Automatically start simulation if directly invoked -if __name__ == "__main__": - AA1_IOC_NAME = "X02DA-ES1-SMP1" - AA1_AXIS_NAME = "ROTY" - # Drive data collection - task = aa1Tasks(AA1_IOC_NAME + ":TASK:", name="tsk") - task.wait_for_connection() - task.describe() - ddc = aa1AxisDriveDataCollection("X02DA-ES1-SMP1:ROTY:DDC:", name="ddc") - ddc.wait_for_connection() - globb = aa1GlobalVariableBindings(AA1_IOC_NAME + ":VAR:", name="globb") - globb.wait_for_connection() - globb.describe() - mot = EpicsMotor(AA1_IOC_NAME + ":" + AA1_AXIS_NAME, name="x") - mot.wait_for_connection() diff --git a/ophyd_devices/epics/devices/aerotech/AerotechAutomation1Enums.py b/ophyd_devices/epics/devices/aerotech/AerotechAutomation1Enums.py deleted file mode 100644 index ce75eba..0000000 --- a/ophyd_devices/epics/devices/aerotech/AerotechAutomation1Enums.py +++ /dev/null @@ -1,901 +0,0 @@ -from enum import Enum - - -class TomcatSequencerState: - IDLE = 0 - READY = 1 - ARMED = 2 - RUNNING = 3 - CHANGING = 4 - ERROR = 15 - - -class AxisDataSignal: - PositionFeedback = 0 - PositionCommand = 1 - PositionError = 2 - VelocityFeedback = 3 - VelocityCommand = 4 - VelocityError = 5 - AccelerationCommand = 6 - CurrentError = 9 - PositionCommandRaw = 12 - VelocityCommandRaw = 13 - AuxiliaryFeedback = 14 - DigitalInput = 15 - DigitalOutput = 16 - FixtureOffset = 18 - CoordinatedPositionTarget = 41 - DriveStatus = 42 - AxisStatus = 43 - AxisFault = 44 - AccelerationCommandRaw = 45 - PositionCalibrationAll = 50 - PositionFeedbackRollover = 63 - PositionCommandRollover = 64 - VelocityFeedbackAverage = 65 - CurrentFeedbackAverage = 66 - AxisParameter = 68 - Backlash = 72 - HomeState = 73 - PositionCalibration2D = 74 - NormalcyDebug = 75 - TotalMoveTime = 76 - JerkCommandRaw = 78 - ProgramPositionCommand = 79 - PositionOffset = 80 - PositionCommandRawBackwardsDiff = 82 - VelocityCommandRawBackwardsDiffDelta = 83 - DriveStatusActual = 85 - ProgramPositionFeedback = 89 - JogTrajectoryStatus = 94 - PingTest = 95 - AccelerationTime = 109 - DecelerationTime = 110 - AccelerationRate = 111 - DecelerationRate = 112 - AccelerationType = 113 - DecelerationType = 114 - AccelerationMode = 115 - DecelerationMode = 116 - ProgramPosition = 124 - SpeedTarget = 128 - PositionCommandPacket = 131 - DriveSmcMotionState = 132 - PositionCommandRawCal = 140 - VelocityCommandRawCal = 141 - VelocityCommandDrive = 142 - AccelerationCommandDrive = 143 - GalvoLaserOutputRaw = 144 - DriveInterfacePacketInt32 = 147 - DriveInterfacePacketInt16 = 148 - DriveInterfacePacketInt8 = 149 - DriveInterfacePacketDouble = 150 - DriveInterfacePacketFloat = 151 - DriveInterfaceCommandCode = 152 - AccelerationFeedback = 153 - AccelerationCommandRawCal = 154 - PositionCalibrationAllDrive = 155 - BacklashTarget = 156 - DriveMotionRate = 158 - DriveMotionDelay = 159 - CalibrationAdjustmentValue = 160 - ServoRounding = 161 - FeedforwardCurrent = 162 - DriveInterfacePacketInfoBitValue = 164 - AccelerationError = 165 - SuppressedFaults = 167 - DriveInterfacePacketStreamingData = 168 - PositionCommandRawUnfiltered = 169 - TransitionOffsetErrors = 170 - FreezeVelocityCommand = 179 - FreezeVelocityFeedback = 180 - InternalPositionOffsets = 181 - StatusHighLevelOffsetsLastMsec = 182 - ProgramVelocityCommand = 183 - ProgramVelocityFeedback = 184 - DriveMotionDelayLive = 185 - DriveCommunicationDelay = 186 - DriveCommunicationDelayLive = 187 - DriveInterfacePacketResponseInt32 = 189 - DriveInterfacePacketResponseInt16 = 190 - DriveInterfacePacketResponseInt8 = 191 - DriveInterfacePacketResponseDouble = 192 - DriveInterfacePacketResponseFloat = 193 - DriveInterfacePacketBit = 194 - DriveInterfacePacketResponseBit = 195 - SpeedTargetActual = 196 - CoordinatedDistanceRemaining = 199 - SafeZoneState = 230 - PositionErrorGalvo = 235 - MoveReferencePosition = 237 - MoveReferenceCutterOffset = 250 - MoveReferenceCornerOffset = 251 - MoveReferenceTotalOffset = 252 - DistanceLog = 264 - AutoFocusError = 295 - GalvoLaserOutputRawAdvance = 296 - GalvoLaserOnDelay = 297 - GalvoLaserOffDelay = 298 - CalibrationAdjustmentState = 301 - AccuracyCorrectionStartingPosition = 302 - AccuracyCorrectionEndingPosition = 303 - DriveCommandsDelayed = 309 - DriveCommandsLost = 310 - StoStatus = 327 - DriveAssert = 336 - PrimaryFeedback = 366 - AccelerationSCurvePercentage = 371 - DecelerationSCurvePercentage = 372 - GantryMarkerDifference = 390 - PrimaryFeedbackStatus = 392 - HomeTargetPosition = 398 - GantryRealignmentMoveTargetPosition = 399 - GantryDriveControlRealignmentState = 400 - DriveInterfacePositionCommandPhysical = 434 - DriveControlReason = 435 - CurrentFeedback = 7 - CurrentCommand = 8 - AnalogInput0 = 10 - AnalogInput1 = 11 - PhaseACurrentFeedback = 19 - PhaseBCurrentFeedback = 20 - EncoderSine = 21 - EncoderCosine = 22 - AnalogInput2 = 23 - AnalogInput3 = 24 - FrequencyResponseBefore = 25 - FrequencyResponseAfter = 26 - AnalogOutput0 = 31 - AnalogOutput1 = 32 - AnalogOutput2 = 33 - AnalogOutput3 = 34 - DriveMemoryInt32 = 35 - DriveMemoryFloat = 36 - DriveMemoryDouble = 37 - PsoStatus = 38 - DriveTimerDebug = 39 - PositionFeedbackDrive = 77 - PositionCommandDrive = 84 - DriveMemoryInt16 = 125 - DriveMemoryInt8 = 126 - PsoCounter0 = 171 - PsoCounter1 = 172 - PsoCounter2 = 173 - PsoWindow0 = 174 - PsoWindow1 = 175 - DriveDataCaptureSamples = 176 - PositionCommandGalvo = 178 - PrimaryEnDatAbsolutePosition = 197 - ControlEffort = 201 - PhaseAVoltageCommand = 208 - PhaseBVoltageCommand = 209 - PhaseCVoltageCommand = 210 - AmplifierPeakCurrent = 211 - FpgaVersion = 212 - DriveTypeId = 213 - PsoWindow0ArrayIndex = 214 - PsoWindow1ArrayIndex = 215 - PsoDistanceArrayIndex = 216 - AmplifierTemperature = 217 - PsoBitArrayIndex = 218 - MxAbsolutePosition = 219 - ServoUpdateRate = 220 - SettlingTime = 221 - InternalStatusCode = 222 - FirmwareVersionMajor = 223 - FirmwareVersionMinor = 224 - FirmwareVersionPatch = 225 - FirmwareVersionBuild = 226 - DriveTimerDebugMax = 227 - MarkerSearchDistance = 228 - PositionFeedbackGalvo = 234 - LatchedMarkerPosition = 236 - PrimaryBissAbsolutePosition = 255 - FaultPositionFeedback = 258 - MotorCommutationAngle = 259 - ExpansionBoardOption = 260 - BusVoltage = 261 - PiezoVoltageCommand = 262 - PiezoVoltageFeedback = 263 - TimeSinceReset = 273 - MaximumVoltage = 274 - CommandOutputType = 275 - DriveFeedforwardOutput = 290 - LastTickCounter = 291 - BoardRevision = 292 - GalvoLaserOutput = 294 - GalvoLaserPowerCorrectionOutput = 299 - CapacitanceSensorRawPosition = 300 - PositionCalibrationGalvo = 304 - BusVoltageNegative = 325 - ProcessorTemperature = 326 - InternalStatusTimestamp = 328 - AnalogSensorInput = 329 - MotorTemperature = 330 - PrimaryBissStatus = 332 - PsoExternalSyncFrequency = 337 - EncoderSineRaw = 346 - EncoderCosineRaw = 347 - FpgaTemperature = 353 - PrimaryEnDatStatus = 355 - DriveTimerHighPriorityThread = 356 - DriveTimerLowPriorityThread = 357 - DriveTimerLowPriorityPacket = 358 - DriveTimerServoPacket = 359 - DriveTimerServoThread = 360 - DriveTimerCurrentPacket = 361 - DriveTimerCommonCoreThread = 362 - DriveTimerServoCorePacket0 = 363 - DriveTimerServoCorePacket1 = 364 - MultiplierOption = 365 - ServoLoopFeedbackInput0 = 367 - ServoLoopFeedbackInput1 = 368 - FaultSubcode = 376 - ProcessorTemperatureMax = 378 - DriveTimerHyperWireDma = 381 - AmplifierTemperatureMax = 382 - AuxiliaryEnDatAbsolutePosition = 383 - AuxiliaryEnDatStatus = 384 - AuxiliaryBissAbsolutePosition = 385 - AuxiliaryBissStatus = 386 - PsoOption = 387 - DriveArraySize = 388 - RatedMotorSupplyVoltageOption = 389 - AbsoluteEncoderOption = 391 - AuxiliaryFeedbackStatus = 393 - AmplifierStatus = 394 - LatchedCwLimitPosition = 395 - LatchedCcwLimitPosition = 396 - GalvoLaserFpgaTransitionDelay = 397 - PiezoAccumulatedCharge = 401 - PiezoChargingTime = 402 - PrimarySsiAbsolutePosition = 403 - PrimarySsiStatus = 404 - AuxiliarySsiAbsolutePosition = 405 - AuxiliarySsiStatus = 406 - PsoDistanceActiveDistance = 407 - PsoWindow0ActiveLowerBound = 408 - PsoWindow0ActiveUpperBound = 409 - PsoWindow1ActiveLowerBound = 410 - PsoWindow1ActiveUpperBound = 411 - PsoWaveformActiveTotalTime = 412 - PsoWaveformActiveOnTime = 413 - PsoWaveformActivePulseCount = 414 - PsoEventActiveBitValue = 415 - DriveTimerDriveBasedControllerOutputDma = 419 - DriveTimerPcieInboundFsm = 420 - PrimaryFeedbackServo = 425 - AuxiliaryFeedbackServo = 426 - DriveStackUsage = 427 - ShuntResistorTemperature = 436 - - -class TaskDataSignal: - ProgramLineNumber = 17 - CoordinatedFlags = 40 - CoordinatedArcStartAngle = 53 - CoordinatedArcEndAngle = 54 - CoordinatedArcRadius = 55 - CoordinatedArcRadiusError = 56 - CoordinatedPositionCommand = 57 - CoordinatedSpeedCommand = 58 - CoordinatedAccelerationCommand = 59 - CoordinatedTotalDistance = 60 - CoordinatedPercentDone = 61 - CoordinatedPositionCommandBackwardsDiff = 62 - TaskParameter = 69 - TaskError = 70 - TaskWarning = 71 - CoordinatedSpeedTargetActual = 86 - DependentCoordinatedSpeedTargetActual = 87 - ActiveFixtureOffset = 88 - TaskStatus0 = 90 - TaskStatus1 = 91 - TaskStatus2 = 92 - SpindleSpeedTarget = 93 - CoordinateSystem1I = 96 - CoordinateSystem1J = 97 - CoordinateSystem1K = 98 - CoordinateSystem1Plane = 99 - ToolNumberActive = 100 - Mfo = 101 - CoordinatedSpeedTarget = 102 - DependentCoordinatedSpeedTarget = 103 - CoordinatedAccelerationRate = 104 - CoordinatedDecelerationRate = 105 - CoordinatedAccelerationTime = 106 - CoordinatedDecelerationTime = 107 - TaskMode = 108 - TaskState = 117 - TaskStateInternal = 118 - ExecutionMode = 121 - EnableAlignmentAxes = 127 - CoordinatedGalvoLaserOutput = 133 - CoordinatedMotionRate = 145 - CoordinatedTaskCommand = 146 - EnableState = 166 - LookaheadMovesExamined = 200 - ProfileControlMask = 231 - CoordinatedArcRadiusReciprocal = 253 - MotionEngineStage = 254 - CoordinatedTimeScale = 256 - CoordinatedTimeScaleDerivative = 257 - IfovSpeedScale = 266 - IfovSpeedScaleAverage = 267 - IfovGenerationFrameCounter = 268 - IfovGenerationTimeOriginal = 269 - IfovGenerationTimeModified = 270 - IfovCoordinatedPositionCommand = 271 - IfovCoordinatedSpeedCommand = 272 - IfovCenterPointH = 276 - IfovCenterPointV = 277 - IfovTrajectoryCount = 278 - IfovTrajectoryIndex = 279 - IfovAttemptCode = 280 - IfovGenerationFrameIndex = 281 - IfovMaximumVelocity = 282 - IfovIdealVelocity = 283 - TaskInternalDebug = 284 - IfovCoordinatedAccelerationCommand = 285 - IfovFovPositionH = 286 - IfovFovPositionV = 287 - IfovFovDimensionH = 288 - IfovFovDimensionV = 289 - MotionBufferElements = 311 - MotionBufferMoves = 312 - MotionLineNumber = 313 - MotionBufferRetraceMoves = 314 - MotionBufferRetraceElements = 315 - MotionBufferIndex = 316 - MotionBufferIndexLookahead = 317 - MotionBufferProcessingBlocked = 318 - ActiveMoveValid = 319 - TaskExecutionLines = 320 - SchedulerTaskHolds = 321 - SchedulerProgramLoopRuns = 322 - SchedulerTaskBlocked = 323 - CriticalSectionsActive = 324 - AxesSlowdownReason = 331 - TaskExecutionTime = 333 - TaskExecutionTimeMaximum = 334 - TaskExecutionLinesMaximum = 335 - LookaheadDecelReason = 338 - LookaheadDecelMoves = 339 - LookaheadDecelDistance = 340 - ProgramCounter = 341 - StackPointer = 342 - FramePointer = 343 - StringStackPointer = 344 - ProgramLineNumberSourceFileId = 349 - MotionLineNumberSourceFileId = 350 - ProgramLineNumberSourcePathId = 351 - MotionLineNumberSourcePathId = 352 - StringArgumentStackPointer = 354 - CoordinatedAccelerationSCurvePercentage = 369 - CoordinatedDecelerationSCurvePercentage = 370 - DependentCoordinatedAccelerationRate = 373 - DependentCoordinatedDecelerationRate = 374 - CriticalSectionTimeout = 375 - CommandQueueCapacity = 421 - CommandQueueUnexecutedCount = 422 - CommandQueueTimesEmptied = 423 - CommandQueueExecutedCount = 424 - - -class SystemDataSignal: - VirtualBinaryInput = 46 - VirtualBinaryOutput = 47 - VirtualRegisterInput = 48 - VirtualRegisterOutput = 49 - Timer = 51 - TimerPerformance = 52 - GlobalReal = 67 - CommunicationRealTimeErrors = 81 - LibraryCommand = 119 - DataCollectionSampleTime = 120 - DataCollectionSampleIndex = 129 - ModbusClientConnected = 134 - ModbusServerConnected = 135 - ModbusClientError = 136 - ModbusServerError = 137 - StopWatchTimer = 157 - ScopetrigId = 163 - EstimatedProcessorUsage = 177 - DataCollectionStatus = 188 - SignalLogState = 198 - SafeZoneViolationMask = 207 - SafeZoneActiveMask = 229 - ModbusClientInputWords = 240 - ModbusClientOutputWords = 241 - ModbusClientInputBits = 242 - ModbusClientOutputBits = 243 - ModbusClientOutputBitsStatus = 244 - ModbusClientOutputWordsStatus = 245 - ModbusServerInputWords = 246 - ModbusServerOutputWords = 247 - ModbusServerInputBits = 248 - ModbusServerOutputBits = 249 - SystemParameter = 265 - ThermoCompSensorTemperature = 305 - ThermoCompControllingTemperature = 306 - ThermoCompCompensatingTemperature = 307 - ThermoCompStatus = 308 - GlobalInteger = 345 - AliveAxesMask = 348 - SignalLogPointsStored = 377 - ControllerInitializationWarning = 379 - StopWatchTimerMin = 416 - StopWatchTimerMax = 417 - StopWatchTimerAvg = 418 - EthercatEnabled = 428 - EthercatError = 429 - EthercatTxPdo = 430 - EthercatTxPdoSize = 431 - EthercatRxPdo = 432 - EthercatRxPdoSize = 433 - EthercatState = 437 - ModbusClientEnabled = 438 - ModbusServerEnabled = 439 - - -class DataCollectionFrequency: - Undefined = 0 - Fixed1kHz = 1 - Fixed10kHz = 2 - Fixed20kHz = 3 - Fixed100kHz = 4 - Fixed200kHz = 5 - - -class DataCollectionMode: - Snapshot = 0 - Continouous = 1 - - -# Specifies the PSO distance input settings for the XC4e drive. -class PsoDistanceInput: - XC4PrimaryFeedback = 130 - XC4AuxiliaryFeedback = 131 - XC4SyncPortA = 132 - XC4SyncPortB = 133 - XC4DrivePulseStream = 134 - XC4ePrimaryFeedback = 135 - XC4eAuxiliaryFeedback = 136 - XC4eSyncPortA = 137 - XC4eSyncPortB = 138 - XC4eDrivePulseStream = 139 - - -class PsoWindowInput: - XC4PrimaryFeedback = 130 - XC4AuxiliaryFeedback = 131 - XC4SyncPortA = 132 - XC4SyncPortB = 133 - XC4DrivePulseStream = 134 - XC4ePrimaryFeedback = 135 - XC4eAuxiliaryFeedback = 136 - XC4eSyncPortA = 137 - XC4eSyncPortB = 138 - XC4eDrivePulseStream = 139 - XL5ePrimaryFeedback = (145,) - XL5eAuxiliaryFeedback = (146,) - XL5eSyncPortA = (147,) - XL5eSyncPortB = (148,) - XL5eDrivePulseStream = (149,) - - -# @brief Specifies the PSO output pin settings for each drive. -class XC4ePsoOutputPin: - DedicatedOutput = 111 - AuxiliaryMarkerDifferential = 112 - AuxiliaryMarkerSingleEnded = 113 - - -class XC4PsoOutputPin: - DedicatedOutput = 108 - AuxiliaryMarkerDifferential = 109 - AuxiliaryMarkerSingleEnded = 110 - - -""" -# @brief Specifies the PSO distance input settings for each drive. -class Automation1PsoDistanceInput: - Automation1PsoDistanceInput_GL4PrimaryFeedbackAxis1 = 100, - Automation1PsoDistanceInput_GL4PrimaryFeedbackAxis2 = 101, - Automation1PsoDistanceInput_GL4IfovFeedbackAxis1 = 102, - Automation1PsoDistanceInput_GL4IfovFeedbackAxis2 = 103, - Automation1PsoDistanceInput_GL4AuxiliaryFeedbackAxis1 = 104, - Automation1PsoDistanceInput_GL4AuxiliaryFeedbackAxis2 = 105, - Automation1PsoDistanceInput_GL4SyncPortA = 106, - Automation1PsoDistanceInput_GL4SyncPortB = 107, - Automation1PsoDistanceInput_GL4DrivePulseStreamAxis1 = 108, - Automation1PsoDistanceInput_GL4DrivePulseStreamAxis2 = 109, - Automation1PsoDistanceInput_XL4sPrimaryFeedback = 110, - Automation1PsoDistanceInput_XL4sAuxiliaryFeedback = 111, - Automation1PsoDistanceInput_XL4sSyncPortA = 112, - Automation1PsoDistanceInput_XL4sSyncPortB = 113, - Automation1PsoDistanceInput_XL4sDrivePulseStream = 114, - Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis1 = 115, - Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis2 = 116, - Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis3 = 117, - Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis4 = 118, - Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis5 = 119, - Automation1PsoDistanceInput_XR3PrimaryFeedbackAxis6 = 120, - Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis1 = 121, - Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis2 = 122, - Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis3 = 123, - Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis4 = 124, - Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis5 = 125, - Automation1PsoDistanceInput_XR3AuxiliaryFeedbackAxis6 = 126, - Automation1PsoDistanceInput_XR3SyncPortA = 127, - Automation1PsoDistanceInput_XR3SyncPortB = 128, - Automation1PsoDistanceInput_XR3DrivePulseStream = 129, - Automation1PsoDistanceInput_XC4PrimaryFeedback = 130, - Automation1PsoDistanceInput_XC4AuxiliaryFeedback = 131, - Automation1PsoDistanceInput_XC4SyncPortA = 132, - Automation1PsoDistanceInput_XC4SyncPortB = 133, - Automation1PsoDistanceInput_XC4DrivePulseStream = 134, - XC4ePrimaryFeedback = 135, - XC4eAuxiliaryFeedback = 136, - XC4eSyncPortA = 137, - XC4eSyncPortB = 138, - XC4eDrivePulseStream = 139, - Automation1PsoDistanceInput_XC6ePrimaryFeedback = 140, - Automation1PsoDistanceInput_XC6eAuxiliaryFeedback = 141, - Automation1PsoDistanceInput_XC6eSyncPortA = 142, - Automation1PsoDistanceInput_XC6eSyncPortB = 143, - Automation1PsoDistanceInput_XC6eDrivePulseStream = 144, - Automation1PsoDistanceInput_XL5ePrimaryFeedback = 145, - Automation1PsoDistanceInput_XL5eAuxiliaryFeedback = 146, - Automation1PsoDistanceInput_XL5eSyncPortA = 147, - Automation1PsoDistanceInput_XL5eSyncPortB = 148, - Automation1PsoDistanceInput_XL5eDrivePulseStream = 149, - Automation1PsoDistanceInput_XC2PrimaryFeedback = 150, - Automation1PsoDistanceInput_XC2AuxiliaryFeedback = 151, - Automation1PsoDistanceInput_XC2DrivePulseStream = 152, - Automation1PsoDistanceInput_XC2ePrimaryFeedback = 153, - Automation1PsoDistanceInput_XC2eAuxiliaryFeedback = 154, - Automation1PsoDistanceInput_XC2eDrivePulseStream = 155, - Automation1PsoDistanceInput_XL2ePrimaryFeedback = 156, - Automation1PsoDistanceInput_XL2eAuxiliaryFeedback = 157, - Automation1PsoDistanceInput_XL2eSyncPortA = 158, - Automation1PsoDistanceInput_XL2eSyncPortB = 159, - Automation1PsoDistanceInput_XL2eDrivePulseStream = 160, - Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis1 = 161, - Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis2 = 162, - Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis3 = 163, - Automation1PsoDistanceInput_XI4PrimaryFeedbackAxis4 = 164, - Automation1PsoDistanceInput_XI4AuxiliaryFeedback1 = 165, - Automation1PsoDistanceInput_XI4AuxiliaryFeedback2 = 166, - Automation1PsoDistanceInput_XI4AuxiliaryFeedback3 = 167, - Automation1PsoDistanceInput_XI4AuxiliaryFeedback4 = 168, - Automation1PsoDistanceInput_XI4SyncPortA = 169, - Automation1PsoDistanceInput_XI4SyncPortB = 170, - Automation1PsoDistanceInput_XI4DrivePulseStreamAxis1 = 171, - Automation1PsoDistanceInput_XI4DrivePulseStreamAxis2 = 172, - Automation1PsoDistanceInput_XI4DrivePulseStreamAxis3 = 173, - Automation1PsoDistanceInput_XI4DrivePulseStreamAxis4 = 174, - Automation1PsoDistanceInput_iXC4PrimaryFeedback = 175, - Automation1PsoDistanceInput_iXC4AuxiliaryFeedback = 176, - Automation1PsoDistanceInput_iXC4SyncPortA = 177, - Automation1PsoDistanceInput_iXC4SyncPortB = 178, - Automation1PsoDistanceInput_iXC4DrivePulseStream = 179, - Automation1PsoDistanceInput_iXC4ePrimaryFeedback = 180, - Automation1PsoDistanceInput_iXC4eAuxiliaryFeedback = 181, - Automation1PsoDistanceInput_iXC4eSyncPortA = 182, - Automation1PsoDistanceInput_iXC4eSyncPortB = 183, - Automation1PsoDistanceInput_iXC4eDrivePulseStream = 184, - Automation1PsoDistanceInput_iXC6ePrimaryFeedback = 185, - Automation1PsoDistanceInput_iXC6eAuxiliaryFeedback = 186, - Automation1PsoDistanceInput_iXC6eSyncPortA = 187, - Automation1PsoDistanceInput_iXC6eSyncPortB = 188, - Automation1PsoDistanceInput_iXC6eDrivePulseStream = 189, - Automation1PsoDistanceInput_iXL5ePrimaryFeedback = 190, - Automation1PsoDistanceInput_iXL5eAuxiliaryFeedback = 191, - Automation1PsoDistanceInput_iXL5eSyncPortA = 192, - Automation1PsoDistanceInput_iXL5eSyncPortB = 193, - Automation1PsoDistanceInput_iXL5eDrivePulseStream = 194, - Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis1 = 195, - Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis2 = 196, - Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis3 = 197, - Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis4 = 198, - Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis5 = 199, - Automation1PsoDistanceInput_iXR3PrimaryFeedbackAxis6 = 200, - Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis1 = 201, - Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis2 = 202, - Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis3 = 203, - Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis4 = 204, - Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis5 = 205, - Automation1PsoDistanceInput_iXR3AuxiliaryFeedbackAxis6 = 206, - Automation1PsoDistanceInput_iXR3SyncPortA = 207, - Automation1PsoDistanceInput_iXR3SyncPortB = 208, - Automation1PsoDistanceInput_iXR3DrivePulseStream = 209, - Automation1PsoDistanceInput_GI4DrivePulseStreamAxis1 = 210, - Automation1PsoDistanceInput_GI4DrivePulseStreamAxis2 = 211, - Automation1PsoDistanceInput_GI4DrivePulseStreamAxis3 = 212, - Automation1PsoDistanceInput_iXC2PrimaryFeedback = 213, - Automation1PsoDistanceInput_iXC2AuxiliaryFeedback = 214, - Automation1PsoDistanceInput_iXC2DrivePulseStream = 215, - Automation1PsoDistanceInput_iXC2ePrimaryFeedback = 216, - Automation1PsoDistanceInput_iXC2eAuxiliaryFeedback = 217, - Automation1PsoDistanceInput_iXC2eDrivePulseStream = 218, - Automation1PsoDistanceInput_iXL2ePrimaryFeedback = 219, - Automation1PsoDistanceInput_iXL2eAuxiliaryFeedback = 220, - Automation1PsoDistanceInput_iXL2eSyncPortA = 221, - Automation1PsoDistanceInput_iXL2eSyncPortB = 222, - Automation1PsoDistanceInput_iXL2eDrivePulseStream = 223, - Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis1 = 224, - Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis2 = 225, - Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis3 = 226, - Automation1PsoDistanceInput_iXI4PrimaryFeedbackAxis4 = 227, - Automation1PsoDistanceInput_iXI4AuxiliaryFeedback1 = 228, - Automation1PsoDistanceInput_iXI4AuxiliaryFeedback2 = 229, - Automation1PsoDistanceInput_iXI4AuxiliaryFeedback3 = 230, - Automation1PsoDistanceInput_iXI4AuxiliaryFeedback4 = 231, - Automation1PsoDistanceInput_iXI4SyncPortA = 232, - Automation1PsoDistanceInput_iXI4SyncPortB = 233, - Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis1 = 234, - Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis2 = 235, - Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis3 = 236, - Automation1PsoDistanceInput_iXI4DrivePulseStreamAxis4 = 237, - -# @brief Specifies the PSO window input settings for each drive. -class Automation1PsoWindowInput: - Automation1PsoWindowInput_GL4PrimaryFeedbackAxis1 = 100, - Automation1PsoWindowInput_GL4PrimaryFeedbackAxis2 = 101, - Automation1PsoWindowInput_GL4IfovFeedbackAxis1 = 102, - Automation1PsoWindowInput_GL4IfovFeedbackAxis2 = 103, - Automation1PsoWindowInput_GL4AuxiliaryFeedbackAxis1 = 104, - Automation1PsoWindowInput_GL4AuxiliaryFeedbackAxis2 = 105, - Automation1PsoWindowInput_GL4SyncPortA = 106, - Automation1PsoWindowInput_GL4SyncPortB = 107, - Automation1PsoWindowInput_GL4DrivePulseStreamAxis1 = 108, - Automation1PsoWindowInput_GL4DrivePulseStreamAxis2 = 109, - Automation1PsoWindowInput_XL4sPrimaryFeedback = 110, - Automation1PsoWindowInput_XL4sAuxiliaryFeedback = 111, - Automation1PsoWindowInput_XL4sSyncPortA = 112, - Automation1PsoWindowInput_XL4sSyncPortB = 113, - Automation1PsoWindowInput_XL4sDrivePulseStream = 114, - Automation1PsoWindowInput_XR3PrimaryFeedbackAxis1 = 115, - Automation1PsoWindowInput_XR3PrimaryFeedbackAxis2 = 116, - Automation1PsoWindowInput_XR3PrimaryFeedbackAxis3 = 117, - Automation1PsoWindowInput_XR3PrimaryFeedbackAxis4 = 118, - Automation1PsoWindowInput_XR3PrimaryFeedbackAxis5 = 119, - Automation1PsoWindowInput_XR3PrimaryFeedbackAxis6 = 120, - Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis1 = 121, - Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis2 = 122, - Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis3 = 123, - Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis4 = 124, - Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis5 = 125, - Automation1PsoWindowInput_XR3AuxiliaryFeedbackAxis6 = 126, - Automation1PsoWindowInput_XR3SyncPortA = 127, - Automation1PsoWindowInput_XR3SyncPortB = 128, - Automation1PsoWindowInput_XR3DrivePulseStream = 129, - Automation1PsoWindowInput_XC4PrimaryFeedback = 130, - Automation1PsoWindowInput_XC4AuxiliaryFeedback = 131, - Automation1PsoWindowInput_XC4SyncPortA = 132, - Automation1PsoWindowInput_XC4SyncPortB = 133, - Automation1PsoWindowInput_XC4DrivePulseStream = 134, - XC4ePrimaryFeedback = 135, - XC4eAuxiliaryFeedback = 136, - XC4eSyncPortA = 137, - XC4eSyncPortB = 138, - XC4eDrivePulseStream = 139, - Automation1PsoWindowInput_XC6ePrimaryFeedback = 140, - Automation1PsoWindowInput_XC6eAuxiliaryFeedback = 141, - Automation1PsoWindowInput_XC6eSyncPortA = 142, - Automation1PsoWindowInput_XC6eSyncPortB = 143, - Automation1PsoWindowInput_XC6eDrivePulseStream = 144, - Automation1PsoWindowInput_XL5ePrimaryFeedback = 145, - Automation1PsoWindowInput_XL5eAuxiliaryFeedback = 146, - Automation1PsoWindowInput_XL5eSyncPortA = 147, - Automation1PsoWindowInput_XL5eSyncPortB = 148, - Automation1PsoWindowInput_XL5eDrivePulseStream = 149, - Automation1PsoWindowInput_XC2PrimaryFeedback = 150, - Automation1PsoWindowInput_XC2AuxiliaryFeedback = 151, - Automation1PsoWindowInput_XC2DrivePulseStream = 152, - Automation1PsoWindowInput_XC2ePrimaryFeedback = 153, - Automation1PsoWindowInput_XC2eAuxiliaryFeedback = 154, - Automation1PsoWindowInput_XC2eDrivePulseStream = 155, - Automation1PsoWindowInput_XL2ePrimaryFeedback = 156, - Automation1PsoWindowInput_XL2eAuxiliaryFeedback = 157, - Automation1PsoWindowInput_XL2eSyncPortA = 158, - Automation1PsoWindowInput_XL2eSyncPortB = 159, - Automation1PsoWindowInput_XL2eDrivePulseStream = 160, - Automation1PsoWindowInput_XI4PrimaryFeedbackAxis1 = 161, - Automation1PsoWindowInput_XI4PrimaryFeedbackAxis2 = 162, - Automation1PsoWindowInput_XI4PrimaryFeedbackAxis3 = 163, - Automation1PsoWindowInput_XI4PrimaryFeedbackAxis4 = 164, - Automation1PsoWindowInput_XI4AuxiliaryFeedback1 = 165, - Automation1PsoWindowInput_XI4AuxiliaryFeedback2 = 166, - Automation1PsoWindowInput_XI4AuxiliaryFeedback3 = 167, - Automation1PsoWindowInput_XI4AuxiliaryFeedback4 = 168, - Automation1PsoWindowInput_XI4SyncPortA = 169, - Automation1PsoWindowInput_XI4SyncPortB = 170, - Automation1PsoWindowInput_XI4DrivePulseStreamAxis1 = 171, - Automation1PsoWindowInput_XI4DrivePulseStreamAxis2 = 172, - Automation1PsoWindowInput_XI4DrivePulseStreamAxis3 = 173, - Automation1PsoWindowInput_XI4DrivePulseStreamAxis4 = 174, - Automation1PsoWindowInput_iXC4PrimaryFeedback = 175, - Automation1PsoWindowInput_iXC4AuxiliaryFeedback = 176, - Automation1PsoWindowInput_iXC4SyncPortA = 177, - Automation1PsoWindowInput_iXC4SyncPortB = 178, - Automation1PsoWindowInput_iXC4DrivePulseStream = 179, - Automation1PsoWindowInput_iXC4ePrimaryFeedback = 180, - Automation1PsoWindowInput_iXC4eAuxiliaryFeedback = 181, - Automation1PsoWindowInput_iXC4eSyncPortA = 182, - Automation1PsoWindowInput_iXC4eSyncPortB = 183, - Automation1PsoWindowInput_iXC4eDrivePulseStream = 184, - Automation1PsoWindowInput_iXC6ePrimaryFeedback = 185, - Automation1PsoWindowInput_iXC6eAuxiliaryFeedback = 186, - Automation1PsoWindowInput_iXC6eSyncPortA = 187, - Automation1PsoWindowInput_iXC6eSyncPortB = 188, - Automation1PsoWindowInput_iXC6eDrivePulseStream = 189, - Automation1PsoWindowInput_iXL5ePrimaryFeedback = 190, - Automation1PsoWindowInput_iXL5eAuxiliaryFeedback = 191, - Automation1PsoWindowInput_iXL5eSyncPortA = 192, - Automation1PsoWindowInput_iXL5eSyncPortB = 193, - Automation1PsoWindowInput_iXL5eDrivePulseStream = 194, - Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis1 = 195, - Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis2 = 196, - Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis3 = 197, - Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis4 = 198, - Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis5 = 199, - Automation1PsoWindowInput_iXR3PrimaryFeedbackAxis6 = 200, - Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis1 = 201, - Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis2 = 202, - Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis3 = 203, - Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis4 = 204, - Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis5 = 205, - Automation1PsoWindowInput_iXR3AuxiliaryFeedbackAxis6 = 206, - Automation1PsoWindowInput_iXR3SyncPortA = 207, - Automation1PsoWindowInput_iXR3SyncPortB = 208, - Automation1PsoWindowInput_iXR3DrivePulseStream = 209, - Automation1PsoWindowInput_GI4DrivePulseStreamAxis1 = 210, - Automation1PsoWindowInput_GI4DrivePulseStreamAxis2 = 211, - Automation1PsoWindowInput_GI4DrivePulseStreamAxis3 = 212, - Automation1PsoWindowInput_iXC2PrimaryFeedback = 213, - Automation1PsoWindowInput_iXC2AuxiliaryFeedback = 214, - Automation1PsoWindowInput_iXC2DrivePulseStream = 215, - Automation1PsoWindowInput_iXC2ePrimaryFeedback = 216, - Automation1PsoWindowInput_iXC2eAuxiliaryFeedback = 217, - Automation1PsoWindowInput_iXC2eDrivePulseStream = 218, - Automation1PsoWindowInput_iXL2ePrimaryFeedback = 219, - Automation1PsoWindowInput_iXL2eAuxiliaryFeedback = 220, - Automation1PsoWindowInput_iXL2eSyncPortA = 221, - Automation1PsoWindowInput_iXL2eSyncPortB = 222, - Automation1PsoWindowInput_iXL2eDrivePulseStream = 223, - Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis1 = 224, - Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis2 = 225, - Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis3 = 226, - Automation1PsoWindowInput_iXI4PrimaryFeedbackAxis4 = 227, - Automation1PsoWindowInput_iXI4AuxiliaryFeedback1 = 228, - Automation1PsoWindowInput_iXI4AuxiliaryFeedback2 = 229, - Automation1PsoWindowInput_iXI4AuxiliaryFeedback3 = 230, - Automation1PsoWindowInput_iXI4AuxiliaryFeedback4 = 231, - Automation1PsoWindowInput_iXI4SyncPortA = 232, - Automation1PsoWindowInput_iXI4SyncPortB = 233, - Automation1PsoWindowInput_iXI4DrivePulseStreamAxis1 = 234, - Automation1PsoWindowInput_iXI4DrivePulseStreamAxis2 = 235, - Automation1PsoWindowInput_iXI4DrivePulseStreamAxis3 = 236, - Automation1PsoWindowInput_iXI4DrivePulseStreamAxis4 = 237, - -# @brief Specifies the PSO output pin settings for each drive. -class Automation1PsoOutputPin: - Automation1PsoOutputPin_GL4None = 100, - Automation1PsoOutputPin_GL4LaserOutput0 = 101, - Automation1PsoOutputPin_XL4sNone = 102, - Automation1PsoOutputPin_XL4sLaserOutput0 = 103, - Automation1PsoOutputPin_XR3None = 104, - Automation1PsoOutputPin_XR3PsoOutput1 = 105, - Automation1PsoOutputPin_XR3PsoOutput2 = 106, - Automation1PsoOutputPin_XR3PsoOutput3 = 107, - Automation1PsoOutputPin_XC4DedicatedOutput = 108, - Automation1PsoOutputPin_XC4AuxiliaryMarkerDifferential = 109, - Automation1PsoOutputPin_XC4AuxiliaryMarkerSingleEnded = 110, - XC4eDedicatedOutput = 111, - XC4eAuxiliaryMarkerDifferential = 112, - XC4eAuxiliaryMarkerSingleEnded = 113, - Automation1PsoOutputPin_XC6eDedicatedOutput = 114, - Automation1PsoOutputPin_XC6eAuxiliaryMarkerDifferential = 115, - Automation1PsoOutputPin_XC6eAuxiliaryMarkerSingleEnded = 116, - Automation1PsoOutputPin_XL5eDedicatedOutput = 117, - Automation1PsoOutputPin_XL5eAuxiliaryMarkerDifferential = 118, - Automation1PsoOutputPin_XL5eAuxiliaryMarkerSingleEnded = 119, - Automation1PsoOutputPin_XC2DedicatedOutput = 120, - Automation1PsoOutputPin_XC2eDedicatedOutput = 121, - Automation1PsoOutputPin_XL2eDedicatedOutput = 122, - Automation1PsoOutputPin_XI4DedicatedOutput = 123, - Automation1PsoOutputPin_iXC4DedicatedOutput = 124, - Automation1PsoOutputPin_iXC4AuxiliaryMarkerDifferential = 125, - Automation1PsoOutputPin_iXC4AuxiliaryMarkerSingleEnded = 126, - Automation1PsoOutputPin_iXC4eDedicatedOutput = 127, - Automation1PsoOutputPin_iXC4eAuxiliaryMarkerDifferential = 128, - Automation1PsoOutputPin_iXC4eAuxiliaryMarkerSingleEnded = 129, - Automation1PsoOutputPin_iXC6eDedicatedOutput = 130, - Automation1PsoOutputPin_iXC6eAuxiliaryMarkerDifferential = 131, - Automation1PsoOutputPin_iXC6eAuxiliaryMarkerSingleEnded = 132, - Automation1PsoOutputPin_iXL5eDedicatedOutput = 133, - Automation1PsoOutputPin_iXL5eAuxiliaryMarkerDifferential = 134, - Automation1PsoOutputPin_iXL5eAuxiliaryMarkerSingleEnded = 135, - Automation1PsoOutputPin_iXR3None = 136, - Automation1PsoOutputPin_iXR3PsoOutput1 = 137, - Automation1PsoOutputPin_iXR3PsoOutput2 = 138, - Automation1PsoOutputPin_iXR3PsoOutput3 = 139, - Automation1PsoOutputPin_GI4None = 140, - Automation1PsoOutputPin_GI4LaserOutput0 = 141, - Automation1PsoOutputPin_iXC2eDedicatedOutput = 143, - Automation1PsoOutputPin_iXL2eDedicatedOutput = 144, - Automation1PsoOutputPin_iXI4DedicatedOutput = 145, -""" - - -class DriveDataCaptureInput: - PositionCommand = 0 - PrimaryFeedback = 1 - AuxiliaryFeedback = 2 - AnalogInput0 = 3 - AnalogInput1 = 4 - AnalogInput2 = 5 - AnalogInput3 = 6 - - -class DriveDataCaptureTrigger: - PsoOutput = 0 - PsoEvent = 1 - HighSpeedInput0RisingEdge = 2 - HighSpeedInput0FallingEdge = 3 - HighSpeedInput1RisingEdge = 4 - HighSpeedInput1FallingEdge = 5 - AuxiliaryMarkerRisingEdge = 6 - AuxiliaryMarkerFallingEdge = 7 - - -class PsoOutputPin: - GL4None = (100,) - GL4LaserOutput0 = (101,) - XL4sNone = (102,) - XL4sLaserOutput0 = (103,) - XR3None = (104,) - XR3PsoOutput1 = (105,) - XR3PsoOutput2 = (106,) - XR3PsoOutput3 = (107,) - XC4DedicatedOutput = (108,) - XC4AuxiliaryMarkerDifferential = (109,) - XC4AuxiliaryMarkerSingleEnded = (110,) - XC4eDedicatedOutput = (111,) - XC4eAuxiliaryMarkerDifferential = (112,) - XC4eAuxiliaryMarkerSingleEnded = (113,) - XC6eDedicatedOutput = (114,) - XC6eAuxiliaryMarkerDifferential = (115,) - XC6eAuxiliaryMarkerSingleEnded = (116,) - XL5eDedicatedOutput = (117,) - XL5eAuxiliaryMarkerDifferential = (118,) - XL5eAuxiliaryMarkerSingleEnded = (119,) - XC2DedicatedOutput = (120,) - XC2eDedicatedOutput = (121,) - XL2eDedicatedOutput = (122,) - XI4DedicatedOutput = (123,) - iXC4DedicatedOutput = (124,) - iXC4AuxiliaryMarkerDifferential = (125,) - iXC4AuxiliaryMarkerSingleEnded = (126,) - iXC4eDedicatedOutput = (127,) - iXC4eAuxiliaryMarkerDifferential = (128,) - iXC4eAuxiliaryMarkerSingleEnded = (129,) - iXC6eDedicatedOutput = (130,) - iXC6eAuxiliaryMarkerDifferential = (131,) - iXC6eAuxiliaryMarkerSingleEnded = (132,) - iXL5eDedicatedOutput = (133,) - iXL5eAuxiliaryMarkerDifferential = (134,) - iXL5eAuxiliaryMarkerSingleEnded = (135,) - iXR3None = (136,) - iXR3PsoOutput1 = (137,) - iXR3PsoOutput2 = (138,) - iXR3PsoOutput3 = (139,) - GI4None = (140,) - GI4LaserOutput0 = (141,) - iXC2DedicatedOutput = (142,) - iXC2eDedicatedOutput = (143,) - iXL2eDedicatedOutput = (144,) - iXI4DedicatedOutput = (145,) diff --git a/ophyd_devices/epics/devices/aerotech/AerotechSimpleSequenceTemplate.ascript b/ophyd_devices/epics/devices/aerotech/AerotechSimpleSequenceTemplate.ascript deleted file mode 100644 index 94d4caa..0000000 --- a/ophyd_devices/epics/devices/aerotech/AerotechSimpleSequenceTemplate.ascript +++ /dev/null @@ -1,140 +0,0 @@ -// Test program for simple zig-zag line scanning with PSO window output -// "enable" signal and DDC synchronized to external trigger input. -// The file expects external parameter validation -// The PSO locations arrays are set externally from EPICS PV -// - -enum ScanType - Pos = 0 - Neg = 1 - PosNeg = 2 - NegPos = 3 -end - - -program - ////////////////////////////////////////////////////////////////////////// - // External parameters - USE THEESE - var $fStartPosition as real = {{ scan.startpos }} - var $fScanRange as real = $fStartPosition + {{ scan.scanrange }} - var $iNumRepeat as integer = {{ scan.nrepeat }} - var $eScanType as ScanType = ScanType.{{ scan.scandir or 'Pos' }} - var $iNumDdcRead as integer = {{ scan.npoints }} - - - var $fVelJog as real = {{ scan.jogvel or 200 }} - var $fVelScan as real = {{ scan.scanvel }} - var $fAcceleration = {{ scan.scanacc or 500 }} - var $fSafeDist = 10.0 - - ////////////////////////////////////////////////////////////////////////// - // Internal parameters - dont use - var $axis as axis = ROTY - var $ii as integer - var $iDdcSafeSpace as integer = 4096 - - // Set acceleration - SetupAxisRampType($axis, RampType.Linear) - SetupAxisRampValue($axis,0,$fAcceleration) - var $fAccDistance as real = 0.5 * $fVelScan * $fVelScan / $fAcceleration + $fSafeDist - - // set the actual scan range - var $fPosStart as real - var $fPosEnd as real - if $eScanType == ScanType.Pos - $fPosStart = $fStartPosition - $fAccDistance - $fPosEnd = $fStartPosition + $fScanRange + $fAccDistance - elseif $eScanType == ScanType.Neg - $fPosStart = $fStartPosition + $fAccDistance - $fPosEnd = $fStartPosition - $fScanRange - $fAccDistance - elseif $eScanType == ScanType.PosNeg - $fPosStart = $fStartPosition - $fAccDistance - $fPosEnd = $fStartPosition + $fScanRange + $fAccDistance - elseif $eScanType == ScanType.NegPos - $fPosStart = $fStartPosition + $fAccDistance - $fPosEnd = $fStartPosition - $fScanRange - $fAccDistance - end - - // Move to start position before the scan - MoveAbsolute($axis, $fPosStart, $fVelJog) - WaitForInPosition($axis) - - // Configure PSO - PsoDistanceCounterOff($axis) - PsoDistanceEventsOff($axis) - PsoWindowConfigureEvents($axis, PsoWindowEventMode.None) - PsoWaveformOff($axis) - PsoOutputConfigureSource($axis, PsoOutputSource.Waveform) - var $iPsoArrayAddr as integer = 0 - // Simple PSO trigger pattern - var $iPsoArrayPos[] as real = [{% for psoDist in scan.psoBoundsPos[:-1] %} UnitsToCounts($axis, {{ psoDist }}), {% endfor %} UnitsToCounts($axis, {{ scan.psoBoundsPos[-1] }})] - // var $iPsoArrayPos[] as real = [UnitsToCounts($axis, $fAccDistance), {% for psoDist in scan.psoBoundsPos[:-1] %} UnitsToCounts($axis, psoDist), {% endfor %}] - // var $iPsoArrayNeg[] as real = [UnitsToCounts($axis, $fAccDistance), {% for psoDist in scan.psoBoundsNeg[:-1] %} UnitsToCounts($axis, psoDist), {% endfor %}] - - DriveArrayWrite($axis, $iPsoArrayPos, $iPsoArrayAddr, length($iPsoArrayPos), DriveArrayType.PsoDistanceEventDistances) - PsoDistanceConfigureArrayDistances($axis, $iPsoArrayAddr, length($iPsoArrayPos), 0) - PsoDistanceEventsOn($axis) - - PsoWaveformConfigureMode($axis, PsoWaveformMode.Toggle) - PsoWaveformOn($axis) - - // Configure Drive Data Collection - var $iDdcArrayAddr as integer = 8388608 - var $iDdcArraySize as integer = $iNumDdcRead - - DriveDataCaptureConfigureInput($axis, 0, DriveDataCaptureInput.PrimaryFeedback); - DriveDataCaptureConfigureInput($axis, 1, DriveDataCaptureInput.AnalogInput0 ); - - DriveDataCaptureConfigureTrigger($axis, 0, DriveDataCaptureTrigger.PsoOutput ); - DriveDataCaptureConfigureTrigger($axis, 1, DriveDataCaptureTrigger.PsoOutput ); - - DriveDataCaptureConfigureArray($axis, 0, $iDdcArrayAddr, $iDdcArraySize); - DriveDataCaptureConfigureArray($axis, 1, $iDdcArrayAddr + $iDdcSafeSpace + 8 * $iDdcArraySize, $iDdcArraySize); - - // Directly before scan - PsoDistanceCounterOn($axis) - DriveDataCaptureOn($axis, 0) - DriveDataCaptureOn($axis, 1) - - /////////////////////////////////////////////////////////// - // Start the actual scanning - /////////////////////////////////////////////////////////// - for $ii = 0 to ($iNumRepeat-1) - PsoDistanceConfigureArrayDistances($axis, $iPsoArrayAddr, length($iPsoArrayPos), 0) - - if $eScanType == ScanType.Pos - MoveAbsolute($axis, $fPosEnd, $fVelScan) - WaitForInPosition($axis) - MoveAbsolute($axis, $fPosStart, $fVelScan) - WaitForInPosition($axis) - elseif $eScanType == ScanType.Neg - MoveAbsolute($axis, $fPosEnd, $fVelScan) - WaitForInPosition($axis) - MoveAbsolute($axis, $fPosStart, $fVelScan) - WaitForInPosition($axis) - elseif $eScanType == ScanType.PosNeg - if ($ii % 2) == 0 - MoveAbsolute($axis, $fPosEnd, $fVelScan) - WaitForInPosition($axis) - elseif ($ii % 2) == 1 - MoveAbsolute($axis, $fPosStart, $fVelScan) - WaitForInPosition($axis) - end - elseif $eScanType == ScanType.NegPos - if ($ii % 2) == 0 - MoveAbsolute($axis, $fPosEnd, $fVelScan) - WaitForInPosition($axis) - elseif ($ii % 2) == 1 - MoveAbsolute($axis, $fPosStart, $fVelScan) - WaitForInPosition($axis) - end - end - Dwell(0.2) - end - - // Directly after scan - PsoDistanceCounterOff($axis) - DriveDataCaptureOff($axis, 0) - DriveDataCaptureOff($axis, 1) -end - diff --git a/ophyd_devices/epics/devices/aerotech/AerotechSnapAndStepTemplate.ascript b/ophyd_devices/epics/devices/aerotech/AerotechSnapAndStepTemplate.ascript deleted file mode 100644 index c7e01c8..0000000 --- a/ophyd_devices/epics/devices/aerotech/AerotechSnapAndStepTemplate.ascript +++ /dev/null @@ -1,87 +0,0 @@ -// Snap-and-step -// Test program for high speed step scanning with individual triggers on PSO output. -// The file expects external parameter validation. - - - -program - // External parameters - var $fStartPosition as real = {{ scan.startpos }} - var $iNumSteps as integer = {{ scan.numsteps }} - var $fStepSize as real = {{ scan.stepsize }} - var $fExposureTimeSec as real = {{ scan.exptime }} - var $fVelJog as real = {{ scan.travel or 200 }} - var $fVelScan as real = {{ scan.velocity or 50 }} - var $fAcceleration = {{ scan.acceleration or 500 }} - - // Internal - var $axis as axis = ROTY - var $ii as integer - - // Set acceleration - SetupAxisRampType($axis, RampType.Linear) - SetupAxisRampValue($axis,0,$fAcceleration) - - // Move to start position before the scan - var $fPosNext as real = $fStartPosition - MoveAbsolute($axis, $fPosNext, $fVelJog) - WaitForInPosition($axis) - - // Configure PSO (to manual event generation) - PsoDistanceEventsOff($axis) - PsoWindowConfigureEvents($axis, PsoWindowEventMode.None) - PsoWaveformConfigurePulseFixedTotalTime($axis, 50) - PsoWaveformConfigurePulseFixedOnTime($axis, 20) - PsoWaveformConfigurePulseFixedCount($axis, 1) - PsoWaveformApplyPulseConfiguration($axis) - PsoWaveformConfigureMode($axis, PsoWaveformMode.Pulse) - PsoOutputConfigureSource($axis, PsoOutputSource.Waveform) - PsoWaveformOn($axis) - - // Configure Drive Data Collection - var $iDdcArrayAddr as integer = 8388608 - var $iDdcArraySize as integer = iMaximum(5000, $iNumSteps) - var $iDdcSafeSpace as integer = 4096 - - DriveDataCaptureConfigureInput($axis, 0, DriveDataCaptureInput.PrimaryFeedback); - DriveDataCaptureConfigureInput($axis, 1, DriveDataCaptureInput.AnalogInput0 ); - - DriveDataCaptureConfigureTrigger($axis, 0, DriveDataCaptureTrigger.PsoOutput ); - DriveDataCaptureConfigureTrigger($axis, 1, DriveDataCaptureTrigger.PsoOutput ); - - DriveDataCaptureConfigureArray($axis, 0, $iDdcArrayAddr, $iDdcArraySize); - DriveDataCaptureConfigureArray($axis, 1, $iDdcArrayAddr + $iDdcSafeSpace + 8 * $iDdcArraySize, $iDdcArraySize); - - // Directly before scan - PsoDistanceCounterOn($axis) - DriveDataCaptureOn($axis, 0) - DriveDataCaptureOn($axis, 1) - - /////////////////////////////////////////////////////////// - // Start the actual scanning - /////////////////////////////////////////////////////////// - for $ii = 0 to ($iNumSteps-1) - MoveAbsolute($axis, $fPosNext, $fVelScan) - WaitForInPosition($axis) - PsoEventGenerateSingle($axis) - Dwell($fExposureTimeSec) - $fPosNext = $fPosNext + $fStepSize - end - - // Directly after scan - PsoWaveformOff($axis) - DriveDataCaptureOff($axis, 0) - DriveDataCaptureOff($axis, 1) -end - -// Demonstrates using a switch/case conditional. -function iMaximum($A as integer, $B as integer) as integer - var $retVal - if ($A > $B) - $retVal = $A - else - $retVal = $B - end - return $retVal -end - diff --git a/ophyd_devices/epics/devices/grashopper_tomcat.py b/ophyd_devices/epics/devices/grashopper_tomcat.py deleted file mode 100644 index c6e48aa..0000000 --- a/ophyd_devices/epics/devices/grashopper_tomcat.py +++ /dev/null @@ -1,434 +0,0 @@ -import enum -import os -import threading -import time as ttime - -from bec_lib.logger import bec_logger - -# from typing import Any -from ophyd import ADComponent as ADCpt -from ophyd import Component as Cpt -from ophyd import Device, EpicsSignal, EpicsSignalRO, EpicsSignalWithRBV - -# import numpy as np -from ophyd.ophydobj import Kind - -from ophyd_devices.epics.devices.psi_detector_base import CustomDetectorMixin, PSIDetectorBase - -# os.environ["EPICS_CA_AUTO_ADDR_LIST"] = "No" -# os.environ["EPICS_CA_ADDR_LIST"] = "129.129.208.143" - - -logger = bec_logger.logger - - -class GrashopperError(Exception): - """Base class for Grashopper detector errors.""" - - -class GrashopperTimeoutError(GrashopperError): - """Base class for Grashopper detector errors.""" - - -class AutoMode(enum.IntEnum): - """Acquire time auto for Grashopper detector. - - class for acquire_auto and gain_auto - - Off: Gain tap balancing is user controlled using Gain. - Once: Gain tap balancing is automatically adjusted once by the device. - Once it has converged, it automatically returns to the Off state. - Continuous: Gain tap balancing is constantly adjusted by the device. - """ - - OFF = 0 - ONCE = 1 - CONTINUOUS = 2 - - -class ImageMode(enum.IntEnum): - """Image mode for Grashopper detector. - - Single: Acquire a single image, ignores NumImages PV - Multiple: Acquire NumImages images - Continuous: Acquire images continuously - """ - - SINGLE = 0 - MULTIPLE = 1 - CONTINUOUS = 2 - - -class DetectorState(enum.IntEnum): - """Detector states for Grashopper detector""" - - IDLE = 0 - ACQUIRE = 1 - READOUT = 2 - CORRECT = 3 - SAVING = 4 - ABORTING = 5 - ERROR = 6 - WAITING = 7 - INITIALIZING = 8 - DISCONNECTED = 9 - ABORTED = 10 - - -class ImageBinning(enum.IntEnum): - """Image binning for Grashopper detector""" - - X1 = 1 - X2 = 2 - X4 = 4 - - -class VideoMode(enum.IntEnum): - """Video mode for Grashopper detector. - - For details, consult EPICs IOC manual. - """ - - MODE0 = 0 - MODE1 = 1 - MODE2 = 2 - MODE3 = 3 - - -class PixelFormat(enum.IntEnum): - """Pixel format for Grashopper detector.""" - - MONO8 = 0 - MONO12PACKED = 1 - MONO12P = 2 - MONO16 = 3 - - -class COLORMODE(enum.IntEnum): - """Color mode for Grashopper detector. - - Only for readback values from color_mode RO PV. - """ - - MONO = 0 - BAYER = 1 - RGB1 = 2 - RGB2 = 3 - RGB3 = 4 - YUV444 = 5 - YUV422 = 6 - YUV421 = 7 - - -class TriggerSource(enum.IntEnum): - """Trigger signals for Grashopper detector""" - - SOFTWARE = 0 - LINE0 = 1 - LINE2 = 2 - LINE3 = 3 - - -class MemoryPolling(enum.IntEnum): - """Memory polling for Grashopper detector. - - Defines update rate of memory polling for IOC (1s suggested). - """ - - PASSIVE = 0 - EVENT = 1 - IOINTR = 2 - SECONDS10 = 3 - SECONDS5 = 4 - SECONDS2 = 5 - SECONDS1 = 6 - SECONDS05 = 7 - SECONDS02 = 8 - SECONDS01 = 9 - - -class GrashopperTOMCATSetup(CustomDetectorMixin): - """Mixin class to setup TOMCAT specific implementations of the detector. - - This class will be called by the custom_prepare_cls attribute of the detector class. - """ - - def __init__(self, *_args, parent: Device = None, **_kwargs) -> None: - super().__init__(*_args, parent=parent, **_kwargs) - - self.image_shape = (self.parent.cam.image_size_y.get(), self.parent.cam.image_size_x.get()) - self.monitor_thread = None - self.stop_monitor = False - self.update_frequency = 1 - self.low_frame_rate = 80 - - def initialize_detector(self) -> None: - """Initialize detector.""" - self.parent.cam.acquire.put(0) - self.parent.cam.acquire_time_auto.put(AutoMode.CONTINUOUS) - self.parent.cam.gain_auto.put(AutoMode.CONTINUOUS) - self.parent.cam.image_mode.put(ImageMode.MULTIPLE) - self.parent.cam.image_binning.put(ImageBinning.X1) - self.parent.cam.video_mode.put(VideoMode.MODE0) - self.parent.cam.pixel_format.put(PixelFormat.MONO16) - self.parent.cam.trigger_source.put(TriggerSource.SOFTWARE) - self.parent.cam.memory_polling.put(MemoryPolling.SECONDS1) - self.parent.cam.set_image_counter.put(0) - - def initialize_detector_backend(self) -> None: - self.parent.image.queue_size.put(5) - self.parent.image.array_port.put(self.parent.cam.port_name.get()) - self.parent.image.enable_cb.put(1) - self.parent.image.set_array_counter.put(0) - - def set_exposure_time(self, exposure_time: float) -> None: - """Set the detector framerate. - - Args: - framerate (float): Desired framerate in Hz smallest is 87Hz - """ - framerate = 1 / exposure_time - if framerate > self.low_frame_rate: - raise GrashopperError( - f"Trying to set exposure time to {exposure_time}s, this is below the lowest" - f" possible exposure of {1/self.low_frame_rate}s" - ) - self.parent.cam.frame_rate.put(framerate) - - def prepare_detector(self) -> None: - """Prepare detector for acquisition.""" - self.parent.cam.image_mode.put(ImageMode.MULTIPLE) - self.parent.cam.acquire_time_auto.put(AutoMode.CONTINUOUS) - self.set_exposure_time(self.parent.scaninfo.exp_time) - self.parent.set_trigger(TriggerSource.SOFTWARE) - self.parent.cam.set_image_counter.put(0) - self.set_acquisition_params() - - def set_acquisition_params(self) -> None: - """Set acquisition parameters for the detector""" - - # Set number of images and frames (frames is for internal burst of detector) - self.parent.cam.num_images.put( - int(self.parent.scaninfo.num_points * self.parent.scaninfo.frames_per_trigger) - ) - - def prepare_detector_backend(self) -> None: - """Prepare detector backend for acquisition.""" - self.parent.image.set_array_counter.put(0) - self.monitor_thread = None - self.stop_monitor = False - # self.run_monitor() - - def arm_acquisition(self) -> None: - """Arm grashopper detector for acquisition""" - self.parent.cam.acquire.put(1) - signal_conditions = [(self.parent.cam.detector_state.get, DetectorState.WAITING)] - if not self.wait_for_signals( - signal_conditions=signal_conditions, - timeout=self.parent.timeout, - check_stopped=True, - all_signals=False, - ): - raise GrashopperTimeoutError( - f"Failed to arm the acquisition. Detector state {signal_conditions[0][0]}" - ) - - def on_trigger(self) -> None: - """Trigger the detector""" - if self.parent.cam.trigger_source.get() == TriggerSource.SOFTWARE: - self.parent.cam.software_trigger_device.put(1) - ttime.sleep(0.1) - self.send_data() - - def run_monitor(self) -> None: - """ - Run the monitor loop in a separate thread. - """ - self.monitor_thread = threading.Thread(target=self.monitor_loop, daemon=True) - self.monitor_thread.start() - - def monitor_loop(self) -> None: - """ - Monitor the detector status and send data. - """ - while True: - self.send_data() - ttime.sleep(1 / self.update_frequency) - if self.parent.stopped: - break - - def send_data(self) -> None: - """Send data to monitor endpoint in redis.""" - try: - img = self.parent.image.array_data.get().reshape(self.image_shape) - # pylint: disable=protected-access - self.parent._run_subs(sub_type=self.parent.SUB_VALUE, value=img) - except Exception as e: - logger.debug(f"{e} for image with shape {self.parent.image.array_data.get().shape}") - - def stop_detector(self) -> None: - """Stop detector.""" - self.parent.cam.acquire.put(0) - signal_conditions = [(self.parent.cam.detector_state.get, DetectorState.IDLE)] - if not self.wait_for_signals( - signal_conditions=signal_conditions, - timeout=self.parent.timeout - self.parent.timeout // 2, - check_stopped=True, - all_signals=False, - ): - # Retry stop detector and wait for remaining time - self.parent.cam.acquire.put(0) - if not self.wait_for_signals( - signal_conditions=signal_conditions, - timeout=self.parent.timeout - self.parent.timeout // 2, - check_stopped=True, - all_signals=False, - ): - raise GrashopperTimeoutError( - f"Failed to stop detector, detector state {signal_conditions[0][0]}" - ) - - def stop_detector_backend(self) -> None: - """Stop the data backend sending data.""" - self.stop_monitor = True - - -class SLSDetectorCam(Device): - """ - SLS Detector Camera - Grashoppter - - Base class to map EPICS PVs to ophyd signals. - """ - - ## Deprecated PVs, to be checked! - # acquire_time = ADCpt(EpicsSignal, "AcquireTime", kind=Kind.omitted) - # num_exposures = ADCpt(EpicsSignal, "NumExposures", kind=Kind.omitted) - # acquire_period = ADCpt(EpicsSignalWithRBV, "AcquirePeriod", kind=Kind.config) - - # Control PVs - acquire_time_auto = ADCpt(EpicsSignal, "AcquireTimeAuto", kind=Kind.config) - - frame_rate = ADCpt(EpicsSignalWithRBV, "FrameRate", kind=Kind.normal) - num_images = ADCpt(EpicsSignalWithRBV, "NumImages", kind=Kind.normal) - num_images_counter = ADCpt(EpicsSignalRO, "NumImagesCounter_RBV", kind=Kind.normal) - image_mode = ADCpt(EpicsSignalWithRBV, "ImageMode", kind=Kind.config) - acquire = ADCpt(EpicsSignalWithRBV, "Acquire", kind=Kind.config) - detector_state = ADCpt(EpicsSignalRO, "DetectorState_RBV", kind=Kind.normal) - status_message = ADCpt(EpicsSignalRO, "StatusMessage_RBV", string=True, kind=Kind.config) - - set_image_counter = ADCpt(EpicsSignal, "ArrayCounter", kind=Kind.config) - image_counter = ADCpt(EpicsSignal, "ArrayCounter_RBV", kind=Kind.normal) - image_rate = ADCpt(EpicsSignalRO, "ArrayRate_RBV", kind=Kind.omitted) - - # Asyn Port name - port_name = ADCpt(EpicsSignalRO, "PortName_RBV", string=True, kind=Kind.omitted) - - # Readout related PVs - max_image_size_x = ADCpt(EpicsSignalRO, "MaxSizeX_RBV", kind=Kind.config) - max_image_size_y = ADCpt(EpicsSignalRO, "MaxSizeY_RBV", kind=Kind.config) - image_size_x = ADCpt(EpicsSignalRO, "ArraySizeX_RBV", kind=Kind.config) - image_size_y = ADCpt(EpicsSignalRO, "ArraySizeY_RBV", kind=Kind.config) - # Only BinY PV is working, sets both - image_binning = ADCpt(EpicsSignalWithRBV, "BinY", kind=Kind.config) - - gain = ADCpt(EpicsSignalWithRBV, "Gain", kind=Kind.config) - gain_auto = ADCpt(EpicsSignalWithRBV, "GainAuto", kind=Kind.config) - video_mode = ADCpt(EpicsSignalWithRBV, "VideoMode", kind=Kind.config) - pixel_format = ADCpt(EpicsSignalWithRBV, "PixelFormat", kind=Kind.config) - # Desired to set this in future? - color_mode = ADCpt(EpicsSignalRO, "ColorMode_RBV", kind=Kind.config) - - # HW Status PVs - temperature_actual = ADCpt(EpicsSignal, "TemperatureActual", kind=Kind.omitted) - - # Trigger - trigger_mode_active = ADCpt(EpicsSignalWithRBV, "TriggerMode", kind=Kind.config) - trigger_source = ADCpt(EpicsSignalWithRBV, "TriggerSource", kind=Kind.config) - trigger_delay = ADCpt(EpicsSignalWithRBV, "TriggerDelay", kind=Kind.omitted) - exposure_mode = ADCpt(EpicsSignalWithRBV, "ExposureMode", kind=Kind.omitted) - software_trigger_device = ADCpt(EpicsSignal, "SoftwareTrigger", kind=Kind.config) - - # buffer - memory_polling = ADCpt(EpicsSignal, "PoolUsedMem.SCAN", kind=Kind.omitted) - - -class SLSImagePlugin(Device): - """SLS Image Plugin - - Image plugin for SLS detector imitating the behaviour of ImagePlugin from - ophyd's areadetector plugins. - """ - - # Control - array_port = Cpt(EpicsSignal, "NDArrayPort", kind=Kind.omitted, string=True) - enable_cb = Cpt(EpicsSignal, "EnableCallbacks", kind=Kind.config) - queue_size = Cpt(EpicsSignal, "QueueSize", kind=Kind.config) - set_array_counter = Cpt(EpicsSignal, "ArrayCounter", kind=Kind.config) - array_counter = Cpt(EpicsSignal, "ArrayCounter_RBV", kind=Kind.normal) - set_dropped_arrays = Cpt(EpicsSignal, "DroppedArrays", kind=Kind.config) - dropped_arrays = Cpt(EpicsSignal, "DroppedArrays_RBV", kind=Kind.normal) - image_id = Cpt(EpicsSignal, "UniqueId_RBV", kind=Kind.normal) - - # Data - array_data = Cpt(EpicsSignal, "ArrayData", kind=Kind.omitted) - - # Size related PVs from Plugin - array_size_0 = Cpt(EpicsSignalRO, "ArraySize0_RBV", kind=Kind.omitted) - array_size_1 = Cpt(EpicsSignalRO, "ArraySize1_RBV", kind=Kind.omitted) - array_size_2 = Cpt(EpicsSignalRO, "ArraySize2_RBV", kind=Kind.omitted) - array_dimension_size = Cpt(EpicsSignalRO, "NDimensions_RBV", kind=Kind.omitted) - - -class GrashopperTOMCAT(PSIDetectorBase): - """ - Grashopper detector for TOMCAT - - Parent class: PSIDetectorBase - - class attributes: - custom_prepare_cls (GrashopperTOMCATSetup) : Custom detector setup class for TOMCAT, - inherits from CustomDetectorMixin - cam (SLSDetectorCam) : Detector camera - image (SLSImagePlugin) : Image plugin for detector - """ - - # Specify which functions are revealed to the user in BEC client - USER_ACCESS = ["describe"] - - SUB_MONITOR = "monitor" - SUB_VALUE = "value" - _default_sub = SUB_VALUE - - # specify Setup class - custom_prepare_cls = GrashopperTOMCATSetup - # specify minimum readout time for detector - MIN_READOUT = 0 - # specify class attributes - cam = ADCpt(SLSDetectorCam, "cam1:") - image = ADCpt(SLSImagePlugin, "image1:") - - def stage(self) -> list[object]: - rtr = super().stage() - self.custom_prepare.arm_acquisition() - return rtr - - def unstage(self) -> list[object]: - rtr = super().unstage() - self.custom_prepare.stop_monitor = True - return rtr - - def set_trigger(self, trigger_source: TriggerSource) -> None: - """Set trigger source for the detector. - Check the TriggerSource enum for possible values - - Args: - trigger_source (TriggerSource): Trigger source for the detector - - """ - value = trigger_source - self.cam.trigger_source.put(value) - - -if __name__ == "__main__": - hopper = GrashopperTOMCAT(name="hopper", prefix="X02DA-PG-USB:", sim_mode=True) - hopper.wait_for_connection(all_signals=True) diff --git a/ophyd_devices/epics/devices/sequencer_x12sa.py b/ophyd_devices/epics/devices/sequencer_x12sa.py deleted file mode 100644 index 5b2da00..0000000 --- a/ophyd_devices/epics/devices/sequencer_x12sa.py +++ /dev/null @@ -1,59 +0,0 @@ -""" TODO This class was never properly tested, it is solely a first draft and should be tested and extended before use! """ - -from ophyd import Component as Cpt -from ophyd import Device, EpicsSignal, EpicsSignalRO - - -class SequencerX12SA(Device): - """Sequencer for flyscans with epics motor controller and owis stages""" - - desired_output_link_1 = Cpt(Signal, "DOL1") - desired_output_value_1 = Cpt(EpicsSignal, "DO1") - output_link_1 = Cpt(EpicsSignal, "LNK1") - delay_time_1 = Cpt(EpicsSignal, "DLY1") - desired_output_link_2 = Cpt(EpicsSignal, "DOL2") - desired_output_value_2 = Cpt(EpicsSignal, "DO2") - output_link_2 = Cpt(EpicsSignal, "LNK2") - delay_time_2 = Cpt(EpicsSignal, "DLY2") - - select_mechanism = Cpt(EpicsSignal, "SELM", string=True) - link_selection = Cpt(EpicsSignal, "SELN") - process = Cpt(EpicsSignal, "PROC", string=True) - - status = Cpt(EpicsSignalRO, "STAT", string=True) - processing_active = Cpt(EpicsSignalRO, "PACT") - - # def __init__( - # self, - # prefix="", - # *, - # name, - # kind=None, - # read_attrs=None, - # configuration_attrs=None, - # parent=None, - # **kwargs - # ): - # # get configuration attributes from kwargs and then remove them - # attrs = {} - # for key, value in kwargs.items(): - # if hasattr(EpicsMotorEx, key) and isinstance(getattr(EpicsMotorEx, key), Cpt): - # attrs[key] = value - # for key in attrs: - # kwargs.pop(key) - - # super().__init__( - # prefix, - # name=name, - # kind=kind, - # read_attrs=read_attrs, - # configuration_attrs=configuration_attrs, - # parent=parent, - # **kwargs - # ) - - # # set configuration attributes - # for key, value in attrs.items(): - # # print out attributes that are being configured - # print("setting ", key, "=", value) - # getattr(self, key).put(value) diff --git a/ophyd_devices/sls_devices/__init__.py b/ophyd_devices/interfaces/__init__.py similarity index 100% rename from ophyd_devices/sls_devices/__init__.py rename to ophyd_devices/interfaces/__init__.py diff --git a/ophyd_devices/interfaces/base_classes/__init__.py b/ophyd_devices/interfaces/base_classes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ophyd_devices/ophyd_base_devices/ophyd_rotation_base.py b/ophyd_devices/interfaces/base_classes/ophyd_rotation_base.py similarity index 97% rename from ophyd_devices/ophyd_base_devices/ophyd_rotation_base.py rename to ophyd_devices/interfaces/base_classes/ophyd_rotation_base.py index ca2734e..a351d29 100644 --- a/ophyd_devices/ophyd_base_devices/ophyd_rotation_base.py +++ b/ophyd_devices/interfaces/base_classes/ophyd_rotation_base.py @@ -5,7 +5,7 @@ from ophyd import Component as Cpt from ophyd import EpicsMotor from typeguard import typechecked -from ophyd_devices.ophyd_base_devices.bec_protocols import BECRotationProtocol +from ophyd_devices.interfaces.protocols.bec_protocols import BECRotationProtocol from ophyd_devices.utils.bec_utils import ConfigSignal logger = bec_logger.logger diff --git a/ophyd_devices/epics/devices/psi_delay_generator_base.py b/ophyd_devices/interfaces/base_classes/psi_delay_generator_base.py similarity index 100% rename from ophyd_devices/epics/devices/psi_delay_generator_base.py rename to ophyd_devices/interfaces/base_classes/psi_delay_generator_base.py diff --git a/ophyd_devices/epics/devices/psi_detector_base.py b/ophyd_devices/interfaces/base_classes/psi_detector_base.py similarity index 100% rename from ophyd_devices/epics/devices/psi_detector_base.py rename to ophyd_devices/interfaces/base_classes/psi_detector_base.py diff --git a/ophyd_devices/interfaces/protocols/__init__.py b/ophyd_devices/interfaces/protocols/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ophyd_devices/ophyd_base_devices/bec_protocols.py b/ophyd_devices/interfaces/protocols/bec_protocols.py similarity index 100% rename from ophyd_devices/ophyd_base_devices/bec_protocols.py rename to ophyd_devices/interfaces/protocols/bec_protocols.py diff --git a/ophyd_devices/ophyd_base_devices/__init__.py b/ophyd_devices/ophyd_base_devices/__init__.py deleted file mode 100644 index 20bff9f..0000000 --- a/ophyd_devices/ophyd_base_devices/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -from .bec_protocols import ( - BECDeviceProtocol, - BECFlyerProtocol, - BECMixinProtocol, - BECPositionerProtocol, - BECRotationProtocol, - BECScanProtocol, - BECSignalProtocol, -) -from .tomcat_rotation_motors import TomcatAerotechRotation diff --git a/ophyd_devices/ophyd_base_devices/tomcat_rotation_motors.py b/ophyd_devices/ophyd_base_devices/tomcat_rotation_motors.py deleted file mode 100644 index 63f44e0..0000000 --- a/ophyd_devices/ophyd_base_devices/tomcat_rotation_motors.py +++ /dev/null @@ -1,183 +0,0 @@ -""" Module for Tomcat rotation motors. - -The following classes implement the rotation motors for: - -- AerotechAutomation1 (Tomcat), based on EpicsMotorIOC. - -""" - -import threading -import time - -import numpy as np -from bec_lib import threadlocked -from ophyd import DeviceStatus - -from ophyd_devices.ophyd_base_devices.bec_protocols import BECFlyerProtocol, BECScanProtocol -from ophyd_devices.ophyd_base_devices.ophyd_rotation_base import EpicsRotationBase - - -class TomcatAerotechRotation(EpicsRotationBase, BECFlyerProtocol, BECScanProtocol): - """Special motor class that provides flyer interface and progress bar.""" - - SUB_PROGRESS = "progress" - - def __init__( - self, - prefix="", - *, - name, - kind=None, - read_attrs=None, - configuration_attrs=None, - parent=None, - **kwargs, - ): - """Implementation of the Tomcat AerotechAutomation 1 rotation motor class. - - This motor class is based on EpicsRotationBase and provides in addition the flyer interface for BEC - and a progress update. - """ - super().__init__( - prefix=prefix, - name=name, - kind=kind, - read_attrs=read_attrs, - configuration_attrs=configuration_attrs, - parent=parent, - **kwargs, - ) - self._start_position = None - self._target_position = None - self._stopped = False - self._rlock = threading.RLock() - self.subscribe(self._progress_update, run=False) - - # ------------------ alternative to using configure method --------------------- # - @property - def start_position(self) -> float: - """Get the start position.""" - return self._start_position - - @start_position.setter - def start_position(self, value: float) -> None: - """Set the start position.""" - self._start_position = value - - @property - def target_position(self) -> float: - """Get the start position.""" - return self._target_position - - @target_position.setter - def target_position(self, value: float) -> None: - """Set the start position.""" - self._target_position = value - - # ------------------ alternative to using configure method --------------------- # - # configure method is optional for flyers within BEC, you can use stage method or pre_scan method to - # set relevant parameters on the device. - - # def configure(self, d: dict) -> dict: - # """Configure method from the device. - - # This method is usually used to set configuration parameters for the device. - - # Args: - # d (dict): Dictionary with configuration parameters. - - # """ - # if "target" in d: - # self._target_position = d["target"] - # del d["target"] - # if "position" in d: - # self._target_position = d["position"] - # del d["position"] - # return super().configure(d) - - def pre_scan(self): - """Perform pre-scan operation, e.g. move to start position.""" - if self._start_position: - self.move(self._start_position, wait=True) - - def kickoff(self) -> DeviceStatus: - """Kickoff the scan. - - The kickoff method should return a status object that is set to finish once the flyer flys, and is ready for the next actions. - I would consider the following implementation. - """ - self._start_position = float(self.position) - self.move(self._target_position, wait=False) - status = DeviceStatus(self) - status.set_finished() - return status - - def complete(self) -> DeviceStatus: - """Complete method of the scan. - - This will be called in a fly scan after the kickoff, thus, the stage will be moving to it's target position. - It should - - The complete method should return a status object that is set to finish once the flyer is done and the scan is complete. - I would consider the following implementation. - """ - threading.Thread(target=self._is_motor_moving, daemon=True).start() - status = DeviceStatus(self) - self.subscribe(status.set_finished, event_type=self.SUB_DONE, run=False) - return status - - def stage(self) -> list[object]: - """Stage the scan. - - We add here in addition the setting of the _stopped flag to False for the thread. - """ - self._stopped = False - return super().stage() - - def stop(self, success: bool = False) -> None: - """Stop the scan. - - If the device is stopped, the _stopped flag is set to True. - """ - self._stopped = True - super().stop(success=success) - - @threadlocked - def _is_motor_moving(self): - """Function to check if the motor is moving. - - This function is used in a thread to check if the motor is moving. - It resolves by running""" - while self.motor_done_move.get(): - if self._stopped: - self._done_moving(success=False) - return - time.sleep(0.1) - self._done_moving(success=True) - - # TODO This logic could be refined to be more robust for various scan types, i.e. at the moment it just takes - # the start and target position and calculates the progress based on the current position. - def _progress_update(self, value, **kwargs) -> None: - """Progress update on the scan. - - Runs the progress update on the device progress during the scan. - It uses the SUB_PROGRESS event from ophyd to update BEC about the progress. - Scans need to be aware which device progress is relevant for the scan. - - Args: - value (float): The value of the motor position. - """ - if (self._start_position is None) or (self._target_position is None) or (not self.moving): - self._run_subs(sub_type=self.SUB_PROGRESS, value=1, max_value=1, done=1) - return - - progress = np.abs( - (value - self._start_position) / (self._target_position - self._start_position) - ) - max_value = 100 - self._run_subs( - sub_type=self.SUB_PROGRESS, - value=int(100 * progress), - max_value=max_value, - done=int(np.isclose(max_value, progress, 1e-3)), - ) diff --git a/ophyd_devices/sim/test.py b/ophyd_devices/sim/test.py deleted file mode 100644 index b1e1f1c..0000000 --- a/ophyd_devices/sim/test.py +++ /dev/null @@ -1,51 +0,0 @@ -import inspect - -import lmfit - - -class LmfitModelMixin: - - # def __init__(self): - # self.model = lmfit.models.GaussianModel() - # self.params = self.model.make_params() - # self.params["center"].set(value=0) - # self.params["amplitude"].set(value=1) - # self.params["sigma"].set(value=1) - - @staticmethod - def available_models() -> dict: - """ - Get available models from lmfit.models. - - Exclude Gaussian2dModel, ExpressionModel, Model, SplineModel. - """ - avail_models = {} - for name, model_cls in inspect.getmembers(lmfit.models): - try: - is_model = issubclass(model_cls, lmfit.model.Model) - except TypeError: - is_model = False - if is_model and name not in [ - "Gaussian2dModel", - "ExpressionModel", - "Model", - "SplineModel", - ]: - avail_models[name] = model_cls - return avail_models - - def create_properties(self): - """ - Create properties for model parameters. - """ - for name in self.available_models(): - setattr(self, name, param) - - @staticmethod - def get_model(model: str) -> lmfit.Model: - """Get model for given string.""" - if isinstance(model, str): - model = getattr(lmfit.models, model, None) - if not model: - raise ValueError(f"Model {model} not found.") - return model diff --git a/tests/test_grashopper_tomcat.py b/tests/test_grashopper_tomcat.py deleted file mode 100644 index eb6ceeb..0000000 --- a/tests/test_grashopper_tomcat.py +++ /dev/null @@ -1,226 +0,0 @@ -# pylint: skip-file -from unittest import mock - -import pytest - -from ophyd_devices.epics.devices.grashopper_tomcat import ( - COLORMODE, - AutoMode, - DetectorState, - GrashopperError, - GrashopperTimeoutError, - GrashopperTOMCATSetup, - ImageBinning, - ImageMode, - MemoryPolling, - PixelFormat, - TriggerSource, - VideoMode, -) - - -def patch_dual_pvs(device): - for walk in device.walk_signals(): - if not hasattr(walk.item, "_read_pv"): - continue - if not hasattr(walk.item, "_write_pv"): - continue - if walk.item._read_pv.pvname.endswith("_RBV"): - walk.item._read_pv = walk.item._write_pv - - -@pytest.fixture(scope="function") -def mock_GrashopperSetup(): - mock_Grashopper = mock.MagicMock() - yield GrashopperTOMCATSetup(parent=mock_Grashopper) - - -# Fixture for scaninfo -@pytest.fixture( - params=[ - { - "scan_id": "1234", - "scan_type": "step", - "num_points": 500, - "frames_per_trigger": 1, - "exp_time": 0.1, - "readout_time": 0.1, - }, - { - "scan_id": "1234", - "scan_type": "step", - "num_points": 500, - "frames_per_trigger": 5, - "exp_time": 0.01, - "readout_time": 0, - }, - { - "scan_id": "1234", - "scan_type": "fly", - "num_points": 500, - "frames_per_trigger": 1, - "exp_time": 1, - "readout_time": 0.2, - }, - { - "scan_id": "1234", - "scan_type": "fly", - "num_points": 500, - "frames_per_trigger": 5, - "exp_time": 0.1, - "readout_time": 0.4, - }, - ] -) -def scaninfo(request): - return request.param - - -def test_initialize_detector(mock_GrashopperSetup): - """Test the initialize_detector method.""" - # Call the function you want to test - mock_GrashopperSetup.initialize_detector() - - # Assert the correct methods are called with the expected arguments - mock_GrashopperSetup.parent.cam.acquire.put.assert_called_once_with(0) - mock_GrashopperSetup.parent.cam.acquire_time_auto.put.assert_called_once_with( - AutoMode.CONTINUOUS - ) - mock_GrashopperSetup.parent.cam.gain_auto.put.assert_called_once_with(AutoMode.CONTINUOUS) - mock_GrashopperSetup.parent.cam.image_mode.put.assert_called_once_with(ImageMode.MULTIPLE) - mock_GrashopperSetup.parent.cam.image_binning.put.assert_called_once_with(ImageBinning.X1) - mock_GrashopperSetup.parent.cam.video_mode.put.assert_called_once_with(VideoMode.MODE0) - mock_GrashopperSetup.parent.cam.pixel_format.put.assert_called_once_with(PixelFormat.MONO16) - mock_GrashopperSetup.parent.cam.trigger_source.put.assert_called_once_with( - TriggerSource.SOFTWARE - ) - mock_GrashopperSetup.parent.cam.memory_polling.put.assert_called_once_with( - MemoryPolling.SECONDS1 - ) - mock_GrashopperSetup.parent.cam.set_image_counter.put.assert_called_once_with(0) - - -def test_initialize_detector_backend(mock_GrashopperSetup): - """Test the initialize_detector_backend method.""" - # Call the function you want to test - mock_GrashopperSetup.initialize_detector_backend() - - # Assert the correct methods are called with the expected arguments - mock_GrashopperSetup.parent.image.queue_size.put.assert_called_once_with(5) - mock_GrashopperSetup.parent.image.array_port.put.assert_called_once_with( - mock_GrashopperSetup.parent.cam.port_name.get() - ) - mock_GrashopperSetup.parent.image.enable_cb.put.assert_called_once_with(1) - mock_GrashopperSetup.parent.image.set_array_counter.put.assert_called_once_with(0) - - -@pytest.mark.parametrize("exposure_time", [1, 0.1, 0.01, 0.001, 0.0001]) -def test_set_exposure_time(mock_GrashopperSetup, exposure_time): - """Test the set_exposure_time method.""" - # Call the function you want to test - frame_rate = 1 / exposure_time - if frame_rate > mock_GrashopperSetup.low_frame_rate: - with pytest.raises(GrashopperError): - mock_GrashopperSetup.set_exposure_time(exposure_time) - else: - mock_GrashopperSetup.set_exposure_time(exposure_time) - mock_GrashopperSetup.parent.cam.frame_rate.put.assert_called_once_with(frame_rate) - - -def test_prepare_detector(mock_GrashopperSetup, scaninfo): - """Test the prepare_detector method.""" - # setup scaninof - for k, v in scaninfo.items(): - setattr(mock_GrashopperSetup.parent.scaninfo, k, v) - - # Call the function you want to test - with ( - mock.patch.object( - mock_GrashopperSetup, "set_acquisition_params" - ) as mock_set_acquisition_params, - mock.patch.object(mock_GrashopperSetup, "set_exposure_time") as mock_set_exposure_time, - ): - mock_GrashopperSetup.prepare_detector() - - # Assert the correct methods are called with the expected arguments - mock_GrashopperSetup.parent.cam.image_mode.put.assert_called_once_with(ImageMode.MULTIPLE) - mock_GrashopperSetup.parent.cam.acquire_time_auto.put.assert_called_once_with( - AutoMode.CONTINUOUS - ) - mock_GrashopperSetup.parent.set_trigger.assert_called_once_with(TriggerSource.SOFTWARE) - mock_GrashopperSetup.parent.cam.set_image_counter.put.assert_called_once_with(0) - mock_set_acquisition_params.assert_called_once() - mock_set_exposure_time.assert_called_once_with(scaninfo["exp_time"]) - - -def test_prepare_detector_backend(mock_GrashopperSetup): - """Test the prepare_detector_backend method.""" - # Call the function you want to test - mock_GrashopperSetup.prepare_detector_backend() - mock_GrashopperSetup.parent.image.set_array_counter.put.assert_called_once_with(0) - assert mock_GrashopperSetup.monitor_thread is None - assert not mock_GrashopperSetup.stop_monitor - - -@pytest.mark.parametrize("detector_state", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) -def test_arm_acquisition(mock_GrashopperSetup, detector_state): - """Test the arm_acquisition method.""" - # Call the function you want to test - mock_GrashopperSetup.parent.cam.detector_state.get.return_value = detector_state - mock_GrashopperSetup.parent.timeout = 0.1 - - if detector_state != DetectorState.WAITING: - with pytest.raises(GrashopperTimeoutError): - mock_GrashopperSetup.arm_acquisition() - assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 1 - assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(1) - else: - mock_GrashopperSetup.arm_acquisition() - assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 1 - assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(1) - - -@pytest.mark.parametrize("trigger_source", [0, 1, 2, 3]) -def test_on_trigger(mock_GrashopperSetup, trigger_source): - """Test the on_trigger method.""" - # Call the function you want to test - mock_GrashopperSetup.parent.cam.trigger_source.get.return_value = trigger_source - with mock.patch.object(mock_GrashopperSetup, "send_data") as mock_send_data: - mock_GrashopperSetup.on_trigger() - - # Assert the correct methods are called with the expected arguments - if trigger_source == TriggerSource.SOFTWARE: - mock_GrashopperSetup.parent.cam.software_trigger_device.put.assert_called_once_with(1) - assert mock_send_data.call_count == 1 - - -def test_set_acquisition_params(mock_GrashopperSetup, scaninfo): - """Test the set_acquisition_params method.""" - # Setup scaninfo - mock_GrashopperSetup.parent.scaninfo.num_points = scaninfo["num_points"] - mock_GrashopperSetup.parent.scaninfo.frames_per_trigger = scaninfo["frames_per_trigger"] - - # Call the function you want to test - mock_GrashopperSetup.set_acquisition_params() - - # Assert the correct methods are called with the expected arguments - expected_num_images = scaninfo["num_points"] * scaninfo["frames_per_trigger"] - mock_GrashopperSetup.parent.cam.num_images.put.assert_called_once_with(expected_num_images) - - -@pytest.mark.parametrize("detector_state", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) -def test_stop_detector(mock_GrashopperSetup, detector_state): - """Test the arm_acquisition method.""" - # Call the function you want to test - mock_GrashopperSetup.parent.cam.detector_state.get.return_value = detector_state - mock_GrashopperSetup.parent.timeout = 0.1 - - if detector_state != DetectorState.IDLE: - with pytest.raises(GrashopperTimeoutError): - mock_GrashopperSetup.stop_detector() - assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 2 - assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(0) - else: - mock_GrashopperSetup.stop_detector() - assert mock_GrashopperSetup.parent.cam.acquire.put.call_count == 1 - assert mock_GrashopperSetup.parent.cam.acquire.put.call_args == mock.call(0) diff --git a/tests/test_simulation.py b/tests/test_simulation.py index ca8eb74..a335e72 100644 --- a/tests/test_simulation.py +++ b/tests/test_simulation.py @@ -10,7 +10,7 @@ import pytest from bec_server.device_server.tests.utils import DMMock from ophyd import Device, Signal -from ophyd_devices.ophyd_base_devices.bec_protocols import ( +from ophyd_devices.interfaces.protocols.bec_protocols import ( BECDeviceProtocol, BECFlyerProtocol, BECPositionerProtocol,