Skip to content

Commit

Permalink
Merge pull request #19 from ZLLentz/begin-cfg
Browse files Browse the repository at this point in the history
ENH: Record in Begin
  • Loading branch information
ZLLentz authored May 4, 2018
2 parents c15bb13 + 54a6326 commit 2af7668
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 14 deletions.
52 changes: 40 additions & 12 deletions pcdsdaq/daq.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ def wait(self, timeout=None):
status = self._get_end_status()
status_wait(status, timeout=timeout)

def begin(self, events=None, duration=None, use_l3t=None, controls=None,
wait=False):
def begin(self, events=None, duration=None, record=None, use_l3t=None,
controls=None, wait=False):
"""
Start the daq and block until the daq has begun acquiring data.
Optionally block until the daq has finished aquiring data.
Expand All @@ -212,6 +212,10 @@ def begin(self, events=None, duration=None, use_l3t=None, controls=None,
duration: ``int``, optional
Time to run the daq in seconds, if ``events`` was not provided.
record: ``bool``, optional
If ``True``, we'll configure the daq to record data before this
run.
use_l3t: ``bool``, optional
If ``True``, we'll run with the level 3 trigger. This means that
if we specified a number of events, we will wait for that many
Expand All @@ -224,13 +228,21 @@ def begin(self, events=None, duration=None, use_l3t=None, controls=None,
values each time begin is called. To provide a list, all devices
must have a ``name`` attribute.
wait: bool, optional
wait: ``bool``, optional
If ``True``, wait for the daq to finish aquiring data.
"""
logger.debug('Daq.begin(events=%s, duration=%s, wait=%s)',
events, duration, wait)
logger.debug(('Daq.begin(events=%s, duration=%s, record=%s, '
'use_l3t=%s, controls=%s, wait=%s)'),
events, duration, record, use_l3t, controls, wait)
if record is not None and record != self.record:
old_record = self.record
self.record = record
begin_status = self.kickoff(events=events, duration=duration,
use_l3t=use_l3t, controls=controls)
try:
self.record = old_record
except NameError:
pass
status_wait(begin_status, timeout=BEGIN_TIMEOUT)
if wait:
self.wait()
Expand Down Expand Up @@ -260,6 +272,10 @@ def kickoff(self, events=None, duration=None, use_l3t=None, controls=None):
Begin acquisition. This method is non-blocking.
See `begin` for a description of the parameters.
This method does not supply arguments for configuration parameters, it
supplies arguments directly to ``pydaq.Control.begin``. It will
configure before running if there are queued configuration changes.
This is part of the ``bluesky`` ``Flyer`` interface.
Returns
Expand All @@ -271,7 +287,14 @@ def kickoff(self, events=None, duration=None, use_l3t=None, controls=None):

self._check_duration(duration)
if self._desired_config or not self.configured:
self.configure()
try:
self.configure()
except StateTransitionError:
err = ('Illegal reconfigure with {} during an open run. End '
'the current run with daq.end_run() before running '
'with a new configuration'.format(self._desired_config))
logger.debug(err, exc_info=True)
raise StateTransitionError(err)

def start_thread(control, status, events, duration, use_l3t, controls):
tmo = BEGIN_TIMEOUT
Expand Down Expand Up @@ -394,16 +417,16 @@ def configure(self, events=None, duration=None, record=None,
If not provided, and ``events`` was also not provided, an empty
call like ``begin()`` will run indefinitely.
use_l3t: ``bool``, optional
If ``True``, an ``events`` argument to begin will be reinterpreted
to only count events that pass the level 3 trigger. Defaults to
``False``.
record: ``bool``, optional
If ``True``, we'll record the data. Otherwise, we'll run without
recording. Defaults to ``False``, or the last set value for
``record``.
use_l3t: ``bool``, optional
If ``True``, an ``events`` argument to begin will be reinterpreted
to only count events that pass the level 3 trigger. Defaults to
``False``.
controls: ``dict{name: device}`` or ``list[device...]``, optional
If provided, values from these will make it into the DAQ data
stream as variables. We will check ``device.position`` and
Expand Down Expand Up @@ -431,7 +454,8 @@ def configure(self, events=None, duration=None, record=None,
events, duration, record, use_l3t, controls, mode)
state = self.state
if state not in ('Connected', 'Configured'):
raise RuntimeError('Cannot configure from state {}!'.format(state))
err = 'Cannot configure from state {}!'.format(state)
raise StateTransitionError(err)

self._check_duration(duration)

Expand Down Expand Up @@ -751,6 +775,10 @@ def __del__(self):
pass


class StateTransitionError(Exception):
pass


_daq_instance = None


Expand Down
70 changes: 68 additions & 2 deletions tests/test_daq.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from ophyd.status import wait as status_wait

from pcdsdaq import daq as daq_module
from pcdsdaq.daq import BEGIN_TIMEOUT
from pcdsdaq.daq import BEGIN_TIMEOUT, StateTransitionError

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -134,6 +134,68 @@ def test_basic_run(daq, sig):
assert dt < BEGIN_TIMEOUT + 1


@pytest.mark.timeout(10)
def test_begin_record_arg(daq):
"""
We expect that the record argument in begin overrides the daq's record
configuration for the run.
"""
logger.debug('test_begin_record_arg')
# Sanity checks
daq.configure(record=False)
assert not daq.record
daq.begin(events=1, wait=True)
daq.end_run()
assert not daq.config['record']
assert not daq._desired_config
# Did we record?
daq.begin(events=1, wait=True, record=True)
daq.end_run()
assert daq.config['record']
assert not daq._desired_config['record']
# 2 in a row: did we record?
daq.begin(events=1, wait=True, record=True)
daq.end_run()
assert daq.config['record']
assert not daq._desired_config['record']
# Remove record arg: did we not record?
daq.begin(events=1, wait=True)
daq.end_run()
assert not daq.config['record']
assert not daq._desired_config
# Configure for record=True, then also pass to begin
daq.record = True
daq.begin(events=1, wait=True, record=True)
daq.end_run()
assert daq.config['record']
assert not daq._desired_config

# Same tests, but swap all the booleans
daq.configure(record=True)
assert daq.record
daq.begin(events=1, wait=True)
daq.end_run()
assert daq.config['record']
assert not daq._desired_config
daq.begin(events=1, wait=True, record=False)
daq.end_run()
assert not daq.config['record']
assert daq._desired_config['record']
daq.begin(events=1, wait=True, record=False)
daq.end_run()
assert not daq.config['record']
assert daq._desired_config['record']
daq.begin(events=1, wait=True)
daq.end_run()
assert daq.config['record']
assert not daq._desired_config
daq.record = False
daq.begin(events=1, wait=True, record=False)
daq.end_run()
assert not daq.config['record']
assert not daq._desired_config


@pytest.mark.timeout(3)
def test_stop_run(daq):
"""
Expand Down Expand Up @@ -243,8 +305,12 @@ def test_bad_stuff(daq, RE):

# Configure during a run
daq.begin(duration=1)
with pytest.raises(RuntimeError):
with pytest.raises(StateTransitionError):
daq.configure()

with pytest.raises(StateTransitionError):
daq.begin(record=True)

daq.end_run() # Prevent thread stalling


Expand Down

0 comments on commit 2af7668

Please sign in to comment.