예제 #1
0
 def test_run_hook_table(self):
     hook = MagicMock()
     func = MagicMock()
     func.return_value = {"foo": ["bar", "bat", "baz"], "two": 2}
     func2 = MagicMock()
     func2.return_value = {"foo": ["bar2", "bat2"], "two": 3}
     task = MagicMock()
     task2 = MagicMock()
     part = MagicMock()
     part2 = MagicMock()
     part_tasks = {part: task, part2: task2}
     hook_queue = self.c.process.create_queue.return_value
     hook_queue.get.side_effect = [(func, func.return_value),
                                   (func2, func2.return_value)]
     hook.find_func_tasks.return_value = {func: task, func2: task2}
     return_table = hook.make_return_table.return_value
     return_table.endpoints = ["name", "foo", "two"]
     return_table.meta.elements = dict(
         name=StringArrayMeta(),
         foo=StringArrayMeta(tags=["hook:return_array"]),
         two=NumberArrayMeta("int32"))
     self.c.hook_names = {hook: "test_hook"}
     self.c.parts = {"test_part": part, "part2": part2}
     result = self.c.run_hook(hook, part_tasks)
     self.assertEquals(hook.make_return_table.return_value, result)
     result.append.assert_has_calls([
         call(["test_part", "bar", 2]),
         call(["test_part", "bat", 2]),
         call(["test_part", "baz", 2]),
         call(["part2", "bar2", 3]),
         call(["part2", "bat2", 3]),
     ],
                                    any_order=True)
class TestStringArrayMeta(unittest.TestCase):

    def setUp(self):
        self.meta = StringArrayMeta("test description")

    def test_init(self):
        self.assertEqual("test description", self.meta.description)
        self.assertEqual(self.meta.label, "")
        self.assertEqual(self.meta.typeid, "malcolm:core/StringArrayMeta:1.0")

    def test_validate_none(self):
        self.assertEquals(self.meta.validate(None), [])

    def test_validate_array(self):
        array = ["test_string", 123, 123.456]
        self.assertEquals(
            ["test_string", "123", "123.456"],
            self.meta.validate(array))

    def test_not_iterable_raises(self):
        value = 12346
        self.assertRaises(ValueError, self.meta.validate, value)

    def test_null_element_raises(self):
        array = ["test", None]
        self.assertRaises(ValueError, self.meta.validate, array)
예제 #3
0
 def test_set_elements_from_serialized(self):
     tm = self.tm
     elements = OrderedDict()
     elements["col1"] = StringArrayMeta()
     elements["col2"] = StringArrayMeta()
     elements = TableElementMap(elements)
     serialized = elements.to_dict()
     tm.set_elements(serialized)
     self.assertEqual(serialized, tm.elements.to_dict())
예제 #4
0
 def test_set_elements(self):
     tm = self.tm
     elements = OrderedDict()
     elements["col1"] = StringArrayMeta()
     elements["col2"] = StringArrayMeta()
     elements = TableElementMap(elements)
     tm.set_elements(elements)
     self.assertEqual(elements, tm.elements)
     tm.process.report_changes.assert_called_once_with([["elements"],
                                                        elements.to_dict()])
예제 #5
0
 def setUp(self):
     self.sam = StringArrayMeta()
     self.serialized = OrderedDict()
     self.serialized["typeid"] = "malcolm:core/TableMeta:1.0"
     self.serialized["elements"] = TableElementMap(
         dict(c1=self.sam)).to_dict()
     self.serialized["description"] = "desc"
     self.serialized["tags"] = ()
     self.serialized["writeable"] = True
     self.serialized["label"] = "Name"
예제 #6
0
 def setUp(self):
     self.sam = StringArrayMeta()
     self.serialized = OrderedDict()
     self.serialized["typeid"] = "malcolm:core/MapMeta:1.0"
     self.serialized["elements"] = ElementMap(dict(c1=self.sam)).to_dict()
     self.serialized["description"] = "desc"
     self.serialized["tags"] = ()
     self.serialized["writeable"] = False
     self.serialized["label"] = ""
     self.serialized["required"] = ("c1",)
예제 #7
0
 def create_process_block(self):
     self.process_block = Block()
     # TODO: add a meta here
     children = OrderedDict()
     children["blocks"] = StringArrayMeta(
         description="Blocks hosted by this Process").make_attribute([])
     children["remoteBlocks"] = StringArrayMeta(
         description="Blocks reachable via ClientComms").make_attribute([])
     self.process_block.replace_endpoints(children)
     self.process_block.set_process_path(self, [self.name])
     self.add_block(self.process_block, self)
예제 #8
0
 def test_init_with_dict(self):
     meta = Mock()
     meta.elements = {
         "e1": NumberArrayMeta("int32"),
         "e2": StringArrayMeta(),
         "e3": StringArrayMeta()
     }
     d = {"e1": [0, 1], "e3": ["value"]}
     t = Table(meta, d)
     self.assertEquals([0, 1], list(t.e1))
     self.assertEquals((), t.e2)
     self.assertEquals(("value", ), t.e3)
     self.assertEquals("malcolm:core/Table:1.0", t.typeid)
예제 #9
0
 def test_to_dict(self):
     columns = OrderedDict()
     columns["foo"] = StringArrayMeta(label="Foo")
     columns["bar"] = StringArrayMeta()
     meta = TableMeta(description="desc",
                      tags=[],
                      writeable=True,
                      label="my label",
                      columns=columns)
     value = Table(meta)
     value.foo = ["foo1", "foo2"]
     value.bar = ["bar1", "bar2"]
     o = meta.make_attribute(value)
     self.assertEqual(o.to_dict(), self.serialized)
예제 #10
0
class TestSerialization(unittest.TestCase):

    def setUp(self):
        self.sam = StringArrayMeta()
        self.serialized = OrderedDict()
        self.serialized["typeid"] = "malcolm:core/MapMeta:1.0"
        self.serialized["elements"] = ElementMap(dict(c1=self.sam)).to_dict()
        self.serialized["description"] = "desc"
        self.serialized["tags"] = []
        self.serialized["writeable"] = False
        self.serialized["label"] = ""
        self.serialized["required"] = ["c1"]

    def test_to_dict(self):
        tm = MapMeta("desc")
        tm.set_elements(ElementMap(dict(c1=self.sam)))
        tm.set_required(["c1"])
        self.assertEqual(tm.to_dict(), self.serialized)

    def test_from_dict(self):
        tm = MapMeta.from_dict(self.serialized)
        self.assertEquals(tm.description, "desc")
        self.assertEquals(len(tm.elements), 1)
        self.assertEquals(tm.elements["c1"].to_dict(), self.sam.to_dict())
        self.assertEquals(tm.tags, [])
        self.assertEquals(tm.required, ["c1"])
예제 #11
0
 def setUp(self):
     meta = Mock()
     meta.elements = OrderedDict()
     meta.elements["e1"] = StringArrayMeta()
     meta.elements["e2"] = NumberArrayMeta("int32")
     meta.elements["e3"] = NumberArrayMeta("int32")
     self.meta = meta
예제 #12
0
class TestTableMetaSerialization(unittest.TestCase):

    def setUp(self):
        self.sam = StringArrayMeta()
        self.serialized = OrderedDict()
        self.serialized["typeid"] = "malcolm:core/TableMeta:1.0"
        self.serialized["elements"] = TableElementMap(
            dict(c1=self.sam)).to_dict()
        self.serialized["description"] = "desc"
        self.serialized["tags"] = []
        self.serialized["writeable"] = True
        self.serialized["label"] = "Name"

    def test_to_dict(self):
        tm = TableMeta("desc")
        tm.set_label("Name")
        tm.set_elements(TableElementMap(dict(c1=self.sam)))
        tm.set_writeable(True)
        self.assertEqual(tm.to_dict(), self.serialized)

    def test_from_dict(self):
        tm = TableMeta.from_dict(self.serialized)
        self.assertEquals(tm.description, "desc")
        self.assertEquals(len(tm.elements), 1)
        self.assertEquals(tm.elements["c1"].to_dict(), self.sam.to_dict())
        self.assertEquals(tm.tags, [])
        self.assertEquals(tm.writeable, True)
        self.assertEquals(tm.label, "Name")
예제 #13
0
 def make_return_table(self, part_tasks):
     # Filter part tasks so that we only run the ones hooked to us
     columns = OrderedDict(name=StringArrayMeta("Part name"))
     for part in part_tasks:
         hooked = [
             method_name
             for (method_name, hook, _) in get_hook_decorated(part)
             if hook is self
         ]
         for method_name, method_meta, func in get_method_decorated(part):
             if method_name in hooked:
                 # Add return metas to the table columns
                 for arg_name in method_meta.returns.elements:
                     md = method_meta.returns.elements[arg_name].to_dict()
                     if "ArrayMeta" in md["typeid"]:
                         md["tags"] = md["tags"] + ["hook:return_array"]
                     else:
                         md["typeid"] = md["typeid"].replace(
                             "Meta", "ArrayMeta")
                     meta = deserialize_object(md, VArrayMeta)
                     if arg_name in columns:
                         column_d = columns[arg_name].to_dict()
                         assert column_d == md, \
                             "%s != %s" % (column_d, md)
                     columns[arg_name] = meta
     meta = TableMeta("Part returns from hook", columns=columns)
     return_table = Table(meta)
     return return_table
예제 #14
0
class TestTableMetaSerialization(unittest.TestCase):
    def setUp(self):
        self.sam = StringArrayMeta()
        self.serialized = OrderedDict()
        self.serialized["typeid"] = "malcolm:core/TableMeta:1.0"
        self.serialized["elements"] = TableElementMap(
            dict(c1=self.sam)).to_dict()
        self.serialized["description"] = "desc"
        self.serialized["tags"] = ()
        self.serialized["writeable"] = True
        self.serialized["label"] = "Name"

    def test_to_dict(self):
        tm = TableMeta("desc")
        tm.set_label("Name")
        tm.set_elements(TableElementMap(dict(c1=self.sam)))
        tm.set_writeable(True)
        self.assertEqual(tm.to_dict(), self.serialized)

    def test_from_dict(self):
        tm = TableMeta.from_dict(self.serialized)
        self.assertEquals(tm.description, "desc")
        self.assertEquals(len(tm.elements), 1)
        self.assertEquals(tm.elements["c1"].to_dict(), self.sam.to_dict())
        self.assertEquals(tm.tags, ())
        self.assertEquals(tm.writeable, True)
        self.assertEquals(tm.label, "Name")
예제 #15
0
class TestSerialization(unittest.TestCase):

    def setUp(self):
        self.sam = StringArrayMeta()
        self.serialized = OrderedDict()
        self.serialized["typeid"] = "malcolm:core/MapMeta:1.0"
        self.serialized["elements"] = ElementMap(dict(c1=self.sam)).to_dict()
        self.serialized["description"] = "desc"
        self.serialized["tags"] = ()
        self.serialized["writeable"] = False
        self.serialized["label"] = ""
        self.serialized["required"] = ("c1",)

    def test_to_dict(self):
        tm = MapMeta("desc")
        tm.set_elements(ElementMap(dict(c1=self.sam)))
        tm.set_required(["c1"])
        self.assertEqual(tm.to_dict(), self.serialized)

    def test_from_dict(self):
        tm = MapMeta.from_dict(self.serialized)
        self.assertEquals(tm.description, "desc")
        self.assertEquals(len(tm.elements), 1)
        self.assertEquals(tm.elements["c1"].to_dict(), self.sam.to_dict())
        self.assertEquals(tm.tags, ())
        self.assertEquals(tm.required, ("c1",))
예제 #16
0
 def create_attributes(self):
     for data in super(RunnableController, self).create_attributes():
         yield data
     self.completed_steps = NumberMeta(
         "int32", "Readback of number of scan steps").make_attribute(0)
     self.completed_steps.meta.set_writeable_in(sm.PAUSED, sm.READY)
     yield "completedSteps", self.completed_steps, self.set_completed_steps
     self.configured_steps = NumberMeta(
         "int32", "Number of steps currently configured").make_attribute(0)
     yield "configuredSteps", self.configured_steps, None
     self.total_steps = NumberMeta(
         "int32", "Readback of number of scan steps").make_attribute(0)
     yield "totalSteps", self.total_steps, None
     self.axes_to_move = StringArrayMeta(
         "Default axis names to scan for configure()").make_attribute(
             self.params.axesToMove)
     self.axes_to_move.meta.set_writeable_in(sm.EDITABLE)
     yield "axesToMove", self.axes_to_move, self.set_axes_to_move
예제 #17
0
 def test_init(self):
     meta = Mock()
     s = StringArrayMeta()
     meta.elements = {"e1": s, "e2": s, "e3": s}
     t = Table(meta)
     self.assertEquals((), t.e1)
     self.assertEquals((), t.e2)
     self.assertEquals((), t.e3)
     self.assertEquals("malcolm:core/Table:1.0", t.typeid)
예제 #18
0
 def setUp(self):
     self.sam = StringArrayMeta()
     self.serialized = OrderedDict()
     self.serialized["typeid"] = "malcolm:core/MapMeta:1.0"
     self.serialized["elements"] = ElementMap(dict(c1=self.sam)).to_dict()
     self.serialized["description"] = "desc"
     self.serialized["tags"] = []
     self.serialized["writeable"] = False
     self.serialized["label"] = ""
     self.serialized["required"] = ["c1"]
예제 #19
0
 def setUp(self):
     self.sam = StringArrayMeta()
     self.serialized = OrderedDict()
     self.serialized["typeid"] = "malcolm:core/TableMeta:1.0"
     self.serialized["elements"] = TableElementMap(
         dict(c1=self.sam)).to_dict()
     self.serialized["description"] = "desc"
     self.serialized["tags"] = []
     self.serialized["writeable"] = True
     self.serialized["label"] = "Name"
예제 #20
0
class ScanTickerPart(ChildPart):
    # Generator instance
    generator = None
    # Where to start
    completed_steps = None
    # How many steps to do
    steps_to_do = None
    # When to blow up
    exception_step = None

    @RunnableController.Configure
    @RunnableController.PostRunReady
    @RunnableController.Seek
    @method_takes(
        "generator", PointGeneratorMeta("Generator instance"), REQUIRED,
        "axesToMove",
        StringArrayMeta(
            "List of axes in inner dimension of generator that should be moved"
        ), REQUIRED, "exceptionStep",
        NumberMeta("int32",
                   "If >0, raise an exception at the end of this step"), 0)
    def configure(self, task, completed_steps, steps_to_do, part_info, params):
        # If we are being asked to move
        if self.name in params.axesToMove:
            # Just store the generator and place we need to start
            self.generator = params.generator
            self.completed_steps = completed_steps
            self.steps_to_do = steps_to_do
            self.exception_step = params.exceptionStep
        else:
            # Flag nothing to do
            self.generator = None

    @RunnableController.Run
    @RunnableController.Resume
    def run(self, task, update_completed_steps):
        # Start time so everything is relative
        point_time = time.time()
        if self.generator:
            for i in range(self.completed_steps,
                           self.completed_steps + self.steps_to_do):
                # Get the point we are meant to be scanning
                point = self.generator.get_point(i)
                # Update the child counter to be the demand position
                position = point.positions[self.name]
                task.put(self.child["counter"], position)
                # Wait until the next point is due
                point_time += point.duration
                wait_time = point_time - time.time()
                task.sleep(wait_time)
                # Update the point as being complete
                update_completed_steps(i + 1, self)
                # If this is the exception step then blow up
                assert i +1 != self.exception_step, \
                    "Raising exception at step %s" % self.exception_step
예제 #21
0
 def setUp(self):
     elements = OrderedDict()
     elements["typeid"] = "malcolm:core/TableElementMap:1.0"
     elements["foo"] = StringArrayMeta(label="Foo").to_dict()
     elements["bar"] = StringArrayMeta().to_dict()
     meta = OrderedDict()
     meta["typeid"] = "malcolm:core/TableMeta:1.0"
     meta["elements"] = elements
     meta["description"] = "desc"
     meta["tags"] = []
     meta["writeable"] = True
     meta["label"] = "my label"
     value = OrderedDict()
     value["typeid"] = "malcolm:core/Table:1.0"
     value["foo"] = ["foo1", "foo2"]
     value["bar"] = ["bar1", "bar2"]
     self.serialized = OrderedDict()
     self.serialized["typeid"] = "epics:nt/NTTable:1.0"
     self.serialized["labels"] = ["Foo", "bar"]
     self.serialized["meta"] = meta
     self.serialized["value"] = value
예제 #22
0
 def create_attributes(self):
     for data in super(ManagerController, self).create_attributes():
         yield data
     # Make a table for the layout info we need
     columns = OrderedDict()
     columns["name"] = StringArrayMeta("Name of layout part")
     columns["mri"] = StringArrayMeta("Malcolm full name of child block")
     columns["x"] = NumberArrayMeta("float64", "X Coordinate of child block")
     columns["y"] = NumberArrayMeta("float64", "Y Coordinate of child block")
     columns["visible"] = BooleanArrayMeta("Whether child block is visible")
     layout_table_meta = TableMeta("Layout of child blocks", columns=columns)
     layout_table_meta.set_writeable_in(sm.EDITABLE)
     self.layout = layout_table_meta.make_attribute()
     yield "layout", self.layout, self.set_layout
     self.layout_name = ChoiceMeta(
         "Saved layout name to load", []).make_attribute()
     self.layout_name.meta.set_writeable_in(
         self.stateMachine.AFTER_RESETTING)
     yield "layoutName", self.layout_name, self.load_layout
     assert os.path.isdir(self.params.configDir), \
         "%s is not a directory" % self.params.configDir
예제 #23
0
class TestStringArrayMeta(unittest.TestCase):
    def setUp(self):
        self.meta = StringArrayMeta("test description")

    def test_init(self):
        self.assertEqual("test description", self.meta.description)
        self.assertEqual(self.meta.label, "")
        self.assertEqual(self.meta.typeid, "malcolm:core/StringArrayMeta:1.0")

    def test_validate_none(self):
        self.assertIsNone(self.meta.validate(None))

    def test_validate_array(self):
        array = ["test_string", 123, 123.456]
        self.assertEquals(["test_string", "123", "123.456"],
                          self.meta.validate(array))

    def test_not_iterable_raises(self):
        value = 12346
        self.assertRaises(ValueError, self.meta.validate, value)

    def test_null_element_raises(self):
        array = ["test", None]
        self.assertRaises(ValueError, self.meta.validate, array)
예제 #24
0
 def create_attributes(self):
     for data in super(RunnableController, self).create_attributes():
         yield data
     self.completed_steps = NumberMeta(
         "int32", "Readback of number of scan steps").make_attribute(0)
     self.completed_steps.meta.set_writeable_in(sm.PAUSED, sm.READY)
     yield "completedSteps", self.completed_steps, self.set_completed_steps
     self.configured_steps = NumberMeta(
         "int32", "Number of steps currently configured").make_attribute(0)
     yield "configuredSteps", self.configured_steps, None
     self.total_steps = NumberMeta(
         "int32", "Readback of number of scan steps"
     ).make_attribute(0)
     yield "totalSteps", self.total_steps, None
     self.axes_to_move = StringArrayMeta(
         "Default axis names to scan for configure()"
     ).make_attribute(self.params.axesToMove)
     self.axes_to_move.meta.set_writeable_in(sm.EDITABLE)
     yield "axesToMove", self.axes_to_move, self.set_axes_to_move
예제 #25
0
 def setUp(self):
     self.meta = StringArrayMeta("test description")
from collections import OrderedDict

from malcolm.controllers.defaultcontroller import DefaultController
from malcolm.core import RunnableDeviceStateMachine, REQUIRED, method_returns, \
    method_only_in, method_takes, ElementMap, Attribute, Task, Hook, Table
from malcolm.core.vmetas import PointGeneratorMeta, StringArrayMeta, \
    NumberMeta, NumberArrayMeta, BooleanArrayMeta, TableMeta

sm = RunnableDeviceStateMachine

configure_args = [
    "generator",
    PointGeneratorMeta("Generator instance"), REQUIRED, "axes_to_move",
    StringArrayMeta("Axes that should be moved"), REQUIRED, "exposure",
    NumberMeta("float64", "How long to remain at each point"), REQUIRED
]

# Make a table for the layout info we need
columns = OrderedDict()
columns["name"] = StringArrayMeta("Name of layout part")
columns["mri"] = StringArrayMeta("Malcolm full name of child block")
columns["x"] = NumberArrayMeta("float64", "X Co-ordinate of child block")
columns["y"] = NumberArrayMeta("float64", "X Co-ordinate of child block")
columns["visible"] = BooleanArrayMeta("Whether child block is visible")
layout_table_meta = TableMeta("Layout of child blocks", columns=columns)

# Make a table for the port info we need
columns = OrderedDict()
columns["name"] = StringArrayMeta("Name of layout part")
columns["type"] = StringArrayMeta("Type of outport (e.g. bit or pos)")
columns["value"] = StringArrayMeta("Value of outport (e.g. PULSE1.OUT)")
예제 #27
0
class RunnableController(ManagerController):
    """RunnableDevice implementer that also exposes GUI for child parts"""
    # The stateMachine that this controller implements
    stateMachine = sm()

    Validate = Hook()
    """Called at validate() to check parameters are valid

    Args:
        task (Task): The task used to perform operations on child blocks
        part_info (dict): {part_name: [Info]} returned from ReportStatus
        params (Map): Any configuration parameters asked for by part validate()
            method_takes() decorator

    Returns:
        [`ParameterTweakInfo`] - any parameters tweaks that have occurred
            to make them compatible with this part. If any are returned,
            Validate will be re-run with the modified parameters.
    """

    ReportStatus = Hook()
    """Called before Validate, Configure, PostRunReady and Seek hooks to report
    the current configuration of all parts

    Args:
        task (Task): The task used to perform operations on child blocks

    Returns:
        [`Info`] - any configuration Info objects relevant to other parts
    """

    Configure = Hook()
    """Called at configure() to configure child block for a run

    Args:
        task (Task): The task used to perform operations on child blocks
        completed_steps (int): Number of steps already completed
        steps_to_do (int): Number of steps we should configure for
        part_info (dict): {part_name: [Info]} returned from ReportStatus
        params (Map): Any configuration parameters asked for by part configure()
            method_takes() decorator

    Returns:
        [`Info`] - any Info objects that need to be passed to other parts for
            storing in attributes
    """

    PostConfigure = Hook()
    """Called at the end of configure() to store configuration info calculated
     in the Configure hook

    Args:
        task (Task): The task used to perform operations on child blocks
        part_info (dict): {part_name: [Info]} returned from Configure hook
    """

    Run = Hook()
    """Called at run() to start the configured steps running

    Args:
        task (Task): The task used to perform operations on child blocks
        update_completed_steps (callable): If part can report progress, this
            part should call update_completed_steps(completed_steps, self) with
            the integer step value each time progress is updated
    """

    PostRunReady = Hook()
    """Called at the end of run() when there are more steps to be run

    Args:
        task (Task): The task used to perform operations on child blocks
        completed_steps (int): Number of steps already completed
        steps_to_do (int): Number of steps we should configure for
        part_info (dict): {part_name: [Info]} returned from ReportStatus
        params (Map): Any configuration parameters asked for by part configure()
            method_takes() decorator
    """

    PostRunIdle = Hook()
    """Called at the end of run() when there are no more steps to be run

    Args:
        task (Task): The task used to perform operations on child blocks
    """

    Pause = Hook()
    """Called at pause() to pause the current scan before Seek is called

    Args:
        task (Task): The task used to perform operations on child blocks
    """

    Seek = Hook()
    """Called at seek() or at the end of pause() to reconfigure for a different
    number of completed_steps

    Args:
        task (Task): The task used to perform operations on child blocks
        completed_steps (int): Number of steps already completed
        steps_to_do (int): Number of steps we should configure for
        part_info (dict): {part_name: [Info]} returned from ReportStatus
        params (Map): Any configuration parameters asked for by part configure()
            method_takes() decorator
    """

    Resume = Hook()
    """Called at resume() to continue a paused scan

    Args:
        task (Task): The task used to perform operations on child blocks
        update_completed_steps (callable): If part can report progress, this
            part should call update_completed_steps(completed_steps, self) with
            the integer step value each time progress is updated
    """

    Abort = Hook()
    """Called at abort() to stop the current scan

    Args:
        task (Task): The task used to perform operations on child blocks
    """

    # Attributes
    completed_steps = None
    configured_steps = None
    total_steps = None
    axes_to_move = None

    # Params passed to configure()
    configure_params = None

    # Stored for pause
    steps_per_run = 0

    # Progress reporting dict
    # {part: completed_steps for that part}
    progress_reporting = None

    @method_writeable_in(sm.IDLE)
    def edit(self):
        # Override edit to only work from Idle
        super(RunnableController, self).edit()

    @method_writeable_in(sm.FAULT, sm.DISABLED, sm.ABORTED, sm.READY)
    def reset(self):
        # Override reset to work from aborted and ready too
        super(RunnableController, self).reset()

    def create_attributes(self):
        for data in super(RunnableController, self).create_attributes():
            yield data
        self.completed_steps = NumberMeta(
            "int32", "Readback of number of scan steps").make_attribute(0)
        self.completed_steps.meta.set_writeable_in(sm.PAUSED, sm.READY)
        yield "completedSteps", self.completed_steps, self.set_completed_steps
        self.configured_steps = NumberMeta(
            "int32", "Number of steps currently configured").make_attribute(0)
        yield "configuredSteps", self.configured_steps, None
        self.total_steps = NumberMeta(
            "int32", "Readback of number of scan steps"
        ).make_attribute(0)
        yield "totalSteps", self.total_steps, None
        self.axes_to_move = StringArrayMeta(
            "Default axis names to scan for configure()"
        ).make_attribute(self.params.axesToMove)
        self.axes_to_move.meta.set_writeable_in(sm.EDITABLE)
        yield "axesToMove", self.axes_to_move, self.set_axes_to_move

    def do_reset(self):
        super(RunnableController, self).do_reset()
        self._update_configure_args()
        self.configured_steps.set_value(0)
        self.completed_steps.set_value(0)
        self.total_steps.set_value(0)

    def go_to_error_state(self, exception):
        if isinstance(exception, AbortedError):
            self.log_info("Got AbortedError in %s" % self.state.value)
        else:
            super(RunnableController, self).go_to_error_state(exception)

    def _update_configure_args(self):
        # Look for all parts that hook into Configure
        configure_funcs = self.Configure.find_hooked_functions(self.parts)
        method_metas = []
        for part, func_name in configure_funcs.items():
            method_metas.append(part.method_metas[func_name])

        # Update takes with the things we need
        default_configure = MethodMeta.from_dict(
            RunnableController.configure.MethodMeta.to_dict())
        default_configure.defaults["axesToMove"] = self.axes_to_move.value
        method_metas.append(default_configure)

        # Decorate validate and configure with the sum of its parts
        self.block["validate"].recreate_from_others(method_metas)
        self.block["validate"].set_returns(self.block["validate"].takes)
        self.block["configure"].recreate_from_others(method_metas)

    def set_axes_to_move(self, value):
        self.axes_to_move.set_value(value)
        self._update_configure_args()

    @method_takes(*configure_args)
    def validate(self, params, returns):
        iterations = 10
        # Make some tasks just for validate
        part_tasks = self.create_part_tasks()
        # Get any status from all parts
        status_part_info = self.run_hook(self.ReportStatus, part_tasks)
        while iterations > 0:
            # Try up to 10 times to get a valid set of parameters
            iterations -= 1
            # Validate the params with all the parts
            validate_part_info = self.run_hook(
                self.Validate, part_tasks, status_part_info, **params)
            tweaks = ParameterTweakInfo.filter_values(validate_part_info)
            if tweaks:
                for tweak in tweaks:
                    params[tweak.parameter] = tweak.value
                    self.log_debug(
                        "Tweaking %s to %s", tweak.parameter, tweak.value)
            else:
                # Consistent set, just return the params
                return params
        raise ValueError("Could not get a consistent set of parameters")

    @method_takes(*configure_args)
    @method_writeable_in(sm.IDLE)
    def configure(self, params):
        """Configure for a scan"""
        self.validate(params, params)
        self.try_stateful_function(
            sm.CONFIGURING, sm.READY, self.do_configure, params)

    def do_configure(self, params):
        # These are the part tasks that abort() and pause() will operate on
        self.part_tasks = self.create_part_tasks()
        # Store the params for use in seek()
        self.configure_params = params
        # Set the steps attributes that we will do across many run() calls
        self.total_steps.set_value(params.generator.num)
        self.completed_steps.set_value(0)
        self.configured_steps.set_value(0)
        # TODO: this should come from tne generator
        self.steps_per_run = self._get_steps_per_run(
            params.generator, params.axesToMove)
        # Get any status from all parts
        part_info = self.run_hook(self.ReportStatus, self.part_tasks)
        # Use the ProgressReporting classes for ourselves
        self.progress_reporting = {}
        # Run the configure command on all parts, passing them info from
        # ReportStatus. Parts should return any reporting info for PostConfigure
        completed_steps = 0
        steps_to_do = self.steps_per_run
        part_info = self.run_hook(
            self.Configure, self.part_tasks, completed_steps, steps_to_do,
            part_info, **self.configure_params)
        # Take configuration info and reflect it as attribute updates
        self.run_hook(self.PostConfigure, self.part_tasks, part_info)
        # Update the completed and configured steps
        self.configured_steps.set_value(steps_to_do)

    def _get_steps_per_run(self, generator, axes_to_move):
        steps = 1
        axes_set = set(axes_to_move)
        for g in reversed(generator.generators):
            # If the axes_set is empty then we are done
            if not axes_set:
                break
            # Consume the axes that this generator scans
            for axis in g.position_units:
                assert axis in axes_set, \
                    "Axis %s is not in %s" % (axis, axes_to_move)
                axes_set.remove(axis)
            # Now multiply by the dimensions to get the number of steps
            for dim in g.index_dims:
                steps *= dim
        return steps

    @method_writeable_in(sm.READY)
    def run(self):
        """Run an already configured scan"""
        if self.configured_steps.value < self.total_steps.value:
            next_state = sm.READY
        else:
            next_state = sm.IDLE
        self.try_stateful_function(sm.RUNNING, next_state, self._call_do_run)

    def _call_do_run(self):
        hook = self.Run
        while True:
            try:
                self.do_run(hook)
            except AbortedError:
                # Work out if it was an abort or pause
                state = self.state.value
                self.log_debug("Do run got AbortedError from %s", state)
                if state in (sm.SEEKING, sm.PAUSED):
                    # Wait to be restarted
                    task = Task("StateWaiter", self.process)
                    bad_states = [sm.DISABLING, sm.ABORTING, sm.FAULT]
                    try:
                        task.when_matches(self.state, sm.RUNNING, bad_states)
                    except BadValueError:
                        # raise AbortedError so we don't try to transition
                        raise AbortedError()
                    # Restart it
                    hook = self.Resume
                    self.status.set_value("Run resumed")
                else:
                    # just drop out
                    raise
            else:
                return

    def do_run(self, hook):
        self.run_hook(hook, self.part_tasks, self.update_completed_steps)
        self.transition(sm.POSTRUN, "Finishing run")
        completed_steps = self.configured_steps.value
        if completed_steps < self.total_steps.value:
            steps_to_do = self.steps_per_run
            part_info = self.run_hook(self.ReportStatus, self.part_tasks)
            self.completed_steps.set_value(completed_steps)
            self.run_hook(
                self.PostRunReady, self.part_tasks, completed_steps,
                steps_to_do, part_info, **self.configure_params)
            self.configured_steps.set_value(completed_steps + steps_to_do)
        else:
            self.run_hook(self.PostRunIdle, self.part_tasks)

    def update_completed_steps(self, completed_steps, part):
        # This is run in the child thread, so make sure it is thread safe
        self.progress_reporting[part] = completed_steps
        min_completed_steps = min(self.progress_reporting.values())
        if min_completed_steps > self.completed_steps.value:
            self.completed_steps.set_value(min_completed_steps)

    @method_writeable_in(
        sm.IDLE, sm.CONFIGURING, sm.READY, sm.RUNNING, sm.POSTRUN, sm.PAUSED,
        sm.SEEKING)
    def abort(self):
        self.try_stateful_function(
            sm.ABORTING, sm.ABORTED, self.do_abort, self.Abort)

    def do_abort(self, hook):
        for task in self.part_tasks.values():
            task.stop()
        self.run_hook(hook, self.create_part_tasks())
        for task in self.part_tasks.values():
            task.wait()

    def set_completed_steps(self, completed_steps):
        params = self.pause.MethodMeta.prepare_input_map(
            completedSteps=completed_steps)
        self.pause(params)

    @method_writeable_in(sm.READY, sm.PAUSED, sm.RUNNING)
    @method_takes("completedSteps", NumberMeta(
        "int32", "Step to mark as the last completed step, -1 for current"), -1)
    def pause(self, params):
        current_state = self.state.value
        if params.completedSteps < 0:
            completed_steps = self.completed_steps.value
        else:
            completed_steps = params.completedSteps
        if current_state == sm.RUNNING:
            next_state = sm.PAUSED
        else:
            next_state = current_state
        assert completed_steps < self.total_steps.value, \
            "Cannot seek to after the end of the scan"
        self.try_stateful_function(
            sm.SEEKING, next_state, self.do_pause, completed_steps)

    def do_pause(self, completed_steps):
        self.do_abort(self.Pause)
        in_run_steps = completed_steps % self.steps_per_run
        steps_to_do = self.steps_per_run - in_run_steps
        part_info = self.run_hook(self.ReportStatus, self.part_tasks)
        self.completed_steps.set_value(completed_steps)
        self.run_hook(
            self.Seek, self.part_tasks, completed_steps,
            steps_to_do, part_info, **self.configure_params)
        self.configured_steps.set_value(completed_steps + steps_to_do)

    @method_writeable_in(sm.PAUSED)
    def resume(self):
        self.transition(sm.RUNNING, "Resuming run")
예제 #28
0
class LayoutPart(Part):
    # Child block object
    child = None

    # {part_name: visible} saying whether part_name is visible
    part_visible = None

    # Layout options
    x = 0
    y = 0
    visible = False
    mri = None
    name = None

    def store_params(self, params):
        self.name = params.name
        self.child = self.process.get_block(params.child)
        self.mri = params.child
        self.part_visible = {}

    @ManagerController.UpdateLayout
    @method_takes("layout_table", layout_table_meta, REQUIRED, "outport_table",
                  outport_table_meta, REQUIRED)
    @method_returns("mri", StringMeta("Malcolm full name of child block"),
                    REQUIRED, "x",
                    NumberMeta("float64",
                               "X Co-ordinate of child block"), REQUIRED, "y",
                    NumberMeta("float64", "X Co-ordinate of child block"),
                    REQUIRED, "visible",
                    BooleanMeta("Whether child block is visible"), REQUIRED)
    def update_layout_table(self, task, params, returns):
        for i, name in enumerate(params.layout_table.name):
            _, _, x, y, visible = params.layout_table[i]
            if name == self.name:
                if self.visible and not visible:
                    self.sever_inports(task)
                self.x = x
                self.y = y
                self.visible = visible
            else:
                was_visible = self.part_visible.get(name, True)
                if was_visible and not visible:
                    self.sever_outports(task, name, params.outport_table)
                self.part_visible[name] = visible
        returns.mri = self.mri
        returns.x = self.x
        returns.y = self.y
        returns.visible = self.visible
        return returns

    def _get_flowgraph_ports(self, direction="out"):
        # {attr_name: port_tag}
        ports = OrderedDict()
        for attr_name in self.child.endpoints:
            attr = self.child[attr_name]
            if isinstance(attr, Attribute):
                for tag in attr.meta.tags:
                    if tag.startswith("flowgraph:%sport" % direction):
                        ports[attr] = tag
        return ports

    def sever_inports(self, task):
        inports = self._get_flowgraph_ports("in")
        futures = []
        for attr in inports:
            futures += task.put_async(attr, attr.meta.choices[0])
        task.wait_all(futures)

    def sever_outports(self, task, name, outport_table):
        # Find the outports of this part
        # {outport_value: typ} e.g. "PCOMP.OUT" -> "bit"
        outports = {}
        for i, n in enumerate(outport_table.name):
            if n == name:
                outports[outport_table.value[i]] = outport_table.type[i]
        inports = self._get_flowgraph_ports("in")
        futures = []
        for attr, port_tag in inports.items():
            typ = port_tag.split(":")[2]
            if outports.get(attr.value, None) == typ:
                futures += task.put_async(attr, attr.meta.choices[0])
        task.wait_all(futures)

    @ManagerController.ListOutports
    @method_returns("type",
                    StringArrayMeta("Type of outport (e.g. bit or pos)"),
                    REQUIRED, "value",
                    StringArrayMeta("Value of outport (e.g. PULSE1.OUT)"),
                    REQUIRED)
    def list_outports(self, _, returns):
        outports = self._get_flowgraph_ports("out")
        types = []
        values = []
        for port_tag in outports.values():
            _, _, typ, name = port_tag.split(":", 4)
            types.append(typ)
            values.append(name)
        returns.type = types
        returns.value = values
        return returns
예제 #29
0
from malcolm.parts.builtin.attributepart import AttributePart
from malcolm.core import method_also_takes, REQUIRED
from malcolm.core.vmetas import ChoiceMeta, StringMeta, StringArrayMeta


@method_also_takes(
    "choices",
    StringArrayMeta("Possible choices for this attribute"),
    REQUIRED,
    "initialValue",
    StringMeta("Initial value of attribute"),
    REQUIRED,
)
class ChoicePart(AttributePart):
    def get_initial_value(self):
        return self.params.initialValue

    def create_meta(self, description, tags):
        return ChoiceMeta(choices=self.params.choices,
                          description=description,
                          tags=tags)
예제 #30
0
 def setUp(self):
     self.tm = TableMeta("desc")
     self.tm.set_elements(TableElementMap(dict(c1=StringArrayMeta())))
예제 #31
0
NO_PROGRAM = 0       # Do nothing
TRIG_CAPTURE = 4     # Capture 1, Frame 0, Detector 0
TRIG_DEAD_FRAME = 2  # Capture 0, Frame 1, Detector 0
TRIG_LIVE_FRAME = 3  # Capture 0, Frame 1, Detector 1
TRIG_ZERO = 8        # Capture 0, Frame 0, Detector 0

# How many generator points to load each time
POINTS_PER_BUILD = 4000

# All possible PMAC CS axis assignment
cs_axis_names = list("ABCUVWXYZ")

# Args for configure and validate
configure_args = [
    "generator", PointGeneratorMeta("Generator instance"), REQUIRED,
    "axesToMove", StringArrayMeta(
        "List of axes in inner dimension of generator that should be moved"),
    []]


# Class for these motor variables
class MotorInfo(Info):
    def __init__(self, cs_axis, cs_port, acceleration, resolution, offset,
                 max_velocity, current_position, scannable, velocity_settle):
        self.cs_axis = cs_axis
        self.cs_port = cs_port
        self.acceleration = acceleration
        self.resolution = resolution
        self.offset = offset
        self.max_velocity = max_velocity
        self.current_position = current_position
        self.scannable = scannable
예제 #32
0
 def test_init_with_none(self):
     meta = Mock()
     meta.elements = {"e1": StringArrayMeta()}
     t = Table(meta, None)
     self.assertEquals((), t.e1)
     self.assertEquals("malcolm:core/Table:1.0", t.typeid)
예제 #33
0
 def setUp(self):
     self.meta = StringArrayMeta("test description")
예제 #34
0
from malcolm.compat import OrderedDict
from malcolm.core import Part, Table, Info
from malcolm.core.vmetas import StringArrayMeta, ChoiceArrayMeta, TableMeta, \
    NumberArrayMeta
from malcolm.controllers.runnablecontroller import RunnableController

# Make a table for the dataset info we produce
dataset_types = [
    "primary", "secondary", "monitor", "position_set", "position_value"]
columns = OrderedDict()
columns["name"] = StringArrayMeta("Dataset name")
columns["filename"] = StringArrayMeta(
    "Filename of HDF file relative to fileDir")
columns["type"] = ChoiceArrayMeta("Type of dataset", dataset_types)
columns["rank"] = NumberArrayMeta("int32", "Rank (number of dimensions)")
columns["path"] = StringArrayMeta("Dataset path within HDF file")
columns["uniqueid"] = StringArrayMeta("UniqueID array path within HDF file")
dataset_table_meta = TableMeta("Datsets produced in HDF file", columns=columns)


# Produced by plugins in part_info
class DatasetProducedInfo(Info):
    def __init__(self, name, filename, type, rank, path, uniqueid):
        self.name = name
        self.filename = filename
        assert type in dataset_types, \
            "Dataset type %s not in %s" % (type, dataset_types)
        self.type = type
        self.rank = rank
        self.path = path
        self.uniqueid = uniqueid
예제 #35
0
from malcolm.core.vmetas import PointGeneratorMeta, NumberMeta, StringArrayMeta


class ParameterTweakInfo(Info):
    """Tweaks"""
    def __init__(self, parameter, value):
        self.parameter = parameter
        self.value = value


sm = RunnableStateMachine

configure_args = [
    "generator",
    PointGeneratorMeta("Generator instance"), REQUIRED, "axesToMove",
    StringArrayMeta(
        "List of axes in inner dimension of generator that should be moved"),
    []
]


@method_also_takes("axesToMove",
                   StringArrayMeta("Default value for configure() axesToMove"),
                   [])
class RunnableController(ManagerController):
    """RunnableDevice implementer that also exposes GUI for child parts"""
    # The stateMachine that this controller implements
    stateMachine = sm()

    Validate = Hook()
    """Called at validate() to check parameters are valid
예제 #36
0
class RunnableController(ManagerController):
    """RunnableDevice implementer that also exposes GUI for child parts"""
    # The stateMachine that this controller implements
    stateMachine = sm()

    Validate = Hook()
    """Called at validate() to check parameters are valid

    Args:
        task (Task): The task used to perform operations on child blocks
        part_info (dict): {part_name: [Info]} returned from ReportStatus
        params (Map): Any configuration parameters asked for by part validate()
            method_takes() decorator

    Returns:
        [`ParameterTweakInfo`] - any parameters tweaks that have occurred
            to make them compatible with this part. If any are returned,
            Validate will be re-run with the modified parameters.
    """

    ReportStatus = Hook()
    """Called before Validate, Configure, PostRunReady and Seek hooks to report
    the current configuration of all parts

    Args:
        task (Task): The task used to perform operations on child blocks

    Returns:
        [`Info`] - any configuration Info objects relevant to other parts
    """

    Configure = Hook()
    """Called at configure() to configure child block for a run

    Args:
        task (Task): The task used to perform operations on child blocks
        completed_steps (int): Number of steps already completed
        steps_to_do (int): Number of steps we should configure for
        part_info (dict): {part_name: [Info]} returned from ReportStatus
        params (Map): Any configuration parameters asked for by part configure()
            method_takes() decorator

    Returns:
        [`Info`] - any Info objects that need to be passed to other parts for
            storing in attributes
    """

    PostConfigure = Hook()
    """Called at the end of configure() to store configuration info calculated
     in the Configure hook

    Args:
        task (Task): The task used to perform operations on child blocks
        part_info (dict): {part_name: [Info]} returned from Configure hook
    """

    Run = Hook()
    """Called at run() to start the configured steps running

    Args:
        task (Task): The task used to perform operations on child blocks
        update_completed_steps (callable): If part can report progress, this
            part should call update_completed_steps(completed_steps, self) with
            the integer step value each time progress is updated
    """

    PostRunReady = Hook()
    """Called at the end of run() when there are more steps to be run

    Args:
        task (Task): The task used to perform operations on child blocks
        completed_steps (int): Number of steps already completed
        steps_to_do (int): Number of steps we should configure for
        part_info (dict): {part_name: [Info]} returned from ReportStatus
        params (Map): Any configuration parameters asked for by part configure()
            method_takes() decorator
    """

    PostRunIdle = Hook()
    """Called at the end of run() when there are no more steps to be run

    Args:
        task (Task): The task used to perform operations on child blocks
    """

    Pause = Hook()
    """Called at pause() to pause the current scan before Seek is called

    Args:
        task (Task): The task used to perform operations on child blocks
    """

    Seek = Hook()
    """Called at seek() or at the end of pause() to reconfigure for a different
    number of completed_steps

    Args:
        task (Task): The task used to perform operations on child blocks
        completed_steps (int): Number of steps already completed
        steps_to_do (int): Number of steps we should configure for
        part_info (dict): {part_name: [Info]} returned from ReportStatus
        params (Map): Any configuration parameters asked for by part configure()
            method_takes() decorator
    """

    Resume = Hook()
    """Called at resume() to continue a paused scan

    Args:
        task (Task): The task used to perform operations on child blocks
        update_completed_steps (callable): If part can report progress, this
            part should call update_completed_steps(completed_steps, self) with
            the integer step value each time progress is updated
    """

    Abort = Hook()
    """Called at abort() to stop the current scan

    Args:
        task (Task): The task used to perform operations on child blocks
    """

    # Attributes
    completed_steps = None
    configured_steps = None
    total_steps = None
    axes_to_move = None

    # Params passed to configure()
    configure_params = None

    # Stored for pause
    steps_per_run = 0

    # Progress reporting dict
    # {part: completed_steps for that part}
    progress_reporting = None

    @method_writeable_in(sm.IDLE)
    def edit(self):
        # Override edit to only work from Idle
        super(RunnableController, self).edit()

    @method_writeable_in(sm.FAULT, sm.DISABLED, sm.ABORTED, sm.READY)
    def reset(self):
        # Override reset to work from aborted and ready too
        super(RunnableController, self).reset()

    def create_attributes(self):
        for data in super(RunnableController, self).create_attributes():
            yield data
        self.completed_steps = NumberMeta(
            "int32", "Readback of number of scan steps").make_attribute(0)
        self.completed_steps.meta.set_writeable_in(sm.PAUSED, sm.READY)
        yield "completedSteps", self.completed_steps, self.set_completed_steps
        self.configured_steps = NumberMeta(
            "int32", "Number of steps currently configured").make_attribute(0)
        yield "configuredSteps", self.configured_steps, None
        self.total_steps = NumberMeta(
            "int32", "Readback of number of scan steps").make_attribute(0)
        yield "totalSteps", self.total_steps, None
        self.axes_to_move = StringArrayMeta(
            "Default axis names to scan for configure()").make_attribute(
                self.params.axesToMove)
        self.axes_to_move.meta.set_writeable_in(sm.EDITABLE)
        yield "axesToMove", self.axes_to_move, self.set_axes_to_move

    def do_reset(self):
        super(RunnableController, self).do_reset()
        self._update_configure_args()
        self.configured_steps.set_value(0)
        self.completed_steps.set_value(0)
        self.total_steps.set_value(0)

    def go_to_error_state(self, exception):
        if isinstance(exception, AbortedError):
            self.log_info("Got AbortedError in %s" % self.state.value)
        else:
            super(RunnableController, self).go_to_error_state(exception)

    def _update_configure_args(self):
        # Look for all parts that hook into Configure
        configure_funcs = self.Configure.find_hooked_functions(self.parts)
        method_metas = []
        for part, func_name in configure_funcs.items():
            method_metas.append(part.method_metas[func_name])

        # Update takes with the things we need
        default_configure = MethodMeta.from_dict(
            RunnableController.configure.MethodMeta.to_dict())
        default_configure.defaults["axesToMove"] = self.axes_to_move.value
        method_metas.append(default_configure)

        # Decorate validate and configure with the sum of its parts
        self.block["validate"].recreate_from_others(method_metas)
        self.block["validate"].set_returns(self.block["validate"].takes)
        self.block["configure"].recreate_from_others(method_metas)

    def set_axes_to_move(self, value):
        self.axes_to_move.set_value(value)
        self._update_configure_args()

    @method_takes(*configure_args)
    def validate(self, params, returns):
        iterations = 10
        # Make some tasks just for validate
        part_tasks = self.create_part_tasks()
        # Get any status from all parts
        status_part_info = self.run_hook(self.ReportStatus, part_tasks)
        while iterations > 0:
            # Try up to 10 times to get a valid set of parameters
            iterations -= 1
            # Validate the params with all the parts
            validate_part_info = self.run_hook(self.Validate, part_tasks,
                                               status_part_info, **params)
            tweaks = ParameterTweakInfo.filter_values(validate_part_info)
            if tweaks:
                for tweak in tweaks:
                    params[tweak.parameter] = tweak.value
                    self.log_debug("Tweaking %s to %s", tweak.parameter,
                                   tweak.value)
            else:
                # Consistent set, just return the params
                return params
        raise ValueError("Could not get a consistent set of parameters")

    @method_takes(*configure_args)
    @method_writeable_in(sm.IDLE)
    def configure(self, params):
        """Configure for a scan"""
        self.validate(params, params)
        self.try_stateful_function(sm.CONFIGURING, sm.READY, self.do_configure,
                                   params)

    def do_configure(self, params):
        # These are the part tasks that abort() and pause() will operate on
        self.part_tasks = self.create_part_tasks()
        # Load the saved settings first
        self.run_hook(self.Load, self.part_tasks, self.load_structure)
        # Store the params for use in seek()
        self.configure_params = params
        # This will calculate what we need from the generator, possibly a long
        # call
        params.generator.prepare()
        # Set the steps attributes that we will do across many run() calls
        self.total_steps.set_value(params.generator.size)
        self.completed_steps.set_value(0)
        self.configured_steps.set_value(0)
        # TODO: We can be cleverer about this and support a different number
        # of steps per run for each run by examining the generator structure
        self.steps_per_run = self._get_steps_per_run(params.generator,
                                                     params.axesToMove)
        # Get any status from all parts
        part_info = self.run_hook(self.ReportStatus, self.part_tasks)
        # Use the ProgressReporting classes for ourselves
        self.progress_reporting = {}
        # Run the configure command on all parts, passing them info from
        # ReportStatus. Parts should return any reporting info for PostConfigure
        completed_steps = 0
        steps_to_do = self.steps_per_run
        part_info = self.run_hook(self.Configure, self.part_tasks,
                                  completed_steps, steps_to_do, part_info,
                                  **self.configure_params)
        # Take configuration info and reflect it as attribute updates
        self.run_hook(self.PostConfigure, self.part_tasks, part_info)
        # Update the completed and configured steps
        self.configured_steps.set_value(steps_to_do)

    def _get_steps_per_run(self, generator, axes_to_move):
        steps = 1
        axes_set = set(axes_to_move)
        for dim in reversed(generator.dimensions):
            # If the axes_set is empty then we are done
            if not axes_set:
                break
            # Consume the axes that this generator scans
            for axis in dim.axes:
                assert axis in axes_set, \
                    "Axis %s is not in %s" % (axis, axes_to_move)
                axes_set.remove(axis)
            # Now multiply by the dimensions to get the number of steps
            steps *= dim.size
        return steps

    @method_writeable_in(sm.READY)
    def run(self):
        """Run an already configured scan"""
        if self.configured_steps.value < self.total_steps.value:
            next_state = sm.READY
        else:
            next_state = sm.IDLE
        self.try_stateful_function(sm.RUNNING, next_state, self._call_do_run)

    def _call_do_run(self):
        hook = self.Run
        while True:
            try:
                self.do_run(hook)
            except AbortedError:
                # Work out if it was an abort or pause
                state = self.state.value
                self.log_debug("Do run got AbortedError from %s", state)
                if state in (sm.SEEKING, sm.PAUSED):
                    # Wait to be restarted
                    task = Task("StateWaiter", self.process)
                    bad_states = [sm.DISABLING, sm.ABORTING, sm.FAULT]
                    try:
                        task.when_matches(self.state, sm.RUNNING, bad_states)
                    except BadValueError:
                        # raise AbortedError so we don't try to transition
                        raise AbortedError()
                    # Restart it
                    hook = self.Resume
                    self.status.set_value("Run resumed")
                else:
                    # just drop out
                    raise
            else:
                return

    def do_run(self, hook):
        self.run_hook(hook, self.part_tasks, self.update_completed_steps)
        self.transition(sm.POSTRUN, "Finishing run")
        completed_steps = self.configured_steps.value
        if completed_steps < self.total_steps.value:
            steps_to_do = self.steps_per_run
            part_info = self.run_hook(self.ReportStatus, self.part_tasks)
            self.completed_steps.set_value(completed_steps)
            self.run_hook(self.PostRunReady, self.part_tasks, completed_steps,
                          steps_to_do, part_info, **self.configure_params)
            self.configured_steps.set_value(completed_steps + steps_to_do)
        else:
            self.run_hook(self.PostRunIdle, self.part_tasks)

    def update_completed_steps(self, completed_steps, part):
        # This is run in the child thread, so make sure it is thread safe
        self.progress_reporting[part] = completed_steps
        min_completed_steps = min(self.progress_reporting.values())
        if min_completed_steps > self.completed_steps.value:
            self.completed_steps.set_value(min_completed_steps)

    @method_writeable_in(sm.IDLE, sm.CONFIGURING, sm.READY, sm.RUNNING,
                         sm.POSTRUN, sm.PAUSED, sm.SEEKING)
    def abort(self):
        self.try_stateful_function(sm.ABORTING, sm.ABORTED, self.do_abort,
                                   self.Abort)

    def do_abort(self, hook):
        for task in self.part_tasks.values():
            task.stop()
        self.run_hook(hook, self.create_part_tasks())
        for task in self.part_tasks.values():
            task.wait()

    def set_completed_steps(self, completed_steps):
        params = self.pause.MethodMeta.prepare_input_map(
            completedSteps=completed_steps)
        self.pause(params)

    @method_writeable_in(sm.READY, sm.PAUSED, sm.RUNNING)
    @method_takes(
        "completedSteps",
        NumberMeta("int32",
                   "Step to mark as the last completed step, -1 for current"),
        -1)
    def pause(self, params):
        current_state = self.state.value
        if params.completedSteps < 0:
            completed_steps = self.completed_steps.value
        else:
            completed_steps = params.completedSteps
        if current_state == sm.RUNNING:
            next_state = sm.PAUSED
        else:
            next_state = current_state
        assert completed_steps < self.total_steps.value, \
            "Cannot seek to after the end of the scan"
        self.try_stateful_function(sm.SEEKING, next_state, self.do_pause,
                                   completed_steps)

    def do_pause(self, completed_steps):
        self.do_abort(self.Pause)
        in_run_steps = completed_steps % self.steps_per_run
        steps_to_do = self.steps_per_run - in_run_steps
        part_info = self.run_hook(self.ReportStatus, self.part_tasks)
        self.completed_steps.set_value(completed_steps)
        self.run_hook(self.Seek, self.part_tasks, completed_steps, steps_to_do,
                      part_info, **self.configure_params)
        self.configured_steps.set_value(completed_steps + steps_to_do)

    @method_writeable_in(sm.PAUSED)
    def resume(self):
        self.transition(sm.RUNNING, "Resuming run")
예제 #37
0
 def test_set_elements(self):
     els = ElementMap(dict(sam=StringArrayMeta()))
     self.mm.set_elements(els)
     self.assertEqual(self.mm.elements, els)