Skip to content

Commit

Permalink
Merge pull request #218 from dls-controls/fix-runnable-child
Browse files Browse the repository at this point in the history
Fix runnable child
  • Loading branch information
coretl committed Nov 7, 2017
2 parents 88c992f + 107671b commit 38e85f7
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 29 deletions.
23 changes: 18 additions & 5 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
Change Log
==========
All notable changes to this project will be documented in this file.
This project adheres to `Semantic Versioning <http://semver.org/>`_.
This project adheres to `Semantic Versioning <http://semver.org/>`_ after 2-1.

`Unreleased`_ - unreleased
--------------------------
Added:
`Unreleased`_
-------------

Changed:

- Nothing yet

Fixed:

- Made RunnableChildPart handle a resume on a child that was Armed not Paused


`2-1`_ - 2017-08-30
-------------------
Changed:

- Major refactor, many breaking changes

`2-0a6`_ - 2016-10-03
---------------------
Changed:
Expand Down Expand Up @@ -54,7 +66,8 @@ Added:

- Initial release with hello world and websocket comms

.. _Unreleased: https://github.com/dls-controls/pymalcolm/compare/2-0a6...HEAD
.. _Unreleased: https://github.com/dls-controls/pymalcolm/compare/2-1...HEAD
.. _2-1: https://github.com/dls-controls/pymalcolm/compare/2-0a6...2-1
.. _2-0a6: https://github.com/dls-controls/pymalcolm/compare/2-0a5...2-0a6
.. _2-0a5: https://github.com/dls-controls/pymalcolm/compare/2-0a4...2-0a5
.. _2-0a4: https://github.com/dls-controls/pymalcolm/compare/2-0a3...2-0a4
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ install: dist docs

# Upload to pypi
publish:
$(PYTHON) setup.py register -r https://pypi.python.org/pypi sdist upload -r https://pypi.python.org/pypi
$(PYTHON) setup.py sdist upload -r pypi

testpublish:
$(PYTHON) setup.py register -r https://testpypi.python.org/pypi sdist upload -r https://testpypi.python.org/pypi
$(PYTHON) setup.py sdist upload -r pypitest

test:
PYTHONPATH=../scanpointgenerator $(PYTHON) setup.py test
Expand Down
4 changes: 3 additions & 1 deletion malcolm/modules/scanning/controllers/runnablecontroller.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from malcolm.core import method_takes, REQUIRED, method_also_takes, \
method_writeable_in, Hook, AbortedError, MethodModel, Queue, \
call_with_params, Context, ABORT_TIMEOUT, TimeoutError
call_with_params, Context, ABORT_TIMEOUT, TimeoutError, method_returns
from malcolm.modules.builtin.controllers import ManagerStates, \
ManagerController
from malcolm.modules.builtin.vmetas import NumberMeta, StringArrayMeta
Expand Down Expand Up @@ -54,6 +54,7 @@ def create_block_transitions(self):
"List of axes in inner dimension of generator that should be moved"),
[]
)
validate_args = configure_args[:-1] + (REQUIRED,)


@method_also_takes(
Expand Down Expand Up @@ -300,6 +301,7 @@ def set_axes_to_move(self, value):
self.axes_to_move.set_value(value)

@method_takes(*configure_args)
@method_returns(*validate_args)
def validate(self, params, returns):
"""Validate configuration parameters and return validated parameters.
Expand Down
24 changes: 8 additions & 16 deletions malcolm/modules/scanning/parts/runnablechildpart.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,18 @@ def configure(self, context, completed_steps, steps_to_do, part_info,
child.configure(**params)

@RunnableController.Run
@RunnableController.Resume
def run(self, context, update_completed_steps):
context.unsubscribe_all()
child = context.block_view(self.params.mri)
child.completedSteps.subscribe_value(update_completed_steps, self)
match_future = self._wait_for_postrun(child)
self.run_future = child.run_async()
bad_states = [ss.DISABLING, ss.ABORTING, ss.FAULT]
match_future = child.when_value_matches_async(
"state", ss.POSTRUN, bad_states)
if child.state.value == ss.ARMED:
self.run_future = child.run_async()
else:
child.resume()
try:
context.wait_all_futures(match_future)
except BadValueError:
Expand All @@ -131,20 +137,6 @@ def seek(self, context, completed_steps, steps_to_do, part_info):
child = context.block_view(self.params.mri)
child.pause(completedSteps=completed_steps)

@RunnableController.Resume
def resume(self, context, update_completed_steps):
child = context.block_view(self.params.mri)
child.completedSteps.subscribe_value(update_completed_steps, self)
match_future = self._wait_for_postrun(child)
child.resume()
context.wait_all_futures(match_future)

def _wait_for_postrun(self, child):
bad_states = [ss.DISABLING, ss.ABORTING, ss.FAULT]
match_future = child.when_value_matches_async(
"state", ss.POSTRUN, bad_states)
return match_future

@RunnableController.Abort
def abort(self, context):
child = context.block_view(self.params.mri)
Expand Down
2 changes: 1 addition & 1 deletion malcolm/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '2.1a1'
__version__ = '2.1'
90 changes: 86 additions & 4 deletions tests/test_modules/test_scanning/test_runnablechildpart.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,93 @@
import unittest

from scanpointgenerator import LineGenerator, CompoundGenerator

from malcolm.core import call_with_params, Part, Process, Context
from malcolm.modules.scanning.parts import RunnableChildPart
from malcolm.modules.scanning.controllers import RunnableController


class WaitingPart(Part):
def __init__(self, name, wait):
super(WaitingPart, self).__init__(name)
self.wait = wait

@RunnableController.Run
def run(self, context, update_completed_steps):
context.sleep(self.wait)


class TestRunnableChildPart(unittest.TestCase):

def setUp(self):
# code coverage is currently provided via TestRunnableController
pass
self.p = Process('process1')
self.context = Context(self.p)

# Make a fast child
c1 = call_with_params(RunnableController, self.p,
[WaitingPart("p", 0.01)],
mri="fast", configDir="/tmp")
self.p.add_controller("fast", c1)

# And a slow one
c2 = call_with_params(RunnableController, self.p,
[WaitingPart("p", 1.0)],
mri="slow", configDir="/tmp")
self.p.add_controller("slow", c2)

# And a top level one
p1 = call_with_params(RunnableChildPart, name="FAST", mri="fast")
p2 = call_with_params(RunnableChildPart, name="SLOW", mri="slow")
c3 = call_with_params(RunnableController, self.p, [p1, p2],
mri="top", configDir="/tmp")
self.p.add_controller("top", c3)
self.b = self.context.block_view("top")
self.bf = self.context.block_view("fast")
self.bs = self.context.block_view("slow")

# start the process off
self.p.start()

def tearDown(self):
self.p.stop(timeout=1)

def make_generator(self):
line1 = LineGenerator('y', 'mm', 0, 2, 3)
line2 = LineGenerator('x', 'mm', 0, 2, 2)
compound = CompoundGenerator([line1, line2], [], [])
return compound

def test_not_paused_when_resume(self):
# Set it up to do 6 steps
self.b.configure(generator=self.make_generator())
assert self.b.completedSteps.value == 0
assert self.b.totalSteps.value == 6
assert self.b.configuredSteps.value == 1
# Do one step
self.b.run()
assert self.b.completedSteps.value == 1
assert self.b.totalSteps.value == 6
assert self.b.configuredSteps.value == 2
# Now do a second step but pause before the second one is done
f = self.b.run_async()
self.context.sleep(0.2)
assert self.b.state.value == "Running"
assert self.bf.state.value == "Armed"
assert self.bs.state.value == "Running"
self.b.pause()
assert self.b.state.value == "Paused"
assert self.bf.state.value == "Armed"
assert self.bs.state.value == "Paused"
assert self.b.completedSteps.value == 1
assert self.b.totalSteps.value == 6
assert self.b.configuredSteps.value == 2
self.b.resume()
self.context.wait_all_futures(f)
assert self.b.completedSteps.value == 2
assert self.b.totalSteps.value == 6
assert self.b.configuredSteps.value == 3





def test_init(self):
pass

0 comments on commit 38e85f7

Please sign in to comment.