示例#1
0
class Controller(Loggable):
    """Implement the logic that takes a Block through its statemachine"""

    Resetting = Hook()

    def __init__(self, process, block, block_name):
        """
        Args:
            process (Process): The process this should run under
            block (Block): Block instance to add Methods and Attributes to
        """
        process.add_block(block_name, block)
        self.set_logger_name("%s.controller" % block_name)

        # dictionary of dictionaries
        # {state (str): {Method: writeable (bool)}
        self.methods_writeable = {}
        self.process = process
        self.parts = []
        self.block = block
        for name, attribute in self._create_default_attributes():
            block.add_attribute(name, attribute)
        for name, attribute in self.create_attributes():
            block.add_attribute(name, attribute)
        for name, method in self.create_methods():
            block.add_method(name, method)
            # Set if the method is writeable
            if method.only_in is None:
                states = [
                    state for state in self.stateMachine.possible_states
                    if state != sm.DISABLED
                ]
            else:
                states = method.only_in
                for state in states:
                    assert state in self.stateMachine.possible_states, \
                        "State %s is not one of the valid states %s" % \
                        (state, self.stateMachine.possible_states)
            self.set_method_writeable_in(method, states)

    def create_methods(self):
        """Abstract method that should provide Method instances for Block

        Yields:
            Method: Each one will be attached to the Block by calling
            block.add_method(method)
        """

        for name, member in inspect.getmembers(self, inspect.ismethod):
            if hasattr(member, "Method"):
                member.Method.set_function(member)
                yield (name, member.Method)

    def create_attributes(self):
        """Abstract method that should provide Attribute instances for Block

        Yields:
            Attribute: Each one will be attached to the Block by calling
            block.add_attribute(attribute)
        """
        return iter(())

    def _create_default_attributes(self):
        self.state = Attribute(
            ChoiceMeta(description="State of Block",
                       choices=self.stateMachine.possible_states))
        self.state.set_parent(self.block, 'state')
        self.state.set_value(self.stateMachine.DISABLED)
        yield ('state', self.state)
        self.status = Attribute(StringMeta(description="Status of Block"))
        self.status.set_value("Disabled")
        yield ('status', self.status)
        self.busy = Attribute(
            BooleanMeta(description="Whether Block busy or not"))
        self.busy.set_value(False)
        yield ('busy', self.busy)

    @takes()
    @only_in(sm.DISABLED, sm.FAULT)
    def reset(self):
        try:
            self.transition(sm.RESETTING, "Resetting")
            self.Resetting.run(self)
            self.transition(sm.AFTER_RESETTING, "Done resetting")
        except Exception as e:
            self.log_exception("Fault occurred while Resetting")
            self.transition(sm.FAULT, str(e))

    @takes()
    def disable(self):
        self.transition(sm.DISABLED, "Disabled")

    def add_parts(self, parts):
        self.parts.extend(parts)

    def transition(self, state, message):
        """
        Change to a new state if the transition is allowed

        Args:
            state(str): State to transition to
            message(str): Status message
        """

        if self.stateMachine.is_allowed(initial_state=self.state.value,
                                        target_state=state):

            self.state.set_value(state)

            if state in self.stateMachine.busy_states:
                self.busy.set_value(True)
            else:
                self.busy.set_value(False)

            self.status.set_value(message)

            for method in self.block.methods.values():
                writeable = self.methods_writeable[state][method.name]
                method.set_writeable(writeable)

            self.block.notify_subscribers()

        else:
            raise TypeError("Cannot transition from %s to %s" %
                            (self.state.value, state))

    def set_method_writeable_in(self, method, states):
        """
        Set the states that the given method can be called in

        Args:
            method(Method): Method that will be set writeable or not
            states(list[str]): List of states where method is writeable
        """
        for state in self.stateMachine.possible_states:
            writeable_dict = self.methods_writeable.setdefault(state, {})
            is_writeable = state in states
            writeable_dict[method.name] = is_writeable
示例#2
0
class TestTask(unittest.TestCase):
    def setUp(self):
        self.callback_result = 0
        self.callback_value = ''
        meta = VMeta("meta for unit tests")
        self.block = MagicMock()
        self.proc = MagicMock(q=queue.Queue())
        self.proc.create_queue = MagicMock(side_effect=queue.Queue)
        self.attr = Attribute(meta)
        self.attr.set_parent(self.block, "testAttr")
        self.attr2 = Attribute(meta)
        self.attr2.set_parent(self.block, "testAttr2")
        self.method = Method("method for unit tests")
        self.method.set_parent(self.block, "testFunc")
        self.method2 = Method("method for unit tests")
        self.method2.set_parent(self.block, "testFunc")
        self.bad_called_back = False

    def test_init(self):
        t = Task("testTask", self.proc)
        self.assertIsInstance(t._logger, logging.Logger)
        self.assertIsInstance(t.q, queue.Queue)
        self.assertEqual(t.process, self.proc)

    def test_put_async(self):
        t = Task("testTask", self.proc)
        t.put_async(self.attr, "testValue")
        req = self.proc.q.get(timeout=0)
        self.assertIsInstance(req, Request)
        self.assertEqual(req.endpoint, [self.block.name, 'testAttr', 'value'])
        self.assertEqual(len(t._futures), 1)

        d = {self.attr: "testValue", self.attr2: "testValue2"}
        t.put_async(d)
        self.proc.q.get(timeout=0)
        req2 = self.proc.q.get(timeout=0)
        self.assertEqual(self.proc.q.qsize(), 0)
        self.assertIsInstance(req2, Request)
        self.assertEqual(len(t._futures), 3)

    def test_put(self):
        # single attribute
        t = Task("testTask", self.proc)
        resp = Return(0, None, None)
        resp.set_value('testVal')
        # cheat and add the response before the blocking call to put
        t.q.put(resp)
        t.stop()
        t.put(self.attr, "testValue")
        self.assertEqual(len(t._futures), 0)
        self.assertEqual(self.proc.q.qsize(), 1)

    def test_post(self):
        t = Task("testTask", self.proc)
        resp1 = Return(0, None, None)
        resp1.set_value('testVal')
        resp2 = Error(1, None, None)
        # cheat and add the responses before the blocking call to put
        t.q.put(resp1)
        t.q.put(resp2)
        t.stop()
        t.post(self.method, "testParm")
        t.post(self.method, "testParm2")
        self.assertEqual(len(t._futures), 0)
        self.assertEqual(self.proc.q.qsize(), 2)

    def test_wait_all(self):
        t = Task("testTask", self.proc)
        f1 = Future(t)
        f2 = Future(t)
        f3 = Future(t)
        f0 = Future(t)
        t._futures = {0: f0, 1: f1, 2: f2, 3: f3}
        f_wait1 = [f2, f0]
        self.assertRaises(queue.Empty, t.wait_all, f_wait1, 0)

        resp0 = Return(0, None, None)
        resp0.set_value('testVal')
        resp2 = Error(2, None, None)
        t.q.put(resp0)
        t.q.put(resp2)
        t.wait_all(f_wait1, 0)
        self.assertEqual(t._futures, {1: f1, 3: f3})
        self.assertEqual(f0.done(), True)
        self.assertEqual(f1.done(), False)
        self.assertEqual(f2.done(), True)
        self.assertEqual(f3.done(), False)
        self.assertEqual(self.proc.q.qsize(), 0)

        resp3 = Delta(3, None, None)
        t.q.put(resp3)
        f_wait1 = [f3]
        self.assertRaises(ValueError, t.wait_all, f_wait1, 0.01)
        t.stop()
        self.assertRaises(RuntimeWarning, t.wait_all, f_wait1, 0.01)

        resp1 = Return(1, None, None)
        resp1.set_value('testVal')
        t.q.put(resp1)
        self.assertRaises(queue.Empty, t.wait_all, f_wait1, 0.01)
        self.assertEqual(t._futures, {})

        t._futures = {0: f0, 1: f1, 2: f2}
        t.q.put(resp1)
        t.q.put(Task.TASK_STOP)
        self.assertEqual(f1.result(), 'testVal')

    def test_wait_all_missing_futures(self):
        # unsolicited response
        t = Task("testTask", self.proc)
        f1 = Future(t)
        resp10 = Return(10, None, None)
        t.q.put(resp10)
        t.q.put(Task.TASK_STOP)
        self.assertRaises(RuntimeWarning, t.wait_all, f1, 0)

        # same future twice
        f2 = Future(t)
        t._futures = {1: f2}
        resp1 = Return(1, None, None)
        t.q.put(resp1)
        t.q.put(Task.TASK_STOP)
        t.wait_all(f2, 0)
        t.wait_all(f2, 0)

    def _callback(self, value, a, b):
        self.callback_result = a + b
        self.callback_value = value

    def test_subscribe(self):
        t = Task("testTask", self.proc)
        resp = Update(0, None, None)
        resp.set_value('changedVal')
        t.q.put(resp)
        t.stop()

        new_id = t.subscribe(self.attr, self._callback, 3, 5)
        f1 = Future(t)
        t._futures = {1: f1}

        self.assertRaises(RuntimeWarning, t.wait_all, f1, 0)
        self.assertEqual(self.callback_value, 'changedVal')
        self.assertEqual(self.callback_result, 8)
        t.unsubscribe(new_id)

    def test_callback_error(self):
        t = Task("testTask", self.proc)
        resp = Error(0, None, None)
        resp.set_message('error')
        t.q.put(resp)
        t.stop()

        t.subscribe(self.attr, self._callback, 3, 5)
        f1 = Future(t)
        t._futures = {1: f1}
        self.assertRaises(RuntimeError, t.wait_all, f1, 0)

    def test_callback_unexpected(self):
        t = Task("testTask", self.proc)
        resp = Delta(0, None, None)
        t.q.put(resp)
        t.stop()
        t.subscribe(self.attr, self._callback, 3, 5)
        f1 = Future(t)
        t._futures = {1: f1}
        self.assertRaises(ValueError, t.wait_all, f1, 0)

    def _bad_callback(self, value):
        self.bad_called_back = True
        raise TestWarning()

    def test_callback_crash(self):
        t = Task("testTask", self.proc)
        resp = Update(0, None, None)
        resp.set_value('changedVal')
        t.q.put(resp)
        t.stop()

        t.subscribe(self.attr, self._bad_callback)
        f1 = Future(t)
        t._futures = {1: f1}
        self.assertRaises(RuntimeWarning, t.wait_all, f1, 0)
        self.assertEquals(self.bad_called_back, True)

    def test_when_matches(self):
        t = Task("testTask", self.proc)
        f = t.when_matches(self.attr, "matchTest")

        # match (response goes to the subscription at id 1,
        # not the future at id 0)
        resp = Update(1, None, None)
        resp.set_value('matchTest')
        t.q.put(resp)
        t.stop()
        self.assertEqual(f[0].result(0), 'matchTest')

    def test_not_when_matches(self):
        t = Task("testTask", self.proc)
        f = t.when_matches(self.attr, "matchTest")

        # match (response goes to the subscription at id 1,
        # not the future at id 0)
        resp = Update(1, None, None)
        resp.set_value('NOTmatchTest')
        t.q.put(resp)
        t.stop()

        # this will abort the task because f[0] never gets filled
        self.assertRaises(RuntimeWarning, f[0].result)
示例#3
0
class TestTask(unittest.TestCase):

    def setUp(self):
        self.callback_result = 0
        self.callback_value = ''
        meta = VMeta("meta for unit tests")
        self.block = MagicMock()
        self.proc = MagicMock(q=queue.Queue())
        self.proc.create_queue = MagicMock(side_effect=queue.Queue)
        self.attr = Attribute(meta)
        self.attr.set_parent(self.block, "testAttr")
        self.attr2 = Attribute(meta)
        self.attr2.set_parent(self.block, "testAttr2")
        self.method = Method("method for unit tests")
        self.method.set_parent(self.block, "testFunc")
        self.method2 = Method("method for unit tests")
        self.method2.set_parent(self.block, "testFunc")
        self.bad_called_back = False

    def test_init(self):
        t = Task("testTask", self.proc)
        self.assertIsInstance(t._logger, logging.Logger)
        self.assertIsInstance(t.q, queue.Queue)
        self.assertEqual(t.process, self.proc)

    def test_put_async(self):
        t = Task("testTask", self.proc)
        t.put_async(self.attr, "testValue")
        req = self.proc.q.get(timeout=0)
        self.assertIsInstance(req, Request)
        self.assertEqual(req.endpoint,
                         [self.block.name, 'testAttr', 'value'])
        self.assertEqual(len(t._futures), 1)

        d = {self.attr: "testValue", self.attr2: "testValue2"}
        t.put_async(d)
        self.proc.q.get(timeout=0)
        req2 = self.proc.q.get(timeout=0)
        self.assertEqual(self.proc.q.qsize(), 0)
        self.assertIsInstance(req2, Request)
        self.assertEqual(len(t._futures), 3)

    def test_put(self):
        # single attribute
        t = Task("testTask", self.proc)
        resp = Return(0, None, None)
        resp.set_value('testVal')
        # cheat and add the response before the blocking call to put
        t.q.put(resp)
        t.stop()
        t.put(self.attr, "testValue")
        self.assertEqual(len(t._futures), 0)
        self.assertEqual(self.proc.q.qsize(), 1)

    def test_post(self):
        t = Task("testTask", self.proc)
        resp1 = Return(0, None, None)
        resp1.set_value('testVal')
        resp2 = Error(1, None, None)
        # cheat and add the responses before the blocking call to put
        t.q.put(resp1)
        t.q.put(resp2)
        t.stop()
        t.post(self.method, "testParm")
        t.post(self.method, "testParm2")
        self.assertEqual(len(t._futures), 0)
        self.assertEqual(self.proc.q.qsize(), 2)

    def test_wait_all(self):
        t = Task("testTask", self.proc)
        f1 = Future(t)
        f2 = Future(t)
        f3 = Future(t)
        f0 = Future(t)
        t._futures = {0: f0, 1: f1, 2: f2, 3: f3}
        f_wait1 = [f2, f0]
        self.assertRaises(queue.Empty, t.wait_all, f_wait1, 0)

        resp0 = Return(0, None, None)
        resp0.set_value('testVal')
        resp2 = Error(2, None, None)
        t.q.put(resp0)
        t.q.put(resp2)
        t.wait_all( f_wait1, 0)
        self.assertEqual(t._futures, {1: f1, 3: f3})
        self.assertEqual(f0.done(), True)
        self.assertEqual(f1.done(), False)
        self.assertEqual(f2.done(), True)
        self.assertEqual(f3.done(), False)
        self.assertEqual(self.proc.q.qsize(), 0)

        resp3 = Delta(3, None, None)
        t.q.put(resp3)
        f_wait1 = [f3]
        self.assertRaises(ValueError, t.wait_all, f_wait1, 0.01)
        t.stop()
        self.assertRaises(RuntimeWarning, t.wait_all, f_wait1, 0.01)

        resp1 = Return(1, None, None)
        resp1.set_value('testVal')
        t.q.put(resp1)
        self.assertRaises(queue.Empty, t.wait_all, f_wait1, 0.01)
        self.assertEqual(t._futures, {})

        t._futures = {0: f0, 1: f1, 2: f2}
        t.q.put(resp1)
        t.q.put(Task.TASK_STOP)
        self.assertEqual(f1.result(), 'testVal')

    def test_wait_all_missing_futures(self):
        # unsolicited response
        t = Task("testTask", self.proc)
        f1 = Future(t)
        resp10 = Return(10, None, None)
        t.q.put(resp10)
        t.q.put(Task.TASK_STOP)
        self.assertRaises(RuntimeWarning, t.wait_all, f1, 0)

        # same future twice
        f2 = Future(t)
        t._futures = {1: f2}
        resp1 = Return(1, None, None)
        t.q.put(resp1)
        t.q.put(Task.TASK_STOP)
        t.wait_all(f2,0)
        t.wait_all(f2,0)


    def _callback(self, value, a, b):
        self.callback_result = a+b
        self.callback_value = value

    def test_subscribe(self):
        t = Task("testTask", self.proc)
        resp = Update(0, None, None)
        resp.set_value('changedVal')
        t.q.put(resp)
        t.stop()

        new_id = t.subscribe(self.attr, self._callback, 3, 5)
        f1 = Future(t)
        t._futures = {1: f1}

        self.assertRaises(RuntimeWarning, t.wait_all, f1, 0)
        self.assertEqual(self.callback_value, 'changedVal')
        self.assertEqual(self.callback_result, 8)
        t.unsubscribe(new_id)

    def test_callback_error(self):
        t = Task("testTask", self.proc)
        resp = Error(0, None, None)
        resp.set_message('error')
        t.q.put(resp)
        t.stop()

        t.subscribe(self.attr, self._callback, 3, 5)
        f1 = Future(t)
        t._futures = {1: f1}
        self.assertRaises(RuntimeError, t.wait_all, f1, 0)

    def test_callback_unexpected(self):
        t = Task("testTask", self.proc)
        resp = Delta(0, None, None)
        t.q.put(resp)
        t.stop()
        t.subscribe(self.attr, self._callback, 3, 5)
        f1 = Future(t)
        t._futures = {1: f1}
        self.assertRaises(ValueError, t.wait_all, f1, 0)


    def _bad_callback(self, value):
        self.bad_called_back = True
        raise TestWarning()

    def test_callback_crash(self):
        t = Task("testTask", self.proc)
        resp = Update(0, None, None)
        resp.set_value('changedVal')
        t.q.put(resp)
        t.stop()

        t.subscribe(self.attr, self._bad_callback)
        f1 = Future(t)
        t._futures = {1: f1}
        self.assertRaises(RuntimeWarning, t.wait_all, f1, 0)
        self.assertEquals(self.bad_called_back, True)

    def test_when_matches(self):
        t = Task("testTask", self.proc)
        f = t.when_matches(self.attr, "matchTest")

        # match (response goes to the subscription at id 1,
        # not the future at id 0)
        resp = Update(1, None, None)
        resp.set_value('matchTest')
        t.q.put(resp)
        t.stop()
        self.assertEqual(f[0].result(0),'matchTest')

    def test_not_when_matches(self):
        t = Task("testTask", self.proc)
        f = t.when_matches(self.attr, "matchTest")

        # match (response goes to the subscription at id 1,
        # not the future at id 0)
        resp = Update(1, None, None)
        resp.set_value('NOTmatchTest')
        t.q.put(resp)
        t.stop()

        # this will abort the task because f[0] never gets filled
        self.assertRaises(RuntimeWarning, f[0].result)
示例#4
0
class Controller(Loggable):
    """Implement the logic that takes a Block through its statemachine"""

    Resetting = Hook()

    def __init__(self, process, block, block_name):
        """
        Args:
            process (Process): The process this should run under
            block (Block): Block instance to add Methods and Attributes to
        """
        process.add_block(block_name, block)
        self.set_logger_name("%s.controller" % block_name)

        # dictionary of dictionaries
        # {state (str): {Method: writeable (bool)}
        self.methods_writeable = {}
        self.process = process
        self.parts = []
        self.block = block
        for name, attribute in self._create_default_attributes():
            block.add_attribute(name, attribute)
        for name, attribute in self.create_attributes():
            block.add_attribute(name, attribute)
        for name, method in self.create_methods():
            block.add_method(name, method)
            # Set if the method is writeable
            if method.only_in is None:
                states = [state for state in self.stateMachine.possible_states
                          if state != sm.DISABLED]
            else:
                states = method.only_in
                for state in states:
                    assert state in self.stateMachine.possible_states, \
                        "State %s is not one of the valid states %s" % \
                        (state, self.stateMachine.possible_states)
            self.set_method_writeable_in(method, states)

    def create_methods(self):
        """Abstract method that should provide Method instances for Block

        Yields:
            Method: Each one will be attached to the Block by calling
            block.add_method(method)
        """

        for name, member in inspect.getmembers(self, inspect.ismethod):
            if hasattr(member, "Method"):
                member.Method.set_function(member)
                yield (name, member.Method)

    def create_attributes(self):
        """Abstract method that should provide Attribute instances for Block

        Yields:
            Attribute: Each one will be attached to the Block by calling
            block.add_attribute(attribute)
        """
        return iter(())

    def _create_default_attributes(self):
        self.state = Attribute(ChoiceMeta(description="State of Block",
                            choices=self.stateMachine.possible_states))
        self.state.set_parent(self.block,'state')
        self.state.set_value(self.stateMachine.DISABLED)
        yield ('state', self.state)
        self.status = Attribute(StringMeta(description="Status of Block"))
        self.status.set_value("Disabled")
        yield ('status', self.status)
        self.busy = Attribute(BooleanMeta(
            description="Whether Block busy or not"))
        self.busy.set_value(False)
        yield ('busy', self.busy)

    @takes()
    @only_in(sm.DISABLED, sm.FAULT)
    def reset(self):
        try:
            self.transition(sm.RESETTING, "Resetting")
            self.Resetting.run(self)
            self.transition(sm.AFTER_RESETTING, "Done resetting")
        except Exception as e:
            self.log_exception("Fault occurred while Resetting")
            self.transition(sm.FAULT, str(e))

    @takes()
    def disable(self):
        self.transition(sm.DISABLED, "Disabled")

    def add_parts(self, parts):
        self.parts.extend(parts)

    def transition(self, state, message):
        """
        Change to a new state if the transition is allowed

        Args:
            state(str): State to transition to
            message(str): Status message
        """

        if self.stateMachine.is_allowed(initial_state=self.state.value,
                                        target_state=state):

            self.state.set_value(state)

            if state in self.stateMachine.busy_states:
                self.busy.set_value(True)
            else:
                self.busy.set_value(False)

            self.status.set_value(message)

            for method in self.block.methods.values():
                writeable = self.methods_writeable[state][method.name]
                method.set_writeable(writeable)

            self.block.notify_subscribers()

        else:
            raise TypeError("Cannot transition from %s to %s" %
                            (self.state.value, state))

    def set_method_writeable_in(self, method, states):
        """
        Set the states that the given method can be called in

        Args:
            method(Method): Method that will be set writeable or not
            states(list[str]): List of states where method is writeable
        """
        for state in self.stateMachine.possible_states:
            writeable_dict = self.methods_writeable.setdefault(state, {})
            is_writeable = state in states
            writeable_dict[method.name] = is_writeable