Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Break devices module into submodule _devices #539

Merged
merged 7 commits into from
Sep 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ describe the future plans.
* `#528 <https://github.com/BCDA-APS/apstools/pull/528>`_
Add ``kind=`` kwarg to synApps Devices.

* `#539 <https://github.com/BCDA-APS/apstools/pull/539>`_
Break ``devices`` into submodule ``_devices``.

:1.5.1: release expected by 2021-07-31

* `#522 <https://github.com/BCDA-APS/apstools/issues/522>`_
Expand Down
35 changes: 35 additions & 0 deletions apstools/_devices/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from .aps_bss_user import ApsBssUserInfoDevice
from .aps_cycle import ApsCycleComputedRO
from .aps_cycle import ApsCycleDM
from .aps_machine import ApsMachineParametersDevice
from .aps_undulator import ApsUndulator
from .aps_undulator import ApsUndulatorDual
from .area_detector_support import AD_FrameType_schemes
from .area_detector_support import AD_plugin_primed
from .area_detector_support import AD_prime_plugin
from .area_detector_support import AD_prime_plugin2
from .area_detector_support import AD_EpicsHdf5FileName
from .area_detector_support import AD_EpicsJpegFileName
from .axis_tuner import AxisTunerException
from .axis_tuner import AxisTunerMixin
from .description_mixin import EpicsDescriptionMixin
from .kohzu_monochromator import KohzuSeqCtl_Monochromator
from .mixin_base import DeviceMixinBase
from .motor_mixins import EpicsMotorDialMixin
from .motor_mixins import EpicsMotorEnableMixin
from .motor_mixins import EpicsMotorLimitsMixin
from .motor_mixins import EpicsMotorRawMixin
from .motor_mixins import EpicsMotorResolutionMixin
from .motor_mixins import EpicsMotorServoMixin
from .scaler_support import SCALER_AUTOCOUNT_MODE
from .scaler_support import use_EPICS_scaler_channels
from .shutters import ApsPssShutter
from .shutters import ApsPssShutterWithStatus
from .shutters import EpicsMotorShutter
from .shutters import EpicsOnOffShutter
from .shutters import OneSignalShutter
from .shutters import ShutterBase
from .shutters import SimulatedApsPssShutterWithStatus
from .struck3820 import Struck3820
from .tracking_signal import TrackingSignal
from .xia_pf4 import DualPf4FilterBox
49 changes: 49 additions & 0 deletions apstools/_devices/aps_bss_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""
APS User Proposal and ESAF Information
+++++++++++++++++++++++++++++++++++++++

.. autosummary::

~ApsBssUserInfoDevice
"""

from ophyd import Component
from ophyd import Device
from ophyd import EpicsSignal


class ApsBssUserInfoDevice(Device):
"""
Provide current experiment info from the APS BSS.

.. index:: Ophyd Device; ApsBssUserInfoDevice

BSS: Beamtime Scheduling System

EXAMPLE::

bss_user_info = ApsBssUserInfoDevice(
"9id_bss:",
name="bss_user_info")
sd.baseline.append(bss_user_info)

NOTE: There is info provided by the APS proposal & ESAF systems.
"""

proposal_number = Component(EpicsSignal, "proposal_number")
activity = Component(EpicsSignal, "activity", string=True)
badge = Component(EpicsSignal, "badge", string=True)
bss_name = Component(EpicsSignal, "bss_name", string=True)
contact = Component(EpicsSignal, "contact", string=True)
email = Component(EpicsSignal, "email", string=True)
institution = Component(EpicsSignal, "institution", string=True)
station = Component(EpicsSignal, "station", string=True)
team_others = Component(EpicsSignal, "team_others", string=True)
time_begin = Component(EpicsSignal, "time_begin", string=True)
time_end = Component(EpicsSignal, "time_end", string=True)
timestamp = Component(EpicsSignal, "timestamp", string=True)
title = Component(EpicsSignal, "title", string=True)
# not yet updated, see: https://git.aps.anl.gov/jemian/aps_bss_user_info/issues/10
esaf = Component(EpicsSignal, "esaf", string=True)
esaf_contact = Component(EpicsSignal, "esaf_contact", string=True)
esaf_team = Component(EpicsSignal, "esaf_team", string=True)
57 changes: 57 additions & 0 deletions apstools/_devices/aps_cycle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
"""
APS cycles
+++++++++++++++++++++++++++++++++++++++

.. autosummary::

~ApsCycleComputedRO
~ApsCycleDM
"""

import datetime
from ophyd.sim import SynSignalRO


class ApsCycleDM(SynSignalRO):
"""
Get the APS cycle name from the APS Data Management system.

.. index:: Ophyd Signal; ApsCycleDM

This signal is read-only.
"""

_cycle_ends = "1980" # force a read from DM on first get()
_cycle_name = "unknown"

def get(self):
if datetime.now().isoformat(sep=" ") >= self._cycle_ends:
from ..beamtime.apsbss import api_bss

# only update from data management after the end of the run
cycle = api_bss.getCurrentRun()
self._cycle_name = cycle["name"]
self._cycle_ends = cycle["endTime"]
return self._cycle_name


class ApsCycleComputedRO(SynSignalRO):
"""
Compute the APS cycle name based on the calendar and the usual practice.

.. index:: Ophyd Signal; ApsCycleComputedRO

Absent any facility PV that provides the name of the current operating
cycle, this can be approximated by python computation (as long as the
present scheduling pattern is maintained)

This signal is read-only.

NOTE: There is info provided by the APS proposal & ESAF systems. See
:class:`~ApsCycleDM`.
"""

def get(self):
dt = datetime.now()
aps_cycle = f"{dt.year}-{int((dt.month-0.1)/4) + 1}"
return aps_cycle
123 changes: 123 additions & 0 deletions apstools/_devices/aps_machine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
"""
APS Machine Parameters
+++++++++++++++++++++++++++++++++++++++

**APS machine parameters**

.. autosummary::

~ApsMachineParametersDevice
~ApsOperatorMessagesDevice
"""

from ophyd import Component
from ophyd import Device
from ophyd import EpicsSignalRO
from .aps_cycle import ApsCycleDM


class ApsOperatorMessagesDevice(Device):
"""
General messages from the APS main control room.

.. index:: Ophyd Device; ApsOperatorMessagesDevice
"""

operators = Component(EpicsSignalRO, "OPS:message1", string=True)
floor_coordinator = Component(EpicsSignalRO, "OPS:message2", string=True)
fill_pattern = Component(EpicsSignalRO, "OPS:message3", string=True)
last_problem_message = Component(EpicsSignalRO, "OPS:message4", string=True)
last_trip_message = Component(EpicsSignalRO, "OPS:message5", string=True)
# messages 6-8: meaning?
message6 = Component(EpicsSignalRO, "OPS:message6", string=True)
message7 = Component(EpicsSignalRO, "OPS:message7", string=True)
message8 = Component(EpicsSignalRO, "OPS:message8", string=True)


class ApsMachineParametersDevice(Device):
"""
Common operational parameters of the APS of general interest.

.. index:: Ophyd Device; ApsMachineParametersDevice

EXAMPLE::

import apstools.devices as APS_devices
APS = APS_devices.ApsMachineParametersDevice(name="APS")
aps_current = APS.current

# make sure these values are logged at start and stop of every scan
sd.baseline.append(APS)
# record storage ring current as secondary stream during scans
# name: aps_current_monitor
# db[-1].table("aps_current_monitor")
sd.monitors.append(aps_current)

The `sd.baseline` and `sd.monitors` usage relies on this global setup:

from bluesky import SupplementalData
sd = SupplementalData()
RE.preprocessors.append(sd)

.. autosummary::

~inUserOperations

"""

current = Component(EpicsSignalRO, "S:SRcurrentAI")
lifetime = Component(EpicsSignalRO, "S:SRlifeTimeHrsCC")
aps_cycle = Component(ApsCycleDM)
machine_status = Component(EpicsSignalRO, "S:DesiredMode", string=True)
# In [3]: APS.machine_status.enum_strs
# Out[3]:
# ('State Unknown',
# 'USER OPERATIONS',
# 'Bm Ln Studies',
# 'INJ Studies',
# 'ASD Studies',
# 'NO BEAM',
# 'MAINTENANCE')
operating_mode = Component(EpicsSignalRO, "S:ActualMode", string=True)
# In [4]: APS.operating_mode.enum_strs
# Out[4]:
# ('State Unknown',
# 'NO BEAM',
# 'Injecting',
# 'Stored Beam',
# 'Delivered Beam',
# 'MAINTENANCE')
shutter_permit = Component(EpicsSignalRO, "ACIS:ShutterPermit", string=True)
fill_number = Component(EpicsSignalRO, "S:FillNumber")
orbit_correction = Component(EpicsSignalRO, "S:OrbitCorrection:CC")
global_feedback = Component(EpicsSignalRO, "SRFB:GBL:LoopStatusBI", string=True)
global_feedback_h = Component(EpicsSignalRO, "SRFB:GBL:HLoopStatusBI", string=True)
global_feedback_v = Component(EpicsSignalRO, "SRFB:GBL:VLoopStatusBI", string=True)
operator_messages = Component(ApsOperatorMessagesDevice)

@property
def inUserOperations(self):
"""
determine if APS is in User Operations mode (boolean)

Use this property to configure ophyd Devices for direct or simulated hardware.
See issue #49 (https://github.com/BCDA-APS/apstools/issues/49) for details.

EXAMPLE::

APS = apstools.devices.ApsMachineParametersDevice(name="APS")

if APS.inUserOperations:
suspend_APS_current = bluesky.suspenders.SuspendFloor(APS.current, 2, resume_thresh=10)
RE.install_suspender(suspend_APS_current)
else:
# use pseudo shutter controls and no current suspenders
pass

"""
# fmt: off
return self.machine_status.get() in (
1, "USER OPERATIONS",
2, "Bm Ln Studies",
)
# fmt: on
79 changes: 79 additions & 0 deletions apstools/_devices/aps_undulator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
"""
APS undulator
+++++++++++++++++++++++++++++++++++++++

.. autosummary::

~ApsUndulator
~ApsUndulatorDual
"""

from ophyd import Component
from ophyd import Device
from ophyd import EpicsSignal
from ophyd import EpicsSignalRO
from ophyd import Signal
from .tracking_signal import TrackingSignal


class ApsUndulator(Device):
"""
APS Undulator

.. index:: Ophyd Device; ApsUndulator

EXAMPLE::

undulator = ApsUndulator("ID09ds:", name="undulator")
"""

energy = Component(
EpicsSignal, "Energy", write_pv="EnergySet", put_complete=True, kind="hinted",
)
energy_taper = Component(
EpicsSignal, "TaperEnergy", write_pv="TaperEnergySet", kind="config",
)
gap = Component(EpicsSignal, "Gap", write_pv="GapSet")
gap_taper = Component(
EpicsSignal, "TaperGap", write_pv="TaperGapSet", kind="config"
)
start_button = Component(EpicsSignal, "Start", put_complete=True, kind="omitted")
stop_button = Component(EpicsSignal, "Stop", kind="omitted")
harmonic_value = Component(EpicsSignal, "HarmonicValue", kind="config")
gap_deadband = Component(EpicsSignal, "DeadbandGap", kind="config")
device_limit = Component(EpicsSignal, "DeviceLimit", kind="config")

access_mode = Component(EpicsSignalRO, "AccessSecurity", kind="omitted")
device_status = Component(EpicsSignalRO, "Busy", kind="omitted")
total_power = Component(EpicsSignalRO, "TotalPower", kind="config")
message1 = Component(EpicsSignalRO, "Message1", kind="omitted")
message2 = Component(EpicsSignalRO, "Message2", kind="omitted")
message3 = Component(EpicsSignalRO, "Message3", kind="omitted")
time_left = Component(EpicsSignalRO, "ShClosedTime", kind="omitted")

device = Component(EpicsSignalRO, "Device", kind="config")
location = Component(EpicsSignalRO, "Location", kind="config")
version = Component(EpicsSignalRO, "Version", kind="config")

# Useful undulator parameters that are not EPICS PVs.
energy_deadband = Component(Signal, value=0.0, kind="config")
energy_backlash = Component(Signal, value=0.0, kind="config")
energy_offset = Component(Signal, value=0, kind="config")
tracking = Component(TrackingSignal, value=False, kind="config")


class ApsUndulatorDual(Device):
"""
APS Undulator with upstream *and* downstream controls

.. index:: Ophyd Device; ApsUndulatorDual

EXAMPLE::

undulator = ApsUndulatorDual("ID09", name="undulator")

note:: the trailing ``:`` in the PV prefix should be omitted
"""

upstream = Component(ApsUndulator, "us:")
downstream = Component(ApsUndulator, "ds:")
Loading