Skip to content

Commit

Permalink
DEV/DOC: Specify lcls 1 or 2 timing in initializer, updated documenta…
Browse files Browse the repository at this point in the history
…tion
  • Loading branch information
Kaushik Malapati authored and Kaushik Malapati committed Oct 31, 2023
1 parent 2eceb5f commit 03a1ba6
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 55 deletions.
6 changes: 3 additions & 3 deletions docs/source/upcoming_release_notes/1178-new-tpr-class.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ API Breaks

Features
--------
- New TprTrigger and TprMotor device classes in tpr.py
- Analogous to Trigger and EvrMotor from evr.py
- N/A

Device Updates
--------------
- N/A

New Devices
-----------
- N/A
- New TprTrigger and TprMotor device classes in tpr.py
- Analogous to Trigger and EvrMotor from evr.py

Bugfixes
--------
Expand Down
12 changes: 7 additions & 5 deletions pcdsdevices/tests/test_tpr.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
import pytest
from ophyd.sim import make_fake_device

from ..tpr import TprTrigger
from ..tpr import TimingMode, TprTrigger


@pytest.fixture(scope='function')
def fake_trigger():
cls = make_fake_device(TprTrigger)
return cls('TST:TPR', channel=1, name='trig_a')
return cls('TST:TPR', channel=10, timing_mode=TimingMode.LCLS2, name='trig_a')


def test_enable(fake_trigger):
fake_trigger.enable()
assert fake_trigger.enable_cmd.get() == 1
assert fake_trigger.enable_ch_cmd.get() == 1
assert fake_trigger.enable_trg_cmd.get() == 1
fake_trigger.disable()
assert fake_trigger.enable_cmd.get() == 0
assert fake_trigger.enable_ch_cmd.get() == 0
assert fake_trigger.enable_trg_cmd.get() == 0


@pytest.mark.timeout(5)
def test_disconnected_trigger():
TprTrigger('TST', channel=1, name='test')
TprTrigger('TST', channel=1, timing_mode=TimingMode.LCLS1, name='test')
111 changes: 64 additions & 47 deletions pcdsdevices/tpr.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from enum import Enum

from ophyd import Component as Cpt
from ophyd import Device, EpicsSignal, EpicsSignalRO
from ophyd import FormattedComponent as FCpt
Expand All @@ -6,11 +8,17 @@
from .pv_positioner import PVPositionerIsClose
from .signal import MultiDerivedSignal, MultiDerivedSignalRO
from .type_hints import SignalToValue
from .variety import set_metadata

TPR_TICK_NS = 5.384
TPR_TAP_NS = 0.08


class TimingMode(Enum):
LCLS1 = 1
LCLS2 = 2


def _get_delay(mds: MultiDerivedSignal, items: SignalToValue) -> float:
"""Calculates delay in ns from ticks and taps"""
return items[mds.attrs[0]] * TPR_TICK_NS + items[mds.attrs[1] * TPR_TAP_NS]
Expand All @@ -33,9 +41,9 @@ class TprMotor(PVPositionerIsClose):
Moves that are less than one tick
are considered immediately complete.
"""
setpoint = FCpt(EpicsSignal, "{self.prefix}{self.sys}TDES", kind="normal")
delay_ticks = FCpt(EpicsSignal, '{self.prefix}{self.trg}TDESTICKS', kind="omitted")
delay_taps = FCpt(EpicsSignal, '{self.prefix}{self.trg}TDESTAPS', kind="omitted")
setpoint = FCpt(EpicsSignal, "{self.prefix}{self.sys}TDES", kind="normal", doc="Trigger delay setpoint in nsec")
delay_ticks = FCpt(EpicsSignal, '{self.prefix}TDESTICKS', kind="omitted", doc="Trigger delay in clock ticks")
delay_taps = FCpt(EpicsSignal, '{self.prefix}TDESTAPS', kind="omitted", doc="Trigger delay in delay taps")
readback = Cpt(
MultiDerivedSignalRO,
attrs=['delay_ticks', 'delay_taps'],
Expand All @@ -44,78 +52,87 @@ class TprMotor(PVPositionerIsClose):
atol = TPR_TICK_NS
rtol = 0

def __init__(self, prefix, *, sys, trg, name, **kwargs):
def __init__(self, prefix, *, sys, name, **kwargs):
self.prefix = prefix
self.sys = sys
self.trg = trg
super().__init__(prefix, name=name, **kwargs)


class TprTrigger(BaseInterface, Device):
"""Class for an individual TprTrigger."""
ratemode = FCpt(EpicsSignal, '{self.prefix}{self.ch}RATECODE', kind="config")
group = FCpt(EpicsSignal, '{self.prefix}{self.ch}GROUP', kind="omitted")
seqcode = FCpt(EpicsSignal, '{self.prefix}{self.ch}SEQCODE', kind="omitted")
fixedrate = FCpt(EpicsSignal, '{self.prefix}{self.ch}FIXEDRATE', kind="omitted")
count = FCpt(EpicsSignal, '{self.prefix}{self.ch}COUNT', kind="omitted")
destmask = FCpt(EpicsSignal, '{self.prefix}{self.ch}DESTMASK', kind="omitted")
destmode = FCpt(EpicsSignal, '{self.prefix}{self.ch}DESTMODE', kind="omitted")
src = FCpt(EpicsSignal, '{self.prefix}{self.trg}SOURCE', kind="omitted")
eventcode = FCpt(EpicsSignal, '{self.prefix}{self.ch}EVCODE', kind="config")
eventrate = FCpt(EpicsSignalRO, '{self.prefix}{self.ch}RATE', kind="normal")
label = FCpt(EpicsSignal, '{self.prefix}{self.ch}{self.sys}TCTL.DESC', kind="omitted")
label2 = FCpt(EpicsSignal, '{self.prefix}{self.ch}{self.sys2}TCTL.DESC', kind="omitted")
delay_ticks = FCpt(EpicsSignal, '{self.prefix}{self.trg}TDESTICKS', kind="omitted")
delay_taps = FCpt(EpicsSignal, '{self.prefix}{self.trg}TDESTAPS', kind="omitted")
delay_setpoint = FCpt(EpicsSignal, '{self.prefix}{self.sys}TDES', kind="omitted")
delay_setpoint2 = FCpt(EpicsSignal, '{self.prefix}{self.sys2}TDES', kind="omitted")
ratemode = FCpt(EpicsSignal, '{self.prefix}{self.ch}RATEMODE', kind="config", doc="Channel rate mode selector")
group = FCpt(EpicsSignal, '{self.prefix}{self.ch}GROUP', kind="omitted", doc="Channel group Bit")
seqcode = FCpt(EpicsSignal, '{self.prefix}{self.ch}SEQCODE', kind="omitted", doc="Channel sequence code")
fixedrate = FCpt(EpicsSignal, '{self.prefix}{self.ch}FIXEDRATE', kind="omitted", doc="Channel Fxed rate selector")
count = FCpt(EpicsSignal, '{self.prefix}{self.ch}CNT', kind="omitted", doc="Channel counter")
destmask = FCpt(EpicsSignal, '{self.prefix}{self.ch}DESTMASK', kind="omitted", doc="Channel destination mask")
destmode = FCpt(EpicsSignal, '{self.prefix}{self.ch}DESTMODE', kind="omitted", doc="Channel destination mode selector")
src = FCpt(EpicsSignal, '{self.prefix}{self.trg}SOURCE', kind="omitted", doc="Trigger source")
eventcode = FCpt(EpicsSignal, '{self.prefix}{self.ch}EVCODE', kind="config", doc="Channel LCLS1 event code")
eventrate = FCpt(EpicsSignalRO, '{self.prefix}{self.ch}RATE', kind="normal", doc="Channel event rates")
label = FCpt(EpicsSignal, '{self.prefix}{self.ch}{self.sys}TCTL.DESC', kind="omitted", doc="Enable/disable")
delay_ticks = FCpt(EpicsSignal, '{self.prefix}{self.trg}TDESTICKS', kind="omitted", doc="Trigger delay in clock ticks")
delay_taps = FCpt(EpicsSignal, '{self.prefix}{self.trg}TDESTAPS', kind="omitted", doc="Trigger delay in delay taps")
delay_setpoint = FCpt(EpicsSignal, '{self.prefix}{self.trg}{self.sys}TDES', kind="omitted", doc="Trigger delay setpoint in nsec")
ns_delay = Cpt(
MultiDerivedSignal,
attrs=['delay_ticks', 'delay_taps', 'delay_setpoint'],
calculate_on_get=_get_delay,
calculate_on_put=_put_last,
doc="Get/set trigger delay in ns",
)
ns_delay2 = Cpt(
MultiDerivedSignal,
attrs=['delay_ticks', 'delay_taps', 'delay_setpoint2'],
calculate_on_get=_get_delay,
calculate_on_put=_put_last,
)
ns_delay_scan = FCpt(TprMotor, '{self.prefix}', sys='{self.sys}', trg='{self.trg}', kind="omitted")
ns_delay_scan2 = FCpt(TprMotor, '{self.prefix}', sys='{self.sys2}', trg='{self.trg}', kind="omitted")
polarity = FCpt(EpicsSignal, '{self.prefix}{self.trg}TPOL', kind="config")
width_setpoint = FCpt(EpicsSignal, '{self.prefix}{self.sys}TWID', kind="omitted")
width_setpoint2 = FCpt(EpicsSignal, '{self.prefix}{self.sys2}TWID', kind="omitted")
width_ticks = FCpt(EpicsSignalRO, '{self.prefix}{self.trg}TWIDTICKS', kind="omitted")
ns_delay_scan = FCpt(TprMotor, '{self.prefix}{self.trg}', sys='{self.sys}', kind="omitted", doc="Motor-like tpr interface")
polarity = FCpt(EpicsSignal, '{self.prefix}{self.trg}TPOL', kind="config", doc="Trigger description")
width_setpoint = FCpt(EpicsSignal, '{self.prefix}{self.trg}{self.sys}TWID', kind="omitted", doc="Trigger width in ns")
width_ticks = FCpt(EpicsSignalRO, '{self.prefix}{self.trg}TWIDTICKS', kind="omitted", doc="Trigger width in clock ticks")
width = Cpt(
MultiDerivedSignal,
attrs=['width_ticks', 'width_setpoint'],
calculate_on_get=_get_width,
calculate_on_put=_put_last,
doc="Get/set trigger width in nsec",
)
width2 = Cpt(
MultiDerivedSignal,
attrs=['width_ticks', 'width_setpoint2'],
calculate_on_get=_get_width,
calculate_on_put=_put_last,
)
enable_cmd = FCpt(EpicsSignal, '{self.prefix}{self.ch}{self.sys}TCTL', kind="omitted")
enable_cmd2 = FCpt(EpicsSignal, '{self.prefix}{self.ch}{self.sys2}TCTL', kind="omitted")
enable_ch_cmd = FCpt(EpicsSignal, '{self.prefix}{self.ch}{self.sys}TCTL', kind="omitted", doc="Channel enable/disable")
set_metadata(enable_ch_cmd, dict(variety='command-proc', value=1))
enable_trg_cmd = FCpt(EpicsSignal, '{self.prefix}{self.trg}{self.sys}TCTL', kind="omitted", doc="Trigger enable/disable")
set_metadata(enable_trg_cmd, dict(variety='command-proc', value=1))

tab_whitelist = ['enable', 'disable']
tab_component_names = True

def __init__(self, prefix, *, channel, name, **kwargs):
self.sys = 'SYS0_'
self.sys2 = 'SYS2_'
def __init__(self, prefix, *, channel, timing_mode, name, **kwargs):
if timing_mode == TimingMode.LCLS1:
self.sys = 'SYS0_'
elif timing_mode == TimingMode.LCLS2:
self.sys = 'SYS2_'
else:
raise TypeError("timing_mode must be TimingMode.LCLS1 or TimingMode.LCLS2")
self.ch = f':CH{channel:02}_'
self.trg = f':TRG{channel:02}_'
super().__init__(prefix, name=name, **kwargs)

def enable(self):
"""Enable the trigger."""
self.enable_cmd.put(1)
"""Enable the channel and trigger."""
self.enable_ch()
self.enable_trg()

def disable(self):
"""Disable the channel and trigger."""
self.disable_ch()
self.disable_trg()

def enable_ch(self):
"""Enable the channel."""
self.enable_ch_cmd.put(1)

def disable_ch(self):
"""Disable the channel."""
self.enable_ch_cmd.put(0)

def enable_trg(self):
"""Enable the trigger."""
self.enable_trg_cmd.put(1)

def disable_trg(self):
"""Disable the trigger."""
self.enable_cmd.put(0)
self.enable_trg_cmd.put(0)

0 comments on commit 03a1ba6

Please sign in to comment.