示例#1
0
    def __init__(self):
        states = ['wait',
                  'in_trial',
                  'post_trial',
                  'done']
        
        transitions = [
            {'source': 'wait',
             'trigger': 'step',
             'conditions': 'check_for_space',
             'after': 'remove_text',
             'dest': 'in_trial'},

            {'source': 'in_trial',
             'trigger': 'step',
             'prepare': ['update_target_pos', 'update_target_color_and_count'],
             'conditions': 'samples_exhausted',
             'after': ['draw_time_on_target', 'start_countdown'],
             'dest': 'post_trial'},
            
            {'source': 'post_trial',
             'trigger': 'step',
             'conditions': 'time_elapsed',
             'dest': 'done'}
        ]
        Machine.__init__(self, states=states,
                         transitions=transitions, initial='wait')
示例#2
0
    def __init__(self, manifold):
        self.manifold = manifold
        self.gui = None

        # Record controller states
        states = [
            State(name='disconnected',  on_enter=['unbusyUI', 'updateUI']),
            State(name='connected',     on_enter=['unbusyUI', 'updateUI']),
            State(name='confirmed',     on_enter=['unbusyUI', 'updateUI']),
            State(name='ready',         on_enter=['unbusyUI', 'updateUI', 'prepRecording']),
            State(name='started',       on_enter=['unbusyUI', 'updateUI', 'startRecording']),
            State(name='paused',        on_enter=['unbusyUI', 'updateUI', 'pauseRecording'])
        ]

        # Record controller state transition definition
        transitions = [
            {'trigger': 'connect',    'source': 'disconnected', 'dest': 'connected'    , 'prepare': ['busyUI', 'connectToServers', 'pingServers'], 'conditions': 'connection_confirmed'},
            {'trigger': 'disconnect', 'source': 'connected',    'dest': 'disconnected' , 'prepare': ['busyUI'] },
            {'trigger': 'disconnect', 'source': 'paused',       'dest': 'disconnected' , 'prepare': ['busyUI'] },
            {'trigger': 'new',        'source': 'connected',    'dest': 'ready'        , 'prepare': ['busyUI'] },
            {'trigger': 'new',        'source': 'paused',       'dest': 'ready'        , 'prepare': ['busyUI'] },
            {'trigger': 'start',      'source': 'ready',        'dest': 'started'      , 'prepare': ['busyUI'] },
            {'trigger': 'pause',      'source': 'started',      'dest': 'paused'       , 'prepare': ['busyUI'] },
        ]

        # Record machine
        Machine.__init__(self,
                         states=states,
                         transitions=transitions,
                         initial='disconnected')
示例#3
0
文件: fsm.py 项目: anbasile/pmu
    def __init__(self):


        states=['chitchat',
                'requestingRide', 'offeringRide',
                'sendingLocation','sendingDestination',
                'waiting',
                'matchFound','nomatchFound',
                'completed']
            
        transitions = [
            { 'trigger': 'abort', 'source': '*', 'dest': 'chitchat'},
            { 'trigger': 'wantRide', 'source': 'chitchat', 'dest': 'requestingRide'},
            { 'trigger': 'offerRide', 'source': 'chitchat', 'dest': 'offeringRide'},
            { 'trigger': 'hasToSendLocation', 'source': 'requestingRide', 'dest': 'sendingLocation'},
            { 'trigger': 'hasToSendLocation', 'source': 'offeringRide', 'dest': 'sendingLocation' },
            { 'trigger': 'hasToSendDestination', 'source': 'sendingLocation', 'dest': 'sendingDestination' },
            { 'trigger': 'gotPosition', 'source': 'sendingDestination', 'dest': 'waiting'},
            { 'trigger': 'OK', 'source': 'waiting', 'dest': 'matchFound' },
            { 'trigger': 'sorry', 'source': 'waiting', 'dest': 'nomatchFound' },
            { 'trigger': 'done', 'source': 'matchFound', 'dest': 'completed' },
            { 'trigger': 'done', 'source': 'nomatchFound', 'dest': 'completed' },
            { 'trigger': 'end', 'source': 'completed', 'dest': 'chitchat' },
            ]
        Machine.__init__(self, states=states, transitions=transitions,auto_transitions=False,initial='chitchat')
示例#4
0
    def test_agraph_diagram(self):
        states = ['A', 'B', 'C', 'D']
        transitions = [
            {'trigger': 'walk', 'source': 'A', 'dest': 'B'},
            {'trigger': 'run', 'source': 'B', 'dest': 'C'},
            {'trigger': 'sprint', 'source': 'C', 'dest': 'D', 'conditions': 'is_fast'},
            {'trigger': 'sprint', 'source': 'C', 'dest': 'B'}
        ]

        m = Machine(states=states, transitions=transitions, initial='A', auto_transitions=False)
        graph = m.get_graph()
        self.assertIsNotNone(graph)
        self.assertTrue("digraph" in str(graph))

        # Test that graph properties match the Machine
        self.assertEqual(
            set(m.states.keys()), set([n.name for n in graph.nodes()]))
        triggers = set([n.attr['label'] for n in graph.edges()])
        for t in triggers:
            self.assertIsNotNone(getattr(m, t))

        self.assertEqual(len(graph.edges()), len(transitions))
        # check for a valid pygraphviz diagram

        # write diagram to temp file
        target = tempfile.NamedTemporaryFile()
        graph.draw(target.name, prog='dot')
        self.assertTrue(os.path.getsize(target.name) > 0)

        # cleanup temp file
        target.close()
        print(graph)
示例#5
0
 def test_before_after_callback_addition(self):
     m = Machine(Stuff(), states=["A", "B", "C"], initial="A")
     m.add_transition("move", "A", "B")
     trans = m.events["move"].transitions["A"][0]
     trans.add_callback("after", "increase_level")
     m.model.move()
     self.assertEquals(m.model.level, 2)
示例#6
0
    def test_state_callbacks(self):

        class Model:
            def on_enter_A(self):
                pass

            def on_exit_A(self):
                pass

            def on_enter_B(self):
                pass

            def on_exit_B(self):
                pass

        states = [State(name='A', on_enter='on_enter_A', on_exit='on_exit_A'),
                  State(name='B', on_enter='on_enter_B', on_exit='on_exit_B')]

        machine = Machine(Model(), states=states)
        state_a = machine.get_state('A')
        state_b = machine.get_state('B')
        self.assertEqual(len(state_a.on_enter), 1)
        self.assertEqual(len(state_a.on_exit), 1)
        self.assertEqual(len(state_b.on_enter), 1)
        self.assertEqual(len(state_b.on_exit), 1)
示例#7
0
    def test_repr(self):
        def a_condition(event_data):
            self.assertRegex(
                str(event_data.transition.conditions),
                r"\[<Condition\(<function TestTransitions.test_repr.<locals>"
                r".a_condition at [^>]+>\)@\d+>\]")
            return True

        # No transition has been assigned to EventData yet
        def check_prepare_repr(event_data):
            self.assertRegex(
                str(event_data),
                r"<EventData\('<State\('A'\)@\d+>', "
                r"None\)@\d+>")

        def check_before_repr(event_data):
            self.assertRegex(
                str(event_data),
                r"<EventData\('<State\('A'\)@\d+>', "
                r"<Transition\('A', 'B'\)@\d+>\)@\d+>")
            m.checked = True

        m = Machine(states=['A', 'B'],
                    prepare_event=check_prepare_repr,
                    before_state_change=check_before_repr, send_event=True,
                    initial='A')
        m.add_transition('do_strcheck', 'A', 'B', conditions=a_condition)

        self.assertTrue(m.do_strcheck())
        self.assertIn('checked', vars(m))
示例#8
0
 def test_before_after_callback_addition_callable(self):
     m = Machine(Stuff(), states=['A', 'B', 'C'], initial='A')
     m.add_transition('move', 'A', 'B')
     trans = m.events['move'].transitions['A'][0]
     trans.add_callback('after', m.model.increase_level)
     m.model.move()
     self.assertEquals(m.model.level, 2)
示例#9
0
    def __init__(self):
        state = ['Up', 'Failed', 'Maintenance', 'Blocked']
        Machine.__init__(self, states = state, initial='Up')
        self.add_transition('start', 'Up', 'Up', after = "startJob")
        self.add_transition('fail', 'Up', 'Failed', after = 'startFail')
        self.add_transition('repair', 'Failed', 'Up', after = 'rep')
        self.add_transition('maint', 'Up', 'Maintenance', after='startmaint')
        self.add_transition('maintcpl', 'Maintenance', 'Up')
        self.add_transition('interrep', 'Failed', 'Maintenance', after='startmaint')
        self.add_transition('block', 'Up', 'Blocked')
        self.add_transition('unblock', 'Blocked', 'Up')
        self.queue = SortedSet(key = lambda job: job.arrivalTime)
        self.numServers = 1
        self.busyServers = 0
        self.prevState = None
        Server._ids +=1
        self.serviceTimeDistribution = None
        self.name = "Server {}".format(Server._ids)
        self.In = None
        self.Out = None
        self.scheduler = None
        self.activejob = None
        self.interuptjob = None

        #debugging
        self.jobsarrived = 0
        self.jobsprocessed = 0
        self.numfailures = 0
        self.nummaint = 0
        self.onzin = 0
    def __init__(self):
        # Define the different states of the state machine
        states = ['secure', 'takeoff', 'follow', 'take_picture', \
                  'land', 'reacquisition', 'emergency']

        # Define the transitions between states
        # FORMAT: ['trigger_event', 'source_state', 'destination_state']
        transitions = [
            ['takeoff_command', 'secure', 'takeoff'],
            ['takeoff_alt_reached', 'takeoff', 'follow'],
            ['picture_command', 'follow', 'take_picture'],
            ['picture_taken', 'take_picture', 'land'],
            ['land_alt_reached', 'land', 'secure'],
            ['emergency_condition', ['takeoff', 'follow', 'take_picture', 'land'], 'emergency'],
            ['takeoff_tag_lost', 'takeoff', 'reacquisition'],
            ['follow_tag_lost', 'follow', 'reacquisition'],
            ['land_tag_lost', 'land', 'reacquisition'],
            ['take_picture_tag_lost', 'take_picture', 'reacquisition'],
            ['takeoff_tag_found', 'reacquisition', 'takeoff'],
            ['follow_tag_found', 'reacquisition', 'follow'],
            ['land_tag_found', 'reacquisition', 'land'],
            ['take_picture_tag_found', 'reacquisition', 'take_picture'],
            ['timed_out', 'reacquisition', 'emergency'],
            ['reset', 'emergency', 'secure']
        ]

        Machine.__init__(self, states=states, transitions=transitions, \
                         initial='secure')
示例#11
0
 def __init__(self):
     self.states = [
         {'name': 'Begin'},         #
         {'name': 'End'},           #
         {'name': 'ChoosePage'},    #
         {'name': 'FirstPage'},     #
         {'name': 'SecondPage'},    #
         {'name': 'Error'}          #
     ]
     self.transitions = [
         {'trigger': 'step', 'source': 'Begin',      'dest': 'ChoosePage'},
         {'trigger': 'step', 'source': 'ChoosePage', 'dest': 'FirstPage',  'conditions': ['verify', 'answerIsOne'], 'after': 'to_ChoosePage'},
         # {'trigger': 'step', 'source': 'ChoosePage', 'dest': 'Error',  'after': 'err'},
         # {'trigger': 'step', 'source': 'ChoosePage', 'dest': 'FirstPage',  'conditions': 'verify', 'after': 'to_ChoosePage'},
         {'trigger': 'step', 'source': 'FirstPage',  'dest': 'ChoosePage'},
         # {'trigger': 'step', 'source': 'FirstPage',  'dest': 'Error',  'after': 'err'},
         {'trigger': 'step', 'source': 'ChoosePage', 'dest': 'SecondPage', 'conditions': ['verify', 'answerIsTwo'], 'after': 'to_ChoosePage'},
         # {'trigger': 'step', 'source': 'ChoosePage', 'dest': 'SecondPage', 'conditions': 'verify', 'after': 'to_ChoosePage'},
         {'trigger': 'step', 'source': 'SecondPage', 'dest': 'ChoosePage'},
         {'trigger': 'step', 'source': 'SecondPage', 'dest': 'Error',  'after': 'err'},
         # {'trigger': 'step', 'source': 'ChoosePage', 'dest': 'End',        'conditions': ['verify', 'answerIsThree']},
         # {'trigger': 'step', 'source': 'ChoosePage', 'dest': 'End',        'conditions': 'verify'}
         # {'trigger': 'step', 'source': 'ChoosePage', 'dest': 'End'},
         {'trigger': 'step', 'source': '*', 'dest': 'End'}
     ]
     Machine.__init__(self, states=self.states, transitions=self.transitions,
                      initial='Begin', send_event=True)
     self.pageNumber = None
     self.answer = None
示例#12
0
    def test_agraph_diagram(self):
        states = ["A", "B", "C", "D"]
        transitions = [
            {"trigger": "walk", "source": "A", "dest": "B"},
            {"trigger": "run", "source": "B", "dest": "C"},
            {"trigger": "sprint", "source": "C", "dest": "D", "conditions": "is_fast"},
            {"trigger": "sprint", "source": "C", "dest": "B"},
        ]

        m = Machine(states=states, transitions=transitions, initial="A", auto_transitions=False)
        graph = m.get_graph()
        self.assertIsNotNone(graph)
        self.assertTrue("digraph" in str(graph))

        # Test that graph properties match the Machine
        self.assertEqual(set(m.states.keys()), set([n.name for n in graph.nodes()]))
        triggers = set([n.attr["label"] for n in graph.edges()])
        for t in triggers:
            self.assertIsNotNone(getattr(m, t))

        self.assertEqual(len(graph.edges()), len(transitions))
        # check for a valid pygraphviz diagram

        # write diagram to temp file
        target = tempfile.NamedTemporaryFile()
        graph.draw(target.name, prog="dot")
        self.assertTrue(os.path.getsize(target.name) > 0)

        # cleanup temp file
        target.close()
        print(graph)
示例#13
0
    def test_machine_prepare(self):

        global_mock = MagicMock()
        local_mock = MagicMock()

        def global_callback():
            global_mock()

        def local_callback():
            local_mock()

        def always_fails():
            return False

        transitions = [
            {'trigger': 'go', 'source': 'A', 'dest': 'B', 'conditions': always_fails, 'prepare': local_callback},
            {'trigger': 'go', 'source': 'A', 'dest': 'B', 'conditions': always_fails, 'prepare': local_callback},
            {'trigger': 'go', 'source': 'A', 'dest': 'B', 'conditions': always_fails, 'prepare': local_callback},
            {'trigger': 'go', 'source': 'A', 'dest': 'B', 'conditions': always_fails, 'prepare': local_callback},
            {'trigger': 'go', 'source': 'A', 'dest': 'B', 'prepare': local_callback},

        ]
        m = Machine(states=['A', 'B'], transitions=transitions,
                    prepare_event=global_callback, initial='A')

        m.go()
        self.assertEqual(global_mock.call_count, 1)
        self.assertEqual(local_mock.call_count, len(transitions))
示例#14
0
    def test_state_callable_callbacks(self):

        class Model:

            def __init__(self):
                self.exit_A_called = False
                self.exit_B_called = False

            def on_enter_A(self, event):
                pass

            def on_enter_B(self, event):
                pass

        states = [State(name='A', on_enter='on_enter_A', on_exit='tests.test_core.on_exit_A'),
                  State(name='B', on_enter='on_enter_B', on_exit=on_exit_B),
                  State(name='C', on_enter='tests.test_core.AAAA')]

        model = Model()
        machine = Machine(model, states=states, send_event=True, initial='A')
        state_a = machine.get_state('A')
        state_b = machine.get_state('B')
        self.assertEqual(len(state_a.on_enter), 1)
        self.assertEqual(len(state_a.on_exit), 1)
        self.assertEqual(len(state_b.on_enter), 1)
        self.assertEqual(len(state_b.on_exit), 1)
        model.to_B()
        self.assertTrue(model.exit_A_called)
        model.to_A()
        self.assertTrue(model.exit_B_called)
        with self.assertRaises(AttributeError):
            model.to_C()
示例#15
0
 def test_dispatch(self):
     s1, s2 = Stuff(), Stuff()
     states = ['A', 'B', 'C']
     m = Machine(model=s1, states=states, ignore_invalid_triggers=True,
                 initial=states[0], transitions=[['go', 'A', 'B'], ['go', 'B', 'C']])
     m.add_model(s2, initial='B')
     m.dispatch('go')
     self.assertEqual(s1.state, 'B')
     self.assertEqual(s2.state, 'C')
示例#16
0
class ShoppingList(object):
    states = ['todo', 'progress', 'finished']

    """
    @:param str name
    """
    def __init__(self, name):
        self.name = name
        self.items = {}
        self.machine = Machine(model=self, states=ShoppingList.states, initial='todo')
        self.machine.add_transition('done', ['todo', 'progress'], 'finished', conditions='is_done')
        self.machine.add_transition('shopping', 'todo', 'progress')

    """
    @:param ListItem item
    """
    def add(self, item):
        self.items[item.name] = item

    """
    @:param str name
    @:return ListItem
    """
    def get(self, name):
        return self.items.get(name)

    """
    @:param str name
    """
    def remove(self, name):
        del self.items[name]

    """
    Look in the items of this shopping list to see if the item was
    found, and if it was, reduce the quantity required by the given
    amount

    @:param str name
    @:param int quantity
    """
    def update(self, name, quantity=1):
        item = self.get(name)

        if item is not None:
            item.found(quantity)

        if self.is_done():
            self.to_finished()

    """
    Determines if the shopping is done by making sure all the list
    items have been fully acquired
    """
    def is_done(self):
        items = [item for item in self.items.values() if item.state is not 'acquired']

        return len(items) is 0
示例#17
0
 def test_pass_state_instances_instead_of_names(self):
     state_A = State('A')
     state_B = State('B')
     states = [state_A, state_B]
     m = Machine(states=states, initial=state_A)
     assert m.state == 'A'
     m.add_transition('advance', state_A, state_B)
     m.advance()
     assert m.state == 'B'
示例#18
0
    def test_before_after_transition_listeners(self):
        m = Machine(Stuff(), states=['A', 'B', 'C'], initial='A')
        m.add_transition('move', 'A', 'B')
        m.add_transition('move', 'B', 'C')

        m.before_move('increase_level')
        m.model.move()
        self.assertEquals(m.model.level, 2)
        m.model.move()
        self.assertEquals(m.model.level, 3)
示例#19
0
    def test_before_after_transition_listeners(self):
        m = Machine(Stuff(), states=["A", "B", "C"], initial="A")
        m.add_transition("move", "A", "B")
        m.add_transition("move", "B", "C")

        m.before_move("increase_level")
        m.model.move()
        self.assertEquals(m.model.level, 2)
        m.model.move()
        self.assertEquals(m.model.level, 3)
示例#20
0
    def test_prepare(self):
        m = Machine(Stuff(), states=['A', 'B', 'C'], initial='A')
        m.add_transition('move', 'A', 'B', prepare='increase_level')
        m.add_transition('move', 'B', 'C', prepare='increase_level')
        m.add_transition('move', 'C', 'A', prepare='increase_level', conditions='this_fails')
        m.add_transition('dont_move', 'A', 'C', prepare='increase_level')

        m.prepare_move('increase_level')

        m.model.move()
        self.assertEquals(m.model.state, 'B')
        self.assertEquals(m.model.level, 3)

        m.model.move()
        self.assertEquals(m.model.state, 'C')
        self.assertEquals(m.model.level, 5)

        # State does not advance, but increase_level still runs
        m.model.move()
        self.assertEquals(m.model.state, 'C')
        self.assertEquals(m.model.level, 7)

        # An invalid transition shouldn't execute the callback
        with self.assertRaises(MachineError):
            m.model.dont_move()

        self.assertEquals(m.model.state, 'C')
        self.assertEquals(m.model.level, 7)
示例#21
0
    def test_prepare(self):
        m = Machine(Stuff(), states=['A', 'B', 'C'], initial='A')
        m.add_transition('move', 'A', 'B', prepare='increase_level')
        m.add_transition('move', 'B', 'C', prepare='increase_level')
        m.add_transition('move', 'C', 'A', prepare='increase_level', conditions='this_fails')
        m.add_transition('dont_move', 'A', 'C', prepare='increase_level')

        m.prepare_move('increase_level')

        m.model.move()
        self.assertEqual(m.model.state, 'B')
        self.assertEqual(m.model.level, 3)

        m.model.move()
        self.assertEqual(m.model.state, 'C')
        self.assertEqual(m.model.level, 5)

        # State does not advance, but increase_level still runs
        m.model.move()
        self.assertEqual(m.model.state, 'C')
        self.assertEqual(m.model.level, 7)

        # An invalid transition shouldn't execute the callback
        try:
            m.model.dont_move()
        except MachineError as e:
            self.assertTrue("Can't trigger event" in str(e))

        self.assertEqual(m.model.state, 'C')
        self.assertEqual(m.model.level, 7)
示例#22
0
    def test_prepare(self):
        m = Machine(Stuff(), states=["A", "B", "C"], initial="A")
        m.add_transition("move", "A", "B", prepare="increase_level")
        m.add_transition("move", "B", "C", prepare="increase_level")
        m.add_transition("move", "C", "A", prepare="increase_level", conditions="this_fails")
        m.add_transition("dont_move", "A", "C", prepare="increase_level")

        m.prepare_move("increase_level")

        m.model.move()
        self.assertEquals(m.model.state, "B")
        self.assertEquals(m.model.level, 3)

        m.model.move()
        self.assertEquals(m.model.state, "C")
        self.assertEquals(m.model.level, 5)

        # State does not advance, but increase_level still runs
        m.model.move()
        self.assertEquals(m.model.state, "C")
        self.assertEquals(m.model.level, 7)

        # An invalid transition shouldn't execute the callback
        with self.assertRaises(MachineError):
            m.model.dont_move()

        self.assertEquals(m.model.state, "C")
        self.assertEquals(m.model.level, 7)
    def test_generic_callbacks(self):

        m = Machine(None, states=['A', 'B'], before_state_change='before_state_change',
            after_state_change='after_state_change', send_event=True, initial='A', auto_transitions=True)

        m.before_state_change = MagicMock()
        m.after_state_change = MagicMock()

        m.to_B()
        self.assertTrue(m.before_state_change.called)
        self.assertTrue(m.after_state_change.called)
示例#24
0
 def test_get_triggers(self):
     states = ['A', 'B', 'C']
     transitions = [['a2b', 'A', 'B'],
                    ['a2c', 'A', 'C'],
                    ['c2b', 'C', 'B']]
     machine = Machine(states=states, transitions=transitions, initial='A', auto_transitions=False)
     self.assertEqual(len(machine.get_triggers('A')), 2)
     self.assertEqual(len(machine.get_triggers('B')), 0)
     self.assertEqual(len(machine.get_triggers('C')), 1)
     # self stuff machine should have to-transitions to every state
     self.assertEqual(len(self.stuff.machine.get_triggers('B')), len(self.stuff.machine.states))
示例#25
0
class UnSubscription:
    states = ["new", "unsubscribed", "acknowledged"]

    def __init__(self, packet_id, topics):
        self.topics = topics
        self.packet_id = packet_id
        self._init_states()

    def _init_states(self):
        self.machine = Machine(model=self, states=UnSubscription.states, initial="new")
        self.machine.add_transition(trigger="unsubscribe", source="new", dest="unsubscribed")
        self.machine.add_transition(trigger="acknowledge", source="unsubscribed", dest="acknowledged")
示例#26
0
 def __init__(self):
     self.states = [
         {'name': 'Begin'},         #
         {'name': 'End'},           #
         {'name': 'Error'}          #
     ]
     self.transitions = [
         {'trigger': 'step', 'source': 'Begin',      'dest': 'End'}
     ]
     Machine.__init__(self, states=self.states, transitions=self.transitions,
                      initial='Begin', send_event=True)
     self.answer = None
示例#27
0
    def test_function_callbacks(self):
        before_state_change = MagicMock()
        after_state_change = MagicMock()

        m = Machine('self', states=['A', 'B'],
                    before_state_change=before_state_change,
                    after_state_change=after_state_change, send_event=True,
                    initial='A', auto_transitions=True)

        m.to_B()
        self.assertTrue(m.before_state_change[0].called)
        self.assertTrue(m.after_state_change[0].called)
示例#28
0
 def test_auto_transitions(self):
     states = ['A', {'name': 'B'}, State(name='C')]
     m = Machine(None, states, initial='A', auto_transitions=True)
     m.to_B()
     self.assertEquals(m.state, 'B')
     m.to_C()
     self.assertEquals(m.state, 'C')
     m.to_A()
     self.assertEquals(m.state, 'A')
     # Should fail if auto transitions is off...
     m = Machine(None, states, initial='A', auto_transitions=False)
     with self.assertRaises(AttributeError):
         m.to_C()
示例#29
0
    def test_generic_callbacks_callable(self):

        before_mock = MagicMock()
        after_mock = MagicMock()

        m = Machine(None, states=['A', 'B'],
                    before_state_change=before_mock,
                    after_state_change=after_mock, send_event=True,
                    initial='A', auto_transitions=True)

        m.to_B()
        self.assertTrue(before_mock.called)
        self.assertTrue(after_mock.called)
示例#30
0
 def test_auto_transitions(self):
     states = ["A", {"name": "B"}, State(name="C")]
     m = Machine(None, states, initial="A", auto_transitions=True)
     m.to_B()
     self.assertEquals(m.state, "B")
     m.to_C()
     self.assertEquals(m.state, "C")
     m.to_A()
     self.assertEquals(m.state, "A")
     # Should fail if auto transitions is off...
     m = Machine(None, states, initial="A", auto_transitions=False)
     with self.assertRaises(AttributeError):
         m.to_C()
示例#31
0
    def __init__(self, verbose=2, locals=locals, globals=globals):
        super(ActionMaskCallback, self).__init__(verbose)
        
        self.model = None  # type: BaseRLModel
        self.training_env = None  # type: Union[gym.Env, VecEnv, None]
        self.n_calls = 0  # Number of time the callback was called # type: int
        self.num_timesteps = 0  # type: int
        self.locals = None  # type: Dict[str, Any]
        self.globals = None  # type: Dict[str, Any]

        # Set all actions to valid.
        self.action_space = MultiDiscrete([3, 21, 21, 21, 21])
        self.action_mask = mask(self.action_space)
        self.action_mask[0] = [1, 0, 0]

        states = ['start', 'start_spring_forward', 'lift_leg', 'plant_leg', 'switch_leg']
        self.gait = Walk()
        self.gait.num_timesteps = self.num_timesteps
        self.gait.action_mask = mask(self.action_space)
        self.gait.action_mask[0] = [1, 0, 0]
        self.gait.swinging_leg = 'right'
        self.gait.terminal = False
        self.gait.start_spring_forward_reward = 0
        self.gait.start = True
        self.gait.step_count = 0
        self.gait.step_flag = False
        self.gait.log = False
        self.gait.starting_position = 4
        self.gait.teaching = None

        machine = Machine(self.gait, states=states, send_event=True, initial='start')

        # Setup for Pure Selector Orchestration
        machine.add_transition('move', 'start', 'plant_leg', conditions='brain_transition_plant_leg', prepare=['log_iteration', 'set_mask'], before=['reset_iter_counter'], after=['set_mask'])
        machine.add_transition('move', 'lift_leg', 'plant_leg', conditions='brain_transition_plant_leg', unless='is_start', prepare=['log_iteration', 'set_mask'], before=['reset_iter_counter'], after=['set_mask'])
        machine.add_transition('move', 'plant_leg', 'switch_leg', conditions='is_swinging_leg_planted', prepare=['log_iteration', 'set_mask'], before=['reset_iter_counter', 'switch_legs', 'increment_step_count'], after=['set_mask'])

        #machine.add_transition('move', 'switch_leg', 'lift_leg', conditions='brain_transition_lift_leg', prepare=['log_iteration', 'set_mask'], before=['reset_iter_counter'])
        machine.add_transition('move', 'switch_leg', 'plant_leg', conditions='brain_transition_lift_leg', prepare=['log_iteration', 'set_mask'], before=['reset_iter_counter'], after=['set_mask'])

        machine.add_transition('reset', 'start', 'start', before=['reinstate', 'reset_step_counter', 'set_mask'])
        machine.add_transition('reset', 'plant_leg', 'start', before=['reinstate', 'reset_step_counter', 'set_mask'])
        machine.add_transition('reset', 'switch_leg', 'start', before=['reinstate', 'reset_step_counter', 'set_mask'])
        machine.add_transition('reset', 'lift_leg', 'start', before=['reinstate', 'reset_step_counter', 'set_mask'])

        #print('callback init')
        """
示例#32
0
class FSM(object):
    fsm_states = [
        'start', 'inicio', 'media_vuelta_inter_1', 'buscar_cubo', 'avanzar',
        'avanzar_cubo', 'encerrar_cubo', 'girar_linea', 'buscar_linea',
        'seguir_linea_1_f', 'media_vuelta_inicio', 'seguir_linea_1_b',
        'seguir_linea_2_f', 'girar_color_f', 'seguir_linea_3_f', 'soltar_cubo',
        'media_vuelta_fin', 'seguir_linea_3_b', 'girar_color_b',
        'seguir_linea_2_b'
    ]

    color = 'r'
    side_detected = False
    orientation = 0.0
    real_orientation = 0.0
    last_orientation = 0.0
    action = 'null'
    cmd_vel_select = None
    vel = Twist()
    ready = False

    def stop_robot(self):
        self.cmd_vel_select('raw_cmd_vel')
        self.vel.linear.x = 0.0
        self.vel.angular.z = 0.0
        line_pub.publish(False)
        cmd_vel_pub.publish(self.vel)

    def negro(self):
        return (self.color == 'k')

    def activar_seguidor_linea(self):
        self.action = 'following'
        print("siguiendo linea")
        self.cmd_vel_select('follow_line_cmd_vel')
        line_pub.publish(True)
        if self.state == 'avanzar':
            rospy.sleep(0.5)
            self.timeout()
            line_pub.publish(False)

    def girar(self):
        self.action = 'turning'
        self.cmd_vel_select('angle_cmd_vel')
        if (self.state == 'media_vuelta_inter_1') or (
                self.state
                == 'media_vuelta_inicio') or (self.state
                                              == 'media_vuelta_fin'):
            print('girando 180 grados')
            self.orientation = math.pi - 0.1
            angle_pub.publish(self.orientation)
        elif (self.state == 'girar_linea'):
            if self.negro():
                if self.side_detected == 1:
                    self.orientation = -90
                else:
                    self.orientation = 90
                angle_pub.publish(self.orientation)
                print('girando 105 grados')
            else:
                if self.side_detected == 1:
                    self.orientation = -180
                else:
                    self.orientation = 180
                angle_pub.publish(self.orientation)
                print('girando 75 grados')
        elif self.state == 'girar_color_f':
            if self.color == 'r':
                self.orientation = math.pi / 2
                angle_pub.publish(self.orientation)
                print('girar rojo')
            elif self.color == 'g':
                self.orientation = -math.pi / 2
                angle_pub.publish(self.orientation)
                print('girar verde')
            else:
                print('girar azul')
        elif self.state == 'girar_color_b':
            if self.color == 'r':
                self.orientation = -math.pi / 2
                angle_pub.publish(self.orientation)
                print('girar rojo b')
            elif self.color == 'g':
                self.orientation = math.pi / 2
                angle_pub.publish(self.orientation)
                print('girar verde b')
            else:
                print('girar azul b')

    def sondeo(self):
        self.last_orientation = self.real_orientation
        self.action = 'pre_searching'
        print('sondeando')
        self.cmd_vel_select('angle_cmd_vel')
        self.orientation = -math.pi / 2
        self.ready = False
        angle_pub.publish(self.orientation)
        while not self.ready:
            pass
        self.action = 'searching'
        found = False
        for i in range(10):
            self.orientation = math.pi / 10
            angle_pub.publish(self.orientation)
            rospy.sleep(0.75)
            if self.state != 'buscar_cubo':
                found = True
                break
        if not found:
            self.action = 'pre_searching'
            self.orientation = math.pi / 2
            self.ready = False
            angle_pub.publish(self.orientation)
            while not self.ready:
                pass
            self.no_cubo()

    def perseguir_cubo(self):
        if (self.real_orientation > self.last_orientation):
            self.side_detected = False
        else:
            self.side_detected = True
        self.action = 'hunting'
        print('persiguiendo cubo')
        self.cmd_vel_select('follow_color_cmd_vel')

    def cerrar_brazo(self):
        self.action = 'hugging'
        print('cerrando brazo')
        servo.min()
        rospy.sleep(1)
        self.cubo_encerrado()

    def encontrar_linea(self):
        self.action = 'returning'
        print('buscando linea')
        self.cmd_vel_select('raw_cmd_vel')
        self.vel.linear.x = 0.2
        self.vel.angular.z = 0.0
        cmd_vel_pub.publish(self.vel)

    def abrir_brazo(self):
        self.action = 'realising'
        print('abriendo_brazo')
        servo.max()
        rospy.sleep(1)
        self.cubo_liberado()

    def __init__(self):
        self.machine = Machine(model=self,
                               states=FSM.fsm_states,
                               initial='start')
        self.machine.add_transition('comenzar',
                                    'start',
                                    'inicio',
                                    after='activar_seguidor_linea')
        self.machine.add_transition('interseccion',
                                    'inicio',
                                    'media_vuelta_inter_1',
                                    after='girar')
        self.machine.add_transition('giro_completo',
                                    'media_vuelta_inter_1',
                                    'buscar_cubo',
                                    after='sondeo')
        self.machine.add_transition('no_cubo',
                                    'buscar_cubo',
                                    'avanzar',
                                    after='activar_seguidor_linea')
        self.machine.add_transition('timeout',
                                    'avanzar',
                                    'buscar_cubo',
                                    after='sondeo')
        self.machine.add_transition('interseccion',
                                    'avanzar',
                                    'media_vuelta_inicio',
                                    after='girar')
        self.machine.add_transition('cubo',
                                    'buscar_cubo',
                                    'avanzar_cubo',
                                    after='perseguir_cubo')
        self.machine.add_transition('cubo_atrapado',
                                    'avanzar_cubo',
                                    'encerrar_cubo',
                                    after='cerrar_brazo')
        self.machine.add_transition('cubo_encerrado',
                                    'encerrar_cubo',
                                    'girar_linea',
                                    after='girar')
        self.machine.add_transition('giro_completo',
                                    'girar_linea',
                                    'buscar_linea',
                                    after='encontrar_linea')
        self.machine.add_transition('linea',
                                    'buscar_linea',
                                    'seguir_linea_1_f',
                                    conditions='negro',
                                    after='activar_seguidor_linea')
        self.machine.add_transition('interseccion',
                                    'seguir_linea_1_f',
                                    'soltar_cubo',
                                    after='abrir_brazo')
        self.machine.add_transition('cubo_liberado',
                                    'soltar_cubo',
                                    'media_vuelta_inicio',
                                    conditions='negro',
                                    after='girar')
        self.machine.add_transition('giro_completo',
                                    'media_vuelta_inicio',
                                    'inicio',
                                    after='activar_seguidor_linea')
        self.machine.add_transition('linea',
                                    'buscar_linea',
                                    'seguir_linea_1_b',
                                    unless='negro',
                                    after='activar_seguidor_linea')
        self.machine.add_transition('interseccion',
                                    'seguir_linea_1_b',
                                    'seguir_linea_2_f',
                                    after='activar_seguidor_linea')
        self.machine.add_transition('interseccion',
                                    'seguir_linea_2_f',
                                    'girar_color_f',
                                    after='girar')
        self.machine.add_transition('giro_completo',
                                    'girar_color_f',
                                    'seguir_linea_3_f',
                                    after='activar_seguidor_linea')
        self.machine.add_transition('interseccion',
                                    'seguir_linea_3_f',
                                    'soltar_cubo',
                                    after='abrir_brazo')
        self.machine.add_transition('cubo_liberado',
                                    'soltar_cubo',
                                    'media_vuelta_fin',
                                    after='girar')
        self.machine.add_transition('giro_completo',
                                    'media_vuelta_fin',
                                    'seguir_linea_3_b',
                                    after='activar_seguidor_linea')
        self.machine.add_transition('interseccion',
                                    'seguir_linea_3_b',
                                    'girar_color_b',
                                    after='girar')
        self.machine.add_transition('giro_completo',
                                    'girar_color_b',
                                    'seguir_linea_2_b',
                                    after='activar_seguidor_linea')
        self.machine.add_transition('interseccion',
                                    'seguir_linea_2_b',
                                    'buscar_cubo',
                                    after='sondeo')
示例#33
0
        'source': 'initiated',
        'dest': 'initiated'
    },
    {
        'trigger': 'timeout',
        'source': ['initiated', 'proved', 'published'],
        'dest': 'cancelled'
    },
    {
        'trigger': 'receive_commitment_prove',
        'source': 'initiated',
        'unless': 'is_matched',
        'dest': 'proved'
    },
    {
        'trigger': 'receive_commitment_prove',
        'source': 'initiated',
        'conditions': 'is_matched',
        'dest': 'pending'
    },
    {
        'trigger': 'received_offer',
        'source': 'proved',
        'dest': 'published'
    },
]

fsm_offer = Machine(states=OFFER_STATES,
                    transitions=TRANSITIONS,
                    initial='created',
                    after_state_change='log_state')
    def __init__(self, state_based_provider):
        """
        :param state_based_provider: The state machine based provider.
        """
        self._polling_timer = None
        self._query_timer = None

        self._register_callback = None
        self._cancel_callback = None

        # self.on_registration_complete = None

        self._registration_error = None
        self._registration_result = None

        self._operations = {}

        self._request_response_provider = RequestResponseProvider(state_based_provider)

        states = [
            "disconnected",
            "initializing",
            "registering",
            "waiting_to_poll",
            "polling",
            "completed",
            "error",
            "cancelling",
        ]

        transitions = [
            {
                "trigger": "_trig_register",
                "source": "disconnected",
                "before": "_initialize_register",
                "dest": "initializing",
            },
            {
                "trigger": "_trig_register",
                "source": "error",
                "before": "_initialize_register",
                "dest": "initializing",
            },
            {"trigger": "_trig_register", "source": "registering", "dest": None},
            {
                "trigger": "_trig_send_register_request",
                "source": "initializing",
                "before": "_send_register_request",
                "dest": "registering",
            },
            {
                "trigger": "_trig_send_register_request",
                "source": "waiting_to_poll",
                "before": "_send_register_request",
                "dest": "registering",
            },
            {
                "trigger": "_trig_wait",
                "source": "registering",
                "dest": "waiting_to_poll",
                "after": "_wait_for_interval",
            },
            {"trigger": "_trig_wait", "source": "cancelling", "dest": None},
            {
                "trigger": "_trig_wait",
                "source": "polling",
                "dest": "waiting_to_poll",
                "after": "_wait_for_interval",
            },
            {
                "trigger": "_trig_poll",
                "source": "waiting_to_poll",
                "dest": "polling",
                "after": "_query_operation_status",
            },
            {"trigger": "_trig_poll", "source": "cancelling", "dest": None},
            {
                "trigger": "_trig_complete",
                "source": ["registering", "waiting_to_poll", "polling"],
                "dest": "completed",
                "after": "_call_complete",
            },
            {
                "trigger": "_trig_error",
                "source": ["registering", "waiting_to_poll", "polling"],
                "dest": "error",
                "after": "_call_error",
            },
            {"trigger": "_trig_error", "source": "cancelling", "dest": None},
            {
                "trigger": "_trig_cancel",
                "source": ["disconnected", "completed"],
                "dest": None,
                "after": "_inform_no_process",
            },
            {
                "trigger": "_trig_cancel",
                "source": ["initializing", "registering", "waiting_to_poll", "polling"],
                "dest": "cancelling",
                "after": "_call_cancel",
            },
        ]

        def _on_transition_complete(event_data):
            if not event_data.transition:
                dest = "[no transition]"
            else:
                dest = event_data.transition.dest
            logger.info(
                "Transition complete.  Trigger={}, Src={}, Dest={}, result={}, error{}".format(
                    event_data.event.name,
                    event_data.transition.source,
                    dest,
                    str(event_data.result),
                    str(event_data.error),
                )
            )

        self._state_machine = Machine(
            model=self,
            states=states,
            transitions=transitions,
            initial="disconnected",
            send_event=True,  # Use event_data structures to pass transition arguments
            finalize_event=_on_transition_complete,
            queued=True,
        )
示例#35
0
    def __init__(self):

        # Transitions between the states. 'trigger' is a function you can call
        # on the class object which triggers the transition to occur. The
        # functions listed in 'before' are functions within the class that will
        # run just after the transition takes place. Calling the function returns
        # the transition's success or lack thereof.
        transitions = [{
            'source': 'Standby',
            'dest': 'Drive',
            'trigger': 'setDrive',
            'after': 'drive',
            'conditions': ['connected']
        }, {
            'source': 'Standby',
            'dest': 'Arm',
            'trigger': 'setArm',
            'after': 'arm',
            'conditions': ['missionERTorEQP', 'connected']
        }, {
            'source': 'Standby',
            'dest': 'Drill',
            'trigger': 'setDrill',
            'after': 'drill',
            'conditions': ['missionSCI', 'connected']
        }, {
            'source': 'Standby',
            'dest': 'Auto',
            'trigger': 'setAuto',
            'after': 'auto',
            'conditions': ['missionAUT', 'connected']
        }, {
            'source': 'Drive',
            'dest': 'Standby',
            'trigger': 'setStandby',
            'after': 'standby'
        }, {
            'source': 'Drive',
            'dest': 'Arm',
            'trigger': 'setArm',
            'after': 'arm',
            'conditions': ['missionERTorEQP', 'connected']
        }, {
            'source': 'Drive',
            'dest': 'Drill',
            'trigger': 'setDrill',
            'after': 'drill',
            'conditions': ['missionSCI', 'connected']
        }, {
            'source': 'Arm',
            'dest': 'Standby',
            'trigger': 'setStandby',
            'after': 'standby'
        }, {
            'source': 'Arm',
            'dest': 'Drive',
            'trigger': 'setDrive',
            'after': 'drive',
            'conditions': ['missionERTorEQP', 'connected']
        }, {
            'source': 'Drill',
            'dest': 'Standby',
            'trigger': 'setStandby',
            'after': 'standby'
        }, {
            'source': 'Drill',
            'dest': 'Drive',
            'trigger': 'setDrive',
            'after': 'drive',
            'conditions': ['missionSCI', 'connected']
        }, {
            'source': 'Auto',
            'dest': 'Standby',
            'trigger': 'setStandby',
            'after': 'standby'
        }]

        # Initialize the state machine
        self.mach = Machine(model=self,
                            states=RoverStateMachine.states,
                            initial='Standby',
                            transitions=transitions,
                            ignore_invalid_triggers=True)

        # Initialise the global state parameter
        self.setMode('Standby')
示例#36
0
import logging
from transitions import logger, Machine, State

logger.setLevel(logging.INFO)

states = ['liquid', 'solid', 'gas', 'plasma']

transitions = [{
    'trigger': 'melt',
    'source': 'solid',
    'dest': 'liquid'
}, {
    'trigger': 'evaporate',
    'source': 'liquid',
    'dest': 'gas'
}, {
    'trigger': 'ionize',
    'source': 'gas',
    'dest': 'plasma'
}]

# Business as usual
machine = Machine(states=states, transitions=transitions, initial='solid')
示例#37
0
class CameraController(object):
    """
    Class containing the state machine to manage triggers to garage
    door opening and closing, and scheduling recording. Uses
    transitions library to implement a Finite State Machine (FSM).

    The states are:

    on_hold (initial):
        The system is dormant waiting for door to be opene
    prepare:
        The system is activated waiting for certain time to start recording
    recording:
        The system camera is recording video

    The events:

    door_open:
        Triggered when the door sensor goes from closed to open
    door_closed:
        Triggered when the door sensor goes from open to closed
    prepare_finished:
        Triggered when preparations to record video are completed
    cancel_requested:
        Triggered to cancel ongoing recording or its preparations

    """

    states = ('on_hold', 'prepare', 'record')

    def __init__(self):

        # Define the transitions.Machine state machine.
        # ignore_invalid_triggers=True to avoid exceptions
        self.machine = Machine(model=self,
                               states=CameraController.states,
                               initial='on_hold',
                               ignore_invalid_triggers=True)

        # transition on door opening while on_hold
        self.machine.add_transition(trigger='door_open',
                                    source='on_hold',
                                    dest='prepare')
        # do nothing on door closing while on_hold, defined to hook logging
        self.machine.add_transition(trigger='door_closed',
                                    source='on_hold',
                                    dest='on_hold')

        # transtion from recording preparations to record
        self.machine.add_transition(trigger='prepare_finished',
                                    source='prepare',
                                    dest='record')

        # transitions happening when door closes while recording or preparing
        self.machine.add_transition(trigger='door_closed',
                                    source='record',
                                    dest='on_hold')
        self.machine.add_transition(trigger='door_closed',
                                    source='prepare',
                                    dest='on_hold',
                                    before=['_log_preparation_cancelled'])

        # transitions for cancellation requested
        self.machine.add_transition(trigger='cancel_requested',
                                    source='prepare',
                                    dest='on_hold',
                                    before=['_log_preparation_cancelled'])
        self.machine.add_transition(trigger='cancel_requested',
                                    source='record',
                                    dest='on_hold',
                                    before=['_log_record_cancelled'])

        # not documented in transitions API but it is possible to add
        # a prepare callback when an event is triggered on a state accepting it
        # it will
        for event in self.machine.events:
            if hasattr(self, 'on_event_' + event):
                self.machine.events[event].add_callback(
                    'prepare', 'on_event_' + event)

    def on_event_door_open(self):
        """
        Callback when door_open event happens in a valid state
        """
        logger.info("Door openened", extra=dict(event='door_open'))

    def on_event_door_closed(self):
        """
        Callback when door_closed event happens in a valid state
        """
        logger.info("Door closed", extra=dict(event='door_closed'))

    def on_event_cancel_requested(self):
        """
        Callback when cancel_requested event happens in a valid state
        """
        logger.info("Cancel requested", extra=dict(event='cancel_requested'))

    def on_event_prepare_finished(self):
        """
        Callback when prepare_finished event happens in a valid state
        """
        logger.info("Preparations finished",
                    extra=dict(event='prepare_finished'))

    def on_enter_prepare(self):
        """
        Callback happening when the `prepare` state is entered. Calls
        the abstract method `prepare_recording` to perform whatever
        task needed before starting video recording, for example wait
        for some time until delay is finally open or perform any other
        checks
        """
        logger.info("Preparing to record",
                    extra=dict(event='record_prepare_start'))
        self.prepare_recording()

    def on_enter_record(self):
        """
        Callback happening when the `record` state is entered. Calls
        the abstract method `start_recording` to start the recording of
        video
        """
        logger.info("Recording started", extra=dict(event='record_start'))
        self.start_recording()

    def on_exit_record(self):
        """
        Callback happening when the `record` state is exited. Calls
        the abstract method `stop_recording` to stop the recording of
        video
        """
        logger.info("Recording finished", extra=dict(event='record_end'))
        self.stop_recording()

    def prepare_recording(self):
        """
        Abstract method for defining preparations for recording
        after the preparations are done, call `self.prepare_finished()`
        to move to recording state
        """
        raise NotImplementedError()

    def start_recording(self):
        """
        Abstract method to execute start of video recording
        """
        raise NotImplementedError()

    def stop_recording(self):
        """
        Abstract method to manage stop of video recording
        """
        raise NotImplementedError()

    def _log_preparation_cancelled(self):
        """
        Logs that preparations for recording have been cancelled
        """
        logger.info("Preparations for recording cancelled",
                    extra=dict(event='record_prepare_cancel'))

    def _log_record_cancelled(self):
        """
        Logs that recording recording has been cancelled
        """
        logger.info("Recording cancelled", extra=dict(event='record_cancel'))
示例#38
0
 def test_ignore_invalid_triggers(self):
     a_state = State('A')
     transitions = [['a_to_b', 'A', 'B']]
     # Exception is triggered by default
     b_state = State('B')
     m1 = Machine(None, states=[a_state, b_state], transitions=transitions,
                  initial='B')
     with self.assertRaises(MachineError):
         m1.a_to_b()
     # Exception is suppressed, so this passes
     b_state = State('B', ignore_invalid_triggers=True)
     m2 = Machine(None, states=[a_state, b_state], transitions=transitions,
                  initial='B')
     m2.a_to_b()
     # Set for some states but not others
     new_states = ['C', 'D']
     m1.add_states(new_states, ignore_invalid_triggers=True)
     m1.to_D()
     m1.a_to_b()  # passes because exception suppressed for D
     m1.to_B()
     with self.assertRaises(MachineError):
         m1.a_to_b()
     # Set at machine level
     m3 = Machine(None, states=[a_state, b_state], transitions=transitions,
                  initial='B', ignore_invalid_triggers=True)
     m3.a_to_b()
示例#39
0
    def test_ordered_transitions(self):
        states = ['beginning', 'middle', 'end']
        m = Machine(None, states)
        m.add_ordered_transitions()
        self.assertEquals(m.state, 'initial')
        m.next_state()
        self.assertEquals(m.state, 'beginning')
        m.next_state()
        m.next_state()
        self.assertEquals(m.state, 'end')
        m.next_state()
        self.assertEquals(m.state, 'initial')

        # Include initial state in loop
        m = Machine(None, states)
        m.add_ordered_transitions(loop_includes_initial=False)
        m.to_end()
        m.next_state()
        self.assertEquals(m.state, 'beginning')

        # Test user-determined sequence and trigger name
        m = Machine(None, states, initial='beginning')
        m.add_ordered_transitions(['end', 'beginning'], trigger='advance')
        m.advance()
        self.assertEquals(m.state, 'end')
        m.advance()
        self.assertEquals(m.state, 'beginning')

        # Via init argument
        m = Machine(
            None, states, initial='beginning', ordered_transitions=True)
        m.next_state()
        self.assertEquals(m.state, 'middle')
示例#40
0
    def test___getattr___and_identify_callback(self):
        m = Machine(Stuff(), states=['A', 'B', 'C'], initial='A')
        m.add_transition('move', 'A', 'B')
        m.add_transition('move', 'B', 'C')

        callback = m.__getattr__('before_move')
        self.assertTrue(callable(callback))

        with self.assertRaises(MachineError):
            m.__getattr__('before_no_such_transition')

        with self.assertRaises(MachineError):
            m.__getattr__('before_no_such_transition')

        with self.assertRaises(AttributeError):
            m.__getattr__('__no_such_method__')

        with self.assertRaises(AttributeError):
            m.__getattr__('')

        type, target = m._identify_callback('on_exit_foobar')
        self.assertEqual(type, 'on_exit')
        self.assertEqual(target, 'foobar')

        type, target = m._identify_callback('on_exitfoobar')
        self.assertEqual(type, None)
        self.assertEqual(target, None)

        type, target = m._identify_callback('notacallback_foobar')
        self.assertEqual(type, None)
        self.assertEqual(target, None)

        type, target = m._identify_callback('totallyinvalid')
        self.assertEqual(type, None)
        self.assertEqual(target, None)

        type, target = m._identify_callback('before__foobar')
        self.assertEqual(type, 'before')
        self.assertEqual(target, '_foobar')

        type, target = m._identify_callback('before__this__user__likes__underscores___')
        self.assertEqual(type, 'before')
        self.assertEqual(target, '_this__user__likes__underscores___')

        type, target = m._identify_callback('before_stuff')
        self.assertEqual(type, 'before')
        self.assertEqual(target, 'stuff')

        type, target = m._identify_callback('before_trailing_underscore_')
        self.assertEqual(type, 'before')
        self.assertEqual(target, 'trailing_underscore_')

        type, target = m._identify_callback('before_')
        self.assertIs(type, None)
        self.assertIs(target, None)

        type, target = m._identify_callback('__')
        self.assertIs(type, None)
        self.assertIs(target, None)

        type, target = m._identify_callback('')
        self.assertIs(type, None)
        self.assertIs(target, None)
示例#41
0
class Devices(QtCore.QObject):

    States = [
        'start', 'temptUp', 'temptDown', 'control', 'stable', 'measure',
        'stop', 'idle', 'undefined'
    ]
    Triggers = [
        'startAutoStep', 'timerTick', 'nextTemptPoint', 'nextTemptPoint'
        'startControl', 'achieveSteady', 'startMeasure', 'suspendAutoControl',
        'finishAll', 'forceStop', 'uindefinedOccur'
    ]

    # when auto control, the control flow enter a new state
    # emit([stateName])
    controlStateChangedSignal = QtCore.pyqtSignal(list)
    # when auto control, the relay device status changed
    # emit([err, st])
    ryStatusUpdateSignal = QtCore.pyqtSignal(list)
    # when the temperature parameters are updated into the T-C board
    # emit(err, param])
    tpParamUpdateSignal = QtCore.pyqtSignal(list)
    # in every tick, the temperature and power are updated from the T-C board
    # emit([errT, tp, errP, p])
    tpUpdateTickSignal = QtCore.pyqtSignal(list)
    # check error in every tick
    tpErrorOccurTickSignal = QtCore.pyqtSignal(list)

    @typeassert(name=str)
    def __init__(self, name='Device', parent=None):
        super(Devices, self).__init__(parent)
        # tempt parameters
        # relay device
        self.ryDevice = RelayDevice()
        # temperature control device
        self.tpDevice = TemptDevice()
        # whether perform auto control
        self.autoControl = False
        # temperature list
        self.temptPointList = []
        # current temperature control state
        self.currentTemptPointState = TemptPointStruct()
        # threading lock
        self.stepLocker = threading.Lock()
        # some parameters
        self.thrParam = ThresholdParamStruct()

        # state machine
        self.name = name
        self._machine = None
        self._init_machine()

        self._timer = QtCore.QTimer(self)
        self._timer.timeout.connect(self._timer_tick)

    def _init_machine(self):
        self._machine = Machine(model=self,
                                states=Devices.States,
                                initial=Devices.States[7],
                                ignore_invalid_triggers=True)

        # States.idle
        # -> States.start
        # -> States.stop
        self._machine.on_enter_idle('_enter_idle')
        self._machine.on_exit_idle('_exit_idle')
        self._machine.add_transition('internal',
                                     source='idle',
                                     dest=None,
                                     after='_idle_tick')
        self._machine.add_transition(trigger='startAutoStep',
                                     source='idle',
                                     dest='start')

        # States.start
        # -> States.control
        # -> States.temptUp
        # -> States.temptDown
        self._machine.on_enter_start('_enter_start')
        self._machine.on_exit_start('_exit_start')
        self._machine.add_transition('internal',
                                     source='start',
                                     dest=None,
                                     after='_start_tick')
        self._machine.add_transition(trigger='startControl',
                                     source='start',
                                     dest='control')
        self._machine.add_transition(trigger='nextTemptPoint',
                                     source='start',
                                     dest='temptDown',
                                     conditions='_next_point_down')
        self._machine.add_transition(trigger='nextTemptPoint',
                                     source='start',
                                     dest='temptUp')

        # States.temptUp
        # -> States.control
        self._machine.on_enter_temptUp('_enter_temptUp')
        self._machine.on_exit_temptUp('_exit_temptUp')
        self._machine.add_transition('internal',
                                     source='temptUp',
                                     dest=None,
                                     after='_temptUp_tick')
        self._machine.add_transition(trigger='startControl',
                                     source='temptUp',
                                     dest='control')

        # States.temptDown
        # -> States.control
        self._machine.on_enter_temptDown('_enter_temptDown')
        self._machine.on_exit_temptDown('_exit_temptDown')
        self._machine.add_transition('internal',
                                     source='temptDown',
                                     dest=None,
                                     after='_temptDown_tick')
        self._machine.add_transition(trigger='startControl',
                                     source='temptDown',
                                     dest='control')

        # States.control
        # -> States.stable
        self._machine.on_enter_control('_enter_control')
        self._machine.on_exit_control('_exit_control')
        self._machine.add_transition('internal',
                                     source='control',
                                     dest=None,
                                     after='_control_tick')
        self._machine.add_transition(trigger='achieveSteady',
                                     source='control',
                                     dest='stable')

        # States.stable
        # -> States.measure
        self._machine.on_enter_stable('_enter_stable')
        self._machine.on_exit_stable('_exit_stable')
        self._machine.add_transition('internal',
                                     source='stable',
                                     dest=None,
                                     after='_stable_tick')
        self._machine.add_transition(trigger='startMeasure',
                                     source='stable',
                                     dest='measure')

        # States.measure
        # -> States.temptUp
        # -> States.temptDown
        self._machine.on_enter_measure('_enter_measure')
        self._machine.on_exit_measure('_exit_measure')
        self._machine.add_transition('internal',
                                     source='measure',
                                     dest=None,
                                     after='_measure_tick')
        self._machine.add_transition(trigger='nextTemptPoint',
                                     source='measure',
                                     dest='temptDown',
                                     conditions='_next_point_down')
        self._machine.add_transition(trigger='nextTemptPoint',
                                     source='measure',
                                     dest='temptUp')
        self._machine.add_transition(trigger='finishAll',
                                     source='measure',
                                     dest='stop',
                                     conditions='_stop_when_finished')
        self._machine.add_transition(trigger='finishAll',
                                     source='measure',
                                     dest='idle')

        # States.stop
        self._machine.on_enter_stop('_enter_stop')
        self._machine.on_exit_stop('_exit_stop')
        self._machine.add_transition('internal',
                                     source='stop',
                                     dest=None,
                                     after='_stop_tick')

        # suspend the auto control
        self._machine.add_transition(trigger='suspendAutoControl',
                                     source=[
                                         'start', 'temptUp', 'temptDown',
                                         'control', 'stable', 'measure'
                                     ],
                                     dest='idle')

        # force to stop
        self._machine.add_transition(trigger='forceStop',
                                     source='*',
                                     dest='stop')

    # States idle
    def _enter_idle(self):
        print('enter the idle state')
        # clear state time count
        self.currentTemptPointState.stateCount = 0
        # state change signal
        self.controlStateChangedSignal.emit(['idle'])

    def _exit_idle(self):
        print('exit the idle state')
        pass

    def _idle_tick(self, sec):
        print('idle tick')
        # if start auto control as well as there tpPoint in the list
        if self.autoControl is True and len(self.temptPointList) != 0:
            self._machine.startAutoStep()
        pass

    # States start
    def _enter_start(self):
        print('enter the start state')
        # clear state time count
        self.currentTemptPointState.stateCount = 0
        # state change signal
        self.controlStateChangedSignal.emit(['start'])

    def _exit_start(self):
        print('exit the start state')
        pass

    @typeassert(sec=float)
    def _start_tick(self, sec):
        print('start tick')
        for pt in self.temptPointList:
            if pt.finished == False:
                self.currentTemptPointState = pt
        # if all the points have been measured
        # which means the finished of all the point is True
        # then suspend the auto control and go to idle
        if self.currentTemptPointState.finished == True:
            self._machine.suspendAutoControl()
            return

        # else start the auto control flow
        if abs(self.currentTemptPointState.stateTemp -
               self.tpDevice.temperatures[-1]) < self.thrParam.controlTemptThr:
            self._machine.startControl()
        else:
            self._machine.nextTemptPoint()

    def _next_point_down(self):
        if self.currentTemptPointState.stateTemp(
        ) < self.tpDevice.temperatures[-1]:
            return True
        else:
            return False

    # States temptUp
    def _enter_temptUp(self):
        print('enter tempUp')
        # update relay status
        rySt = [False] * 16
        rySt[RelayProtocol.CmdRelay.Elect] = True
        rySt[RelayProtocol.CmdRelay.MainHeat] = True
        rySt[RelayProtocol.CmdRelay.Cool] = False
        rySt[RelayProtocol.CmdRelay.Circle] = True
        errR, st = self.ryDevice.updatestatustodevice(rySt, True)
        self.ryStatusUpdateSignal.emit([errR, st])
        # update T-C parameters
        errT, prmm = self.tpDevice.update_param_to_device(
            self.currentTemptPointState.paramM, True)
        self.tpParamUpdateSignal.emit(errT, self.currentTemptPointState.paramM)
        # clear state time count
        self.currentTemptPointState.stateCount = 0
        # state change signal
        self.controlStateChangedSignal.emit(['temptUp'])

    def _exit_temptUp(self):
        print('exit the temptUp state')
        pass

    @typeassert(sec=float)
    def _temptUp_tick(self, sec):
        print('temptUp tick: %d' % sec)
        # error check

        # judge enter control
        if self.tpDevice.temperatures[
                -1] > self.currentTemptPointState.stateTemp() - 0.1:
            self._machine.startControl()

    # States temptDown
    def _enter_temptDown(self):
        print('enter the temptDown state')
        # update relay status
        rySt = [False] * 16
        rySt[RelayProtocol.CmdRelay.Elect] = True
        rySt[RelayProtocol.CmdRelay.MainHeat] = False
        rySt[RelayProtocol.CmdRelay.Cool] = True
        rySt[RelayProtocol.CmdRelay.Circle] = True
        errR, st = self.ryDevice.updatestatustodevice(rySt, True)
        self.ryStatusUpdateSignal.emit([errR, st])
        # update T-C parameters
        errT = self.tpDevice.updateparamtodevice(
            self.currentTemptPointState.paramM, True)
        self.tpParamUpdateSignal.emit(errT, self.currentTemptPointState.paramM)
        # clear state time count
        self.currentTemptPointState.stateCount = 0
        # state change signal
        self.controlStateChangedSignal.emit(['temptDown'])

    def _exit_temptDown(self):
        print('exit the temptDown state')
        pass

    @typeassert(sec=float)
    def _temptDown_tick(self, sec):
        print('temptDown tick: %d' % sec)
        # error check

        # judge enter control
        if self.tpDevice.temperatures[
                -1] < self.currentTemptPointState.stateTemp() + 0.1:
            self._machine.startControl()

    # States control
    def _enter_control(self):
        print('enter the control state')
        rySt = [False] * 16
        rySt[RelayProtocol.CmdRelay.Elect] = True
        rySt[RelayProtocol.CmdRelay.MainHeat] = False
        rySt[RelayProtocol.CmdRelay.Cool] = True
        rySt[RelayProtocol.CmdRelay.Circle] = True
        errR, st = self.ryDevice.updatestatustodevice(rySt, True)
        self.ryStatusUpdateSignal.emit([errR, st])
        # clear state time count
        self.currentTemptPointState.stateCount = 0
        # state change signal
        self.controlStateChangedSignal.emit(['control'])

    def _exit_control(self):
        print('exit the control state')
        pass

    @typeassert(sec=float)
    def _control_tick(self, sec):
        print('control tick: %d' % sec)
        # error check

        # if the fluctuation satisfy the criteria, then go to stable
        if self.tpDevice.check_fluc_cnt(
                self.thrParam.steadyTimeSec * 1000 /
                self.thrParam.tpUpdateInterval, self.thrParam.flucValue):
            self._machine.achieveSteady()

    # States stable
    def _enter_stable(self):
        print('enter the stable state')
        rySt = [False] * 16
        rySt[RelayProtocol.CmdRelay.Elect] = True
        rySt[RelayProtocol.CmdRelay.MainHeat] = False
        rySt[RelayProtocol.CmdRelay.Cool] = True
        rySt[RelayProtocol.CmdRelay.Circle] = True
        errR, st = self.ryDevice.updatestatustodevice(rySt, True)
        self.ryStatusUpdateSignal.emit([errR, st])
        # clear state time count
        self.currentTemptPointState.stateCount = 0
        # state change signal
        self.controlStateChangedSignal.emit(['stable'])

    def _exit_stable(self):
        print('exit the stable state')
        pass

    @typeassert(sec=float)
    def _stable_tick(self, sec):
        print('stable tick: %d' % sec)
        # enter this state more than xx time
        # then check the fluctuation again
        if self.currentTemptPointState.stateCount > \
                self.thrParam.bridgeSteadyTimeSec * 1000 /self.thrParam.tpUpdateInterval and\
                self.tpDevice.check_fluc_cnt(self.thrParam.steadyTimeSec * 1000  /
                                             self.thrParam.tpUpdateInterval,self.thrParam.flucValue):
            self._machine.startMeasure()

    # States measure
    def _enter_measure(self):
        print('enter the measure state')
        # clear state time count
        self.currentTemptPointState.stateCount = 0
        # state change signal
        self.controlStateChangedSignal.emit(['measure'])

    def _exit_measure(self):
        print('exit the measure state')
        pass

    @typeassert(sec=float)
    def _measure_tick(self, sec):
        print('measure tick: %d' % sec)
        # error check

        #
        self.temptPointList[
            self.currentTemptPointState.temptPointIndex].finished = True
        # search in the list, and find whether there is still any point which is unfinished
        for pt in self.temptPointList:
            if pt.finished == False:
                self.currentTemptPointState = pt

        # all the point is finished
        if self.currentTemptPointState.finished == True:
            self._machine.finishAll()
        else:
            self._machine.nextTemptPoint()

    def _stop_when_finished(self):
        return self.thrParam.shutDownComputer

    # States stop
    def _enter_stop(self):
        print('enter the stop state')
        # clear state time count
        self.currentTemptPointState.stateCount = 0
        # state change signal
        self.controlStateChangedSignal.emit(['stop'])

    def _exit_stop(self):
        print('exit the stop state')
        pass

    @typeassert(sec=float)
    def _stop_tick(self, sec):
        print('stop tick: %d' % sec)
        pass

    def start_timer(self):
        self._timer.start(4000)

    def _timer_tick(self):
        """
        device timer tick function
        :return:
        """
        print('_timer_tick')
        # read temperature and power value
        errT, valT = self.tpDevice.get_temptshow(True)
        errP, valP = self.tpDevice.get_powershow(True)
        errF, fluc = self.tpDevice.get_fluc_cnt_orless(
            int(5000 / self.thrParam.tpUpdateInterval))
        self.tpUpdateTickSignal.emit([errT, valT, errP, valP, errF, fluc])
        # state count
        # bug : wghou 20190213 overflow
        self.currentTemptPointState.stateCount += 1
示例#42
0
    def __init__(self, session: UssdSession, user: User):
        self.session = session
        self.user = user
        Machine.__init__(self,
                         model=self,
                         states=self.states,
                         initial=session.state)

        # event: initial_language_selection transitions
        initial_language_selection_transitions = [{
            'trigger':
            'feed_char',
            'source':
            'initial_language_selection',
            'dest':
            'initial_pin_entry',
            'after':
            'initial_change_preferred_language_to_en',
            'conditions':
            'menu_one_selected'
        }, {
            'trigger':
            'feed_char',
            'source':
            'initial_language_selection',
            'dest':
            'initial_pin_entry',
            'after':
            'initial_change_preferred_language_to_sw',
            'conditions':
            'menu_two_selected'
        }, {
            'trigger':
            'feed_char',
            'source':
            'initial_language_selection',
            'dest':
            'help',
            'conditions':
            'menu_three_selected'
        }, {
            'trigger':
            'feed_char',
            'source':
            'initial_language_selection',
            'dest':
            'exit_invalid_menu_option'
        }]
        self.add_transitions(initial_language_selection_transitions)

        # event: initial_pin_entry transitions
        initial_pin_entry_transitions = [{
            'trigger': 'feed_char',
            'source': 'initial_pin_entry',
            'dest': 'initial_pin_confirmation',
            'after': 'save_pin_data',
            'conditions': 'is_valid_pin'
        }, {
            'trigger': 'feed_char',
            'source': 'initial_pin_entry',
            'dest': 'exit_invalid_pin'
        }]
        self.add_transitions(initial_pin_entry_transitions)

        # event: initial_pin_confirmation transitions
        initial_pin_confirmation_transitions = [{
            'trigger':
            'feed_char',
            'source':
            'initial_pin_confirmation',
            'dest':
            'exit_account_creation_prompt',
            'unless':
            'is_admin_pin_reset',
            'after': [
                'complete_initial_pin_change', 'set_phone_as_verified',
                'send_terms_to_user_if_required',
                'process_account_creation_request'
            ],
            'conditions': ['is_ussd_signup', 'new_pins_match']
        }, {
            'trigger':
            'feed_char',
            'source':
            'initial_pin_confirmation',
            'dest':
            'start',
            'conditions': ['new_pins_match', 'is_admin_pin_reset'],
            'after':
            'complete_initial_pin_change'
        }, {
            'trigger':
            'feed_char',
            'source':
            'initial_pin_confirmation',
            'dest':
            'start',
            'after': [
                'complete_initial_pin_change', 'set_phone_as_verified',
                'send_terms_to_user_if_required'
            ],
            'conditions':
            'new_pins_match'
        }, {
            'trigger': 'feed_char',
            'source': 'initial_pin_confirmation',
            'dest': 'exit_pin_mismatch'
        }]
        self.add_transitions(initial_pin_confirmation_transitions)

        # event: start transitions
        start_transitions = [{
            'trigger': 'feed_char',
            'source': 'start',
            'dest': 'send_enter_recipient',
            'conditions': 'menu_one_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'start',
            'dest': 'account_management',
            'conditions': 'menu_two_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'start',
            'dest': 'directory_listing',
            'conditions': 'menu_three_selected',
            'after': 'store_transfer_usage'
        }, {
            'trigger': 'feed_char',
            'source': 'start',
            'dest': 'exchange_token',
            'conditions': 'menu_four_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'start',
            'dest': 'help',
            'conditions': 'menu_five_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'start',
            'dest': 'exit_invalid_menu_option'
        }]
        self.add_transitions(start_transitions)

        # event: send_enter_recipient transitions
        send_enter_recipient_transitions = [{
            'trigger': 'feed_char',
            'source': 'send_enter_recipient',
            'dest': 'send_token_amount',
            'conditions': 'is_user',
            'after': 'save_recipient_phone'
        }, {
            'trigger': 'feed_char',
            'source': 'send_enter_recipient',
            'dest': 'exit_use_exchange_menu',
            'conditions': 'is_token_agent'
        }, {
            'trigger':
            'feed_char',
            'source':
            'send_enter_recipient',
            'dest':
            'exit_invalid_recipient',
            'after':
            'upsell_unregistered_recipient'
        }]
        self.add_transitions(send_enter_recipient_transitions)

        # event: send_token_amount transitions
        send_token_amount_transitions = [
            {
                'trigger': 'feed_char',
                'source': 'send_token_amount',
                'dest': 'send_token_pin_authorization',
                'conditions': 'is_valid_token_amount',
                'after': ['save_transaction_amount', 'store_transfer_usage']
            },
            {
                'trigger': 'feed_char',
                'source': 'send_token_amount',
                'dest': 'exit_invalid_input'
            },
        ]
        self.add_transitions(send_token_amount_transitions)

        # event: send_token_reason transitions
        send_token_reason_transitions = [
            {
                'trigger': 'feed_char',
                'source': 'send_token_reason',
                'dest': 'send_token_reason_other',
                'conditions': 'menu_nine_selected',
                'after': 'set_usage_menu_number'
            },
            {
                'trigger': 'feed_char',
                'source': 'send_token_reason',
                'dest': 'send_token_pin_authorization',
                'after': 'save_transaction_reason',
                'conditions': 'is_valid_menu_option'
            },
            {
                'trigger': 'feed_char',
                'source': 'send_token_reason',
                'dest': 'exit_invalid_menu_option'
            },
            {
                'trigger': 'feed_char',
                'source': 'send_token_reason_other',
                'dest': 'send_token_reason_other',
                'conditions': 'menu_nine_selected',
                'after': 'set_usage_menu_number'
            },
            {
                'trigger': 'feed_char',
                'source': 'send_token_reason_other',
                'dest': 'send_token_reason_other',
                'conditions': 'menu_ten_selected',
                'after': 'set_usage_menu_number'
            },
            {
                'trigger': 'feed_char',
                'source': 'send_token_reason_other',
                'dest': 'send_token_pin_authorization',
                'after': 'save_transaction_reason',
                'conditions': 'is_valid_other_menu_option'
            },
            {
                'trigger': 'feed_char',
                'source': 'send_token_reason_other',
                'dest': 'exit_invalid_menu_option'
            },
        ]
        self.add_transitions(send_token_reason_transitions)

        directory_listing_transitions = [
            {
                'trigger': 'feed_char',
                'source': 'directory_listing',
                'dest': 'directory_listing_other',
                'conditions': 'menu_nine_selected',
                'after': 'set_usage_menu_number'
            },
            {
                'trigger': 'feed_char',
                'source': 'directory_listing',
                'dest': 'complete',
                'after': 'send_directory_listing',
                'conditions': 'is_valid_menu_option'
            },
            {
                'trigger': 'feed_char',
                'source': 'directory_listing',
                'dest': 'exit_invalid_menu_option'
            },
            {
                'trigger': 'feed_char',
                'source': 'directory_listing_other',
                'dest': 'directory_listing_other',
                'conditions': 'menu_nine_selected',
                'after': 'set_usage_menu_number'
            },
            {
                'trigger': 'feed_char',
                'source': 'directory_listing_other',
                'dest': 'directory_listing_other',
                'conditions': 'menu_ten_selected',
                'after': 'set_usage_menu_number'
            },
            {
                'trigger': 'feed_char',
                'source': 'directory_listing_other',
                'dest': 'complete',
                'after': 'send_directory_listing',
                'conditions': 'is_valid_other_menu_option'
            },
            {
                'trigger': 'feed_char',
                'source': 'directory_listing_other',
                'dest': 'exit_invalid_menu_option'
            },
        ]
        self.add_transitions(directory_listing_transitions)

        # event: send_token_pin_authorization transitions
        send_token_pin_authorization_transitions = [{
            'trigger':
            'feed_char',
            'source':
            'send_token_pin_authorization',
            'dest':
            'exit_successful_send_token',
            'conditions':
            'is_authorized_pin',
            'after':
            'process_send_token_request'
        }, {
            'trigger':
            'feed_char',
            'source':
            'send_token_pin_authorization',
            'dest':
            'exit_pin_blocked',
            'conditions':
            'is_blocked_pin'
        }]
        self.add_transitions(send_token_pin_authorization_transitions)

        # event: account_management transitions
        account_management_transitions = [{
            'trigger': 'feed_char',
            'source': 'account_management',
            'dest': 'user_profile',
            'conditions': 'menu_one_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'account_management',
            'dest': 'choose_language',
            'conditions': 'menu_two_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'account_management',
            'dest': 'balance_inquiry_pin_authorization',
            'conditions': 'menu_three_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'account_management',
            'dest': 'current_pin',
            'conditions': 'menu_four_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'account_management',
            'dest': 'opt_out_of_market_place_pin_authorization',
            'conditions': 'menu_five_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'account_management',
            'dest': 'exit_invalid_menu_option'
        }]
        self.add_transitions(account_management_transitions)

        # event: user_profile transitions
        user_profile_transitions = [{
            'trigger': 'feed_char',
            'source': 'user_profile',
            'dest': 'first_name_entry',
            'conditions': 'menu_one_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'user_profile',
            'dest': 'gender_entry',
            'conditions': 'menu_two_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'user_profile',
            'dest': 'location_entry',
            'conditions': 'menu_three_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'user_profile',
            'dest': 'change_my_business_prompt',
            'conditions': 'menu_four_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'user_profile',
            'dest': 'view_profile_pin_authorization',
            'conditions': 'menu_five_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'user_profile',
            'dest': 'exit_invalid_menu_option'
        }]
        self.add_transitions(user_profile_transitions)

        # event: choose_language transition
        choose_language_transitions = [{
            'trigger': 'feed_char',
            'source': 'choose_language',
            'dest': 'complete',
            'after': 'change_preferred_language_to_en',
            'conditions': 'menu_one_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'choose_language',
            'dest': 'complete',
            'after': 'change_preferred_language_to_sw',
            'conditions': 'menu_two_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'choose_language',
            'dest': 'exit_invalid_menu_option'
        }]
        self.add_transitions(choose_language_transitions)

        # event: balance_inquiry_pin_authorization transitions
        balance_inquiry_pin_authorization_transitions = [{
            'trigger':
            'feed_char',
            'source':
            'balance_inquiry_pin_authorization',
            'dest':
            'complete',
            'conditions':
            'is_authorized_pin',
            'after':
            'inquire_balance'
        }, {
            'trigger':
            'feed_char',
            'source':
            'balance_inquiry_pin_authorization',
            'dest':
            'exit_pin_blocked',
            'conditions':
            'is_blocked_pin'
        }]
        self.add_transitions(balance_inquiry_pin_authorization_transitions)

        # event: current_pin transitions
        current_pin_transitions = [{
            'trigger': 'feed_char',
            'source': 'current_pin',
            'dest': 'new_pin',
            'conditions': 'is_authorized_pin'
        }, {
            'trigger': 'feed_char',
            'source': 'current_pin',
            'dest': 'exit_pin_blocked',
            'conditions': 'is_blocked_pin'
        }]
        self.add_transitions(current_pin_transitions)

        # event: new_pin transitions
        new_pin_transitions = [{
            'trigger': 'feed_char',
            'source': 'new_pin',
            'dest': 'new_pin_confirmation',
            'after': 'save_pin_data',
            'conditions': 'is_valid_new_pin'
        }, {
            'trigger': 'feed_char',
            'source': 'new_pin',
            'dest': 'exit_invalid_pin'
        }]
        self.add_transitions(new_pin_transitions)

        # event: new_pin_confirmation transitions
        new_pin_confirmation = [{
            'trigger': 'feed_char',
            'source': 'new_pin_confirmation',
            'dest': 'complete',
            'conditions': 'new_pins_match',
            'after': 'complete_pin_change'
        }, {
            'trigger': 'feed_char',
            'source': 'new_pin_confirmation',
            'dest': 'exit_pin_mismatch'
        }]
        self.add_transitions(new_pin_confirmation)

        # event: opt_out_of_market_place_pin_authorization transitions
        opt_out_of_market_place_pin_authorization_transitions = [{
            'trigger':
            'feed_char',
            'source':
            'opt_out_of_market_place_pin_authorization',
            'dest':
            'complete',
            'conditions':
            'is_authorized_pin',
            'after':
            'change_opted_in_market_status'
        }, {
            'trigger':
            'feed_char',
            'source':
            'opt_out_of_market_place_pin_authorization',
            'dest':
            'exit_pin_blocked',
            'conditions':
            'is_blocked_pin'
        }]
        self.add_transitions(
            opt_out_of_market_place_pin_authorization_transitions)

        # first_name_entry transitions
        self.add_transition(trigger='feed_char',
                            source='first_name_entry',
                            dest='last_name_entry',
                            after='add_first_name_to_session_data')

        # last_name_entry transitions
        last_name_entry_transitions = [
            # if profile is complete change last name and authorize..
            {
                'trigger': 'feed_char',
                'source': 'last_name_entry',
                'dest': 'name_change_pin_authorization',
                'conditions': 'has_complete_profile',
                'after': 'add_last_name_to_session_data'
            },

            # if profile is complete save for last_name_entry change last name and authorize.
            {
                'trigger':
                'feed_char',
                'source':
                'last_name_entry',
                'dest':
                'name_change_pin_authorization',
                'conditions':
                'has_empty_name_info',
                'unless': [
                    'has_complete_profile', 'has_empty_gender_info',
                    'has_empty_location_info', 'has_empty_bio_info'
                ],
                'after':
                'add_last_name_to_session_data'
            },

            # if gender info is empty proceed to gender entry menu
            {
                'trigger': 'feed_char',
                'source': 'last_name_entry',
                'dest': 'gender_entry',
                'conditions': 'has_empty_gender_info',
                'after': 'add_last_name_to_session_data'
            },

            # if location info is empty and gender info is filled proceed to location entry menu
            {
                'trigger': 'feed_char',
                'source': 'last_name_entry',
                'dest': 'location_entry',
                'unless': 'has_empty_gender_info',
                'conditions': 'has_empty_location_info',
                'after': 'add_last_name_to_session_data'
            },

            # if business info is empty and gender and location info is filled proceed to business_entry menu
            {
                'trigger': 'feed_char',
                'source': 'last_name_entry',
                'dest': 'change_my_business_prompt',
                'unless': ['has_empty_gender_info', 'has_empty_location_info'],
                'conditions': 'has_empty_bio_info',
                'after': 'add_last_name_to_session_data'
            }
        ]
        self.add_transitions(last_name_entry_transitions)

        # gender_entry transitions
        gender_entry_transitions = [
            # if profile is complete, edit gender_entry and authorize.
            {
                'trigger': 'feed_char',
                'source': 'gender_entry',
                'dest': 'gender_change_pin_authorization',
                'conditions': 'has_complete_profile',
                'after': 'add_gender_to_session_data'
            },

            # if profile is complete save for gender_entry change gender and authorize.
            {
                'trigger':
                'feed_char',
                'source':
                'gender_entry',
                'dest':
                'gender_change_pin_authorization',
                'conditions':
                'has_empty_gender_info',
                'unless': [
                    'has_complete_profile', 'has_empty_name_info',
                    'has_empty_location_info', 'has_empty_location_info'
                ],
                'after':
                'add_gender_to_session_data'
            },

            # if location info is empty proceed to location entry menu
            {
                'trigger': 'feed_char',
                'source': 'gender_entry',
                'dest': 'location_entry',
                'conditions': 'has_empty_location_info',
                'after': 'add_gender_to_session_data'
            },

            # if business info is empty and gender and location info is filled proceed to business_entry menu
            {
                'trigger': 'feed_char',
                'source': 'gender_entry',
                'dest': 'change_my_business_prompt',
                'unless': 'has_empty_location_info',
                'conditions': 'has_empty_bio_info'
            }
        ]
        self.add_transitions(gender_entry_transitions)

        # location_entry_transitions
        location_entry_transitions = [
            # if profile is complete, edit location_entry and authorize.
            {
                'trigger': 'feed_char',
                'source': 'location_entry',
                'dest': 'location_change_pin_authorization',
                'conditions': 'has_complete_profile',
                'after': 'add_location_to_session_data'
            },

            # if profile is complete save for location_entry change location and authorize.
            {
                'trigger':
                'feed_char',
                'source':
                'location_entry',
                'dest':
                'location_change_pin_authorization',
                'conditions':
                'has_empty_location_info',
                'unless': [
                    'has_complete_profile', 'has_empty_name_info',
                    'has_empty_gender_info', 'has_empty_bio_info'
                ],
                'after':
                'add_location_to_session_data'
            },

            # if bio info is empty proceed to bio entry menu
            {
                'trigger': 'feed_char',
                'source': 'location_entry',
                'dest': 'change_my_business_prompt',
                'conditions': 'has_empty_bio_info',
                'after': 'add_location_to_session_data'
            },
        ]
        self.add_transitions(location_entry_transitions)

        # change_my_business_prompt_transitions
        change_my_business_prompt_transitions = [
            # if profile is complete, edit change_my_business_prompt and authorize.
            {
                'trigger': 'feed_char',
                'source': 'change_my_business_prompt',
                'dest': 'bio_change_pin_authorization',
                'conditions': 'has_complete_profile',
                'after': 'add_bio_to_session_data'
            },

            # if bio info is empty proceed to bio entry menu
            {
                'trigger': 'feed_char',
                'source': 'change_my_business_prompt',
                'dest': 'profile_info_change_pin_authorization',
                'conditions': 'has_empty_bio_info',
                'after': 'add_bio_to_session_data'
            },
        ]
        self.add_transitions(change_my_business_prompt_transitions)

        # name_change_pin_authorization transitions
        self.add_transition(trigger='feed_char',
                            source='name_change_pin_authorization',
                            dest='exit',
                            after='save_username_info')

        # gender_change_pin_authorization transitions
        self.add_transition(trigger='feed_char',
                            source='gender_change_pin_authorization',
                            dest='exit',
                            after='save_gender_info')

        # location_change_pin_authorization transitions
        self.add_transition(trigger='feed_char',
                            source='location_change_pin_authorization',
                            dest='exit',
                            after='save_location_info')

        # bio_change_pin_authorization transitions
        self.add_transition(trigger='feed_char',
                            source='bio_change_pin_authorization',
                            dest='exit',
                            after='save_bio_info')

        # profile_info_change_pin_authorization transitions
        self.add_transition(trigger='feed_char',
                            source='profile_info_change_pin_authorization',
                            dest='exit',
                            after='save_profile_info')

        # view_profile_pin_authorization transitions
        self.add_transition(trigger='feed_char',
                            source='view_profile_pin_authorization',
                            dest='about_me')

        # event: exchange_token transitions
        exchange_token_transitions = [{
            'trigger': 'feed_char',
            'source': 'exchange_token',
            'dest': 'complete',
            'conditions': 'menu_one_selected',
            'after': 'fetch_user_exchange_rate'
        }, {
            'trigger': 'feed_char',
            'source': 'exchange_token',
            'dest': 'exchange_token_agent_number_entry',
            'conditions': 'menu_two_selected'
        }, {
            'trigger': 'feed_char',
            'source': 'exchange_token',
            'dest': 'exit_invalid_menu_option'
        }]
        self.add_transitions(exchange_token_transitions)

        # DEPRECATED - exchange rate currently given without requiring pin
        # event: exchange_rate_pin_authorization transitions
        exchange_rate_pin_authorization_transitions = [{
            'trigger':
            'feed_char',
            'source':
            'exchange_rate_pin_authorization',
            'dest':
            'complete',
            'conditions':
            'is_authorized_pin',
            'after':
            'fetch_user_exchange_rate'
        }, {
            'trigger':
            'feed_char',
            'source':
            'exchange_rate_pin_authorization',
            'dest':
            'exit_pin_blocked',
            'conditions':
            'is_blocked_pin'
        }]
        self.add_transitions(exchange_rate_pin_authorization_transitions)

        # event: exchange_token_agent_number_entry transitions
        exchange_token_agent_number_entry_transitions = [{
            'trigger':
            'feed_char',
            'source':
            'exchange_token_agent_number_entry',
            'dest':
            'exchange_token_amount_entry',
            'conditions':
            'is_valid_token_agent',
            'after':
            'save_exchange_agent_phone'
        }, {
            'trigger':
            'feed_char',
            'source':
            'exchange_token_agent_number_entry',
            'dest':
            'exit_invalid_token_agent'
        }]
        self.add_transitions(exchange_token_agent_number_entry_transitions)

        # event: exchange_token_amount_entry transitions
        exchange_token_amount_entry_transitions = [{
            'trigger':
            'feed_char',
            'source':
            'exchange_token_amount_entry',
            'dest':
            'exchange_token_pin_authorization',
            'conditions':
            ['is_valid_token_amount', 'is_valid_token_exchange_amount'],
            'after':
            'save_exchange_amount'
        }, {
            'trigger':
            'feed_char',
            'source':
            'exchange_token_amount_entry',
            'dest':
            'exit_invalid_exchange_amount'
        }]
        self.add_transitions(exchange_token_amount_entry_transitions)

        # event: exchange_token_pin_authorization transitions
        exchange_token_pin_authorization_transitions = [{
            'trigger':
            'feed_char',
            'source':
            'exchange_token_pin_authorization',
            'dest':
            'exchange_token_confirmation',
            'conditions':
            'is_authorized_pin'
        }, {
            'trigger':
            'feed_char',
            'source':
            'exchange_token_pin_authorization',
            'dest':
            'exit_pin_blocked',
            'conditions':
            'is_blocked_pin'
        }]
        self.add_transitions(exchange_token_pin_authorization_transitions)

        # event: exchange_token_confirmation transitions
        exchange_token_confirmation_transitions = [{
            'trigger':
            'feed_char',
            'source':
            'exchange_token_confirmation',
            'dest':
            'complete',
            'conditions':
            'menu_one_selected',
            'after':
            'process_exchange_token_request'
        }, {
            'trigger':
            'feed_char',
            'source':
            'exchange_token_confirmation',
            'dest':
            'exit',
            'conditions':
            'menu_two_selected'
        }, {
            'trigger':
            'feed_char',
            'source':
            'exchange_token_confirmation',
            'dest':
            'exit_invalid_menu_option'
        }]
        self.add_transitions(exchange_token_confirmation_transitions)
示例#43
0
        #print("*** action_Insert ***")
        #同じ文字をInsertするかどうか
        pass

    def action_Substitution(self):
        #print("*** action_Substitution ***")
        pass

    def action_Stop(self):
        #print("*** action_Stop ***")
        pass


lump = Matter()
machine = Machine(model=lump,
                  states=states,
                  transitions=transitions,
                  initial='Si')
#http://www.speech.cs.cmu.edu/cgi-bin/cmudict
#https://pronouncing.readthedocs.io/en/latest/tutorial.html
#補助シンボルなし


def Insert(word):
    lump.Insert()
    #print("Insert")
    index = int(np.random.randint(0, len(phenome), 1))
    Nosiyword = d[index]
    return Nosiyword


def Substitution(word):
示例#44
0
 def __init__(self):
     self.machine = Machine(model=self, states=Document.states, initial="draft")
     self.machine.add_transition(trigger="publish", source="draft", dest="moderation")
     self.machine.add_transition(trigger="publish", source="moderation", dest="published")
     self.machine.add_transition(trigger="reject", source="moderation", dest="draft")
示例#45
0
class PerpetualRiderAgent(Agent):
    lateral_states = ['keep_lane', 'change_lane']

    def __init__(self, desired_speed=7.5, radical=0.75, minimal_ttc=5.):
        super().__init__()
        self.lateral_decider = Machine(
            model=self,
            states=PerpetualRiderAgent.lateral_states,
            initial='keep_lane')
        self.lateral_decider.add_transition(
            "decide",
            "keep_lane",
            "change_lane",
            conditions=['am_too_slow', 'can_safely_change_lane'])
        self.lateral_decider.add_transition("decide",
                                            "keep_lane",
                                            "keep_lane",
                                            after='act_keep_lane')
        self.lateral_decider.add_transition(
            "decide",
            "change_lane",
            "keep_lane",
            conditions=['changing_seems_finished'],
            after='act_keep_lane')
        self.lateral_decider.add_transition("decide", "change_lane",
                                            "change_lane")

        self.desired_speed = desired_speed
        self.radical = radical
        self.minimal_ttc = minimal_ttc

    @property
    def am_too_slow(self):
        return self.ego_speed < self.desired_speed * self.radical

    @property
    def can_safely_change_lane(self):
        if self.neighbors['ego_lane_front'] is None:
            return False

        for waypoints in self.obs.waypoint_paths:
            if waypoints[0].lane_index + 1 == self.ego_lane_index:
                if self.calc_ttc(self.neighbors['left_lane_front']) <= \
                        self.calc_ttc(self.neighbors['ego_lane_front']):
                    continue
                safe_speed_min = max(
                    0, self.safe_speed(self.neighbors['left_lane_behind']))
                safe_speed_max = self.safe_speed(
                    self.neighbors['left_lane_front'])
                if safe_speed_min > safe_speed_max:
                    continue
                self.ego_lane_id_before = self.obs.ego_vehicle_state.lane_id
                self.command = ((safe_speed_min + safe_speed_max) / 2., 1)
                return True
            if waypoints[0].lane_index - 1 == self.ego_lane_index:
                if self.calc_ttc(self.neighbors['right_lane_front']) <= \
                        self.calc_ttc(self.neighbors['ego_lane_front']):
                    continue
                safe_speed_min = max(
                    0, self.safe_speed(self.neighbors['right_lane_behind']))
                safe_speed_max = self.safe_speed(
                    self.neighbors['right_lane_front'])
                if safe_speed_min > safe_speed_max:
                    continue
                self.ego_lane_id_before = self.obs.ego_vehicle_state.lane_id
                self.command = ((safe_speed_min + safe_speed_max) / 2., -1)
                return True
        return False

    @property
    def changing_seems_finished(self):
        if self.ego_lane_id_before != self.obs.ego_vehicle_state.lane_id:
            return True
        return False

    def act_keep_lane(self):
        all_safe_speed = [self.desired_speed]

        safe_speed = self.desired_speed
        for ne in self.obs.neighborhood_vehicle_states:
            if self.afront_of_me(ne) and self.dist(ne) < 80:
                safe_speed = min(self.safe_speed(ne), safe_speed)
                all_safe_speed.append(safe_speed)

        self.command = (safe_speed, 0)

        min_dist = math.inf
        nearest_ne = None
        for ne in self.obs.neighborhood_vehicle_states:
            if self.dist(ne) < min_dist:
                nearest_ne = ne
                min_dist = self.dist(nearest_ne)

    def afront_of_me(self, ne):
        rel_pos = ne.position - self.ego_position
        rel_ang_world = math.atan2(rel_pos[1].item(), rel_pos[0].item())
        rel_ang_self = rel_ang_world - self.obs.ego_vehicle_state.heading

        return math.sin(rel_ang_self) > 0

    def dist(self, ne):
        if ne is None: return float('inf')
        rel_pos = ne.position - self.ego_position
        dist_ = np.linalg.norm(rel_pos, ord=2)
        return dist_

    def calc_ttc(self, ne):
        if ne is None: return float('inf')
        if self.afront_of_me(ne):
            ttc = self.dist(ne) / (self.ego_speed - ne.speed)
        else:
            ttc = self.dist(ne) / (ne.speed - self.ego_speed)
        return ttc

    def safe_speed(self, ne):
        # print(f"ne is None: {ne is None}, ", end="\n" if ne is None else "")
        if ne is None: return self.desired_speed
        if self.afront_of_me(ne):
            # print("ne is afront of me")
            ssp = self.dist(ne) / self.minimal_ttc + ne.speed
        else:
            # print("ne is behind me")
            ssp = -self.dist(ne) / self.minimal_ttc + ne.speed
        return ssp

    def parse_obs(self):
        self.ego_speed = self.obs.ego_vehicle_state.speed
        self.ego_lane_index = self.obs.ego_vehicle_state.lane_index
        self.ego_position = self.obs.ego_vehicle_state.position

        self.neighbors = dict(ego_lane_front=None,
                              left_lane_front=None,
                              left_lane_behind=None,
                              right_lane_front=None,
                              right_lane_behind=None)

        def update_neighbors(key, ne):
            if self.neighbors[key] is None or self.dist(ne) < self.dist(
                    self.neighbors[key]):
                self.neighbors[key] = ne

        for ne in self.obs.neighborhood_vehicle_states:
            if self.ego_lane_index == ne.lane_index and self.afront_of_me(ne):
                update_neighbors('ego_lane_front', ne)
            elif self.ego_lane_index - 1 == ne.lane_index:
                if self.afront_of_me(ne):
                    update_neighbors('left_lane_front', ne)
                else:
                    update_neighbors('left_lane_behind', ne)
            elif self.ego_lane_index + 1 == ne.lane_index:
                if self.afront_of_me(ne):
                    update_neighbors('right_lane_front', ne)
                else:
                    update_neighbors('right_lane_behind', ne)
            else:
                continue

    def act(self, obs: Observation):
        self.obs = obs
        self.parse_obs()
        self.decide()
        return self.command
示例#46
0
 def test_auto_transitions(self):
     states = ['A', {'name': 'B'}, State(name='C')]
     m = Machine(None, states, initial='A', auto_transitions=True)
     m.to_B()
     self.assertEquals(m.state, 'B')
     m.to_C()
     self.assertEquals(m.state, 'C')
     m.to_A()
     self.assertEquals(m.state, 'A')
     # Should fail if auto transitions is off...
     m = Machine(None, states, initial='A', auto_transitions=False)
     with self.assertRaises(AttributeError):
         m.to_C()
示例#47
0
    def run(self, filename):
        logger.setLevel(logging.INFO)

        machine = self
        M = Machine(model=machine,
                    states=self.states,
                    transitions=self.transitions,
                    initial='none')

        start_stepper = 'Entering Rythmos::.*::advanceStepperToTime'
        end_stepper = 'Leaving Rythmos::.*::advanceStepperToTime'
        start_c_step = 'Entering Rythmos::.*::takeStep'
        end_c_step = 'Leaving Rythmos::.*::takeStep'
        start_nl_step = '(?<=Nonlinear Solver Step )\d*'
        end_nl_step_good = '\(Converged!\)'
        end_nl_step_bad = '\(Failed!\)'
        start_residual_belos = '\*\*\*\*\* Belos Iterative Solver: '
        mid_residual = 'Iter.*, \[.*\] :\s*.*'
        end_residual_belos_good = '(?<=returned a solve status of "SOLVE_STATUS_CONVERGED" in )\d*'
        end_residual_belos_bad = '(?<=returned a solve status of "SOLVE_STATUS_UNCONVERGED" in )\d*'
        start_timers = '(?<=TimeMonitor results over )\d*'
        mid_timers = '.* \(.*\)\s*$'
        end_timers = '==========='

        timer_names = []
        timer_times = {}
        timer_calls = {}
        timer_serial = None

        nl_step = 0
        t_step = 0
        lineno = 0

        yaml_string = '{"scheme":"drekar","Steps":{'
        with open(filename) as f:
            try:
                for line in f:
                    lineno += 1
                    if re.search(start_stepper, line) != None:
                        if machine.state != 'none':
                            raise RuntimeError('Wrong state: ' + machine.state)
                        t_step = 0

                    elif re.search(end_stepper, line) != None:
                        if machine.state != 'none':
                            raise RuntimeError('Wrong state: ' + machine.state)

                    elif re.search(start_c_step, line) != None:
                        if machine.state != 'none':
                            raise RuntimeError('Wrong state: ' + machine.state)
                        machine.n2t()

                        if yaml_string[-1] != '{':
                            yaml_string += ','
                        yaml_string += '"c_step_' + str(t_step) + '":{'

                    elif re.search(end_c_step, line) != None:
                        if machine.state != 'transient':
                            raise RuntimeError('Wrong state: ' + machine.state)
                        machine.t2n()

                        yaml_string += ', "nl_its":' + str(nl_step) + '}'

                        nl_step = 0
                        t_step += 1

                    elif re.search(start_nl_step, line) != None:
                        if machine.state != 'transient':
                            raise RuntimeError('Wrong state: ' + machine.state)
                        machine.t2nl()

                        if yaml_string[-1] != '{':
                            yaml_string += ','
                        yaml_string += '"nl_step_' + str(nl_step) + '":{'

                        nl_step += 1

                    elif re.search(end_nl_step_good,
                                   line) != None or re.search(
                                       end_nl_step_bad, line) != None:
                        if machine.state != 'nonlinear':
                            raise RuntimeError('Wrong state: ' + machine.state)
                        machine.nl2t()

                        # Get rid of ",nl_step_?:{}
                        i = 1
                        while (yaml_string[-i] != ','):
                            i += 1
                        yaml_string = yaml_string[:-i]

                    elif re.search(start_residual_belos, line) != None:
                        if machine.state != 'nonlinear':
                            raise RuntimeError('Wrong state: ' + machine.state)
                        machine.nl2l()

                        if yaml_string[-1] != '{':
                            yaml_string += ','
                        yaml_string += '"res_hist":['

                    elif re.search(end_residual_belos_good,
                                   line) != None or re.search(
                                       end_residual_belos_bad, line) != None:
                        if machine.state != 'linear':
                            raise RuntimeError('Wrong state: ' + machine.state)

                        machine.l2t()

                        m = re.search(end_residual_belos_good, line)
                        if m != None:
                            its = m.group()
                        else:
                            m = re.search(end_residual_belos_bad, line)
                            its = m.group()
                        yaml_string += '], "its":' + its

                        m = re.search(
                            '(?<=with total CPU time of ).*(?=\ sec)', line)
                        belos_time = m.group()
                        yaml_string += ', "solve_time":' + belos_time + '}'

                    elif re.search(mid_residual, line) != None:
                        if machine.state != 'linear':
                            raise RuntimeError('Wrong state: ' + machine.state)

                        m = re.search('[^\s]*$', line)
                        res = m.group()
                        if yaml_string[-1] != '[':
                            yaml_string += ','
                        yaml_string += res

                    elif re.search(start_timers, line) != None:
                        if machine.state != 'none':
                            raise RuntimeError('Wrong state: ' + machine.state)
                        machine.n2m()

                        m = re.search(start_timers, line)
                        nprocs = m.group()

                        if nprocs == "1":
                            timer_serial = True
                        else:
                            timer_serial = False

                        # Finalize stepping
                        yaml_string += '}'

                        yaml_string += ',"Number of processes":' + nprocs
                        yaml_string += ',"Time unit":s'
                        yaml_string += ',"Statistics collected":["MinOverProcs","MeanOverProcs","MaxOverProcs","MeanOverCallCounts"]'

                    elif re.search(end_timers, line) != None:
                        if machine.state != 'timers':
                            # there could be other ======== lines
                            continue
                        machine.m2n()

                        # Timer names
                        yaml_string += ',"Timer names":['
                        for name in timer_names:
                            if yaml_string[-1] != '[':
                                yaml_string += ','
                            yaml_string += '"' + name + '"'
                        yaml_string += ']'

                        # Total times
                        yaml_string += ',"Total times":{'
                        for name in timer_names:
                            if yaml_string[-1] != '{':
                                yaml_string += ','
                            yaml_string += '"' + name + '":{'
                            yaml_string += '"MinOverProcs":' + timer_times[
                                name]['MinOverProcs']
                            yaml_string += ',"MeanOverProcs":' + timer_times[
                                name]['MeanOverProcs']
                            yaml_string += ',"MaxOverProcs":' + timer_times[
                                name]['MaxOverProcs']
                            yaml_string += ',"MeanOverCallCounts":' + timer_times[
                                name]['MeanOverCallCounts']
                            yaml_string += '}'
                        yaml_string += '}'

                        # Call counts
                        yaml_string += ',"Call counts":{'
                        for name in timer_calls:
                            if yaml_string[-1] != '{':
                                yaml_string += ','
                            yaml_string += '"' + name + '":{'
                            yaml_string += '"MinOverProcs":' + timer_times[
                                name]['MinOverProcs']
                            yaml_string += ',"MeanOverProcs":' + timer_times[
                                name]['MeanOverProcs']
                            yaml_string += ',"MaxOverProcs":' + timer_times[
                                name]['MaxOverProcs']
                            yaml_string += ',"MeanOverCallCounts":' + timer_times[
                                name]['MeanOverCallCounts']
                            yaml_string += '}'
                        yaml_string += '}'

                        yaml_string += '}'

                    elif re.search(mid_timers, line) != None:
                        if machine.state != 'timers':
                            # there could be other matching lines
                            continue

                        if re.search('Timer Name', line) != None:
                            # Skip header (in serial it matches the pattern)
                            continue

                        splits = line.split()
                        if timer_serial == True:
                            name = ' '.join(splits[0:-2])
                            name = name.replace('"', '\\"')
                            timer_names.append(name)
                            timer_times[name] = {}
                            timer_times[name]['MinOverProcs'] = splits[-2]
                            timer_times[name]['MeanOverProcs'] = splits[-2]
                            timer_times[name]['MaxOverProcs'] = splits[-2]
                            timer_times[name]['MeanOverCallCounts'] = splits[
                                -2]
                            timer_calls[name] = {}
                            timer_calls[name]['MinOverProcs'] = splits[-1][
                                1:-1]
                            timer_calls[name]['MeanOverProcs'] = splits[-1][
                                1:-1]
                            timer_calls[name]['MaxOverProcs'] = splits[-1][
                                1:-1]
                            timer_calls[name]['MeanOverCallCounts'] = splits[
                                -1][1:-1]
                        else:
                            name = ' '.join(splits[0:-8])
                            name = name.replace('"', '\\"')
                            timer_names.append(name)
                            timer_times[name] = {}
                            timer_times[name]['MinOverProcs'] = splits[-8]
                            timer_times[name]['MeanOverProcs'] = splits[-6]
                            timer_times[name]['MaxOverProcs'] = splits[-4]
                            timer_times[name]['MeanOverCallCounts'] = splits[
                                -2]
                            timer_calls[name] = {}
                            timer_calls[name]['MinOverProcs'] = splits[-7][
                                1:-1]
                            timer_calls[name]['MeanOverProcs'] = splits[-5][
                                1:-1]
                            timer_calls[name]['MaxOverProcs'] = splits[-3][
                                1:-1]
                            timer_calls[name]['MeanOverCallCounts'] = splits[
                                -1][1:-1]
            except RuntimeError as e:
                raise RuntimeError("Caught an error while parsing on line " +
                                   str(lineno) + ": " + e.args[0])

        if timer_serial == None:
            # We did not encounter any timers
            yaml_string += '}}'

        try:
            yaml_data = yaml.load(yaml_string)
        except yaml.parser.ParserError:
            raise RuntimeError(
                'Could not parse YAML out. Did you select the right mode?')

        return yaml_data
示例#48
0
 def __init__(self, **machine_configs):
     self.machine = Machine(model=self, **machine_configs)
     self.mode = 'all'
     self.lastResult = None
示例#49
0
class Session:
    states = ["new", "connected", "disconnected"]

    def __init__(self, plugins_manager):
        self._init_states()
        self._plugins_manager = plugins_manager
        self.remote_address = None
        self.remote_port = None
        self.client_id = None
        self.clean_session = None
        self.will_flag = False
        self.will_message = None
        self.will_qos = None
        self.will_retain = None
        self.will_topic = None
        self.keep_alive = 0
        self.publish_retry_delay = 0
        self.broker_uri = None
        self.username = None
        self.password = None
        self.cafile = None
        self.capath = None
        self.cadata = None
        self._packet_id = 0
        self.parent = 0

        self.logger = logging.getLogger(__name__)

        # Used to store outgoing ApplicationMessage while publish protocol flows
        self.inflight_out = OrderedDict()

        # Used to store incoming ApplicationMessage while publish protocol flows
        self.inflight_in = OrderedDict()

        # Stores messages retained for this session
        self.retained_messages = anyio.create_queue(9999)

        # Stores PUBLISH messages ID received in order and ready for application process
        self._delivered_message_queue = anyio.create_queue(9999)

        # The actual delivery process
        self._delivery_task = None
        self._delivery_stopped = anyio.create_event()

        # The broker we're attached to
        self._broker = None

    def _init_states(self):
        self.transitions = Machine(states=Session.states, initial="new")
        self.transitions.add_transition(trigger="connect", source="new", dest="connected")
        self.transitions.add_transition(trigger="connect", source="disconnected", dest="connected")
        self.transitions.add_transition(
            trigger="disconnect", source="connected", dest="disconnected"
        )
        self.transitions.add_transition(trigger="disconnect", source="new", dest="disconnected")
        self.transitions.add_transition(
            trigger="disconnect", source="disconnected", dest="disconnected"
        )

    def __hash__(self):
        return hash(self.client_id)

    def __eq__(self, other):
        other = getattr(other, "client_id", other)
        return self.client_id == other

    async def start(self, broker=None):
        if broker is not None:
            self._broker = broker
            if self._delivery_task is not None:
                raise RuntimeError("Already running")
            await broker._tg.spawn(self._delivery_loop)

    async def stop(self):
        if self._delivery_task is not None:
            await self._delivery_task.cancel()
            await self._delivery_stopped.wait()
        self._broker = None  # break ref loop

    async def put_message(self, app_message):
        if app_message.retain and self._broker is not None and not self._broker._do_retain:
            raise RuntimeError("The broker doesn't do retains", repr(app_message.__getstate__()))
        if not app_message.topic:
            self.logger.warning(
                "[MQTT-4.7.3-1] - %s invalid TOPIC sent in PUBLISH message,closing connection",
                self.client_id,
            )
            raise MQTTException(
                "[MQTT-4.7.3-1] - %s invalid TOPIC sent in PUBLISH message,closing connection"
                % self.client_id
            )
        if "#" in app_message.topic or "+" in app_message.topic:
            self.logger.warning(
                "[MQTT-3.3.2-2] - %s invalid TOPIC sent in PUBLISH message, closing connection",
                self.client_id,
            )
            raise MQTTException(
                "[MQTT-3.3.2-2] - %s invalid TOPIC sent in PUBLISH message, closing connection"
                % self.client_id
            )
        if app_message.qos == QOS_0 and self._delivered_message_queue.qsize() >= 9999:
            self.logger.warning("delivered messages queue full. QOS_0 message discarded")
        else:
            await self._delivered_message_queue.put(app_message)

    async def get_next_message(self):
        """Client: get the next message"""
        m = await self._delivered_message_queue.get()
        # split up so that a breakpoint may be set
        return m

    async def _delivery_loop(self):
        """Server: process incoming messages"""
        try:
            async with anyio.open_cancel_scope() as scope:
                self._delivery_task = scope
                broker = self._broker
                broker.logger.debug("%s handling message delivery", self.client_id)
                while True:
                    app_message = await self.get_next_message()
                    await self._plugins_manager.fire_event(
                        EVENT_BROKER_MESSAGE_RECEIVED,
                        client_id=self.client_id,
                        message=app_message,
                    )

                    await broker.broadcast_message(
                        self,
                        app_message.topic,
                        app_message.data,
                        qos=app_message.qos,
                        retain=app_message.publish_packet.retain_flag,
                    )
        finally:
            async with anyio.fail_after(2, shield=True):
                broker.logger.debug("%s finished message delivery", self.client_id)
                self._delivery_task = None
                await self._delivery_stopped.set()

    @property
    def next_packet_id(self):
        self._packet_id += 1
        if self._packet_id > 65535:
            self._packet_id = 1
        limit = self._packet_id
        while self._packet_id in self.inflight_in or self._packet_id in self.inflight_out:
            self._packet_id += 1
            if self._packet_id > 65535:
                self._packet_id = 1
            if self._packet_id == limit:
                raise DistMQTTException("More than 65535 messages pending. No free packet ID")

        return self._packet_id

    @property
    def inflight_in_count(self):
        return len(self.inflight_in)

    @property
    def inflight_out_count(self):
        return len(self.inflight_out)

    @property
    def retained_messages_count(self):
        return self.retained_messages.qsize()

    def __repr__(self):
        return type(self).__name__ + "(clientId={0}, state={1})".format(
            self.client_id, self.transitions.state
        )

    def __getstate__(self):
        state = self.__dict__.copy()
        # Remove the unpicklable entries.
        # del state['transitions']
        del state["retained_messages"]
        del state["_delivered_message_queue"]
        del state["_delivery_task"]
        del state["_delivery_stopped"]
        del state["_broker"]
        return state

    def __setstate(self, state):
        self.__dict__.update(state)
        self.retained_messages = anyio.create_queue(9999)
        self._delivered_message_queue = anyio.create_queue(9999)
示例#50
0
lump = Matter()

# The states
states = ['solid', 'liquid', 'gas', 'plasma']

# And some transitions between states. We're lazy, so we'll leave out
# the inverse phase transitions (freezing, condensation, etc.).
transitions = [{
    'trigger': 'melt',
    'source': 'solid',
    'dest': 'liquid'
}, {
    'trigger': 'evaporate',
    'source': 'liquid',
    'dest': 'gas'
}, {
    'trigger': 'sublimate',
    'source': 'solid',
    'dest': 'gas'
}, {
    'trigger': 'ionize',
    'source': 'gas',
    'dest': 'plasma'
}]

# Initialize
machine = Machine(lump,
                  states=states,
                  transitions=transitions,
                  initial='liquid')
示例#51
0
    def __init__(self,
                 agent,
                 device_id,
                 alarm_sync_tasks,
                 db,
                 advertise_events=False,
                 states=DEFAULT_STATES,
                 transitions=DEFAULT_TRANSITIONS,
                 initial_state='disabled',
                 timeout_delay=DEFAULT_TIMEOUT_RETRY,
                 audit_delay=DEFAULT_AUDIT_DELAY,
                 resync_delay=DEFAULT_RESYNC_DELAY):
        """
        Class initialization

        :param agent: (OpenOmciAgent) Agent
        :param device_id: (str) ONU Device ID
        :param db: (MibDbApi) MIB/Alarm Database
        :param advertise_events: (bool) Advertise events on OpenOMCI Event Bus
        :param alarm_sync_tasks: (dict) Tasks to run
        :param states: (list) List of valid states
        :param transitions: (dict) Dictionary of triggers and state changes
        :param initial_state: (str) Initial state machine state
        :param timeout_delay: (int/float) Number of seconds after a timeout to attempt
                                          a retry (goes back to starting state)
        :param audit_delay: (int) Seconds between Alarm audits while in sync. Set to
                                  zero to disable audit. An operator can request
                                  an audit manually by calling 'self.audit_alarm'
        :param resync_delay: (int) Seconds in sync before performing a forced Alarm
                                   resynchronization
        """
        self.log = structlog.get_logger(device_id=device_id)

        self._agent = agent
        self._device_id = device_id
        self._device = None
        self._database = db
        self._timeout_delay = timeout_delay
        self._audit_delay = audit_delay
        self._resync_delay = resync_delay

        self._update_task = alarm_sync_tasks['alarm-sync']
        self._check_task = alarm_sync_tasks['alarm-check']
        self._resync_task = alarm_sync_tasks['alarm-resync']
        self._audit_task = alarm_sync_tasks['alarm-audit']
        self._advertise_events = advertise_events

        self._deferred = None
        self._current_task = None  # TODO: Support multiple running tasks after v.1.3.0 release
        self._task_deferred = None
        self._last_alarm_sync_time = None
        self._last_alarm_sequence_value = None
        self._device_in_db = False
        self._alarm_class_id = None
        self._alarm_entity_id = None
        self._commands_retrieved = None
        self._alarm_table = None

        self._event_bus = EventBusClient()
        self._omci_cc_subscriptions = {  # RxEvent.enum -> Subscription Object
            RxEvent.Get_ALARM_Get: None,
            RxEvent.Get_ALARM_Get_Next: None
        }
        self._omci_cc_sub_mapping = {
            RxEvent.Get_ALARM_Get: self.on_alarm_update_response,
            RxEvent.Get_ALARM_Get_Next: self.on_alarm_update_next_response
        }

        # Statistics and attributes
        # TODO: add any others if it will support problem diagnosis

        # Set up state machine to manage states
        self.machine = Machine(model=self,
                               states=states,
                               transitions=transitions,
                               initial=initial_state,
                               queued=True,
                               name='{}-{}'.format(self.__class__.__name__,
                                                   device_id))
示例#52
0
    def __init__(self,
                 input_file,
                 allowed_archive_path_regexes=None,
                 allowed_dest_path_regexes=None,
                 allowed_extensions=None,
                 allowed_regexes=None,
                 archive_input_file=False,
                 archive_path_function=None,
                 celery_task=None,
                 check_params=None,
                 config=None,
                 custom_params=None,
                 dest_path_function=None,
                 error_cleanup_regexes=None,
                 exclude_regexes=None,
                 harvest_params=None,
                 harvest_type='talend',
                 include_regexes=None,
                 notify_params=None,
                 upload_path=None,
                 resolve_params=None):

        # property backing variables
        self._config = None
        self._default_addition_publish_type = PipelineFilePublishType.HARVEST_UPLOAD
        self._default_deletion_publish_type = PipelineFilePublishType.DELETE_UNHARVEST
        self._error = None
        self._error_details = None
        self._exclude_regexes = None
        self._file_basename = None
        self._file_checksum = None
        self._file_collection = None
        self._file_extension = None
        self._file_type = None
        self._include_regexes = None
        self._input_file_archive_path = None
        self._instance_working_directory = None
        self._notification_results = None
        self._is_archived = False
        self._logger = None
        self._result = HandlerResult.UNKNOWN
        self._should_notify = None
        self._start_time = datetime.now()

        # public attributes
        self.input_file = input_file
        self.allowed_archive_path_regexes = allowed_archive_path_regexes
        self.allowed_dest_path_regexes = allowed_dest_path_regexes
        self.allowed_extensions = allowed_extensions
        self.allowed_regexes = allowed_regexes
        self.archive_input_file = archive_input_file
        self.archive_path_function = archive_path_function
        self.celery_task = celery_task
        self.check_params = check_params
        self.custom_params = custom_params
        self.config = config
        self.dest_path_function = dest_path_function
        self.error_cleanup_regexes = error_cleanup_regexes
        self.exclude_regexes = exclude_regexes
        self.harvest_params = harvest_params
        self.harvest_type = harvest_type
        self.include_regexes = include_regexes
        self.notify_params = notify_params
        self.upload_path = upload_path
        self.resolve_params = resolve_params

        # private attributes
        self._archive_path_function_ref = None
        self._archive_path_function_name = None
        self._dest_path_function_ref = None
        self._dest_path_function_name = None
        self._handler_run = False

        self._machine = Machine(model=self,
                                states=HandlerBase.all_states,
                                initial='HANDLER_INITIAL',
                                auto_transitions=False,
                                transitions=HandlerBase.all_transitions,
                                after_state_change='_after_state_change')
示例#53
0
class QuestState(metaclass=MetaSingleton):
    _instance = None

    def get_state_handler(self):
        if self._instance is None:
            self._instance = QuestState()
            return self._instance

    def __init__(self):
        self.machine = Machine(
            model=self,
            states=[state.name for state in QuestStateType],
            initial=QuestStateType(1).name,
            ignore_invalid_triggers=True,
            auto_transitions=False,
        )

        # init -> editing
        self.machine.add_transition(trigger=Actions.EDIT_START_EDITING.name,
                                    source=QuestStateType.MODE_SELECTION.name,
                                    dest=QuestStateType.EDIT_INIT.name)
        # editing -> init
        self.machine.add_transition(
            trigger=Actions.LEVEL_UP.name,
            source=QuestStateType.EDIT_INIT.name,
            dest=QuestStateType.MODE_SELECTION.name,
        )
        # editing -> edit existing quest
        self.machine.add_transition(
            trigger=Actions.SELECT_EXISTING_QUEST.name,
            source=QuestStateType.EDIT_INIT.name,
            dest=QuestStateType.EDIT_QUEST.name,
        )
        # edit existing quest -> editing
        self.machine.add_transition(
            trigger=Actions.LEVEL_UP.name,
            source=QuestStateType.EDIT_QUEST.name,
            dest=QuestStateType.EDIT_INIT.name,
        )
        # editing -> edit new quest
        self.machine.add_transition(
            trigger=Actions.CREATE_NEW_QUEST.name,
            source=QuestStateType.EDIT_INIT.name,
            dest=QuestStateType.EDIT_QUEST.name,
        )
        self.machine.add_transition(
            trigger=Actions.ENTER_STEP_ID.name,
            source=QuestStateType.EDIT_QUEST.name,
            dest=QuestStateType.EDIT_QUEST_STEP.name,
        )
        self.machine.add_transition(
            trigger=Actions.LEVEL_UP.name,
            source=QuestStateType.EDIT_QUEST_STEP.name,
            dest=QuestStateType.EDIT_QUEST.name,
        )
        self.machine.add_transition(
            trigger=Actions.PLAY.name,
            source=QuestStateType.MODE_SELECTION.name,
            dest=QuestStateType.PLAY_START.name,
        )
        self.machine.add_transition(
            trigger=Actions.LEVEL_UP.name,
            source=QuestStateType.PLAY_START.name,
            dest=QuestStateType.MODE_SELECTION.name,
        )
示例#54
0
    def test_callback_identification(self):
        m = Machine(Stuff(), states=['A', 'B', 'C', 'D', 'E', 'F'], initial='A')
        m.add_transition('transition', 'A', 'B', before='increase_level')
        m.add_transition('after', 'B', 'C', before='increase_level')
        m.add_transition('on_exit_A', 'C', 'D', before='increase_level', conditions='this_fails')
        m.add_transition('check', 'C', 'E', before='increase_level')
        m.add_transition('prepare', 'E', 'F', before='increase_level')
        m.add_transition('before', 'F', 'A', before='increase_level')

        m.before_transition('increase_level')
        m.before_after('increase_level')
        m.before_on_exit_A('increase_level')
        m.after_check('increase_level')
        m.before_prepare('increase_level')
        m.before_before('increase_level')

        m.model.transition()
        self.assertEquals(m.model.state, 'B')
        self.assertEquals(m.model.level, 3)

        m.model.after()
        self.assertEquals(m.model.state, 'C')
        self.assertEquals(m.model.level, 5)

        m.model.on_exit_A()
        self.assertEquals(m.model.state, 'C')
        self.assertEquals(m.model.level, 5)

        m.model.check()
        self.assertEquals(m.model.state, 'E')
        self.assertEquals(m.model.level, 7)

        m.model.prepare()
        self.assertEquals(m.model.state, 'F')
        self.assertEquals(m.model.level, 9)

        m.model.before()
        self.assertEquals(m.model.state, 'A')
        self.assertEquals(m.model.level, 11)

        # An invalid transition shouldn't execute the callback
        with self.assertRaises(MachineError):
                m.model.on_exit_A()
示例#55
0
class player:
    def __init__(self, nick):
        self.__nick = nick  # player's nick in irc
        self.__alive = True
        self.__max_hp = 0
        self.__hp = 0
        self.__range = 1
        self.__use_sha = True
        self.__cards = []
        self.__id = IDENTITY['unknown']
        self.__machine = Machine(model=self,
                                 states=STATUS,
                                 initial=STATUS_DICT['normal'])

        list(
            map(
                lambda t: self.__machine.add_transition(
                    t['trigger'], t['source'], t['dest']), TRANSITIONS))

    def nick(self):
        return self.__nick

    def set_id(self, id):
        self.__id = id

    def secret(self):
        return self.__id

    def id(self):
        if self.is_king() or not (self.is_alive()):
            return self.__id
        else:
            return IDENTITY['unknown']

    def is_king(self):
        return self.__id == IDENTITY['king']

    def is_alive(self):
        return self.__alive

    def set_dead(self):
        self.__alive = False

    def max_hp(self):
        return self.__max_hp

    def change_max_hp(self, value):
        self.__max_hp = value

    def hp(self):
        return self.__hp

    def set_initial_hp(self, value):
        self.change_max_hp(value)
        self.__hp = value

    def increase_hp(self):
        if self.__hp < self.__max_hp:
            self.__hp += 1
            return True
        else:
            return False

    def decrease_hp(self, value):
        self.__hp -= value

    def range(self):
        return self.__range

    def get_card(self, card):
        self.__cards.append(card)

    def card(self):
        return self.__cards

    def choose_card(self, card):
        if card:
            index = self.__cards.index(card)
            return self.__cards.pop(index)

    def can_use_sha(self):
        return self.__use_sha

    def used_sha(self):
        self.__use_sha = False
示例#56
0

#状態を管理したいオブジェクトの元となるクラス
# 遷移時やイベント発生時のアクションがある場合は、当クラスのmethodに記載する
class Matter(object):
    def action_l2g(self):
        print("*** from liquid to gas ***")

    def action_g2p(self):
        print("*** from gas to plasma ***")


lump = Matter()
machine = Machine(model=lump,
                  states=states,
                  transitions=transitions,
                  initial='liquid',
                  auto_transitions=False)


class StateMachine(object):
    #状態の定義
    states = ['solid', 'liquid', 'gas', 'plasma']

    #初期化(ステートマシンの定義:とりうる状態の定義、初期状態の定義、各種遷移と紐付くアクションの定義)
    def __init__(self, name):
        self.name = name
        self.machine = Machine(model=self,
                               states=StateMachine.states,
                               initial='liquid',
                               auto_transitions=False)
示例#57
0
文件: broker.py 项目: smurfix/hbmqtt
class Broker:
    """
    MQTT 3.1.1 compliant broker implementation

    :param tg: The task group used to run the broker's tasks.
    :param config: Example Yaml config
    :param plugin_namespace: Plugin namespace to use when loading plugin entry_points. Defaults to ``hbmqtt.broker.plugins``

    Usage::

        async with anyio.create_task_group() as tg: 
            b = Broker(tg, config, plugin_namespace)
            try:
                await b.start()
                pass ## do something with the broker
            finally:
                await b.shutdown()
                await tg.cancel_scope.cancel()

    Typically, though, you'll want to use :func:`create_broker`, which does
    this for you.
    """
    states = [
        'new', 'starting', 'started', 'not_started', 'stopping', 'stopped',
        'not_stopped', 'stopped'
    ]

    def __init__(self,
                 tg: anyio.abc.TaskGroup,
                 config=None,
                 plugin_namespace=None):
        self.logger = logging.getLogger(__name__)
        self.config = _defaults
        if config is not None:
            self.config.update(config)
        self._build_listeners_config(self.config)

        self._servers = dict()
        self._init_states()
        self._sessions = dict()
        self._subscriptions = dict()
        self._retained_messages = dict()
        self._broadcast_queue = anyio.create_queue(9999)
        self._tg = tg

        # Init plugins manager
        context = BrokerContext(self)
        context.config = self.config
        if plugin_namespace:
            namespace = plugin_namespace
        else:
            namespace = 'hbmqtt.broker.plugins'
        self.plugins_manager = PluginManager(tg, namespace, context)

    def _build_listeners_config(self, broker_config):
        self.listeners_config = dict()
        try:
            listeners_config = broker_config['listeners']
            defaults = listeners_config.get('default', {})
            for listener in listeners_config:
                config = dict(defaults)
                config.update(listeners_config[listener])
                self.listeners_config[listener] = config
        except KeyError as ke:
            raise BrokerException(
                "Listener config not found or invalid") from ke

    def _init_states(self):
        self.transitions = Machine(states=Broker.states, initial='new')
        self.transitions.add_transition(trigger='start',
                                        source='new',
                                        dest='starting')
        self.transitions.add_transition(trigger='starting_fail',
                                        source='starting',
                                        dest='not_started')
        self.transitions.add_transition(trigger='starting_success',
                                        source='starting',
                                        dest='started')
        self.transitions.add_transition(trigger='shutdown',
                                        source='started',
                                        dest='stopping')
        self.transitions.add_transition(trigger='shutdown',
                                        source='not_started',
                                        dest='stopping')
        self.transitions.add_transition(trigger='stopping_success',
                                        source='stopped',
                                        dest='stopped')
        self.transitions.add_transition(trigger='stopping_failure',
                                        source='stopping',
                                        dest='not_stopped')
        self.transitions.add_transition(trigger='start',
                                        source='stopped',
                                        dest='starting')
        self.transitions.add_transition(trigger='shutdown',
                                        source='new',
                                        dest='stopped')
        self.transitions.add_transition(trigger='stopping_success',
                                        source='stopping',
                                        dest='stopped')

    async def start(self):
        """
            Start the broker to serve with the given configuration

            Start method opens network sockets and will start listening for incoming connections.

            This method is a *coroutine*.
        """
        try:
            self._sessions = dict()
            self._subscriptions = dict()
            self._retained_messages = dict()
            self.transitions.start()
            self.logger.debug("Broker starting")
        except (MachineError, ValueError) as exc:
            # Backwards compat: MachineError is raised by transitions < 0.5.0.
            self.logger.warning(
                "[WARN-0001] Invalid method call at this moment: %r", exc)
            raise BrokerException("Broker instance can't be started: %s" % exc)

        await self.plugins_manager.fire_event(EVENT_BROKER_PRE_START)
        try:
            # Start network listeners
            for listener_name in self.listeners_config:
                listener = self.listeners_config[listener_name]

                if 'bind' not in listener:
                    self.logger.debug(
                        "Listener configuration '%s' is not bound",
                        listener_name)
                else:
                    # Max connections
                    try:
                        max_connections = listener['max_connections']
                    except KeyError:
                        max_connections = -1

                    # SSL Context
                    sc = None

                    # accept string "on" / "off" or boolean
                    ssl_active = listener.get('ssl', False)
                    if isinstance(ssl_active, str):
                        ssl_active = ssl_active.upper() == 'ON'

                    if ssl_active:
                        try:
                            sc = ssl.create_default_context(
                                ssl.Purpose.CLIENT_AUTH,
                                cafile=listener.get('cafile'),
                                capath=listener.get('capath'),
                                cadata=listener.get('cadata'))
                            sc.load_cert_chain(listener['certfile'],
                                               listener['keyfile'])
                            sc.verify_mode = ssl.CERT_OPTIONAL
                        except KeyError as ke:
                            raise BrokerException(
                                "'certfile' or 'keyfile' configuration parameter missing: %s"
                                % ke)
                        except FileNotFoundError as fnfe:
                            raise BrokerException(
                                "Can't read cert files '%s' or '%s' : %s" %
                                (listener['certfile'], listener['keyfile'],
                                 fnfe))

                    address, s_port = listener['bind'].split(':')
                    port = 0
                    try:
                        port = int(s_port)
                    except ValueError as ve:
                        raise BrokerException(
                            "Invalid port value in bind value: %s" %
                            listener['bind'])

                    async def server_task(evt, cb, address, port, ssl):
                        async with anyio.open_cancel_scope() as scope:
                            await evt.set(scope)
                            async with await anyio.create_tcp_server(
                                    port, interface=address,
                                    ssl_context=ssl) as server:
                                async for conn in server.accept_connections():
                                    await self._tg.spawn(cb, conn)

                    if listener['type'] == 'tcp':
                        cb_partial = partial(self.stream_connected,
                                             listener_name=listener_name)
                    elif listener['type'] == 'ws':
                        cb_partial = partial(self.ws_connected,
                                             listener_name=listener_name)
                    else:
                        self.logger.error("Listener '%s': unknown type '%s'",
                                          listener_name, listener['type'])
                        continue
                    fut = Future()
                    await self._tg.spawn(server_task,
                                         fut,
                                         cb_partial,
                                         address,
                                         port,
                                         sc,
                                         name=listener_name)
                    instance = await fut.get()
                    self._servers[listener_name] = Server(
                        listener_name, instance, max_connections)

                    self.logger.info(
                        "Listener '%s' bind to %s (max_connections=%d)",
                        listener_name, listener['bind'], max_connections)

            self.transitions.starting_success()
            await self.plugins_manager.fire_event(EVENT_BROKER_POST_START)

            #Start broadcast loop
            await self._tg.spawn(self._broadcast_loop)

            self.logger.debug("Broker started")
        except Exception as e:
            if "Cancel" in repr(e):
                raise  # bah
            self.logger.error("Broker startup failed: %r", e)
            self.transitions.starting_fail()
            raise BrokerException("Broker instance can't be started") from e

    async def shutdown(self):
        """
            Stop broker instance.

            Closes all connected session, stop listening on network socket and free resources.
        """
        for s in self._sessions.values():
            await s[0].stop()

        self._sessions = dict()
        self._subscriptions = dict()
        self._retained_messages = dict()
        try:
            self.transitions.shutdown()
        except MachineError as exc:
            # Backwards compat: MachineError is raised by transitions < 0.5.0.
            raise BrokerException("Broker instance can't be stopped") from exc

        # Fire broker_shutdown event to plugins
        await self.plugins_manager.fire_event(EVENT_BROKER_PRE_SHUTDOWN)

        # Stop broadcast loop
        if self._broadcast_queue.qsize() > 0:
            self.logger.warning("%d messages not broadcasted",
                                self._broadcast_queue.qsize())

        for listener_name in self._servers:
            server = self._servers[listener_name]
            await server.close_instance()
        self.logger.debug("Broker closing")
        self.logger.info("Broker closed")
        await self.plugins_manager.fire_event(EVENT_BROKER_POST_SHUTDOWN)
        self.transitions.stopping_success()

    async def internal_message_broadcast(self,
                                         topic,
                                         data,
                                         qos=None,
                                         retain=None):
        return await self.broadcast_message(None,
                                            topic,
                                            data,
                                            qos=qos,
                                            retain=retain)

    async def ws_connected(self, conn, listener_name):
        async def subpro(req):
            if "mqtt" not in req.subprotocols:
                return False
            return "mqtt"

        websocket = await create_websocket_server(conn, filter=subpro)
        await self.client_connected(listener_name,
                                    WebSocketsAdapter(websocket))

    async def stream_connected(self, conn, listener_name):
        await self.client_connected(listener_name, StreamAdapter(conn))

    async def client_connected(self, listener_name, adapter: BaseAdapter):
        server = self._servers.get(listener_name, None)
        if not server:
            raise BrokerException("Invalid listener name '%s'" % listener_name)

        async with server._client_limit():
            return await self.client_connected_(server, listener_name, adapter)

    async def client_connected_(self, server, listener_name,
                                adapter: BaseAdapter):
        # Wait for connection available on listener

        remote_address, remote_port = adapter.get_peer_info()
        self.logger.info("Connection from %s:%d on listener '%s'",
                         remote_address, remote_port, listener_name)

        # Wait for first packet and expect a CONNECT
        try:
            handler, client_session = await BrokerProtocolHandler.init_from_connect(
                adapter, self.plugins_manager)
        except HBMQTTException as exc:
            self.logger.warning(
                "[MQTT-3.1.0-1] %s: Can't read first packet an CONNECT: %s",
                format_client_message(address=remote_address,
                                      port=remote_port), exc)
            #await writer.close()
            self.logger.debug("Connection closed")
            return
        except MQTTException as me:
            self.logger.error(
                'Invalid connection from %s : %s',
                format_client_message(address=remote_address,
                                      port=remote_port), me)
            await adapter.close()
            self.logger.debug("Connection closed")
            return

        if client_session.clean_session:
            # Delete existing session and create a new one
            if client_session.client_id is not None and client_session.client_id != "":
                await self.delete_session(client_session.client_id)
            else:
                client_session.client_id = gen_client_id()
            client_session.parent = 0
        else:
            # Get session from cache
            if client_session.client_id in self._sessions:
                self.logger.debug("Found old session %r",
                                  self._sessions[client_session.client_id])
                (client_session, h) = self._sessions[client_session.client_id]
                client_session.parent = 1
            else:
                client_session.parent = 0
        if not client_session.parent:
            await client_session.start(self)
        if client_session.keep_alive > 0 and not client_session.parent:
            # MQTT 3.1.2.10: one and a half keepalive times, plus configurable grace
            client_session.keep_alive += client_session.keep_alive / 2 + self.config[
                'timeout-disconnect-delay']
        self.logger.debug("Keep-alive timeout=%d", client_session.keep_alive)

        await handler.attach(client_session, adapter)
        self._sessions[client_session.client_id] = (client_session, handler)

        authenticated = await self.authenticate(
            client_session, self.listeners_config[listener_name])
        if not authenticated:
            await adapter.close()
            return

        while True:
            try:
                client_session.transitions.connect()
                break
            except (MachineError, ValueError) as exc:
                # Backwards compat: MachineError is raised by transitions < 0.5.0.
                self.logger.warning(
                    "Client %s is reconnecting too quickly, make it wait",
                    client_session.client_id,
                    exc_info=exc)
                # Wait a bit may be client is reconnecting too fast
                await anyio.sleep(1)
        await handler.mqtt_connack_authorize(authenticated)

        await self.plugins_manager.fire_event(
            EVENT_BROKER_CLIENT_CONNECTED, client_id=client_session.client_id)

        self.logger.debug("%s Start messages handling",
                          client_session.client_id)
        await handler.start()
        self.logger.debug("Retained messages queue size: %d",
                          client_session.retained_messages.qsize())
        await self.publish_session_retained_messages(client_session)

        # Init and start loop for handling client messages (publish, subscribe/unsubscribe, disconnect)
        async with anyio.create_task_group() as tg:

            async def handle_unsubscribe():
                while True:
                    unsubscription = await handler.get_next_pending_unsubscription(
                    )
                    self.logger.debug("%s handling unsubscription",
                                      client_session.client_id)
                    for topic in unsubscription['topics']:
                        self._del_subscription(topic, client_session)
                        await self.plugins_manager.fire_event(
                            EVENT_BROKER_CLIENT_UNSUBSCRIBED,
                            client_id=client_session.client_id,
                            topic=topic)
                    await handler.mqtt_acknowledge_unsubscription(
                        unsubscription['packet_id'])

            async def handle_subscribe():
                while True:
                    subscriptions = await handler.get_next_pending_subscription(
                    )
                    self.logger.debug("%s handling subscription",
                                      client_session.client_id)
                    return_codes = []
                    for subscription in subscriptions['topics']:
                        result = await self.add_subscription(
                            subscription, client_session)
                        return_codes.append(result)
                    await handler.mqtt_acknowledge_subscription(
                        subscriptions['packet_id'], return_codes)
                    for index, subscription in enumerate(
                            subscriptions['topics']):
                        if return_codes[index] != 0x80:
                            await self.plugins_manager.fire_event(
                                EVENT_BROKER_CLIENT_SUBSCRIBED,
                                client_id=client_session.client_id,
                                topic=subscription[0],
                                qos=subscription[1])
                            await self.publish_retained_messages_for_subscription(
                                subscription, client_session)
                    self.logger.debug(repr(self._subscriptions))

            await tg.spawn(handle_unsubscribe)
            await tg.spawn(handle_subscribe)

            try:
                await handler.wait_disconnect()
                self.logger.debug("%s wait_diconnect: %sclean",
                                  client_session.client_id,
                                  "" if handler.clean_disconnect else "un")

                if not handler.clean_disconnect:
                    # Connection closed anormally, send will message
                    self.logger.debug("Will flag: %s",
                                      client_session.will_flag)
                    if client_session.will_flag:
                        if self.logger.isEnabledFor(logging.DEBUG):
                            self.logger.debug(
                                "Client %s disconnected abnormally, sending will message",
                                format_client_message(session=client_session))
                        await self.broadcast_message(
                            client_session,
                            client_session.will_topic,
                            client_session.will_message,
                            client_session.will_qos,
                            retain=client_session.will_retain)
                self.logger.debug("%s Disconnecting session",
                                  client_session.client_id)
                await self._stop_handler(handler)
                client_session.transitions.disconnect()
                await self.plugins_manager.fire_event(
                    EVENT_BROKER_CLIENT_DISCONNECTED,
                    client_id=client_session.client_id)
            finally:
                async with anyio.open_cancel_scope(shield=True):
                    await tg.cancel_scope.cancel()
            pass  # end taskgroup

        self.logger.debug("%s Client disconnected", client_session.client_id)

    async def _stop_handler(self, handler):
        """
        Stop a running handler and detach if from the session
        :param handler:
        :return:
        """
        try:
            await handler.detach()
            await handler.stop()
        except Exception as e:
            #self.logger.exception("Stop handler")
            raise

    async def authenticate(self, session: Session, listener):
        """
        This method call the authenticate method on registered plugins to test user authentication.
        User is considered authenticated if all plugins called returns True.
        Plugins authenticate() method are supposed to return :
         - True if user is authentication succeed
         - False if user authentication fails
         - None if authentication can't be achieved (then plugin result is then ignored)
        :param session:
        :param listener:
        :return:
        """
        auth_plugins = None
        auth_config = self.config.get('auth', None)
        if auth_config:
            auth_plugins = auth_config.get('plugins', None)
        returns = await self.plugins_manager.map_plugin_coro(
            "authenticate", session=session, filter_plugins=auth_plugins)
        auth_result = True
        if returns:
            for plugin in returns:
                res = returns[plugin]
                if res is False:
                    auth_result = False
                    self.logger.debug(
                        "Authentication failed due to '%s' plugin result: %s",
                        plugin.name, res)
                else:
                    self.logger.debug("'%s' plugin result: %s", plugin.name,
                                      res)
        # If all plugins returned True, authentication is success
        return auth_result

    async def topic_filtering(self, session: Session, topic):
        """
        This method call the topic_filtering method on registered plugins to check that the subscription is allowed.
        User is considered allowed if all plugins called return True.
        Plugins topic_filtering() method are supposed to return :
         - True if MQTT client can be subscribed to the topic
         - False if MQTT client is not allowed to subscribe to the topic
         - None if topic filtering can't be achieved (then plugin result is then ignored)
        :param session:
        :param listener:
        :param topic: Topic in which the client wants to subscribe
        :return:
        """
        topic_plugins = None
        topic_config = self.config.get('topic-check', None)
        if topic_config and topic_config.get('enabled', False):
            topic_plugins = topic_config.get('plugins', None)
        returns = await self.plugins_manager.map_plugin_coro(
            "topic_filtering",
            session=session,
            topic=topic,
            filter_plugins=topic_plugins)

        topic_result = True
        if returns:
            for plugin in returns:
                res = returns[plugin]
                if res is False:
                    topic_result = False
                    self.logger.debug(
                        "Topic filtering failed due to '%s' plugin result: %s",
                        plugin.name, res)
                else:
                    self.logger.debug("'%s' plugin result: %s", plugin.name,
                                      res)
        # If all plugins returned True, authentication is success
        return topic_result

    def retain_message(self, source_session, topic_name, data, qos=None):
        if data is not None and data != b'':
            # If retained flag set, store the message for further subscriptions
            self.logger.debug("Retaining message on topic %s", topic_name)
            retained_message = RetainedApplicationMessage(
                source_session, topic_name, data, qos)
            self._retained_messages[topic_name] = retained_message
        else:
            # [MQTT-3.3.1-10]
            if topic_name in self._retained_messages:
                self.logger.debug("Clear retained messages for topic '%s'",
                                  topic_name)
                del self._retained_messages[topic_name]

    async def add_subscription(self, subscription, session):
        a_filter = subscription[0]
        if '#' in a_filter and not a_filter.endswith('#'):
            # [MQTT-4.7.1-2] Wildcard character '#' is only allowed as last character in filter
            return 0x80
        if a_filter != "+":
            if '+' in a_filter:
                if "/+" not in a_filter and "+/" not in a_filter:
                    # [MQTT-4.7.1-3] + wildcard character must occupy entire level
                    return 0x80
        # Check if the client is authorised to connect to the topic
        permitted = await self.topic_filtering(session, topic=a_filter)
        if not permitted:
            return 0x80
        qos = subscription[1]
        if 'max-qos' in self.config and qos > self.config['max-qos']:
            qos = self.config['max-qos']
        if a_filter not in self._subscriptions:
            self._subscriptions[a_filter] = []
        already_subscribed = next(
            (s for (s, qos) in self._subscriptions[a_filter]
             if s.client_id == session.client_id), None)
        if not already_subscribed:
            self._subscriptions[a_filter].append((session, qos))
        else:
            if self.logger.isEnabledFor(logging.DEBUG):
                self.logger.debug("Client %s has already subscribed to %s",
                                  format_client_message(session=session),
                                  a_filter)
        return qos

    def _del_subscription(self, a_filter, session):
        """
        Delete a session subscription on a given topic
        :param a_filter:
        :param session:
        :return:
        """
        deleted = 0
        try:
            subscriptions = self._subscriptions[a_filter]
            for index, (sub_session, qos) in enumerate(subscriptions):
                if sub_session.client_id == session.client_id:
                    if self.logger.isEnabledFor(logging.DEBUG):
                        self.logger.debug(
                            "Removing subscription on topic '%s' for client %s",
                            a_filter, format_client_message(session=session))
                    subscriptions.pop(index)
                    deleted += 1
                    break
        except KeyError:
            # Unsubscribe topic not found in current subscribed topics
            pass
        finally:
            return deleted

    def _del_all_subscriptions(self, session):
        """
        Delete all topic subscriptions for a given session
        :param session:
        :return:
        """
        filter_queue = deque()
        for topic in self._subscriptions:
            if self._del_subscription(topic, session):
                filter_queue.append(topic)
        for topic in filter_queue:
            if not self._subscriptions[topic]:
                del self._subscriptions[topic]

    def matches(self, topic, a_filter):
        if "#" not in a_filter and "+" not in a_filter:
            # if filter doesn't contain wildcard, return exact match
            return a_filter == topic
        else:
            # else use regex
            match_pattern = re.compile(
                a_filter.replace('#', '.*').replace('$', r'\$').replace(
                    '+', r'[/\$\s\w\d]+'))
            return match_pattern.match(topic)

    async def _broadcast_loop(self):
        async with anyio.create_task_group() as tg:
            while True:
                broadcast = await self._broadcast_queue.get()
                self.logger.debug("broadcasting %r", broadcast)
                for k_filter, subscriptions in self._subscriptions.items():
                    if broadcast['topic'].startswith("$") and (
                            k_filter.startswith("+")
                            or k_filter.startswith("#")):
                        self.logger.debug(
                            "[MQTT-4.7.2-1] - ignoring brodcasting $ topic to subscriptions starting with + or #"
                        )
                    elif self.matches(broadcast['topic'], k_filter):
                        for (target_session, qos) in subscriptions:
                            if 'qos' in broadcast:
                                qos = broadcast['qos']
                            if target_session.transitions.state == 'connected':
                                if self.logger.isEnabledFor(logging.DEBUG):
                                    self.logger.debug(
                                        "broadcasting application message from %s on topic '%s' to %s",
                                        format_client_message(
                                            session=broadcast['session']),
                                        broadcast['topic'],
                                        format_client_message(
                                            session=target_session))
                                handler = self._get_handler(target_session)
                                await tg.spawn(
                                    partial(handler.mqtt_publish,
                                            broadcast['topic'],
                                            broadcast['data'],
                                            qos,
                                            retain=False))
                            else:
                                if self.logger.isEnabledFor(logging.DEBUG):
                                    self.logger.debug(
                                        "retaining application message from %s on topic '%s' to client '%s'",
                                        format_client_message(
                                            session=broadcast['session']),
                                        broadcast['topic'],
                                        format_client_message(
                                            session=target_session))
                                retained_message = RetainedApplicationMessage(
                                    broadcast['session'], broadcast['topic'],
                                    broadcast['data'], qos)
                                await target_session.retained_messages.put(
                                    retained_message)

    async def broadcast_message(self,
                                session,
                                topic,
                                data,
                                force_qos=None,
                                qos=None,
                                retain=False):
        broadcast = {
            'session': session,
            'topic': topic,
            'data': data,
        }
        if force_qos:
            broadcast['qos'] = force_qos
        await self._broadcast_queue.put(broadcast)
        if retain:
            self.retain_message(session, topic, data, force_qos or qos)

    async def publish_session_retained_messages(self, session):
        if self.logger.isEnabledFor(logging.DEBUG):
            self.logger.debug("Publishing %d messages retained for session %s",
                              session.retained_messages.qsize(),
                              format_client_message(session=session))
        handler = self._get_handler(session)
        async with anyio.create_task_group() as tg:
            while not session.retained_messages.empty():
                retained = await session.retained_messages.get()
                await tg.spawn(handler.mqtt_publish, retained.topic,
                               retained.data, retained.qos, True)

    async def publish_retained_messages_for_subscription(
            self, subscription, session):
        if self.logger.isEnabledFor(logging.DEBUG):
            self.logger.debug(
                "Begin broadcasting messages retained due to subscription on '%s' from %s",
                subscription[0], format_client_message(session=session))
        handler = self._get_handler(session)
        async with anyio.create_task_group() as tg:
            for d_topic in self._retained_messages:
                self.logger.debug("matching : %s %s", d_topic, subscription[0])
                if self.matches(d_topic, subscription[0]):
                    self.logger.debug("%s and %s match", d_topic,
                                      subscription[0])
                    retained = self._retained_messages[d_topic]
                    await tg.spawn(handler.mqtt_publish, retained.topic,
                                   retained.data, subscription[1], True)
        if self.logger.isEnabledFor(logging.DEBUG):
            self.logger.debug(
                "End broadcasting messages retained due to subscription on '%s' from %s",
                subscription[0], format_client_message(session=session))

    async def delete_session(self, client_id):
        """
        Delete an existing session data, for example due to clean session set in CONNECT
        :param client_id:
        :return:
        """
        try:
            session = self._sessions[client_id][0]
        except KeyError:
            session = None
        if session is None:
            self.logger.debug("Delete session : session %s doesn't exist",
                              client_id)
            return

        # Delete subscriptions
        self.logger.debug("deleting session %r subscriptions", session)
        self._del_all_subscriptions(session)

        self.logger.debug("deleting existing session %r",
                          self._sessions[client_id])
        await session.stop()
        del self._sessions[client_id]

    def _get_handler(self, session):
        client_id = session.client_id
        if client_id:
            try:
                return self._sessions[client_id][1]
            except KeyError:
                pass
        return None
示例#58
0
class Broker:
    """
    MQTT 3.1.1 compliant broker implementation

    :param config: Example Yaml config
    :param loop: asyncio loop to use. Defaults to ``asyncio.get_event_loop()`` if none is given
    :param plugin_namespace: Plugin namespace to use when loading plugin entry_points. Defaults to ``hbmqtt.broker.plugins``

    """
    states = ['new', 'starting', 'started', 'not_started', 'stopping', 'stopped', 'not_stopped', 'stopped']

    def __init__(self, config=None, loop=None, plugin_namespace=None):
        self.logger = logging.getLogger(__name__)
        self.config = _defaults
        if config is not None:
            self.config.update(config)
        self._build_listeners_config(self.config)

        if loop is not None:
            self._loop = loop
        else:
            self._loop = asyncio.get_event_loop()

        self._servers = dict()
        self._init_states()
        self._sessions = dict()
        self._subscriptions = dict()
        self._retained_messages = dict()
        self._broadcast_queue = asyncio.Queue(loop=self._loop)

        self._broadcast_task = None

        # Init plugins manager
        context = BrokerContext(self)
        context.config = self.config
        if plugin_namespace:
            namespace = plugin_namespace
        else:
            namespace = 'hbmqtt.broker.plugins'
        self.plugins_manager = PluginManager(namespace, context, self._loop)

    def _build_listeners_config(self, broker_config):
        self.listeners_config = dict()
        try:
            listeners_config = broker_config['listeners']
            defaults = listeners_config['default']
            for listener in listeners_config:
                config = dict(defaults)
                config.update(listeners_config[listener])
                self.listeners_config[listener] = config
        except KeyError as ke:
            raise BrokerException("Listener config not found invalid: %s" % ke)

    def _init_states(self):
        self.transitions = Machine(states=Broker.states, initial='new')
        self.transitions.add_transition(trigger='start', source='new', dest='starting')
        self.transitions.add_transition(trigger='starting_fail', source='starting', dest='not_started')
        self.transitions.add_transition(trigger='starting_success', source='starting', dest='started')
        self.transitions.add_transition(trigger='shutdown', source='started', dest='stopping')
        self.transitions.add_transition(trigger='stopping_success', source='stopping', dest='stopped')
        self.transitions.add_transition(trigger='stopping_failure', source='stopping', dest='not_stopped')
        self.transitions.add_transition(trigger='start', source='stopped', dest='starting')

    @asyncio.coroutine
    def start(self):
        """
            Start the broker to serve with the given configuration

            Start method opens network sockets and will start listening for incoming connections.

            This method is a *coroutine*.
        """
        try:
            self._sessions = dict()
            self._subscriptions = dict()
            self._retained_messages = dict()
            self.transitions.start()
            self.logger.debug("Broker starting")
        except (MachineError, ValueError) as exc:
            # Backwards compat: MachineError is raised by transitions < 0.5.0.
            self.logger.warning("[WARN-0001] Invalid method call at this moment: %s" % exc)
            raise BrokerException("Broker instance can't be started: %s" % exc)

        yield from self.plugins_manager.fire_event(EVENT_BROKER_PRE_START)
        try:
            # Start network listeners
            for listener_name in self.listeners_config:
                listener = self.listeners_config[listener_name]

                if 'bind' not in listener:
                    self.logger.debug("Listener configuration '%s' is not bound" % listener_name)
                else:
                    # Max connections
                    try:
                        max_connections = listener['max_connections']
                    except KeyError:
                        max_connections = -1

                    # SSL Context
                    sc = None

                    # accept string "on" / "off" or boolean
                    ssl_active = listener.get('ssl', False)
                    if isinstance(ssl_active, str):
                        ssl_active = ssl_active.upper() == 'ON'

                    if ssl_active:
                        try:
                            sc = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
                            sc.load_cert_chain(listener['certfile'], listener['keyfile'])
                            sc.verify_mode = ssl.CERT_OPTIONAL
                        except KeyError as ke:
                            raise BrokerException("'certfile' or 'keyfile' configuration parameter missing: %s" % ke)
                        except FileNotFoundError as fnfe:
                            raise BrokerException("Can't read cert files '%s' or '%s' : %s" %
                                                  (listener['certfile'], listener['keyfile'], fnfe))

                    address, s_port = listener['bind'].split(':')
                    port = 0
                    try:
                        port = int(s_port)
                    except ValueError as ve:
                        raise BrokerException("Invalid port value in bind value: %s" % listener['bind'])

                    if listener['type'] == 'tcp':
                        cb_partial = partial(self.stream_connected, listener_name=listener_name)
                        instance = yield from asyncio.start_server(cb_partial,
                                                                   address,
                                                                   port,
                                                                   ssl=sc,
                                                                   loop=self._loop)
                        self._servers[listener_name] = Server(listener_name, instance, max_connections, self._loop)
                    elif listener['type'] == 'ws':
                        cb_partial = partial(self.ws_connected, listener_name=listener_name)
                        instance = yield from websockets.serve(cb_partial, address, port, ssl=sc, loop=self._loop,
                                                               subprotocols=['mqtt'])
                        self._servers[listener_name] = Server(listener_name, instance, max_connections, self._loop)

                    self.logger.info("Listener '%s' bind to %s (max_connections=%d)" %
                                     (listener_name, listener['bind'], max_connections))

            self.transitions.starting_success()
            yield from self.plugins_manager.fire_event(EVENT_BROKER_POST_START)

            #Start broadcast loop
            self._broadcast_task = ensure_future(self._broadcast_loop(), loop=self._loop)

            self.logger.debug("Broker started")
        except Exception as e:
            self.logger.error("Broker startup failed: %s" % e)
            self.transitions.starting_fail()
            raise BrokerException("Broker instance can't be started: %s" % e)

    @asyncio.coroutine
    def shutdown(self):
        """
            Stop broker instance.

            Closes all connected session, stop listening on network socket and free resources.
        """
        try:
            self._sessions = dict()
            self._subscriptions = dict()
            self._retained_messages = dict()
            self.transitions.shutdown()
        except (MachineError, ValueError) as exc:
            # Backwards compat: MachineError is raised by transitions < 0.5.0.
            self.logger.debug("Invalid method call at this moment: %s" % exc)
            raise BrokerException("Broker instance can't be stopped: %s" % exc)

        # Fire broker_shutdown event to plugins
        yield from self.plugins_manager.fire_event(EVENT_BROKER_PRE_SHUTDOWN)

        # Stop broadcast loop
        if self._broadcast_task:
            self._broadcast_task.cancel()
        if self._broadcast_queue.qsize() > 0:
            self.logger.warning("%d messages not broadcasted" % self._broadcast_queue.qsize())

        for listener_name in self._servers:
            server = self._servers[listener_name]
            yield from server.close_instance()
        self.logger.debug("Broker closing")
        self.logger.info("Broker closed")
        yield from self.plugins_manager.fire_event(EVENT_BROKER_POST_SHUTDOWN)
        self.transitions.stopping_success()

    @asyncio.coroutine
    def internal_message_broadcast(self, topic, data, qos=None):
        return (yield from self._broadcast_message(None, topic, data))

    @asyncio.coroutine
    def ws_connected(self, websocket, uri, listener_name):
        yield from self.client_connected(listener_name, WebSocketsReader(websocket), WebSocketsWriter(websocket))

    @asyncio.coroutine
    def stream_connected(self, reader, writer, listener_name):
        yield from self.client_connected(listener_name, StreamReaderAdapter(reader), StreamWriterAdapter(writer))

    @asyncio.coroutine
    def client_connected(self, listener_name, reader: ReaderAdapter, writer: WriterAdapter):
        # Wait for connection available on listener
        server = self._servers.get(listener_name, None)
        if not server:
            raise BrokerException("Invalid listener name '%s'" % listener_name)
        yield from server.acquire_connection()

        remote_address, remote_port = writer.get_peer_info()
        self.logger.info("Connection from %s:%d on listener '%s'" % (remote_address, remote_port, listener_name))

        # Wait for first packet and expect a CONNECT
        logh = logging.getLogger("logh")
        try:
            handler, client_session, connect1 = yield from BrokerProtocolHandler.init_from_connect(reader, writer, self.plugins_manager, loop=self._loop)
            smsg = str(remote_address)+":"+str(remote_port)+"::"+str(connect1)	    
            logh.info(smsg)
        except HBMQTTException as exc:
            self.logger.warning("[MQTT-3.1.0-1] %s: Can't read first packet an CONNECT: %s" %
                                (format_client_message(address=remote_address, port=remote_port), exc))
            #yield from writer.close()
            self.logger.debug("Connection closed")
            return
        except MQTTException as me:
            self.logger.error('Invalid connection from %s : %s' %
                              (format_client_message(address=remote_address, port=remote_port), me))
            yield from writer.close()
            self.logger.debug("Connection closed")
            return

        if client_session.clean_session:
            # Delete existing session and create a new one
            if client_session.client_id is not None:
                self.delete_session(client_session.client_id)
            else:
                client_session.client_id = gen_client_id()
            client_session.parent = 0
        else:
            # Get session from cache
            if client_session.client_id in self._sessions:
                self.logger.debug("Found old session %s" % repr(self._sessions[client_session.client_id]))
                (client_session, h) = self._sessions[client_session.client_id]
                client_session.parent = 1
            else:
                client_session.parent = 0
        if client_session.keep_alive > 0:
            client_session.keep_alive += self.config['timeout-disconnect-delay']
        self.logger.debug("Keep-alive timeout=%d" % client_session.keep_alive)

        handler.attach(client_session, reader, writer)
        self._sessions[client_session.client_id] = (client_session, handler)

        authenticated = yield from self.authenticate(client_session, self.listeners_config[listener_name])
        if not authenticated:
            yield from writer.close()
            return

        while True:
            try:
                client_session.transitions.connect()
                break
            except (MachineError, ValueError):
                # Backwards compat: MachineError is raised by transitions < 0.5.0.
                self.logger.warning("Client %s is reconnecting too quickly, make it wait" % client_session.client_id)
                # Wait a bit may be client is reconnecting too fast
                yield from asyncio.sleep(1, loop=self._loop)
        yield from handler.mqtt_connack_authorize(authenticated)

        yield from self.plugins_manager.fire_event(EVENT_BROKER_CLIENT_CONNECTED, client_id=client_session.client_id)

        self.logger.debug("%s Start messages handling" % client_session.client_id)
        yield from handler.start()
        self.logger.debug("Retained messages queue size: %d" % client_session.retained_messages.qsize())
        yield from self.publish_session_retained_messages(client_session)

        # Init and start loop for handling client messages (publish, subscribe/unsubscribe, disconnect)
        disconnect_waiter = ensure_future(handler.wait_disconnect(), loop=self._loop)
        subscribe_waiter = ensure_future(handler.get_next_pending_subscription(), loop=self._loop)
        unsubscribe_waiter = ensure_future(handler.get_next_pending_unsubscription(), loop=self._loop)
        wait_deliver = ensure_future(handler.mqtt_deliver_next_message(), loop=self._loop)
        connected = True
        while connected:
            try:
                done, pending = yield from asyncio.wait(
                    [disconnect_waiter, subscribe_waiter, unsubscribe_waiter, wait_deliver],
                    return_when=asyncio.FIRST_COMPLETED, loop=self._loop)
                if disconnect_waiter in done:
                    result = disconnect_waiter.result()
                    self.logger.debug("%s Result from wait_diconnect: %s" % (client_session.client_id, result))
                    if result is None:
                        self.logger.debug("Will flag: %s" % client_session.will_flag)
                        # Connection closed anormally, send will message
                        if client_session.will_flag:
                            self.logger.debug("Client %s disconnected abnormally, sending will message" %
                                              format_client_message(client_session))
                            yield from self._broadcast_message(
                                client_session,
                                client_session.will_topic,
                                client_session.will_message,
                                client_session.will_qos)
                            if client_session.will_retain:
                                self.retain_message(client_session,
                                                    client_session.will_topic,
                                                    client_session.will_message,
                                                    client_session.will_qos)
                    self.logger.debug("%s Disconnecting session" % client_session.client_id)
                    yield from self._stop_handler(handler)
                    client_session.transitions.disconnect()
                    yield from self.plugins_manager.fire_event(EVENT_BROKER_CLIENT_DISCONNECTED, client_id=client_session.client_id)
                    connected = False
                if unsubscribe_waiter in done:
                    self.logger.debug("%s handling unsubscription" % client_session.client_id)
                    unsubscription = unsubscribe_waiter.result()
                    for topic in unsubscription['topics']:
                        self._del_subscription(topic, client_session)
                        yield from self.plugins_manager.fire_event(
                            EVENT_BROKER_CLIENT_UNSUBSCRIBED,
                            client_id=client_session.client_id,
                            topic=topic)
                    yield from handler.mqtt_acknowledge_unsubscription(unsubscription['packet_id'])
                    unsubscribe_waiter = asyncio.Task(handler.get_next_pending_unsubscription(), loop=self._loop)
                if subscribe_waiter in done:
                    self.logger.debug("%s handling subscription" % client_session.client_id)
                    subscriptions = subscribe_waiter.result()
                    return_codes = []
                    for subscription in subscriptions['topics']:
                        return_codes.append(self.add_subscription(subscription, client_session))
                    yield from handler.mqtt_acknowledge_subscription(subscriptions['packet_id'], return_codes)
                    for index, subscription in enumerate(subscriptions['topics']):
                        if return_codes[index] != 0x80:
                            yield from self.plugins_manager.fire_event(
                                EVENT_BROKER_CLIENT_SUBSCRIBED,
                                client_id=client_session.client_id,
                                topic=subscription[0],
                                qos=subscription[1])
                            yield from self.publish_retained_messages_for_subscription(subscription, client_session)
                    subscribe_waiter = asyncio.Task(handler.get_next_pending_subscription(), loop=self._loop)
                    self.logger.debug(repr(self._subscriptions))
                if wait_deliver in done:
                    if self.logger.isEnabledFor(logging.DEBUG):
                        self.logger.debug("%s handling message delivery" % client_session.client_id)
                    app_message = wait_deliver.result()
                    if not app_message.topic:
                        self.logger.warning("[MQTT-4.7.3-1] - %s invalid TOPIC sent in PUBLISH message, closing connection" % client_session.client_id)
                        break
                    if "#" in app_message.topic or "+" in app_message.topic:
                        self.logger.warning("[MQTT-3.3.2-2] - %s invalid TOPIC sent in PUBLISH message, closing connection" % client_session.client_id)
                        break
                    yield from self.plugins_manager.fire_event(EVENT_BROKER_MESSAGE_RECEIVED,
                                                               client_id=client_session.client_id,
                                                               message=app_message)
                    yield from self._broadcast_message(client_session, app_message.topic, app_message.data)
                    if app_message.publish_packet.retain_flag:
                        self.retain_message(client_session, app_message.topic, app_message.data, app_message.qos)
                    wait_deliver = asyncio.Task(handler.mqtt_deliver_next_message(), loop=self._loop)
            except asyncio.CancelledError:
                self.logger.debug("Client loop cancelled")
                break
        disconnect_waiter.cancel()
        subscribe_waiter.cancel()
        unsubscribe_waiter.cancel()
        wait_deliver.cancel()

        self.logger.debug("%s Client disconnected" % client_session.client_id)
        server.release_connection()

    def _init_handler(self, session, reader, writer):
        """
        Create a BrokerProtocolHandler and attach to a session
        :return:
        """
        handler = BrokerProtocolHandler(self.plugins_manager, self._loop)
        handler.attach(session, reader, writer)
        return handler

    @asyncio.coroutine
    def _stop_handler(self, handler):
        """
        Stop a running handler and detach if from the session
        :param handler:
        :return:
        """
        try:
            yield from handler.stop()
        except Exception as e:
            self.logger.error(e)

    @asyncio.coroutine
    def authenticate(self, session: Session, listener):
        """
        This method call the authenticate method on registered plugins to test user authentication.
        User is considered authenticated if all plugins called returns True.
        Plugins authenticate() method are supposed to return :
         - True if user is authentication succeed
         - False if user authentication fails
         - None if authentication can't be achieved (then plugin result is then ignored)
        :param session:
        :param listener:
        :return:
        """
        auth_plugins = None
        auth_config = self.config.get('auth', None)
        if auth_config:
            auth_plugins = auth_config.get('plugins', None)
        returns = yield from self.plugins_manager.map_plugin_coro(
            "authenticate",
            session=session,
            filter_plugins=auth_plugins)
        auth_result = True
        if returns:
            for plugin in returns:
                res = returns[plugin]
                if res is False:
                    auth_result = False
                    self.logger.debug("Authentication failed due to '%s' plugin result: %s" % (plugin.name, res))
                else:
                    self.logger.debug("'%s' plugin result: %s" % (plugin.name, res))
        # If all plugins returned True, authentication is success
        return auth_result

    def retain_message(self, source_session, topic_name, data, qos=None):
        if data is not None and data != b'':
            # If retained flag set, store the message for further subscriptions
            self.logger.debug("Retaining message on topic %s" % topic_name)
            retained_message = RetainedApplicationMessage(source_session, topic_name, data, qos)
            self._retained_messages[topic_name] = retained_message
        else:
            # [MQTT-3.3.1-10]
            if topic_name in self._retained_messages:
                self.logger.debug("Clear retained messages for topic '%s'" % topic_name)
                del self._retained_messages[topic_name]

    def add_subscription(self, subscription, session):
        try:
            a_filter = subscription[0]
            if '#' in a_filter and not a_filter.endswith('#'):
                # [MQTT-4.7.1-2] Wildcard character '#' is only allowed as last character in filter
                return 0x80
            if a_filter != "+":
                if '+' in a_filter:
                    if "/+" not in a_filter and "+/" not in a_filter:
                        # [MQTT-4.7.1-3] + wildcard character must occupy entire level
                        return 0x80

            qos = subscription[1]
            if 'max-qos' in self.config and qos > self.config['max-qos']:
                qos = self.config['max-qos']
            if a_filter not in self._subscriptions:
                self._subscriptions[a_filter] = []
            already_subscribed = next(
                (s for (s, qos) in self._subscriptions[a_filter] if s.client_id == session.client_id), None)
            if not already_subscribed:
                self._subscriptions[a_filter].append((session, qos))
            else:
                self.logger.debug("Client %s has already subscribed to %s" % (format_client_message(session=session), a_filter))
            return qos
        except KeyError:
            return 0x80

    def _del_subscription(self, a_filter, session):
        """
        Delete a session subscription on a given topic
        :param a_filter:
        :param session:
        :return:
        """
        deleted = 0
        try:
            subscriptions = self._subscriptions[a_filter]
            for index, (sub_session, qos) in enumerate(subscriptions):
                if sub_session.client_id == session.client_id:
                    self.logger.debug("Removing subscription on topic '%s' for client %s" %
                                      (a_filter, format_client_message(session=session)))
                    subscriptions.pop(index)
                    deleted += 1
                    break
        except KeyError:
            # Unsubscribe topic not found in current subscribed topics
            pass
        finally:
            return deleted

    def _del_all_subscriptions(self, session):
        """
        Delete all topic subscriptions for a given session
        :param session:
        :return:
        """
        filter_queue = deque()
        for topic in self._subscriptions:
            if self._del_subscription(topic, session):
                filter_queue.append(topic)
        for topic in filter_queue:
            if not self._subscriptions[topic]:
                del self._subscriptions[topic]

    def matches(self, topic, a_filter):
        if "#" not in a_filter and "+" not in a_filter:
            # if filter doesn't contain wildcard, return exact match
            return a_filter == topic
        else:
            # else use regex
            match_pattern = re.compile(a_filter.replace('#', '.*').replace('$', '\$').replace('+', '[/\$\s\w\d]+'))
            return match_pattern.match(topic)

    @asyncio.coroutine
    def _broadcast_loop(self):
        running_tasks = deque()
        try:
            while True:
                while running_tasks and running_tasks[0].done():
                    running_tasks.popleft()
                broadcast = yield from self._broadcast_queue.get()
                if self.logger.isEnabledFor(logging.DEBUG):
                    self.logger.debug("broadcasting %r" % broadcast)
                for k_filter in self._subscriptions:
                    if broadcast['topic'].startswith("$") and (k_filter.startswith("+") or k_filter.startswith("#")):
                        self.logger.debug("[MQTT-4.7.2-1] - ignoring brodcasting $ topic to subscriptions starting with + or #")
                    elif self.matches(broadcast['topic'], k_filter):
                        subscriptions = self._subscriptions[k_filter]
                        for (target_session, qos) in subscriptions:
                            if 'qos' in broadcast:
                                qos = broadcast['qos']
                            if target_session.transitions.state == 'connected':
                                self.logger.debug("broadcasting application message from %s on topic '%s' to %s" %
                                                  (format_client_message(session=broadcast['session']),
                                                   broadcast['topic'], format_client_message(session=target_session)))
                                handler = self._get_handler(target_session)
                                task = ensure_future(
                                    handler.mqtt_publish(broadcast['topic'], broadcast['data'], qos, retain=False),
                                    loop=self._loop)
                                running_tasks.append(task)
                            else:
                                self.logger.debug("retaining application message from %s on topic '%s' to client '%s'" %
                                                  (format_client_message(session=broadcast['session']),
                                                   broadcast['topic'], format_client_message(session=target_session)))
                                retained_message = RetainedApplicationMessage(
                                    broadcast['session'], broadcast['topic'], broadcast['data'], qos)
                                yield from target_session.retained_messages.put(retained_message)
        except CancelledError:
            # Wait until current broadcasting tasks end
            if running_tasks:
                yield from asyncio.wait(running_tasks, loop=self._loop)

    @asyncio.coroutine
    def _broadcast_message(self, session, topic, data, force_qos=None):
        broadcast = {
            'session': session,
            'topic': topic,
            'data': data
        }
        if force_qos:
            broadcast['qos'] = force_qos
        yield from self._broadcast_queue.put(broadcast)

    @asyncio.coroutine
    def publish_session_retained_messages(self, session):
        self.logger.debug("Publishing %d messages retained for session %s" %
                          (session.retained_messages.qsize(), format_client_message(session=session))
                          )
        publish_tasks = []
        handler = self._get_handler(session)
        while not session.retained_messages.empty():
            retained = yield from session.retained_messages.get()
            publish_tasks.append(ensure_future(
                handler.mqtt_publish(
                    retained.topic, retained.data, retained.qos, True), loop=self._loop))
        if publish_tasks:
            yield from asyncio.wait(publish_tasks, loop=self._loop)

    @asyncio.coroutine
    def publish_retained_messages_for_subscription(self, subscription, session):
        self.logger.debug("Begin broadcasting messages retained due to subscription on '%s' from %s" %
                          (subscription[0], format_client_message(session=session)))
        publish_tasks = []
        handler = self._get_handler(session)
        for d_topic in self._retained_messages:
            self.logger.debug("matching : %s %s" % (d_topic, subscription[0]))
            if self.matches(d_topic, subscription[0]):
                self.logger.debug("%s and %s match" % (d_topic, subscription[0]))
                retained = self._retained_messages[d_topic]
                publish_tasks.append(asyncio.Task(
                    handler.mqtt_publish(
                        retained.topic, retained.data, subscription[1], True), loop=self._loop))
        if publish_tasks:
            yield from asyncio.wait(publish_tasks, loop=self._loop)
        self.logger.debug("End broadcasting messages retained due to subscription on '%s' from %s" %
                          (subscription[0], format_client_message(session=session)))

    def delete_session(self, client_id):
        """
        Delete an existing session data, for example due to clean session set in CONNECT
        :param client_id:
        :return:
        """
        try:
            session = self._sessions[client_id][0]
        except KeyError:
            session = None
        if session is None:
            self.logger.debug("Delete session : session %s doesn't exist" % client_id)
            return

        # Delete subscriptions
        self.logger.debug("deleting session %s subscriptions" % repr(session))
        self._del_all_subscriptions(session)

        self.logger.debug("deleting existing session %s" % repr(self._sessions[client_id]))
        del self._sessions[client_id]

    def _get_handler(self, session):
        client_id = session.client_id
        if client_id:
            try:
                return self._sessions[client_id][1]
            except KeyError:
                pass
        return None
示例#59
0
 def __init__(self):
     self.machine = Machine(model=self,
                            states=FSM.fsm_states,
                            initial='start')
     self.machine.add_transition('comenzar',
                                 'start',
                                 'inicio',
                                 after='activar_seguidor_linea')
     self.machine.add_transition('interseccion',
                                 'inicio',
                                 'media_vuelta_inter_1',
                                 after='girar')
     self.machine.add_transition('giro_completo',
                                 'media_vuelta_inter_1',
                                 'buscar_cubo',
                                 after='sondeo')
     self.machine.add_transition('no_cubo',
                                 'buscar_cubo',
                                 'avanzar',
                                 after='activar_seguidor_linea')
     self.machine.add_transition('timeout',
                                 'avanzar',
                                 'buscar_cubo',
                                 after='sondeo')
     self.machine.add_transition('interseccion',
                                 'avanzar',
                                 'media_vuelta_inicio',
                                 after='girar')
     self.machine.add_transition('cubo',
                                 'buscar_cubo',
                                 'avanzar_cubo',
                                 after='perseguir_cubo')
     self.machine.add_transition('cubo_atrapado',
                                 'avanzar_cubo',
                                 'encerrar_cubo',
                                 after='cerrar_brazo')
     self.machine.add_transition('cubo_encerrado',
                                 'encerrar_cubo',
                                 'girar_linea',
                                 after='girar')
     self.machine.add_transition('giro_completo',
                                 'girar_linea',
                                 'buscar_linea',
                                 after='encontrar_linea')
     self.machine.add_transition('linea',
                                 'buscar_linea',
                                 'seguir_linea_1_f',
                                 conditions='negro',
                                 after='activar_seguidor_linea')
     self.machine.add_transition('interseccion',
                                 'seguir_linea_1_f',
                                 'soltar_cubo',
                                 after='abrir_brazo')
     self.machine.add_transition('cubo_liberado',
                                 'soltar_cubo',
                                 'media_vuelta_inicio',
                                 conditions='negro',
                                 after='girar')
     self.machine.add_transition('giro_completo',
                                 'media_vuelta_inicio',
                                 'inicio',
                                 after='activar_seguidor_linea')
     self.machine.add_transition('linea',
                                 'buscar_linea',
                                 'seguir_linea_1_b',
                                 unless='negro',
                                 after='activar_seguidor_linea')
     self.machine.add_transition('interseccion',
                                 'seguir_linea_1_b',
                                 'seguir_linea_2_f',
                                 after='activar_seguidor_linea')
     self.machine.add_transition('interseccion',
                                 'seguir_linea_2_f',
                                 'girar_color_f',
                                 after='girar')
     self.machine.add_transition('giro_completo',
                                 'girar_color_f',
                                 'seguir_linea_3_f',
                                 after='activar_seguidor_linea')
     self.machine.add_transition('interseccion',
                                 'seguir_linea_3_f',
                                 'soltar_cubo',
                                 after='abrir_brazo')
     self.machine.add_transition('cubo_liberado',
                                 'soltar_cubo',
                                 'media_vuelta_fin',
                                 after='girar')
     self.machine.add_transition('giro_completo',
                                 'media_vuelta_fin',
                                 'seguir_linea_3_b',
                                 after='activar_seguidor_linea')
     self.machine.add_transition('interseccion',
                                 'seguir_linea_3_b',
                                 'girar_color_b',
                                 after='girar')
     self.machine.add_transition('giro_completo',
                                 'girar_color_b',
                                 'seguir_linea_2_b',
                                 after='activar_seguidor_linea')
     self.machine.add_transition('interseccion',
                                 'seguir_linea_2_b',
                                 'buscar_cubo',
                                 after='sondeo')
示例#60
0
    def __init__(self, game):
        self.game = game

        m = Machine(self, states=GAME_STATES, initial='turn_start'
                    )  #Note! on_enter won't fire for initial state first time!
        m.add_ordered_transitions()